Hopefully fix crash when the same frame is encoded twice.
[dcpomatic.git] / test / config_test.cc
1 /*
2     Copyright (C) 2018-2021 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 "test.h"
25 #include <boost/test/unit_test.hpp>
26 #include <fstream>
27
28
29 using std::list;
30 using std::ofstream;
31 using std::make_shared;
32 using std::string;
33 using boost::optional;
34
35
36 static string
37 rewrite_bad_config (string filename, string extra_line)
38 {
39         using namespace boost::filesystem;
40
41         auto base = path("build/test/bad_config/2.18");
42         auto file = base / filename;
43
44         boost::system::error_code ec;
45         remove (file, ec);
46
47         boost::filesystem::create_directories (base);
48         std::ofstream f (file.string().c_str());
49         f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
50           << "<Config>\n"
51           << "<Foo></Foo>\n"
52           << extra_line << "\n"
53           << "</Config>\n";
54         f.close ();
55
56         return dcp::file_to_string (file);
57 }
58
59
60 BOOST_AUTO_TEST_CASE (config_backup_test)
61 {
62         ConfigRestorer cr;
63
64         Config::override_path = "build/test/bad_config";
65         Config::drop();
66         boost::filesystem::remove_all ("build/test/bad_config");
67
68         /* Write an invalid config file to config.xml */
69         auto const first_write_xml = rewrite_bad_config("config.xml", "first write");
70
71         /* Load the config; this should fail, causing the bad config to be copied to config.xml.1
72          * and a new config.xml created in its place.
73          */
74         Config::instance();
75
76         boost::filesystem::path const prefix = "build/test/bad_config/2.18";
77
78         BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.1"));
79         BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.1") == first_write_xml);
80         BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.2"));
81         BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.3"));
82         BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.4"));
83
84         Config::drop();
85         auto const second_write_xml = rewrite_bad_config("config.xml", "second write");
86         Config::instance();
87
88         BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.1"));
89         BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.1") == first_write_xml);
90         BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.2"));
91         BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.2") == second_write_xml);
92         BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.3"));
93         BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.4"));
94
95         Config::drop();
96         auto const third_write_xml = rewrite_bad_config("config.xml", "third write");
97         Config::instance();
98
99         BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.1"));
100         BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.1") == first_write_xml);
101         BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.2"));
102         BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.2") == second_write_xml);
103         BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.3"));
104         BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.3") == third_write_xml);
105         BOOST_CHECK(!boost::filesystem::exists(prefix / "config.xml.4"));
106
107         Config::drop();
108         auto const fourth_write_xml = rewrite_bad_config("config.xml", "fourth write");
109         Config::instance();
110
111         BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.1"));
112         BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.1") == first_write_xml);
113         BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.2"));
114         BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.2") == second_write_xml);
115         BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.3"));
116         BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.3") == third_write_xml);
117         BOOST_CHECK(boost::filesystem::exists(prefix / "config.xml.4"));
118         BOOST_CHECK(dcp::file_to_string(prefix / "config.xml.4") == fourth_write_xml);
119 }
120
121
122 BOOST_AUTO_TEST_CASE (config_backup_with_link_test)
123 {
124         using namespace boost::filesystem;
125
126         ConfigRestorer cr;
127
128         auto base = path("build/test/bad_config");
129         auto version = base / "2.18";
130
131         Config::override_path = base;
132         Config::drop();
133
134         boost::filesystem::remove_all (base);
135
136         boost::filesystem::create_directories (version);
137         std::ofstream f (path(version / "config.xml").string().c_str());
138         f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
139           << "<Config>\n"
140           << "<Link>" << path(version / "actual.xml").string() << "</Link>\n"
141           << "</Config>\n";
142         f.close ();
143
144         Config::drop ();
145         /* Cause actual.xml to be backed up */
146         rewrite_bad_config ("actual.xml", "first write");
147         Config::instance ();
148
149         /* Make sure actual.xml was backed up to the right place */
150         BOOST_CHECK (boost::filesystem::exists(version / "actual.xml.1"));
151 }
152
153
154 BOOST_AUTO_TEST_CASE (config_write_utf8_test)
155 {
156         ConfigRestorer cr;
157
158         boost::filesystem::remove_all ("build/test/config.xml");
159         boost::filesystem::copy_file ("test/data/utf8_config.xml", "build/test/config.xml");
160         Config::override_path = "build/test";
161         Config::drop ();
162         Config::instance()->write();
163
164         check_text_file ("test/data/utf8_config.xml", "build/test/config.xml");
165 }
166
167
168 /* 2.14 -> 2.18 */
169 BOOST_AUTO_TEST_CASE (config_upgrade_test1)
170 {
171         ConfigRestorer cr;
172
173         boost::filesystem::path dir = "build/test/config_upgrade_test";
174         Config::override_path = dir;
175         Config::drop ();
176         boost::filesystem::remove_all (dir);
177         boost::filesystem::create_directories (dir);
178
179         boost::filesystem::copy_file ("test/data/2.14.config.xml", dir / "config.xml");
180         boost::filesystem::copy_file ("test/data/2.14.cinemas.xml", dir / "cinemas.xml");
181         Config::instance();
182         try {
183                 /* This will fail to write cinemas.xml since the link is to a non-existent directory */
184                 Config::instance()->write();
185         } catch (...) {}
186
187         check_xml (dir / "config.xml", "test/data/2.14.config.xml", {});
188         check_xml (dir / "cinemas.xml", "test/data/2.14.cinemas.xml", {});
189 #ifdef DCPOMATIC_WINDOWS
190         /* This file has the windows path for dkdm_recipients.xml (with backslashes) */
191         check_xml(dir / "2.18" / "config.xml", "test/data/2.18.config.windows.xml", {});
192 #else
193         check_xml(dir / "2.18" / "config.xml", "test/data/2.18.config.xml", {});
194 #endif
195         /* cinemas.xml is not copied into 2.18 as its format has not changed */
196         BOOST_REQUIRE (!boost::filesystem::exists(dir / "2.18" / "cinemas.xml"));
197 }
198
199
200 /* 2.16 -> 2.18 */
201 BOOST_AUTO_TEST_CASE (config_upgrade_test2)
202 {
203         ConfigRestorer cr;
204
205         boost::filesystem::path dir = "build/test/config_upgrade_test";
206         Config::override_path = dir;
207         Config::drop ();
208         boost::filesystem::remove_all (dir);
209         boost::filesystem::create_directories (dir);
210
211 #ifdef DCPOMATIC_WINDOWS
212         boost::filesystem::copy_file("test/data/2.16.config.windows.xml", dir / "config.xml");
213 #else
214         boost::filesystem::copy_file("test/data/2.16.config.xml", dir / "config.xml");
215 #endif
216         boost::filesystem::copy_file("test/data/2.14.cinemas.xml", dir / "cinemas.xml");
217         Config::instance();
218         try {
219                 /* This will fail to write cinemas.xml since the link is to a non-existent directory */
220                 Config::instance()->write();
221         } catch (...) {}
222
223         check_xml(dir / "cinemas.xml", "test/data/2.14.cinemas.xml", {});
224 #ifdef DCPOMATIC_WINDOWS
225         /* This file has the windows path for dkdm_recipients.xml (with backslashes) */
226         check_xml(dir / "2.18" / "config.xml", "test/data/2.18.config.windows.xml", {});
227         check_xml(dir / "config.xml", "test/data/2.16.config.windows.xml", {});
228 #else
229         check_xml(dir / "2.18" / "config.xml", "test/data/2.18.config.xml", {});
230         check_xml(dir / "config.xml", "test/data/2.16.config.xml", {});
231 #endif
232         /* cinemas.xml is not copied into 2.18 as its format has not changed */
233         BOOST_REQUIRE (!boost::filesystem::exists(dir / "2.18" / "cinemas.xml"));
234 }
235
236
237 BOOST_AUTO_TEST_CASE (config_keep_cinemas_if_making_new_config)
238 {
239         ConfigRestorer cr;
240
241         boost::filesystem::path dir = "build/test/config_keep_cinemas_if_making_new_config";
242         Config::override_path = dir;
243         Config::drop ();
244         boost::filesystem::remove_all (dir);
245         boost::filesystem::create_directories (dir);
246
247         Config::instance()->write();
248
249         Config::instance()->add_cinema(make_shared<Cinema>("My Great Cinema", list<string>(), "", 0, 0));
250         Config::instance()->write();
251
252         boost::filesystem::copy_file (dir / "cinemas.xml", dir / "backup_for_test.xml");
253
254         Config::drop ();
255         boost::filesystem::remove (dir / "2.18" / "config.xml");
256         Config::instance();
257
258         check_text_file(dir / "backup_for_test.xml", dir / "cinemas.xml");
259 }
260
261
262 BOOST_AUTO_TEST_CASE(keep_config_if_cinemas_fail_to_load)
263 {
264         ConfigRestorer cr;
265
266         /* Make a new config */
267         boost::filesystem::path dir = "build/test/keep_config_if_cinemas_fail_to_load";
268         Config::override_path = dir;
269         Config::drop();
270         boost::filesystem::remove_all(dir);
271         boost::filesystem::create_directories(dir);
272         Config::instance()->write();
273
274         auto const cinemas = dir / "cinemas.xml";
275
276         /* Back things up */
277         boost::filesystem::copy_file(dir / "2.18" / "config.xml", dir / "config_backup_for_test.xml");
278         boost::filesystem::copy_file(cinemas, dir / "cinemas_backup_for_test.xml");
279
280         /* Corrupt the cinemas */
281         Config::drop();
282         std::ofstream corrupt(cinemas.string().c_str());
283         corrupt << "foo\n";
284         corrupt.close();
285         Config::instance();
286
287         /* We should have a new cinemas.xml and the old config.xml */
288         check_text_file(dir / "2.18" / "config.xml", dir / "config_backup_for_test.xml");
289         check_text_file(cinemas, dir / "cinemas_backup_for_test.xml");
290 }
291