Hopefully make all tests stable enough to be run with --random,
[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_ae8a9818-872a-4f86-8493-11dfdea03e09.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_ae8a9818-872a-4f86-8493-11dfdea03e09.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_ae8a9818-872a-4f86-8493-11dfdea03e09.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:ae8", "<Id>urn:uuid:xe8",
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:74e", "<Id>urn:uuid:x4e",
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 shared_ptr<dcp::OpenJPEGImage>
519 black_image ()
520 {
521         shared_ptr<dcp::OpenJPEGImage> image(new dcp::OpenJPEGImage(dcp::Size(1998, 1080)));
522         int const pixels = 1998 * 1080;
523         for (int i = 0; i < 3; ++i) {
524                 memset (image->data(i), 0, pixels * sizeof(int));
525         }
526         return image;
527 }
528
529
530 static
531 void
532 dcp_from_frame (dcp::Data const& frame, boost::filesystem::path dir)
533 {
534         shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
535         boost::filesystem::create_directories (dir);
536         shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
537         for (int i = 0; i < 24; ++i) {
538                 writer->write (frame.data().get(), frame.size());
539         }
540         writer->finalize ();
541
542         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelMonoPictureAsset(asset, 0));
543         shared_ptr<dcp::Reel> reel(new dcp::Reel());
544         reel->add (reel_asset);
545         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
546         cpl->add (reel);
547         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
548         dcp->add (cpl);
549         dcp->write_xml (dcp::SMPTE);
550 }
551
552
553 /* DCP with an over-sized JPEG2000 frame */
554 BOOST_AUTO_TEST_CASE (verify_test15)
555 {
556         int const too_big = 1302083 * 2;
557
558         /* Compress a black image */
559         shared_ptr<dcp::OpenJPEGImage> image = black_image ();
560         dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
561         BOOST_REQUIRE (frame.size() < too_big);
562
563         /* Place it in a bigger block with some zero padding at the end */
564         dcp::Data oversized_frame(too_big);
565         memcpy (oversized_frame.data().get(), frame.data().get(), frame.size());
566         memset (oversized_frame.data().get() + frame.size(), 0, too_big - frame.size());
567
568         boost::filesystem::path const dir("build/test/verify_test15");
569         boost::filesystem::remove_all (dir);
570         dcp_from_frame (oversized_frame, dir);
571
572         vector<boost::filesystem::path> dirs;
573         dirs.push_back (dir);
574         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
575         BOOST_REQUIRE_EQUAL (notes.size(), 1);
576         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE);
577 }
578
579
580 /* DCP with a nearly over-sized JPEG2000 frame */
581 BOOST_AUTO_TEST_CASE (verify_test16)
582 {
583         int const nearly_too_big = 1302083 * 0.98;
584
585         /* Compress a black image */
586         shared_ptr<dcp::OpenJPEGImage> image = black_image ();
587         dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
588         BOOST_REQUIRE (frame.size() < nearly_too_big);
589
590         /* Place it in a bigger block with some zero padding at the end */
591         dcp::Data oversized_frame(nearly_too_big);
592         memcpy (oversized_frame.data().get(), frame.data().get(), frame.size());
593         memset (oversized_frame.data().get() + frame.size(), 0, nearly_too_big - frame.size());
594
595         boost::filesystem::path const dir("build/test/verify_test16");
596         boost::filesystem::remove_all (dir);
597         dcp_from_frame (oversized_frame, dir);
598
599         vector<boost::filesystem::path> dirs;
600         dirs.push_back (dir);
601         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
602         BOOST_REQUIRE_EQUAL (notes.size(), 1);
603         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE);
604 }
605
606
607 /* DCP with a within-range JPEG2000 frame */
608 BOOST_AUTO_TEST_CASE (verify_test17)
609 {
610         /* Compress a black image */
611         shared_ptr<dcp::OpenJPEGImage> image = black_image ();
612         dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
613         BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
614
615         boost::filesystem::path const dir("build/test/verify_test17");
616         boost::filesystem::remove_all (dir);
617         dcp_from_frame (frame, dir);
618
619         vector<boost::filesystem::path> dirs;
620         dirs.push_back (dir);
621         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
622         BOOST_REQUIRE_EQUAL (notes.size(), 0);
623 }
624
625
626 /* DCP with valid Interop subtitles */
627 BOOST_AUTO_TEST_CASE (verify_test18)
628 {
629         boost::filesystem::path const dir("build/test/verify_test18");
630         boost::filesystem::remove_all (dir);
631         boost::filesystem::create_directories (dir);
632         boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
633         shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
634         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
635         shared_ptr<dcp::Reel> reel(new dcp::Reel());
636         reel->add (reel_asset);
637         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
638         cpl->add (reel);
639         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
640         dcp->add (cpl);
641         dcp->write_xml (dcp::INTEROP);
642
643         vector<boost::filesystem::path> dirs;
644         dirs.push_back (dir);
645         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
646         BOOST_REQUIRE_EQUAL (notes.size(), 0);
647 }
648
649
650 /* DCP with broken Interop subtitles */
651 BOOST_AUTO_TEST_CASE (verify_test19)
652 {
653         boost::filesystem::path const dir("build/test/verify_test19");
654         boost::filesystem::remove_all (dir);
655         boost::filesystem::create_directories (dir);
656         boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
657         shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
658         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
659         shared_ptr<dcp::Reel> reel(new dcp::Reel());
660         reel->add (reel_asset);
661         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
662         cpl->add (reel);
663         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
664         dcp->add (cpl);
665         dcp->write_xml (dcp::INTEROP);
666
667         {
668                 Editor e (dir / "subs.xml");
669                 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
670         }
671
672         vector<boost::filesystem::path> dirs;
673         dirs.push_back (dir);
674         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
675         dump_notes(notes);
676         BOOST_REQUIRE_EQUAL (notes.size(), 2);
677         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
678         BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
679 }
680
681
682 /* DCP with valid SMPTE subtitles */
683 BOOST_AUTO_TEST_CASE (verify_test20)
684 {
685         boost::filesystem::path const dir("build/test/verify_test20");
686         boost::filesystem::remove_all (dir);
687         boost::filesystem::create_directories (dir);
688         boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
689         shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
690         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
691         shared_ptr<dcp::Reel> reel(new dcp::Reel());
692         reel->add (reel_asset);
693         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
694         cpl->add (reel);
695         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
696         dcp->add (cpl);
697         dcp->write_xml (dcp::SMPTE);
698
699         vector<boost::filesystem::path> dirs;
700         dirs.push_back (dir);
701         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
702         dump_notes (notes);
703         BOOST_REQUIRE_EQUAL (notes.size(), 0);
704 }
705
706
707 /* DCP with broken SMPTE subtitles */
708 BOOST_AUTO_TEST_CASE (verify_test21)
709 {
710         boost::filesystem::path const dir("build/test/verify_test21");
711         boost::filesystem::remove_all (dir);
712         boost::filesystem::create_directories (dir);
713         boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
714         shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
715         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
716         shared_ptr<dcp::Reel> reel(new dcp::Reel());
717         reel->add (reel_asset);
718         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
719         cpl->add (reel);
720         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
721         dcp->add (cpl);
722         dcp->write_xml (dcp::SMPTE);
723
724         vector<boost::filesystem::path> dirs;
725         dirs.push_back (dir);
726         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
727         dump_notes (notes);
728         BOOST_REQUIRE_EQUAL (notes.size(), 2);
729         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
730         BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
731 }
732
733
734 /* VF */
735 BOOST_AUTO_TEST_CASE (verify_test22)
736 {
737         boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
738         boost::filesystem::remove_all (ov_dir);
739         boost::filesystem::create_directories (ov_dir);
740
741         shared_ptr<dcp::OpenJPEGImage> image = black_image ();
742         dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
743         BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
744         dcp_from_frame (frame, ov_dir);
745
746         dcp::DCP ov (ov_dir);
747         ov.read ();
748
749         boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
750         boost::filesystem::remove_all (vf_dir);
751         boost::filesystem::create_directories (vf_dir);
752
753         shared_ptr<dcp::Reel> reel(new dcp::Reel());
754         reel->add (ov.cpls().front()->reels().front()->main_picture());
755         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
756         cpl->add (reel);
757         dcp::DCP vf (vf_dir);
758         vf.add (cpl);
759         vf.write_xml (dcp::SMPTE);
760
761         vector<boost::filesystem::path> dirs;
762         dirs.push_back (vf_dir);
763         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
764         dump_notes (notes);
765         BOOST_REQUIRE_EQUAL (notes.size(), 1);
766         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::EXTERNAL_ASSET);
767 }
768
769