Cleanup: ues BOOST_CHECK_EQUAL.
[libdcp.git] / test / dcp_test.cc
1 /*
2     Copyright (C) 2013-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 "dcp.h"
35 #include "metadata.h"
36 #include "cpl.h"
37 #include "mono_picture_asset.h"
38 #include "stereo_picture_asset.h"
39 #include "picture_asset_writer.h"
40 #include "reel_picture_asset.h"
41 #include "sound_asset_writer.h"
42 #include "sound_asset.h"
43 #include "atmos_asset.h"
44 #include "reel.h"
45 #include "test.h"
46 #include "reel_mono_picture_asset.h"
47 #include "reel_stereo_picture_asset.h"
48 #include "reel_sound_asset.h"
49 #include "reel_atmos_asset.h"
50 #include "reel_markers_asset.h"
51 #include <asdcp/KM_util.h>
52 #include <sndfile.h>
53 #include <boost/test/unit_test.hpp>
54
55 using std::string;
56 using std::vector;
57 using std::dynamic_pointer_cast;
58 using std::shared_ptr;
59 using std::make_shared;
60 #if BOOST_VERSION >= 106100
61 using namespace boost::placeholders;
62 #endif
63
64
65 /** Test creation of a 2D SMPTE DCP from very simple inputs */
66 BOOST_AUTO_TEST_CASE (dcp_test1)
67 {
68         RNGFixer fixer;
69
70         auto dcp = make_simple("build/test/DCP/dcp_test1");
71         dcp->set_issuer("OpenDCP 0.0.25");
72         dcp->set_creator("OpenDCP 0.0.25");
73         dcp->set_issue_date("2012-07-17T04:45:18+00:00");
74         dcp->set_annotation_text("A Test DCP");
75         dcp->write_xml();
76
77         /* build/test/DCP/dcp_test1 is checked against test/ref/DCP/dcp_test1 by run/tests */
78 }
79
80 /** Test creation of a 3D DCP from very simple inputs */
81 BOOST_AUTO_TEST_CASE (dcp_test2)
82 {
83         RNGFixer fix;
84
85         /* Some known metadata */
86         dcp::MXFMetadata mxf_meta;
87         mxf_meta.company_name = "OpenDCP";
88         mxf_meta.product_name = "OpenDCP";
89         mxf_meta.product_version = "0.0.25";
90
91         /* We're making build/test/DCP/dcp_test2 */
92         boost::filesystem::remove_all ("build/test/DCP/dcp_test2");
93         boost::filesystem::create_directories ("build/test/DCP/dcp_test2");
94         dcp::DCP d ("build/test/DCP/dcp_test2");
95         auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
96         cpl->set_content_version (
97                 dcp::ContentVersion("urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00", "81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00")
98                 );
99         cpl->set_issuer ("OpenDCP 0.0.25");
100         cpl->set_creator ("OpenDCP 0.0.25");
101         cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
102         cpl->set_annotation_text ("A Test DCP");
103
104         auto mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE);
105         mp->set_metadata (mxf_meta);
106         auto picture_writer = mp->start_write("build/test/DCP/dcp_test2/video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW);
107         dcp::ArrayData j2c ("test/data/flat_red.j2c");
108         for (int i = 0; i < 24; ++i) {
109                 /* Left */
110                 picture_writer->write (j2c.data (), j2c.size ());
111                 /* Right */
112                 picture_writer->write (j2c.data (), j2c.size ());
113         }
114         picture_writer->finalize ();
115
116         auto ms = make_shared<dcp::SoundAsset>(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::Standard::SMPTE);
117         ms->set_metadata (mxf_meta);
118         auto sound_writer = ms->start_write("build/test/DCP/dcp_test2/audio.mxf", {}, dcp::SoundAsset::AtmosSync::DISABLED, dcp::SoundAsset::MCASubDescriptors::ENABLED);
119
120         SF_INFO info;
121         info.format = 0;
122         auto sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
123         BOOST_CHECK (sndfile);
124         float buffer[4096*6];
125         float* channels[1];
126         channels[0] = buffer;
127         while (true) {
128                 auto N = sf_readf_float (sndfile, buffer, 4096);
129                 sound_writer->write(channels, 1, N);
130                 if (N < 4096) {
131                         break;
132                 }
133         }
134
135         sound_writer->finalize ();
136
137         cpl->add (make_shared<dcp::Reel>(
138                           make_shared<dcp::ReelStereoPictureAsset>(mp, 0),
139                           make_shared<dcp::ReelSoundAsset>(ms, 0)
140                           ));
141
142         d.add (cpl);
143
144         d.set_issuer("OpenDCP 0.0.25");
145         d.set_creator("OpenDCP 0.0.25");
146         d.set_issue_date("2012-07-17T04:45:18+00:00");
147         d.set_annotation_text("Created by libdcp");
148         d.write_xml();
149
150         /* build/test/DCP/dcp_test2 is checked against test/ref/DCP/dcp_test2 by run/tests */
151 }
152
153 static void
154 note (dcp::NoteType, string)
155 {
156
157 }
158
159 /** Test comparison of a DCP with itself */
160 BOOST_AUTO_TEST_CASE (dcp_test3)
161 {
162         dcp::DCP A ("test/ref/DCP/dcp_test1");
163         A.read ();
164         dcp::DCP B ("test/ref/DCP/dcp_test1");
165         B.read ();
166
167         BOOST_CHECK (A.equals (B, dcp::EqualityOptions(), boost::bind (&note, _1, _2)));
168 }
169
170 /** Test comparison of a DCP with a different DCP */
171 BOOST_AUTO_TEST_CASE (dcp_test4)
172 {
173         dcp::DCP A ("test/ref/DCP/dcp_test1");
174         A.read ();
175         dcp::DCP B ("test/ref/DCP/dcp_test2");
176         B.read ();
177
178         BOOST_CHECK (!A.equals (B, dcp::EqualityOptions(), boost::bind (&note, _1, _2)));
179 }
180
181 static
182 void
183 test_rewriting_sound(string name, bool modify)
184 {
185         using namespace boost::filesystem;
186
187         dcp::DCP A ("test/ref/DCP/dcp_test1");
188         A.read ();
189
190         BOOST_REQUIRE (!A.cpls().empty());
191         BOOST_REQUIRE (!A.cpls().front()->reels().empty());
192         auto A_picture = dynamic_pointer_cast<dcp::ReelMonoPictureAsset>(A.cpls().front()->reels().front()->main_picture());
193         BOOST_REQUIRE (A_picture);
194         auto A_sound = dynamic_pointer_cast<dcp::ReelSoundAsset>(A.cpls().front()->reels().front()->main_sound());
195
196         string const picture = "j2c_5279f9aa-94d7-42a6-b0e0-e4eaec4e2a15.mxf";
197
198         remove_all ("build/test/" + name);
199         dcp::DCP B ("build/test/" + name);
200         auto reel = make_shared<dcp::Reel>();
201
202         BOOST_REQUIRE (A_picture->mono_asset());
203         BOOST_REQUIRE (A_picture->mono_asset()->file());
204         copy_file (A_picture->mono_asset()->file().get(), path("build") / "test" / name / picture);
205         reel->add(make_shared<dcp::ReelMonoPictureAsset>(make_shared<dcp::MonoPictureAsset>(path("build") / "test" / name / picture), 0));
206
207         auto reader = A_sound->asset()->start_read();
208         auto sound = make_shared<dcp::SoundAsset>(A_sound->asset()->edit_rate(), A_sound->asset()->sampling_rate(), A_sound->asset()->channels(), dcp::LanguageTag("en-US"), dcp::Standard::SMPTE);
209         auto writer = sound->start_write(path("build") / "test" / name / "pcm_8246f87f-e1df-4c42-a290-f3b3069ff021.mxf", {}, dcp::SoundAsset::AtmosSync::DISABLED, dcp::SoundAsset::MCASubDescriptors::ENABLED);
210
211         bool need_to_modify = modify;
212         for (int i = 0; i < A_sound->asset()->intrinsic_duration(); ++i) {
213                 auto sf = reader->get_frame (i);
214                 float* out[sf->channels()];
215                 for (int j = 0; j < sf->channels(); ++j) {
216                         out[j] = new float[sf->samples()];
217                 }
218                 for (int j = 0; j < sf->samples(); ++j) {
219                         for (int k = 0; k < sf->channels(); ++k) {
220                                 out[k][j] = static_cast<float>(sf->get(k, j)) / (1 << 23);
221                                 if (need_to_modify) {
222                                         out[k][j] += 1.0 / (1 << 23);
223                                         need_to_modify = false;
224                                 }
225                         }
226                 }
227                 writer->write(out, sf->channels(), sf->samples());
228                 for (int j = 0; j < sf->channels(); ++j) {
229                         delete[] out[j];
230                 }
231         }
232         writer->finalize();
233
234         reel->add(make_shared<dcp::ReelSoundAsset>(sound, 0));
235         reel->add(simple_markers());
236
237         auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
238         cpl->add (reel);
239
240         B.add (cpl);
241         B.write_xml ();
242
243         dcp::EqualityOptions eq;
244         eq.reel_hashes_can_differ = true;
245         eq.max_audio_sample_error = 0;
246         if (modify) {
247                 BOOST_CHECK (!A.equals(B, eq, boost::bind(&note, _1, _2)));
248         } else {
249                 BOOST_CHECK (A.equals(B, eq, boost::bind(&note, _1, _2)));
250         }
251 }
252
253 /** Test comparison of a DCP with another that has the same picture and the same (but re-written) sound */
254 BOOST_AUTO_TEST_CASE (dcp_test9)
255 {
256         test_rewriting_sound ("dcp_test9", false);
257 }
258
259 /** Test comparison of a DCP with another that has the same picture and very slightly modified sound */
260 BOOST_AUTO_TEST_CASE (dcp_test10)
261 {
262         test_rewriting_sound ("dcp_test10", true);
263 }
264
265 /** Test creation of a 2D DCP with an Atmos track */
266 BOOST_AUTO_TEST_CASE (dcp_test5)
267 {
268         RNGFixer fix;
269
270         /* Some known metadata */
271         dcp::MXFMetadata mxf_meta;
272         mxf_meta.company_name = "OpenDCP";
273         mxf_meta.product_name = "OpenDCP";
274         mxf_meta.product_version = "0.0.25";
275
276         /* We're making build/test/DCP/dcp_test5 */
277         boost::filesystem::remove_all ("build/test/DCP/dcp_test5");
278         boost::filesystem::create_directories ("build/test/DCP/dcp_test5");
279         dcp::DCP d ("build/test/DCP/dcp_test5");
280         auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
281         cpl->set_content_version (
282                 dcp::ContentVersion("urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00", "81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00")
283                 );
284         cpl->set_issuer ("OpenDCP 0.0.25");
285         cpl->set_creator ("OpenDCP 0.0.25");
286         cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
287         cpl->set_annotation_text ("A Test DCP");
288
289         auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE);
290         mp->set_metadata (mxf_meta);
291         auto picture_writer = mp->start_write("build/test/DCP/dcp_test5/video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW);
292         dcp::ArrayData j2c ("test/data/flat_red.j2c");
293         for (int i = 0; i < 24; ++i) {
294                 picture_writer->write (j2c.data (), j2c.size ());
295         }
296         picture_writer->finalize ();
297
298         auto ms = make_shared<dcp::SoundAsset>(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::Standard::SMPTE);
299         ms->set_metadata (mxf_meta);
300         auto sound_writer = ms->start_write("build/test/DCP/dcp_test5/audio.mxf", {}, dcp::SoundAsset::AtmosSync::DISABLED, dcp::SoundAsset::MCASubDescriptors::ENABLED);
301
302         SF_INFO info;
303         info.format = 0;
304         SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
305         BOOST_CHECK (sndfile);
306         float buffer[4096*6];
307         float* channels[1];
308         channels[0] = buffer;
309         while (true) {
310                 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
311                 sound_writer->write(channels, 1, N);
312                 if (N < 4096) {
313                         break;
314                 }
315         }
316
317         sound_writer->finalize ();
318
319         auto am = make_shared<dcp::AtmosAsset>(private_test / "20160218_NameOfFilm_FTR_OV_EN_A_dcs_r01.mxf");
320
321         cpl->add(make_shared<dcp::Reel>(
322                         make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
323                         make_shared<dcp::ReelSoundAsset>(ms, 0),
324                         shared_ptr<dcp::ReelSubtitleAsset>(),
325                         shared_ptr<dcp::ReelMarkersAsset>(),
326                         make_shared<dcp::ReelAtmosAsset>(am, 0)
327                         ));
328
329         d.add (cpl);
330
331         d.set_issuer("OpenDCP 0.0.25");
332         d.set_creator("OpenDCP 0.0.25");
333         d.set_issue_date("2012-07-17T04:45:18+00:00");
334         d.set_annotation_text("Created by libdcp");
335         d.write_xml();
336
337         /* build/test/DCP/dcp_test5 is checked against test/ref/DCP/dcp_test5 by run/tests */
338 }
339
340 /** Basic tests of reading a 2D DCP with an Atmos track */
341 BOOST_AUTO_TEST_CASE (dcp_test6)
342 {
343         dcp::DCP dcp ("test/ref/DCP/dcp_test5");
344         dcp.read ();
345
346         BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 1U);
347         BOOST_REQUIRE_EQUAL (dcp.cpls()[0]->reels().size(), 1U);
348         BOOST_CHECK (dcp.cpls().front()->reels().front()->main_picture());
349         BOOST_CHECK (dcp.cpls().front()->reels().front()->main_sound());
350         BOOST_CHECK (!dcp.cpls().front()->reels().front()->main_subtitle());
351         BOOST_CHECK (dcp.cpls().front()->reels().front()->atmos());
352 }
353
354 /** Test creation of a 2D Interop DCP from very simple inputs */
355 BOOST_AUTO_TEST_CASE (dcp_test7)
356 {
357         RNGFixer fix;
358
359         auto dcp = make_simple("build/test/DCP/dcp_test7", 1, 24, dcp::Standard::INTEROP);
360         dcp->set_issuer("OpenDCP 0.0.25");
361         dcp->set_creator("OpenDCP 0.0.25");
362         dcp->set_issue_date("2012-07-17T04:45:18+00:00");
363         dcp->set_annotation_text("Created by libdcp");
364         dcp->write_xml();
365
366         /* build/test/DCP/dcp_test7 is checked against test/ref/DCP/dcp_test7 by run/tests */
367 }
368
369 /** Test reading of a DCP with multiple CPLs */
370 BOOST_AUTO_TEST_CASE (dcp_test8)
371 {
372         dcp::DCP dcp (private_test / "data/SMPTE_TST-B1PB2P_S_EN-EN-CCAP_5171-HI-VI_2K_ISDCF_20151123_DPPT_SMPTE_combo/");
373         dcp.read ();
374
375         BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 2U);
376 }
377
378
379 /** Test reading a DCP whose ASSETMAP contains assets not used by any PKL */
380 BOOST_AUTO_TEST_CASE (dcp_things_in_assetmap_not_in_pkl)
381 {
382         dcp::DCP dcp ("test/data/extra_assetmap");
383         BOOST_CHECK_NO_THROW (dcp.read());
384 }
385
386
387 /** Test that writing the XML for a DCP with no CPLs throws */
388 BOOST_AUTO_TEST_CASE (dcp_with_no_cpls)
389 {
390         dcp::DCP dcp ("build/test/dcp_with_no_cpls");
391         BOOST_REQUIRE_THROW (dcp.write_xml(), dcp::MiscError);
392 }
393
394
395 /** Test that writing the XML for a DCP with Interop CPLs makes a SMPTE assetmap */
396 BOOST_AUTO_TEST_CASE (dcp_with_interop_cpls)
397 {
398         boost::filesystem::path path = "build/test/dcp_with_interop_cpls";
399         boost::filesystem::remove_all (path);
400         dcp::DCP dcp (path);
401         auto cpl1 = make_shared<dcp::CPL>("", dcp::ContentKind::FEATURE, dcp::Standard::INTEROP);
402         cpl1->add(make_shared<dcp::Reel>());
403         dcp.add(cpl1);
404         auto cpl2 = make_shared<dcp::CPL>("", dcp::ContentKind::FEATURE, dcp::Standard::INTEROP);
405         cpl2->add(make_shared<dcp::Reel>());
406         dcp.add(cpl2);
407         dcp.write_xml ();
408         BOOST_REQUIRE (boost::filesystem::exists(path / "ASSETMAP"));
409         BOOST_REQUIRE (!boost::filesystem::exists(path / "ASSETMAP.xml"));
410 }
411
412
413 /** Test that writing the XML for a DCP with SMPTE CPLs makes a SMPTE assetmap */
414 BOOST_AUTO_TEST_CASE (dcp_with_smpte_cpls)
415 {
416         boost::filesystem::path path = "build/test/dcp_with_smpte_cpls";
417         boost::filesystem::remove_all (path);
418         dcp::DCP dcp (path);
419         auto cpl1 = make_shared<dcp::CPL>("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
420         cpl1->add(make_shared<dcp::Reel>());
421         dcp.add(cpl1);
422         auto cpl2 = make_shared<dcp::CPL>("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
423         cpl2->add(make_shared<dcp::Reel>());
424         dcp.add(cpl2);
425         dcp.write_xml ();
426         BOOST_REQUIRE (!boost::filesystem::exists(path / "ASSETMAP"));
427         BOOST_REQUIRE (boost::filesystem::exists(path / "ASSETMAP.xml"));
428 }
429
430
431 /** Test that writing the XML for a DCP with mixed-standard CPLs throws */
432 BOOST_AUTO_TEST_CASE (dcp_with_mixed_cpls)
433 {
434         dcp::DCP dcp ("build/test/dcp_with_mixed_cpls");
435         dcp.add(make_shared<dcp::CPL>("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE));
436         dcp.add(make_shared<dcp::CPL>("", dcp::ContentKind::FEATURE, dcp::Standard::INTEROP));
437         dcp.add(make_shared<dcp::CPL>("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE));
438         BOOST_REQUIRE_THROW (dcp.write_xml(), dcp::MiscError);
439 }
440
441
442 BOOST_AUTO_TEST_CASE (dcp_add_kdm_test)
443 {
444         /* Some CPLs, each with a reel */
445
446         shared_ptr<dcp::CPL> cpls[] = {
447                 make_shared<dcp::CPL>("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE),
448                 make_shared<dcp::CPL>("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE),
449                 make_shared<dcp::CPL>("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE)
450         };
451
452         shared_ptr<dcp::Reel> reels[] = {
453                 make_shared<dcp::Reel>(),
454                 make_shared<dcp::Reel>(),
455                 make_shared<dcp::Reel>()
456         };
457
458         for (auto i = 0; i < 3; ++i) {
459                 cpls[i]->add(reels[i]);
460         }
461
462         dcp::DCP dcp ("build/test/dcp_add_kdm_test");
463         dcp.add(cpls[0]);
464         dcp.add(cpls[1]);
465         dcp.add(cpls[2]);
466
467         /* Simple KDM with one key that should be given to cpls[0] */
468
469         auto kdm_1 = dcp::DecryptedKDM({}, {}, "", "", "");
470         auto kdm_1_uuid = dcp::make_uuid();
471         kdm_1.add_key (dcp::DecryptedKDMKey(string("MDIK"), kdm_1_uuid, dcp::Key(), cpls[0]->id(), dcp::Standard::SMPTE));
472         dcp.add (kdm_1);
473         BOOST_REQUIRE_EQUAL (reels[0]->_kdms.size(), 1U);
474         BOOST_CHECK_EQUAL (reels[0]->_kdms[0].keys().size(), 1U);
475         BOOST_CHECK_EQUAL (reels[0]->_kdms[0].keys()[0].id(), kdm_1_uuid);
476         BOOST_CHECK_EQUAL (reels[1]->_kdms.size(), 0U);
477         BOOST_CHECK_EQUAL (reels[2]->_kdms.size(), 0U);
478
479         /* KDM with two keys that should be given to cpls[1] and cpls[2] */
480
481         auto kdm_2 = dcp::DecryptedKDM({}, {}, "", "", "");
482         auto kdm_2_uuid_1 = dcp::make_uuid();
483         kdm_2.add_key (dcp::DecryptedKDMKey(string("MDIK"), kdm_2_uuid_1, dcp::Key(), cpls[1]->id(), dcp::Standard::SMPTE));
484         auto kdm_2_uuid_2 = dcp::make_uuid();
485         kdm_2.add_key (dcp::DecryptedKDMKey(string("MDIK"), kdm_2_uuid_2, dcp::Key(), cpls[2]->id(), dcp::Standard::SMPTE));
486         dcp.add (kdm_2);
487         /* Unchanged from previous test */
488         BOOST_CHECK (reels[0]->_kdms.size() == 1);
489         /* kdm_2 should have been added to both the other CPLs */
490         BOOST_REQUIRE_EQUAL (reels[1]->_kdms.size(), 1U);
491         BOOST_REQUIRE_EQUAL (reels[1]->_kdms[0].keys().size(), 2U);
492         BOOST_CHECK_EQUAL (reels[1]->_kdms[0].keys()[0].id(), kdm_2_uuid_1);
493         BOOST_CHECK_EQUAL (reels[1]->_kdms[0].keys()[1].id(), kdm_2_uuid_2);
494         BOOST_REQUIRE_EQUAL (reels[2]->_kdms.size(), 1U);
495         BOOST_REQUIRE_EQUAL (reels[2]->_kdms[0].keys().size(), 2U);
496         BOOST_CHECK_EQUAL (reels[2]->_kdms[0].keys()[0].id(), kdm_2_uuid_1);
497         BOOST_CHECK_EQUAL (reels[2]->_kdms[0].keys()[1].id(), kdm_2_uuid_2);
498 }
499