blob: d6a3e68e1fd698e631be373ae64167831d0556e7 [file] [log] [blame]
Mickey Sweatt3b0bea62016-01-25 22:12:27 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3* Copyright (c) 2016 Regents of the University of California.
4*
5* This file is part of the nTorrent codebase.
6*
7* nTorrent is free software: you can redistribute it and/or modify it under the
8* terms of the GNU Lesser General Public License as published by the Free Software
9* Foundation, either version 3 of the License, or (at your option) any later version.
10*
11* nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY
12* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14*
15* You should have received copies of the GNU General Public License and GNU Lesser
16* General Public License along with nTorrent, e.g., in COPYING.md file. If not, see
17* <http://www.gnu.org/licenses/>.
18*
19* See AUTHORS for complete list of nTorrent authors and contributors.
20*/
21
22#include "file-manifest.hpp"
23#include "boost-test.hpp"
24
25#include <vector>
26
27#include <ndn-cxx/data.hpp>
Mickey Sweatt3b0bea62016-01-25 22:12:27 -080028#include <ndn-cxx/security/key-chain.hpp>
Mickey Sweattebc01952016-02-19 11:38:30 -080029#include <ndn-cxx/security/signing-helpers.hpp>
30#include <ndn-cxx/signature.hpp>
31
32#include <boost/filesystem.hpp>
33#include <boost/filesystem/fstream.hpp>
34#include <boost/lexical_cast.hpp>
35
36namespace fs = boost::filesystem;
Mickey Sweatt3b0bea62016-01-25 22:12:27 -080037
38BOOST_TEST_DONT_PRINT_LOG_VALUE(std::nullptr_t)
39BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<ndn::Name>)
Mickey Sweattebc01952016-02-19 11:38:30 -080040BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<ndn::ntorrent::FileManifest>)
41BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<uint8_t>)
42BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<ndn::Data>::iterator)
Mickey Sweatt3b0bea62016-01-25 22:12:27 -080043
44namespace ndn {
45namespace ntorrent {
46namespace tests {
47
48using std::vector;
49using ndn::Name;
50
51BOOST_AUTO_TEST_SUITE(TestFileManifest)
52
53BOOST_AUTO_TEST_CASE(CheckPrimaryAccessorsAndManipulators)
54{
55 FileManifest m1("/file0/1A2B3C4D", 256, "/foo/");
56
57 // Check the values on most simply constructed m1
58 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
59 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
60 BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
61 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
62 BOOST_CHECK_EQUAL(vector<Name>({}), m1.catalog());
63
64 // Append names to catalog and recheck all salient attributes
65 m1.push_back("/foo/0/ABC123");
66 m1.push_back("/foo/1/DEADBEFF");
67 m1.push_back("/foo/2/CAFEBABE");
68
69 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
70 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
71 BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
72 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
73 BOOST_CHECK_EQUAL(vector<Name>({"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}),
74 m1.catalog());
75
76 // Remove a value from the catalog and recheck all salient attributes
77 BOOST_CHECK_EQUAL(true, m1.remove("/foo/0/ABC123"));
78
79 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
80 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
81 BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
82 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
83 BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog());
84
85 // Try to remove a value no longer in the catalog, and recheck that all salient attributes
86 BOOST_CHECK_EQUAL(false, m1.remove("/foo/0/ABC123"));
87
88 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
89 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
90 BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
91 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
92 BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog());
93
94 // Try to remove a value never in the catalog, and recheck that all salient attributes
95 BOOST_CHECK_EQUAL(false, m1.remove("/bar/0/ABC123"));
96 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
97 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
98 BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
99 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
100 BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}),
101 m1.catalog());
102
103 // Remove a value from the end of the list
104 BOOST_CHECK_EQUAL(true, m1.remove("/foo/2/CAFEBABE"));
105
106 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
107 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
108 BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
109 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
110 BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF"}),
111 m1.catalog());
112}
113
114BOOST_AUTO_TEST_CASE(CheckValueCtors)
115{
116 FileManifest m1("/file0/1A2B3C4D",
117 256,
118 "/foo/",
119 {"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"},
120 std::make_shared<Name>("/file0/1/5E6F7G8H"));
121
122 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
123 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
124 BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr());
125 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
126 BOOST_CHECK_EQUAL(vector<Name>({"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}),
127 m1.catalog());
128
129 // Remove a value from the catalog and recheck all salient attributes
130 BOOST_CHECK_EQUAL(true, m1.remove("/foo/0/ABC123"));
131
132 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
133 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
134 BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr());
135 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
136 BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog());
137
138 // Try to remove a value no longer in the catalog, and recheck that all salient attributes
139 BOOST_CHECK_EQUAL(false, m1.remove("/foo/0/ABC123"));
140
141 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
142 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
143 BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr());
144 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
145 BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog());
146
147 // Try to remove a value never in the catalog, and recheck that all salient attributes
148 BOOST_CHECK_EQUAL(false, m1.remove("/bar/0/ABC123"));
149 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
150 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
151 BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr());
152 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
153 BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog());
154
155 // Remove a value from the end of the list
156 BOOST_CHECK_EQUAL(true, m1.remove("/foo/2/CAFEBABE"));
157
158 BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
159 BOOST_CHECK_EQUAL(256, m1.data_packet_size());
160 BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr());
161 BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
162 BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF"}), m1.catalog());
163}
164
165BOOST_AUTO_TEST_CASE(CheckAssignmentOperatorEqaulityAndCopyCtor)
166{
167 // Construct two manifests with the same attributes, and check that they related equal
168 {
169 FileManifest m1("/file0/1A2B3C4D", 256, "/foo/", vector<Name>({}), std::make_shared<Name>("/file0/1/5E6F7G8H"));
170 FileManifest m2("/file0/1A2B3C4D", 256, "/foo/", vector<Name>({}), std::make_shared<Name>("/file0/1/5E6F7G8H"));
171
172 BOOST_CHECK_EQUAL(m1, m2);
173 BOOST_CHECK(!(m1 != m2));
174
175 // Change value of one
176 m1.push_back("/foo/0/ABC123");
177 BOOST_CHECK_NE(m1, m2);
178 BOOST_CHECK(!(m1 == m2));
179
180 // Update other
181 m2.push_back("/foo/0/ABC123");
182 BOOST_CHECK_EQUAL(m1, m2);
183 BOOST_CHECK(!(m1 != m2));
184
185 // Change value again
186 m1.remove("/foo/0/ABC123");
187 BOOST_CHECK_NE(m1, m2);
188 BOOST_CHECK(!(m1 == m2));
189
190 m2.remove("/foo/0/ABC123");
191 BOOST_CHECK_EQUAL(m1, m2);
192 BOOST_CHECK(!(m1 != m2));
193 }
194
195 // Set sub-manifest pointer in one and not the other
196 {
197 FileManifest m1("/file0/1A2B3C4D",
198 256,
199 "/foo/",
200 vector<Name>({}),
201 std::make_shared<Name>("/file0/1/5E6F7G8H"));
202
203 FileManifest m2("/file0/1A2B3C4D", 256, "/foo/");
204 BOOST_CHECK_NE(m1, m2);
205 BOOST_CHECK(!(m1 == m2));
206
207 std::swap(m1, m2);
208 BOOST_CHECK_NE(m1, m2);
209 BOOST_CHECK(!(m1 == m2));
210 }
211
212 // Construct two manifests using copy ctor for one
213 {
214 FileManifest m1("/file0/1A2B3C4D", 256, "/foo/");
215 FileManifest m2(m1);
216
217 BOOST_CHECK_EQUAL(m1, m2);
218 BOOST_CHECK(!(m1 != m2));
219
220 // Change value of one
221 m1.push_back("/foo/0/ABC123");
222 BOOST_CHECK_NE(m1, m2);
223 BOOST_CHECK(!(m1 == m2));
224
225 // Update other
226 m2.push_back("/foo/0/ABC123");
227 BOOST_CHECK_EQUAL(m1, m2);
228 BOOST_CHECK(!(m1 != m2));
229
230 // Change value again
231 m1.remove("/foo/0/ABC123");
232 BOOST_CHECK_NE(m1, m2);
233 BOOST_CHECK(!(m1 == m2));
234
235 m2.remove("/foo/0/ABC123");
236 BOOST_CHECK_EQUAL(m1, m2);
237 BOOST_CHECK(!(m1 != m2));
238 }
239
240 // Use assignment operator
241 {
242 FileManifest m1("/file1/1A2B3C4D", 256, "/foo/");
243 FileManifest m2("/file1/5E6F7G8H", 256, "/foo/");
244
245 BOOST_CHECK_NE(m1, m2);
246 BOOST_CHECK(!(m1 == m2));
247
248 m2 = m1;
249 BOOST_CHECK_EQUAL(m1, m2);
250 BOOST_CHECK(!(m1 != m2));
251
252 // Change value of one
253 m1.push_back("/foo/0/ABC123");
254 BOOST_CHECK_NE(m1, m2);
255 BOOST_CHECK(!(m1 == m2));
256
257 // Update other
258 m2.push_back("/foo/0/ABC123");
259 BOOST_CHECK_EQUAL(m1, m2);
260 BOOST_CHECK(!(m1 != m2));
261
262 // Change value again
263 m1.remove("/foo/0/ABC123");
264 BOOST_CHECK_NE(m1, m2);
265 BOOST_CHECK(!(m1 == m2));
266
267 m2.remove("/foo/0/ABC123");
268 BOOST_CHECK_EQUAL(m1, m2);
269 BOOST_CHECK(!(m1 != m2));
270 }
271}
272
273BOOST_AUTO_TEST_CASE(CheckEncodeDecode)
274{
275 // Construct new FileManifest from wire encoding of another FileManifest
276 FileManifest m1("/file0/1A2B3C4D",
277 256,
278 "/foo/",
279 {"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"},
280 std::make_shared<Name>("/file0/1/5E6F7G8H"));
281
282 KeyChain keyChain;
Mickey Sweatta768b242016-02-29 20:08:05 -0800283 m1.finalize();
Mickey Sweatt3b0bea62016-01-25 22:12:27 -0800284 keyChain.sign(m1);
285 BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode()));
286
287 // Change value and be sure that wireEncoding still works
288 m1.remove("/foo/2/CAFEBABE");
Mickey Sweatta768b242016-02-29 20:08:05 -0800289 m1.finalize();
Mickey Sweatt3b0bea62016-01-25 22:12:27 -0800290 keyChain.sign(m1);
Mickey Sweatt3b0bea62016-01-25 22:12:27 -0800291 BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode()));
292
293 // Explicitly call wireEncode and ensure the value works
294 FileManifest m2 = m1;
295 m1.remove("/foo/0/ABC123");
296 keyChain.sign(m1);
Mickey Sweatta768b242016-02-29 20:08:05 -0800297 m1.finalize();
Mickey Sweatt3b0bea62016-01-25 22:12:27 -0800298 m2.wireDecode(m1.wireEncode());
299 BOOST_CHECK_EQUAL(m1, m2);
300}
301
Mickey Sweattebc01952016-02-19 11:38:30 -0800302BOOST_AUTO_TEST_CASE(CheckGenerateFileManifest)
303{
304 const size_t TEST_FILE_LEN = fs::file_size("tests/testdata/foo/bar.txt");
305 const struct {
306 size_t d_dataPacketSize;
307 size_t d_subManifestSize;
308 const char *d_filePath;
309 const char *d_catalogPrefix;
310 bool d_getData;
311 bool d_shouldThrow;
312 } DATA [] = {
313 // Affirmative tests
314 {1 , TEST_FILE_LEN, "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
315 {10 , 10 , "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
316 {10 , 1 , "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
317 {1 , 10 , "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
318 {1 , 1 , "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
319 {1024 , 1 , "tests/testdata/foo/bar1.txt", "/NTORRENT/foo/", true, false },
320 {1024 , 100 , "tests/testdata/foo/bar1.txt", "/NTORRENT/foo/", true, false },
321 {TEST_FILE_LEN, 1 , "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
322 // Negative tests
323 // non-existent file
324 {128 , 128 , "tests/testdata/foo/fake.txt", "/NTORRENT/foo/", false, true },
325 // prefix mismatch
326 {128 , 128 , "tests/testdata/foo/bar.txt", "/NTORRENT/bar/", false, true },
327 // scaling test
328 // {10240 , 5120 , "tests/testdata/foo/huge_file", "/NTORRENT/foo/", false, false },
329 // assertion failures (tests not supported on platforms)
330 // {0 , 128 , "tests/testdata/foo/bar.txt", "/NTORRENT/bar/", true },
331 // {128 , 0 , "tests/testdata/foo/bar.txt", "/NTORRENT/bar/", true },
332 };
333 enum { NUM_DATA = sizeof DATA / sizeof *DATA };
334 for (int i = 0; i < NUM_DATA; ++i) {
335 auto dataPacketSize = DATA[i].d_dataPacketSize;
336 auto subManifestSize = DATA[i].d_subManifestSize;
337 auto filePath = DATA[i].d_filePath;
338 auto catalogPrefix = DATA[i].d_catalogPrefix;
339 auto getData = DATA[i].d_getData;
340 auto shouldThrow = DATA[i].d_shouldThrow;
341
342 std::pair<std::vector<FileManifest>, std::vector<Data>> manifestsDataPair;
343 if (shouldThrow) {
344 BOOST_CHECK_THROW(FileManifest::generate(filePath,
345 catalogPrefix,
346 subManifestSize,
347 dataPacketSize,
348 getData),
349 FileManifest::Error);
350 }
351 else {
352 manifestsDataPair = FileManifest::generate(filePath,
353 catalogPrefix,
354 subManifestSize,
355 dataPacketSize,
356 getData);
357 auto manifests = manifestsDataPair.first;
358 auto data = manifestsDataPair.second;
359 // Verify the basic attributes of the manifest
360 size_t file_length = 0;
361 for (auto it = manifests.begin(); it != manifests.end(); ++it) {
362 // Verify that each file manifest is signed.
363 BOOST_CHECK_NO_THROW(it->getFullName());
364 BOOST_CHECK_EQUAL(it->data_packet_size(), dataPacketSize);
365 BOOST_CHECK_EQUAL(it->catalog_prefix(), catalogPrefix);
366 if (it != manifests.end() -1) {
367 BOOST_CHECK_EQUAL(it->catalog().size(), subManifestSize);
368 BOOST_CHECK_EQUAL(*(it->submanifest_ptr()), (it+1)->getFullName());
369 }
370 else {
371 BOOST_CHECK_LE(it->catalog().size(), subManifestSize);
372 BOOST_CHECK_EQUAL(it->submanifest_ptr(), nullptr);
373 file_length += it->wireEncode().value_size();
374 }
375 }
376 // confirm the manifests catalogs includes exactly the data packets
377 if (getData) {
378 auto data_it = data.begin();
379 size_t total_manifest_length = 0;
380 for (auto& m : manifests) {
381 total_manifest_length += m.wireEncode().value_size();
382 for (auto& data_name : m.catalog()) {
383 BOOST_CHECK_EQUAL(data_name, data_it->getFullName());
384 data_it++;
385 }
386 }
387 BOOST_CHECK_EQUAL(data_it, data.end());
388 std::vector<uint8_t> data_bytes;
389 data_bytes.reserve(fs::file_size(filePath));
390 for (const auto& d : data) {
391 auto content = d.getContent();
392 data_bytes.insert(data_bytes.end(), content.value_begin(), content.value_end());
393 }
394 data_bytes.shrink_to_fit();
395 // load the contents of the file from disk
396 fs::ifstream is(filePath, fs::ifstream::binary | fs::ifstream::in);
397 is >> std::noskipws;
398 std::istream_iterator<uint8_t> start(is), end;
399 std::vector<uint8_t> file_bytes;
400 file_bytes.reserve(fs::file_size(filePath));
401 file_bytes.insert(file_bytes.begin(), start, end);
402 file_bytes.shrink_to_fit();
403 BOOST_CHECK_EQUAL(file_bytes, data_bytes);
404 }
405 else {
406 BOOST_CHECK(data.empty());
407 BOOST_CHECK(data.capacity() == 0);
408 }
409 }
410 }
411}
412
Mickey Sweatt3b0bea62016-01-25 22:12:27 -0800413BOOST_AUTO_TEST_SUITE_END()
414
415} // namespace tests
416} // namespace nTorrent
417} // namespace ndn