Replace std::list with std::vector in the API.
[libdcp.git] / test / cpl_metadata_test.cc
1 /*
2     Copyright (C) 2020 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 "certificate_chain.h"
36 #include "cpl.h"
37 #include "exceptions.h"
38 #include "language_tag.h"
39 #include "reel.h"
40 #include "reel_subtitle_asset.h"
41 #include "test.h"
42 #include <memory>
43 #include <boost/test/unit_test.hpp>
44
45
46 using std::list;
47 using std::string;
48 using std::vector;
49 using std::shared_ptr;
50
51
52 BOOST_AUTO_TEST_CASE (cpl_metadata_bad_values_test)
53 {
54         dcp::CPL cpl("", dcp::FEATURE);
55         BOOST_CHECK_THROW (cpl.set_version_number(-1), dcp::BadSettingError);
56
57         vector<dcp::ContentVersion> cv;
58         cv.push_back (dcp::ContentVersion("same-id", "version 1"));
59         cv.push_back (dcp::ContentVersion("same-id", "version 2"));
60         BOOST_CHECK_THROW (cpl.set_content_versions(cv), dcp::DuplicateIdError);
61 }
62
63
64 BOOST_AUTO_TEST_CASE (main_sound_configuration_test1)
65 {
66         dcp::MainSoundConfiguration msc("51/L,R,C,LFE,-,-");
67         BOOST_CHECK_EQUAL (msc.to_string(), "51/L,R,C,LFE,-,-");
68         BOOST_CHECK_EQUAL (msc.channels(), 6);
69         BOOST_CHECK_EQUAL (msc.field(), dcp::FIVE_POINT_ONE);
70         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
71         BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
72         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
73         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
74         BOOST_CHECK (!msc.mapping(4));
75         BOOST_CHECK (!msc.mapping(5));
76 }
77
78
79 BOOST_AUTO_TEST_CASE (main_sound_configuration_test2)
80 {
81         dcp::MainSoundConfiguration msc("71/L,R,C,LFE,-,-");
82         BOOST_CHECK_EQUAL (msc.to_string(), "71/L,R,C,LFE,-,-");
83         BOOST_CHECK_EQUAL (msc.channels(), 6);
84         BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
85         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
86         BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
87         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
88         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
89         BOOST_CHECK (!msc.mapping(4));
90         BOOST_CHECK (!msc.mapping(5));
91 }
92
93
94 BOOST_AUTO_TEST_CASE (main_sound_configuration_test3)
95 {
96         dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss");
97         BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss");
98         BOOST_CHECK_EQUAL (msc.channels(), 6);
99         BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
100         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
101         BOOST_CHECK (!msc.mapping(1));
102         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
103         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
104         BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::LS);
105         BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::RS);
106 }
107
108
109 BOOST_AUTO_TEST_CASE (main_sound_configuration_test4)
110 {
111         dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss,-,-,-,-,-,-,-,-,-");
112         BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss,-,-,-,-,-,-,-,-,-");
113         BOOST_CHECK_EQUAL (msc.channels(), 15);
114         BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
115         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
116         BOOST_CHECK (!msc.mapping(1));
117         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
118         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
119         BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::LS);
120         BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::RS);
121         for (int i = 6; i < 15; ++i) {
122                 BOOST_CHECK (!msc.mapping(i));
123         }
124 }
125
126
127 BOOST_AUTO_TEST_CASE (main_sound_configuration_test5)
128 {
129         dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss,HI,VIN,-,-,Lrs,Rrs,DBOX,FSKSync,SLVS");
130         BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss,HI,VIN,-,-,Lrs,Rrs,DBOX,FSKSync,SLVS");
131         BOOST_CHECK_EQUAL (msc.channels(), 15);
132         BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
133         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
134         BOOST_CHECK (!msc.mapping(1));
135         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
136         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
137         BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::LS);
138         BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::RS);
139         BOOST_CHECK_EQUAL (msc.mapping(6).get(), dcp::HI);
140         BOOST_CHECK_EQUAL (msc.mapping(7).get(), dcp::VI);
141         BOOST_CHECK (!msc.mapping(8));
142         BOOST_CHECK (!msc.mapping(9));
143         BOOST_CHECK_EQUAL (msc.mapping(10).get(), dcp::BSL);
144         BOOST_CHECK_EQUAL (msc.mapping(11).get(), dcp::BSR);
145         BOOST_CHECK_EQUAL (msc.mapping(12).get(), dcp::MOTION_DATA);
146         BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::SYNC_SIGNAL);
147         BOOST_CHECK_EQUAL (msc.mapping(14).get(), dcp::SIGN_LANGUAGE);
148 }
149
150
151 BOOST_AUTO_TEST_CASE (luminance_test1)
152 {
153         BOOST_CHECK_NO_THROW (dcp::Luminance(4, dcp::Luminance::CANDELA_PER_SQUARE_METRE));
154         BOOST_CHECK_THROW (dcp::Luminance(-4, dcp::Luminance::CANDELA_PER_SQUARE_METRE), dcp::MiscError);
155 }
156
157
158 BOOST_AUTO_TEST_CASE (luminance_test2)
159 {
160         shared_ptr<cxml::Document> doc (new cxml::Document("Luminance"));
161
162         doc->read_string (
163                 "<Luminance units=\"candela-per-square-metre\">4.5</Luminance>"
164                 );
165
166         dcp::Luminance lum (doc);
167         BOOST_CHECK (lum.unit() == dcp::Luminance::CANDELA_PER_SQUARE_METRE);
168         BOOST_CHECK_CLOSE (lum.value(), 4.5, 0.1);
169 }
170
171
172 BOOST_AUTO_TEST_CASE (luminance_test3)
173 {
174         shared_ptr<cxml::Document> doc (new cxml::Document("Luminance"));
175
176         doc->read_string (
177                 "<Luminance units=\"candela-per-square-motre\">4.5</Luminance>"
178                 );
179
180         BOOST_CHECK_THROW (new dcp::Luminance(doc), dcp::XMLError);
181 }
182
183
184 BOOST_AUTO_TEST_CASE (luminance_test4)
185 {
186         shared_ptr<cxml::Document> doc (new cxml::Document("Luminance"));
187
188         doc->read_string (
189                 "<Luminance units=\"candela-per-square-metre\">-4.5</Luminance>"
190                 );
191
192         /* We tolerate out-of-range values when reading from XML */
193         dcp::Luminance lum (doc);
194         BOOST_CHECK (lum.unit() == dcp::Luminance::CANDELA_PER_SQUARE_METRE);
195         BOOST_CHECK_CLOSE (lum.value(), -4.5, 0.1);
196 }
197
198
199 /** A test where most CPL metadata is present */
200 BOOST_AUTO_TEST_CASE (cpl_metadata_read_test1)
201 {
202         dcp::CPL cpl("test/ref/cpl_metadata_test1.xml");
203
204         BOOST_CHECK_EQUAL (cpl.full_content_title_text().get(), "full-content-title");
205         BOOST_CHECK (cpl.full_content_title_text_language().get() == "de");
206         BOOST_CHECK (cpl.release_territory().get() == "ES");
207         BOOST_CHECK_EQUAL (cpl.version_number().get(), 2);
208         BOOST_CHECK_EQUAL (cpl.status().get(), dcp::FINAL);
209         BOOST_CHECK_EQUAL (cpl.chain().get(), "the-chain");
210         BOOST_CHECK_EQUAL (cpl.distributor().get(), "the-distributor");
211         BOOST_CHECK_EQUAL (cpl.facility().get(), "the-facility");
212         BOOST_CHECK (cpl.luminance() == dcp::Luminance(4.5, dcp::Luminance::FOOT_LAMBERT));
213
214         dcp::MainSoundConfiguration msc(cpl.main_sound_configuration().get());
215         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
216         BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
217         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
218         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
219         BOOST_CHECK (!msc.mapping(4));
220         BOOST_CHECK (!msc.mapping(5));
221         BOOST_CHECK (!msc.mapping(6));
222         BOOST_CHECK (!msc.mapping(7));
223         BOOST_CHECK (!msc.mapping(8));
224         BOOST_CHECK (!msc.mapping(9));
225         BOOST_CHECK (!msc.mapping(10));
226         BOOST_CHECK (!msc.mapping(11));
227         BOOST_CHECK (!msc.mapping(12));
228         BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::SYNC_SIGNAL);
229
230         BOOST_CHECK_EQUAL (cpl.main_sound_sample_rate().get(), 48000);
231         BOOST_CHECK (cpl.main_picture_stored_area().get() == dcp::Size(1998, 1080));
232         BOOST_CHECK (cpl.main_picture_active_area().get() == dcp::Size(1440, 1080));
233
234         auto reels = cpl.reels ();
235         BOOST_REQUIRE_EQUAL (reels.size(), 1);
236         BOOST_REQUIRE (reels.front()->main_subtitle()->language());
237         BOOST_CHECK_EQUAL (reels.front()->main_subtitle()->language().get(), "de-DE");
238
239         vector<string> asl = cpl.additional_subtitle_languages();
240         BOOST_REQUIRE_EQUAL (asl.size(), 2);
241         BOOST_CHECK_EQUAL (asl[0], "en-US");
242         BOOST_CHECK_EQUAL (asl[1], "fr-ZA");
243
244         BOOST_CHECK (cpl.additional_subtitle_languages() == asl);
245 }
246
247
248 /** A test where most CPL metadata is present */
249 BOOST_AUTO_TEST_CASE (cpl_metadata_write_test1)
250 {
251         RNGFixer fix;
252
253         dcp::CPL cpl("", dcp::FEATURE);
254         cpl.set_issue_date ("2020-08-28T13:35:06+02:00");
255
256         vector<dcp::ContentVersion> cv;
257         cv.push_back (dcp::ContentVersion("some-id", "version 1"));
258         cv.push_back (dcp::ContentVersion("another-id", "version 2"));
259         cpl.set_content_versions (cv);
260
261         cpl.set_full_content_title_text ("full-content-title");
262         cpl.set_full_content_title_text_language (dcp::LanguageTag("de"));
263         cpl.set_release_territory (dcp::LanguageTag::RegionSubtag("ES"));
264         cpl.set_version_number (2);
265         cpl.set_status (dcp::FINAL);
266         cpl.set_chain ("the-chain");
267         cpl.set_distributor ("the-distributor");
268         cpl.set_facility ("the-facility");
269         cpl.set_luminance (dcp::Luminance(4.5, dcp::Luminance::FOOT_LAMBERT));
270         cpl.set_issuer ("libdcp1.6.4devel");
271         cpl.set_creator ("libdcp1.6.4devel");
272
273         dcp::MainSoundConfiguration msc(dcp::SEVEN_POINT_ONE, 16);
274         msc.set_mapping (0, dcp::LEFT);
275         msc.set_mapping (1, dcp::RIGHT);
276         msc.set_mapping (2, dcp::CENTRE);
277         msc.set_mapping (3, dcp::LFE);
278         msc.set_mapping (13, dcp::SYNC_SIGNAL);
279         cpl.set_main_sound_configuration (msc.to_string());
280
281         cpl.set_main_sound_sample_rate (48000);
282         cpl.set_main_picture_stored_area (dcp::Size(1998, 1080));
283         cpl.set_main_picture_active_area (dcp::Size(1440, 1080));
284
285         shared_ptr<cxml::Document> doc (new cxml::Document("MainSubtitle"));
286
287         doc->read_string (
288                 "<MainSubtitle>"
289                 "<Id>urn:uuid:8bca1489-aab1-9259-a4fd-8150abc1de12</Id>"
290                 "<AnnotationText>Goodbye world!</AnnotationText>"
291                 "<EditRate>25 1</EditRate>"
292                 "<IntrinsicDuration>1870</IntrinsicDuration>"
293                 "<EntryPoint>0</EntryPoint>"
294                 "<Duration>525</Duration>"
295                 "<KeyId>urn:uuid:540cbf10-ab14-0233-ab1f-fb31501cabfa</KeyId>"
296                 "<Hash>3EABjX9BB1CAWhLUtHhrGSyLgOY=</Hash>"
297                 "<Language>de-DE</Language>"
298                 "</MainSubtitle>"
299                 );
300
301         shared_ptr<dcp::Reel> reel(new dcp::Reel());
302         reel->add (black_picture_asset("build/test/cpl_metadata_write_test1"));
303         reel->add (shared_ptr<dcp::ReelSubtitleAsset>(new dcp::ReelSubtitleAsset(doc)));
304         cpl.add (reel);
305
306         vector<dcp::LanguageTag> lt;
307         lt.push_back(dcp::LanguageTag("en-US"));
308         lt.push_back(dcp::LanguageTag("fr-ZA"));
309         cpl.set_additional_subtitle_languages (lt);
310
311         cpl.write_xml ("build/test/cpl_metadata_write_test1.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
312         check_xml (
313                 dcp::file_to_string("test/ref/cpl_metadata_test1.xml"),
314                 dcp::file_to_string("build/test/cpl_metadata_write_test1.xml"),
315                 vector<string>()
316                 );
317 }
318
319
320 /** A test where most CPL metadata is present */
321 BOOST_AUTO_TEST_CASE (cpl_metadata_roundtrip_test_1)
322 {
323         dcp::CPL cpl ("test/ref/cpl_metadata_test1.xml");
324         cpl.write_xml ("build/test/cpl_metadata_roundtrip_test1.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
325         vector<string> ignore;
326         ignore.push_back ("Id");
327         check_xml (
328                 dcp::file_to_string("test/ref/cpl_metadata_test1.xml"),
329                 dcp::file_to_string("build/test/cpl_metadata_roundtrip_test1.xml"),
330                 ignore
331                 );
332 }
333
334
335 /** A test where only a bare minimum of CPL metadata is present */
336 BOOST_AUTO_TEST_CASE (cpl_metadata_write_test2)
337 {
338         RNGFixer fix;
339
340         dcp::CPL cpl("", dcp::FEATURE);
341         cpl.set_issue_date ("2020-08-28T13:35:06+02:00");
342         cpl.set_content_version (dcp::ContentVersion("id", "version"));
343         cpl.set_issuer ("libdcp1.6.4devel");
344         cpl.set_creator ("libdcp1.6.4devel");
345
346         dcp::MainSoundConfiguration msc(dcp::SEVEN_POINT_ONE, 16);
347         msc.set_mapping (0, dcp::LEFT);
348         msc.set_mapping (1, dcp::RIGHT);
349         msc.set_mapping (2, dcp::CENTRE);
350         msc.set_mapping (3, dcp::LFE);
351         msc.set_mapping (13, dcp::SYNC_SIGNAL);
352         cpl.set_main_sound_configuration (msc.to_string());
353
354         cpl.set_main_sound_sample_rate (48000);
355         cpl.set_main_picture_stored_area (dcp::Size(1998, 1080));
356         cpl.set_main_picture_active_area (dcp::Size(1440, 1080));
357
358         shared_ptr<dcp::Reel> reel(new dcp::Reel());
359         reel->add (black_picture_asset("build/test/cpl_metadata_write_test1"));
360         cpl.add (reel);
361
362         cpl.write_xml ("build/test/cpl_metadata_write_test2.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
363         check_xml (
364                 dcp::file_to_string("test/ref/cpl_metadata_test2.xml"),
365                 dcp::file_to_string("build/test/cpl_metadata_write_test2.xml"),
366                 vector<string>()
367                 );
368 }
369
370
371 /** A test where only a bare minimum of CPL metadata is present */
372 BOOST_AUTO_TEST_CASE (cpl_metadata_read_test2)
373 {
374         dcp::CPL cpl("test/ref/cpl_metadata_test2.xml");
375
376         BOOST_CHECK_EQUAL (cpl.full_content_title_text().get(), "");
377         BOOST_CHECK (!cpl.full_content_title_text_language());
378         BOOST_CHECK (!cpl.release_territory());
379         BOOST_CHECK (!cpl.version_number());
380         BOOST_CHECK (!cpl.status());
381         BOOST_CHECK (!cpl.chain());
382         BOOST_CHECK (!cpl.distributor());
383         BOOST_CHECK (!cpl.facility());
384         BOOST_CHECK (!cpl.luminance());
385
386         dcp::MainSoundConfiguration msc(cpl.main_sound_configuration().get());
387         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
388         BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
389         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
390         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
391         BOOST_CHECK (!msc.mapping(4));
392         BOOST_CHECK (!msc.mapping(5));
393         BOOST_CHECK (!msc.mapping(6));
394         BOOST_CHECK (!msc.mapping(7));
395         BOOST_CHECK (!msc.mapping(8));
396         BOOST_CHECK (!msc.mapping(9));
397         BOOST_CHECK (!msc.mapping(10));
398         BOOST_CHECK (!msc.mapping(11));
399         BOOST_CHECK (!msc.mapping(12));
400         BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::SYNC_SIGNAL);
401
402         BOOST_CHECK_EQUAL (cpl.main_sound_sample_rate().get(), 48000);
403         BOOST_CHECK (cpl.main_picture_stored_area().get() == dcp::Size(1998, 1080));
404         BOOST_CHECK (cpl.main_picture_active_area().get() == dcp::Size(1440, 1080));
405
406         auto reels = cpl.reels ();
407         BOOST_REQUIRE_EQUAL (reels.size(), 1);
408 }
409
410
411 /** A test where only a bare minimum of CPL metadata is present */
412 BOOST_AUTO_TEST_CASE (cpl_metadata_roundtrip_test_2)
413 {
414         dcp::CPL cpl ("test/ref/cpl_metadata_test2.xml");
415         cpl.write_xml ("build/test/cpl_metadata_roundtrip_test2.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
416         vector<string> ignore;
417         ignore.push_back ("Id");
418         check_xml (
419                 dcp::file_to_string("test/ref/cpl_metadata_test2.xml"),
420                 dcp::file_to_string("build/test/cpl_metadata_roundtrip_test2.xml"),
421                 ignore
422                 );
423 }
424