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