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