2 Copyright (C) 2022 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
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.
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.
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/>.
22 #include "lib/cinema.h"
23 #include "lib/config.h"
24 #include "lib/content_factory.h"
25 #include "lib/cross.h"
26 #include "lib/dkdm_wrapper.h"
28 #include "lib/kdm_cli.h"
29 #include "lib/screen.h"
30 #include "lib/trusted_device.h"
32 #include <boost/algorithm/string/predicate.hpp>
33 #include <boost/filesystem.hpp>
34 #include <boost/test/unit_test.hpp>
38 using std::dynamic_pointer_cast;
41 using boost::optional;
45 run(vector<string> const& args, vector<string>& output, bool dump_errors = true)
47 std::vector<char*> argv(args.size());
48 for (auto i = 0U; i < args.size(); ++i) {
49 argv[i] = const_cast<char*>(args[i].c_str());
52 auto error = kdm_cli(args.size(), argv.data(), [&output](string s) { output.push_back(s); });
53 if (error && dump_errors) {
54 std::cout << *error << "\n";
61 BOOST_AUTO_TEST_CASE (kdm_cli_test_certificate)
63 vector<string> args = {
66 "--valid-from", "now",
67 "--valid-duration", "2 weeks",
68 "--projector-certificate", "test/data/cert.pem",
69 "-S", "my great screen",
74 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";
75 boost::system::error_code ec;
76 boost::filesystem::remove(kdm_filename, ec);
78 vector<string> output;
79 auto error = run(args, output);
82 BOOST_CHECK(boost::filesystem::exists(kdm_filename));
86 BOOST_AUTO_TEST_CASE(kdm_cli_specify_decryption_key_test)
88 using boost::filesystem::path;
92 path const dir = "build/test/kdm_cli_specify_decryption_key_test";
94 boost::system::error_code ec;
95 boost::filesystem::remove_all(dir, ec);
96 boost::filesystem::create_directories(dir);
98 dcp::CertificateChain chain(openssl_path(), 365);
99 dcp::write_string_to_file(chain.leaf().certificate(true), dir / "cert.pem");
100 dcp::write_string_to_file(*chain.key(), dir / "key.pem");
102 vector<string> make_args = {
104 "--valid-from", "now",
105 "--valid-duration", "2 weeks",
106 "--projector-certificate", path(dir / "cert.pem").string(),
112 vector<string> output;
113 auto error = run(make_args, output);
116 vector<string> bad_args = {
118 "--valid-from", "now",
119 "--valid-duration", "2 weeks",
120 "--projector-certificate", path(dir / "cert.pem").string(),
123 path(dir / "KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV__base.xml").string()
126 /* This should fail because we're using the wrong decryption certificate */
128 error = run(bad_args, output, false);
129 BOOST_REQUIRE(error);
130 BOOST_CHECK_MESSAGE(error->find("Could not decrypt KDM") != string::npos, "Error was " << *error);
132 vector<string> good_args = {
134 "--valid-from", "now",
135 "--valid-duration", "2 weeks",
136 "--projector-certificate", path(dir / "cert.pem").string(),
137 "--decryption-key", path(dir / "key.pem").string(),
140 path(dir / "KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV__base.xml").string()
143 /* This should succeed */
145 error = run(good_args, output);
154 auto config = Config::instance();
155 auto const cert = dcp::Certificate(dcp::file_to_string("test/data/cert.pem"));
157 auto cinema_a = std::make_shared<Cinema>("Dean's Screens", vector<string>(), "");
158 cinema_a->add_screen(std::make_shared<dcpomatic::Screen>("Screen 1", "", cert, boost::none, std::vector<TrustedDevice>()));
159 cinema_a->add_screen(std::make_shared<dcpomatic::Screen>("Screen 2", "", cert, boost::none, std::vector<TrustedDevice>()));
160 cinema_a->add_screen(std::make_shared<dcpomatic::Screen>("Screen 3", "", cert, boost::none, std::vector<TrustedDevice>()));
161 config->add_cinema(cinema_a);
163 auto cinema_b = std::make_shared<Cinema>("Floyd's Celluloid", vector<string>(), "");
164 cinema_b->add_screen(std::make_shared<dcpomatic::Screen>("Foo", "", cert, boost::none, std::vector<TrustedDevice>()));
165 cinema_b->add_screen(std::make_shared<dcpomatic::Screen>("Bar", "", cert, boost::none, std::vector<TrustedDevice>()));
166 config->add_cinema(cinema_b);
170 BOOST_AUTO_TEST_CASE(kdm_cli_select_cinema)
176 vector<boost::filesystem::path> kdm_filenames = {
177 "build/test/KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV_Floyds_Celluloid_Foo.xml",
178 "build/test/KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV_Floyds_Celluloid_Bar.xml"
181 for (auto path: kdm_filenames) {
182 boost::system::error_code ec;
183 boost::filesystem::remove(path, ec);
186 vector<string> args = {
189 "--valid-from", "now",
190 "--valid-duration", "2 weeks",
191 "-c", "Floyd's Celluloid",
196 vector<string> output;
197 auto error = run(args, output);
200 BOOST_REQUIRE_EQUAL(output.size(), 2U);
201 BOOST_CHECK(boost::algorithm::starts_with(output[0], "Making KDMs valid from"));
202 BOOST_CHECK_EQUAL(output[1], "Wrote 2 KDM files to build/test");
204 for (auto path: kdm_filenames) {
205 BOOST_CHECK(boost::filesystem::exists(path));
210 BOOST_AUTO_TEST_CASE(kdm_cli_select_screen)
216 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";
218 boost::system::error_code ec;
219 boost::filesystem::remove(kdm_filename, ec);
221 vector<string> args = {
224 "--valid-from", "now",
225 "--valid-duration", "2 weeks",
226 "-c", "Dean's Screens",
232 vector<string> output;
233 auto error = run(args, output);
236 BOOST_REQUIRE_EQUAL(output.size(), 2U);
237 BOOST_CHECK(boost::algorithm::starts_with(output[0], "Making KDMs valid from"));
238 BOOST_CHECK_EQUAL(output[1], "Wrote 1 KDM files to build/test");
240 BOOST_CHECK(boost::filesystem::exists(kdm_filename));
244 BOOST_AUTO_TEST_CASE(kdm_cli_specify_cinemas_file)
250 vector<string> args = {
253 "test/data/cinemas.xml",
257 vector<string> output;
258 auto const error = run(args, output);
261 BOOST_REQUIRE_EQUAL(output.size(), 3U);
262 BOOST_CHECK_EQUAL(output[0], "stinking dump ()");
263 BOOST_CHECK_EQUAL(output[1], "classy joint ()");
264 BOOST_CHECK_EQUAL(output[2], "Great ()");
268 BOOST_AUTO_TEST_CASE(kdm_cli_specify_cert)
270 boost::filesystem::path kdm_filename = "build/test/KDM_KDMCLI__.xml";
272 boost::system::error_code ec;
273 boost::filesystem::remove(kdm_filename, ec);
275 auto film = new_test_film2("kdm_cli_specify_cert", content_factory("test/data/flat_red.png"));
276 film->set_encrypted(true);
277 film->set_name("KDMCLI");
278 film->set_use_isdcf_name(false);
279 make_and_verify_dcp(film);
281 vector<string> args = {
283 "--valid-from", "2024-01-01 10:10:10",
284 "--valid-duration", "2 weeks",
285 "-C", "test/data/cert.pem",
288 "build/test/kdm_cli_specify_cert"
291 vector<string> output;
292 auto error = run(args, output);
295 BOOST_CHECK(output.empty());
296 BOOST_CHECK(boost::filesystem::exists(kdm_filename));
300 BOOST_AUTO_TEST_CASE(kdm_cli_time)
306 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";
308 boost::system::error_code ec;
309 boost::filesystem::remove(kdm_filename, ec);
314 vector<string> args = {
317 "--valid-from", now.as_string(),
318 "--valid-duration", "2 weeks",
319 "-c", "Dean's Screens",
325 vector<string> output;
326 auto error = run(args, output);
329 BOOST_REQUIRE_EQUAL(output.size(), 2U);
330 BOOST_CHECK(boost::algorithm::starts_with(output[0], "Making KDMs valid from"));
331 BOOST_CHECK_EQUAL(output[1], "Wrote 1 KDM files to build/test");
333 BOOST_CHECK(boost::filesystem::exists(kdm_filename));
337 BOOST_AUTO_TEST_CASE(kdm_cli_add_dkdm)
343 BOOST_CHECK_EQUAL(Config::instance()->dkdms()->children().size(), 0U);
345 vector<string> args = {
351 vector<string> output;
352 auto error = run(args, output);
355 auto dkdms = Config::instance()->dkdms()->children();
356 BOOST_CHECK_EQUAL(dkdms.size(), 1U);
357 auto dkdm = dynamic_pointer_cast<DKDM>(dkdms.front());
359 BOOST_CHECK_EQUAL(dkdm->dkdm().as_xml(), dcp::file_to_string("test/data/dkdm.xml"));