Small bits of tidying up.
[libdcp.git] / test / combine_test.cc
1 /*
2     Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp 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     libdcp 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 libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34
35 #include "combine.h"
36 #include "cpl.h"
37 #include "dcp.h"
38 #include "interop_subtitle_asset.h"
39 #include "reel_subtitle_asset.h"
40 #include "reel_mono_picture_asset.h"
41 #include "reel_sound_asset.h"
42 #include "test.h"
43 #include "types.h"
44 #include "verify.h"
45 #include "reel_markers_asset.h"
46 #include <boost/algorithm/string.hpp>
47 #include <boost/optional.hpp>
48 #include <boost/test/unit_test.hpp>
49 #include <iostream>
50
51
52 using std::list;
53 using std::string;
54 using std::make_shared;
55 using std::vector;
56 using std::shared_ptr;
57 using boost::optional;
58
59
60 static void
61 stage (string, optional<boost::filesystem::path>)
62 {
63 }
64
65
66 static void
67 progress (float)
68 {
69 }
70
71
72 static
73 void
74 dump_notes (vector<dcp::VerificationNote> const & notes)
75 {
76         for (auto i: notes) {
77                 std::cout << dcp::note_to_string(i) << "\n";
78         }
79 }
80
81
82 static
83 void
84 check_no_errors (boost::filesystem::path path)
85 {
86         vector<boost::filesystem::path> directories;
87         directories.push_back (path);
88         auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
89         vector<dcp::VerificationNote> filtered_notes;
90         std::copy_if (notes.begin(), notes.end(), std::back_inserter(filtered_notes), [](dcp::VerificationNote const& i) {
91                 return i.code() != dcp::VerificationNote::Code::INVALID_STANDARD && i.code() != dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION;
92         });
93         dump_notes (filtered_notes);
94         BOOST_CHECK (filtered_notes.empty());
95 }
96
97
98 template <class T>
99 shared_ptr<T>
100 pointer_to_id_in_vector (shared_ptr<T> needle, vector<shared_ptr<T> > haystack)
101 {
102         for (auto i: haystack) {
103                 if (i->id() == needle->id()) {
104                         return i;
105                 }
106         }
107
108         return shared_ptr<T>();
109 }
110
111
112 static
113 void
114 note_handler (dcp::NoteType, std::string)
115 {
116         // std::cout << "> " << n << "\n";
117 }
118
119
120 static
121 void
122 check_combined (vector<boost::filesystem::path> inputs, boost::filesystem::path output)
123 {
124         dcp::DCP output_dcp (output);
125         output_dcp.read ();
126
127         dcp::EqualityOptions options;
128         options.load_font_nodes_can_differ = true;
129
130         for (auto i: inputs) {
131                 dcp::DCP input_dcp (i);
132                 input_dcp.read ();
133
134                 BOOST_REQUIRE (input_dcp.cpls().size() == 1);
135                 auto input_cpl = input_dcp.cpls().front();
136
137                 auto output_cpl = pointer_to_id_in_vector (input_cpl, output_dcp.cpls());
138                 BOOST_REQUIRE (output_cpl);
139
140                 for (auto i: input_dcp.assets(true)) {
141                         auto o = pointer_to_id_in_vector(i, output_dcp.assets());
142                         BOOST_REQUIRE_MESSAGE (o, "Could not find " << i->id() << " in combined DCP.");
143                         BOOST_CHECK (i->equals(o, options, note_handler));
144                 }
145         }
146 }
147
148
149 BOOST_AUTO_TEST_CASE (combine_single_dcp_test)
150 {
151         using namespace boost::algorithm;
152         using namespace boost::filesystem;
153         boost::filesystem::path const out = "build/test/combine_single_dcp_test";
154
155         remove_all (out);
156         vector<path> inputs;
157         inputs.push_back ("test/ref/DCP/dcp_test1");
158         dcp::combine (
159                 inputs,
160                 out,
161                 dcp::String::compose("libdcp %1", dcp::version),
162                 dcp::String::compose("libdcp %1", dcp::version),
163                 dcp::LocalTime().as_string(),
164                 "A Test DCP"
165                 );
166
167         check_no_errors (out);
168         check_combined (inputs, out);
169 }
170
171
172 BOOST_AUTO_TEST_CASE (combine_two_dcps_with_same_asset_filenames_test)
173 {
174         using namespace boost::algorithm;
175         using namespace boost::filesystem;
176         boost::filesystem::path const out = "build/test/combine_two_dcps_with_same_asset_filenames_test";
177
178         shared_ptr<dcp::DCP> second = make_simple ("build/test/combine_input2");
179         second->write_xml (dcp::Standard::SMPTE);
180
181         remove_all (out);
182         vector<path> inputs;
183         inputs.push_back ("test/ref/DCP/dcp_test1");
184         inputs.push_back ("build/test/combine_input2");
185         dcp::combine (inputs, out);
186
187         check_no_errors (out);
188         check_combined (inputs, out);
189 }
190
191
192 BOOST_AUTO_TEST_CASE (combine_two_dcps_with_interop_subs_test)
193 {
194         using namespace boost::algorithm;
195         using namespace boost::filesystem;
196         boost::filesystem::path const out = "build/test/combine_two_dcps_with_interop_subs_test";
197
198         auto first = make_simple_with_interop_subs ("build/test/combine_input1");
199         first->write_xml (dcp::Standard::INTEROP);
200
201         auto second = make_simple_with_interop_subs ("build/test/combine_input2");
202         second->write_xml (dcp::Standard::INTEROP);
203
204         remove_all (out);
205         vector<path> inputs;
206         inputs.push_back ("build/test/combine_input1");
207         inputs.push_back ("build/test/combine_input2");
208         dcp::combine (inputs, out);
209
210         check_no_errors (out);
211         check_combined (inputs, out);
212 }
213
214
215 BOOST_AUTO_TEST_CASE (combine_two_dcps_with_smpte_subs_test)
216 {
217         using namespace boost::algorithm;
218         using namespace boost::filesystem;
219         boost::filesystem::path const out = "build/test/combine_two_dcps_with_smpte_subs_test";
220
221         shared_ptr<dcp::DCP> first = make_simple_with_smpte_subs ("build/test/combine_input1");
222         first->write_xml (dcp::Standard::SMPTE);
223
224         shared_ptr<dcp::DCP> second = make_simple_with_smpte_subs ("build/test/combine_input2");
225         second->write_xml (dcp::Standard::SMPTE);
226
227         remove_all (out);
228         vector<path> inputs;
229         inputs.push_back ("build/test/combine_input1");
230         inputs.push_back ("build/test/combine_input2");
231         dcp::combine (inputs, out);
232
233         check_no_errors (out);
234         check_combined (inputs, out);
235 }
236
237
238 BOOST_AUTO_TEST_CASE (combine_two_dcps_with_interop_ccaps_test)
239 {
240         using namespace boost::algorithm;
241         using namespace boost::filesystem;
242         boost::filesystem::path const out = "build/test/combine_two_dcps_with_interop_ccaps_test";
243
244         shared_ptr<dcp::DCP> first = make_simple_with_interop_ccaps ("build/test/combine_input1");
245         first->write_xml (dcp::Standard::INTEROP);
246
247         shared_ptr<dcp::DCP> second = make_simple_with_interop_ccaps ("build/test/combine_input2");
248         second->write_xml (dcp::Standard::INTEROP);
249
250         remove_all (out);
251         vector<path> inputs;
252         inputs.push_back ("build/test/combine_input1");
253         inputs.push_back ("build/test/combine_input2");
254         dcp::combine (inputs, out);
255
256         check_no_errors (out);
257         check_combined (inputs, out);
258 }
259
260
261 BOOST_AUTO_TEST_CASE (combine_two_dcps_with_smpte_ccaps_test)
262 {
263         using namespace boost::algorithm;
264         using namespace boost::filesystem;
265         boost::filesystem::path const out = "build/test/combine_two_dcps_with_interop_ccaps_test";
266
267         shared_ptr<dcp::DCP> first = make_simple_with_smpte_ccaps ("build/test/combine_input1");
268         first->write_xml (dcp::Standard::SMPTE);
269
270         shared_ptr<dcp::DCP> second = make_simple_with_smpte_ccaps ("build/test/combine_input2");
271         second->write_xml (dcp::Standard::SMPTE);
272
273         remove_all (out);
274         vector<path> inputs;
275         inputs.push_back ("build/test/combine_input1");
276         inputs.push_back ("build/test/combine_input2");
277         dcp::combine (inputs, out);
278
279         check_no_errors (out);
280         check_combined (inputs, out);
281 }
282
283
284 BOOST_AUTO_TEST_CASE (combine_two_multi_reel_dcps)
285 {
286         using namespace boost::algorithm;
287         using namespace boost::filesystem;
288         boost::filesystem::path const out = "build/test/combine_two_multi_reel_dcps";
289
290         shared_ptr<dcp::DCP> first = make_simple ("build/test/combine_input1", 4);
291         first->write_xml (dcp::Standard::SMPTE);
292
293         shared_ptr<dcp::DCP> second = make_simple ("build/test/combine_input2", 4);
294         second->write_xml (dcp::Standard::SMPTE);
295
296         remove_all (out);
297         vector<path> inputs;
298         inputs.push_back ("build/test/combine_input1");
299         inputs.push_back ("build/test/combine_input2");
300         dcp::combine (inputs, out);
301
302         check_no_errors (out);
303         check_combined (inputs, out);
304 }
305
306
307 BOOST_AUTO_TEST_CASE (combine_two_dcps_with_shared_asset)
308 {
309         using namespace boost::filesystem;
310         boost::filesystem::path const out = "build/test/combine_two_dcps_with_shared_asset";
311
312         shared_ptr<dcp::DCP> first = make_simple ("build/test/combine_input1", 1);
313         first->write_xml (dcp::Standard::SMPTE);
314
315         remove_all ("build/test/combine_input2");
316         shared_ptr<dcp::DCP> second(new dcp::DCP("build/test/combine_input2"));
317
318         dcp::MXFMetadata mxf_meta;
319         mxf_meta.company_name = "OpenDCP";
320         mxf_meta.product_version = "0.0.25";
321
322         auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER);
323         cpl->set_content_version (
324                 dcp::ContentVersion("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11","content-version-label-text")
325                 );
326         cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
327         cpl->set_main_sound_sample_rate (48000);
328         cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
329         cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
330         cpl->set_version_number(1);
331
332         auto pic = make_shared<dcp::ReelMonoPictureAsset>(simple_picture("build/test/combine_input2", ""), 0);
333         auto sound = make_shared<dcp::ReelSoundAsset>(first->cpls().front()->reels().front()->main_sound()->asset(), 0);
334         auto reel = make_shared<dcp::Reel>(pic, sound);
335         reel->add (simple_markers());
336         cpl->add (reel);
337         second->add (cpl);
338         second->write_xml (dcp::Standard::SMPTE);
339
340         remove_all (out);
341         vector<path> inputs;
342         inputs.push_back ("build/test/combine_input1");
343         inputs.push_back ("build/test/combine_input2");
344         dcp::combine (inputs, out);
345
346         check_no_errors (out);
347         check_combined (inputs, out);
348 }
349
350
351 /** Two DCPs each with a copy of the exact same asset */
352 BOOST_AUTO_TEST_CASE (combine_two_dcps_with_duplicated_asset)
353 {
354         using namespace boost::filesystem;
355         boost::filesystem::path const out = "build/test/combine_two_dcps_with_duplicated_asset";
356
357         auto first = make_simple ("build/test/combine_input1", 1);
358         first->write_xml (dcp::Standard::SMPTE);
359
360         remove_all ("build/test/combine_input2");
361         auto second = make_shared<dcp::DCP>("build/test/combine_input2");
362
363         dcp::MXFMetadata mxf_meta;
364         mxf_meta.company_name = "OpenDCP";
365         mxf_meta.product_version = "0.0.25";
366
367         auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER);
368         cpl->set_content_version (
369                 dcp::ContentVersion("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11","content-version-label-text")
370                 );
371         cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
372         cpl->set_main_sound_sample_rate (48000);
373         cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
374         cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
375         cpl->set_version_number(1);
376
377         auto pic = make_shared<dcp::ReelMonoPictureAsset>(simple_picture("build/test/combine_input2", ""), 0);
378         auto first_sound_asset = first->cpls()[0]->reels()[0]->main_sound()->asset()->file();
379         BOOST_REQUIRE (first_sound_asset);
380         boost::filesystem::path second_sound_asset = "build/test/combine_input2/my_great_audio.mxf";
381         boost::filesystem::copy_file (*first_sound_asset, second_sound_asset);
382         auto sound = make_shared<dcp::ReelSoundAsset>(make_shared<dcp::SoundAsset>(second_sound_asset), 0);
383         auto reel = make_shared<dcp::Reel>(pic, sound);
384         reel->add (simple_markers());
385         cpl->add (reel);
386         second->add (cpl);
387         second->write_xml (dcp::Standard::SMPTE);
388
389         remove_all (out);
390         vector<path> inputs;
391         inputs.push_back ("build/test/combine_input1");
392         inputs.push_back ("build/test/combine_input2");
393         dcp::combine (inputs, out);
394
395         check_no_errors (out);
396         check_combined (inputs, out);
397
398         BOOST_REQUIRE (!boost::filesystem::exists(out / "my_great_audio.mxf"));
399 }
400
401
402 /* XXX: same CPL names */
403 /* XXX: Interop PNG subs */