Switch "command-like" options --list-cinemas and --list-dkdm-cpls to actual commands.
[dcpomatic.git] / test / kdm_cli_test.cc
1 /*
2     Copyright (C) 2022 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "lib/cinema.h"
23 #include "lib/config.h"
24 #include "lib/content_factory.h"
25 #include "lib/cross.h"
26 #include "lib/film.h"
27 #include "lib/kdm_cli.h"
28 #include "lib/screen.h"
29 #include "lib/trusted_device.h"
30 #include "test.h"
31 #include <boost/algorithm/string/predicate.hpp>
32 #include <boost/filesystem.hpp>
33 #include <boost/test/unit_test.hpp>
34 #include <iostream>
35
36
37 using std::string;
38 using std::vector;
39 using boost::optional;
40
41
42 optional<string>
43 run(vector<string> const& args, vector<string>& output, bool dump_errors = true)
44 {
45         std::vector<char*> argv(args.size());
46         for (auto i = 0U; i < args.size(); ++i) {
47                 argv[i] = const_cast<char*>(args[i].c_str());
48         }
49
50         auto error = kdm_cli(args.size(), argv.data(), [&output](string s) { output.push_back(s); });
51         if (error && dump_errors) {
52                 std::cout << *error << "\n";
53         }
54
55         return error;
56 }
57
58
59 BOOST_AUTO_TEST_CASE (kdm_cli_test_certificate)
60 {
61         vector<string> args = {
62                 "kdm_cli",
63                 "--verbose",
64                 "--valid-from", "now",
65                 "--valid-duration", "2 weeks",
66                 "--projector-certificate", "test/data/cert.pem",
67                 "-S", "my great screen",
68                 "-o", "build/test",
69                 "test/data/dkdm.xml"
70         };
71
72         boost::filesystem::path const kdm_filename = "build/test/KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV__my_great_screen.xml";
73         boost::system::error_code ec;
74         boost::filesystem::remove(kdm_filename, ec);
75
76         vector<string> output;
77         auto error = run(args, output);
78         BOOST_CHECK (!error);
79
80         BOOST_CHECK(boost::filesystem::exists(kdm_filename));
81 }
82
83
84 BOOST_AUTO_TEST_CASE(kdm_cli_specify_decryption_key_test)
85 {
86         using boost::filesystem::path;
87
88         ConfigRestorer cr;
89
90         path const dir = "build/test/kdm_cli_specify_decryption_key_test";
91
92         boost::system::error_code ec;
93         boost::filesystem::remove_all(dir, ec);
94         boost::filesystem::create_directories(dir);
95
96         dcp::CertificateChain chain(openssl_path(), 365);
97         dcp::write_string_to_file(chain.leaf().certificate(true), dir / "cert.pem");
98         dcp::write_string_to_file(*chain.key(), dir / "key.pem");
99
100         vector<string> make_args = {
101                 "kdm_cli",
102                 "--valid-from", "now",
103                 "--valid-duration", "2 weeks",
104                 "--projector-certificate", path(dir / "cert.pem").string(),
105                 "-S", "base",
106                 "-o", dir.string(),
107                 "test/data/dkdm.xml"
108         };
109
110         vector<string> output;
111         auto error = run(make_args, output);
112         BOOST_CHECK(!error);
113
114         vector<string> bad_args = {
115                 "kdm_cli",
116                 "--valid-from", "now",
117                 "--valid-duration", "2 weeks",
118                 "--projector-certificate", path(dir / "cert.pem").string(),
119                 "-S", "bad",
120                 "-o", dir.string(),
121                 path(dir / "KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV__base.xml").string()
122         };
123
124         /* This should fail because we're using the wrong decryption certificate */
125         output.clear();
126         error = run(bad_args, output, false);
127         BOOST_REQUIRE(error);
128         BOOST_CHECK(error->find("oaep decoding error") != string::npos);
129
130         vector<string> good_args = {
131                 "kdm_cli",
132                 "--valid-from", "now",
133                 "--valid-duration", "2 weeks",
134                 "--projector-certificate", path(dir / "cert.pem").string(),
135                 "--decryption-key", path(dir / "key.pem").string(),
136                 "-S", "good",
137                 "-o", dir.string(),
138                 path(dir / "KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV__base.xml").string()
139         };
140
141         /* This should succeed */
142         output.clear();
143         error = run(good_args, output);
144         BOOST_CHECK(!error);
145 }
146
147
148 static
149 void
150 setup_test_config()
151 {
152         auto config = Config::instance();
153         auto const cert = dcp::Certificate(dcp::file_to_string("test/data/cert.pem"));
154
155         auto cinema_a = std::make_shared<Cinema>("Dean's Screens", vector<string>(), "");
156         cinema_a->add_screen(std::make_shared<dcpomatic::Screen>("Screen 1", "", cert, boost::none, std::vector<TrustedDevice>()));
157         cinema_a->add_screen(std::make_shared<dcpomatic::Screen>("Screen 2", "", cert, boost::none, std::vector<TrustedDevice>()));
158         cinema_a->add_screen(std::make_shared<dcpomatic::Screen>("Screen 3", "", cert, boost::none, std::vector<TrustedDevice>()));
159         config->add_cinema(cinema_a);
160
161         auto cinema_b = std::make_shared<Cinema>("Floyd's Celluloid", vector<string>(), "");
162         cinema_b->add_screen(std::make_shared<dcpomatic::Screen>("Foo", "", cert, boost::none, std::vector<TrustedDevice>()));
163         cinema_b->add_screen(std::make_shared<dcpomatic::Screen>("Bar", "", cert, boost::none, std::vector<TrustedDevice>()));
164         config->add_cinema(cinema_b);
165 }
166
167
168 BOOST_AUTO_TEST_CASE(kdm_cli_select_cinema)
169 {
170         ConfigRestorer cr;
171
172         setup_test_config();
173
174         vector<boost::filesystem::path> kdm_filenames = {
175                 "build/test/KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV_Floyds_Celluloid_Foo.xml",
176                 "build/test/KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV_Floyds_Celluloid_Bar.xml"
177         };
178
179         for (auto path: kdm_filenames) {
180                 boost::system::error_code ec;
181                 boost::filesystem::remove(path, ec);
182         }
183
184         vector<string> args = {
185                 "kdm_cli",
186                 "--verbose",
187                 "--valid-from", "now",
188                 "--valid-duration", "2 weeks",
189                 "-c", "Floyd's Celluloid",
190                 "-o", "build/test",
191                 "test/data/dkdm.xml"
192         };
193
194         vector<string> output;
195         auto error = run(args, output);
196         BOOST_CHECK(!error);
197
198         BOOST_REQUIRE_EQUAL(output.size(), 2U);
199         BOOST_CHECK(boost::algorithm::starts_with(output[0], "Making KDMs valid from"));
200         BOOST_CHECK_EQUAL(output[1], "Wrote 2 KDM files to build/test");
201
202         for (auto path: kdm_filenames) {
203                 BOOST_CHECK(boost::filesystem::exists(path));
204         }
205 }
206
207
208 BOOST_AUTO_TEST_CASE(kdm_cli_select_screen)
209 {
210         ConfigRestorer cr;
211
212         setup_test_config();
213
214         boost::filesystem::path kdm_filename = "build/test/KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV_Deans_Screens_Screen_2.xml";
215
216         boost::system::error_code ec;
217         boost::filesystem::remove(kdm_filename, ec);
218
219         vector<string> args = {
220                 "kdm_cli",
221                 "--verbose",
222                 "--valid-from", "now",
223                 "--valid-duration", "2 weeks",
224                 "-c", "Dean's Screens",
225                 "-S", "Screen 2",
226                 "-o", "build/test",
227                 "test/data/dkdm.xml"
228         };
229
230         vector<string> output;
231         auto error = run(args, output);
232         BOOST_CHECK(!error);
233
234         BOOST_REQUIRE_EQUAL(output.size(), 2U);
235         BOOST_CHECK(boost::algorithm::starts_with(output[0], "Making KDMs valid from"));
236         BOOST_CHECK_EQUAL(output[1], "Wrote 1 KDM files to build/test");
237
238         BOOST_CHECK(boost::filesystem::exists(kdm_filename));
239 }
240
241
242 BOOST_AUTO_TEST_CASE(kdm_cli_specify_cinemas_file)
243 {
244         ConfigRestorer cr;
245
246         setup_test_config();
247
248         vector<string> args = {
249                 "kdm_cli",
250                 "--cinemas-file",
251                 "test/data/cinemas.xml",
252                 "list-cinemas"
253         };
254
255         vector<string> output;
256         auto const error = run(args, output);
257         BOOST_CHECK(!error);
258
259         BOOST_REQUIRE_EQUAL(output.size(), 3U);
260         BOOST_CHECK_EQUAL(output[0], "stinking dump ()");
261         BOOST_CHECK_EQUAL(output[1], "classy joint ()");
262         BOOST_CHECK_EQUAL(output[2], "Great ()");
263 }
264
265
266 BOOST_AUTO_TEST_CASE(kdm_cli_specify_cert)
267 {
268         boost::filesystem::path kdm_filename = "build/test/KDM_KDMCLI__.xml";
269
270         boost::system::error_code ec;
271         boost::filesystem::remove(kdm_filename, ec);
272
273         auto film = new_test_film2("kdm_cli_specify_cert", content_factory("test/data/flat_red.png"));
274         film->set_encrypted(true);
275         film->set_name("KDMCLI");
276         film->set_use_isdcf_name(false);
277         make_and_verify_dcp(film);
278
279         vector<string> args = {
280                 "kdm_cli",
281                 "--valid-from", "2024-01-01 10:10:10",
282                 "--valid-duration", "2 weeks",
283                 "-C", "test/data/cert.pem",
284                 "-o", "build/test",
285                 "create",
286                 "build/test/kdm_cli_specify_cert"
287         };
288
289         vector<string> output;
290         auto error = run(args, output);
291         BOOST_CHECK(!error);
292
293         BOOST_CHECK(output.empty());
294         BOOST_CHECK(boost::filesystem::exists(kdm_filename));
295 }
296
297
298 BOOST_AUTO_TEST_CASE(kdm_cli_time)
299 {
300         ConfigRestorer cr;
301
302         setup_test_config();
303
304         boost::filesystem::path kdm_filename = "build/test/KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV_Deans_Screens_Screen_2.xml";
305
306         boost::system::error_code ec;
307         boost::filesystem::remove(kdm_filename, ec);
308
309         dcp::LocalTime now;
310         now.add_days(2);
311
312         vector<string> args = {
313                 "kdm_cli",
314                 "--verbose",
315                 "--valid-from", now.as_string(),
316                 "--valid-duration", "2 weeks",
317                 "-c", "Dean's Screens",
318                 "-S", "Screen 2",
319                 "-o", "build/test",
320                 "test/data/dkdm.xml"
321         };
322
323         vector<string> output;
324         auto error = run(args, output);
325         BOOST_CHECK(!error);
326
327         BOOST_REQUIRE_EQUAL(output.size(), 2U);
328         BOOST_CHECK(boost::algorithm::starts_with(output[0], "Making KDMs valid from"));
329         BOOST_CHECK_EQUAL(output[1], "Wrote 1 KDM files to build/test");
330
331         BOOST_CHECK(boost::filesystem::exists(kdm_filename));
332 }
333