2 Copyright (C) 2018-2021 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/cinema_list.h"
24 #include "lib/config.h"
25 #include "lib/dkdm_recipient.h"
26 #include "lib/dkdm_recipient_list.h"
27 #include "lib/unzipper.h"
28 #include "lib/zipper.h"
30 #include <boost/test/unit_test.hpp>
34 using std::make_shared;
38 using boost::optional;
42 rewrite_bad_config (string filename, string extra_line)
44 using namespace boost::filesystem;
46 auto base = path("build/test/bad_config/2.18");
47 auto file = base / filename;
49 boost::system::error_code ec;
52 boost::filesystem::create_directories (base);
53 std::ofstream f (file.string().c_str());
54 f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
61 return dcp::file_to_string (file);
65 BOOST_AUTO_TEST_CASE (config_backup_test)
69 Config::override_path = "build/test/bad_config";
71 boost::filesystem::remove_all ("build/test/bad_config");
73 /* Write an invalid config file to config.xml */
74 auto const first_write_xml = rewrite_bad_config("config.xml", "first write");
76 /* Load the config; this should fail, causing the bad config to be copied to config.xml.1
77 * and a new config.xml created in its place.
81 boost::filesystem::path const prefix = "build/test/bad_config/2.18";
83 BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.1"));
84 BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.1") == first_write_xml);
85 BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.2"));
86 BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.3"));
87 BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.4"));
90 auto const second_write_xml = rewrite_bad_config("config.xml", "second write");
93 BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.1"));
94 BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.1") == first_write_xml);
95 BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.2"));
96 BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.2") == second_write_xml);
97 BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.3"));
98 BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.4"));
101 auto const third_write_xml = rewrite_bad_config("config.xml", "third write");
104 BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.1"));
105 BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.1") == first_write_xml);
106 BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.2"));
107 BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.2") == second_write_xml);
108 BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.3"));
109 BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.3") == third_write_xml);
110 BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.4"));
113 auto const fourth_write_xml = rewrite_bad_config("config.xml", "fourth write");
116 BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.1"));
117 BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.1") == first_write_xml);
118 BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.2"));
119 BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.2") == second_write_xml);
120 BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.3"));
121 BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.3") == third_write_xml);
122 BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.4"));
123 BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.4") == fourth_write_xml);
127 BOOST_AUTO_TEST_CASE (config_backup_with_link_test)
129 using namespace boost::filesystem;
133 auto base = path("build/test/bad_config");
134 auto version = base / "2.18";
136 Config::override_path = base;
139 boost::filesystem::remove_all (base);
141 boost::filesystem::create_directories (version);
142 std::ofstream f (path(version / "config.xml").string().c_str());
143 f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
145 << "<Link>" << path(version / "actual.xml").string() << "</Link>\n"
150 /* Cause actual.xml to be backed up */
151 rewrite_bad_config ("actual.xml", "first write");
154 /* Make sure actual.xml was backed up to the right place */
155 BOOST_CHECK (boost::filesystem::exists(version / "actual.xml.1"));
159 BOOST_AUTO_TEST_CASE (config_write_utf8_test)
163 boost::filesystem::remove_all ("build/test/config.xml");
164 boost::filesystem::copy_file ("test/data/utf8_config.xml", "build/test/config.xml");
165 Config::override_path = "build/test";
167 Config::instance()->write();
169 check_text_file ("test/data/utf8_config.xml", "build/test/config.xml");
174 BOOST_AUTO_TEST_CASE (config_upgrade_test1)
178 boost::filesystem::path dir = "build/test/config_upgrade_test";
179 Config::override_path = dir;
181 boost::filesystem::remove_all (dir);
182 boost::filesystem::create_directories (dir);
184 boost::filesystem::copy_file ("test/data/2.14.config.xml", dir / "config.xml");
185 boost::filesystem::copy_file ("test/data/2.14.cinemas.xml", dir / "cinemas.xml");
187 /* This will fail to read cinemas.xml since the link is to a non-existent directory */
191 Config::instance()->write();
193 check_xml (dir / "config.xml", "test/data/2.14.config.xml", {});
194 check_xml (dir / "cinemas.xml", "test/data/2.14.cinemas.xml", {});
195 #ifdef DCPOMATIC_WINDOWS
196 /* This file has the windows path for dkdm_recipients.xml (with backslashes) */
197 check_xml(dir / "2.18" / "config.xml", "test/data/2.18.config.windows.sqlite.xml", {});
199 check_xml(dir / "2.18" / "config.xml", "test/data/2.18.config.sqlite.xml", {});
201 /* cinemas.xml is not copied into 2.18 as its format has not changed */
202 BOOST_REQUIRE (!boost::filesystem::exists(dir / "2.18" / "cinemas.xml"));
207 BOOST_AUTO_TEST_CASE (config_upgrade_test2)
211 boost::filesystem::path dir = "build/test/config_upgrade_test";
212 Config::override_path = dir;
214 boost::filesystem::remove_all (dir);
215 boost::filesystem::create_directories (dir);
217 #ifdef DCPOMATIC_WINDOWS
218 boost::filesystem::copy_file("test/data/2.16.config.windows.xml", dir / "config.xml");
220 boost::filesystem::copy_file("test/data/2.16.config.xml", dir / "config.xml");
222 boost::filesystem::copy_file("test/data/2.14.cinemas.xml", dir / "cinemas.xml");
224 /* This will fail to read cinemas.xml since the link is to a non-existent directory */
228 Config::instance()->write();
230 check_xml(dir / "cinemas.xml", "test/data/2.14.cinemas.xml", {});
231 #ifdef DCPOMATIC_WINDOWS
232 /* This file has the windows path for dkdm_recipients.xml (with backslashes) */
233 check_xml(dir / "2.18" / "config.xml", "test/data/2.18.config.windows.xml", {});
234 check_xml(dir / "config.xml", "test/data/2.16.config.windows.xml", {});
236 check_xml(dir / "2.18" / "config.xml", "test/data/2.18.config.xml", {});
237 check_xml(dir / "config.xml", "test/data/2.16.config.xml", {});
239 /* cinemas.xml is not copied into 2.18 as its format has not changed */
240 BOOST_REQUIRE (!boost::filesystem::exists(dir / "2.18" / "cinemas.xml"));
244 BOOST_AUTO_TEST_CASE (config_keep_cinemas_if_making_new_config)
248 boost::filesystem::path dir = "build/test/config_keep_cinemas_if_making_new_config";
249 Config::override_path = dir;
251 boost::filesystem::remove_all (dir);
252 boost::filesystem::create_directories (dir);
254 Config::instance()->write();
257 cinemas.add_cinema({"My Great Cinema", {}, "", dcp::UTCOffset()});
259 boost::filesystem::copy_file(dir / "cinemas.sqlite3", dir / "backup_for_test.sqlite3");
262 boost::filesystem::remove (dir / "2.18" / "config.xml");
265 check_file(dir / "backup_for_test.sqlite3", dir / "cinemas.sqlite3");
269 BOOST_AUTO_TEST_CASE(keep_config_if_cinemas_fail_to_load)
273 /* Make a new config */
274 boost::filesystem::path dir = "build/test/keep_config_if_cinemas_fail_to_load";
275 Config::override_path = dir;
277 boost::filesystem::remove_all(dir);
278 boost::filesystem::create_directories(dir);
279 Config::instance()->write();
281 CinemaList cinema_list;
282 cinema_list.add_cinema(Cinema("Foo", {}, "Bar", dcp::UTCOffset()));
284 auto const cinemas = dir / "cinemas.sqlite3";
287 boost::filesystem::copy_file(dir / "2.18" / "config.xml", dir / "config_backup_for_test.xml");
288 boost::filesystem::copy_file(cinemas, dir / "cinemas_backup_for_test.sqlite3");
290 /* Corrupt the cinemas */
292 std::ofstream corrupt(cinemas.string().c_str());
297 /* We should have the old config.xml */
298 check_text_file(dir / "2.18" / "config.xml", dir / "config_backup_for_test.xml");
302 BOOST_AUTO_TEST_CASE(read_cinemas_xml_and_write_sqlite)
306 /* Set up a config with an XML cinemas file */
307 boost::filesystem::path dir = "build/test/read_cinemas_xml_and_write_sqlite";
308 boost::filesystem::remove_all(dir);
309 boost::filesystem::create_directories(dir);
310 boost::filesystem::create_directories(dir / "2.18");
312 boost::filesystem::copy_file("test/data/cinemas.xml", dir / "cinemas.xml");
313 boost::filesystem::copy_file("test/data/2.18.config.xml", dir / "2.18" / "config.xml");
315 Editor editor(dir / "2.18" / "config.xml");
317 "/home/realldoesnt/exist/this/path/is/nonsense.xml",
318 boost::filesystem::canonical(dir / "cinemas.xml").string()
322 Config::override_path = dir;
325 /* This should make a sqlite3 file containing the recipients from cinemas.xml */
329 CinemaList test(dir / "cinemas.sqlite3");
331 /* The detailed creation of sqlite3 from XML is tested in cinema_list_test.cc */
332 auto cinemas = test.cinemas();
333 BOOST_REQUIRE_EQUAL(cinemas.size(), 3U);
334 BOOST_CHECK_EQUAL(cinemas[0].second.name, "Great");
335 BOOST_CHECK_EQUAL(cinemas[1].second.name, "classy joint");
336 BOOST_CHECK_EQUAL(cinemas[2].second.name, "stinking dump");
338 /* Add another recipient to the sqlite */
339 test.add_cinema({"The ol' 1-seater", {}, "Quiet but lonely", dcp::UTCOffset()});
342 /* Reload the config; the old XML should not clobber the new sqlite3 */
347 CinemaList test(dir / "cinemas.sqlite3");
349 auto cinemas = test.cinemas();
350 BOOST_REQUIRE_EQUAL(cinemas.size(), 4U);
351 BOOST_CHECK_EQUAL(cinemas[0].second.name, "Great");
352 BOOST_CHECK_EQUAL(cinemas[1].second.name, "The ol' 1-seater");
353 BOOST_CHECK_EQUAL(cinemas[2].second.name, "classy joint");
354 BOOST_CHECK_EQUAL(cinemas[3].second.name, "stinking dump");
359 BOOST_AUTO_TEST_CASE(read_dkdm_recipients_xml_and_write_sqlite)
363 /* Set up a config with an XML cinemas file */
364 boost::filesystem::path dir = "build/test/read_dkdm_recipients_xml_and_write_sqlite";
365 boost::filesystem::remove_all(dir);
366 boost::filesystem::create_directories(dir);
367 boost::filesystem::create_directories(dir / "2.18");
369 boost::filesystem::copy_file("test/data/dkdm_recipients.xml", dir / "dkdm_recipients.xml");
370 boost::filesystem::copy_file("test/data/2.18.config.xml", dir / "2.18" / "config.xml");
372 Editor editor(dir / "2.18" / "config.xml");
374 "build/test/config_upgrade_test/dkdm_recipients.xml",
375 boost::filesystem::canonical(dir / "dkdm_recipients.xml").string()
379 Config::override_path = dir;
382 /* This should make a sqlite3 file containing the recipients from dkdm_recipients.xml */
386 DKDMRecipientList test(dir / "dkdm_recipients.sqlite3");
388 /* The detailed creation of sqlite3 from XML is tested in dkdm_recipient_list_test.cc */
389 auto recipients = test.dkdm_recipients();
390 BOOST_REQUIRE_EQUAL(recipients.size(), 2U);
391 BOOST_CHECK_EQUAL(recipients[0].second.name, "Bob's Epics");
392 BOOST_CHECK_EQUAL(recipients[1].second.name, "Sharon's Shorts");
394 /* Add another recipient to the sqlite */
395 test.add_dkdm_recipient({"Carl's Classics", "Oldies but goodies", {}, {}});
398 /* Reload the config; the old XML should not clobber the new sqlite3 */
403 DKDMRecipientList test(dir / "dkdm_recipients.sqlite3");
405 auto recipients = test.dkdm_recipients();
406 BOOST_REQUIRE_EQUAL(recipients.size(), 3U);
407 BOOST_CHECK_EQUAL(recipients[0].second.name, "Bob's Epics");
408 BOOST_CHECK_EQUAL(recipients[1].second.name, "Carl's Classics");
409 BOOST_CHECK_EQUAL(recipients[2].second.name, "Sharon's Shorts");
414 BOOST_AUTO_TEST_CASE(save_config_as_zip_test)
419 cinemas.add_cinema({"My Great Cinema", {}, "", dcp::UTCOffset()});
420 DKDMRecipientList recipients;
421 recipients.add_dkdm_recipient({"Carl's Classics", "Oldies but goodies", {}, {}});
423 boost::filesystem::path const zip = "build/test/save.zip";
424 boost::system::error_code ec;
425 boost::filesystem::remove(zip, ec);
426 save_all_config_as_zip(zip);
427 Unzipper unzipper(zip);
429 BOOST_CHECK(unzipper.contains("config.xml"));
430 BOOST_CHECK(unzipper.contains("cinemas.sqlite3"));
431 BOOST_CHECK(unzipper.contains("dkdm_recipients.sqlite3"));
435 /** Load a config ZIP file, which contains an XML cinemas file, and ask to overwrite
436 * the existing cinemas file that we had.
438 BOOST_AUTO_TEST_CASE(load_config_from_zip_with_only_xml_current)
442 auto cinemas_file = Config::instance()->cinemas_file();
444 boost::filesystem::path const zip = "build/test/load.zip";
445 boost::system::error_code ec;
446 boost::filesystem::remove(zip, ec);
451 boost::algorithm::replace_all_copy(
452 dcp::file_to_string("test/data/2.18.config.xml"),
453 "/home/realldoesnt/exist/this/path/is/nonsense.xml",
458 zipper.add("cinemas.xml", dcp::file_to_string("test/data/cinemas.xml"));
461 Config::instance()->load_from_zip(zip, Config::CinemasAction::WRITE_TO_CURRENT_PATH);
463 CinemaList cinema_list(cinemas_file);
464 auto cinemas = cinema_list.cinemas();
465 BOOST_REQUIRE_EQUAL(cinemas.size(), 3U);
466 BOOST_CHECK_EQUAL(cinemas[0].second.name, "Great");
467 BOOST_CHECK_EQUAL(cinemas[1].second.name, "classy joint");
468 BOOST_CHECK_EQUAL(cinemas[2].second.name, "stinking dump");
472 /** Load a config ZIP file, which contains an XML cinemas file, and ask to write it to
473 * the location specified by the zipped config.xml.
475 BOOST_AUTO_TEST_CASE(load_config_from_zip_with_only_xml_zip)
479 boost::filesystem::path const zip = "build/test/load.zip";
480 boost::system::error_code ec;
481 boost::filesystem::remove(zip, ec);
486 boost::algorithm::replace_all_copy(
487 dcp::file_to_string("test/data/2.18.config.xml"),
488 "/home/realldoesnt/exist/this/path/is/nonsense.xml",
489 "build/test/hide/it/here/cinemas.sqlite3"
493 zipper.add("cinemas.xml", dcp::file_to_string("test/data/cinemas.xml"));
496 Config::instance()->load_from_zip(zip, Config::CinemasAction::WRITE_TO_PATH_IN_ZIPPED_CONFIG);
498 CinemaList cinema_list("build/test/hide/it/here/cinemas.sqlite3");
499 auto cinemas = cinema_list.cinemas();
500 BOOST_REQUIRE_EQUAL(cinemas.size(), 3U);
501 BOOST_CHECK_EQUAL(cinemas[0].second.name, "Great");
502 BOOST_CHECK_EQUAL(cinemas[1].second.name, "classy joint");
503 BOOST_CHECK_EQUAL(cinemas[2].second.name, "stinking dump");
507 /** Load a config ZIP file, which contains an XML cinemas file, and ask to ignore it */
508 BOOST_AUTO_TEST_CASE(load_config_from_zip_with_only_xml_ignore)
512 boost::filesystem::path const zip = "build/test/load.zip";
513 boost::system::error_code ec;
514 boost::filesystem::remove(zip, ec);
519 boost::algorithm::replace_all_copy(
520 dcp::file_to_string("test/data/2.18.config.xml"),
521 "/home/realldoesnt/exist/this/path/is/nonsense.xml",
522 "build/test/hide/it/here/cinemas.sqlite3"
526 zipper.add("cinemas.xml", "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Cinemas/>");
529 Config::instance()->load_from_zip(zip, Config::CinemasAction::IGNORE);
531 CinemaList cinema_list("build/test/hide/it/here/cinemas.sqlite3");
532 auto cinemas = cinema_list.cinemas();
533 BOOST_CHECK(!cinemas.empty());