2 Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <boost/filesystem.hpp>
22 #include <libxml++/libxml++.h>
28 #include "exceptions.h"
29 #include "subtitle_asset.h"
30 #include "picture_asset.h"
31 #include "sound_asset.h"
33 #include "certificates.h"
34 #include "crypt_chain.h"
36 #define BOOST_TEST_DYN_LINK
37 #define BOOST_TEST_MODULE libdcp_test
38 #include <boost/test/unit_test.hpp>
44 using boost::shared_ptr;
49 return "test/data/32x32_red_square.j2c";
55 return "test/data/1s_24-bit_48k_silence.wav";
59 BOOST_AUTO_TEST_CASE (dcp_test)
63 Kumu::libdcp_test = true;
65 libdcp::Metadata* t = libdcp::Metadata::instance ();
66 t->issuer = "OpenDCP 0.0.25";
67 t->creator = "OpenDCP 0.0.25";
68 t->company_name = "OpenDCP";
69 t->product_name = "OpenDCP";
70 t->product_version = "0.0.25";
71 t->issue_date = "2012-07-17T04:45:18+00:00";
72 boost::filesystem::remove_all ("build/test/foo");
73 boost::filesystem::create_directories ("build/test/foo");
74 libdcp::DCP d ("build/test/foo");
75 shared_ptr<libdcp::CPL> cpl (new libdcp::CPL ("build/test/foo", "A Test DCP", libdcp::FEATURE, 24, 24));
77 shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset (
89 shared_ptr<libdcp::SoundAsset> ms (new libdcp::SoundAsset (
101 cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (mp, ms, shared_ptr<libdcp::SubtitleAsset> ())));
107 BOOST_AUTO_TEST_CASE (error_test)
109 libdcp::DCP d ("build/test/fred");
111 p.push_back ("frobozz");
113 BOOST_CHECK_THROW (new libdcp::MonoPictureAsset (p, "build/test/bar", "video.mxf", &d.Progress, 24, 24, 32, 32, false), libdcp::FileError);
114 BOOST_CHECK_THROW (new libdcp::SoundAsset (p, "build/test/bar", "audio.mxf", &d.Progress, 24, 24, 0, false), libdcp::FileError);
117 BOOST_AUTO_TEST_CASE (read_dcp)
119 libdcp::DCP d ("test/ref/DCP/foo");
122 list<shared_ptr<const libdcp::CPL> > cpls = d.cpls ();
123 BOOST_CHECK_EQUAL (cpls.size(), 1);
125 BOOST_CHECK_EQUAL (cpls.front()->name(), "A Test DCP");
126 BOOST_CHECK_EQUAL (cpls.front()->content_kind(), libdcp::FEATURE);
127 BOOST_CHECK_EQUAL (cpls.front()->frames_per_second(), 24);
128 BOOST_CHECK_EQUAL (cpls.front()->length(), 24);
131 BOOST_AUTO_TEST_CASE (subtitles1)
133 libdcp::SubtitleAsset subs ("test/data", "subs1.xml");
135 BOOST_CHECK_EQUAL (subs.language(), "French");
137 list<shared_ptr<libdcp::Subtitle> > s = subs.subtitles_at (libdcp::Time (0, 0, 6, 1));
138 BOOST_CHECK_EQUAL (s.size(), 1);
139 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
142 libdcp::Color (255, 255, 255),
144 libdcp::Time (0, 0, 5, 198),
145 libdcp::Time (0, 0, 7, 115),
148 "My jacket was Idi Amin's",
150 libdcp::Color (0, 0, 0),
151 libdcp::Time (0, 0, 0, 1),
152 libdcp::Time (0, 0, 0, 1)
155 s = subs.subtitles_at (libdcp::Time (0, 0, 7, 190));
156 BOOST_CHECK_EQUAL (s.size(), 2);
157 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
160 libdcp::Color (255, 255, 255),
162 libdcp::Time (0, 0, 7, 177),
163 libdcp::Time (0, 0, 11, 31),
166 "My corset was H.M. The Queen's",
168 libdcp::Color (0, 0, 0),
169 libdcp::Time (0, 0, 0, 1),
170 libdcp::Time (0, 0, 0, 1)
172 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
175 libdcp::Color (255, 255, 255),
177 libdcp::Time (0, 0, 7, 177),
178 libdcp::Time (0, 0, 11, 31),
181 "My large wonderbra",
183 libdcp::Color (0, 0, 0),
184 libdcp::Time (0, 0, 0, 1),
185 libdcp::Time (0, 0, 0, 1)
188 s = subs.subtitles_at (libdcp::Time (0, 0, 11, 95));
189 BOOST_CHECK_EQUAL (s.size(), 1);
190 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
193 libdcp::Color (255, 255, 255),
195 libdcp::Time (0, 0, 11, 94),
196 libdcp::Time (0, 0, 13, 63),
199 "Once belonged to the Shah",
201 libdcp::Color (0, 0, 0),
202 libdcp::Time (0, 0, 0, 1),
203 libdcp::Time (0, 0, 0, 1)
206 s = subs.subtitles_at (libdcp::Time (0, 0, 14, 42));
207 BOOST_CHECK_EQUAL (s.size(), 1);
208 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
211 libdcp::Color (255, 255, 255),
213 libdcp::Time (0, 0, 13, 104),
214 libdcp::Time (0, 0, 15, 177),
217 "And these are Roy Hattersley's jeans",
219 libdcp::Color (0, 0, 0),
220 libdcp::Time (0, 0, 0, 1),
221 libdcp::Time (0, 0, 0, 1)
225 BOOST_AUTO_TEST_CASE (subtitles2)
227 libdcp::SubtitleAsset subs ("test/data", "subs2.xml");
229 list<shared_ptr<libdcp::Subtitle> > s = subs.subtitles_at (libdcp::Time (0, 0, 42, 100));
230 BOOST_CHECK_EQUAL (s.size(), 2);
231 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
234 libdcp::Color (255, 255, 255),
236 libdcp::Time (0, 0, 41, 62),
237 libdcp::Time (0, 0, 43, 52),
240 "At afternoon tea with John Peel",
242 libdcp::Color (0, 0, 0),
243 libdcp::Time (0, 0, 0, 0),
244 libdcp::Time (0, 0, 0, 0)
246 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
249 libdcp::Color (255, 255, 255),
251 libdcp::Time (0, 0, 41, 62),
252 libdcp::Time (0, 0, 43, 52),
255 "I enquired if his accent was real",
257 libdcp::Color (0, 0, 0),
258 libdcp::Time (0, 0, 0, 0),
259 libdcp::Time (0, 0, 0, 0)
262 s = subs.subtitles_at (libdcp::Time (0, 0, 50, 50));
263 BOOST_CHECK_EQUAL (s.size(), 2);
264 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
267 libdcp::Color (255, 255, 255),
269 libdcp::Time (0, 0, 50, 42),
270 libdcp::Time (0, 0, 52, 21),
273 "He said \"out of the house",
275 libdcp::Color (0, 0, 0),
276 libdcp::Time (0, 0, 0, 0),
277 libdcp::Time (0, 0, 0, 0)
279 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
282 libdcp::Color (255, 255, 255),
284 libdcp::Time (0, 0, 50, 42),
285 libdcp::Time (0, 0, 52, 21),
288 "I'm incredibly scouse",
290 libdcp::Color (0, 0, 0),
291 libdcp::Time (0, 0, 0, 0),
292 libdcp::Time (0, 0, 0, 0)
295 s = subs.subtitles_at (libdcp::Time (0, 1, 2, 300));
296 BOOST_CHECK_EQUAL (s.size(), 2);
297 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
300 libdcp::Color (255, 255, 255),
302 libdcp::Time (0, 1, 2, 208),
303 libdcp::Time (0, 1, 4, 10),
306 "At home it depends how I feel.\"",
308 libdcp::Color (0, 0, 0),
309 libdcp::Time (0, 0, 0, 0),
310 libdcp::Time (0, 0, 0, 0)
312 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
315 libdcp::Color (255, 255, 255),
317 libdcp::Time (0, 1, 2, 208),
318 libdcp::Time (0, 1, 4, 10),
321 "I spent a long weekend in Brighton",
323 libdcp::Color (0, 0, 0),
324 libdcp::Time (0, 0, 0, 0),
325 libdcp::Time (0, 0, 0, 0)
328 s = subs.subtitles_at (libdcp::Time (0, 1, 15, 50));
329 BOOST_CHECK_EQUAL (s.size(), 2);
330 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
333 libdcp::Color (255, 255, 255),
335 libdcp::Time (0, 1, 15, 42),
336 libdcp::Time (0, 1, 16, 42),
339 "With the legendary Miss Enid Blyton",
341 libdcp::Color (0, 0, 0),
342 libdcp::Time (0, 0, 0, 0),
343 libdcp::Time (0, 0, 0, 0)
345 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
348 libdcp::Color (255, 255, 255),
350 libdcp::Time (0, 1, 15, 42),
351 libdcp::Time (0, 1, 16, 42),
354 "She said \"you be Noddy",
356 libdcp::Color (0, 0, 0),
357 libdcp::Time (0, 0, 0, 0),
358 libdcp::Time (0, 0, 0, 0)
361 s = subs.subtitles_at (libdcp::Time (0, 1, 27, 200));
362 BOOST_CHECK_EQUAL (s.size(), 2);
363 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
366 libdcp::Color (255, 255, 255),
368 libdcp::Time (0, 1, 27, 115),
369 libdcp::Time (0, 1, 28, 208),
372 "That curious creature the Sphinx",
374 libdcp::Color (0, 0, 0),
375 libdcp::Time (0, 0, 0, 0),
376 libdcp::Time (0, 0, 0, 0)
378 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
381 libdcp::Color (255, 255, 255),
383 libdcp::Time (0, 1, 27, 115),
384 libdcp::Time (0, 1, 28, 208),
387 "Is smarter than anyone thinks",
389 libdcp::Color (0, 0, 0),
390 libdcp::Time (0, 0, 0, 0),
391 libdcp::Time (0, 0, 0, 0)
394 s = subs.subtitles_at (libdcp::Time (0, 1, 42, 300));
395 BOOST_CHECK_EQUAL (s.size(), 2);
396 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
399 libdcp::Color (255, 255, 255),
401 libdcp::Time (0, 1, 42, 229),
402 libdcp::Time (0, 1, 45, 62),
405 "It sits there and smirks",
407 libdcp::Color (0, 0, 0),
408 libdcp::Time (0, 0, 0, 0),
409 libdcp::Time (0, 0, 0, 0)
411 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
414 libdcp::Color (255, 255, 255),
416 libdcp::Time (0, 1, 42, 229),
417 libdcp::Time (0, 1, 45, 62),
420 "And you don't think it works",
422 libdcp::Color (0, 0, 0),
423 libdcp::Time (0, 0, 0, 0),
424 libdcp::Time (0, 0, 0, 0)
427 s = subs.subtitles_at (libdcp::Time (0, 1, 45, 200));
428 BOOST_CHECK_EQUAL (s.size(), 2);
429 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
432 libdcp::Color (255, 255, 255),
434 libdcp::Time (0, 1, 45, 146),
435 libdcp::Time (0, 1, 47, 94),
438 "Then when you're not looking, it winks.",
440 libdcp::Color (0, 0, 0),
441 libdcp::Time (0, 0, 0, 0),
442 libdcp::Time (0, 0, 0, 0)
444 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
447 libdcp::Color (255, 255, 255),
449 libdcp::Time (0, 1, 45, 146),
450 libdcp::Time (0, 1, 47, 94),
453 "When it snows you will find Sister Sledge",
455 libdcp::Color (0, 0, 0),
456 libdcp::Time (0, 0, 0, 0),
457 libdcp::Time (0, 0, 0, 0)
460 s = subs.subtitles_at (libdcp::Time (0, 1, 47, 249));
461 BOOST_CHECK_EQUAL (s.size(), 2);
462 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
465 libdcp::Color (255, 255, 255),
467 libdcp::Time (0, 1, 47, 146),
468 libdcp::Time (0, 1, 48, 167),
471 "Out mooning, at night, on the ledge",
473 libdcp::Color (0, 0, 0),
474 libdcp::Time (0, 0, 0, 0),
475 libdcp::Time (0, 0, 0, 0)
477 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
480 libdcp::Color (255, 255, 255),
482 libdcp::Time (0, 1, 47, 146),
483 libdcp::Time (0, 1, 48, 167),
488 libdcp::Color (0, 0, 0),
489 libdcp::Time (0, 0, 0, 0),
490 libdcp::Time (0, 0, 0, 0)
493 s = subs.subtitles_at (libdcp::Time (0, 2, 6, 210));
494 BOOST_CHECK_EQUAL (s.size(), 2);
495 BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
498 libdcp::Color (255, 255, 255),
500 libdcp::Time (0, 2, 5, 208),
501 libdcp::Time (0, 2, 7, 31),
506 libdcp::Color (0, 0, 0),
507 libdcp::Time (0, 0, 0, 0),
508 libdcp::Time (0, 0, 0, 0)
510 BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
513 libdcp::Color (255, 255, 255),
515 libdcp::Time (0, 2, 5, 208),
516 libdcp::Time (0, 2, 7, 31),
521 libdcp::Color (0, 0, 0),
522 libdcp::Time (0, 0, 0, 0),
523 libdcp::Time (0, 0, 0, 0)
530 BOOST_AUTO_TEST_CASE (dcp_time)
532 libdcp::Time t (977143, 24);
534 BOOST_CHECK_EQUAL (t.t, 73);
535 BOOST_CHECK_EQUAL (t.s, 34);
536 BOOST_CHECK_EQUAL (t.m, 18);
537 BOOST_CHECK_EQUAL (t.h, 11);
538 BOOST_CHECK_EQUAL (t.to_string(), "11:18:34:73");
539 BOOST_CHECK_EQUAL (t.to_ticks(), 1017923);
541 libdcp::Time a (3, 2, 3, 4);
542 libdcp::Time b (2, 3, 4, 5);
544 libdcp::Time r = a - b;
545 BOOST_CHECK_EQUAL (r.h, 0);
546 BOOST_CHECK_EQUAL (r.m, 58);
547 BOOST_CHECK_EQUAL (r.s, 58);
548 BOOST_CHECK_EQUAL (r.t, 249);
549 BOOST_CHECK_EQUAL (r.to_string(), "0:58:58:249");
550 BOOST_CHECK_EQUAL (r.to_ticks(), 88699);
552 a = libdcp::Time (1, 58, 56, 240);
553 b = libdcp::Time (1, 7, 12, 120);
555 BOOST_CHECK_EQUAL (r.h, 3);
556 BOOST_CHECK_EQUAL (r.m, 6);
557 BOOST_CHECK_EQUAL (r.s, 9);
558 BOOST_CHECK_EQUAL (r.t, 110);
559 BOOST_CHECK_EQUAL (r.to_string(), "3:6:9:110");
560 BOOST_CHECK_EQUAL (r.to_ticks(), 279335);
562 a = libdcp::Time (24, 12, 6, 3);
563 b = libdcp::Time (16, 8, 4, 2);
564 BOOST_CHECK_CLOSE (a / b, 1.5, 1e-5);
567 BOOST_AUTO_TEST_CASE (color)
569 libdcp::Color c ("FFFF0000");
571 BOOST_CHECK_EQUAL (c.r, 255);
572 BOOST_CHECK_EQUAL (c.g, 0);
573 BOOST_CHECK_EQUAL (c.b, 0);
574 BOOST_CHECK_EQUAL (c.to_argb_string(), "FFFF0000");
576 c = libdcp::Color ("FF00FF00");
578 BOOST_CHECK_EQUAL (c.r, 0);
579 BOOST_CHECK_EQUAL (c.g, 255);
580 BOOST_CHECK_EQUAL (c.b, 0);
581 BOOST_CHECK_EQUAL (c.to_argb_string(), "FF00FF00");
583 c = libdcp::Color ("FF0000FF");
585 BOOST_CHECK_EQUAL (c.r, 0);
586 BOOST_CHECK_EQUAL (c.g, 0);
587 BOOST_CHECK_EQUAL (c.b, 255);
588 BOOST_CHECK_EQUAL (c.to_argb_string(), "FF0000FF");
592 BOOST_AUTO_TEST_CASE (encryption)
594 Kumu::libdcp_test = true;
596 libdcp::Metadata* t = libdcp::Metadata::instance ();
597 t->issuer = "OpenDCP 0.0.25";
598 t->creator = "OpenDCP 0.0.25";
599 t->company_name = "OpenDCP";
600 t->product_name = "OpenDCP";
601 t->product_version = "0.0.25";
602 t->issue_date = "2012-07-17T04:45:18+00:00";
603 boost::filesystem::remove_all ("build/test/bar");
604 boost::filesystem::create_directories ("build/test/bar");
605 libdcp::DCP d ("build/test/bar");
607 libdcp::CertificateChain chain;
608 chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/ca.self-signed.pem")));
609 chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/intermediate.signed.pem")));
610 chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/leaf.signed.pem")));
612 shared_ptr<libdcp::Encryption> crypt (
613 new libdcp::Encryption (
615 "test/data/signer.key"
619 shared_ptr<libdcp::CPL> cpl (new libdcp::CPL ("build/test/bar", "A Test DCP", libdcp::FEATURE, 24, 24));
621 shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset (
633 shared_ptr<libdcp::SoundAsset> ms (new libdcp::SoundAsset (
644 cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (mp, ms, shared_ptr<libdcp::SubtitleAsset> ())));
649 shared_ptr<xmlpp::Document> kdm = cpl->make_kdm (
652 crypt->certificates.leaf(),
653 boost::posix_time::time_from_string ("2013-01-01 00:00:00"),
654 boost::posix_time::time_from_string ("2013-01-08 00:00:00")
657 kdm->write_to_file_formatted ("build/test/bar.kdm.xml", "UTF-8");
660 BOOST_AUTO_TEST_CASE (certificates)
662 libdcp::CertificateChain c;
664 c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/ca.self-signed.pem")));
665 c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/intermediate.signed.pem")));
666 c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/leaf.signed.pem")));
670 "/O=example.org/OU=example.org/CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION/dnQualifier=rTeK7x+nopFkyphflooz6p2ZM7A="
674 libdcp::Certificate::name_for_xml (c.root()->issuer()),
675 "dnQualifier=rTeK7x\\+nopFkyphflooz6p2ZM7A=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
678 BOOST_CHECK_EQUAL (c.root()->serial(), "5");
681 libdcp::Certificate::name_for_xml (c.root()->subject()),
682 "dnQualifier=rTeK7x\\+nopFkyphflooz6p2ZM7A=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
686 BOOST_AUTO_TEST_CASE (crypt_chain)
688 boost::filesystem::remove_all ("build/test/crypt");
689 boost::filesystem::create_directory ("build/test/crypt");
690 libdcp::make_crypt_chain ("build/test/crypt");