Add namespaces and tweak CPL namespace so Xerces checks CompositionMetadataAsset.
[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 <boost/shared_ptr.hpp>
43 #include <boost/test/unit_test.hpp>
44
45
46 using std::list;
47 using std::string;
48 using std::vector;
49 using boost::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::MainSoundConfiguration::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::MainSoundConfiguration::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::MainSoundConfiguration::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::MainSoundConfiguration::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,Sync,Sign");
130         BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss,HI,VIN,-,-,Lrs,Rrs,DBOX,Sync,Sign");
131         BOOST_CHECK_EQUAL (msc.channels(), 15);
132         BOOST_CHECK_EQUAL (msc.field(), dcp::MainSoundConfiguration::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         list<shared_ptr<dcp::Reel> > reels = cpl.reels ();
235         BOOST_REQUIRE_EQUAL (reels.size(), 1);
236         BOOST_CHECK_EQUAL (reels.front()->main_subtitle()->language().get(), dcp::LanguageTag("de-DE"));
237
238         vector<string> asl = cpl.additional_subtitle_languages();
239         BOOST_REQUIRE_EQUAL (asl.size(), 2);
240         BOOST_CHECK_EQUAL (asl[0], "en-US");
241         BOOST_CHECK_EQUAL (asl[1], "fr-ZA");
242
243         BOOST_CHECK (cpl.additional_subtitle_languages() == asl);
244 }
245
246
247 /** A test where most CPL metadata is present */
248 BOOST_AUTO_TEST_CASE (cpl_metadata_write_test1)
249 {
250         RNGFixer fix;
251
252         dcp::CPL cpl("", dcp::FEATURE);
253         cpl.set_issue_date ("2020-08-28T13:35:06+02:00");
254
255         vector<dcp::ContentVersion> cv;
256         cv.push_back (dcp::ContentVersion("some-id", "version 1"));
257         cv.push_back (dcp::ContentVersion("another-id", "version 2"));
258         cpl.set_content_versions (cv);
259
260         cpl.set_full_content_title_text ("full-content-title");
261         cpl.set_full_content_title_text_language (dcp::LanguageTag("de"));
262         cpl.set_release_territory (dcp::LanguageTag::RegionSubtag("ES"));
263         cpl.set_version_number (2);
264         cpl.set_status (dcp::FINAL);
265         cpl.set_chain ("the-chain");
266         cpl.set_distributor ("the-distributor");
267         cpl.set_facility ("the-facility");
268         cpl.set_luminance (dcp::Luminance(4.5, dcp::Luminance::FOOT_LAMBERT));
269
270         dcp::MainSoundConfiguration msc(dcp::MainSoundConfiguration::SEVEN_POINT_ONE, 16);
271         msc.set_mapping (0, dcp::LEFT);
272         msc.set_mapping (1, dcp::RIGHT);
273         msc.set_mapping (2, dcp::CENTRE);
274         msc.set_mapping (3, dcp::LFE);
275         msc.set_mapping (13, dcp::SYNC_SIGNAL);
276         cpl.set_main_sound_configuration (msc.to_string());
277
278         cpl.set_main_sound_sample_rate (48000);
279         cpl.set_main_picture_stored_area (dcp::Size(1998, 1080));
280         cpl.set_main_picture_active_area (dcp::Size(1440, 1080));
281
282         shared_ptr<cxml::Document> doc (new cxml::Document("MainSubtitle"));
283
284         doc->read_string (
285                 "<MainSubtitle>"
286                 "<Id>urn:uuid:8bca1489-aab1-9259-a4fd-8150abc1de12</Id>"
287                 "<AnnotationText>Goodbye world!</AnnotationText>"
288                 "<EditRate>25 1</EditRate>"
289                 "<IntrinsicDuration>1870</IntrinsicDuration>"
290                 "<EntryPoint>0</EntryPoint>"
291                 "<Duration>525</Duration>"
292                 "<KeyId>urn:uuid:540cbf10-ab14-0233-ab1f-fb31501cabfa</KeyId>"
293                 "<Hash>3EABjX9BB1CAWhLUtHhrGSyLgOY=</Hash>"
294                 "<Language>de-DE</Language>"
295                 "</MainSubtitle>"
296                 );
297
298         shared_ptr<dcp::Reel> reel(new dcp::Reel());
299         reel->add (black_picture_asset("build/test/cpl_metadata_write_test1"));
300         reel->add (shared_ptr<dcp::ReelSubtitleAsset>(new dcp::ReelSubtitleAsset(doc)));
301         cpl.add (reel);
302
303         vector<dcp::LanguageTag> lt;
304         lt.push_back(dcp::LanguageTag("en-US"));
305         lt.push_back(dcp::LanguageTag("fr-ZA"));
306         cpl.set_additional_subtitle_languages (lt);
307
308         cpl.write_xml ("build/test/cpl_metadata_write_test1.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
309         check_xml (
310                 dcp::file_to_string("test/ref/cpl_metadata_test1.xml"),
311                 dcp::file_to_string("build/test/cpl_metadata_write_test1.xml"),
312                 list<string>()
313                 );
314 }
315
316
317 /** A test where most CPL metadata is present */
318 BOOST_AUTO_TEST_CASE (cpl_metadata_roundtrip_test_1)
319 {
320         dcp::CPL cpl ("test/ref/cpl_metadata_test1.xml");
321         cpl.write_xml ("build/test/cpl_metadata_roundtrip_test1.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
322         list<string> ignore;
323         ignore.push_back ("Id");
324         check_xml (
325                 dcp::file_to_string("test/ref/cpl_metadata_test1.xml"),
326                 dcp::file_to_string("build/test/cpl_metadata_roundtrip_test1.xml"),
327                 ignore
328                 );
329 }
330
331
332 /** A test where only a bare minimum of CPL metadata is present */
333 BOOST_AUTO_TEST_CASE (cpl_metadata_write_test2)
334 {
335         RNGFixer fix;
336
337         dcp::CPL cpl("", dcp::FEATURE);
338         cpl.set_issue_date ("2020-08-28T13:35:06+02:00");
339         cpl.set_content_version (dcp::ContentVersion("id", "version"));
340
341         dcp::MainSoundConfiguration msc(dcp::MainSoundConfiguration::SEVEN_POINT_ONE, 16);
342         msc.set_mapping (0, dcp::LEFT);
343         msc.set_mapping (1, dcp::RIGHT);
344         msc.set_mapping (2, dcp::CENTRE);
345         msc.set_mapping (3, dcp::LFE);
346         msc.set_mapping (13, dcp::SYNC_SIGNAL);
347         cpl.set_main_sound_configuration (msc.to_string());
348
349         cpl.set_main_sound_sample_rate (48000);
350         cpl.set_main_picture_stored_area (dcp::Size(1998, 1080));
351         cpl.set_main_picture_active_area (dcp::Size(1440, 1080));
352
353         shared_ptr<dcp::Reel> reel(new dcp::Reel());
354         reel->add (black_picture_asset("build/test/cpl_metadata_write_test1"));
355         cpl.add (reel);
356
357         cpl.write_xml ("build/test/cpl_metadata_write_test2.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
358         check_xml (
359                 dcp::file_to_string("test/ref/cpl_metadata_test2.xml"),
360                 dcp::file_to_string("build/test/cpl_metadata_write_test2.xml"),
361                 list<string>()
362                 );
363 }
364
365
366 /** A test where only a bare minimum of CPL metadata is present */
367 BOOST_AUTO_TEST_CASE (cpl_metadata_read_test2)
368 {
369         dcp::CPL cpl("test/ref/cpl_metadata_test2.xml");
370
371         BOOST_CHECK_EQUAL (cpl.full_content_title_text().get(), "");
372         BOOST_CHECK (!cpl.full_content_title_text_language());
373         BOOST_CHECK (!cpl.release_territory());
374         BOOST_CHECK (!cpl.version_number());
375         BOOST_CHECK (!cpl.status());
376         BOOST_CHECK (!cpl.chain());
377         BOOST_CHECK (!cpl.distributor());
378         BOOST_CHECK (!cpl.facility());
379         BOOST_CHECK (!cpl.luminance());
380
381         dcp::MainSoundConfiguration msc(cpl.main_sound_configuration().get());
382         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
383         BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
384         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
385         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
386         BOOST_CHECK (!msc.mapping(4));
387         BOOST_CHECK (!msc.mapping(5));
388         BOOST_CHECK (!msc.mapping(6));
389         BOOST_CHECK (!msc.mapping(7));
390         BOOST_CHECK (!msc.mapping(8));
391         BOOST_CHECK (!msc.mapping(9));
392         BOOST_CHECK (!msc.mapping(10));
393         BOOST_CHECK (!msc.mapping(11));
394         BOOST_CHECK (!msc.mapping(12));
395         BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::SYNC_SIGNAL);
396
397         BOOST_CHECK_EQUAL (cpl.main_sound_sample_rate().get(), 48000);
398         BOOST_CHECK (cpl.main_picture_stored_area().get() == dcp::Size(1998, 1080));
399         BOOST_CHECK (cpl.main_picture_active_area().get() == dcp::Size(1440, 1080));
400
401         list<shared_ptr<dcp::Reel> > reels = cpl.reels ();
402         BOOST_REQUIRE_EQUAL (reels.size(), 1);
403 }
404
405
406 /** A test where only a bare minimum of CPL metadata is present */
407 BOOST_AUTO_TEST_CASE (cpl_metadata_roundtrip_test_2)
408 {
409         dcp::CPL cpl ("test/ref/cpl_metadata_test2.xml");
410         cpl.write_xml ("build/test/cpl_metadata_roundtrip_test2.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
411         list<string> ignore;
412         ignore.push_back ("Id");
413         check_xml (
414                 dcp::file_to_string("test/ref/cpl_metadata_test2.xml"),
415                 dcp::file_to_string("build/test/cpl_metadata_roundtrip_test2.xml"),
416                 ignore
417                 );
418 }
419