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"
41 #include "openjpeg_image.h"
42 #include "mono_picture_asset.h"
43 #include "mono_picture_asset_writer.h"
44 #include "interop_subtitle_asset.h"
45 #include "smpte_subtitle_asset.h"
46 #include "reel_subtitle_asset.h"
47 #include "compose.hpp"
49 #include <boost/test/unit_test.hpp>
50 #include <boost/foreach.hpp>
51 #include <boost/algorithm/string.hpp>
60 using boost::optional;
61 using std::shared_ptr;
64 static list<pair<string, optional<boost::filesystem::path> > > stages;
67 stage (string s, optional<boost::filesystem::path> p)
69 stages.push_back (make_pair (s, p));
79 prepare_directory (boost::filesystem::path path)
81 using namespace boost::filesystem;
83 create_directories (path);
87 static vector<boost::filesystem::path>
88 setup (int reference_number, int verify_test_number)
90 prepare_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
91 for (boost::filesystem::directory_iterator i(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number)); i != boost::filesystem::directory_iterator(); ++i) {
92 boost::filesystem::copy_file (i->path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i->path().filename());
95 vector<boost::filesystem::path> directories;
96 directories.push_back (dcp::String::compose("build/test/verify_test%1", verify_test_number));
102 /** Class that can alter a file by searching and replacing strings within it.
103 * On destruction modifies the file whose name was given to the constructor.
108 Editor (boost::filesystem::path path)
111 _content = dcp::file_to_string (_path);
116 FILE* f = fopen(_path.string().c_str(), "w");
118 fwrite (_content.c_str(), _content.length(), 1, f);
122 void replace (string a, string b)
124 boost::algorithm::replace_all (_content, a, b);
128 boost::filesystem::path _path;
129 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";
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 PKL");
180 BOOST_REQUIRE (st->second);
181 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
183 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
184 BOOST_REQUIRE (st->second);
185 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
187 BOOST_REQUIRE (st == stages.end());
189 BOOST_CHECK_EQUAL (notes.size(), 0);
192 /* Corrupt the MXFs and check that this is spotted */
193 BOOST_AUTO_TEST_CASE (verify_test2)
195 vector<boost::filesystem::path> directories = setup (1, 2);
197 FILE* mod = fopen("build/test/verify_test2/video.mxf", "r+b");
199 fseek (mod, 4096, SEEK_SET);
201 fwrite (&x, sizeof(x), 1, mod);
204 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
206 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
207 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
210 list<dcp::VerificationNote> notes;
212 dcp::ASDCPErrorSuspender sus;
213 notes = dcp::verify (directories, &stage, &progress, xsd_test);
216 BOOST_REQUIRE_EQUAL (notes.size(), 2);
217 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
218 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_HASH_INCORRECT);
219 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_ERROR);
220 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::SOUND_HASH_INCORRECT);
223 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
224 BOOST_AUTO_TEST_CASE (verify_test3)
226 vector<boost::filesystem::path> directories = setup (1, 3);
229 Editor e ("build/test/verify_test3/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml");
230 e.replace ("<Hash>", "<Hash>x");
233 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
235 BOOST_REQUIRE_EQUAL (notes.size(), 6);
236 list<dcp::VerificationNote>::const_iterator i = notes.begin();
237 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
238 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
240 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
241 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DISAGREE);
243 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
244 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DISAGREE);
246 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
247 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
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);
257 /* Corrupt the ContentKind in the CPL */
258 BOOST_AUTO_TEST_CASE (verify_test4)
260 vector<boost::filesystem::path> directories = setup (1, 4);
263 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
264 e.replace ("<ContentKind>", "<ContentKind>x");
267 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
269 BOOST_REQUIRE_EQUAL (notes.size(), 1);
270 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
271 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xfeature'");
275 boost::filesystem::path
278 return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
282 boost::filesystem::path
285 return dcp::String::compose("build/test/verify_test%1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml", n);
289 boost::filesystem::path
292 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
296 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1)
298 vector<boost::filesystem::path> directories = setup (1, n);
302 e.replace (from, to);
305 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
307 BOOST_REQUIRE_EQUAL (notes.size(), 1);
308 BOOST_CHECK_EQUAL (notes.front().code(), code1);
312 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)
314 vector<boost::filesystem::path> directories = setup (1, n);
318 e.replace (from, to);
321 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
323 BOOST_REQUIRE_EQUAL (notes.size(), 2);
324 BOOST_CHECK_EQUAL (notes.front().code(), code1);
325 BOOST_CHECK_EQUAL (notes.back().code(), code2);
329 void check_after_replace (
330 int n, boost::function<boost::filesystem::path (int)> file,
333 dcp::VerificationNote::Code code1,
334 dcp::VerificationNote::Code code2,
335 dcp::VerificationNote::Code code3
338 vector<boost::filesystem::path> directories = setup (1, n);
342 e.replace (from, to);
345 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
347 BOOST_REQUIRE_EQUAL (notes.size(), 3);
348 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
349 BOOST_CHECK_EQUAL (i->code(), code1);
351 BOOST_CHECK_EQUAL (i->code(), code2);
353 BOOST_CHECK_EQUAL (i->code(), code3);
357 BOOST_AUTO_TEST_CASE (verify_test5)
359 check_after_replace (
361 "<FrameRate>24 1", "<FrameRate>99 1",
362 dcp::VerificationNote::CPL_HASH_INCORRECT,
363 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE
368 BOOST_AUTO_TEST_CASE (verify_test6)
370 vector<boost::filesystem::path> directories = setup (1, 6);
372 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
373 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
375 BOOST_REQUIRE_EQUAL (notes.size(), 1);
376 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
377 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_ASSET);
381 boost::filesystem::path
384 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
387 /* Empty asset filename in ASSETMAP */
388 BOOST_AUTO_TEST_CASE (verify_test7)
390 check_after_replace (
392 "<Path>video.mxf</Path>", "<Path></Path>",
393 dcp::VerificationNote::EMPTY_ASSET_PATH
397 /* Mismatched standard */
398 BOOST_AUTO_TEST_CASE (verify_test8)
400 check_after_replace (
402 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
403 dcp::VerificationNote::MISMATCHED_STANDARD,
404 dcp::VerificationNote::XML_VALIDATION_ERROR,
405 dcp::VerificationNote::CPL_HASH_INCORRECT
409 /* Badly formatted <Id> in CPL */
410 BOOST_AUTO_TEST_CASE (verify_test9)
412 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
413 check_after_replace (
415 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
416 dcp::VerificationNote::XML_VALIDATION_ERROR
420 /* Badly formatted <IssueDate> in CPL */
421 BOOST_AUTO_TEST_CASE (verify_test10)
423 check_after_replace (
425 "<IssueDate>", "<IssueDate>x",
426 dcp::VerificationNote::XML_VALIDATION_ERROR,
427 dcp::VerificationNote::CPL_HASH_INCORRECT
431 /* Badly-formatted <Id> in PKL */
432 BOOST_AUTO_TEST_CASE (verify_test11)
434 check_after_replace (
436 "<Id>urn:uuid:cd4", "<Id>urn:uuid:xd4",
437 dcp::VerificationNote::XML_VALIDATION_ERROR
441 /* Badly-formatted <Id> in ASSETMAP */
442 BOOST_AUTO_TEST_CASE (verify_test12)
444 check_after_replace (
446 "<Id>urn:uuid:63c", "<Id>urn:uuid:x3c",
447 dcp::VerificationNote::XML_VALIDATION_ERROR
451 /* Basic test of an Interop DCP */
452 BOOST_AUTO_TEST_CASE (verify_test13)
455 vector<boost::filesystem::path> directories = setup (3, 13);
456 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
458 boost::filesystem::path const cpl_file = "build/test/verify_test13/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
459 boost::filesystem::path const pkl_file = "build/test/verify_test13/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
460 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
462 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
463 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
464 BOOST_REQUIRE (st->second);
465 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
467 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
468 BOOST_REQUIRE (st->second);
469 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
471 BOOST_CHECK_EQUAL (st->first, "Checking reel");
472 BOOST_REQUIRE (!st->second);
474 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
475 BOOST_REQUIRE (st->second);
476 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
478 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
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 sound asset hash");
483 BOOST_REQUIRE (st->second);
484 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
486 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
487 BOOST_REQUIRE (st->second);
488 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
490 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
491 BOOST_REQUIRE (st->second);
492 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
494 BOOST_REQUIRE (st == stages.end());
496 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
497 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
498 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
499 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
502 /* DCP with a short asset */
503 BOOST_AUTO_TEST_CASE (verify_test14)
505 vector<boost::filesystem::path> directories = setup (8, 14);
506 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
508 BOOST_REQUIRE_EQUAL (notes.size(), 5);
509 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
510 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
512 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
514 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
516 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
518 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
525 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
527 shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
528 boost::filesystem::create_directories (dir);
529 shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
530 for (int i = 0; i < 24; ++i) {
531 writer->write (frame.data(), frame.size());
535 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelMonoPictureAsset(asset, 0));
536 shared_ptr<dcp::Reel> reel(new dcp::Reel());
537 reel->add (reel_asset);
538 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
540 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
542 dcp->write_xml (dcp::SMPTE);
546 /* DCP with an over-sized JPEG2000 frame */
547 BOOST_AUTO_TEST_CASE (verify_test15)
549 int const too_big = 1302083 * 2;
551 /* Compress a black image */
552 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
553 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
554 BOOST_REQUIRE (frame.size() < too_big);
556 /* Place it in a bigger block with some zero padding at the end */
557 dcp::ArrayData oversized_frame(too_big);
558 memcpy (oversized_frame.data(), frame.data(), frame.size());
559 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
561 boost::filesystem::path const dir("build/test/verify_test15");
562 boost::filesystem::remove_all (dir);
563 dcp_from_frame (oversized_frame, dir);
565 vector<boost::filesystem::path> dirs;
566 dirs.push_back (dir);
567 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
568 BOOST_REQUIRE_EQUAL (notes.size(), 1);
569 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE);
573 /* DCP with a nearly over-sized JPEG2000 frame */
574 BOOST_AUTO_TEST_CASE (verify_test16)
576 int const nearly_too_big = 1302083 * 0.98;
578 /* Compress a black image */
579 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
580 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
581 BOOST_REQUIRE (frame.size() < nearly_too_big);
583 /* Place it in a bigger block with some zero padding at the end */
584 dcp::ArrayData oversized_frame(nearly_too_big);
585 memcpy (oversized_frame.data(), frame.data(), frame.size());
586 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
588 boost::filesystem::path const dir("build/test/verify_test16");
589 boost::filesystem::remove_all (dir);
590 dcp_from_frame (oversized_frame, dir);
592 vector<boost::filesystem::path> dirs;
593 dirs.push_back (dir);
594 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
595 BOOST_REQUIRE_EQUAL (notes.size(), 1);
596 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE);
600 /* DCP with a within-range JPEG2000 frame */
601 BOOST_AUTO_TEST_CASE (verify_test17)
603 /* Compress a black image */
604 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
605 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
606 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
608 boost::filesystem::path const dir("build/test/verify_test17");
609 boost::filesystem::remove_all (dir);
610 dcp_from_frame (frame, dir);
612 vector<boost::filesystem::path> dirs;
613 dirs.push_back (dir);
614 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
615 BOOST_REQUIRE_EQUAL (notes.size(), 0);
619 /* DCP with valid Interop subtitles */
620 BOOST_AUTO_TEST_CASE (verify_test18)
622 boost::filesystem::path const dir("build/test/verify_test18");
623 prepare_directory (dir);
624 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
625 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
626 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
627 shared_ptr<dcp::Reel> reel(new dcp::Reel());
628 reel->add (reel_asset);
629 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
631 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
633 dcp->write_xml (dcp::INTEROP);
635 vector<boost::filesystem::path> dirs;
636 dirs.push_back (dir);
637 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
638 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
639 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
640 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
644 /* DCP with broken Interop subtitles */
645 BOOST_AUTO_TEST_CASE (verify_test19)
647 boost::filesystem::path const dir("build/test/verify_test19");
648 prepare_directory (dir);
649 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
650 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
651 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
652 shared_ptr<dcp::Reel> reel(new dcp::Reel());
653 reel->add (reel_asset);
654 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
656 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
658 dcp->write_xml (dcp::INTEROP);
661 Editor e (dir / "subs.xml");
662 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
665 vector<boost::filesystem::path> dirs;
666 dirs.push_back (dir);
667 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
668 BOOST_REQUIRE_EQUAL (notes.size(), 3);
669 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
670 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
672 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
674 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
679 /* DCP with valid SMPTE subtitles */
680 BOOST_AUTO_TEST_CASE (verify_test20)
682 boost::filesystem::path const dir("build/test/verify_test20");
683 prepare_directory (dir);
684 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
685 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
686 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
687 shared_ptr<dcp::Reel> reel(new dcp::Reel());
688 reel->add (reel_asset);
689 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
691 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
693 dcp->write_xml (dcp::SMPTE);
695 vector<boost::filesystem::path> dirs;
696 dirs.push_back (dir);
697 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
698 BOOST_REQUIRE_EQUAL (notes.size(), 0);
702 /* DCP with broken SMPTE subtitles */
703 BOOST_AUTO_TEST_CASE (verify_test21)
705 boost::filesystem::path const dir("build/test/verify_test21");
706 prepare_directory (dir);
707 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
708 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
709 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
710 shared_ptr<dcp::Reel> reel(new dcp::Reel());
711 reel->add (reel_asset);
712 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
714 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
716 dcp->write_xml (dcp::SMPTE);
718 vector<boost::filesystem::path> dirs;
719 dirs.push_back (dir);
720 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
721 BOOST_REQUIRE_EQUAL (notes.size(), 2);
722 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
723 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
728 BOOST_AUTO_TEST_CASE (verify_test22)
730 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
731 prepare_directory (ov_dir);
733 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
734 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
735 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
736 dcp_from_frame (frame, ov_dir);
738 dcp::DCP ov (ov_dir);
741 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
742 prepare_directory (vf_dir);
744 shared_ptr<dcp::Reel> reel(new dcp::Reel());
745 reel->add (ov.cpls().front()->reels().front()->main_picture());
746 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
748 dcp::DCP vf (vf_dir);
750 vf.write_xml (dcp::SMPTE);
752 vector<boost::filesystem::path> dirs;
753 dirs.push_back (vf_dir);
754 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
755 BOOST_REQUIRE_EQUAL (notes.size(), 1);
756 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::EXTERNAL_ASSET);
760 /* DCP with valid CompositionMetadataAsset */
761 BOOST_AUTO_TEST_CASE (verify_test23)
763 boost::filesystem::path const dir("build/test/verify_test23");
764 prepare_directory (dir);
766 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
767 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
768 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
770 shared_ptr<dcp::Reel> reel(new dcp::Reel());
771 reel->add (reel_asset);
772 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
774 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
775 cpl->set_main_sound_sample_rate (48000);
776 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
777 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
781 dcp.write_xml (dcp::SMPTE);
783 vector<boost::filesystem::path> dirs;
784 dirs.push_back (dir);
785 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
789 boost::filesystem::path find_cpl (boost::filesystem::path dir)
791 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); i++) {
792 if (boost::starts_with(i->path().filename().string(), "cpl_")) {
797 BOOST_REQUIRE (false);
798 return boost::filesystem::path();
802 /* DCP with invalid CompositionMetadataAsset */
803 BOOST_AUTO_TEST_CASE (verify_test24)
805 boost::filesystem::path const dir("build/test/verify_test24");
806 prepare_directory (dir);
808 shared_ptr<dcp::Reel> reel(new dcp::Reel());
809 reel->add (black_picture_asset(dir));
810 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
812 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
813 cpl->set_main_sound_sample_rate (48000);
814 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
815 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
819 dcp.write_xml (dcp::SMPTE);
822 Editor e (find_cpl("build/test/verify_test24"));
823 e.replace ("MainSound", "MainSoundX");
826 vector<boost::filesystem::path> dirs;
827 dirs.push_back (dir);
828 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
829 BOOST_REQUIRE_EQUAL (notes.size(), 4);
831 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
832 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
834 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
836 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
838 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
843 /* DCP with invalid CompositionMetadataAsset */
844 BOOST_AUTO_TEST_CASE (verify_test25)
846 boost::filesystem::path const dir("build/test/verify_test25");
847 prepare_directory (dir);
849 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
850 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
851 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
853 shared_ptr<dcp::Reel> reel(new dcp::Reel());
854 reel->add (reel_asset);
855 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
857 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
858 cpl->set_main_sound_sample_rate (48000);
859 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
860 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
864 dcp.write_xml (dcp::SMPTE);
867 Editor e (find_cpl("build/test/verify_test25"));
868 e.replace ("</MainPictureActiveArea>", "</MainPictureActiveArea><BadTag></BadTag>");
871 vector<boost::filesystem::path> dirs;
872 dirs.push_back (dir);
873 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
877 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
878 BOOST_AUTO_TEST_CASE (verify_test26)
880 boost::filesystem::path const dir("build/test/verify_test26");
881 prepare_directory (dir);
882 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
883 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
884 asset->_language = "wrong-andbad";
885 asset->write (dir / "subs.mxf");
886 shared_ptr<dcp::ReelSubtitleAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
887 reel_asset->_language = "badlang";
888 shared_ptr<dcp::Reel> reel(new dcp::Reel());
889 reel->add (reel_asset);
890 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
892 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
894 dcp->write_xml (dcp::SMPTE);
896 vector<boost::filesystem::path> dirs;
897 dirs.push_back (dir);
898 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
899 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
900 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
901 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
902 BOOST_REQUIRE (i->note());
903 BOOST_CHECK_EQUAL (*i->note(), "badlang");
905 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
906 BOOST_REQUIRE (i->note());
907 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");