14d498b06c5ac3a2657c7c35263ddd75d8af441d
[libdcp.git] / test / verify_test.cc
1 /*
2     Copyright (C) 2018-2020 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 "cpl.h"
40 #include "dcp.h"
41 #include "openjpeg_image.h"
42 #include "mono_picture_asset.h"
43 #include "mono_picture_asset_writer.h"
44 #include "interop_subtitle_asset.h"
45 #include "smpte_subtitle_asset.h"
46 #include "reel_subtitle_asset.h"
47 #include "compose.hpp"
48 #include "test.h"
49 #include <boost/test/unit_test.hpp>
50 #include <boost/foreach.hpp>
51 #include <boost/algorithm/string.hpp>
52 #include <cstdio>
53 #include <iostream>
54
55 using std::list;
56 using std::pair;
57 using std::string;
58 using std::vector;
59 using std::make_pair;
60 using boost::optional;
61 using boost::shared_ptr;
62
63
64 static list<pair<string, optional<boost::filesystem::path> > > stages;
65
66 static void
67 stage (string s, optional<boost::filesystem::path> p)
68 {
69         stages.push_back (make_pair (s, p));
70 }
71
72 static void
73 progress (float)
74 {
75
76 }
77
78 static vector<boost::filesystem::path>
79 setup (int reference_number, int verify_test_number)
80 {
81         boost::filesystem::remove_all (dcp::String::compose("build/test/verify_test%1", verify_test_number));
82         boost::filesystem::create_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
83         for (boost::filesystem::directory_iterator i(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number)); i != boost::filesystem::directory_iterator(); ++i) {
84                 boost::filesystem::copy_file (i->path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i->path().filename());
85         }
86
87         vector<boost::filesystem::path> directories;
88         directories.push_back (dcp::String::compose("build/test/verify_test%1", verify_test_number));
89         return directories;
90
91 }
92
93
94 /** Class that can alter a file by searching and replacing strings within it.
95  *  On destruction modifies the file whose name was given to the constructor.
96  */
97 class Editor
98 {
99 public:
100         Editor (boost::filesystem::path path)
101                 : _path(path)
102         {
103                 _content = dcp::file_to_string (_path);
104         }
105
106         ~Editor ()
107         {
108                 FILE* f = fopen(_path.string().c_str(), "w");
109                 BOOST_REQUIRE (f);
110                 fwrite (_content.c_str(), _content.length(), 1, f);
111                 fclose (f);
112         }
113
114         void replace (string a, string b)
115         {
116                 boost::algorithm::replace_all (_content, a, b);
117         }
118
119 private:
120         boost::filesystem::path _path;
121         std::string _content;
122 };
123
124 static
125 void
126 dump_notes (list<dcp::VerificationNote> const & notes)
127 {
128         BOOST_FOREACH (dcp::VerificationNote i, notes) {
129                 std::cout << dcp::note_to_string(i) << "\n";
130         }
131 }
132
133 /* Check DCP as-is (should be OK) */
134 BOOST_AUTO_TEST_CASE (verify_test1)
135 {
136         stages.clear ();
137         vector<boost::filesystem::path> directories = setup (1, 1);
138         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
139
140         boost::filesystem::path const cpl_file = "build/test/verify_test1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
141         boost::filesystem::path const pkl_file = "build/test/verify_test1/pkl_18be072e-5a0f-44e1-b2eb-c8a52ae12789.xml";
142         boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
143
144         list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
145         BOOST_CHECK_EQUAL (st->first, "Checking DCP");
146         BOOST_REQUIRE (st->second);
147         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
148         ++st;
149         BOOST_CHECK_EQUAL (st->first, "Checking CPL");
150         BOOST_REQUIRE (st->second);
151         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
152         ++st;
153         BOOST_CHECK_EQUAL (st->first, "Checking reel");
154         BOOST_REQUIRE (!st->second);
155         ++st;
156         BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
157         BOOST_REQUIRE (st->second);
158         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
159         ++st;
160         BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
161         BOOST_REQUIRE (st->second);
162         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
163         ++st;
164         BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
165         BOOST_REQUIRE (st->second);
166         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
167         ++st;
168         BOOST_CHECK_EQUAL (st->first, "Checking PKL");
169         BOOST_REQUIRE (st->second);
170         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
171         ++st;
172         BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
173         BOOST_REQUIRE (st->second);
174         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
175         ++st;
176         BOOST_REQUIRE (st == stages.end());
177
178         dump_notes (notes);
179
180         BOOST_CHECK_EQUAL (notes.size(), 0);
181 }
182
183 /* Corrupt the MXFs and check that this is spotted */
184 BOOST_AUTO_TEST_CASE (verify_test2)
185 {
186         vector<boost::filesystem::path> directories = setup (1, 2);
187
188         FILE* mod = fopen("build/test/verify_test2/video.mxf", "r+b");
189         BOOST_REQUIRE (mod);
190         fseek (mod, 4096, SEEK_SET);
191         int x = 42;
192         fwrite (&x, sizeof(x), 1, mod);
193         fclose (mod);
194
195         mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
196         BOOST_REQUIRE (mod);
197         fseek (mod, 4096, SEEK_SET);
198         BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
199         fclose (mod);
200
201         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
202
203         BOOST_REQUIRE_EQUAL (notes.size(), 2);
204         BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
205         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_HASH_INCORRECT);
206         BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_ERROR);
207         BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::SOUND_HASH_INCORRECT);
208 }
209
210 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
211 BOOST_AUTO_TEST_CASE (verify_test3)
212 {
213         vector<boost::filesystem::path> directories = setup (1, 3);
214
215         {
216                 Editor e ("build/test/verify_test3/pkl_18be072e-5a0f-44e1-b2eb-c8a52ae12789.xml");
217                 e.replace ("<Hash>", "<Hash>x");
218         }
219
220         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
221
222         dump_notes (notes);
223
224         BOOST_REQUIRE_EQUAL (notes.size(), 6);
225         list<dcp::VerificationNote>::const_iterator i = notes.begin();
226         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
227         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
228         ++i;
229         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
230         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DISAGREE);
231         ++i;
232         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
233         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DISAGREE);
234         ++i;
235         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
236         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
237         ++i;
238         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
239         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
240         ++i;
241         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
242         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
243         ++i;
244 }
245
246 /* Corrupt the ContentKind in the CPL */
247 BOOST_AUTO_TEST_CASE (verify_test4)
248 {
249         vector<boost::filesystem::path> directories = setup (1, 4);
250
251         {
252                 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
253                 e.replace ("<ContentKind>", "<ContentKind>x");
254         }
255
256         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
257
258         BOOST_REQUIRE_EQUAL (notes.size(), 1);
259         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
260         BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xfeature'");
261 }
262
263 static
264 boost::filesystem::path
265 cpl (int n)
266 {
267         return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
268 }
269
270 static
271 boost::filesystem::path
272 pkl (int n)
273 {
274         return dcp::String::compose("build/test/verify_test%1/pkl_18be072e-5a0f-44e1-b2eb-c8a52ae12789.xml", n);
275 }
276
277 static
278 boost::filesystem::path
279 asset_map (int n)
280 {
281         return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
282 }
283
284 static
285 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1)
286 {
287         vector<boost::filesystem::path> directories = setup (1, n);
288
289         {
290                 Editor e (file(n));
291                 e.replace (from, to);
292         }
293
294         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
295
296         dump_notes (notes);
297
298         BOOST_REQUIRE_EQUAL (notes.size(), 1);
299         BOOST_CHECK_EQUAL (notes.front().code(), code1);
300 }
301
302 static
303 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1, dcp::VerificationNote::Code code2)
304 {
305         vector<boost::filesystem::path> directories = setup (1, n);
306
307         {
308                 Editor e (file(n));
309                 e.replace (from, to);
310         }
311
312         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
313
314         dump_notes (notes);
315
316         BOOST_REQUIRE_EQUAL (notes.size(), 2);
317         BOOST_CHECK_EQUAL (notes.front().code(), code1);
318         BOOST_CHECK_EQUAL (notes.back().code(), code2);
319 }
320
321 static
322 void check_after_replace (
323         int n, boost::function<boost::filesystem::path (int)> file,
324         string from,
325         string to,
326         dcp::VerificationNote::Code code1,
327         dcp::VerificationNote::Code code2,
328         dcp::VerificationNote::Code code3
329         )
330 {
331         vector<boost::filesystem::path> directories = setup (1, n);
332
333         {
334                 Editor e (file(n));
335                 e.replace (from, to);
336         }
337
338         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
339
340         dump_notes (notes);
341
342         BOOST_REQUIRE_EQUAL (notes.size(), 3);
343         list<dcp::VerificationNote>::const_iterator i = notes.begin ();
344         BOOST_CHECK_EQUAL (i->code(), code1);
345         ++i;
346         BOOST_CHECK_EQUAL (i->code(), code2);
347         ++i;
348         BOOST_CHECK_EQUAL (i->code(), code3);
349 }
350
351 /* FrameRate */
352 BOOST_AUTO_TEST_CASE (verify_test5)
353 {
354         check_after_replace (
355                         5, &cpl,
356                         "<FrameRate>24 1", "<FrameRate>99 1",
357                         dcp::VerificationNote::CPL_HASH_INCORRECT,
358                         dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE
359                         );
360 }
361
362 /* Missing asset */
363 BOOST_AUTO_TEST_CASE (verify_test6)
364 {
365         vector<boost::filesystem::path> directories = setup (1, 6);
366
367         boost::filesystem::remove ("build/test/verify_test6/video.mxf");
368         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
369
370         BOOST_REQUIRE_EQUAL (notes.size(), 1);
371         BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
372         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_ASSET);
373 }
374
375 static
376 boost::filesystem::path
377 assetmap (int n)
378 {
379         return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
380 }
381
382 /* Empty asset filename in ASSETMAP */
383 BOOST_AUTO_TEST_CASE (verify_test7)
384 {
385         check_after_replace (
386                         7, &assetmap,
387                         "<Path>video.mxf</Path>", "<Path></Path>",
388                         dcp::VerificationNote::EMPTY_ASSET_PATH
389                         );
390 }
391
392 /* Mismatched standard */
393 BOOST_AUTO_TEST_CASE (verify_test8)
394 {
395         check_after_replace (
396                         8, &cpl,
397                         "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
398                         dcp::VerificationNote::MISMATCHED_STANDARD,
399                         dcp::VerificationNote::XML_VALIDATION_ERROR,
400                         dcp::VerificationNote::CPL_HASH_INCORRECT
401                         );
402 }
403
404 /* Badly formatted <Id> in CPL */
405 BOOST_AUTO_TEST_CASE (verify_test9)
406 {
407         /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
408         check_after_replace (
409                         9, &cpl,
410                         "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
411                         dcp::VerificationNote::XML_VALIDATION_ERROR
412                         );
413 }
414
415 /* Badly formatted <IssueDate> in CPL */
416 BOOST_AUTO_TEST_CASE (verify_test10)
417 {
418         check_after_replace (
419                         10, &cpl,
420                         "<IssueDate>", "<IssueDate>x",
421                         dcp::VerificationNote::XML_VALIDATION_ERROR,
422                         dcp::VerificationNote::CPL_HASH_INCORRECT
423                         );
424 }
425
426 /* Badly-formatted <Id> in PKL */
427 BOOST_AUTO_TEST_CASE (verify_test11)
428 {
429         check_after_replace (
430                 11, &pkl,
431                 "<Id>urn:uuid:18b", "<Id>urn:uuid:x8b",
432                 dcp::VerificationNote::XML_VALIDATION_ERROR
433                 );
434 }
435
436 /* Badly-formatted <Id> in ASSETMAP */
437 BOOST_AUTO_TEST_CASE (verify_test12)
438 {
439         check_after_replace (
440                 12, &asset_map,
441                 "<Id>urn:uuid:ae8", "<Id>urn:uuid:xe8",
442                 dcp::VerificationNote::XML_VALIDATION_ERROR
443                 );
444 }
445
446 /* Basic test of an Interop DCP */
447 BOOST_AUTO_TEST_CASE (verify_test13)
448 {
449         stages.clear ();
450         vector<boost::filesystem::path> directories = setup (3, 13);
451         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
452
453         boost::filesystem::path const cpl_file = "build/test/verify_test13/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
454         boost::filesystem::path const pkl_file = "build/test/verify_test13/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
455         boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
456
457         list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
458         BOOST_CHECK_EQUAL (st->first, "Checking DCP");
459         BOOST_REQUIRE (st->second);
460         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
461         ++st;
462         BOOST_CHECK_EQUAL (st->first, "Checking CPL");
463         BOOST_REQUIRE (st->second);
464         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
465         ++st;
466         BOOST_CHECK_EQUAL (st->first, "Checking reel");
467         BOOST_REQUIRE (!st->second);
468         ++st;
469         BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
470         BOOST_REQUIRE (st->second);
471         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
472         ++st;
473         BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
474         BOOST_REQUIRE (st->second);
475         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
476         ++st;
477         BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
478         BOOST_REQUIRE (st->second);
479         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
480         ++st;
481         BOOST_CHECK_EQUAL (st->first, "Checking PKL");
482         BOOST_REQUIRE (st->second);
483         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
484         ++st;
485         BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
486         BOOST_REQUIRE (st->second);
487         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
488         ++st;
489         BOOST_REQUIRE (st == stages.end());
490
491         dump_notes (notes);
492
493         BOOST_CHECK_EQUAL (notes.size(), 0);
494 }
495
496 /* DCP with a short asset */
497 BOOST_AUTO_TEST_CASE (verify_test14)
498 {
499         vector<boost::filesystem::path> directories = setup (8, 14);
500         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
501
502         dump_notes (notes);
503
504         BOOST_REQUIRE_EQUAL (notes.size(), 4);
505         list<dcp::VerificationNote>::const_iterator i = notes.begin ();
506         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
507         ++i;
508         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
509         ++i;
510         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
511         ++i;
512         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
513         ++i;
514 }
515
516
517 static
518 void
519 dcp_from_frame (dcp::Data const& frame, boost::filesystem::path dir)
520 {
521         shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
522         boost::filesystem::create_directories (dir);
523         shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
524         for (int i = 0; i < 24; ++i) {
525                 writer->write (frame.data().get(), frame.size());
526         }
527         writer->finalize ();
528
529         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelMonoPictureAsset(asset, 0));
530         shared_ptr<dcp::Reel> reel(new dcp::Reel());
531         reel->add (reel_asset);
532         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
533         cpl->add (reel);
534         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
535         dcp->add (cpl);
536         dcp->write_xml (dcp::SMPTE);
537 }
538
539
540 /* DCP with an over-sized JPEG2000 frame */
541 BOOST_AUTO_TEST_CASE (verify_test15)
542 {
543         int const too_big = 1302083 * 2;
544
545         /* Compress a black image */
546         shared_ptr<dcp::OpenJPEGImage> image = black_image ();
547         dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
548         BOOST_REQUIRE (frame.size() < too_big);
549
550         /* Place it in a bigger block with some zero padding at the end */
551         dcp::Data oversized_frame(too_big);
552         memcpy (oversized_frame.data().get(), frame.data().get(), frame.size());
553         memset (oversized_frame.data().get() + frame.size(), 0, too_big - frame.size());
554
555         boost::filesystem::path const dir("build/test/verify_test15");
556         boost::filesystem::remove_all (dir);
557         dcp_from_frame (oversized_frame, dir);
558
559         vector<boost::filesystem::path> dirs;
560         dirs.push_back (dir);
561         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
562         BOOST_REQUIRE_EQUAL (notes.size(), 1);
563         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE);
564 }
565
566
567 /* DCP with a nearly over-sized JPEG2000 frame */
568 BOOST_AUTO_TEST_CASE (verify_test16)
569 {
570         int const nearly_too_big = 1302083 * 0.98;
571
572         /* Compress a black image */
573         shared_ptr<dcp::OpenJPEGImage> image = black_image ();
574         dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
575         BOOST_REQUIRE (frame.size() < nearly_too_big);
576
577         /* Place it in a bigger block with some zero padding at the end */
578         dcp::Data oversized_frame(nearly_too_big);
579         memcpy (oversized_frame.data().get(), frame.data().get(), frame.size());
580         memset (oversized_frame.data().get() + frame.size(), 0, nearly_too_big - frame.size());
581
582         boost::filesystem::path const dir("build/test/verify_test16");
583         boost::filesystem::remove_all (dir);
584         dcp_from_frame (oversized_frame, dir);
585
586         vector<boost::filesystem::path> dirs;
587         dirs.push_back (dir);
588         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
589         BOOST_REQUIRE_EQUAL (notes.size(), 1);
590         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE);
591 }
592
593
594 /* DCP with a within-range JPEG2000 frame */
595 BOOST_AUTO_TEST_CASE (verify_test17)
596 {
597         /* Compress a black image */
598         shared_ptr<dcp::OpenJPEGImage> image = black_image ();
599         dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
600         BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
601
602         boost::filesystem::path const dir("build/test/verify_test17");
603         boost::filesystem::remove_all (dir);
604         dcp_from_frame (frame, dir);
605
606         vector<boost::filesystem::path> dirs;
607         dirs.push_back (dir);
608         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
609         BOOST_REQUIRE_EQUAL (notes.size(), 0);
610 }
611
612
613 /* DCP with valid Interop subtitles */
614 BOOST_AUTO_TEST_CASE (verify_test18)
615 {
616         boost::filesystem::path const dir("build/test/verify_test18");
617         boost::filesystem::remove_all (dir);
618         boost::filesystem::create_directories (dir);
619         boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
620         shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
621         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
622         shared_ptr<dcp::Reel> reel(new dcp::Reel());
623         reel->add (reel_asset);
624         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
625         cpl->add (reel);
626         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
627         dcp->add (cpl);
628         dcp->write_xml (dcp::INTEROP);
629
630         vector<boost::filesystem::path> dirs;
631         dirs.push_back (dir);
632         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
633         BOOST_REQUIRE_EQUAL (notes.size(), 0);
634 }
635
636
637 /* DCP with broken Interop subtitles */
638 BOOST_AUTO_TEST_CASE (verify_test19)
639 {
640         boost::filesystem::path const dir("build/test/verify_test19");
641         boost::filesystem::remove_all (dir);
642         boost::filesystem::create_directories (dir);
643         boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
644         shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
645         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
646         shared_ptr<dcp::Reel> reel(new dcp::Reel());
647         reel->add (reel_asset);
648         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
649         cpl->add (reel);
650         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
651         dcp->add (cpl);
652         dcp->write_xml (dcp::INTEROP);
653
654         {
655                 Editor e (dir / "subs.xml");
656                 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
657         }
658
659         vector<boost::filesystem::path> dirs;
660         dirs.push_back (dir);
661         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
662         dump_notes(notes);
663         BOOST_REQUIRE_EQUAL (notes.size(), 2);
664         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
665         BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
666 }
667
668
669 /* DCP with valid SMPTE subtitles */
670 BOOST_AUTO_TEST_CASE (verify_test20)
671 {
672         boost::filesystem::path const dir("build/test/verify_test20");
673         boost::filesystem::remove_all (dir);
674         boost::filesystem::create_directories (dir);
675         boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
676         shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
677         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
678         shared_ptr<dcp::Reel> reel(new dcp::Reel());
679         reel->add (reel_asset);
680         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
681         cpl->add (reel);
682         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
683         dcp->add (cpl);
684         dcp->write_xml (dcp::SMPTE);
685
686         vector<boost::filesystem::path> dirs;
687         dirs.push_back (dir);
688         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
689         dump_notes (notes);
690         BOOST_REQUIRE_EQUAL (notes.size(), 0);
691 }
692
693
694 /* DCP with broken SMPTE subtitles */
695 BOOST_AUTO_TEST_CASE (verify_test21)
696 {
697         boost::filesystem::path const dir("build/test/verify_test21");
698         boost::filesystem::remove_all (dir);
699         boost::filesystem::create_directories (dir);
700         boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
701         shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
702         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
703         shared_ptr<dcp::Reel> reel(new dcp::Reel());
704         reel->add (reel_asset);
705         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
706         cpl->add (reel);
707         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
708         dcp->add (cpl);
709         dcp->write_xml (dcp::SMPTE);
710
711         vector<boost::filesystem::path> dirs;
712         dirs.push_back (dir);
713         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
714         dump_notes (notes);
715         BOOST_REQUIRE_EQUAL (notes.size(), 2);
716         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
717         BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
718 }
719
720
721 /* VF */
722 BOOST_AUTO_TEST_CASE (verify_test22)
723 {
724         boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
725         boost::filesystem::remove_all (ov_dir);
726         boost::filesystem::create_directories (ov_dir);
727
728         shared_ptr<dcp::OpenJPEGImage> image = black_image ();
729         dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
730         BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
731         dcp_from_frame (frame, ov_dir);
732
733         dcp::DCP ov (ov_dir);
734         ov.read ();
735
736         boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
737         boost::filesystem::remove_all (vf_dir);
738         boost::filesystem::create_directories (vf_dir);
739
740         shared_ptr<dcp::Reel> reel(new dcp::Reel());
741         reel->add (ov.cpls().front()->reels().front()->main_picture());
742         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
743         cpl->add (reel);
744         dcp::DCP vf (vf_dir);
745         vf.add (cpl);
746         vf.write_xml (dcp::SMPTE);
747
748         vector<boost::filesystem::path> dirs;
749         dirs.push_back (vf_dir);
750         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
751         dump_notes (notes);
752         BOOST_REQUIRE_EQUAL (notes.size(), 1);
753         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::EXTERNAL_ASSET);
754 }
755
756