2 Copyright (C) 2018-2020 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
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.
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.
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/>.
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
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.
38 #include "reel_mono_picture_asset.h"
39 #include "reel_sound_asset.h"
42 #include "openjpeg_image.h"
43 #include "mono_picture_asset.h"
44 #include "mono_picture_asset_writer.h"
45 #include "interop_subtitle_asset.h"
46 #include "smpte_subtitle_asset.h"
47 #include "reel_closed_caption_asset.h"
48 #include "reel_subtitle_asset.h"
49 #include "compose.hpp"
51 #include <boost/test/unit_test.hpp>
52 #include <boost/foreach.hpp>
53 #include <boost/algorithm/string.hpp>
62 using boost::optional;
63 using std::shared_ptr;
66 static list<pair<string, optional<boost::filesystem::path> > > stages;
69 stage (string s, optional<boost::filesystem::path> p)
71 stages.push_back (make_pair (s, p));
81 prepare_directory (boost::filesystem::path path)
83 using namespace boost::filesystem;
85 create_directories (path);
89 static vector<boost::filesystem::path>
90 setup (int reference_number, int verify_test_number)
92 prepare_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
93 for (boost::filesystem::directory_iterator i(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number)); i != boost::filesystem::directory_iterator(); ++i) {
94 boost::filesystem::copy_file (i->path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i->path().filename());
97 vector<boost::filesystem::path> directories;
98 directories.push_back (dcp::String::compose("build/test/verify_test%1", verify_test_number));
104 /** Class that can alter a file by searching and replacing strings within it.
105 * On destruction modifies the file whose name was given to the constructor.
110 Editor (boost::filesystem::path path)
113 _content = dcp::file_to_string (_path);
118 FILE* f = fopen(_path.string().c_str(), "w");
120 fwrite (_content.c_str(), _content.length(), 1, f);
124 void replace (string a, string b)
126 boost::algorithm::replace_all (_content, a, b);
130 boost::filesystem::path _path;
131 std::string _content;
137 dump_notes (list<dcp::VerificationNote> const & notes)
139 BOOST_FOREACH (dcp::VerificationNote i, notes) {
140 std::cout << dcp::note_to_string(i) << "\n";
144 /* Check DCP as-is (should be OK) */
145 BOOST_AUTO_TEST_CASE (verify_test1)
148 vector<boost::filesystem::path> directories = setup (1, 1);
149 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
151 boost::filesystem::path const cpl_file = "build/test/verify_test1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
152 boost::filesystem::path const pkl_file = "build/test/verify_test1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml";
153 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
155 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
156 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
157 BOOST_REQUIRE (st->second);
158 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
160 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
161 BOOST_REQUIRE (st->second);
162 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
164 BOOST_CHECK_EQUAL (st->first, "Checking reel");
165 BOOST_REQUIRE (!st->second);
167 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
168 BOOST_REQUIRE (st->second);
169 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
171 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
172 BOOST_REQUIRE (st->second);
173 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
175 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
176 BOOST_REQUIRE (st->second);
177 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
179 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
180 BOOST_REQUIRE (st->second);
181 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
183 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
184 BOOST_REQUIRE (st->second);
185 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
187 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
188 BOOST_REQUIRE (st->second);
189 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
191 BOOST_REQUIRE (st == stages.end());
193 BOOST_CHECK_EQUAL (notes.size(), 0);
196 /* Corrupt the MXFs and check that this is spotted */
197 BOOST_AUTO_TEST_CASE (verify_test2)
199 vector<boost::filesystem::path> directories = setup (1, 2);
201 FILE* mod = fopen("build/test/verify_test2/video.mxf", "r+b");
203 fseek (mod, 4096, SEEK_SET);
205 fwrite (&x, sizeof(x), 1, mod);
208 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
210 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
211 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
214 list<dcp::VerificationNote> notes;
216 dcp::ASDCPErrorSuspender sus;
217 notes = dcp::verify (directories, &stage, &progress, xsd_test);
220 BOOST_REQUIRE_EQUAL (notes.size(), 2);
221 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
222 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_HASH_INCORRECT);
223 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_ERROR);
224 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::SOUND_HASH_INCORRECT);
227 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
228 BOOST_AUTO_TEST_CASE (verify_test3)
230 vector<boost::filesystem::path> directories = setup (1, 3);
233 Editor e ("build/test/verify_test3/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml");
234 e.replace ("<Hash>", "<Hash>x");
237 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
239 BOOST_REQUIRE_EQUAL (notes.size(), 6);
240 list<dcp::VerificationNote>::const_iterator i = notes.begin();
241 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
242 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
244 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
245 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DISAGREE);
247 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
248 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DISAGREE);
250 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
251 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
253 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
254 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
256 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
257 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
261 /* Corrupt the ContentKind in the CPL */
262 BOOST_AUTO_TEST_CASE (verify_test4)
264 vector<boost::filesystem::path> directories = setup (1, 4);
267 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
268 e.replace ("<ContentKind>", "<ContentKind>x");
271 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
273 BOOST_REQUIRE_EQUAL (notes.size(), 1);
274 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
275 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xfeature'");
279 boost::filesystem::path
282 return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
286 boost::filesystem::path
289 return dcp::String::compose("build/test/verify_test%1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml", n);
293 boost::filesystem::path
296 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
300 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1)
302 vector<boost::filesystem::path> directories = setup (1, n);
306 e.replace (from, to);
309 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
311 BOOST_REQUIRE_EQUAL (notes.size(), 1);
312 BOOST_CHECK_EQUAL (notes.front().code(), code1);
316 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1, dcp::VerificationNote::Code code2)
318 vector<boost::filesystem::path> directories = setup (1, n);
322 e.replace (from, to);
325 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
327 BOOST_REQUIRE_EQUAL (notes.size(), 2);
328 BOOST_CHECK_EQUAL (notes.front().code(), code1);
329 BOOST_CHECK_EQUAL (notes.back().code(), code2);
333 void check_after_replace (
334 int n, boost::function<boost::filesystem::path (int)> file,
337 dcp::VerificationNote::Code code1,
338 dcp::VerificationNote::Code code2,
339 dcp::VerificationNote::Code code3
342 vector<boost::filesystem::path> directories = setup (1, n);
346 e.replace (from, to);
349 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
351 BOOST_REQUIRE_EQUAL (notes.size(), 3);
352 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
353 BOOST_CHECK_EQUAL (i->code(), code1);
355 BOOST_CHECK_EQUAL (i->code(), code2);
357 BOOST_CHECK_EQUAL (i->code(), code3);
361 BOOST_AUTO_TEST_CASE (verify_test5)
363 check_after_replace (
365 "<FrameRate>24 1", "<FrameRate>99 1",
366 dcp::VerificationNote::CPL_HASH_INCORRECT,
367 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE
372 BOOST_AUTO_TEST_CASE (verify_test6)
374 vector<boost::filesystem::path> directories = setup (1, 6);
376 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
377 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
379 BOOST_REQUIRE_EQUAL (notes.size(), 1);
380 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
381 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_ASSET);
385 boost::filesystem::path
388 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
391 /* Empty asset filename in ASSETMAP */
392 BOOST_AUTO_TEST_CASE (verify_test7)
394 check_after_replace (
396 "<Path>video.mxf</Path>", "<Path></Path>",
397 dcp::VerificationNote::EMPTY_ASSET_PATH
401 /* Mismatched standard */
402 BOOST_AUTO_TEST_CASE (verify_test8)
404 check_after_replace (
406 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
407 dcp::VerificationNote::MISMATCHED_STANDARD,
408 dcp::VerificationNote::XML_VALIDATION_ERROR,
409 dcp::VerificationNote::CPL_HASH_INCORRECT
413 /* Badly formatted <Id> in CPL */
414 BOOST_AUTO_TEST_CASE (verify_test9)
416 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
417 check_after_replace (
419 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
420 dcp::VerificationNote::XML_VALIDATION_ERROR
424 /* Badly formatted <IssueDate> in CPL */
425 BOOST_AUTO_TEST_CASE (verify_test10)
427 check_after_replace (
429 "<IssueDate>", "<IssueDate>x",
430 dcp::VerificationNote::XML_VALIDATION_ERROR,
431 dcp::VerificationNote::CPL_HASH_INCORRECT
435 /* Badly-formatted <Id> in PKL */
436 BOOST_AUTO_TEST_CASE (verify_test11)
438 check_after_replace (
440 "<Id>urn:uuid:cd4", "<Id>urn:uuid:xd4",
441 dcp::VerificationNote::XML_VALIDATION_ERROR
445 /* Badly-formatted <Id> in ASSETMAP */
446 BOOST_AUTO_TEST_CASE (verify_test12)
448 check_after_replace (
450 "<Id>urn:uuid:63c", "<Id>urn:uuid:x3c",
451 dcp::VerificationNote::XML_VALIDATION_ERROR
455 /* Basic test of an Interop DCP */
456 BOOST_AUTO_TEST_CASE (verify_test13)
459 vector<boost::filesystem::path> directories = setup (3, 13);
460 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
462 boost::filesystem::path const cpl_file = "build/test/verify_test13/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
463 boost::filesystem::path const pkl_file = "build/test/verify_test13/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
464 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
466 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
467 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
468 BOOST_REQUIRE (st->second);
469 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
471 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
472 BOOST_REQUIRE (st->second);
473 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
475 BOOST_CHECK_EQUAL (st->first, "Checking reel");
476 BOOST_REQUIRE (!st->second);
478 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
479 BOOST_REQUIRE (st->second);
480 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
482 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
483 BOOST_REQUIRE (st->second);
484 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
486 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
487 BOOST_REQUIRE (st->second);
488 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
490 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
491 BOOST_REQUIRE (st->second);
492 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
494 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
495 BOOST_REQUIRE (st->second);
496 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
498 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
499 BOOST_REQUIRE (st->second);
500 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
502 BOOST_REQUIRE (st == stages.end());
504 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
505 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
506 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
507 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
510 /* DCP with a short asset */
511 BOOST_AUTO_TEST_CASE (verify_test14)
513 vector<boost::filesystem::path> directories = setup (8, 14);
514 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
516 BOOST_REQUIRE_EQUAL (notes.size(), 5);
517 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
518 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
520 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
522 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
524 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
526 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
533 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
535 shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
536 boost::filesystem::create_directories (dir);
537 shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
538 for (int i = 0; i < 24; ++i) {
539 writer->write (frame.data(), frame.size());
543 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelMonoPictureAsset(asset, 0));
544 shared_ptr<dcp::Reel> reel(new dcp::Reel());
545 reel->add (reel_asset);
546 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
548 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
550 dcp->write_xml (dcp::SMPTE);
554 /* DCP with an over-sized JPEG2000 frame */
555 BOOST_AUTO_TEST_CASE (verify_test15)
557 int const too_big = 1302083 * 2;
559 /* Compress a black image */
560 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
561 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
562 BOOST_REQUIRE (frame.size() < too_big);
564 /* Place it in a bigger block with some zero padding at the end */
565 dcp::ArrayData oversized_frame(too_big);
566 memcpy (oversized_frame.data(), frame.data(), frame.size());
567 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
569 boost::filesystem::path const dir("build/test/verify_test15");
570 boost::filesystem::remove_all (dir);
571 dcp_from_frame (oversized_frame, dir);
573 vector<boost::filesystem::path> dirs;
574 dirs.push_back (dir);
575 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
576 BOOST_REQUIRE_EQUAL (notes.size(), 1);
577 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE);
581 /* DCP with a nearly over-sized JPEG2000 frame */
582 BOOST_AUTO_TEST_CASE (verify_test16)
584 int const nearly_too_big = 1302083 * 0.98;
586 /* Compress a black image */
587 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
588 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
589 BOOST_REQUIRE (frame.size() < nearly_too_big);
591 /* Place it in a bigger block with some zero padding at the end */
592 dcp::ArrayData oversized_frame(nearly_too_big);
593 memcpy (oversized_frame.data(), frame.data(), frame.size());
594 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
596 boost::filesystem::path const dir("build/test/verify_test16");
597 boost::filesystem::remove_all (dir);
598 dcp_from_frame (oversized_frame, dir);
600 vector<boost::filesystem::path> dirs;
601 dirs.push_back (dir);
602 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
603 BOOST_REQUIRE_EQUAL (notes.size(), 1);
604 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE);
608 /* DCP with a within-range JPEG2000 frame */
609 BOOST_AUTO_TEST_CASE (verify_test17)
611 /* Compress a black image */
612 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
613 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
614 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
616 boost::filesystem::path const dir("build/test/verify_test17");
617 boost::filesystem::remove_all (dir);
618 dcp_from_frame (frame, dir);
620 vector<boost::filesystem::path> dirs;
621 dirs.push_back (dir);
622 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
623 BOOST_REQUIRE_EQUAL (notes.size(), 0);
627 /* DCP with valid Interop subtitles */
628 BOOST_AUTO_TEST_CASE (verify_test18)
630 boost::filesystem::path const dir("build/test/verify_test18");
631 prepare_directory (dir);
632 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
633 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
634 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
635 shared_ptr<dcp::Reel> reel(new dcp::Reel());
636 reel->add (reel_asset);
637 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
639 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
641 dcp->write_xml (dcp::INTEROP);
643 vector<boost::filesystem::path> dirs;
644 dirs.push_back (dir);
645 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
646 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
647 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
648 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
652 /* DCP with broken Interop subtitles */
653 BOOST_AUTO_TEST_CASE (verify_test19)
655 boost::filesystem::path const dir("build/test/verify_test19");
656 prepare_directory (dir);
657 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
658 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
659 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
660 shared_ptr<dcp::Reel> reel(new dcp::Reel());
661 reel->add (reel_asset);
662 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
664 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
666 dcp->write_xml (dcp::INTEROP);
669 Editor e (dir / "subs.xml");
670 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
673 vector<boost::filesystem::path> dirs;
674 dirs.push_back (dir);
675 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
676 BOOST_REQUIRE_EQUAL (notes.size(), 3);
677 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
678 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
680 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
682 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
687 /* DCP with valid SMPTE subtitles */
688 BOOST_AUTO_TEST_CASE (verify_test20)
690 boost::filesystem::path const dir("build/test/verify_test20");
691 prepare_directory (dir);
692 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
693 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
694 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
695 shared_ptr<dcp::Reel> reel(new dcp::Reel());
696 reel->add (reel_asset);
697 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
699 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
701 dcp->write_xml (dcp::SMPTE);
703 vector<boost::filesystem::path> dirs;
704 dirs.push_back (dir);
705 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
707 BOOST_REQUIRE_EQUAL (notes.size(), 0);
711 /* DCP with broken SMPTE subtitles */
712 BOOST_AUTO_TEST_CASE (verify_test21)
714 boost::filesystem::path const dir("build/test/verify_test21");
715 prepare_directory (dir);
716 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
717 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
718 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
719 shared_ptr<dcp::Reel> reel(new dcp::Reel());
720 reel->add (reel_asset);
721 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
723 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
725 dcp->write_xml (dcp::SMPTE);
727 vector<boost::filesystem::path> dirs;
728 dirs.push_back (dir);
729 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
730 BOOST_REQUIRE_EQUAL (notes.size(), 2);
731 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
732 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
737 BOOST_AUTO_TEST_CASE (verify_test22)
739 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
740 prepare_directory (ov_dir);
742 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
743 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
744 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
745 dcp_from_frame (frame, ov_dir);
747 dcp::DCP ov (ov_dir);
750 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
751 prepare_directory (vf_dir);
753 shared_ptr<dcp::Reel> reel(new dcp::Reel());
754 reel->add (ov.cpls().front()->reels().front()->main_picture());
755 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
757 dcp::DCP vf (vf_dir);
759 vf.write_xml (dcp::SMPTE);
761 vector<boost::filesystem::path> dirs;
762 dirs.push_back (vf_dir);
763 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
764 BOOST_REQUIRE_EQUAL (notes.size(), 1);
765 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::EXTERNAL_ASSET);
769 /* DCP with valid CompositionMetadataAsset */
770 BOOST_AUTO_TEST_CASE (verify_test23)
772 boost::filesystem::path const dir("build/test/verify_test23");
773 prepare_directory (dir);
775 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
776 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
777 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
779 shared_ptr<dcp::Reel> reel(new dcp::Reel());
780 reel->add (reel_asset);
781 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
783 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
784 cpl->set_main_sound_sample_rate (48000);
785 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
786 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
790 dcp.write_xml (dcp::SMPTE);
792 vector<boost::filesystem::path> dirs;
793 dirs.push_back (dir);
794 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
798 boost::filesystem::path find_cpl (boost::filesystem::path dir)
800 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); i++) {
801 if (boost::starts_with(i->path().filename().string(), "cpl_")) {
806 BOOST_REQUIRE (false);
807 return boost::filesystem::path();
811 /* DCP with invalid CompositionMetadataAsset */
812 BOOST_AUTO_TEST_CASE (verify_test24)
814 boost::filesystem::path const dir("build/test/verify_test24");
815 prepare_directory (dir);
817 shared_ptr<dcp::Reel> reel(new dcp::Reel());
818 reel->add (black_picture_asset(dir));
819 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
821 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
822 cpl->set_main_sound_sample_rate (48000);
823 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
824 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
828 dcp.write_xml (dcp::SMPTE);
831 Editor e (find_cpl("build/test/verify_test24"));
832 e.replace ("MainSound", "MainSoundX");
835 vector<boost::filesystem::path> dirs;
836 dirs.push_back (dir);
837 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
838 BOOST_REQUIRE_EQUAL (notes.size(), 4);
840 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
841 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
843 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
845 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
847 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
852 /* DCP with invalid CompositionMetadataAsset */
853 BOOST_AUTO_TEST_CASE (verify_test25)
855 boost::filesystem::path const dir("build/test/verify_test25");
856 prepare_directory (dir);
858 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
859 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
860 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
862 shared_ptr<dcp::Reel> reel(new dcp::Reel());
863 reel->add (reel_asset);
864 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
866 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
867 cpl->set_main_sound_sample_rate (48000);
868 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
869 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
873 dcp.write_xml (dcp::SMPTE);
876 Editor e (find_cpl("build/test/verify_test25"));
877 e.replace ("</MainPictureActiveArea>", "</MainPictureActiveArea><BadTag></BadTag>");
880 vector<boost::filesystem::path> dirs;
881 dirs.push_back (dir);
882 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
886 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
887 BOOST_AUTO_TEST_CASE (verify_test26)
889 boost::filesystem::path const dir("build/test/verify_test26");
890 prepare_directory (dir);
891 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
892 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
893 asset->_language = "wrong-andbad";
894 asset->write (dir / "subs.mxf");
895 shared_ptr<dcp::ReelSubtitleAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
896 reel_asset->_language = "badlang";
897 shared_ptr<dcp::Reel> reel(new dcp::Reel());
898 reel->add (reel_asset);
899 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
901 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
903 dcp->write_xml (dcp::SMPTE);
905 vector<boost::filesystem::path> dirs;
906 dirs.push_back (dir);
907 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
908 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
909 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
910 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
911 BOOST_REQUIRE (i->note());
912 BOOST_CHECK_EQUAL (*i->note(), "badlang");
914 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
915 BOOST_REQUIRE (i->note());
916 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
920 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
921 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_languages)
923 boost::filesystem::path const dir("build/test/verify_invalid_closed_caption_languages");
924 prepare_directory (dir);
925 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
926 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
927 asset->_language = "wrong-andbad";
928 asset->write (dir / "subs.mxf");
929 shared_ptr<dcp::ReelClosedCaptionAsset> reel_asset(new dcp::ReelClosedCaptionAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
930 reel_asset->_language = "badlang";
931 shared_ptr<dcp::Reel> reel(new dcp::Reel());
932 reel->add (reel_asset);
933 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
935 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
937 dcp->write_xml (dcp::SMPTE);
939 vector<boost::filesystem::path> dirs;
940 dirs.push_back (dir);
941 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
942 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
943 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
944 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
945 BOOST_REQUIRE (i->note());
946 BOOST_CHECK_EQUAL (*i->note(), "badlang");
948 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
949 BOOST_REQUIRE (i->note());
950 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
954 /* SMPTE DCP with invalid <Language> in the MainSound reel and in the CPL additional subtitles languages */
955 BOOST_AUTO_TEST_CASE (verify_invalid_sound_reel_and_additional_language)
957 boost::filesystem::path const dir("build/test/verify_invalid_sound_reel_and_additional_language");
958 prepare_directory (dir);
960 shared_ptr<dcp::MonoPictureAsset> picture = simple_picture (dir, "foo");
961 shared_ptr<dcp::ReelPictureAsset> reel_picture(new dcp::ReelMonoPictureAsset(picture, 0));
962 shared_ptr<dcp::Reel> reel(new dcp::Reel());
963 reel->add (reel_picture);
964 shared_ptr<dcp::SoundAsset> sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
965 shared_ptr<dcp::ReelSoundAsset> reel_sound(new dcp::ReelSoundAsset(sound, 0));
966 reel->add (reel_sound);
967 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
969 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
970 cpl->_additional_subtitle_languages.push_back("andso-is-this");
971 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
972 cpl->set_main_sound_sample_rate (48000);
973 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
974 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
975 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
977 dcp->write_xml (dcp::SMPTE);
979 vector<boost::filesystem::path> dirs;
980 dirs.push_back (dir);
981 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
982 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
983 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
984 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
985 BOOST_REQUIRE (i->note());
986 BOOST_CHECK_EQUAL (*i->note(), "this-is-wrong");
988 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
989 BOOST_REQUIRE (i->note());
990 BOOST_CHECK_EQUAL (*i->note(), "andso-is-this");
992 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
993 BOOST_REQUIRE (i->note());
994 BOOST_CHECK_EQUAL (*i->note(), "frobozz");