Bv2.1 9.1: PKL annotation text must match CPL ContentTitleText if there is only one...
[libdcp.git] / test / verify_test.cc
1 /*
2     Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
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.
10
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.
15
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/>.
18
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
23     including the two.
24
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.
32 */
33
34 #include "verify.h"
35 #include "util.h"
36 #include "j2k.h"
37 #include "reel.h"
38 #include "reel_mono_picture_asset.h"
39 #include "reel_sound_asset.h"
40 #include "cpl.h"
41 #include "dcp.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"
53 #include "test.h"
54 #include <boost/test/unit_test.hpp>
55 #include <boost/foreach.hpp>
56 #include <boost/algorithm/string.hpp>
57 #include <cstdio>
58 #include <iostream>
59
60 using std::list;
61 using std::pair;
62 using std::string;
63 using std::vector;
64 using std::make_pair;
65 using std::make_shared;
66 using boost::optional;
67 using std::shared_ptr;
68
69
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";
73
74 static void
75 stage (string s, optional<boost::filesystem::path> p)
76 {
77         stages.push_back (make_pair (s, p));
78 }
79
80 static void
81 progress (float)
82 {
83
84 }
85
86 static void
87 prepare_directory (boost::filesystem::path path)
88 {
89         using namespace boost::filesystem;
90         remove_all (path);
91         create_directories (path);
92 }
93
94
95 static vector<boost::filesystem::path>
96 setup (int reference_number, int verify_test_number)
97 {
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());
101         }
102
103         return { dcp::String::compose("build/test/verify_test%1", verify_test_number) };
104
105 }
106
107
108 static
109 void
110 write_dcp_with_single_asset (boost::filesystem::path dir, shared_ptr<dcp::ReelAsset> reel_asset, dcp::Standard standard = dcp::SMPTE)
111 {
112         auto reel = make_shared<dcp::Reel>();
113         reel->add (reel_asset);
114         reel->add (simple_markers());
115
116         auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
117         cpl->add (reel);
118         auto dcp = make_shared<dcp::DCP>(dir);
119         dcp->add (cpl);
120         dcp->write_xml (
121                 standard,
122                 dcp::String::compose("libdcp %1", dcp::version),
123                 dcp::String::compose("libdcp %1", dcp::version),
124                 dcp::LocalTime().as_string(),
125                 "hello"
126                 );
127 }
128
129
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.
132  */
133 class Editor
134 {
135 public:
136         Editor (boost::filesystem::path path)
137                 : _path(path)
138         {
139                 _content = dcp::file_to_string (_path);
140         }
141
142         ~Editor ()
143         {
144                 auto f = fopen(_path.string().c_str(), "w");
145                 BOOST_REQUIRE (f);
146                 fwrite (_content.c_str(), _content.length(), 1, f);
147                 fclose (f);
148         }
149
150         void replace (string a, string b)
151         {
152                 auto old_content = _content;
153                 boost::algorithm::replace_all (_content, a, b);
154                 BOOST_REQUIRE (_content != old_content);
155         }
156
157         void delete_lines (string from, string to)
158         {
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;
163                 _content = "";
164                 for (auto i: lines) {
165                         if (i.find(from) != string::npos) {
166                                 deleting = true;
167                         }
168                         if (!deleting) {
169                                 _content += i + "\n";
170                         }
171                         if (deleting && i.find(to) != string::npos) {
172                                 deleting = false;
173                         }
174                 }
175                 BOOST_REQUIRE (_content != old_content);
176         }
177
178 private:
179         boost::filesystem::path _path;
180         std::string _content;
181 };
182
183
184 static
185 void
186 dump_notes (vector<dcp::VerificationNote> const & notes)
187 {
188         for (auto i: notes) {
189                 std::cout << dcp::note_to_string(i) << "\n";
190         }
191 }
192
193
194 static
195 void
196 check_verify_result (vector<boost::filesystem::path> dir, vector<dcp::VerificationNote> test_notes)
197 {
198         auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
199         BOOST_REQUIRE_EQUAL (notes.size(), test_notes.size());
200 }
201
202
203 static
204 void
205 check_verify_result_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, vector<dcp::VerificationNote::Code> codes)
206 {
207         auto directories = setup (1, n);
208
209         {
210                 Editor e (file(n));
211                 e.replace (from, to);
212         }
213
214         auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
215
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);
221                 ++i;
222                 ++j;
223         }
224 }
225
226
227 /* Check DCP as-is (should be OK) */
228 BOOST_AUTO_TEST_CASE (verify_test1)
229 {
230         stages.clear ();
231         auto directories = setup (1, 1);
232         auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
233
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";
237
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"));
242         ++st;
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));
246         ++st;
247         BOOST_CHECK_EQUAL (st->first, "Checking reel");
248         BOOST_REQUIRE (!st->second);
249         ++st;
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"));
253         ++st;
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"));
257         ++st;
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"));
261         ++st;
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"));
265         ++st;
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));
269         ++st;
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));
273         ++st;
274         BOOST_REQUIRE (st == stages.end());
275
276         BOOST_CHECK_EQUAL (notes.size(), 0);
277 }
278
279 /* Corrupt the MXFs and check that this is spotted */
280 BOOST_AUTO_TEST_CASE (verify_test2)
281 {
282         auto directories = setup (1, 2);
283
284         auto mod = fopen("build/test/verify_test2/video.mxf", "r+b");
285         BOOST_REQUIRE (mod);
286         fseek (mod, 4096, SEEK_SET);
287         int x = 42;
288         fwrite (&x, sizeof(x), 1, mod);
289         fclose (mod);
290
291         mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
292         BOOST_REQUIRE (mod);
293         BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
294         BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
295         fclose (mod);
296
297         dcp::ASDCPErrorSuspender sus;
298         check_verify_result (
299                 directories,
300                 {
301                         { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_HASH_INCORRECT },
302                         { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::SOUND_HASH_INCORRECT }
303                 });
304 }
305
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)
308 {
309         auto directories = setup (1, 3);
310
311         {
312                 Editor e (boost::filesystem::path("build") / "test" / "verify_test3" / dcp_test1_pkl);
313                 e.replace ("<Hash>", "<Hash>x");
314         }
315
316         check_verify_result (
317                 directories,
318                 {
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 }
325                 });
326 }
327
328 /* Corrupt the ContentKind in the CPL */
329 BOOST_AUTO_TEST_CASE (verify_test4)
330 {
331         auto directories = setup (1, 4);
332
333         {
334                 Editor e (boost::filesystem::path("build") / "test" / "verify_test4" / dcp_test1_cpl);
335                 e.replace ("<ContentKind>", "<ContentKind>x");
336         }
337
338         auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
339
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'");
343 }
344
345 static
346 boost::filesystem::path
347 cpl (int n)
348 {
349         return dcp::String::compose("build/test/verify_test%1/%2", n, dcp_test1_cpl);
350 }
351
352 static
353 boost::filesystem::path
354 pkl (int n)
355 {
356         return dcp::String::compose("build/test/verify_test%1/%2", n, dcp_test1_pkl);
357 }
358
359 static
360 boost::filesystem::path
361 asset_map (int n)
362 {
363         return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
364 }
365
366
367 /* FrameRate */
368 BOOST_AUTO_TEST_CASE (verify_test5)
369 {
370         check_verify_result_after_replace (
371                         5, &cpl,
372                         "<FrameRate>24 1", "<FrameRate>99 1",
373                         { dcp::VerificationNote::CPL_HASH_INCORRECT,
374                           dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE }
375                         );
376 }
377
378 /* Missing asset */
379 BOOST_AUTO_TEST_CASE (verify_test6)
380 {
381         auto directories = setup (1, 6);
382
383         boost::filesystem::remove ("build/test/verify_test6/video.mxf");
384         check_verify_result (directories, {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_ASSET }});
385 }
386
387 static
388 boost::filesystem::path
389 assetmap (int n)
390 {
391         return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
392 }
393
394 /* Empty asset filename in ASSETMAP */
395 BOOST_AUTO_TEST_CASE (verify_test7)
396 {
397         check_verify_result_after_replace (
398                         7, &assetmap,
399                         "<Path>video.mxf</Path>", "<Path></Path>",
400                         { dcp::VerificationNote::EMPTY_ASSET_PATH }
401                         );
402 }
403
404 /* Mismatched standard */
405 BOOST_AUTO_TEST_CASE (verify_test8)
406 {
407         check_verify_result_after_replace (
408                         8, &cpl,
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 }
417                         );
418 }
419
420 /* Badly formatted <Id> in CPL */
421 BOOST_AUTO_TEST_CASE (verify_test9)
422 {
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 (
425                         9, &cpl,
426                         "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
427                         { dcp::VerificationNote::XML_VALIDATION_ERROR }
428                         );
429 }
430
431 /* Badly formatted <IssueDate> in CPL */
432 BOOST_AUTO_TEST_CASE (verify_test10)
433 {
434         check_verify_result_after_replace (
435                         10, &cpl,
436                         "<IssueDate>", "<IssueDate>x",
437                         { dcp::VerificationNote::XML_VALIDATION_ERROR,
438                           dcp::VerificationNote::CPL_HASH_INCORRECT }
439                         );
440 }
441
442 /* Badly-formatted <Id> in PKL */
443 BOOST_AUTO_TEST_CASE (verify_test11)
444 {
445         check_verify_result_after_replace (
446                 11, &pkl,
447                 "<Id>urn:uuid:2b9", "<Id>urn:uuid:xb9",
448                 { dcp::VerificationNote::XML_VALIDATION_ERROR }
449                 );
450 }
451
452 /* Badly-formatted <Id> in ASSETMAP */
453 BOOST_AUTO_TEST_CASE (verify_test12)
454 {
455         check_verify_result_after_replace (
456                 12, &asset_map,
457                 "<Id>urn:uuid:07e", "<Id>urn:uuid:x7e",
458                 { dcp::VerificationNote::XML_VALIDATION_ERROR }
459                 );
460 }
461
462 /* Basic test of an Interop DCP */
463 BOOST_AUTO_TEST_CASE (verify_test13)
464 {
465         stages.clear ();
466         auto directories = setup (3, 13);
467         auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
468
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";
472
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"));
477         ++st;
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));
481         ++st;
482         BOOST_CHECK_EQUAL (st->first, "Checking reel");
483         BOOST_REQUIRE (!st->second);
484         ++st;
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"));
488         ++st;
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"));
492         ++st;
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"));
496         ++st;
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"));
500         ++st;
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));
504         ++st;
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));
508         ++st;
509         BOOST_REQUIRE (st == stages.end());
510
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);
515 }
516
517 /* DCP with a short asset */
518 BOOST_AUTO_TEST_CASE (verify_test14)
519 {
520         auto directories = setup (8, 14);
521         check_verify_result (
522                 directories,
523                 {
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 }
529                 });
530 }
531
532
533 static
534 void
535 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
536 {
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());
542         }
543         writer->finalize ();
544
545         auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
546         write_dcp_with_single_asset (dir, reel_asset);
547 }
548
549
550 /* DCP with an over-sized JPEG2000 frame */
551 BOOST_AUTO_TEST_CASE (verify_test15)
552 {
553         int const too_big = 1302083 * 2;
554
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);
559
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());
564
565         boost::filesystem::path const dir("build/test/verify_test15");
566         boost::filesystem::remove_all (dir);
567         dcp_from_frame (oversized_frame, dir);
568
569         check_verify_result (
570                 { dir },
571                 {
572                         { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE_IN_BYTES },
573                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
574                 });
575 }
576
577
578 /* DCP with a nearly over-sized JPEG2000 frame */
579 BOOST_AUTO_TEST_CASE (verify_test16)
580 {
581         int const nearly_too_big = 1302083 * 0.98;
582
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);
587
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());
592
593         boost::filesystem::path const dir("build/test/verify_test16");
594         boost::filesystem::remove_all (dir);
595         dcp_from_frame (oversized_frame, dir);
596
597         check_verify_result (
598                 { dir },
599                 {
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 }
602                 });
603 }
604
605
606 /* DCP with a within-range JPEG2000 frame */
607 BOOST_AUTO_TEST_CASE (verify_test17)
608 {
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));
613
614         boost::filesystem::path const dir("build/test/verify_test17");
615         boost::filesystem::remove_all (dir);
616         dcp_from_frame (frame, dir);
617
618         check_verify_result ({ dir }, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
619 }
620
621
622 /* DCP with valid Interop subtitles */
623 BOOST_AUTO_TEST_CASE (verify_test18)
624 {
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);
631
632         check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE }});
633 }
634
635
636 /* DCP with broken Interop subtitles */
637 BOOST_AUTO_TEST_CASE (verify_test19)
638 {
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);
645
646         {
647                 Editor e (dir / "subs.xml");
648                 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
649         }
650
651         check_verify_result (
652                 { dir },
653                 {
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 }
657                 });
658 }
659
660
661 /* DCP with valid SMPTE subtitles */
662 BOOST_AUTO_TEST_CASE (verify_test20)
663 {
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);
670
671         check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
672 }
673
674
675 /* DCP with broken SMPTE subtitles */
676 BOOST_AUTO_TEST_CASE (verify_test21)
677 {
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);
684
685         check_verify_result (
686                 { dir },
687                 {
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 }
692                 });
693 }
694
695
696 /* VF */
697 BOOST_AUTO_TEST_CASE (verify_test22)
698 {
699         boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
700         prepare_directory (ov_dir);
701
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);
706
707         dcp::DCP ov (ov_dir);
708         ov.read ();
709
710         boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
711         prepare_directory (vf_dir);
712
713         write_dcp_with_single_asset (vf_dir, ov.cpls().front()->reels().front()->main_picture());
714
715         check_verify_result (
716                 { vf_dir },
717                 {
718                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::EXTERNAL_ASSET },
719                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
720                 });
721 }
722
723
724 /* DCP with valid CompositionMetadataAsset */
725 BOOST_AUTO_TEST_CASE (verify_test23)
726 {
727         boost::filesystem::path const dir("build/test/verify_test23");
728         prepare_directory (dir);
729
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);
733
734         auto reel = make_shared<dcp::Reel>();
735         reel->add (reel_asset);
736
737         reel->add (simple_markers(16 * 24 - 1));
738
739         auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
740         cpl->add (reel);
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));
745
746         dcp::DCP dcp (dir);
747         dcp.add (cpl);
748         dcp.write_xml (
749                 dcp::SMPTE,
750                 dcp::String::compose("libdcp %1", dcp::version),
751                 dcp::String::compose("libdcp %1", dcp::version),
752                 dcp::LocalTime().as_string(),
753                 "hello"
754                 );
755
756         check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
757 }
758
759
760 boost::filesystem::path find_cpl (boost::filesystem::path dir)
761 {
762         for (auto i: boost::filesystem::directory_iterator(dir)) {
763                 if (boost::starts_with(i.path().filename().string(), "cpl_")) {
764                         return i.path();
765                 }
766         }
767
768         BOOST_REQUIRE (false);
769         return {};
770 }
771
772
773 /* DCP with invalid CompositionMetadataAsset */
774 BOOST_AUTO_TEST_CASE (verify_test24)
775 {
776         boost::filesystem::path const dir("build/test/verify_test24");
777         prepare_directory (dir);
778
779         auto reel = make_shared<dcp::Reel>();
780         reel->add (black_picture_asset(dir));
781         auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
782         cpl->add (reel);
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);
788
789         reel->add (simple_markers());
790
791         dcp::DCP dcp (dir);
792         dcp.add (cpl);
793         dcp.write_xml (
794                 dcp::SMPTE,
795                 dcp::String::compose("libdcp %1", dcp::version),
796                 dcp::String::compose("libdcp %1", dcp::version),
797                 dcp::LocalTime().as_string(),
798                 "hello"
799                 );
800
801         {
802                 Editor e (find_cpl("build/test/verify_test24"));
803                 e.replace ("MainSound", "MainSoundX");
804         }
805
806         check_verify_result (
807                 { dir },
808                 {
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 }
813                 });
814 }
815
816
817 /* DCP with invalid CompositionMetadataAsset */
818 BOOST_AUTO_TEST_CASE (verify_test25)
819 {
820         boost::filesystem::path const dir("build/test/verify_test25");
821         prepare_directory (dir);
822
823         auto reel = make_shared<dcp::Reel>();
824         reel->add (black_picture_asset(dir));
825         auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
826         cpl->add (reel);
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));
831
832         dcp::DCP dcp (dir);
833         dcp.add (cpl);
834         dcp.write_xml (
835                 dcp::SMPTE,
836                 dcp::String::compose("libdcp %1", dcp::version),
837                 dcp::String::compose("libdcp %1", dcp::version),
838                 dcp::LocalTime().as_string(),
839                 "hello"
840                 );
841
842         {
843                 Editor e (find_cpl("build/test/verify_test25"));
844                 e.replace ("meta:Width", "meta:WidthX");
845         }
846
847         check_verify_result (
848                 { dir },
849                 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::GENERAL_READ }}
850                 );
851 }
852
853
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)
856 {
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);
866
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");
873         i++;
874         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
875         BOOST_REQUIRE (i->note());
876         BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
877         i++;
878         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_CPL_METADATA);
879 }
880
881
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)
884 {
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);
894
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");
901         i++;
902         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
903         BOOST_REQUIRE (i->note());
904         BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
905         i++;
906         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_CPL_METADATA);
907 }
908
909
910 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
911  * the release territory.
912  */
913 BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
914 {
915         boost::filesystem::path const dir("build/test/verify_various_invalid_languages");
916         prepare_directory (dir);
917
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());
926
927         auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
928         cpl->add (reel);
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);
938         dcp->add (cpl);
939         dcp->write_xml (
940                 dcp::SMPTE,
941                 dcp::String::compose("libdcp %1", dcp::version),
942                 dcp::String::compose("libdcp %1", dcp::version),
943                 dcp::LocalTime().as_string(),
944                 "hello"
945                 );
946
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");
953         ++i;
954         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
955         BOOST_REQUIRE (i->note());
956         BOOST_CHECK_EQUAL (*i->note(), "andso-is-this");
957         ++i;
958         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
959         BOOST_REQUIRE (i->note());
960         BOOST_CHECK_EQUAL (*i->note(), "fred-jim");
961         ++i;
962         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
963         BOOST_REQUIRE (i->note());
964         BOOST_CHECK_EQUAL (*i->note(), "frobozz");
965 }
966
967
968 static
969 vector<dcp::VerificationNote>
970 check_picture_size (int width, int height, int frame_rate, bool three_d)
971 {
972         using namespace boost::filesystem;
973
974         path dcp_path = "build/test/verify_picture_test";
975         remove_all (dcp_path);
976         create_directories (dcp_path);
977
978         shared_ptr<dcp::PictureAsset> mp;
979         if (three_d) {
980                 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
981         } else {
982                 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
983         }
984         auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
985
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());
991         }
992         picture_writer->finalize ();
993
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);
1003
1004         auto reel = make_shared<dcp::Reel>();
1005
1006         if (three_d) {
1007                 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
1008         } else {
1009                 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
1010         }
1011
1012         reel->add (simple_markers(frame_rate));
1013
1014         cpl->add (reel);
1015
1016         d->add (cpl);
1017         d->write_xml (
1018                 dcp::SMPTE,
1019                 dcp::String::compose("libdcp %1", dcp::version),
1020                 dcp::String::compose("libdcp %1", dcp::version),
1021                 dcp::LocalTime().as_string(),
1022                 "A Test DCP"
1023                 );
1024
1025         return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
1026 }
1027
1028
1029 static
1030 void
1031 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1032 {
1033         auto notes = check_picture_size(width, height, frame_rate, three_d);
1034         dump_notes (notes);
1035         BOOST_CHECK_EQUAL (notes.size(), 0U);
1036 }
1037
1038
1039 static
1040 void
1041 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1042 {
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);
1047 }
1048
1049
1050 static
1051 void
1052 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1053 {
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);
1058 }
1059
1060
1061 static
1062 void
1063 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1064 {
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);
1069 }
1070
1071
1072 BOOST_AUTO_TEST_CASE (verify_picture_size)
1073 {
1074         using namespace boost::filesystem;
1075
1076         /* 2K scope */
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);
1083
1084         /* 2K flat */
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);
1091
1092         /* 4K scope */
1093         check_picture_size_ok (4096, 1716, 24, false);
1094
1095         /* 4K flat */
1096         check_picture_size_ok (3996, 2160, 24, false);
1097
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);
1103
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);
1108
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);
1112
1113         /* No 4K 3D */
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);
1118 }
1119
1120
1121 static
1122 void
1123 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame, float v_position = 0, string text = "Hello")
1124 {
1125         asset->add (
1126                 make_shared<dcp::SubtitleString>(
1127                         optional<string>(),
1128                         false,
1129                         false,
1130                         false,
1131                         dcp::Colour(),
1132                         42,
1133                         1,
1134                         dcp::Time(start_frame, 24, 24),
1135                         dcp::Time(end_frame, 24, 24),
1136                         0,
1137                         dcp::HALIGN_CENTER,
1138                         v_position,
1139                         dcp::VALIGN_CENTER,
1140                         dcp::DIRECTION_LTR,
1141                         text,
1142                         dcp::NONE,
1143                         dcp::Colour(),
1144                         dcp::Time(),
1145                         dcp::Time()
1146                 )
1147         );
1148 }
1149
1150
1151 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1152 {
1153         boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1154         prepare_directory (dir);
1155
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);
1159         }
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);
1164
1165         check_verify_result (
1166                 { dir },
1167                 {
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 },
1172                 });
1173 }
1174
1175
1176 static
1177 shared_ptr<dcp::SMPTESubtitleAsset>
1178 make_large_subtitle_asset (boost::filesystem::path font_file)
1179 {
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);
1185         }
1186         return asset;
1187 }
1188
1189
1190 template <class T>
1191 void
1192 verify_timed_text_asset_too_large (string name)
1193 {
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");
1200
1201         auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1202         write_dcp_with_single_asset (dir, reel_asset);
1203
1204         check_verify_result (
1205                 { dir },
1206                 {
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 },
1212                 });
1213 }
1214
1215
1216 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1217 {
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");
1220 }
1221
1222
1223 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1224 {
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);
1228
1229         string const xml =
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>"
1241                 "<SubtitleList>"
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>"
1245                 "</Subtitle>"
1246                 "</Font>"
1247                 "</SubtitleList>"
1248                 "</SubtitleReel>";
1249
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);
1253         fclose (xml_file);
1254         auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1255         subs->write (dir / "subs.mxf");
1256
1257         auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1258         dcp->cpls().front()->reels().front()->add(reel_subs);
1259         dcp->write_xml (
1260                 dcp::SMPTE,
1261                 dcp::String::compose("libdcp %1", dcp::version),
1262                 dcp::String::compose("libdcp %1", dcp::version),
1263                 dcp::LocalTime().as_string(),
1264                 "A Test DCP"
1265                 );
1266
1267         check_verify_result (
1268                 { dir },
1269                 {
1270                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE },
1271                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1272                 });
1273 }
1274
1275
1276 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1277 {
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];
1281
1282         {
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);
1289         }
1290
1291         {
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);
1298         }
1299
1300         dcp->write_xml (
1301                 dcp::SMPTE,
1302                 dcp::String::compose("libdcp %1", dcp::version),
1303                 dcp::String::compose("libdcp %1", dcp::version),
1304                 dcp::LocalTime().as_string(),
1305                 "A Test DCP"
1306                 );
1307
1308         check_verify_result (
1309                 { path },
1310                 {
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 }
1314                 });
1315 }
1316
1317
1318 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1319 {
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);
1323
1324         string const xml =
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>"
1336                 "<SubtitleList>"
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>"
1340                 "</Subtitle>"
1341                 "</Font>"
1342                 "</SubtitleList>"
1343                 "</SubtitleReel>";
1344
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);
1348         fclose (xml_file);
1349         auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1350         subs->write (dir / "subs.mxf");
1351
1352         auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1353         dcp->cpls().front()->reels().front()->add(reel_subs);
1354         dcp->write_xml (
1355                 dcp::SMPTE,
1356                 dcp::String::compose("libdcp %1", dcp::version),
1357                 dcp::String::compose("libdcp %1", dcp::version),
1358                 dcp::LocalTime().as_string(),
1359                 "A Test DCP"
1360                 );
1361
1362         check_verify_result (
1363                 { dir },
1364                 {
1365                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1366                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1367                 });
1368 }
1369
1370
1371 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1372 {
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);
1376
1377         string const xml =
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>"
1390                 "<SubtitleList>"
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>"
1394                 "</Subtitle>"
1395                 "</Font>"
1396                 "</SubtitleList>"
1397                 "</SubtitleReel>";
1398
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);
1402         fclose (xml_file);
1403         auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1404         subs->write (dir / "subs.mxf");
1405
1406         auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1407         dcp->cpls().front()->reels().front()->add(reel_subs);
1408         dcp->write_xml (
1409                 dcp::SMPTE,
1410                 dcp::String::compose("libdcp %1", dcp::version),
1411                 dcp::String::compose("libdcp %1", dcp::version),
1412                 dcp::LocalTime().as_string(),
1413                 "A Test DCP"
1414                 );
1415
1416         check_verify_result (
1417                 { dir },
1418                 {
1419                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO },
1420                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1421                 });
1422 }
1423
1424
1425 class TestText
1426 {
1427 public:
1428         TestText (int in_, int out_, float v_position_ = 0, string text_ = "Hello")
1429                 : in(in_)
1430                 , out(out_)
1431                 , v_position(v_position_)
1432                 , text(text_)
1433         {}
1434
1435         int in;
1436         int out;
1437         float v_position;
1438         string text;
1439 };
1440
1441
1442 template <class T>
1443 void
1444 dcp_with_text (boost::filesystem::path dir, vector<TestText> subs)
1445 {
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);
1451         }
1452         asset->set_language (dcp::LanguageTag("de-DE"));
1453         asset->write (dir / "subs.mxf");
1454
1455         auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1456         write_dcp_with_single_asset (dir, reel_asset);
1457 }
1458
1459
1460 BOOST_AUTO_TEST_CASE (verify_text_too_early)
1461 {
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 (
1466                 { dir },
1467                 {
1468                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1469                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1470                 });
1471
1472 }
1473
1474
1475 BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
1476 {
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 }});
1481 }
1482
1483
1484 BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
1485 {
1486         auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
1487         prepare_directory (dir);
1488
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);
1501
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);
1514
1515         auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1516         cpl->add (reel1);
1517         cpl->add (reel2);
1518         auto dcp = make_shared<dcp::DCP>(dir);
1519         dcp->add (cpl);
1520         dcp->write_xml (
1521                 dcp::SMPTE,
1522                 dcp::String::compose("libdcp %1", dcp::version),
1523                 dcp::String::compose("libdcp %1", dcp::version),
1524                 dcp::LocalTime().as_string(),
1525                 "hello"
1526                 );
1527
1528
1529         check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1530 }
1531
1532
1533 BOOST_AUTO_TEST_CASE (verify_text_too_close)
1534 {
1535         auto const dir = boost::filesystem::path("build/test/verify_text_too_close");
1536         dcp_with_text<dcp::ReelSubtitleAsset> (
1537                 dir,
1538                 {
1539                         { 4 * 24,     5 * 24 },
1540                         { 5 * 24 + 1, 6 * 24 },
1541                 });
1542         check_verify_result (
1543                 {dir},
1544                 {
1545                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_CLOSE },
1546                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1547                 });
1548 }
1549
1550
1551 BOOST_AUTO_TEST_CASE (verify_text_not_too_close)
1552 {
1553         auto const dir = boost::filesystem::path("build/test/verify_text_not_too_close");
1554         dcp_with_text<dcp::ReelSubtitleAsset> (
1555                 dir,
1556                 {
1557                         { 4 * 24,      5 * 24 },
1558                         { 5 * 24 + 16, 8 * 24 },
1559                 });
1560         check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1561 }
1562
1563
1564 BOOST_AUTO_TEST_CASE (verify_text_too_short)
1565 {
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 (
1569                 {dir},
1570                 {
1571                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_SHORT },
1572                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1573                 });
1574 }
1575
1576
1577 BOOST_AUTO_TEST_CASE (verify_text_not_too_short)
1578 {
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 }});
1582 }
1583
1584
1585 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines1)
1586 {
1587         auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines1");
1588         dcp_with_text<dcp::ReelSubtitleAsset> (
1589                 dir,
1590                 {
1591                         { 96, 200, 0.0, "We" },
1592                         { 96, 200, 0.1, "have" },
1593                         { 96, 200, 0.2, "four" },
1594                         { 96, 200, 0.3, "lines" }
1595                 });
1596         check_verify_result (
1597                 {dir},
1598                 {
1599                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES },
1600                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1601                 });
1602 }
1603
1604
1605 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines1)
1606 {
1607         auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines1");
1608         dcp_with_text<dcp::ReelSubtitleAsset> (
1609                 dir,
1610                 {
1611                         { 96, 200, 0.0, "We" },
1612                         { 96, 200, 0.1, "have" },
1613                         { 96, 200, 0.2, "four" },
1614                 });
1615         check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1616 }
1617
1618
1619 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines2)
1620 {
1621         auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines2");
1622         dcp_with_text<dcp::ReelSubtitleAsset> (
1623                 dir,
1624                 {
1625                         { 96, 300, 0.0, "We" },
1626                         { 96, 300, 0.1, "have" },
1627                         { 150, 180, 0.2, "four" },
1628                         { 150, 180, 0.3, "lines" }
1629                 });
1630         check_verify_result (
1631                 {dir},
1632                 {
1633                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES },
1634                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1635                 });
1636 }
1637
1638
1639 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines2)
1640 {
1641         auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines2");
1642         dcp_with_text<dcp::ReelSubtitleAsset> (
1643                 dir,
1644                 {
1645                         { 96, 300, 0.0, "We" },
1646                         { 96, 300, 0.1, "have" },
1647                         { 150, 180, 0.2, "four" },
1648                         { 190, 250, 0.3, "lines" }
1649                 });
1650         check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1651 }
1652
1653
1654 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long1)
1655 {
1656         auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long1");
1657         dcp_with_text<dcp::ReelSubtitleAsset> (
1658                 dir,
1659                 {
1660                         { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123" }
1661                 });
1662         check_verify_result (
1663                 {dir},
1664                 {
1665                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_LONGER_THAN_RECOMMENDED },
1666                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1667                 });
1668 }
1669
1670
1671 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long2)
1672 {
1673         auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long2");
1674         dcp_with_text<dcp::ReelSubtitleAsset> (
1675                 dir,
1676                 {
1677                         { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1678                 });
1679         check_verify_result (
1680                 {dir},
1681                 {
1682                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_TOO_LONG },
1683                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1684                 });
1685 }
1686
1687
1688 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines1)
1689 {
1690         auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines1");
1691         dcp_with_text<dcp::ReelClosedCaptionAsset> (
1692                 dir,
1693                 {
1694                         { 96, 200, 0.0, "We" },
1695                         { 96, 200, 0.1, "have" },
1696                         { 96, 200, 0.2, "four" },
1697                         { 96, 200, 0.3, "lines" }
1698                 });
1699         check_verify_result (
1700                 {dir},
1701                 {
1702                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES},
1703                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1704                 });
1705 }
1706
1707
1708 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines1)
1709 {
1710         auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines1");
1711         dcp_with_text<dcp::ReelClosedCaptionAsset> (
1712                 dir,
1713                 {
1714                         { 96, 200, 0.0, "We" },
1715                         { 96, 200, 0.1, "have" },
1716                         { 96, 200, 0.2, "four" },
1717                 });
1718         check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1719 }
1720
1721
1722 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines2)
1723 {
1724         auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines2");
1725         dcp_with_text<dcp::ReelClosedCaptionAsset> (
1726                 dir,
1727                 {
1728                         { 96, 300, 0.0, "We" },
1729                         { 96, 300, 0.1, "have" },
1730                         { 150, 180, 0.2, "four" },
1731                         { 150, 180, 0.3, "lines" }
1732                 });
1733         check_verify_result (
1734                 {dir},
1735                 {
1736                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES},
1737                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1738                 });
1739 }
1740
1741
1742 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines2)
1743 {
1744         auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines2");
1745         dcp_with_text<dcp::ReelClosedCaptionAsset> (
1746                 dir,
1747                 {
1748                         { 96, 300, 0.0, "We" },
1749                         { 96, 300, 0.1, "have" },
1750                         { 150, 180, 0.2, "four" },
1751                         { 190, 250, 0.3, "lines" }
1752                 });
1753         check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1754 }
1755
1756
1757 BOOST_AUTO_TEST_CASE (verify_closed_caption_lines_too_long1)
1758 {
1759         auto const dir = boost::filesystem::path ("build/test/verify_closed_caption_lines_too_long1");
1760         dcp_with_text<dcp::ReelClosedCaptionAsset> (
1761                 dir,
1762                 {
1763                         { 96, 300, 0.0, "0123456789012345678901234567890123" }
1764                 });
1765         check_verify_result (
1766                 {dir},
1767                 {
1768                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_LINE_TOO_LONG },
1769                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1770                 });
1771 }
1772
1773
1774 BOOST_AUTO_TEST_CASE (verify_sound_sampling_rate_must_be_48k)
1775 {
1776         boost::filesystem::path const dir("build/test/verify_sound_sampling_rate_must_be_48k");
1777         prepare_directory (dir);
1778
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);
1788         cpl->add (reel);
1789         auto dcp = make_shared<dcp::DCP>(dir);
1790         dcp->add (cpl);
1791         dcp->write_xml (
1792                 dcp::SMPTE,
1793                 dcp::String::compose("libdcp %1", dcp::version),
1794                 dcp::String::compose("libdcp %1", dcp::version),
1795                 dcp::LocalTime().as_string(),
1796                 "hello"
1797                 );
1798
1799         check_verify_result (
1800                 {dir},
1801                 {
1802                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_SOUND_FRAME_RATE },
1803                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1804                 });
1805 }
1806
1807
1808 BOOST_AUTO_TEST_CASE (verify_cpl_must_have_annotation_text)
1809 {
1810         boost::filesystem::path const dir("build/test/verify_cpl_must_have_annotation_text");
1811         auto dcp = make_simple (dir);
1812         dcp->write_xml (
1813                 dcp::SMPTE,
1814                 dcp::String::compose("libdcp %1", dcp::version),
1815                 dcp::String::compose("libdcp %1", dcp::version),
1816                 dcp::LocalTime().as_string(),
1817                 "A Test DCP"
1818                 );
1819
1820         BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1821
1822         {
1823                 BOOST_REQUIRE (dcp->cpls()[0]->file());
1824                 Editor e(dcp->cpls()[0]->file().get());
1825                 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
1826         }
1827
1828         check_verify_result (
1829                 {dir},
1830                 {
1831                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_ANNOTATION_TEXT_IN_CPL },
1832                         { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1833                 });
1834 }
1835
1836
1837 BOOST_AUTO_TEST_CASE (verify_cpl_annotation_text_should_be_same_as_content_title_text)
1838 {
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);
1841         dcp->write_xml (
1842                 dcp::SMPTE,
1843                 dcp::String::compose("libdcp %1", dcp::version),
1844                 dcp::String::compose("libdcp %1", dcp::version),
1845                 dcp::LocalTime().as_string(),
1846                 "A Test DCP"
1847                 );
1848
1849         BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1850
1851         {
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>");
1855         }
1856
1857         check_verify_result (
1858                 {dir},
1859                 {
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 }
1862                 });
1863 }
1864
1865
1866 BOOST_AUTO_TEST_CASE (verify_reel_assets_durations_must_match)
1867 {
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));
1873
1874         shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
1875         shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
1876
1877         auto reel = make_shared<dcp::Reel>(
1878                 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
1879                 make_shared<dcp::ReelSoundAsset>(ms, 0)
1880                 );
1881
1882         reel->add (simple_markers());
1883         cpl->add (reel);
1884
1885         dcp->add (cpl);
1886         dcp->write_xml (
1887                 dcp::SMPTE,
1888                 dcp::String::compose("libdcp %1", dcp::version),
1889                 dcp::String::compose("libdcp %1", dcp::version),
1890                 dcp::LocalTime().as_string(),
1891                 "A Test DCP"
1892                 );
1893
1894
1895         check_verify_result (
1896                 {dir},
1897                 {
1898                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_ASSET_DURATION },
1899                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1900                 });
1901 }
1902
1903
1904
1905 static
1906 void
1907 verify_subtitles_must_be_in_all_reels_check (boost::filesystem::path dir, bool add_to_reel1, bool add_to_reel2)
1908 {
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);
1913
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);
1920
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)
1924                 );
1925
1926         if (add_to_reel1) {
1927                 reel1->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1928         }
1929
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);
1933
1934         cpl->add (reel1);
1935
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)
1939                 );
1940
1941         if (add_to_reel2) {
1942                 reel2->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1943         }
1944
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);
1948
1949         cpl->add (reel2);
1950
1951         dcp->add (cpl);
1952         dcp->write_xml (
1953                 dcp::SMPTE,
1954                 dcp::String::compose("libdcp %1", dcp::version),
1955                 dcp::String::compose("libdcp %1", dcp::version),
1956                 dcp::LocalTime().as_string(),
1957                 "A Test DCP"
1958                 );
1959 }
1960
1961
1962 BOOST_AUTO_TEST_CASE (verify_subtitles_must_be_in_all_reels)
1963 {
1964         {
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 (
1968                         { dir },
1969                         {
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 }
1972                         });
1973
1974         }
1975
1976         {
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 }});
1980         }
1981
1982         {
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 }});
1986         }
1987 }
1988
1989
1990 static
1991 void
1992 verify_closed_captions_must_be_in_all_reels_check (boost::filesystem::path dir, int caps_in_reel1, int caps_in_reel2)
1993 {
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);
1998
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");
2004
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)
2008                 );
2009
2010         for (int i = 0; i < caps_in_reel1; ++i) {
2011                 reel1->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
2012         }
2013
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);
2017
2018         cpl->add (reel1);
2019
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)
2023                 );
2024
2025         for (int i = 0; i < caps_in_reel2; ++i) {
2026                 reel2->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
2027         }
2028
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);
2032
2033         cpl->add (reel2);
2034
2035         dcp->add (cpl);
2036         dcp->write_xml (
2037                 dcp::SMPTE,
2038                 dcp::String::compose("libdcp %1", dcp::version),
2039                 dcp::String::compose("libdcp %1", dcp::version),
2040                 dcp::LocalTime().as_string(),
2041                 "A Test DCP"
2042                 );
2043 }
2044
2045
2046 BOOST_AUTO_TEST_CASE (verify_closed_captions_must_be_in_all_reels)
2047 {
2048         {
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 (
2052                         {dir},
2053                         {
2054                                 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_ASSET_COUNTS_DIFFER },
2055                                 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
2056                         });
2057         }
2058
2059         {
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 }});
2063         }
2064
2065         {
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 }});
2069         }
2070 }
2071
2072
2073 template <class T>
2074 void
2075 verify_text_entry_point_check (boost::filesystem::path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
2076 {
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);
2081
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);
2088         adjust (reel_text);
2089
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)
2093                 );
2094
2095         reel->add (reel_text);
2096
2097         reel->add (simple_markers(240));
2098
2099         cpl->add (reel);
2100
2101         dcp->add (cpl);
2102         dcp->write_xml (
2103                 dcp::SMPTE,
2104                 dcp::String::compose("libdcp %1", dcp::version),
2105                 dcp::String::compose("libdcp %1", dcp::version),
2106                 dcp::LocalTime().as_string(),
2107                 "A Test DCP"
2108                 );
2109
2110         check_verify_result (
2111                 {dir},
2112                 {
2113                         { dcp::VerificationNote::VERIFY_BV21_ERROR, code },
2114                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
2115                 });
2116 }
2117
2118
2119 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
2120 {
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 ();
2126                         }
2127                 );
2128
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);
2134                         }
2135                 );
2136
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 ();
2142                         }
2143                 );
2144
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);
2150                         }
2151                 );
2152 }
2153
2154
2155 BOOST_AUTO_TEST_CASE (verify_assets_must_have_hashes)
2156 {
2157         RNGFixer fix;
2158
2159         boost::filesystem::path const dir("build/test/verify_assets_must_have_hashes");
2160         auto dcp = make_simple (dir);
2161         dcp->write_xml (
2162                 dcp::SMPTE,
2163                 dcp::String::compose("libdcp %1", dcp::version),
2164                 dcp::String::compose("libdcp %1", dcp::version),
2165                 dcp::LocalTime().as_string(),
2166                 "A Test DCP"
2167                 );
2168
2169         BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2170
2171         {
2172                 BOOST_REQUIRE (dcp->cpls()[0]->file());
2173                 Editor e(dcp->cpls()[0]->file().get());
2174                 e.replace("<Hash>XGhFVrqZqapOJx5Fh2SLjj48Yjg=</Hash>", "");
2175         }
2176
2177         check_verify_result (
2178                 {dir},
2179                 {
2180                         { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2181                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_HASH }
2182                 });
2183 }
2184
2185
2186 static
2187 void
2188 verify_markers_test (
2189         boost::filesystem::path dir,
2190         vector<pair<dcp::Marker, dcp::Time>> markers,
2191         vector<dcp::VerificationNote> test_notes
2192         )
2193 {
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);
2199         }
2200         dcp->cpls()[0]->reels()[0]->add(markers_asset);
2201         dcp->write_xml (
2202                 dcp::SMPTE,
2203                 dcp::String::compose("libdcp %1", dcp::version),
2204                 dcp::String::compose("libdcp %1", dcp::version),
2205                 dcp::LocalTime().as_string(),
2206                 "A Test DCP"
2207                 );
2208
2209         check_verify_result ({dir}, test_notes);
2210 }
2211
2212
2213 BOOST_AUTO_TEST_CASE (verify_markers)
2214 {
2215         verify_markers_test (
2216                 "build/test/verify_markers_all_correct",
2217                 {
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) }
2222                 },
2223                 {}
2224                 );
2225
2226         verify_markers_test (
2227                 "build/test/verify_markers_missing_ffec",
2228                 {
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) }
2232                 },
2233                 {
2234                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE }
2235                 });
2236
2237         verify_markers_test (
2238                 "build/test/verify_markers_missing_ffmc",
2239                 {
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) }
2243                 },
2244                 {
2245                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE }
2246                 });
2247
2248         verify_markers_test (
2249                 "build/test/verify_markers_missing_ffoc",
2250                 {
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) }
2254                 },
2255                 {
2256                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC}
2257                 });
2258
2259         verify_markers_test (
2260                 "build/test/verify_markers_missing_lfoc",
2261                 {
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) }
2265                 },
2266                 {
2267                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC }
2268                 });
2269
2270         verify_markers_test (
2271                 "build/test/verify_markers_incorrect_ffoc",
2272                 {
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) }
2277                 },
2278                 {
2279                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_FFOC }
2280                 });
2281
2282         verify_markers_test (
2283                 "build/test/verify_markers_incorrect_lfoc",
2284                 {
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) }
2289                 },
2290                 {
2291                         { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_LFOC }
2292                 });
2293 }
2294
2295
2296 BOOST_AUTO_TEST_CASE (verify_cpl_metadata_version)
2297 {
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();
2302         dcp->write_xml (
2303                 dcp::SMPTE,
2304                 dcp::String::compose("libdcp %1", dcp::version),
2305                 dcp::String::compose("libdcp %1", dcp::version),
2306                 dcp::LocalTime().as_string(),
2307                 "A Test DCP"
2308                 );
2309
2310         check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER }});
2311 }
2312
2313
2314 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata1)
2315 {
2316         boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata1";
2317         auto dcp = make_simple (dir);
2318         dcp->write_xml (
2319                 dcp::SMPTE,
2320                 dcp::String::compose("libdcp %1", dcp::version),
2321                 dcp::String::compose("libdcp %1", dcp::version),
2322                 dcp::LocalTime().as_string(),
2323                 "A Test DCP"
2324                 );
2325
2326         {
2327                 Editor e (dcp->cpls()[0]->file().get());
2328                 e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
2329         }
2330
2331         check_verify_result (
2332                 {dir},
2333                 {
2334                         { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2335                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
2336                 });
2337 }
2338
2339
2340 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata2)
2341 {
2342         boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata2";
2343         auto dcp = make_simple (dir);
2344         dcp->write_xml (
2345                 dcp::SMPTE,
2346                 dcp::String::compose("libdcp %1", dcp::version),
2347                 dcp::String::compose("libdcp %1", dcp::version),
2348                 dcp::LocalTime().as_string(),
2349                 "A Test DCP"
2350                 );
2351
2352         {
2353                 Editor e (dcp->cpls()[0]->file().get());
2354                 e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
2355         }
2356
2357         check_verify_result (
2358                 {dir},
2359                 {
2360                         { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2361                         { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
2362                 });
2363 }
2364
2365
2366 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata3)
2367 {
2368         boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata3";
2369         auto dcp = make_simple (dir);
2370         dcp->write_xml (
2371                 dcp::SMPTE,
2372                 dcp::String::compose("libdcp %1", dcp::version),
2373                 dcp::String::compose("libdcp %1", dcp::version),
2374                 dcp::LocalTime().as_string(),
2375                 "A Test DCP"
2376                 );
2377
2378         {
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>");
2382         }
2383
2384         check_verify_result (
2385                 {dir},
2386                 {
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 },
2390                 });
2391 }
2392
2393
2394 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata4)
2395 {
2396         boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata4";
2397         auto dcp = make_simple (dir);
2398         dcp->write_xml (
2399                 dcp::SMPTE,
2400                 dcp::String::compose("libdcp %1", dcp::version),
2401                 dcp::String::compose("libdcp %1", dcp::version),
2402                 dcp::LocalTime().as_string(),
2403                 "A Test DCP"
2404                 );
2405
2406         {
2407                 Editor e (dcp->cpls()[0]->file().get());
2408                 e.replace ("Application", "Fred");
2409         }
2410
2411         check_verify_result (
2412                 {dir},
2413                 {
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'") },
2416                 });
2417 }
2418
2419
2420 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata5)
2421 {
2422         boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata5";
2423         auto dcp = make_simple (dir);
2424         dcp->write_xml (
2425                 dcp::SMPTE,
2426                 dcp::String::compose("libdcp %1", dcp::version),
2427                 dcp::String::compose("libdcp %1", dcp::version),
2428                 dcp::LocalTime().as_string(),
2429                 "A Test DCP"
2430                 );
2431         {
2432                 Editor e (dcp->cpls()[0]->file().get());
2433                 e.replace ("DCP Constraints Profile", "Fred");
2434         }
2435
2436         check_verify_result (
2437                 {dir},
2438                 {
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'") },
2441                 });
2442 }
2443
2444
2445 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata6)
2446 {
2447         boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata6";
2448         auto dcp = make_simple (dir);
2449         dcp->write_xml (
2450                 dcp::SMPTE,
2451                 dcp::String::compose("libdcp %1", dcp::version),
2452                 dcp::String::compose("libdcp %1", dcp::version),
2453                 dcp::LocalTime().as_string(),
2454                 "A Test DCP"
2455                 );
2456
2457         {
2458                 Editor e (dcp->cpls()[0]->file().get());
2459                 e.replace ("<meta:Value>", "<meta:ValueX>");
2460                 e.replace ("</meta:Value>", "</meta:ValueX>");
2461         }
2462
2463         check_verify_result (
2464                 {dir},
2465                 {
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 },
2469                 });
2470 }
2471
2472
2473 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata7)
2474 {
2475         boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata7";
2476         auto dcp = make_simple (dir);
2477         dcp->write_xml (
2478                 dcp::SMPTE,
2479                 dcp::String::compose("libdcp %1", dcp::version),
2480                 dcp::String::compose("libdcp %1", dcp::version),
2481                 dcp::LocalTime().as_string(),
2482                 "A Test DCP"
2483                 );
2484         {
2485                 Editor e (dcp->cpls()[0]->file().get());
2486                 e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
2487         }
2488
2489         check_verify_result (
2490                 {dir},
2491                 {
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'") },
2494                 });
2495 }
2496
2497
2498 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata8)
2499 {
2500         boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata8";
2501         auto dcp = make_simple (dir);
2502         dcp->write_xml (
2503                 dcp::SMPTE,
2504                 dcp::String::compose("libdcp %1", dcp::version),
2505                 dcp::String::compose("libdcp %1", dcp::version),
2506                 dcp::LocalTime().as_string(),
2507                 "A Test DCP"
2508                 );
2509         {
2510                 Editor e (dcp->cpls()[0]->file().get());
2511                 e.replace ("<meta:Property>", "<meta:PropertyX>");
2512                 e.replace ("</meta:Property>", "</meta:PropertyX>");
2513         }
2514
2515         check_verify_result (
2516                 {dir},
2517                 {
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 },
2521                 });
2522 }
2523
2524
2525 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata9)
2526 {
2527         boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata9";
2528         auto dcp = make_simple (dir);
2529         dcp->write_xml (
2530                 dcp::SMPTE,
2531                 dcp::String::compose("libdcp %1", dcp::version),
2532                 dcp::String::compose("libdcp %1", dcp::version),
2533                 dcp::LocalTime().as_string(),
2534                 "A Test DCP"
2535                 );
2536         {
2537                 Editor e (dcp->cpls()[0]->file().get());
2538                 e.replace ("<meta:PropertyList>", "<meta:PropertyListX>");
2539                 e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
2540         }
2541
2542         check_verify_result (
2543                 {dir},
2544                 {
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 },
2548                 });
2549 }
2550
2551
2552
2553 BOOST_AUTO_TEST_CASE (verify_encrypted_cpl_is_signed)
2554 {
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());
2559         }
2560
2561         {
2562                 Editor e (dir / "cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
2563                 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2564         }
2565
2566         check_verify_result (
2567                 {dir},
2568                 {
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 }
2577                 });
2578 }
2579