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;
135 dump_notes (list<dcp::VerificationNote> const & notes)
137 BOOST_FOREACH (dcp::VerificationNote i, notes) {
138 std::cout << dcp::note_to_string(i) << "\n";
142 /* Check DCP as-is (should be OK) */
143 BOOST_AUTO_TEST_CASE (verify_test1)
146 vector<boost::filesystem::path> directories = setup (1, 1);
147 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
149 boost::filesystem::path const cpl_file = "build/test/verify_test1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
150 boost::filesystem::path const pkl_file = "build/test/verify_test1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml";
151 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
153 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
154 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
155 BOOST_REQUIRE (st->second);
156 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
158 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
159 BOOST_REQUIRE (st->second);
160 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
162 BOOST_CHECK_EQUAL (st->first, "Checking reel");
163 BOOST_REQUIRE (!st->second);
165 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
166 BOOST_REQUIRE (st->second);
167 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
169 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
170 BOOST_REQUIRE (st->second);
171 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
173 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
174 BOOST_REQUIRE (st->second);
175 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
177 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
178 BOOST_REQUIRE (st->second);
179 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
181 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
182 BOOST_REQUIRE (st->second);
183 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
185 BOOST_REQUIRE (st == stages.end());
187 BOOST_CHECK_EQUAL (notes.size(), 0);
190 /* Corrupt the MXFs and check that this is spotted */
191 BOOST_AUTO_TEST_CASE (verify_test2)
193 vector<boost::filesystem::path> directories = setup (1, 2);
195 FILE* mod = fopen("build/test/verify_test2/video.mxf", "r+b");
197 fseek (mod, 4096, SEEK_SET);
199 fwrite (&x, sizeof(x), 1, mod);
202 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
204 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
205 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
208 list<dcp::VerificationNote> notes;
210 dcp::ASDCPErrorSuspender sus;
211 notes = dcp::verify (directories, &stage, &progress, xsd_test);
214 BOOST_REQUIRE_EQUAL (notes.size(), 2);
215 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
216 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_HASH_INCORRECT);
217 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_ERROR);
218 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::SOUND_HASH_INCORRECT);
221 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
222 BOOST_AUTO_TEST_CASE (verify_test3)
224 vector<boost::filesystem::path> directories = setup (1, 3);
227 Editor e ("build/test/verify_test3/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml");
228 e.replace ("<Hash>", "<Hash>x");
231 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
233 BOOST_REQUIRE_EQUAL (notes.size(), 6);
234 list<dcp::VerificationNote>::const_iterator i = notes.begin();
235 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
236 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
238 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
239 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DISAGREE);
241 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
242 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DISAGREE);
244 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
245 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
247 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
248 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
250 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
251 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
255 /* Corrupt the ContentKind in the CPL */
256 BOOST_AUTO_TEST_CASE (verify_test4)
258 vector<boost::filesystem::path> directories = setup (1, 4);
261 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
262 e.replace ("<ContentKind>", "<ContentKind>x");
265 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
267 BOOST_REQUIRE_EQUAL (notes.size(), 1);
268 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
269 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xfeature'");
273 boost::filesystem::path
276 return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
280 boost::filesystem::path
283 return dcp::String::compose("build/test/verify_test%1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml", n);
287 boost::filesystem::path
290 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
294 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1)
296 vector<boost::filesystem::path> directories = setup (1, n);
300 e.replace (from, to);
303 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
305 BOOST_REQUIRE_EQUAL (notes.size(), 1);
306 BOOST_CHECK_EQUAL (notes.front().code(), code1);
310 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)
312 vector<boost::filesystem::path> directories = setup (1, n);
316 e.replace (from, to);
319 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
321 BOOST_REQUIRE_EQUAL (notes.size(), 2);
322 BOOST_CHECK_EQUAL (notes.front().code(), code1);
323 BOOST_CHECK_EQUAL (notes.back().code(), code2);
327 void check_after_replace (
328 int n, boost::function<boost::filesystem::path (int)> file,
331 dcp::VerificationNote::Code code1,
332 dcp::VerificationNote::Code code2,
333 dcp::VerificationNote::Code code3
336 vector<boost::filesystem::path> directories = setup (1, n);
340 e.replace (from, to);
343 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
345 BOOST_REQUIRE_EQUAL (notes.size(), 3);
346 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
347 BOOST_CHECK_EQUAL (i->code(), code1);
349 BOOST_CHECK_EQUAL (i->code(), code2);
351 BOOST_CHECK_EQUAL (i->code(), code3);
355 BOOST_AUTO_TEST_CASE (verify_test5)
357 check_after_replace (
359 "<FrameRate>24 1", "<FrameRate>99 1",
360 dcp::VerificationNote::CPL_HASH_INCORRECT,
361 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE
366 BOOST_AUTO_TEST_CASE (verify_test6)
368 vector<boost::filesystem::path> directories = setup (1, 6);
370 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
371 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
373 BOOST_REQUIRE_EQUAL (notes.size(), 1);
374 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
375 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_ASSET);
379 boost::filesystem::path
382 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
385 /* Empty asset filename in ASSETMAP */
386 BOOST_AUTO_TEST_CASE (verify_test7)
388 check_after_replace (
390 "<Path>video.mxf</Path>", "<Path></Path>",
391 dcp::VerificationNote::EMPTY_ASSET_PATH
395 /* Mismatched standard */
396 BOOST_AUTO_TEST_CASE (verify_test8)
398 check_after_replace (
400 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
401 dcp::VerificationNote::MISMATCHED_STANDARD,
402 dcp::VerificationNote::XML_VALIDATION_ERROR,
403 dcp::VerificationNote::CPL_HASH_INCORRECT
407 /* Badly formatted <Id> in CPL */
408 BOOST_AUTO_TEST_CASE (verify_test9)
410 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
411 check_after_replace (
413 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
414 dcp::VerificationNote::XML_VALIDATION_ERROR
418 /* Badly formatted <IssueDate> in CPL */
419 BOOST_AUTO_TEST_CASE (verify_test10)
421 check_after_replace (
423 "<IssueDate>", "<IssueDate>x",
424 dcp::VerificationNote::XML_VALIDATION_ERROR,
425 dcp::VerificationNote::CPL_HASH_INCORRECT
429 /* Badly-formatted <Id> in PKL */
430 BOOST_AUTO_TEST_CASE (verify_test11)
432 check_after_replace (
434 "<Id>urn:uuid:cd4", "<Id>urn:uuid:xd4",
435 dcp::VerificationNote::XML_VALIDATION_ERROR
439 /* Badly-formatted <Id> in ASSETMAP */
440 BOOST_AUTO_TEST_CASE (verify_test12)
442 check_after_replace (
444 "<Id>urn:uuid:63c", "<Id>urn:uuid:x3c",
445 dcp::VerificationNote::XML_VALIDATION_ERROR
449 /* Basic test of an Interop DCP */
450 BOOST_AUTO_TEST_CASE (verify_test13)
453 vector<boost::filesystem::path> directories = setup (3, 13);
454 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
456 boost::filesystem::path const cpl_file = "build/test/verify_test13/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
457 boost::filesystem::path const pkl_file = "build/test/verify_test13/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
458 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
460 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
461 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
462 BOOST_REQUIRE (st->second);
463 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
465 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
466 BOOST_REQUIRE (st->second);
467 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
469 BOOST_CHECK_EQUAL (st->first, "Checking reel");
470 BOOST_REQUIRE (!st->second);
472 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
473 BOOST_REQUIRE (st->second);
474 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
476 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
477 BOOST_REQUIRE (st->second);
478 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
480 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
481 BOOST_REQUIRE (st->second);
482 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
484 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
485 BOOST_REQUIRE (st->second);
486 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
488 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
489 BOOST_REQUIRE (st->second);
490 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
492 BOOST_REQUIRE (st == stages.end());
494 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
495 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
496 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
497 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
500 /* DCP with a short asset */
501 BOOST_AUTO_TEST_CASE (verify_test14)
503 vector<boost::filesystem::path> directories = setup (8, 14);
504 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
506 BOOST_REQUIRE_EQUAL (notes.size(), 5);
507 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
508 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
510 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
512 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
514 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
516 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
523 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
525 shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
526 boost::filesystem::create_directories (dir);
527 shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
528 for (int i = 0; i < 24; ++i) {
529 writer->write (frame.data(), frame.size());
533 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelMonoPictureAsset(asset, 0));
534 shared_ptr<dcp::Reel> reel(new dcp::Reel());
535 reel->add (reel_asset);
536 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
538 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
540 dcp->write_xml (dcp::SMPTE);
544 /* DCP with an over-sized JPEG2000 frame */
545 BOOST_AUTO_TEST_CASE (verify_test15)
547 int const too_big = 1302083 * 2;
549 /* Compress a black image */
550 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
551 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
552 BOOST_REQUIRE (frame.size() < too_big);
554 /* Place it in a bigger block with some zero padding at the end */
555 dcp::ArrayData oversized_frame(too_big);
556 memcpy (oversized_frame.data(), frame.data(), frame.size());
557 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
559 boost::filesystem::path const dir("build/test/verify_test15");
560 boost::filesystem::remove_all (dir);
561 dcp_from_frame (oversized_frame, dir);
563 vector<boost::filesystem::path> dirs;
564 dirs.push_back (dir);
565 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
566 BOOST_REQUIRE_EQUAL (notes.size(), 1);
567 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE);
571 /* DCP with a nearly over-sized JPEG2000 frame */
572 BOOST_AUTO_TEST_CASE (verify_test16)
574 int const nearly_too_big = 1302083 * 0.98;
576 /* Compress a black image */
577 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
578 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
579 BOOST_REQUIRE (frame.size() < nearly_too_big);
581 /* Place it in a bigger block with some zero padding at the end */
582 dcp::ArrayData oversized_frame(nearly_too_big);
583 memcpy (oversized_frame.data(), frame.data(), frame.size());
584 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
586 boost::filesystem::path const dir("build/test/verify_test16");
587 boost::filesystem::remove_all (dir);
588 dcp_from_frame (oversized_frame, dir);
590 vector<boost::filesystem::path> dirs;
591 dirs.push_back (dir);
592 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
593 BOOST_REQUIRE_EQUAL (notes.size(), 1);
594 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE);
598 /* DCP with a within-range JPEG2000 frame */
599 BOOST_AUTO_TEST_CASE (verify_test17)
601 /* Compress a black image */
602 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
603 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
604 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
606 boost::filesystem::path const dir("build/test/verify_test17");
607 boost::filesystem::remove_all (dir);
608 dcp_from_frame (frame, dir);
610 vector<boost::filesystem::path> dirs;
611 dirs.push_back (dir);
612 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
613 BOOST_REQUIRE_EQUAL (notes.size(), 0);
617 /* DCP with valid Interop subtitles */
618 BOOST_AUTO_TEST_CASE (verify_test18)
620 boost::filesystem::path const dir("build/test/verify_test18");
621 prepare_directory (dir);
622 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
623 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
624 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
625 shared_ptr<dcp::Reel> reel(new dcp::Reel());
626 reel->add (reel_asset);
627 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
629 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
631 dcp->write_xml (dcp::INTEROP);
633 vector<boost::filesystem::path> dirs;
634 dirs.push_back (dir);
635 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
636 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
637 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
638 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
642 /* DCP with broken Interop subtitles */
643 BOOST_AUTO_TEST_CASE (verify_test19)
645 boost::filesystem::path const dir("build/test/verify_test19");
646 prepare_directory (dir);
647 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
648 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
649 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
650 shared_ptr<dcp::Reel> reel(new dcp::Reel());
651 reel->add (reel_asset);
652 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
654 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
656 dcp->write_xml (dcp::INTEROP);
659 Editor e (dir / "subs.xml");
660 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
663 vector<boost::filesystem::path> dirs;
664 dirs.push_back (dir);
665 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
666 BOOST_REQUIRE_EQUAL (notes.size(), 3);
667 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
668 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
670 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
672 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
677 /* DCP with valid SMPTE subtitles */
678 BOOST_AUTO_TEST_CASE (verify_test20)
680 boost::filesystem::path const dir("build/test/verify_test20");
681 prepare_directory (dir);
682 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
683 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
684 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
685 shared_ptr<dcp::Reel> reel(new dcp::Reel());
686 reel->add (reel_asset);
687 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
689 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
691 dcp->write_xml (dcp::SMPTE);
693 vector<boost::filesystem::path> dirs;
694 dirs.push_back (dir);
695 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
697 BOOST_REQUIRE_EQUAL (notes.size(), 0);
701 /* DCP with broken SMPTE subtitles */
702 BOOST_AUTO_TEST_CASE (verify_test21)
704 boost::filesystem::path const dir("build/test/verify_test21");
705 prepare_directory (dir);
706 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
707 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
708 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
709 shared_ptr<dcp::Reel> reel(new dcp::Reel());
710 reel->add (reel_asset);
711 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
713 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
715 dcp->write_xml (dcp::SMPTE);
717 vector<boost::filesystem::path> dirs;
718 dirs.push_back (dir);
719 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
720 BOOST_REQUIRE_EQUAL (notes.size(), 2);
721 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
722 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
727 BOOST_AUTO_TEST_CASE (verify_test22)
729 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
730 prepare_directory (ov_dir);
732 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
733 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
734 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
735 dcp_from_frame (frame, ov_dir);
737 dcp::DCP ov (ov_dir);
740 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
741 prepare_directory (vf_dir);
743 shared_ptr<dcp::Reel> reel(new dcp::Reel());
744 reel->add (ov.cpls().front()->reels().front()->main_picture());
745 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
747 dcp::DCP vf (vf_dir);
749 vf.write_xml (dcp::SMPTE);
751 vector<boost::filesystem::path> dirs;
752 dirs.push_back (vf_dir);
753 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
754 BOOST_REQUIRE_EQUAL (notes.size(), 1);
755 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::EXTERNAL_ASSET);
759 /* DCP with valid CompositionMetadataAsset */
760 BOOST_AUTO_TEST_CASE (verify_test23)
762 boost::filesystem::path const dir("build/test/verify_test23");
763 prepare_directory (dir);
765 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
766 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
767 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
769 shared_ptr<dcp::Reel> reel(new dcp::Reel());
770 reel->add (reel_asset);
771 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
773 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
774 cpl->set_main_sound_sample_rate (48000);
775 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
776 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
780 dcp.write_xml (dcp::SMPTE);
782 vector<boost::filesystem::path> dirs;
783 dirs.push_back (dir);
784 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
788 boost::filesystem::path find_cpl (boost::filesystem::path dir)
790 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); i++) {
791 if (boost::starts_with(i->path().filename().string(), "cpl_")) {
796 BOOST_REQUIRE (false);
797 return boost::filesystem::path();
801 /* DCP with invalid CompositionMetadataAsset */
802 BOOST_AUTO_TEST_CASE (verify_test24)
804 boost::filesystem::path const dir("build/test/verify_test24");
805 prepare_directory (dir);
807 shared_ptr<dcp::Reel> reel(new dcp::Reel());
808 reel->add (black_picture_asset(dir));
809 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
811 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
812 cpl->set_main_sound_sample_rate (48000);
813 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
814 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
818 dcp.write_xml (dcp::SMPTE);
821 Editor e (find_cpl("build/test/verify_test24"));
822 e.replace ("MainSound", "MainSoundX");
825 vector<boost::filesystem::path> dirs;
826 dirs.push_back (dir);
827 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
828 BOOST_REQUIRE_EQUAL (notes.size(), 4);
830 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
831 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
833 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
835 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
837 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
842 /* DCP with invalid CompositionMetadataAsset */
843 BOOST_AUTO_TEST_CASE (verify_test25)
845 boost::filesystem::path const dir("build/test/verify_test25");
846 prepare_directory (dir);
848 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
849 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
850 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
852 shared_ptr<dcp::Reel> reel(new dcp::Reel());
853 reel->add (reel_asset);
854 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
856 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
857 cpl->set_main_sound_sample_rate (48000);
858 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
859 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
863 dcp.write_xml (dcp::SMPTE);
866 Editor e (find_cpl("build/test/verify_test25"));
867 e.replace ("</MainPictureActiveArea>", "</MainPictureActiveArea><BadTag></BadTag>");
870 vector<boost::filesystem::path> dirs;
871 dirs.push_back (dir);
872 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
876 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
877 BOOST_AUTO_TEST_CASE (verify_test26)
879 boost::filesystem::path const dir("build/test/verify_test26");
880 prepare_directory (dir);
881 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
882 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
883 asset->_language = "wrong-andbad";
884 asset->write (dir / "subs.mxf");
885 shared_ptr<dcp::ReelSubtitleAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
886 reel_asset->_language = "badlang";
887 shared_ptr<dcp::Reel> reel(new dcp::Reel());
888 reel->add (reel_asset);
889 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
891 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
893 dcp->write_xml (dcp::SMPTE);
895 vector<boost::filesystem::path> dirs;
896 dirs.push_back (dir);
897 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
898 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
899 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
900 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
901 BOOST_REQUIRE (i->note());
902 BOOST_CHECK_EQUAL (*i->note(), "badlang");
904 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
905 BOOST_REQUIRE (i->note());
906 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");