2 Copyright (C) 2018-2021 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 "stereo_picture_asset.h"
45 #include "mono_picture_asset_writer.h"
46 #include "interop_subtitle_asset.h"
47 #include "smpte_subtitle_asset.h"
48 #include "reel_closed_caption_asset.h"
49 #include "reel_stereo_picture_asset.h"
50 #include "reel_subtitle_asset.h"
51 #include "reel_markers_asset.h"
52 #include "compose.hpp"
54 #include <boost/test/unit_test.hpp>
55 #include <boost/foreach.hpp>
56 #include <boost/algorithm/string.hpp>
65 using std::make_shared;
66 using boost::optional;
67 using std::shared_ptr;
70 static list<pair<string, optional<boost::filesystem::path>>> stages;
71 static string const dcp_test1_pkl = "pkl_2b9b857f-ab4a-440e-a313-1ace0f1cfc95.xml";
72 static string const dcp_test1_cpl = "cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
75 stage (string s, optional<boost::filesystem::path> p)
77 stages.push_back (make_pair (s, p));
87 prepare_directory (boost::filesystem::path path)
89 using namespace boost::filesystem;
91 create_directories (path);
95 static vector<boost::filesystem::path>
96 setup (int reference_number, int verify_test_number)
98 prepare_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
99 for (auto i: boost::filesystem::directory_iterator(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number))) {
100 boost::filesystem::copy_file (i.path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i.path().filename());
103 return { dcp::String::compose("build/test/verify_test%1", verify_test_number) };
110 write_dcp_with_single_asset (boost::filesystem::path dir, shared_ptr<dcp::ReelAsset> reel_asset, dcp::Standard standard = dcp::SMPTE)
112 auto reel = make_shared<dcp::Reel>();
113 reel->add (reel_asset);
114 reel->add (simple_markers());
116 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
118 auto dcp = make_shared<dcp::DCP>(dir);
122 dcp::String::compose("libdcp %1", dcp::version),
123 dcp::String::compose("libdcp %1", dcp::version),
124 dcp::LocalTime().as_string(),
130 /** Class that can alter a file by searching and replacing strings within it.
131 * On destruction modifies the file whose name was given to the constructor.
136 Editor (boost::filesystem::path path)
139 _content = dcp::file_to_string (_path);
144 auto f = fopen(_path.string().c_str(), "w");
146 fwrite (_content.c_str(), _content.length(), 1, f);
150 void replace (string a, string b)
152 auto old_content = _content;
153 boost::algorithm::replace_all (_content, a, b);
154 BOOST_REQUIRE (_content != old_content);
157 void delete_lines (string from, string to)
159 vector<string> lines;
160 boost::algorithm::split (lines, _content, boost::is_any_of("\r\n"), boost::token_compress_on);
161 bool deleting = false;
162 auto old_content = _content;
164 for (auto i: lines) {
165 if (i.find(from) != string::npos) {
169 _content += i + "\n";
171 if (deleting && i.find(to) != string::npos) {
175 BOOST_REQUIRE (_content != old_content);
179 boost::filesystem::path _path;
180 std::string _content;
186 dump_notes (vector<dcp::VerificationNote> const & notes)
188 for (auto i: notes) {
189 std::cout << dcp::note_to_string(i) << "\n";
196 check_verify_result (vector<boost::filesystem::path> dir, vector<dcp::VerificationNote> test_notes)
198 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
199 BOOST_REQUIRE_EQUAL (notes.size(), test_notes.size());
205 check_verify_result_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, vector<dcp::VerificationNote::Code> codes)
207 auto directories = setup (1, n);
211 e.replace (from, to);
214 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
216 BOOST_REQUIRE_EQUAL (notes.size(), codes.size());
217 auto i = notes.begin();
218 auto j = codes.begin();
219 while (i != notes.end()) {
220 BOOST_CHECK_EQUAL (i->code(), *j);
227 /* Check DCP as-is (should be OK) */
228 BOOST_AUTO_TEST_CASE (verify_test1)
231 auto directories = setup (1, 1);
232 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
234 boost::filesystem::path const cpl_file = boost::filesystem::path("build") / "test" / "verify_test1" / dcp_test1_cpl;
235 boost::filesystem::path const pkl_file = boost::filesystem::path("build") / "test" / "verify_test1" / dcp_test1_pkl;
236 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
238 auto st = stages.begin();
239 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
240 BOOST_REQUIRE (st->second);
241 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
243 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
244 BOOST_REQUIRE (st->second);
245 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
247 BOOST_CHECK_EQUAL (st->first, "Checking reel");
248 BOOST_REQUIRE (!st->second);
250 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
251 BOOST_REQUIRE (st->second);
252 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
254 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
255 BOOST_REQUIRE (st->second);
256 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
258 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
259 BOOST_REQUIRE (st->second);
260 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
262 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
263 BOOST_REQUIRE (st->second);
264 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
266 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
267 BOOST_REQUIRE (st->second);
268 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
270 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
271 BOOST_REQUIRE (st->second);
272 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
274 BOOST_REQUIRE (st == stages.end());
276 BOOST_CHECK_EQUAL (notes.size(), 0);
279 /* Corrupt the MXFs and check that this is spotted */
280 BOOST_AUTO_TEST_CASE (verify_test2)
282 auto directories = setup (1, 2);
284 auto mod = fopen("build/test/verify_test2/video.mxf", "r+b");
286 fseek (mod, 4096, SEEK_SET);
288 fwrite (&x, sizeof(x), 1, mod);
291 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
293 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
294 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
297 dcp::ASDCPErrorSuspender sus;
298 check_verify_result (
301 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_HASH_INCORRECT },
302 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::SOUND_HASH_INCORRECT }
306 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
307 BOOST_AUTO_TEST_CASE (verify_test3)
309 auto directories = setup (1, 3);
312 Editor e (boost::filesystem::path("build") / "test" / "verify_test3" / dcp_test1_pkl);
313 e.replace ("<Hash>", "<Hash>x");
316 check_verify_result (
319 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
320 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DIFFER },
321 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DIFFER },
322 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
323 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
324 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
328 /* Corrupt the ContentKind in the CPL */
329 BOOST_AUTO_TEST_CASE (verify_test4)
331 auto directories = setup (1, 4);
334 Editor e (boost::filesystem::path("build") / "test" / "verify_test4" / dcp_test1_cpl);
335 e.replace ("<ContentKind>", "<ContentKind>x");
338 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
340 BOOST_REQUIRE_EQUAL (notes.size(), 1);
341 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
342 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xtrailer'");
346 boost::filesystem::path
349 return dcp::String::compose("build/test/verify_test%1/%2", n, dcp_test1_cpl);
353 boost::filesystem::path
356 return dcp::String::compose("build/test/verify_test%1/%2", n, dcp_test1_pkl);
360 boost::filesystem::path
363 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
368 BOOST_AUTO_TEST_CASE (verify_test5)
370 check_verify_result_after_replace (
372 "<FrameRate>24 1", "<FrameRate>99 1",
373 { dcp::VerificationNote::CPL_HASH_INCORRECT,
374 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE }
379 BOOST_AUTO_TEST_CASE (verify_test6)
381 auto directories = setup (1, 6);
383 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
384 check_verify_result (directories, {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_ASSET }});
388 boost::filesystem::path
391 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
394 /* Empty asset filename in ASSETMAP */
395 BOOST_AUTO_TEST_CASE (verify_test7)
397 check_verify_result_after_replace (
399 "<Path>video.mxf</Path>", "<Path></Path>",
400 { dcp::VerificationNote::EMPTY_ASSET_PATH }
404 /* Mismatched standard */
405 BOOST_AUTO_TEST_CASE (verify_test8)
407 check_verify_result_after_replace (
409 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
410 { dcp::VerificationNote::MISMATCHED_STANDARD,
411 dcp::VerificationNote::XML_VALIDATION_ERROR,
412 dcp::VerificationNote::XML_VALIDATION_ERROR,
413 dcp::VerificationNote::XML_VALIDATION_ERROR,
414 dcp::VerificationNote::XML_VALIDATION_ERROR,
415 dcp::VerificationNote::XML_VALIDATION_ERROR,
416 dcp::VerificationNote::CPL_HASH_INCORRECT }
420 /* Badly formatted <Id> in CPL */
421 BOOST_AUTO_TEST_CASE (verify_test9)
423 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
424 check_verify_result_after_replace (
426 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
427 { dcp::VerificationNote::XML_VALIDATION_ERROR }
431 /* Badly formatted <IssueDate> in CPL */
432 BOOST_AUTO_TEST_CASE (verify_test10)
434 check_verify_result_after_replace (
436 "<IssueDate>", "<IssueDate>x",
437 { dcp::VerificationNote::XML_VALIDATION_ERROR,
438 dcp::VerificationNote::CPL_HASH_INCORRECT }
442 /* Badly-formatted <Id> in PKL */
443 BOOST_AUTO_TEST_CASE (verify_test11)
445 check_verify_result_after_replace (
447 "<Id>urn:uuid:2b9", "<Id>urn:uuid:xb9",
448 { dcp::VerificationNote::XML_VALIDATION_ERROR }
452 /* Badly-formatted <Id> in ASSETMAP */
453 BOOST_AUTO_TEST_CASE (verify_test12)
455 check_verify_result_after_replace (
457 "<Id>urn:uuid:07e", "<Id>urn:uuid:x7e",
458 { dcp::VerificationNote::XML_VALIDATION_ERROR }
462 /* Basic test of an Interop DCP */
463 BOOST_AUTO_TEST_CASE (verify_test13)
466 auto directories = setup (3, 13);
467 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
469 boost::filesystem::path const cpl_file = boost::filesystem::path("build") / "test" / "verify_test13" / "cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
470 boost::filesystem::path const pkl_file = boost::filesystem::path("build") / "test" / "verify_test13" / "pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
471 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
473 auto st = stages.begin();
474 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
475 BOOST_REQUIRE (st->second);
476 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
478 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
479 BOOST_REQUIRE (st->second);
480 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
482 BOOST_CHECK_EQUAL (st->first, "Checking reel");
483 BOOST_REQUIRE (!st->second);
485 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
486 BOOST_REQUIRE (st->second);
487 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
489 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
490 BOOST_REQUIRE (st->second);
491 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
493 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
494 BOOST_REQUIRE (st->second);
495 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
497 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
498 BOOST_REQUIRE (st->second);
499 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
501 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
502 BOOST_REQUIRE (st->second);
503 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
505 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
506 BOOST_REQUIRE (st->second);
507 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
509 BOOST_REQUIRE (st == stages.end());
511 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
512 auto i = notes.begin ();
513 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
514 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
517 /* DCP with a short asset */
518 BOOST_AUTO_TEST_CASE (verify_test14)
520 auto directories = setup (8, 14);
521 check_verify_result (
524 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
525 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
526 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL },
527 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
528 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL }
535 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
537 auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::SMPTE);
538 boost::filesystem::create_directories (dir);
539 auto writer = asset->start_write (dir / "pic.mxf", true);
540 for (int i = 0; i < 24; ++i) {
541 writer->write (frame.data(), frame.size());
545 auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
546 write_dcp_with_single_asset (dir, reel_asset);
550 /* DCP with an over-sized JPEG2000 frame */
551 BOOST_AUTO_TEST_CASE (verify_test15)
553 int const too_big = 1302083 * 2;
555 /* Compress a black image */
556 auto image = black_image ();
557 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
558 BOOST_REQUIRE (frame.size() < too_big);
560 /* Place it in a bigger block with some zero padding at the end */
561 dcp::ArrayData oversized_frame(too_big);
562 memcpy (oversized_frame.data(), frame.data(), frame.size());
563 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
565 boost::filesystem::path const dir("build/test/verify_test15");
566 boost::filesystem::remove_all (dir);
567 dcp_from_frame (oversized_frame, dir);
569 check_verify_result (
572 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE_IN_BYTES },
573 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
578 /* DCP with a nearly over-sized JPEG2000 frame */
579 BOOST_AUTO_TEST_CASE (verify_test16)
581 int const nearly_too_big = 1302083 * 0.98;
583 /* Compress a black image */
584 auto image = black_image ();
585 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
586 BOOST_REQUIRE (frame.size() < nearly_too_big);
588 /* Place it in a bigger block with some zero padding at the end */
589 dcp::ArrayData oversized_frame(nearly_too_big);
590 memcpy (oversized_frame.data(), frame.data(), frame.size());
591 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
593 boost::filesystem::path const dir("build/test/verify_test16");
594 boost::filesystem::remove_all (dir);
595 dcp_from_frame (oversized_frame, dir);
597 check_verify_result (
600 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE_IN_BYTES },
601 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
606 /* DCP with a within-range JPEG2000 frame */
607 BOOST_AUTO_TEST_CASE (verify_test17)
609 /* Compress a black image */
610 auto image = black_image ();
611 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
612 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
614 boost::filesystem::path const dir("build/test/verify_test17");
615 boost::filesystem::remove_all (dir);
616 dcp_from_frame (frame, dir);
618 check_verify_result ({ dir }, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
622 /* DCP with valid Interop subtitles */
623 BOOST_AUTO_TEST_CASE (verify_test18)
625 boost::filesystem::path const dir("build/test/verify_test18");
626 prepare_directory (dir);
627 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
628 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
629 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
630 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
632 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE }});
636 /* DCP with broken Interop subtitles */
637 BOOST_AUTO_TEST_CASE (verify_test19)
639 boost::filesystem::path const dir("build/test/verify_test19");
640 prepare_directory (dir);
641 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
642 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
643 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
644 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
647 Editor e (dir / "subs.xml");
648 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
651 check_verify_result (
654 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
655 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
656 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
661 /* DCP with valid SMPTE subtitles */
662 BOOST_AUTO_TEST_CASE (verify_test20)
664 boost::filesystem::path const dir("build/test/verify_test20");
665 prepare_directory (dir);
666 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
667 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
668 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
669 write_dcp_with_single_asset (dir, reel_asset);
671 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
675 /* DCP with broken SMPTE subtitles */
676 BOOST_AUTO_TEST_CASE (verify_test21)
678 boost::filesystem::path const dir("build/test/verify_test21");
679 prepare_directory (dir);
680 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
681 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
682 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
683 write_dcp_with_single_asset (dir, reel_asset);
685 check_verify_result (
688 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
689 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
690 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
691 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
697 BOOST_AUTO_TEST_CASE (verify_test22)
699 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
700 prepare_directory (ov_dir);
702 auto image = black_image ();
703 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
704 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
705 dcp_from_frame (frame, ov_dir);
707 dcp::DCP ov (ov_dir);
710 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
711 prepare_directory (vf_dir);
713 write_dcp_with_single_asset (vf_dir, ov.cpls().front()->reels().front()->main_picture());
715 check_verify_result (
718 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::EXTERNAL_ASSET },
719 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
724 /* DCP with valid CompositionMetadataAsset */
725 BOOST_AUTO_TEST_CASE (verify_test23)
727 boost::filesystem::path const dir("build/test/verify_test23");
728 prepare_directory (dir);
730 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
731 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
732 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
734 auto reel = make_shared<dcp::Reel>();
735 reel->add (reel_asset);
737 reel->add (simple_markers(16 * 24 - 1));
739 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
741 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
742 cpl->set_main_sound_sample_rate (48000);
743 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
744 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
750 dcp::String::compose("libdcp %1", dcp::version),
751 dcp::String::compose("libdcp %1", dcp::version),
752 dcp::LocalTime().as_string(),
756 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
760 boost::filesystem::path find_cpl (boost::filesystem::path dir)
762 for (auto i: boost::filesystem::directory_iterator(dir)) {
763 if (boost::starts_with(i.path().filename().string(), "cpl_")) {
768 BOOST_REQUIRE (false);
773 /* DCP with invalid CompositionMetadataAsset */
774 BOOST_AUTO_TEST_CASE (verify_test24)
776 boost::filesystem::path const dir("build/test/verify_test24");
777 prepare_directory (dir);
779 auto reel = make_shared<dcp::Reel>();
780 reel->add (black_picture_asset(dir));
781 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
783 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
784 cpl->set_main_sound_sample_rate (48000);
785 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
786 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
787 cpl->set_version_number (1);
789 reel->add (simple_markers());
795 dcp::String::compose("libdcp %1", dcp::version),
796 dcp::String::compose("libdcp %1", dcp::version),
797 dcp::LocalTime().as_string(),
802 Editor e (find_cpl("build/test/verify_test24"));
803 e.replace ("MainSound", "MainSoundX");
806 check_verify_result (
809 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
810 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
811 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
812 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
817 /* DCP with invalid CompositionMetadataAsset */
818 BOOST_AUTO_TEST_CASE (verify_test25)
820 boost::filesystem::path const dir("build/test/verify_test25");
821 prepare_directory (dir);
823 auto reel = make_shared<dcp::Reel>();
824 reel->add (black_picture_asset(dir));
825 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
827 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
828 cpl->set_main_sound_sample_rate (48000);
829 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
830 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
836 dcp::String::compose("libdcp %1", dcp::version),
837 dcp::String::compose("libdcp %1", dcp::version),
838 dcp::LocalTime().as_string(),
843 Editor e (find_cpl("build/test/verify_test25"));
844 e.replace ("meta:Width", "meta:WidthX");
847 check_verify_result (
849 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::GENERAL_READ }}
854 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
855 BOOST_AUTO_TEST_CASE (verify_test26)
857 boost::filesystem::path const dir("build/test/verify_test26");
858 prepare_directory (dir);
859 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
860 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
861 asset->_language = "wrong-andbad";
862 asset->write (dir / "subs.mxf");
863 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
864 reel_asset->_language = "badlang";
865 write_dcp_with_single_asset (dir, reel_asset);
867 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
868 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
869 auto i = notes.begin();
870 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
871 BOOST_REQUIRE (i->note());
872 BOOST_CHECK_EQUAL (*i->note(), "badlang");
874 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
875 BOOST_REQUIRE (i->note());
876 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
878 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_CPL_METADATA);
882 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
883 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_languages)
885 boost::filesystem::path const dir("build/test/verify_invalid_closed_caption_languages");
886 prepare_directory (dir);
887 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
888 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
889 asset->_language = "wrong-andbad";
890 asset->write (dir / "subs.mxf");
891 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
892 reel_asset->_language = "badlang";
893 write_dcp_with_single_asset (dir, reel_asset);
895 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
896 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
897 auto i = notes.begin ();
898 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
899 BOOST_REQUIRE (i->note());
900 BOOST_CHECK_EQUAL (*i->note(), "badlang");
902 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
903 BOOST_REQUIRE (i->note());
904 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
906 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_CPL_METADATA);
910 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
911 * the release territory.
913 BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
915 boost::filesystem::path const dir("build/test/verify_various_invalid_languages");
916 prepare_directory (dir);
918 auto picture = simple_picture (dir, "foo");
919 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
920 auto reel = make_shared<dcp::Reel>();
921 reel->add (reel_picture);
922 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
923 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
924 reel->add (reel_sound);
925 reel->add (simple_markers());
927 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
929 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
930 cpl->_additional_subtitle_languages.push_back("andso-is-this");
931 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
932 cpl->set_main_sound_sample_rate (48000);
933 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
934 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
935 cpl->set_version_number (1);
936 cpl->_release_territory = "fred-jim";
937 auto dcp = make_shared<dcp::DCP>(dir);
941 dcp::String::compose("libdcp %1", dcp::version),
942 dcp::String::compose("libdcp %1", dcp::version),
943 dcp::LocalTime().as_string(),
947 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
948 BOOST_REQUIRE_EQUAL (notes.size(), 4U);
949 auto i = notes.begin ();
950 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
951 BOOST_REQUIRE (i->note());
952 BOOST_CHECK_EQUAL (*i->note(), "this-is-wrong");
954 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
955 BOOST_REQUIRE (i->note());
956 BOOST_CHECK_EQUAL (*i->note(), "andso-is-this");
958 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
959 BOOST_REQUIRE (i->note());
960 BOOST_CHECK_EQUAL (*i->note(), "fred-jim");
962 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
963 BOOST_REQUIRE (i->note());
964 BOOST_CHECK_EQUAL (*i->note(), "frobozz");
969 vector<dcp::VerificationNote>
970 check_picture_size (int width, int height, int frame_rate, bool three_d)
972 using namespace boost::filesystem;
974 path dcp_path = "build/test/verify_picture_test";
975 remove_all (dcp_path);
976 create_directories (dcp_path);
978 shared_ptr<dcp::PictureAsset> mp;
980 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
982 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
984 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
986 auto image = black_image (dcp::Size(width, height));
987 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
988 int const length = three_d ? frame_rate * 2 : frame_rate;
989 for (int i = 0; i < length; ++i) {
990 picture_writer->write (j2c.data(), j2c.size());
992 picture_writer->finalize ();
994 auto d = make_shared<dcp::DCP>(dcp_path);
995 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
996 cpl->set_annotation_text ("A Test DCP");
997 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
998 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
999 cpl->set_main_sound_sample_rate (48000);
1000 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1001 cpl->set_main_picture_active_area (dcp::Size(1998, 1080));
1002 cpl->set_version_number (1);
1004 auto reel = make_shared<dcp::Reel>();
1007 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
1009 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
1012 reel->add (simple_markers(frame_rate));
1019 dcp::String::compose("libdcp %1", dcp::version),
1020 dcp::String::compose("libdcp %1", dcp::version),
1021 dcp::LocalTime().as_string(),
1025 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
1031 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1033 auto notes = check_picture_size(width, height, frame_rate, three_d);
1035 BOOST_CHECK_EQUAL (notes.size(), 0U);
1041 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1043 auto notes = check_picture_size(width, height, frame_rate, three_d);
1044 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1045 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1046 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS);
1052 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1054 auto notes = check_picture_size(width, height, frame_rate, three_d);
1055 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1056 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1057 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K);
1063 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1065 auto notes = check_picture_size(width, height, frame_rate, three_d);
1066 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1067 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1068 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K);
1072 BOOST_AUTO_TEST_CASE (verify_picture_size)
1074 using namespace boost::filesystem;
1077 check_picture_size_ok (2048, 858, 24, false);
1078 check_picture_size_ok (2048, 858, 25, false);
1079 check_picture_size_ok (2048, 858, 48, false);
1080 check_picture_size_ok (2048, 858, 24, true);
1081 check_picture_size_ok (2048, 858, 25, true);
1082 check_picture_size_ok (2048, 858, 48, true);
1085 check_picture_size_ok (1998, 1080, 24, false);
1086 check_picture_size_ok (1998, 1080, 25, false);
1087 check_picture_size_ok (1998, 1080, 48, false);
1088 check_picture_size_ok (1998, 1080, 24, true);
1089 check_picture_size_ok (1998, 1080, 25, true);
1090 check_picture_size_ok (1998, 1080, 48, true);
1093 check_picture_size_ok (4096, 1716, 24, false);
1096 check_picture_size_ok (3996, 2160, 24, false);
1098 /* Bad frame size */
1099 check_picture_size_bad_frame_size (2050, 858, 24, false);
1100 check_picture_size_bad_frame_size (2048, 658, 25, false);
1101 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1102 check_picture_size_bad_frame_size (4000, 3000, 24, true);
1104 /* Bad 2K frame rate */
1105 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1106 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1107 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1109 /* Bad 4K frame rate */
1110 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1111 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1114 auto notes = check_picture_size(3996, 2160, 24, true);
1115 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1116 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1117 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_4K_3D);
1123 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame, float v_position = 0, string text = "Hello")
1126 make_shared<dcp::SubtitleString>(
1134 dcp::Time(start_frame, 24, 24),
1135 dcp::Time(end_frame, 24, 24),
1151 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1153 boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1154 prepare_directory (dir);
1156 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1157 for (int i = 0; i < 2048; ++i) {
1158 add_test_subtitle (asset, i * 24, i * 24 + 20);
1160 asset->set_language (dcp::LanguageTag("de-DE"));
1161 asset->write (dir / "subs.mxf");
1162 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1163 write_dcp_with_single_asset (dir, reel_asset);
1165 check_verify_result (
1168 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1169 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_XML_TOO_LARGE_IN_BYTES },
1170 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1171 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
1177 shared_ptr<dcp::SMPTESubtitleAsset>
1178 make_large_subtitle_asset (boost::filesystem::path font_file)
1180 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1181 dcp::ArrayData big_fake_font(1024 * 1024);
1182 big_fake_font.write (font_file);
1183 for (int i = 0; i < 116; ++i) {
1184 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1192 verify_timed_text_asset_too_large (string name)
1194 auto const dir = boost::filesystem::path("build/test") / name;
1195 prepare_directory (dir);
1196 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1197 add_test_subtitle (asset, 0, 20);
1198 asset->set_language (dcp::LanguageTag("de-DE"));
1199 asset->write (dir / "subs.mxf");
1201 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1202 write_dcp_with_single_asset (dir, reel_asset);
1204 check_verify_result (
1207 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_ASSET_TOO_LARGE_IN_BYTES },
1208 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_FONTS_TOO_LARGE_IN_BYTES },
1209 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1210 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1211 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
1216 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1218 verify_timed_text_asset_too_large<dcp::ReelSubtitleAsset>("verify_subtitle_asset_too_large");
1219 verify_timed_text_asset_too_large<dcp::ReelClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1223 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1225 boost::filesystem::path dir = "build/test/verify_missing_language_tag_in_subtitle_xml";
1226 prepare_directory (dir);
1227 auto dcp = make_simple (dir, 1, 240);
1230 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1231 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1232 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1233 "<ContentTitleText>Content</ContentTitleText>"
1234 "<AnnotationText>Annotation</AnnotationText>"
1235 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1236 "<ReelNumber>1</ReelNumber>"
1237 "<EditRate>25 1</EditRate>"
1238 "<TimeCodeRate>25</TimeCodeRate>"
1239 "<StartTime>00:00:00:00</StartTime>"
1240 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1242 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1243 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1244 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1250 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1251 BOOST_REQUIRE (xml_file);
1252 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1254 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1255 subs->write (dir / "subs.mxf");
1257 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1258 dcp->cpls().front()->reels().front()->add(reel_subs);
1261 dcp::String::compose("libdcp %1", dcp::version),
1262 dcp::String::compose("libdcp %1", dcp::version),
1263 dcp::LocalTime().as_string(),
1267 check_verify_result (
1270 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE },
1271 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1276 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1278 boost::filesystem::path path ("build/test/verify_inconsistent_subtitle_languages");
1279 auto dcp = make_simple (path, 2, 240);
1280 auto cpl = dcp->cpls()[0];
1283 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1284 subs->set_language (dcp::LanguageTag("de-DE"));
1285 subs->add (simple_subtitle());
1286 subs->write (path / "subs1.mxf");
1287 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1288 cpl->reels()[0]->add(reel_subs);
1292 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1293 subs->set_language (dcp::LanguageTag("en-US"));
1294 subs->add (simple_subtitle());
1295 subs->write (path / "subs2.mxf");
1296 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1297 cpl->reels()[1]->add(reel_subs);
1302 dcp::String::compose("libdcp %1", dcp::version),
1303 dcp::String::compose("libdcp %1", dcp::version),
1304 dcp::LocalTime().as_string(),
1308 check_verify_result (
1311 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1312 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_LANGUAGES_DIFFER },
1313 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
1318 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1320 boost::filesystem::path dir = "build/test/verify_missing_start_time_tag_in_subtitle_xml";
1321 prepare_directory (dir);
1322 auto dcp = make_simple (dir, 1, 240);
1325 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1326 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1327 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1328 "<ContentTitleText>Content</ContentTitleText>"
1329 "<AnnotationText>Annotation</AnnotationText>"
1330 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1331 "<ReelNumber>1</ReelNumber>"
1332 "<Language>de-DE</Language>"
1333 "<EditRate>25 1</EditRate>"
1334 "<TimeCodeRate>25</TimeCodeRate>"
1335 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1337 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1338 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1339 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1345 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1346 BOOST_REQUIRE (xml_file);
1347 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1349 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1350 subs->write (dir / "subs.mxf");
1352 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1353 dcp->cpls().front()->reels().front()->add(reel_subs);
1356 dcp::String::compose("libdcp %1", dcp::version),
1357 dcp::String::compose("libdcp %1", dcp::version),
1358 dcp::LocalTime().as_string(),
1362 check_verify_result (
1365 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1366 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1371 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1373 boost::filesystem::path dir = "build/test/verify_non_zero_start_time_tag_in_subtitle_xml";
1374 prepare_directory (dir);
1375 auto dcp = make_simple (dir, 1, 240);
1378 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1379 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1380 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1381 "<ContentTitleText>Content</ContentTitleText>"
1382 "<AnnotationText>Annotation</AnnotationText>"
1383 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1384 "<ReelNumber>1</ReelNumber>"
1385 "<Language>de-DE</Language>"
1386 "<EditRate>25 1</EditRate>"
1387 "<TimeCodeRate>25</TimeCodeRate>"
1388 "<StartTime>00:00:02:00</StartTime>"
1389 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1391 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1392 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1393 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1399 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1400 BOOST_REQUIRE (xml_file);
1401 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1403 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1404 subs->write (dir / "subs.mxf");
1406 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1407 dcp->cpls().front()->reels().front()->add(reel_subs);
1410 dcp::String::compose("libdcp %1", dcp::version),
1411 dcp::String::compose("libdcp %1", dcp::version),
1412 dcp::LocalTime().as_string(),
1416 check_verify_result (
1419 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO },
1420 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1428 TestText (int in_, int out_, float v_position_ = 0, string text_ = "Hello")
1431 , v_position(v_position_)
1444 dcp_with_text (boost::filesystem::path dir, vector<TestText> subs)
1446 prepare_directory (dir);
1447 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1448 asset->set_start_time (dcp::Time());
1449 for (auto i: subs) {
1450 add_test_subtitle (asset, i.in, i.out, i.v_position, i.text);
1452 asset->set_language (dcp::LanguageTag("de-DE"));
1453 asset->write (dir / "subs.mxf");
1455 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1456 write_dcp_with_single_asset (dir, reel_asset);
1460 BOOST_AUTO_TEST_CASE (verify_text_too_early)
1462 auto const dir = boost::filesystem::path("build/test/verify_text_too_early");
1463 /* Just too early */
1464 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1465 check_verify_result (
1468 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1469 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1475 BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
1477 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_early");
1478 /* Just late enough */
1479 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1480 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1484 BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
1486 auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
1487 prepare_directory (dir);
1489 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1490 asset1->set_start_time (dcp::Time());
1491 /* Just late enough */
1492 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1493 asset1->set_language (dcp::LanguageTag("de-DE"));
1494 asset1->write (dir / "subs1.mxf");
1495 auto reel_asset1 = make_shared<dcp::ReelSubtitleAsset>(asset1, dcp::Fraction(24, 1), 16 * 24, 0);
1496 auto reel1 = make_shared<dcp::Reel>();
1497 reel1->add (reel_asset1);
1498 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1499 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1500 reel1->add (markers1);
1502 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1503 asset2->set_start_time (dcp::Time());
1504 /* This would be too early on first reel but should be OK on the second */
1505 add_test_subtitle (asset2, 0, 4 * 24);
1506 asset2->set_language (dcp::LanguageTag("de-DE"));
1507 asset2->write (dir / "subs2.mxf");
1508 auto reel_asset2 = make_shared<dcp::ReelSubtitleAsset>(asset2, dcp::Fraction(24, 1), 16 * 24, 0);
1509 auto reel2 = make_shared<dcp::Reel>();
1510 reel2->add (reel_asset2);
1511 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1512 markers2->set (dcp::Marker::LFOC, dcp::Time(16 * 24 - 1, 24, 24));
1513 reel2->add (markers2);
1515 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1518 auto dcp = make_shared<dcp::DCP>(dir);
1522 dcp::String::compose("libdcp %1", dcp::version),
1523 dcp::String::compose("libdcp %1", dcp::version),
1524 dcp::LocalTime().as_string(),
1529 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1533 BOOST_AUTO_TEST_CASE (verify_text_too_close)
1535 auto const dir = boost::filesystem::path("build/test/verify_text_too_close");
1536 dcp_with_text<dcp::ReelSubtitleAsset> (
1540 { 5 * 24 + 1, 6 * 24 },
1542 check_verify_result (
1545 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_CLOSE },
1546 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1551 BOOST_AUTO_TEST_CASE (verify_text_not_too_close)
1553 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_close");
1554 dcp_with_text<dcp::ReelSubtitleAsset> (
1558 { 5 * 24 + 16, 8 * 24 },
1560 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1564 BOOST_AUTO_TEST_CASE (verify_text_too_short)
1566 auto const dir = boost::filesystem::path("build/test/verify_text_too_short");
1567 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1568 check_verify_result (
1571 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_SHORT },
1572 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1577 BOOST_AUTO_TEST_CASE (verify_text_not_too_short)
1579 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_short");
1580 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1581 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1585 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines1)
1587 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines1");
1588 dcp_with_text<dcp::ReelSubtitleAsset> (
1591 { 96, 200, 0.0, "We" },
1592 { 96, 200, 0.1, "have" },
1593 { 96, 200, 0.2, "four" },
1594 { 96, 200, 0.3, "lines" }
1596 check_verify_result (
1599 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES },
1600 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1605 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines1)
1607 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines1");
1608 dcp_with_text<dcp::ReelSubtitleAsset> (
1611 { 96, 200, 0.0, "We" },
1612 { 96, 200, 0.1, "have" },
1613 { 96, 200, 0.2, "four" },
1615 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1619 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines2)
1621 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines2");
1622 dcp_with_text<dcp::ReelSubtitleAsset> (
1625 { 96, 300, 0.0, "We" },
1626 { 96, 300, 0.1, "have" },
1627 { 150, 180, 0.2, "four" },
1628 { 150, 180, 0.3, "lines" }
1630 check_verify_result (
1633 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES },
1634 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1639 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines2)
1641 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines2");
1642 dcp_with_text<dcp::ReelSubtitleAsset> (
1645 { 96, 300, 0.0, "We" },
1646 { 96, 300, 0.1, "have" },
1647 { 150, 180, 0.2, "four" },
1648 { 190, 250, 0.3, "lines" }
1650 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1654 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long1)
1656 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long1");
1657 dcp_with_text<dcp::ReelSubtitleAsset> (
1660 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123" }
1662 check_verify_result (
1665 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_LONGER_THAN_RECOMMENDED },
1666 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1671 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long2)
1673 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long2");
1674 dcp_with_text<dcp::ReelSubtitleAsset> (
1677 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1679 check_verify_result (
1682 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_TOO_LONG },
1683 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1688 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines1)
1690 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines1");
1691 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1694 { 96, 200, 0.0, "We" },
1695 { 96, 200, 0.1, "have" },
1696 { 96, 200, 0.2, "four" },
1697 { 96, 200, 0.3, "lines" }
1699 check_verify_result (
1702 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES},
1703 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1708 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines1)
1710 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines1");
1711 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1714 { 96, 200, 0.0, "We" },
1715 { 96, 200, 0.1, "have" },
1716 { 96, 200, 0.2, "four" },
1718 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1722 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines2)
1724 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines2");
1725 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1728 { 96, 300, 0.0, "We" },
1729 { 96, 300, 0.1, "have" },
1730 { 150, 180, 0.2, "four" },
1731 { 150, 180, 0.3, "lines" }
1733 check_verify_result (
1736 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES},
1737 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1742 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines2)
1744 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines2");
1745 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1748 { 96, 300, 0.0, "We" },
1749 { 96, 300, 0.1, "have" },
1750 { 150, 180, 0.2, "four" },
1751 { 190, 250, 0.3, "lines" }
1753 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1757 BOOST_AUTO_TEST_CASE (verify_closed_caption_lines_too_long1)
1759 auto const dir = boost::filesystem::path ("build/test/verify_closed_caption_lines_too_long1");
1760 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1763 { 96, 300, 0.0, "0123456789012345678901234567890123" }
1765 check_verify_result (
1768 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_LINE_TOO_LONG },
1769 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1774 BOOST_AUTO_TEST_CASE (verify_sound_sampling_rate_must_be_48k)
1776 boost::filesystem::path const dir("build/test/verify_sound_sampling_rate_must_be_48k");
1777 prepare_directory (dir);
1779 auto picture = simple_picture (dir, "foo");
1780 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1781 auto reel = make_shared<dcp::Reel>();
1782 reel->add (reel_picture);
1783 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000);
1784 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1785 reel->add (reel_sound);
1786 reel->add (simple_markers());
1787 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1789 auto dcp = make_shared<dcp::DCP>(dir);
1793 dcp::String::compose("libdcp %1", dcp::version),
1794 dcp::String::compose("libdcp %1", dcp::version),
1795 dcp::LocalTime().as_string(),
1799 check_verify_result (
1802 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_SOUND_FRAME_RATE },
1803 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1808 BOOST_AUTO_TEST_CASE (verify_cpl_must_have_annotation_text)
1810 boost::filesystem::path const dir("build/test/verify_cpl_must_have_annotation_text");
1811 auto dcp = make_simple (dir);
1814 dcp::String::compose("libdcp %1", dcp::version),
1815 dcp::String::compose("libdcp %1", dcp::version),
1816 dcp::LocalTime().as_string(),
1820 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1823 BOOST_REQUIRE (dcp->cpls()[0]->file());
1824 Editor e(dcp->cpls()[0]->file().get());
1825 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
1828 check_verify_result (
1831 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_ANNOTATION_TEXT_IN_CPL },
1832 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1837 BOOST_AUTO_TEST_CASE (verify_cpl_annotation_text_should_be_same_as_content_title_text)
1839 boost::filesystem::path const dir("build/test/verify_cpl_annotation_text_should_be_same_as_content_title_text");
1840 auto dcp = make_simple (dir);
1843 dcp::String::compose("libdcp %1", dcp::version),
1844 dcp::String::compose("libdcp %1", dcp::version),
1845 dcp::LocalTime().as_string(),
1849 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1852 BOOST_REQUIRE (dcp->cpls()[0]->file());
1853 Editor e(dcp->cpls()[0]->file().get());
1854 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
1857 check_verify_result (
1860 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::CPL_ANNOTATION_TEXT_DIFFERS_FROM_CONTENT_TITLE_TEXT },
1861 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1866 BOOST_AUTO_TEST_CASE (verify_reel_assets_durations_must_match)
1868 boost::filesystem::path const dir("build/test/verify_reel_assets_durations_must_match");
1869 boost::filesystem::remove_all (dir);
1870 boost::filesystem::create_directories (dir);
1871 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
1872 shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::TRAILER));
1874 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
1875 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
1877 auto reel = make_shared<dcp::Reel>(
1878 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
1879 make_shared<dcp::ReelSoundAsset>(ms, 0)
1882 reel->add (simple_markers());
1888 dcp::String::compose("libdcp %1", dcp::version),
1889 dcp::String::compose("libdcp %1", dcp::version),
1890 dcp::LocalTime().as_string(),
1895 check_verify_result (
1898 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_ASSET_DURATION },
1899 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1907 verify_subtitles_must_be_in_all_reels_check (boost::filesystem::path dir, bool add_to_reel1, bool add_to_reel2)
1909 boost::filesystem::remove_all (dir);
1910 boost::filesystem::create_directories (dir);
1911 auto dcp = make_shared<dcp::DCP>(dir);
1912 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1914 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1915 subs->set_language (dcp::LanguageTag("de-DE"));
1916 subs->set_start_time (dcp::Time());
1917 subs->add (simple_subtitle());
1918 subs->write (dir / "subs.mxf");
1919 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1921 auto reel1 = make_shared<dcp::Reel>(
1922 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1923 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1927 reel1->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1930 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1931 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1932 reel1->add (markers1);
1936 auto reel2 = make_shared<dcp::Reel>(
1937 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1938 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1942 reel2->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1945 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1946 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
1947 reel2->add (markers2);
1954 dcp::String::compose("libdcp %1", dcp::version),
1955 dcp::String::compose("libdcp %1", dcp::version),
1956 dcp::LocalTime().as_string(),
1962 BOOST_AUTO_TEST_CASE (verify_subtitles_must_be_in_all_reels)
1965 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1966 verify_subtitles_must_be_in_all_reels_check (dir, true, false);
1967 check_verify_result (
1970 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MAIN_SUBTITLE_NOT_IN_ALL_REELS },
1971 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1977 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
1978 verify_subtitles_must_be_in_all_reels_check (dir, true, true);
1979 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1983 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1984 verify_subtitles_must_be_in_all_reels_check (dir, false, false);
1985 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1992 verify_closed_captions_must_be_in_all_reels_check (boost::filesystem::path dir, int caps_in_reel1, int caps_in_reel2)
1994 boost::filesystem::remove_all (dir);
1995 boost::filesystem::create_directories (dir);
1996 auto dcp = make_shared<dcp::DCP>(dir);
1997 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1999 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2000 subs->set_language (dcp::LanguageTag("de-DE"));
2001 subs->set_start_time (dcp::Time());
2002 subs->add (simple_subtitle());
2003 subs->write (dir / "subs.mxf");
2005 auto reel1 = make_shared<dcp::Reel>(
2006 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
2007 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
2010 for (int i = 0; i < caps_in_reel1; ++i) {
2011 reel1->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
2014 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
2015 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
2016 reel1->add (markers1);
2020 auto reel2 = make_shared<dcp::Reel>(
2021 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
2022 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
2025 for (int i = 0; i < caps_in_reel2; ++i) {
2026 reel2->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
2029 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
2030 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
2031 reel2->add (markers2);
2038 dcp::String::compose("libdcp %1", dcp::version),
2039 dcp::String::compose("libdcp %1", dcp::version),
2040 dcp::LocalTime().as_string(),
2046 BOOST_AUTO_TEST_CASE (verify_closed_captions_must_be_in_all_reels)
2049 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels1");
2050 verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
2051 check_verify_result (
2054 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_ASSET_COUNTS_DIFFER },
2055 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
2060 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
2061 verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
2062 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
2066 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
2067 verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
2068 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
2075 verify_text_entry_point_check (boost::filesystem::path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
2077 boost::filesystem::remove_all (dir);
2078 boost::filesystem::create_directories (dir);
2079 auto dcp = make_shared<dcp::DCP>(dir);
2080 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
2082 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2083 subs->set_language (dcp::LanguageTag("de-DE"));
2084 subs->set_start_time (dcp::Time());
2085 subs->add (simple_subtitle());
2086 subs->write (dir / "subs.mxf");
2087 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), 240, 0);
2090 auto reel = make_shared<dcp::Reel>(
2091 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
2092 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
2095 reel->add (reel_text);
2097 reel->add (simple_markers(240));
2104 dcp::String::compose("libdcp %1", dcp::version),
2105 dcp::String::compose("libdcp %1", dcp::version),
2106 dcp::LocalTime().as_string(),
2110 check_verify_result (
2113 { dcp::VerificationNote::VERIFY_BV21_ERROR, code },
2114 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
2119 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
2121 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
2122 "build/test/verify_subtitle_entry_point_must_be_present",
2123 dcp::VerificationNote::MISSING_SUBTITLE_ENTRY_POINT,
2124 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
2125 asset->unset_entry_point ();
2129 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
2130 "build/test/verify_subtitle_entry_point_must_be_zero",
2131 dcp::VerificationNote::SUBTITLE_ENTRY_POINT_NON_ZERO,
2132 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
2133 asset->set_entry_point (4);
2137 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
2138 "build/test/verify_closed_caption_entry_point_must_be_present",
2139 dcp::VerificationNote::MISSING_CLOSED_CAPTION_ENTRY_POINT,
2140 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
2141 asset->unset_entry_point ();
2145 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
2146 "build/test/verify_closed_caption_entry_point_must_be_zero",
2147 dcp::VerificationNote::CLOSED_CAPTION_ENTRY_POINT_NON_ZERO,
2148 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
2149 asset->set_entry_point (9);
2155 BOOST_AUTO_TEST_CASE (verify_assets_must_have_hashes)
2159 boost::filesystem::path const dir("build/test/verify_assets_must_have_hashes");
2160 auto dcp = make_simple (dir);
2163 dcp::String::compose("libdcp %1", dcp::version),
2164 dcp::String::compose("libdcp %1", dcp::version),
2165 dcp::LocalTime().as_string(),
2169 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2172 BOOST_REQUIRE (dcp->cpls()[0]->file());
2173 Editor e(dcp->cpls()[0]->file().get());
2174 e.replace("<Hash>XGhFVrqZqapOJx5Fh2SLjj48Yjg=</Hash>", "");
2177 check_verify_result (
2180 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2181 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_HASH }
2188 verify_markers_test (
2189 boost::filesystem::path dir,
2190 vector<pair<dcp::Marker, dcp::Time>> markers,
2191 vector<dcp::VerificationNote> test_notes
2194 auto dcp = make_simple (dir);
2195 dcp->cpls()[0]->set_content_kind (dcp::FEATURE);
2196 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24, 0);
2197 for (auto const& i: markers) {
2198 markers_asset->set (i.first, i.second);
2200 dcp->cpls()[0]->reels()[0]->add(markers_asset);
2203 dcp::String::compose("libdcp %1", dcp::version),
2204 dcp::String::compose("libdcp %1", dcp::version),
2205 dcp::LocalTime().as_string(),
2209 check_verify_result ({dir}, test_notes);
2213 BOOST_AUTO_TEST_CASE (verify_markers)
2215 verify_markers_test (
2216 "build/test/verify_markers_all_correct",
2218 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2219 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2220 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2221 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2226 verify_markers_test (
2227 "build/test/verify_markers_missing_ffec",
2229 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2230 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2231 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2234 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE }
2237 verify_markers_test (
2238 "build/test/verify_markers_missing_ffmc",
2240 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2241 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2242 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2245 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE }
2248 verify_markers_test (
2249 "build/test/verify_markers_missing_ffoc",
2251 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2252 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2253 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2256 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC}
2259 verify_markers_test (
2260 "build/test/verify_markers_missing_lfoc",
2262 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2263 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2264 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
2267 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC }
2270 verify_markers_test (
2271 "build/test/verify_markers_incorrect_ffoc",
2273 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2274 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2275 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
2276 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2279 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_FFOC }
2282 verify_markers_test (
2283 "build/test/verify_markers_incorrect_lfoc",
2285 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2286 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2287 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2288 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
2291 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_LFOC }
2296 BOOST_AUTO_TEST_CASE (verify_cpl_metadata_version)
2298 boost::filesystem::path dir = "build/test/verify_cpl_metadata_version";
2299 prepare_directory (dir);
2300 auto dcp = make_simple (dir);
2301 dcp->cpls()[0]->unset_version_number();
2304 dcp::String::compose("libdcp %1", dcp::version),
2305 dcp::String::compose("libdcp %1", dcp::version),
2306 dcp::LocalTime().as_string(),
2310 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER }});
2314 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata1)
2316 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata1";
2317 auto dcp = make_simple (dir);
2320 dcp::String::compose("libdcp %1", dcp::version),
2321 dcp::String::compose("libdcp %1", dcp::version),
2322 dcp::LocalTime().as_string(),
2327 Editor e (dcp->cpls()[0]->file().get());
2328 e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
2331 check_verify_result (
2334 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2335 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
2340 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata2)
2342 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata2";
2343 auto dcp = make_simple (dir);
2346 dcp::String::compose("libdcp %1", dcp::version),
2347 dcp::String::compose("libdcp %1", dcp::version),
2348 dcp::LocalTime().as_string(),
2353 Editor e (dcp->cpls()[0]->file().get());
2354 e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
2357 check_verify_result (
2360 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2361 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
2366 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata3)
2368 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata3";
2369 auto dcp = make_simple (dir);
2372 dcp::String::compose("libdcp %1", dcp::version),
2373 dcp::String::compose("libdcp %1", dcp::version),
2374 dcp::LocalTime().as_string(),
2379 Editor e (dcp->cpls()[0]->file().get());
2380 e.replace ("<meta:Name>A", "<meta:NameX>A");
2381 e.replace ("n</meta:Name>", "n</meta:NameX>");
2384 check_verify_result (
2387 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2388 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2389 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2394 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata4)
2396 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata4";
2397 auto dcp = make_simple (dir);
2400 dcp::String::compose("libdcp %1", dcp::version),
2401 dcp::String::compose("libdcp %1", dcp::version),
2402 dcp::LocalTime().as_string(),
2407 Editor e (dcp->cpls()[0]->file().get());
2408 e.replace ("Application", "Fred");
2411 check_verify_result (
2414 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2415 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Name> property should be 'Application'") },
2420 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata5)
2422 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata5";
2423 auto dcp = make_simple (dir);
2426 dcp::String::compose("libdcp %1", dcp::version),
2427 dcp::String::compose("libdcp %1", dcp::version),
2428 dcp::LocalTime().as_string(),
2432 Editor e (dcp->cpls()[0]->file().get());
2433 e.replace ("DCP Constraints Profile", "Fred");
2436 check_verify_result (
2439 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2440 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'") },
2445 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata6)
2447 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata6";
2448 auto dcp = make_simple (dir);
2451 dcp::String::compose("libdcp %1", dcp::version),
2452 dcp::String::compose("libdcp %1", dcp::version),
2453 dcp::LocalTime().as_string(),
2458 Editor e (dcp->cpls()[0]->file().get());
2459 e.replace ("<meta:Value>", "<meta:ValueX>");
2460 e.replace ("</meta:Value>", "</meta:ValueX>");
2463 check_verify_result (
2466 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2467 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2468 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2473 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata7)
2475 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata7";
2476 auto dcp = make_simple (dir);
2479 dcp::String::compose("libdcp %1", dcp::version),
2480 dcp::String::compose("libdcp %1", dcp::version),
2481 dcp::LocalTime().as_string(),
2485 Editor e (dcp->cpls()[0]->file().get());
2486 e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
2489 check_verify_result (
2492 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2493 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Value> property should be 'SMPTE-RDD-52:2020-Bv2.1'") },
2498 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata8)
2500 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata8";
2501 auto dcp = make_simple (dir);
2504 dcp::String::compose("libdcp %1", dcp::version),
2505 dcp::String::compose("libdcp %1", dcp::version),
2506 dcp::LocalTime().as_string(),
2510 Editor e (dcp->cpls()[0]->file().get());
2511 e.replace ("<meta:Property>", "<meta:PropertyX>");
2512 e.replace ("</meta:Property>", "</meta:PropertyX>");
2515 check_verify_result (
2518 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2519 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2520 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2525 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata9)
2527 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata9";
2528 auto dcp = make_simple (dir);
2531 dcp::String::compose("libdcp %1", dcp::version),
2532 dcp::String::compose("libdcp %1", dcp::version),
2533 dcp::LocalTime().as_string(),
2537 Editor e (dcp->cpls()[0]->file().get());
2538 e.replace ("<meta:PropertyList>", "<meta:PropertyListX>");
2539 e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
2542 check_verify_result (
2545 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2546 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2547 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2553 BOOST_AUTO_TEST_CASE (verify_encrypted_cpl_is_signed)
2555 boost::filesystem::path dir = "build/test/verify_encrypted_cpl_is_signed";
2556 prepare_directory (dir);
2557 for (auto i: boost::filesystem::directory_iterator("test/ref/DCP/encryption_test")) {
2558 boost::filesystem::copy_file (i.path(), dir / i.path().filename());
2562 Editor e (dir / "cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
2563 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2566 check_verify_result (
2569 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2570 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::PKL_ANNOTATION_TEXT_DOES_NOT_MATCH_CPL_CONTENT_TITLE_TEXT },
2571 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE },
2572 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE },
2573 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC },
2574 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC },
2575 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_CPL_METADATA },
2576 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CPL_WITH_ENCRYPTED_CONTENT_NOT_SIGNED }