X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=test%2Fverify_test.cc;h=4138138f11d215d8517cfe3f8f24320da9ad7a0f;hb=4dca630057509164494b65c2deeb748a51928c73;hp=38be998bb225395f093511b939d56262afad8b2f;hpb=80a97d01ba21df6d06ca750789bfe0bbacaaee4a;p=libdcp.git diff --git a/test/verify_test.cc b/test/verify_test.cc index 38be998b..4138138f 100644 --- a/test/verify_test.cc +++ b/test/verify_test.cc @@ -58,8 +58,9 @@ #include "util.h" #include "verify.h" #include "verify_j2k.h" -#include #include +#include +#include #include #include @@ -88,7 +89,7 @@ static string const dcp_test1_pkl_id = filename_to_id(dcp_test1_pkl); static boost::filesystem::path const dcp_test1_cpl = find_file("test/ref/DCP/dcp_test1", "cpl_").filename(); static string const dcp_test1_cpl_id = filename_to_id(dcp_test1_cpl); -static string const dcp_test1_asset_map_id = "5d51e8a1-b2a5-4da6-9b66-4615c3609440"; +static string const dcp_test1_asset_map_id = "017b3de4-6dda-408d-b19b-6711354b0bc3"; static boost::filesystem::path const encryption_test_cpl = find_file("test/ref/DCP/encryption_test", "cpl_").filename(); static string const encryption_test_cpl_id = filename_to_id(encryption_test_cpl); @@ -118,7 +119,7 @@ prepare_directory (path path) /** Copy dcp_test{reference_number} to build/test/verify_test{verify_test_suffix} - * to make a new sacrifical test DCP. + * to make a new sacrificial test DCP. */ static path setup (int reference_number, string verify_test_suffix) @@ -152,94 +153,6 @@ write_dcp_with_single_asset (path dir, shared_ptr reel_asset, dc } -/** Class that can alter a file by searching and replacing strings within it. - * On destruction modifies the file whose name was given to the constructor. - */ -class Editor -{ -public: - Editor (path path) - : _path(path) - { - _content = dcp::file_to_string (_path); - } - - ~Editor () - { - auto f = fopen(_path.string().c_str(), "w"); - BOOST_REQUIRE (f); - fwrite (_content.c_str(), _content.length(), 1, f); - fclose (f); - } - - void replace (string a, string b) - { - auto old_content = _content; - boost::algorithm::replace_all (_content, a, b); - BOOST_REQUIRE (_content != old_content); - } - - void delete_first_line_containing (string s) - { - vector lines; - boost::algorithm::split (lines, _content, boost::is_any_of("\r\n"), boost::token_compress_on); - auto old_content = _content; - _content = ""; - bool done = false; - for (auto i: lines) { - if (i.find(s) == string::npos || done) { - _content += i + "\n"; - } else { - done = true; - } - } - BOOST_REQUIRE (_content != old_content); - } - - void delete_lines (string from, string to) - { - vector lines; - boost::algorithm::split (lines, _content, boost::is_any_of("\r\n"), boost::token_compress_on); - bool deleting = false; - auto old_content = _content; - _content = ""; - for (auto i: lines) { - if (i.find(from) != string::npos) { - deleting = true; - } - if (!deleting) { - _content += i + "\n"; - } - if (deleting && i.find(to) != string::npos) { - deleting = false; - } - } - BOOST_REQUIRE (_content != old_content); - } - - void insert (string after, string line) - { - vector lines; - boost::algorithm::split (lines, _content, boost::is_any_of("\r\n"), boost::token_compress_on); - auto old_content = _content; - _content = ""; - bool replaced = false; - for (auto i: lines) { - _content += i; - if (!replaced && i.find(after) != string::npos) { - _content += line; - replaced = true; - } - } - BOOST_REQUIRE (_content != old_content); - } - -private: - path _path; - std::string _content; -}; - - LIBDCP_DISABLE_WARNINGS static void @@ -256,17 +169,33 @@ static void check_verify_result (vector dir, vector test_notes) { - auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test); + auto notes = dcp::verify({dir}, &stage, &progress, {}, xsd_test); std::sort (notes.begin(), notes.end()); std::sort (test_notes.begin(), test_notes.end()); string message = "\nVerification notes from test:\n"; for (auto i: notes) { message += " " + note_to_string(i) + "\n"; + message += dcp::String::compose( + " [%1 %2 %3 %4 %5]\n", + static_cast(i.type()), + static_cast(i.code()), + i.note().get_value_or(""), + i.file().get_value_or(""), + i.line().get_value_or(0) + ); } message += "Expected:\n"; for (auto i: test_notes) { message += " " + note_to_string(i) + "\n"; + message += dcp::String::compose( + " [%1 %2 %3 %4 %5]\n", + static_cast(i.type()), + static_cast(i.code()), + i.note().get_value_or(""), + i.file().get_value_or(""), + i.line().get_value_or(0) + ); } BOOST_REQUIRE_MESSAGE (notes == test_notes, message); @@ -288,7 +217,7 @@ check_verify_result_after_replace (string suffix, boost::function e.replace (from, to); } - auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test); + auto notes = dcp::verify({dir}, &stage, &progress, {}, xsd_test); BOOST_REQUIRE_EQUAL (notes.size(), codes.size()); auto i = notes.begin(); @@ -301,11 +230,20 @@ check_verify_result_after_replace (string suffix, boost::function } +static +void +add_font(shared_ptr asset) +{ + dcp::ArrayData fake_font(1024); + asset->add_font("font", fake_font); +} + + BOOST_AUTO_TEST_CASE (verify_no_error) { stages.clear (); auto dir = setup (1, "no_error"); - auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test); + auto notes = dcp::verify({dir}, &stage, &progress, {}, xsd_test); path const cpl_file = dir / dcp_test1_cpl; path const pkl_file = dir / dcp_test1_pkl; @@ -401,9 +339,9 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_picture_sound_hashes) { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, dcp_test1_cpl_id, canonical(dir / dcp_test1_cpl) }, { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_PICTURE_HASHES, canonical(dir / "video.mxf") }, { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_HASHES, canonical(dir / "audio.mxf") }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xLq7ot/GobgrqUYdlbR8FCD5APqs=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl), 26 }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xgVKhC9IkWyzQbgzpFcJ1bpqbtwk=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl), 19 }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xznqYbl53W9ZQtrU2E1FQ6dwdM2M=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl), 12 }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xKcJb7S2K5cNm8RG4kfQD5FTeS0A=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl), 28 }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xtfX1mVIKJCVr1m7Y32Nzxf0+Rpw=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl), 12 }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xwUmt8G+cFFKMGt0ueS9+F1S4uhc=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl), 20 }, }); } @@ -419,8 +357,10 @@ BOOST_AUTO_TEST_CASE (verify_failed_read_content_kind) check_verify_result ( { dir }, - {{ dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::FAILED_READ, string("Bad content kind 'xtrailer'")}} - ); + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, dcp_test1_cpl_id, canonical(dir / dcp_test1_cpl) }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("xtrailer") } + }); } @@ -501,7 +441,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_id) /* There's no MISMATCHED_CPL_HASHES error here because it can't find the correct hash by ID (since the ID is wrong) */ check_verify_result_after_replace ( "invalid_xml_cpl_id", &cpl, - "urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375", + "urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab", "urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358a", { dcp::VerificationNote::Code::INVALID_XML } ); } @@ -544,7 +484,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_standard) { stages.clear (); auto dir = setup (3, "verify_invalid_standard"); - auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test); + auto notes = dcp::verify({dir}, &stage, &progress, {}, xsd_test); path const cpl_file = dir / "cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml"; path const pkl_file = dir / "pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml"; @@ -601,6 +541,12 @@ BOOST_AUTO_TEST_CASE (verify_invalid_standard) BOOST_AUTO_TEST_CASE (verify_invalid_duration) { auto dir = setup (8, "invalid_duration"); + + dcp::DCP dcp(dir); + dcp.read(); + BOOST_REQUIRE(dcp.cpls().size() == 1); + auto cpl = dcp.cpls()[0]; + check_verify_result ( { dir }, { @@ -609,7 +555,12 @@ BOOST_AUTO_TEST_CASE (verify_invalid_duration) { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91") }, { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626") }, { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, string("2") } + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, string("2") }, + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, + dcp::VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT, + cpl->file().get() + ).set_id("d74fda30-d5f4-4c5f-870f-ebc089d97eb7") }); } @@ -620,7 +571,7 @@ dcp_from_frame (dcp::ArrayData const& frame, path dir) { auto asset = make_shared(dcp::Fraction(24, 1), dcp::Standard::SMPTE); create_directories (dir); - auto writer = asset->start_write (dir / "pic.mxf", true); + auto writer = asset->start_write(dir / "pic.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); for (int i = 0; i < 24; ++i) { writer->write (frame.data(), frame.size()); } @@ -711,7 +662,11 @@ BOOST_AUTO_TEST_CASE (verify_valid_interop_subtitles) auto reel_asset = make_shared(asset, dcp::Fraction(24, 1), 16 * 24, 0); write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP); - check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }}); + check_verify_result ( + {dir}, { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} } + }); } @@ -742,8 +697,48 @@ BOOST_AUTO_TEST_CASE (verify_invalid_interop_subtitles) string("element 'Foo' is not allowed for content model '(SubtitleID,MovieTitle,ReelNumber,Language,LoadFont*,Font*,Subtitle*)'"), path(), 29 - } + }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} } + }); +} + + +BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_no_subtitles) +{ + path const dir("build/test/verify_interop_subtitle_asset_with_no_subtitles"); + prepare_directory(dir); + copy_file("test/data/subs4.xml", dir / "subs.xml"); + auto asset = make_shared(dir / "subs.xml"); + auto reel_asset = make_shared(asset, dcp::Fraction(24, 1), 16 * 24, 0); + write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); + + check_verify_result ( + { dir }, + { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} } + }); + +} + + +BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_single_space_subtitle) +{ + path const dir("build/test/verify_interop_subtitle_asset_with_single_space_subtitle"); + prepare_directory(dir); + copy_file("test/data/subs5.xml", dir / "subs.xml"); + auto asset = make_shared(dir / "subs.xml"); + auto reel_asset = make_shared(asset, dcp::Fraction(24, 1), 16 * 24, 0); + write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); + + check_verify_result ( + { dir }, + { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"Arial"} } }); + } @@ -756,7 +751,13 @@ BOOST_AUTO_TEST_CASE (verify_valid_smpte_subtitles) auto reel_asset = make_shared(asset, dcp::Fraction(24, 1), 6046, 0); auto cpl = write_dcp_with_single_asset (dir, reel_asset); - check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + check_verify_result( + {dir}, + { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-04-14T13:19:14.000+02:00"} }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id() } + }); } @@ -785,6 +786,8 @@ BOOST_AUTO_TEST_CASE (verify_invalid_smpte_subtitles) }, { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") }, { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2020-05-09T00:29:21.000+02:00"} }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id() } }); } @@ -805,6 +808,8 @@ BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles) { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf") }, { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-08-09T18:34:46.000+02:00"} }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id() } }); } @@ -823,6 +828,7 @@ BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_child_nodes) { dir }, { { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"} } }); } @@ -840,8 +846,10 @@ BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_empty_child_nodes check_verify_result ( { dir }, { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) }, { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"} }, }); } @@ -891,7 +899,7 @@ BOOST_AUTO_TEST_CASE (verify_valid_cpl_metadata) auto cpl = make_shared("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE); cpl->add (reel); - cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-"); + cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-")); cpl->set_main_sound_sample_rate (48000); cpl->set_main_picture_stored_area (dcp::Size(1998, 1080)); cpl->set_main_picture_active_area (dcp::Size(1440, 1080)); @@ -904,16 +912,35 @@ BOOST_AUTO_TEST_CASE (verify_valid_cpl_metadata) } +path +find_prefix(path dir, string prefix) +{ + auto iter = std::find_if(directory_iterator(dir), directory_iterator(), [prefix](path const& p) { + return boost::starts_with(p.filename().string(), prefix); + }); + + BOOST_REQUIRE(iter != directory_iterator()); + return iter->path(); +} + + path find_cpl (path dir) { - for (auto i: directory_iterator(dir)) { - if (boost::starts_with(i.path().filename().string(), "cpl_")) { - return i.path(); - } - } + return find_prefix(dir, "cpl_"); +} + + +path +find_pkl(path dir) +{ + return find_prefix(dir, "pkl_"); +} + - BOOST_REQUIRE (false); - return {}; +path +find_asset_map(path dir) +{ + return find_prefix(dir, "ASSETMAP"); } @@ -929,7 +956,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_bad_tag) reel->add (black_picture_asset(dir)); auto cpl = make_shared("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE); cpl->add (reel); - cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-"); + cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-")); cpl->set_main_sound_sample_rate (48000); cpl->set_main_picture_stored_area (dcp::Size(1998, 1080)); cpl->set_main_picture_active_area (dcp::Size(1440, 1080)); @@ -979,7 +1006,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_missing_tag) reel->add (black_picture_asset(dir)); auto cpl = make_shared("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE); cpl->add (reel); - cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-"); + cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-")); cpl->set_main_sound_sample_rate (48000); cpl->set_main_picture_stored_area (dcp::Size(1998, 1080)); cpl->set_main_picture_active_area (dcp::Size(1440, 1080)); @@ -1067,7 +1094,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_language3) cpl->add (reel); cpl->_additional_subtitle_languages.push_back("this-is-wrong"); cpl->_additional_subtitle_languages.push_back("andso-is-this"); - cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-"); + cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-")); cpl->set_main_sound_sample_rate (48000); cpl->set_main_picture_stored_area (dcp::Size(1998, 1080)); cpl->set_main_picture_active_area (dcp::Size(1440, 1080)); @@ -1104,7 +1131,7 @@ check_picture_size (int width, int height, int frame_rate, bool three_d) } else { mp = make_shared(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE); } - auto picture_writer = mp->start_write (dcp_path / "video.mxf", false); + auto picture_writer = mp->start_write(dcp_path / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); auto image = black_image (dcp::Size(width, height)); auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048); @@ -1118,10 +1145,10 @@ check_picture_size (int width, int height, int frame_rate, bool three_d) auto cpl = make_shared("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE); cpl->set_annotation_text ("A Test DCP"); cpl->set_issue_date ("2012-07-17T04:45:18+00:00"); - cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-"); + cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-")); cpl->set_main_sound_sample_rate (48000); - cpl->set_main_picture_stored_area (dcp::Size(1998, 1080)); - cpl->set_main_picture_active_area (dcp::Size(1998, 1080)); + cpl->set_main_picture_stored_area(dcp::Size(width, height)); + cpl->set_main_picture_active_area(dcp::Size(width, height)); cpl->set_version_number (1); auto reel = make_shared(); @@ -1140,7 +1167,7 @@ check_picture_size (int width, int height, int frame_rate, bool three_d) d->set_annotation_text("A Test DCP"); d->write_xml(); - return dcp::verify ({dcp_path}, &stage, &progress, xsd_test); + return dcp::verify({dcp_path}, &stage, &progress, {}, xsd_test); } @@ -1240,7 +1267,7 @@ void add_test_subtitle (shared_ptr asset, int start_frame, int end_frame, float v_position = 0, dcp::VAlign v_align = dcp::VAlign::CENTER, string text = "Hello") { asset->add ( - make_shared( + std::make_shared( optional(), false, false, @@ -1254,6 +1281,7 @@ add_test_subtitle (shared_ptr asset, int start_frame, int en dcp::HAlign::CENTER, v_position, v_align, + 0, dcp::Direction::LTR, text, dcp::Effect::NONE, @@ -1275,6 +1303,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_xml_size_in_bytes) for (int i = 0; i < 2048; ++i) { add_test_subtitle (asset, i * 24, i * 24 + 20); } + add_font(asset); asset->set_language (dcp::LanguageTag("de-DE")); asset->write (dir / "subs.mxf"); auto reel_asset = make_shared(asset, dcp::Fraction(24, 1), 49148, 0); @@ -1287,7 +1316,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_xml_size_in_bytes) { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES, - string("419346"), + string("419371"), canonical(dir / "subs.mxf") }, { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, @@ -1327,7 +1356,7 @@ verify_timed_text_asset_too_large (string name) check_verify_result ( { dir }, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, string("121695542"), canonical(dir / "subs.mxf") }, + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, string("121695488"), canonical(dir / "subs.mxf") }, { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES, string("121634816"), canonical(dir / "subs.mxf") }, { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") }, { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, @@ -1351,7 +1380,7 @@ BOOST_AUTO_TEST_CASE (verify_missing_subtitle_language) string const xml = "" - "" + "" "urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a" "Content" "Annotation" @@ -1379,7 +1408,6 @@ BOOST_AUTO_TEST_CASE (verify_missing_subtitle_language) auto reel_subs = make_shared(subs, dcp::Fraction(24, 1), 106, 0); dcp->cpls()[0]->reels()[0]->add(reel_subs); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); check_verify_result ( @@ -1402,6 +1430,7 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_languages) auto subs = make_shared(); subs->set_language (dcp::LanguageTag("de-DE")); subs->add (simple_subtitle()); + add_font(subs); subs->write (path / "subs1.mxf"); auto reel_subs = make_shared(subs, dcp::Fraction(24, 1), reel_length, 0); cpl->reels()[0]->add(reel_subs); @@ -1411,12 +1440,12 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_languages) auto subs = make_shared(); subs->set_language (dcp::LanguageTag("en-US")); subs->add (simple_subtitle()); + add_font(subs); subs->write (path / "subs2.mxf"); auto reel_subs = make_shared(subs, dcp::Fraction(24, 1), reel_length, 0); cpl->reels()[1]->add(reel_subs); } - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); check_verify_result ( @@ -1440,6 +1469,7 @@ BOOST_AUTO_TEST_CASE (verify_multiple_closed_caption_languages_allowed) auto ccaps = make_shared(); ccaps->set_language (dcp::LanguageTag("de-DE")); ccaps->add (simple_subtitle()); + add_font(ccaps); ccaps->write (path / "subs1.mxf"); auto reel_ccaps = make_shared(ccaps, dcp::Fraction(24, 1), reel_length, 0); cpl->reels()[0]->add(reel_ccaps); @@ -1449,12 +1479,12 @@ BOOST_AUTO_TEST_CASE (verify_multiple_closed_caption_languages_allowed) auto ccaps = make_shared(); ccaps->set_language (dcp::LanguageTag("en-US")); ccaps->add (simple_subtitle()); + add_font(ccaps); ccaps->write (path / "subs2.mxf"); auto reel_ccaps = make_shared(ccaps, dcp::Fraction(24, 1), reel_length, 0); cpl->reels()[1]->add(reel_ccaps); } - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); check_verify_result ( @@ -1474,7 +1504,7 @@ BOOST_AUTO_TEST_CASE (verify_missing_subtitle_start_time) string const xml = "" - "" + "" "urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a" "Content" "Annotation" @@ -1502,7 +1532,6 @@ BOOST_AUTO_TEST_CASE (verify_missing_subtitle_start_time) auto reel_subs = make_shared(subs, dcp::Fraction(24, 1), 106, 0); dcp->cpls()[0]->reels()[0]->add(reel_subs); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); check_verify_result ( @@ -1522,7 +1551,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_start_time) string const xml = "" - "" + "" "urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a" "Content" "Annotation" @@ -1551,7 +1580,6 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_start_time) auto reel_subs = make_shared(subs, dcp::Fraction(24, 1), 106, 0); dcp->cpls().front()->reels().front()->add(reel_subs); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); check_verify_result ( @@ -1593,6 +1621,7 @@ dcp_with_text (path dir, vector subs) add_test_subtitle (asset, i.in, i.out, i.v_position, i.v_align, i.text); } asset->set_language (dcp::LanguageTag("de-DE")); + add_font(asset); asset->write (dir / "subs.mxf"); auto reel_asset = make_shared(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0); @@ -1671,6 +1700,7 @@ BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time_on_second_reel) /* Just late enough */ add_test_subtitle (asset1, 4 * 24, 5 * 24); asset1->set_language (dcp::LanguageTag("de-DE")); + add_font(asset1); asset1->write (dir / "subs1.mxf"); auto reel_asset1 = make_shared(asset1, dcp::Fraction(24, 1), 5 * 24, 0); auto reel1 = make_shared(); @@ -1681,6 +1711,7 @@ BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time_on_second_reel) auto asset2 = make_shared(); asset2->set_start_time (dcp::Time()); + add_font(asset2); /* This would be too early on first reel but should be OK on the second */ add_test_subtitle (asset2, 3, 4 * 24); asset2->set_language (dcp::LanguageTag("de-DE")); @@ -1763,6 +1794,7 @@ BOOST_AUTO_TEST_CASE (verify_subtitle_overlapping_reel_boundary) auto asset = make_shared(); asset->set_start_time (dcp::Time()); add_test_subtitle (asset, 0, 4 * 24); + add_font(asset); asset->set_language (dcp::LanguageTag("de-DE")); asset->write (dir / "subs.mxf"); @@ -2117,7 +2149,6 @@ BOOST_AUTO_TEST_CASE (verify_missing_cpl_annotation_text) { path const dir("build/test/verify_missing_cpl_annotation_text"); auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U); @@ -2143,7 +2174,6 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_cpl_annotation_text) { path const dir("build/test/verify_mismatched_cpl_annotation_text"); auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U); @@ -2210,12 +2240,13 @@ verify_subtitles_must_be_in_all_reels_check (path dir, bool add_to_reel1, bool a subs->set_language (dcp::LanguageTag("de-DE")); subs->set_start_time (dcp::Time()); subs->add (simple_subtitle()); + add_font(subs); subs->write (dir / "subs.mxf"); auto reel_subs = make_shared(subs, dcp::Fraction(24, 1), reel_length, 0); auto reel1 = make_shared( - make_shared(simple_picture(dir, "", reel_length), 0), - make_shared(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0) + make_shared(simple_picture(dir, "1", reel_length), 0), + make_shared(simple_sound(dir, "1", dcp::MXFMetadata(), "en-US", reel_length), 0) ); if (add_to_reel1) { @@ -2229,8 +2260,8 @@ verify_subtitles_must_be_in_all_reels_check (path dir, bool add_to_reel1, bool a cpl->add (reel1); auto reel2 = make_shared( - make_shared(simple_picture(dir, "", reel_length), 0), - make_shared(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0) + make_shared(simple_picture(dir, "2", reel_length), 0), + make_shared(simple_sound(dir, "2", dcp::MXFMetadata(), "en-US", reel_length), 0) ); if (add_to_reel2) { @@ -2260,7 +2291,7 @@ BOOST_AUTO_TEST_CASE (verify_missing_main_subtitle_from_some_reels) { dir }, { { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, }); } @@ -2293,11 +2324,12 @@ verify_closed_captions_must_be_in_all_reels_check (path dir, int caps_in_reel1, subs->set_language (dcp::LanguageTag("de-DE")); subs->set_start_time (dcp::Time()); subs->add (simple_subtitle()); + add_font(subs); subs->write (dir / "subs.mxf"); auto reel1 = make_shared( - make_shared(simple_picture(dir, "", reel_length), 0), - make_shared(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0) + make_shared(simple_picture(dir, "1", reel_length), 0), + make_shared(simple_sound(dir, "1", dcp::MXFMetadata(), "en-US", reel_length), 0) ); for (int i = 0; i < caps_in_reel1; ++i) { @@ -2311,8 +2343,8 @@ verify_closed_captions_must_be_in_all_reels_check (path dir, int caps_in_reel1, cpl->add (reel1); auto reel2 = make_shared( - make_shared(simple_picture(dir, "", reel_length), 0), - make_shared(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0) + make_shared(simple_picture(dir, "2", reel_length), 0), + make_shared(simple_sound(dir, "2", dcp::MXFMetadata(), "en-US", reel_length), 0) ); for (int i = 0; i < caps_in_reel2; ++i) { @@ -2374,6 +2406,7 @@ verify_text_entry_point_check (path dir, dcp::VerificationNote::Code code, boost subs->set_language (dcp::LanguageTag("de-DE")); subs->set_start_time (dcp::Time()); subs->add (simple_subtitle()); + add_font(subs); subs->write (dir / "subs.mxf"); auto reel_text = make_shared(subs, dcp::Fraction(24, 1), reel_length, 0); adjust (reel_text); @@ -2397,7 +2430,7 @@ verify_text_entry_point_check (path dir, dcp::VerificationNote::Code code, boost {dir}, { { dcp::VerificationNote::Type::BV21_ERROR, code, subs->id() }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, }); } @@ -2444,7 +2477,6 @@ BOOST_AUTO_TEST_CASE (verify_missing_hash) path const dir("build/test/verify_missing_hash"); auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U); @@ -2483,7 +2515,6 @@ verify_markers_test ( markers_asset->set (i.first, i.second); } dcp->cpls()[0]->reels()[0]->add(markers_asset); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); check_verify_result ({dir}, test_notes); @@ -2580,7 +2611,6 @@ BOOST_AUTO_TEST_CASE (verify_missing_cpl_metadata_version_number) auto dcp = make_simple (dir); auto cpl = dcp->cpls()[0]; cpl->unset_version_number(); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->id(), cpl->file().get() }}); @@ -2591,7 +2621,6 @@ BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata1) { path dir = "build/test/verify_missing_extension_metadata1"; auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U); @@ -2615,7 +2644,6 @@ BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata2) { path dir = "build/test/verify_missing_extension_metadata2"; auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); auto cpl = dcp->cpls()[0]; @@ -2638,7 +2666,6 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata3) { path dir = "build/test/verify_invalid_xml_cpl_extension_metadata3"; auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); auto const cpl = dcp->cpls()[0]; @@ -2663,7 +2690,6 @@ BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata1) { path dir = "build/test/verify_invalid_extension_metadata1"; auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); auto cpl = dcp->cpls()[0]; @@ -2686,7 +2712,6 @@ BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata2) { path dir = "build/test/verify_invalid_extension_metadata2"; auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); auto cpl = dcp->cpls()[0]; @@ -2709,7 +2734,6 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata6) { path dir = "build/test/verify_invalid_xml_cpl_extension_metadata6"; auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); auto const cpl = dcp->cpls()[0]; @@ -2734,7 +2758,6 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata7) { path dir = "build/test/verify_invalid_xml_cpl_extension_metadata7"; auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); auto const cpl = dcp->cpls()[0]; @@ -2757,7 +2780,6 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata8) { path dir = "build/test/verify_invalid_xml_cpl_extension_metadata8"; auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); auto const cpl = dcp->cpls()[0]; @@ -2782,7 +2804,6 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata9) { path dir = "build/test/verify_invalid_xml_cpl_extension_metadata9"; auto dcp = make_simple (dir); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); auto const cpl = dcp->cpls()[0]; @@ -2901,7 +2922,7 @@ BOOST_AUTO_TEST_CASE (verify_partially_encrypted) auto mp = make_shared(dcp::Fraction (24, 1), dcp::Standard::SMPTE); mp->set_key (key); - auto writer = mp->start_write (dir / "video.mxf", false); + auto writer = mp->start_write(dir / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); dcp::ArrayData j2c ("test/data/flat_red.j2c"); for (int i = 0; i < 24; ++i) { writer->write (j2c.data(), j2c.size()); @@ -2926,7 +2947,7 @@ BOOST_AUTO_TEST_CASE (verify_partially_encrypted) cpl->set_issuer ("OpenDCP 0.0.25"); cpl->set_creator ("OpenDCP 0.0.25"); cpl->set_issue_date ("2012-07-17T04:45:18+00:00"); - cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-"); + cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-")); cpl->set_main_sound_sample_rate (48000); cpl->set_main_picture_stored_area (dcp::Size(1998, 1080)); cpl->set_main_picture_active_area (dcp::Size(1440, 1080)); @@ -2954,7 +2975,7 @@ BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_2k) dcp::MonoPictureAsset picture (find_file(private_test / "data" / "JourneyToJah_TLR-1_F_EN-DE-FR_CH_51_2K_LOK_20140225_DGL_SMPTE_OV", "j2c.mxf")); auto reader = picture.start_read (); auto frame = reader->get_frame (0); - verify_j2k (frame, notes); + verify_j2k(frame, 0, 24, notes); BOOST_REQUIRE_EQUAL (notes.size(), 0U); } @@ -2965,7 +2986,7 @@ BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_4k) dcp::MonoPictureAsset picture (find_file(private_test / "data" / "sul", "TLR")); auto reader = picture.start_read (); auto frame = reader->get_frame (0); - verify_j2k (frame, notes); + verify_j2k(frame, 0, 24, notes); BOOST_REQUIRE_EQUAL (notes.size(), 0U); } @@ -2980,7 +3001,7 @@ BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_libdcp) dcp::MonoPictureAsset picture (find_file(dir, "video")); auto reader = picture.start_read (); auto frame = reader->get_frame (0); - verify_j2k (frame, notes); + verify_j2k(frame, 0, 24, notes); BOOST_REQUIRE_EQUAL (notes.size(), 0U); } @@ -3011,16 +3032,17 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_resource_id) BOOST_REQUIRE (ASDCP_SUCCESS(r)); writer.WriteTimedTextResource (dcp::String::compose( "" - "" + "" "urn:uuid:%1" "Content" "Annotation" - "2018-10-02T12:25:14+02:00" + "2018-10-02T12:25:14" "1" "en-US" "25 1" "25" "00:00:00:00" + "urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6" "" "" "" @@ -3075,7 +3097,7 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id) BOOST_REQUIRE (ASDCP_SUCCESS(r)); writer.WriteTimedTextResource (dcp::String::compose( "" - "" + "" "urn:uuid:%1" "Content" "Annotation" @@ -3085,6 +3107,7 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id) "25 1" "25" "00:00:00:00" + "urn:uuid:0ce6e0ba-58b9-4344-8929-4d9c959c2d55" "" "" "" @@ -3108,7 +3131,8 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id) { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) }, { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID }, { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2018-10-02T12:25:14+02:00"} } }); } @@ -3137,7 +3161,6 @@ BOOST_AUTO_TEST_CASE (verify_unexpected_things_in_main_markers) path dir = "build/test/verify_unexpected_things_in_main_markers"; prepare_directory (dir); auto dcp = make_simple (dir, 1, 24); - dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); { @@ -3159,3 +3182,469 @@ BOOST_AUTO_TEST_CASE (verify_unexpected_things_in_main_markers) }); } + +BOOST_AUTO_TEST_CASE(verify_invalid_content_kind) +{ + path dir = "build/test/verify_invalid_content_kind"; + prepare_directory (dir); + auto dcp = make_simple (dir, 1, 24); + dcp->write_xml(); + + { + Editor e(find_cpl(dir)); + e.replace("trailer", "trip"); + } + + dcp::CPL cpl (find_cpl(dir)); + + check_verify_result ( + { dir }, + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("trip") } + }); + +} + + +BOOST_AUTO_TEST_CASE(verify_valid_content_kind) +{ + path dir = "build/test/verify_valid_content_kind"; + prepare_directory (dir); + auto dcp = make_simple (dir, 1, 24); + dcp->write_xml(); + + { + Editor e(find_cpl(dir)); + e.replace("trailer", "trip"); + } + + dcp::CPL cpl (find_cpl(dir)); + + check_verify_result ( + { dir }, + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) }, + }); + +} + + +BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_1) +{ + path dir = "build/test/verify_invalid_main_picture_active_area_1"; + prepare_directory(dir); + auto dcp = make_simple(dir, 1, 24); + dcp->write_xml(); + + auto constexpr area = ""; + + { + Editor e(find_cpl(dir)); + e.delete_lines_after(area, 2); + e.insert(area, "4080"); + e.insert(area, "1997"); + } + + dcp::PKL pkl(find_pkl(dir)); + dcp::CPL cpl(find_cpl(dir)); + + check_verify_result( + { dir }, + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 1997 is not a multiple of 2", canonical(find_cpl(dir)) }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 4080 is bigger than the asset height 1080", canonical(find_cpl(dir)) }, + }); +} + + +BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_2) +{ + path dir = "build/test/verify_invalid_main_picture_active_area_2"; + prepare_directory(dir); + auto dcp = make_simple(dir, 1, 24); + dcp->write_xml(); + + auto constexpr area = ""; + + { + Editor e(find_cpl(dir)); + e.delete_lines_after(area, 2); + e.insert(area, "5125"); + e.insert(area, "9900"); + } + + dcp::PKL pkl(find_pkl(dir)); + dcp::CPL cpl(find_cpl(dir)); + + check_verify_result( + { dir }, + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is not a multiple of 2", canonical(find_cpl(dir)) }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 9900 is bigger than the asset width 1998", canonical(find_cpl(dir)) }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is bigger than the asset height 1080", canonical(find_cpl(dir)) }, + }); +} + + +BOOST_AUTO_TEST_CASE(verify_duplicate_pkl_asset_ids) +{ + RNGFixer rg; + + path dir = "build/test/verify_duplicate_pkl_asset_ids"; + prepare_directory(dir); + auto dcp = make_simple(dir, 1, 24); + dcp->write_xml(); + + { + Editor e(find_pkl(dir)); + e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab"); + } + + dcp::PKL pkl(find_pkl(dir)); + + check_verify_result( + { dir }, + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_PKL, pkl.id(), canonical(find_pkl(dir)) }, + }); +} + + +BOOST_AUTO_TEST_CASE(verify_duplicate_assetmap_asset_ids) +{ + RNGFixer rg; + + path dir = "build/test/verify_duplicate_assetmap_asset_ids"; + prepare_directory(dir); + auto dcp = make_simple(dir, 1, 24); + dcp->write_xml(); + + { + Editor e(find_asset_map(dir)); + e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:97f0f352-5b77-48ee-a558-9df37717f4fa"); + } + + dcp::PKL pkl(find_pkl(dir)); + dcp::AssetMap asset_map(find_asset_map(dir)); + + check_verify_result( + { dir }, + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP, asset_map.id(), canonical(find_asset_map(dir)) }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, string("5407b210-4441-4e97-8b16-8bdc7c12da54") }, + }); +} + + +BOOST_AUTO_TEST_CASE(verify_mismatched_sound_channel_counts) +{ + boost::filesystem::path const path = "build/test/verify_mismatched_sound_channel_counts"; + + dcp::MXFMetadata mxf_meta; + mxf_meta.company_name = "OpenDCP"; + mxf_meta.product_name = "OpenDCP"; + mxf_meta.product_version = "0.0.25"; + + auto constexpr sample_rate = 48000; + auto constexpr frames = 240; + + boost::filesystem::remove_all(path); + boost::filesystem::create_directories(path); + auto dcp = make_shared(path); + auto cpl = make_shared("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE); + cpl->set_annotation_text("hello"); + cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,R")); + cpl->set_main_sound_sample_rate(sample_rate); + cpl->set_main_picture_stored_area(dcp::Size(1998, 1080)); + cpl->set_main_picture_active_area(dcp::Size(1998, 1080)); + cpl->set_version_number(1); + + { + + /* Reel with 2 channels of audio */ + + auto mp = simple_picture(path, "1", frames, {}); + auto ms = simple_sound(path, "1", mxf_meta, "en-US", frames, sample_rate, {}, 2); + + auto reel = make_shared( + std::make_shared(mp, 0), + std::make_shared(ms, 0) + ); + + auto markers = make_shared(dcp::Fraction(24, 1), frames); + markers->set(dcp::Marker::FFOC, dcp::Time(0, 0, 0, 1, 24)); + reel->add(markers); + + cpl->add(reel); + } + + { + /* Reel with 6 channels of audio */ + + auto mp = simple_picture(path, "2", frames, {}); + auto ms = simple_sound(path, "2", mxf_meta, "en-US", frames, sample_rate, {}, 6); + + auto reel = make_shared( + std::make_shared(mp, 0), + std::make_shared(ms, 0) + ); + + auto markers = make_shared(dcp::Fraction(24, 1), frames); + markers->set(dcp::Marker::LFOC, dcp::Time(0, 0, 0, frames - 1, 24)); + reel->add(markers); + + cpl->add(reel); + } + + dcp->add(cpl); + dcp->set_annotation_text("hello"); + dcp->write_xml(); + + check_verify_result( + { path }, + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_CHANNEL_COUNTS, canonical(find_file(path, "audio2")) }, + }); +} + + +BOOST_AUTO_TEST_CASE(verify_invalid_main_sound_configuration) +{ + boost::filesystem::path const path = "build/test/verify_invalid_main_sound_configuration"; + + dcp::MXFMetadata mxf_meta; + mxf_meta.company_name = "OpenDCP"; + mxf_meta.product_name = "OpenDCP"; + mxf_meta.product_version = "0.0.25"; + + auto constexpr sample_rate = 48000; + auto constexpr frames = 240; + + boost::filesystem::remove_all(path); + boost::filesystem::create_directories(path); + auto dcp = make_shared(path); + auto cpl = make_shared("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE); + cpl->set_annotation_text("hello"); + cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,R,C,LFE,Ls,Rs")); + cpl->set_main_sound_sample_rate(sample_rate); + cpl->set_main_picture_stored_area(dcp::Size(1998, 1080)); + cpl->set_main_picture_active_area(dcp::Size(1998, 1080)); + cpl->set_version_number(1); + + auto mp = simple_picture(path, "1", frames, {}); + auto ms = simple_sound(path, "1", mxf_meta, "en-US", frames, sample_rate, {}, 2); + + auto reel = make_shared( + std::make_shared(mp, 0), + std::make_shared(ms, 0) + ); + + auto markers = make_shared(dcp::Fraction(24, 1), frames); + markers->set(dcp::Marker::FFOC, dcp::Time(0, 0, 0, 1, 24)); + markers->set(dcp::Marker::LFOC, dcp::Time(0, 0, 9, 23, 24)); + reel->add(markers); + + cpl->add(reel); + + dcp->add(cpl); + dcp->set_annotation_text("hello"); + dcp->write_xml(); + + check_verify_result( + { path }, + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_SOUND_CONFIGURATION, std::string{"MainSoundConfiguration has 6 channels but sound assets have 2"}, canonical(find_cpl(path)) }, + }); +} + + +BOOST_AUTO_TEST_CASE(verify_invalid_tile_part_size) +{ + boost::filesystem::path const path = "build/test/verify_invalid_tile_part_size"; + auto constexpr video_frames = 24; + auto constexpr sample_rate = 48000; + + boost::filesystem::remove_all(path); + boost::filesystem::create_directories(path); + + auto mp = make_shared(dcp::Fraction(24, 1), dcp::Standard::SMPTE); + auto picture_writer = mp->start_write(path / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + + dcp::Size const size(1998, 1080); + auto image = make_shared(size); + boost::random::mt19937 rng(1); + boost::random::uniform_int_distribution<> dist(0, 4095); + for (int c = 0; c < 3; ++c) { + for (int p = 0; p < (1998 * 1080); ++p) { + image->data(c)[p] = dist(rng); + } + } + auto j2c = dcp::compress_j2k(image, 750000000, video_frames, false, false); + for (int i = 0; i < 24; ++i) { + picture_writer->write(j2c.data(), j2c.size()); + } + picture_writer->finalize(); + + auto dcp = make_shared(path); + auto cpl = make_shared("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE); + cpl->set_content_version( + dcp::ContentVersion("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11", "content-version-label-text") + ); + cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,R,C,LFE,Ls,Rs")); + cpl->set_main_sound_sample_rate(sample_rate); + cpl->set_main_picture_stored_area(dcp::Size(1998, 1080)); + cpl->set_main_picture_active_area(dcp::Size(1998, 1080)); + cpl->set_version_number(1); + + auto ms = simple_sound(path, "", dcp::MXFMetadata(), "en-US", video_frames, sample_rate, {}); + + auto reel = make_shared( + make_shared(mp, 0), + make_shared(ms, 0) + ); + + cpl->add(reel); + dcp->add(cpl); + dcp->set_annotation_text("A Test DCP"); + dcp->write_xml(); + + check_verify_result( + { path }, + { + dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_TILE_PART_SIZE).set_frame(0).set_component(0).set_size(1321721), + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(path / "video.mxf") }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC }, + }); +} + + +BOOST_AUTO_TEST_CASE(verify_too_many_subtitle_namespaces) +{ + boost::filesystem::path const dir = "test/ref/DCP/subtitle_namespace_test"; + check_verify_result( + { dir }, + { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE }, + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(find_file(dir, "sub_")) }, + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, "fc815694-7977-4a27-a8b3-32b9d4075e4c", canonical(find_file(dir, "cpl_")) }, + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, std::string{"315de731-1173-484c-9a35-bdacf5a9d99d"} } + }); +} + + +BOOST_AUTO_TEST_CASE(verify_missing_load_font_for_font) +{ + path const dir("build/test/verify_missing_load_font"); + prepare_directory (dir); + copy_file ("test/data/subs1.xml", dir / "subs.xml"); + { + Editor editor(dir / "subs.xml"); + editor.delete_first_line_containing("LoadFont"); + } + auto asset = make_shared(dir / "subs.xml"); + auto reel_asset = make_shared(asset, dcp::Fraction(24, 1), 16 * 24, 0); + write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP); + + check_verify_result ( + {dir}, { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, + dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT).set_id("theFontId") + }); + +} + + +BOOST_AUTO_TEST_CASE(verify_missing_load_font) +{ + boost::filesystem::path const dir = dcp::String::compose("build/test/%1", boost::unit_test::framework::current_test_case().full_name()); + prepare_directory(dir); + auto dcp = make_simple (dir, 1, 202); + + string const xml = + "" + "" + "urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a" + "Content" + "Annotation" + "2018-10-02T12:25:14+02:00" + "1" + "24 1" + "24" + "00:00:00:00" + "de-DE" + "" + "" + "" + "Hello world" + "" + "" + "" + ""; + + dcp::File xml_file(dir / "subs.xml", "w"); + BOOST_REQUIRE(xml_file); + xml_file.write(xml.c_str(), xml.size(), 1); + xml_file.close(); + auto subs = make_shared(dir / "subs.xml"); + subs->write(dir / "subs.mxf"); + + auto reel_subs = make_shared(subs, dcp::Fraction(24, 1), 202, 0); + dcp->cpls()[0]->reels()[0]->add(reel_subs); + dcp->write_xml(); + + check_verify_result ( + { dir }, + { + dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_LOAD_FONT).set_id(reel_subs->id()) + }); +} + + +BOOST_AUTO_TEST_CASE(verify_spots_wrong_asset) +{ + boost::filesystem::path const dir = "build/test/verify_spots_wrong_asset"; + boost::filesystem::remove_all(dir); + + auto dcp1 = make_simple(dir / "1"); + dcp1->write_xml(); + + auto const asset_1 = dcp::MonoPictureAsset(dir / "1" / "video.mxf").id(); + + auto dcp2 = make_simple(dir / "2"); + dcp2->write_xml(); + auto const asset_2 = dcp::MonoPictureAsset(dir / "2" / "video.mxf").id(); + + boost::filesystem::remove(dir / "1" / "video.mxf"); + boost::filesystem::copy_file(dir / "2" / "video.mxf", dir / "1" / "video.mxf"); + + check_verify_result( + {dir / "1"}, + { + dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_MAP_ID).set_id(asset_1).set_other_id(asset_2) + }); +} + + +BOOST_AUTO_TEST_CASE(verify_cpl_content_version_label_text_empty) +{ + boost::filesystem::path const dir = "build/test/verify_cpl_content_version_label_text_empty"; + boost::filesystem::remove_all(dir); + + auto dcp = make_simple(dir); + BOOST_REQUIRE(dcp->cpls().size() == 1); + auto cpl = dcp->cpls()[0]; + cpl->set_content_version(dcp::ContentVersion("")); + dcp->write_xml(); + + check_verify_result( + {dir}, + { + dcp::VerificationNote(dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT, cpl->file().get()).set_id(cpl->id()) + }); +}