2 Copyright (C) 2018-2019 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
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.
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.
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/>.
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
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.
36 #include "compose.hpp"
37 #include <boost/test/unit_test.hpp>
38 #include <boost/foreach.hpp>
39 #include <boost/algorithm/string.hpp>
48 using boost::optional;
50 static list<pair<string, optional<boost::filesystem::path> > > stages;
53 stage (string s, optional<boost::filesystem::path> p)
55 stages.push_back (make_pair (s, p));
64 static vector<boost::filesystem::path>
67 boost::filesystem::remove_all (dcp::String::compose("build/test/verify_test%1", n));
68 boost::filesystem::create_directory (dcp::String::compose("build/test/verify_test%1", n));
69 for (boost::filesystem::directory_iterator i("test/ref/DCP/dcp_test1"); i != boost::filesystem::directory_iterator(); ++i) {
70 boost::filesystem::copy_file (i->path(), dcp::String::compose("build/test/verify_test%1", n) / i->path().filename());
73 vector<boost::filesystem::path> directories;
74 directories.push_back (dcp::String::compose("build/test/verify_test%1", n));
82 Editor (boost::filesystem::path path)
85 _content = dcp::file_to_string (_path);
90 FILE* f = fopen(_path.string().c_str(), "w");
92 fwrite (_content.c_str(), _content.length(), 1, f);
96 void replace (string a, string b)
98 boost::algorithm::replace_all (_content, a, b);
102 boost::filesystem::path _path;
103 std::string _content;
108 dump_notes (list<dcp::VerificationNote> const & notes)
110 BOOST_FOREACH (dcp::VerificationNote i, notes) {
111 std::cout << dcp::note_to_string(i) << "\n";
115 /* Check DCP as-is (should be OK) */
116 BOOST_AUTO_TEST_CASE (verify_test1)
118 vector<boost::filesystem::path> directories = setup (1);
119 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress);
121 boost::filesystem::path const cpl_file = "build/test/verify_test1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
123 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
124 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
125 BOOST_REQUIRE (st->second);
126 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
128 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
129 BOOST_REQUIRE (st->second);
130 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
132 BOOST_CHECK_EQUAL (st->first, "Checking reel");
133 BOOST_REQUIRE (!st->second);
135 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
136 BOOST_REQUIRE (st->second);
137 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
139 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
140 BOOST_REQUIRE (st->second);
141 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
143 BOOST_REQUIRE (st == stages.end());
147 BOOST_CHECK_EQUAL (notes.size(), 0);
150 /* Corrupt the MXFs and check that this is spotted */
151 BOOST_AUTO_TEST_CASE (verify_test2)
153 vector<boost::filesystem::path> directories = setup (2);
155 FILE* mod = fopen("build/test/verify_test2/video.mxf", "r+b");
157 fseek (mod, 4096, SEEK_SET);
159 fwrite (&x, sizeof(x), 1, mod);
162 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
164 fseek (mod, 4096, SEEK_SET);
165 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
168 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress);
170 BOOST_REQUIRE_EQUAL (notes.size(), 2);
171 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
172 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_HASH_INCORRECT);
173 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_ERROR);
174 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::SOUND_HASH_INCORRECT);
177 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
178 BOOST_AUTO_TEST_CASE (verify_test3)
180 vector<boost::filesystem::path> directories = setup (3);
183 Editor e ("build/test/verify_test3/pkl_ae8a9818-872a-4f86-8493-11dfdea03e09.xml");
184 e.replace ("<Hash>", "<Hash>x");
187 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress);
189 BOOST_REQUIRE_EQUAL (notes.size(), 3);
190 list<dcp::VerificationNote>::const_iterator i = notes.begin();
191 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
192 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
194 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
195 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DISAGREE);
197 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
198 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DISAGREE);
202 /* Corrupt the ContentKind in the CPL */
203 BOOST_AUTO_TEST_CASE (verify_test4)
205 vector<boost::filesystem::path> directories = setup (4);
208 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
209 e.replace ("<ContentKind>", "<ContentKind>x");
212 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress);
214 BOOST_REQUIRE_EQUAL (notes.size(), 1);
215 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
216 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xfeature'");
220 boost::filesystem::path
223 return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
227 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1)
229 vector<boost::filesystem::path> directories = setup (n);
233 e.replace (from, to);
236 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress);
238 BOOST_REQUIRE_EQUAL (notes.size(), 1);
239 BOOST_CHECK_EQUAL (notes.front().code(), code1);
243 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)
245 vector<boost::filesystem::path> directories = setup (n);
249 e.replace (from, to);
252 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress);
254 BOOST_REQUIRE_EQUAL (notes.size(), 2);
255 BOOST_CHECK_EQUAL (notes.front().code(), code1);
256 BOOST_CHECK_EQUAL (notes.back().code(), code2);
260 BOOST_AUTO_TEST_CASE (verify_test5)
262 check_after_replace (
264 "<FrameRate>24 1", "<FrameRate>99 1",
265 dcp::VerificationNote::CPL_HASH_INCORRECT,
266 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE
271 BOOST_AUTO_TEST_CASE (verify_test6)
273 vector<boost::filesystem::path> directories = setup (6);
275 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
276 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress);
278 BOOST_REQUIRE_EQUAL (notes.size(), 1);
279 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
280 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::MISSING_ASSET);
284 boost::filesystem::path
287 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
290 /* Empty asset filename in ASSETMAP */
291 BOOST_AUTO_TEST_CASE (verify_test7)
293 check_after_replace (
295 "<Path>video.mxf</Path>", "<Path></Path>",
296 dcp::VerificationNote::Code::EMPTY_ASSET_PATH
300 /* Mismatched standard */
301 BOOST_AUTO_TEST_CASE (verify_test8)
303 check_after_replace (
305 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
306 dcp::VerificationNote::Code::MISMATCHED_STANDARD,
307 dcp::VerificationNote::Code::CPL_HASH_INCORRECT
311 /* Badly formatted <Id> in CPL */
312 BOOST_AUTO_TEST_CASE (verify_test9)
314 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
315 check_after_replace (
317 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
318 dcp::VerificationNote::Code::BAD_URN_UUID
322 /* Badly formatted <IssueDate> in CPL */
323 BOOST_AUTO_TEST_CASE (verify_test10)
325 check_after_replace (
327 "<IssueDate>", "<IssueDate>x",
328 dcp::VerificationNote::Code::BAD_DATE,
329 dcp::VerificationNote::Code::CPL_HASH_INCORRECT