Check that JPEG2000 frames aren't too big (i.e. too
[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 "compose.hpp"
45 #include <boost/test/unit_test.hpp>
46 #include <boost/foreach.hpp>
47 #include <boost/algorithm/string.hpp>
48 #include <cstdio>
49 #include <iostream>
50
51 using std::list;
52 using std::pair;
53 using std::string;
54 using std::vector;
55 using std::make_pair;
56 using boost::optional;
57 using boost::shared_ptr;
58
59
60 static list<pair<string, optional<boost::filesystem::path> > > stages;
61 static int next_verify_test_number = 1;
62
63 static void
64 stage (string s, optional<boost::filesystem::path> p)
65 {
66         stages.push_back (make_pair (s, p));
67 }
68
69 static void
70 progress (float)
71 {
72
73 }
74
75 static vector<boost::filesystem::path>
76 setup (int reference_number, int verify_test_number)
77 {
78         boost::filesystem::remove_all (dcp::String::compose("build/test/verify_test%1", verify_test_number));
79         boost::filesystem::create_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
80         for (boost::filesystem::directory_iterator i(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number)); i != boost::filesystem::directory_iterator(); ++i) {
81                 boost::filesystem::copy_file (i->path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i->path().filename());
82         }
83
84         vector<boost::filesystem::path> directories;
85         directories.push_back (dcp::String::compose("build/test/verify_test%1", verify_test_number));
86         return directories;
87
88 }
89
90
91 /** Class that can alter a file by searching and replacing strings within it.
92  *  On destruction modifies the file whose name was given to the constructor.
93  */
94 class Editor
95 {
96 public:
97         Editor (boost::filesystem::path path)
98                 : _path(path)
99         {
100                 _content = dcp::file_to_string (_path);
101         }
102
103         ~Editor ()
104         {
105                 FILE* f = fopen(_path.string().c_str(), "w");
106                 BOOST_REQUIRE (f);
107                 fwrite (_content.c_str(), _content.length(), 1, f);
108                 fclose (f);
109         }
110
111         void replace (string a, string b)
112         {
113                 boost::algorithm::replace_all (_content, a, b);
114         }
115
116 private:
117         boost::filesystem::path _path;
118         std::string _content;
119 };
120
121 static
122 void
123 dump_notes (list<dcp::VerificationNote> const & notes)
124 {
125         BOOST_FOREACH (dcp::VerificationNote i, notes) {
126                 std::cout << dcp::note_to_string(i) << "\n";
127         }
128 }
129
130 /* Check DCP as-is (should be OK) */
131 BOOST_AUTO_TEST_CASE (verify_test1)
132 {
133         stages.clear ();
134         vector<boost::filesystem::path> directories = setup (1, next_verify_test_number);
135         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, "xsd");
136
137         boost::filesystem::path const cpl_file = dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", next_verify_test_number);
138         boost::filesystem::path const pkl_file = dcp::String::compose("build/test/verify_test1/pkl_ae8a9818-872a-4f86-8493-11dfdea03e09.xml", next_verify_test_number);
139         boost::filesystem::path const assetmap_file = dcp::String::compose("build/test/verify_test1/ASSETMAP.xml", next_verify_test_number);
140
141         list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
142         BOOST_CHECK_EQUAL (st->first, "Checking DCP");
143         BOOST_REQUIRE (st->second);
144         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(dcp::String::compose("build/test/verify_test%1", next_verify_test_number)));
145         ++st;
146         BOOST_CHECK_EQUAL (st->first, "Checking CPL");
147         BOOST_REQUIRE (st->second);
148         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
149         ++st;
150         BOOST_CHECK_EQUAL (st->first, "Checking reel");
151         BOOST_REQUIRE (!st->second);
152         ++st;
153         BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
154         BOOST_REQUIRE (st->second);
155         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(dcp::String::compose("build/test/verify_test%1/video.mxf", next_verify_test_number)));
156         ++st;
157         BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
158         BOOST_REQUIRE (st->second);
159         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(dcp::String::compose("build/test/verify_test%1/video.mxf", next_verify_test_number)));
160         ++st;
161         BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
162         BOOST_REQUIRE (st->second);
163         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(dcp::String::compose("build/test/verify_test%1/audio.mxf", next_verify_test_number)));
164         ++st;
165         BOOST_CHECK_EQUAL (st->first, "Checking PKL");
166         BOOST_REQUIRE (st->second);
167         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
168         ++st;
169         BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
170         BOOST_REQUIRE (st->second);
171         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
172         ++st;
173         BOOST_REQUIRE (st == stages.end());
174
175         dump_notes (notes);
176
177         BOOST_CHECK_EQUAL (notes.size(), 0);
178
179         next_verify_test_number++;
180 }
181
182 /* Corrupt the MXFs and check that this is spotted */
183 BOOST_AUTO_TEST_CASE (verify_test2)
184 {
185         vector<boost::filesystem::path> directories = setup (1, next_verify_test_number++);
186
187         FILE* mod = fopen("build/test/verify_test2/video.mxf", "r+b");
188         BOOST_REQUIRE (mod);
189         fseek (mod, 4096, SEEK_SET);
190         int x = 42;
191         fwrite (&x, sizeof(x), 1, mod);
192         fclose (mod);
193
194         mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
195         BOOST_REQUIRE (mod);
196         fseek (mod, 4096, SEEK_SET);
197         BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
198         fclose (mod);
199
200         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, "xsd");
201
202         BOOST_REQUIRE_EQUAL (notes.size(), 2);
203         BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
204         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_HASH_INCORRECT);
205         BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_ERROR);
206         BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::SOUND_HASH_INCORRECT);
207 }
208
209 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
210 BOOST_AUTO_TEST_CASE (verify_test3)
211 {
212         vector<boost::filesystem::path> directories = setup (1, next_verify_test_number++);
213
214         {
215                 Editor e ("build/test/verify_test3/pkl_ae8a9818-872a-4f86-8493-11dfdea03e09.xml");
216                 e.replace ("<Hash>", "<Hash>x");
217         }
218
219         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, "xsd");
220
221         dump_notes (notes);
222
223         BOOST_REQUIRE_EQUAL (notes.size(), 6);
224         list<dcp::VerificationNote>::const_iterator i = notes.begin();
225         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
226         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
227         ++i;
228         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
229         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DISAGREE);
230         ++i;
231         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
232         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DISAGREE);
233         ++i;
234         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
235         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
236         ++i;
237         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
238         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
239         ++i;
240         BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
241         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
242         ++i;
243 }
244
245 /* Corrupt the ContentKind in the CPL */
246 BOOST_AUTO_TEST_CASE (verify_test4)
247 {
248         vector<boost::filesystem::path> directories = setup (1, next_verify_test_number++);
249
250         {
251                 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
252                 e.replace ("<ContentKind>", "<ContentKind>x");
253         }
254
255         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, "xsd");
256
257         BOOST_REQUIRE_EQUAL (notes.size(), 1);
258         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
259         BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xfeature'");
260 }
261
262 static
263 boost::filesystem::path
264 cpl (int n)
265 {
266         return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
267 }
268
269 static
270 boost::filesystem::path
271 pkl (int n)
272 {
273         return dcp::String::compose("build/test/verify_test%1/pkl_ae8a9818-872a-4f86-8493-11dfdea03e09.xml", n);
274 }
275
276 static
277 boost::filesystem::path
278 asset_map (int n)
279 {
280         return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
281 }
282
283 static
284 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1)
285 {
286         vector<boost::filesystem::path> directories = setup (1, n);
287
288         {
289                 Editor e (file(n));
290                 e.replace (from, to);
291         }
292
293         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, "xsd");
294
295         dump_notes (notes);
296
297         BOOST_REQUIRE_EQUAL (notes.size(), 1);
298         BOOST_CHECK_EQUAL (notes.front().code(), code1);
299 }
300
301 static
302 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)
303 {
304         vector<boost::filesystem::path> directories = setup (1, n);
305
306         {
307                 Editor e (file(n));
308                 e.replace (from, to);
309         }
310
311         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, "xsd");
312
313         dump_notes (notes);
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");
338
339         dump_notes (notes);
340
341         BOOST_REQUIRE_EQUAL (notes.size(), 3);
342         list<dcp::VerificationNote>::const_iterator i = notes.begin ();
343         BOOST_CHECK_EQUAL (i->code(), code1);
344         ++i;
345         BOOST_CHECK_EQUAL (i->code(), code2);
346         ++i;
347         BOOST_CHECK_EQUAL (i->code(), code3);
348 }
349
350 /* FrameRate */
351 BOOST_AUTO_TEST_CASE (verify_test5)
352 {
353         check_after_replace (
354                         next_verify_test_number++, &cpl,
355                         "<FrameRate>24 1", "<FrameRate>99 1",
356                         dcp::VerificationNote::CPL_HASH_INCORRECT,
357                         dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE
358                         );
359 }
360
361 /* Missing asset */
362 BOOST_AUTO_TEST_CASE (verify_test6)
363 {
364         vector<boost::filesystem::path> directories = setup (1, next_verify_test_number++);
365
366         boost::filesystem::remove ("build/test/verify_test6/video.mxf");
367         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, "xsd");
368
369         BOOST_REQUIRE_EQUAL (notes.size(), 1);
370         BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
371         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_ASSET);
372 }
373
374 static
375 boost::filesystem::path
376 assetmap (int n)
377 {
378         return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
379 }
380
381 /* Empty asset filename in ASSETMAP */
382 BOOST_AUTO_TEST_CASE (verify_test7)
383 {
384         check_after_replace (
385                         next_verify_test_number++, &assetmap,
386                         "<Path>video.mxf</Path>", "<Path></Path>",
387                         dcp::VerificationNote::EMPTY_ASSET_PATH
388                         );
389 }
390
391 /* Mismatched standard */
392 BOOST_AUTO_TEST_CASE (verify_test8)
393 {
394         check_after_replace (
395                         next_verify_test_number++, &cpl,
396                         "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
397                         dcp::VerificationNote::MISMATCHED_STANDARD,
398                         dcp::VerificationNote::XML_VALIDATION_ERROR,
399                         dcp::VerificationNote::CPL_HASH_INCORRECT
400                         );
401 }
402
403 /* Badly formatted <Id> in CPL */
404 BOOST_AUTO_TEST_CASE (verify_test9)
405 {
406         /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
407         check_after_replace (
408                         next_verify_test_number++, &cpl,
409                         "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
410                         dcp::VerificationNote::XML_VALIDATION_ERROR
411                         );
412 }
413
414 /* Badly formatted <IssueDate> in CPL */
415 BOOST_AUTO_TEST_CASE (verify_test10)
416 {
417         check_after_replace (
418                         next_verify_test_number++, &cpl,
419                         "<IssueDate>", "<IssueDate>x",
420                         dcp::VerificationNote::XML_VALIDATION_ERROR,
421                         dcp::VerificationNote::CPL_HASH_INCORRECT
422                         );
423 }
424
425 /* Badly-formatted <Id> in PKL */
426 BOOST_AUTO_TEST_CASE (verify_test11)
427 {
428         check_after_replace (
429                 next_verify_test_number++, &pkl,
430                 "<Id>urn:uuid:ae8", "<Id>urn:uuid:xe8",
431                 dcp::VerificationNote::XML_VALIDATION_ERROR
432                 );
433 }
434
435 /* Badly-formatted <Id> in ASSETMAP */
436 BOOST_AUTO_TEST_CASE (verify_test12)
437 {
438         check_after_replace (
439                 next_verify_test_number++, &asset_map,
440                 "<Id>urn:uuid:74e", "<Id>urn:uuid:x4e",
441                 dcp::VerificationNote::XML_VALIDATION_ERROR
442                 );
443 }
444
445 /* Basic test of an Interop DCP */
446 BOOST_AUTO_TEST_CASE (verify_test13)
447 {
448         stages.clear ();
449         vector<boost::filesystem::path> directories = setup (3, next_verify_test_number);
450         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, "xsd");
451
452         boost::filesystem::path const cpl_file = dcp::String::compose("build/test/verify_test%1/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml", next_verify_test_number);
453         boost::filesystem::path const pkl_file = dcp::String::compose("build/test/verify_test%1/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml", next_verify_test_number);
454         boost::filesystem::path const assetmap_file = dcp::String::compose("build/test/verify_test%1/ASSETMAP", next_verify_test_number);
455
456         list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
457         BOOST_CHECK_EQUAL (st->first, "Checking DCP");
458         BOOST_REQUIRE (st->second);
459         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(dcp::String::compose("build/test/verify_test%1", next_verify_test_number)));
460         ++st;
461         BOOST_CHECK_EQUAL (st->first, "Checking CPL");
462         BOOST_REQUIRE (st->second);
463         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
464         ++st;
465         BOOST_CHECK_EQUAL (st->first, "Checking reel");
466         BOOST_REQUIRE (!st->second);
467         ++st;
468         BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
469         BOOST_REQUIRE (st->second);
470         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(dcp::String::compose("build/test/verify_test%1/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf", next_verify_test_number)));
471         ++st;
472         BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
473         BOOST_REQUIRE (st->second);
474         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(dcp::String::compose("build/test/verify_test%1/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf", next_verify_test_number)));
475         ++st;
476         BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
477         BOOST_REQUIRE (st->second);
478         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(dcp::String::compose("build/test/verify_test%1/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf", next_verify_test_number)));
479         ++st;
480         BOOST_CHECK_EQUAL (st->first, "Checking PKL");
481         BOOST_REQUIRE (st->second);
482         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
483         ++st;
484         BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
485         BOOST_REQUIRE (st->second);
486         BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
487         ++st;
488         BOOST_REQUIRE (st == stages.end());
489
490         dump_notes (notes);
491
492         BOOST_CHECK_EQUAL (notes.size(), 0);
493
494         next_verify_test_number++;
495 }
496
497 /* DCP with a short asset */
498 BOOST_AUTO_TEST_CASE (verify_test14)
499 {
500         vector<boost::filesystem::path> directories = setup (8, next_verify_test_number);
501         list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, "xsd");
502
503         dump_notes (notes);
504
505         BOOST_REQUIRE_EQUAL (notes.size(), 4);
506         list<dcp::VerificationNote>::const_iterator i = notes.begin ();
507         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
508         ++i;
509         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
510         ++i;
511         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
512         ++i;
513         BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
514         ++i;
515         next_verify_test_number++;
516 }
517
518
519 static
520 shared_ptr<dcp::OpenJPEGImage>
521 random_image ()
522 {
523         shared_ptr<dcp::OpenJPEGImage> image(new dcp::OpenJPEGImage(dcp::Size(1998, 1080)));
524         int const pixels = 1998 * 1080;
525         for (int i = 0; i < 3; ++i) {
526                 int* p = image->data(i);
527                 for (int j = 0; j < pixels; ++j) {
528                         *p++ = rand();
529                 }
530         }
531         return image;
532 }
533
534
535 static
536 void
537 dcp_from_frame (dcp::Data const& frame, boost::filesystem::path dir)
538 {
539         shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
540         boost::filesystem::create_directories (dir);
541         shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
542         for (int i = 0; i < 24; ++i) {
543                 writer->write (frame.data().get(), frame.size());
544         }
545         writer->finalize ();
546
547         shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelMonoPictureAsset(asset, 0));
548         shared_ptr<dcp::Reel> reel(new dcp::Reel());
549         reel->add (reel_asset);
550         shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
551         cpl->add (reel);
552         shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
553         dcp->add (cpl);
554         dcp->write_xml (dcp::SMPTE);
555 }
556
557
558 /* DCP with an over-sized JPEG2000 frame */
559 BOOST_AUTO_TEST_CASE (verify_test15)
560 {
561         /* Compress a random image with a bandwidth of 500Mbit/s */
562         shared_ptr<dcp::OpenJPEGImage> image = random_image ();
563         dcp::Data frame = dcp::compress_j2k (image, 500000000, 24, false, false);
564
565         boost::filesystem::path const dir("build/test/verify_test15");
566         dcp_from_frame (frame, dir);
567
568         vector<boost::filesystem::path> dirs;
569         dirs.push_back (dir);
570         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, "xsd");
571         BOOST_REQUIRE_EQUAL (notes.size(), 1);
572         BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE);
573 }
574
575
576 /* DCP with a nearly over-sized JPEG2000 frame */
577 BOOST_AUTO_TEST_CASE (verify_test16)
578 {
579         /* Compress a random image with a bandwidth of 500Mbit/s */
580         shared_ptr<dcp::OpenJPEGImage> image = random_image ();
581         dcp::Data frame = dcp::compress_j2k (image, 240000000, 24, false, false);
582
583         boost::filesystem::path const dir("build/test/verify_test16");
584         dcp_from_frame (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");
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 random image with a bandwidth of 500Mbit/s */
598         shared_ptr<dcp::OpenJPEGImage> image = random_image ();
599         dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
600
601         boost::filesystem::path const dir("build/test/verify_test17");
602         dcp_from_frame (frame, dir);
603
604         vector<boost::filesystem::path> dirs;
605         dirs.push_back (dir);
606         list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, "xsd");
607         BOOST_REQUIRE_EQUAL (notes.size(), 0);
608 }
609