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_subtitle_asset.h"
48 #include "compose.hpp"
50 #include <boost/test/unit_test.hpp>
51 #include <boost/foreach.hpp>
52 #include <boost/algorithm/string.hpp>
61 using boost::optional;
62 using std::shared_ptr;
65 static list<pair<string, optional<boost::filesystem::path> > > stages;
68 stage (string s, optional<boost::filesystem::path> p)
70 stages.push_back (make_pair (s, p));
80 prepare_directory (boost::filesystem::path path)
82 using namespace boost::filesystem;
84 create_directories (path);
88 static vector<boost::filesystem::path>
89 setup (int reference_number, int verify_test_number)
91 prepare_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
92 for (boost::filesystem::directory_iterator i(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number)); i != boost::filesystem::directory_iterator(); ++i) {
93 boost::filesystem::copy_file (i->path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i->path().filename());
96 vector<boost::filesystem::path> directories;
97 directories.push_back (dcp::String::compose("build/test/verify_test%1", verify_test_number));
103 /** Class that can alter a file by searching and replacing strings within it.
104 * On destruction modifies the file whose name was given to the constructor.
109 Editor (boost::filesystem::path path)
112 _content = dcp::file_to_string (_path);
117 FILE* f = fopen(_path.string().c_str(), "w");
119 fwrite (_content.c_str(), _content.length(), 1, f);
123 void replace (string a, string b)
125 boost::algorithm::replace_all (_content, a, b);
129 boost::filesystem::path _path;
130 std::string _content;
136 dump_notes (list<dcp::VerificationNote> const & notes)
138 BOOST_FOREACH (dcp::VerificationNote i, notes) {
139 std::cout << dcp::note_to_string(i) << "\n";
143 /* Check DCP as-is (should be OK) */
144 BOOST_AUTO_TEST_CASE (verify_test1)
147 vector<boost::filesystem::path> directories = setup (1, 1);
148 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
150 boost::filesystem::path const cpl_file = "build/test/verify_test1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
151 boost::filesystem::path const pkl_file = "build/test/verify_test1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml";
152 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
154 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
155 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
156 BOOST_REQUIRE (st->second);
157 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
159 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
160 BOOST_REQUIRE (st->second);
161 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
163 BOOST_CHECK_EQUAL (st->first, "Checking reel");
164 BOOST_REQUIRE (!st->second);
166 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
167 BOOST_REQUIRE (st->second);
168 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
170 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
171 BOOST_REQUIRE (st->second);
172 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
174 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
175 BOOST_REQUIRE (st->second);
176 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
178 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
179 BOOST_REQUIRE (st->second);
180 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
182 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
183 BOOST_REQUIRE (st->second);
184 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
186 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
187 BOOST_REQUIRE (st->second);
188 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
190 BOOST_REQUIRE (st == stages.end());
192 BOOST_CHECK_EQUAL (notes.size(), 0);
195 /* Corrupt the MXFs and check that this is spotted */
196 BOOST_AUTO_TEST_CASE (verify_test2)
198 vector<boost::filesystem::path> directories = setup (1, 2);
200 FILE* mod = fopen("build/test/verify_test2/video.mxf", "r+b");
202 fseek (mod, 4096, SEEK_SET);
204 fwrite (&x, sizeof(x), 1, mod);
207 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
209 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
210 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
213 list<dcp::VerificationNote> notes;
215 dcp::ASDCPErrorSuspender sus;
216 notes = dcp::verify (directories, &stage, &progress, xsd_test);
219 BOOST_REQUIRE_EQUAL (notes.size(), 2);
220 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
221 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_HASH_INCORRECT);
222 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_ERROR);
223 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::SOUND_HASH_INCORRECT);
226 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
227 BOOST_AUTO_TEST_CASE (verify_test3)
229 vector<boost::filesystem::path> directories = setup (1, 3);
232 Editor e ("build/test/verify_test3/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml");
233 e.replace ("<Hash>", "<Hash>x");
236 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
238 BOOST_REQUIRE_EQUAL (notes.size(), 6);
239 list<dcp::VerificationNote>::const_iterator i = notes.begin();
240 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
241 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
243 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
244 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DISAGREE);
246 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
247 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DISAGREE);
249 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
250 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
252 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
253 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
255 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
256 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
260 /* Corrupt the ContentKind in the CPL */
261 BOOST_AUTO_TEST_CASE (verify_test4)
263 vector<boost::filesystem::path> directories = setup (1, 4);
266 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
267 e.replace ("<ContentKind>", "<ContentKind>x");
270 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
272 BOOST_REQUIRE_EQUAL (notes.size(), 1);
273 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
274 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xfeature'");
278 boost::filesystem::path
281 return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
285 boost::filesystem::path
288 return dcp::String::compose("build/test/verify_test%1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml", n);
292 boost::filesystem::path
295 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
299 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1)
301 vector<boost::filesystem::path> directories = setup (1, n);
305 e.replace (from, to);
308 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
310 BOOST_REQUIRE_EQUAL (notes.size(), 1);
311 BOOST_CHECK_EQUAL (notes.front().code(), code1);
315 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)
317 vector<boost::filesystem::path> directories = setup (1, n);
321 e.replace (from, to);
324 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
326 BOOST_REQUIRE_EQUAL (notes.size(), 2);
327 BOOST_CHECK_EQUAL (notes.front().code(), code1);
328 BOOST_CHECK_EQUAL (notes.back().code(), code2);
332 void check_after_replace (
333 int n, boost::function<boost::filesystem::path (int)> file,
336 dcp::VerificationNote::Code code1,
337 dcp::VerificationNote::Code code2,
338 dcp::VerificationNote::Code code3
341 vector<boost::filesystem::path> directories = setup (1, n);
345 e.replace (from, to);
348 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
350 BOOST_REQUIRE_EQUAL (notes.size(), 3);
351 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
352 BOOST_CHECK_EQUAL (i->code(), code1);
354 BOOST_CHECK_EQUAL (i->code(), code2);
356 BOOST_CHECK_EQUAL (i->code(), code3);
360 BOOST_AUTO_TEST_CASE (verify_test5)
362 check_after_replace (
364 "<FrameRate>24 1", "<FrameRate>99 1",
365 dcp::VerificationNote::CPL_HASH_INCORRECT,
366 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE
371 BOOST_AUTO_TEST_CASE (verify_test6)
373 vector<boost::filesystem::path> directories = setup (1, 6);
375 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
376 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
378 BOOST_REQUIRE_EQUAL (notes.size(), 1);
379 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
380 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_ASSET);
384 boost::filesystem::path
387 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
390 /* Empty asset filename in ASSETMAP */
391 BOOST_AUTO_TEST_CASE (verify_test7)
393 check_after_replace (
395 "<Path>video.mxf</Path>", "<Path></Path>",
396 dcp::VerificationNote::EMPTY_ASSET_PATH
400 /* Mismatched standard */
401 BOOST_AUTO_TEST_CASE (verify_test8)
403 check_after_replace (
405 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
406 dcp::VerificationNote::MISMATCHED_STANDARD,
407 dcp::VerificationNote::XML_VALIDATION_ERROR,
408 dcp::VerificationNote::CPL_HASH_INCORRECT
412 /* Badly formatted <Id> in CPL */
413 BOOST_AUTO_TEST_CASE (verify_test9)
415 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
416 check_after_replace (
418 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
419 dcp::VerificationNote::XML_VALIDATION_ERROR
423 /* Badly formatted <IssueDate> in CPL */
424 BOOST_AUTO_TEST_CASE (verify_test10)
426 check_after_replace (
428 "<IssueDate>", "<IssueDate>x",
429 dcp::VerificationNote::XML_VALIDATION_ERROR,
430 dcp::VerificationNote::CPL_HASH_INCORRECT
434 /* Badly-formatted <Id> in PKL */
435 BOOST_AUTO_TEST_CASE (verify_test11)
437 check_after_replace (
439 "<Id>urn:uuid:cd4", "<Id>urn:uuid:xd4",
440 dcp::VerificationNote::XML_VALIDATION_ERROR
444 /* Badly-formatted <Id> in ASSETMAP */
445 BOOST_AUTO_TEST_CASE (verify_test12)
447 check_after_replace (
449 "<Id>urn:uuid:63c", "<Id>urn:uuid:x3c",
450 dcp::VerificationNote::XML_VALIDATION_ERROR
454 /* Basic test of an Interop DCP */
455 BOOST_AUTO_TEST_CASE (verify_test13)
458 vector<boost::filesystem::path> directories = setup (3, 13);
459 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
461 boost::filesystem::path const cpl_file = "build/test/verify_test13/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
462 boost::filesystem::path const pkl_file = "build/test/verify_test13/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
463 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
465 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
466 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
467 BOOST_REQUIRE (st->second);
468 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
470 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
471 BOOST_REQUIRE (st->second);
472 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
474 BOOST_CHECK_EQUAL (st->first, "Checking reel");
475 BOOST_REQUIRE (!st->second);
477 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
478 BOOST_REQUIRE (st->second);
479 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
481 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
482 BOOST_REQUIRE (st->second);
483 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
485 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
486 BOOST_REQUIRE (st->second);
487 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
489 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
490 BOOST_REQUIRE (st->second);
491 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
493 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
494 BOOST_REQUIRE (st->second);
495 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
497 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
498 BOOST_REQUIRE (st->second);
499 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
501 BOOST_REQUIRE (st == stages.end());
503 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
504 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
505 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
506 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
509 /* DCP with a short asset */
510 BOOST_AUTO_TEST_CASE (verify_test14)
512 vector<boost::filesystem::path> directories = setup (8, 14);
513 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
515 BOOST_REQUIRE_EQUAL (notes.size(), 5);
516 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
517 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
519 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
521 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
523 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
525 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
532 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
534 shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
535 boost::filesystem::create_directories (dir);
536 shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
537 for (int i = 0; i < 24; ++i) {
538 writer->write (frame.data(), frame.size());
542 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelMonoPictureAsset(asset, 0));
543 shared_ptr<dcp::Reel> reel(new dcp::Reel());
544 reel->add (reel_asset);
545 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
547 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
549 dcp->write_xml (dcp::SMPTE);
553 /* DCP with an over-sized JPEG2000 frame */
554 BOOST_AUTO_TEST_CASE (verify_test15)
556 int const too_big = 1302083 * 2;
558 /* Compress a black image */
559 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
560 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
561 BOOST_REQUIRE (frame.size() < too_big);
563 /* Place it in a bigger block with some zero padding at the end */
564 dcp::ArrayData oversized_frame(too_big);
565 memcpy (oversized_frame.data(), frame.data(), frame.size());
566 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
568 boost::filesystem::path const dir("build/test/verify_test15");
569 boost::filesystem::remove_all (dir);
570 dcp_from_frame (oversized_frame, dir);
572 vector<boost::filesystem::path> dirs;
573 dirs.push_back (dir);
574 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
575 BOOST_REQUIRE_EQUAL (notes.size(), 1);
576 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE);
580 /* DCP with a nearly over-sized JPEG2000 frame */
581 BOOST_AUTO_TEST_CASE (verify_test16)
583 int const nearly_too_big = 1302083 * 0.98;
585 /* Compress a black image */
586 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
587 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
588 BOOST_REQUIRE (frame.size() < nearly_too_big);
590 /* Place it in a bigger block with some zero padding at the end */
591 dcp::ArrayData oversized_frame(nearly_too_big);
592 memcpy (oversized_frame.data(), frame.data(), frame.size());
593 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
595 boost::filesystem::path const dir("build/test/verify_test16");
596 boost::filesystem::remove_all (dir);
597 dcp_from_frame (oversized_frame, dir);
599 vector<boost::filesystem::path> dirs;
600 dirs.push_back (dir);
601 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
602 BOOST_REQUIRE_EQUAL (notes.size(), 1);
603 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE);
607 /* DCP with a within-range JPEG2000 frame */
608 BOOST_AUTO_TEST_CASE (verify_test17)
610 /* Compress a black image */
611 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
612 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
613 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
615 boost::filesystem::path const dir("build/test/verify_test17");
616 boost::filesystem::remove_all (dir);
617 dcp_from_frame (frame, dir);
619 vector<boost::filesystem::path> dirs;
620 dirs.push_back (dir);
621 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
622 BOOST_REQUIRE_EQUAL (notes.size(), 0);
626 /* DCP with valid Interop subtitles */
627 BOOST_AUTO_TEST_CASE (verify_test18)
629 boost::filesystem::path const dir("build/test/verify_test18");
630 prepare_directory (dir);
631 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
632 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
633 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
634 shared_ptr<dcp::Reel> reel(new dcp::Reel());
635 reel->add (reel_asset);
636 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
638 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
640 dcp->write_xml (dcp::INTEROP);
642 vector<boost::filesystem::path> dirs;
643 dirs.push_back (dir);
644 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
645 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
646 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
647 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
651 /* DCP with broken Interop subtitles */
652 BOOST_AUTO_TEST_CASE (verify_test19)
654 boost::filesystem::path const dir("build/test/verify_test19");
655 prepare_directory (dir);
656 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
657 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
658 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
659 shared_ptr<dcp::Reel> reel(new dcp::Reel());
660 reel->add (reel_asset);
661 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
663 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
665 dcp->write_xml (dcp::INTEROP);
668 Editor e (dir / "subs.xml");
669 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
672 vector<boost::filesystem::path> dirs;
673 dirs.push_back (dir);
674 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
675 BOOST_REQUIRE_EQUAL (notes.size(), 3);
676 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
677 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
679 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
681 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
686 /* DCP with valid SMPTE subtitles */
687 BOOST_AUTO_TEST_CASE (verify_test20)
689 boost::filesystem::path const dir("build/test/verify_test20");
690 prepare_directory (dir);
691 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
692 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
693 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
694 shared_ptr<dcp::Reel> reel(new dcp::Reel());
695 reel->add (reel_asset);
696 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
698 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
700 dcp->write_xml (dcp::SMPTE);
702 vector<boost::filesystem::path> dirs;
703 dirs.push_back (dir);
704 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
706 BOOST_REQUIRE_EQUAL (notes.size(), 0);
710 /* DCP with broken SMPTE subtitles */
711 BOOST_AUTO_TEST_CASE (verify_test21)
713 boost::filesystem::path const dir("build/test/verify_test21");
714 prepare_directory (dir);
715 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
716 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
717 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
718 shared_ptr<dcp::Reel> reel(new dcp::Reel());
719 reel->add (reel_asset);
720 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
722 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
724 dcp->write_xml (dcp::SMPTE);
726 vector<boost::filesystem::path> dirs;
727 dirs.push_back (dir);
728 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
729 BOOST_REQUIRE_EQUAL (notes.size(), 2);
730 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
731 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
736 BOOST_AUTO_TEST_CASE (verify_test22)
738 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
739 prepare_directory (ov_dir);
741 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
742 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
743 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
744 dcp_from_frame (frame, ov_dir);
746 dcp::DCP ov (ov_dir);
749 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
750 prepare_directory (vf_dir);
752 shared_ptr<dcp::Reel> reel(new dcp::Reel());
753 reel->add (ov.cpls().front()->reels().front()->main_picture());
754 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
756 dcp::DCP vf (vf_dir);
758 vf.write_xml (dcp::SMPTE);
760 vector<boost::filesystem::path> dirs;
761 dirs.push_back (vf_dir);
762 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
763 BOOST_REQUIRE_EQUAL (notes.size(), 1);
764 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::EXTERNAL_ASSET);
768 /* DCP with valid CompositionMetadataAsset */
769 BOOST_AUTO_TEST_CASE (verify_test23)
771 boost::filesystem::path const dir("build/test/verify_test23");
772 prepare_directory (dir);
774 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
775 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
776 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
778 shared_ptr<dcp::Reel> reel(new dcp::Reel());
779 reel->add (reel_asset);
780 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
782 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
783 cpl->set_main_sound_sample_rate (48000);
784 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
785 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
789 dcp.write_xml (dcp::SMPTE);
791 vector<boost::filesystem::path> dirs;
792 dirs.push_back (dir);
793 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
797 boost::filesystem::path find_cpl (boost::filesystem::path dir)
799 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); i++) {
800 if (boost::starts_with(i->path().filename().string(), "cpl_")) {
805 BOOST_REQUIRE (false);
806 return boost::filesystem::path();
810 /* DCP with invalid CompositionMetadataAsset */
811 BOOST_AUTO_TEST_CASE (verify_test24)
813 boost::filesystem::path const dir("build/test/verify_test24");
814 prepare_directory (dir);
816 shared_ptr<dcp::Reel> reel(new dcp::Reel());
817 reel->add (black_picture_asset(dir));
818 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
820 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
821 cpl->set_main_sound_sample_rate (48000);
822 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
823 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
827 dcp.write_xml (dcp::SMPTE);
830 Editor e (find_cpl("build/test/verify_test24"));
831 e.replace ("MainSound", "MainSoundX");
834 vector<boost::filesystem::path> dirs;
835 dirs.push_back (dir);
836 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
837 BOOST_REQUIRE_EQUAL (notes.size(), 4);
839 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
840 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
842 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
844 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
846 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
851 /* DCP with invalid CompositionMetadataAsset */
852 BOOST_AUTO_TEST_CASE (verify_test25)
854 boost::filesystem::path const dir("build/test/verify_test25");
855 prepare_directory (dir);
857 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
858 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
859 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
861 shared_ptr<dcp::Reel> reel(new dcp::Reel());
862 reel->add (reel_asset);
863 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
865 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
866 cpl->set_main_sound_sample_rate (48000);
867 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
868 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
872 dcp.write_xml (dcp::SMPTE);
875 Editor e (find_cpl("build/test/verify_test25"));
876 e.replace ("</MainPictureActiveArea>", "</MainPictureActiveArea><BadTag></BadTag>");
879 vector<boost::filesystem::path> dirs;
880 dirs.push_back (dir);
881 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
885 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
886 BOOST_AUTO_TEST_CASE (verify_test26)
888 boost::filesystem::path const dir("build/test/verify_test26");
889 prepare_directory (dir);
890 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
891 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
892 asset->_language = "wrong-andbad";
893 asset->write (dir / "subs.mxf");
894 shared_ptr<dcp::ReelSubtitleAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
895 reel_asset->_language = "badlang";
896 shared_ptr<dcp::Reel> reel(new dcp::Reel());
897 reel->add (reel_asset);
898 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
900 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
902 dcp->write_xml (dcp::SMPTE);
904 vector<boost::filesystem::path> dirs;
905 dirs.push_back (dir);
906 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
907 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
908 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
909 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
910 BOOST_REQUIRE (i->note());
911 BOOST_CHECK_EQUAL (*i->note(), "badlang");
913 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
914 BOOST_REQUIRE (i->note());
915 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
919 /* SMPTE DCP with invalid <Language> in the MainSound reel */
920 BOOST_AUTO_TEST_CASE (verify_invalid_sound_reel_language)
922 boost::filesystem::path const dir("build/test/verify_invalid_sound_reel_language");
923 prepare_directory (dir);
925 shared_ptr<dcp::SoundAsset> sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
926 shared_ptr<dcp::ReelSoundAsset> reel_sound(new dcp::ReelSoundAsset(sound, 0));
927 shared_ptr<dcp::Reel> reel(new dcp::Reel());
928 reel->add (reel_sound);
929 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
931 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
933 dcp->write_xml (dcp::SMPTE);
935 vector<boost::filesystem::path> dirs;
936 dirs.push_back (dir);
937 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
938 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
939 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
940 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
941 BOOST_REQUIRE (i->note());
942 BOOST_CHECK_EQUAL (*i->note(), "frobozz");