blob: 722173b2947cc80d96257af747ba663a4ec7133d [file] [log] [blame]
Mickey Sweatt527b0492016-03-02 11:07:48 -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 "boost-test.hpp"
23
24#include "torrent-manager.hpp"
25#include "torrent-file.hpp"
26
Mickey Sweattafda1f12016-04-04 17:15:11 -070027#include <set>
28
Mickey Sweatt527b0492016-03-02 11:07:48 -080029#include <boost/filesystem.hpp>
30
31#include <ndn-cxx/util/io.hpp>
32
33namespace ndn {
34namespace ntorrent {
35namespace tests {
36
37using std::vector;
38using ndn::Name;
39
40namespace fs = boost::filesystem;
41
42class TestTorrentManager : public TorrentManager {
43 public:
44 TestTorrentManager(const ndn::Name& torrentFileName,
45 const std::string& filePath)
46 : TorrentManager(torrentFileName, filePath)
47 {}
48
49 std::vector<TorrentFile> torrentSegments() const {
50 return m_torrentSegments;
51 }
52
53 std::vector<FileManifest> fileManifests() const {
54 return m_fileManifests;
55 }
56
57 std::vector<bool> fileState(const ndn::Name& manifestName) {
Mickey Sweattafda1f12016-04-04 17:15:11 -070058 auto fout = m_fileStates[manifestName].first;
59 if (nullptr != fout) {
60 fout->flush();
61 }
Mickey Sweatt527b0492016-03-02 11:07:48 -080062 return m_fileStates[manifestName].second;
63 }
Mickey Sweattafda1f12016-04-04 17:15:11 -070064
65 bool writeData(const Data& data) {
66 return TorrentManager::writeData(data);
67 }
Mickey Sweatt599bfef2016-04-05 19:11:20 -070068
69 bool writeTorrentSegment(const TorrentFile& segment, const std::string& path) {
70 return TorrentManager::writeTorrentSegment(segment, path);
71 }
72 bool writeFileManifest(const FileManifest& manifest, const std::string& path) {
73 return TorrentManager::writeFileManifest(manifest, path);
74 }
Mickey Sweatt527b0492016-03-02 11:07:48 -080075};
76
77BOOST_AUTO_TEST_SUITE(TestTorrentManagerInitialize)
78
79BOOST_AUTO_TEST_CASE(CheckInitializeComplete)
80{
81 vector<FileManifest> manifests;
82 vector<TorrentFile> torrentSegments;
83 std::string filePath = "tests/testdata/";
84 // get torrent files and manifests
85 {
86 auto temp = TorrentFile::generate("tests/testdata/foo",
87 1024,
88 1024,
89 1024,
90 false);
91 torrentSegments = temp.first;
92 auto temp1 = temp.second;
93 for (const auto& ms : temp1) {
Mickey Sweattafda1f12016-04-04 17:15:11 -070094 manifests.insert(manifests.end(), ms.first.begin(), ms.first.end());
Mickey Sweatt527b0492016-03-02 11:07:48 -080095 }
96 }
97 // write the torrent segments and manifests to disk
98 std::string dirPath = ".appdata/foo/";
99 boost::filesystem::create_directories(dirPath);
100 std::string torrentPath = dirPath + "torrent_files/";
101 boost::filesystem::create_directory(torrentPath);
102 auto fileNum = 0;
103 for (const auto& t : torrentSegments) {
104 fileNum++;
105 auto filename = torrentPath + to_string(fileNum);
106 io::save(t, filename);
107 }
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700108 //fileNum = 0;
Mickey Sweatt527b0492016-03-02 11:07:48 -0800109 auto manifestPath = dirPath + "manifests/";
110 boost::filesystem::create_directory(manifestPath);
111 for (const auto& m : manifests) {
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700112 fs::path filename = manifestPath + m.file_name() + "/" + to_string(m.submanifest_number());
113 boost::filesystem::create_directories(filename.parent_path());
114 io::save(m, filename.string());
Mickey Sweatt527b0492016-03-02 11:07:48 -0800115 }
116 // Initialize and verify
117 TestTorrentManager manager("/NTORRENT/foo/torrent-file/sha256digest=02c737fd4c6e7de4b4825b089f39700c2dfa8fd2bb2b91f09201e357c4463253",
118 filePath);
119 manager.Initialize();
120
121 // Check that the torrent segments and file manifests match (content and order)
122 BOOST_CHECK(manager.torrentSegments() == torrentSegments);
123 BOOST_CHECK(manager.fileManifests() == manifests);
124 // next check the data packet state vectors
125 for (auto m : manager.fileManifests()) {
126 auto fileState = manager.fileState(m.getFullName());
127 BOOST_CHECK(fileState.size() == m.catalog().size());
128 for (auto s : fileState) {
129 BOOST_CHECK(s);
130 }
131 }
132 fs::remove_all(dirPath);
133}
134
135BOOST_AUTO_TEST_CASE(CheckInitializeEmpty)
136{
137 TestTorrentManager manager("/NTORRENT/foo/torrent-file/sha256digest=02c737fd4c6e7de4b4825b089f39700c2dfa8fd2bb2b91f09201e357c4463253",
138 "tests/testdata/");
139 manager.Initialize();
140 BOOST_CHECK(manager.torrentSegments() == vector<TorrentFile>());
141 BOOST_CHECK(manager.fileManifests() == vector<FileManifest>());
142}
143
144BOOST_AUTO_TEST_CASE(CheckInitializeNoManifests)
145{
146 vector<TorrentFile> torrentSegments;
147 std::string filePath = "tests/testdata/";
148 // get torrent files and manifests
149 {
150 auto temp = TorrentFile::generate("tests/testdata/foo",
151 1024,
152 1024,
153 1024,
154 false);
155 torrentSegments = temp.first;
156 }
157 // write the torrent segments but no manifests to disk
158 std::string dirPath = ".appdata/foo/";
159 boost::filesystem::create_directories(dirPath);
160 std::string torrentPath = dirPath + "torrent_files/";
161 boost::filesystem::create_directory(torrentPath);
162 auto fileNum = 0;
163 for (const auto& t : torrentSegments) {
164 fileNum++;
165 auto filename = torrentPath + to_string(fileNum);
166 io::save(t, filename);
167 }
168 // Initialize and verify
169 TestTorrentManager manager("/NTORRENT/foo/torrent-file/sha256digest=02c737fd4c6e7de4b4825b089f39700c2dfa8fd2bb2b91f09201e357c4463253",
170 filePath);
171 manager.Initialize();
172
173 // Check that the torrent segments and file manifests match (content and order)
174 BOOST_CHECK(manager.torrentSegments() == torrentSegments);
175 BOOST_CHECK(manager.fileManifests() == vector<FileManifest>());
176
177 fs::remove_all(dirPath);
178}
179
180BOOST_AUTO_TEST_CASE(CheckInitializeMissingManifests)
181{
182 vector<FileManifest> manifests;
183 vector<TorrentFile> torrentSegments;
184 std::string filePath = "tests/testdata/";
185 // get torrent files and manifests
186 {
187 auto temp = TorrentFile::generate("tests/testdata/foo",
188 1024,
189 1024,
190 1024,
191 false);
192 torrentSegments = temp.first;
193 auto temp1 = temp.second;
194 temp1.pop_back(); // remove the manifests for the last file
Mickey Sweattafda1f12016-04-04 17:15:11 -0700195 for (const auto& ms : temp1) {
196 manifests.insert(manifests.end(), ms.first.begin(), ms.first.end());
Mickey Sweatt527b0492016-03-02 11:07:48 -0800197 }
198 }
199 // write the torrent segments and manifests to disk
200 std::string dirPath = ".appdata/foo/";
201 boost::filesystem::create_directories(dirPath);
202 std::string torrentPath = dirPath + "torrent_files/";
203 boost::filesystem::create_directories(torrentPath);
204 auto fileNum = 0;
205 for (const auto& t : torrentSegments) {
206 fileNum++;
207 auto filename = torrentPath + to_string(fileNum);
208 io::save(t, filename);
Mickey Sweatt527b0492016-03-02 11:07:48 -0800209 }
Mickey Sweatt527b0492016-03-02 11:07:48 -0800210 auto manifestPath = dirPath + "manifests/";
211 boost::filesystem::create_directory(manifestPath);
212 for (const auto& m : manifests) {
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700213 fs::path filename = manifestPath + m.file_name() + to_string(m.submanifest_number());
214 boost::filesystem::create_directory(filename.parent_path());
215 io::save(m, filename.string());
Mickey Sweatt527b0492016-03-02 11:07:48 -0800216 }
217 // Initialize and verify
218 TestTorrentManager manager("/NTORRENT/foo/torrent-file/sha256digest=02c737fd4c6e7de4b4825b089f39700c2dfa8fd2bb2b91f09201e357c4463253",
219 filePath);
220 manager.Initialize();
221
222 // Check that the torrent segments and file manifests match (content and order)
223 BOOST_CHECK(manager.torrentSegments() == torrentSegments);
224 BOOST_CHECK(manager.fileManifests() == manifests);
225 // next check the data packet state vectors
226 for (auto m : manager.fileManifests()) {
227 auto fileState = manager.fileState(m.getFullName());
228 BOOST_CHECK(fileState.size() == m.catalog().size());
229 for (auto s : fileState) {
230 BOOST_CHECK(s);
231 }
232 }
233 fs::remove_all(dirPath);
234}
235
236BOOST_AUTO_TEST_SUITE_END()
237
Mickey Sweattafda1f12016-04-04 17:15:11 -0700238BOOST_AUTO_TEST_SUITE(CheckTorrentManagerUtilities)
239
240BOOST_AUTO_TEST_CASE(CheckWriteDataComplete)
241{
242 vector<FileManifest> manifests;
243 vector<TorrentFile> torrentSegments;
244 // for each file, the data packets
245 std::vector<vector<Data>> fileData;
246 std::string filePath = "tests/testdata/temp";
247 // get torrent files and manifests
248 {
249 auto temp = TorrentFile::generate("tests/testdata/foo",
250 1024,
251 1024,
252 1024,
253 true);
254 torrentSegments = temp.first;
255 auto temp1 = temp.second;
256 for (const auto& ms : temp1) {
257 manifests.insert(manifests.end(), ms.first.begin(), ms.first.end());
258 fileData.push_back(ms.second);
259 }
260 }
261 // write the torrent segments and manifests to disk
262 std::string dirPath = ".appdata/foo/";
263 boost::filesystem::create_directories(dirPath);
264 std::string torrentPath = dirPath + "torrent_files/";
265 boost::filesystem::create_directories(torrentPath);
266 auto fileNum = 0;
267 for (const auto& t : torrentSegments) {
268 fileNum++;
269 auto filename = torrentPath + to_string(fileNum);
270 io::save(t, filename);
271 }
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700272 //fileNum = 0;
Mickey Sweattafda1f12016-04-04 17:15:11 -0700273 auto manifestPath = dirPath + "manifests/";
274 boost::filesystem::create_directory(manifestPath);
275 for (const auto& m : manifests) {
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700276 fs::path filename = manifestPath + m.file_name() + to_string(m.submanifest_number());
277 boost::filesystem::create_directory(filename.parent_path());
278 io::save(m, filename.string());
Mickey Sweattafda1f12016-04-04 17:15:11 -0700279 }
280 // Initialize manager
281 TestTorrentManager manager("/NTORRENT/foo/torrent-file/sha256digest=02c737fd4c6e7de4b4825b089f39700c2dfa8fd2bb2b91f09201e357c4463253",
282 filePath);
283 manager.Initialize();
284 // check that initially there is no data on disk
285 for (auto m : manager.fileManifests()) {
286 auto fileState = manager.fileState(m.getFullName());
287 BOOST_CHECK(fileState.empty());
288 }
289 // write all data to disk (for each file manifest)
290 auto manifest_it = manifests.begin();
291 for (auto& data : fileData) {
292 for (auto& d : data) {
293 BOOST_CHECK(manager.writeData(d));
294 }
295 // check that the state is updated appropriately
296 auto fileState = manager.fileState(manifest_it->getFullName());
297 for (auto s : fileState) {
298 BOOST_CHECK(s);
299 }
300 ++manifest_it;
301 }
302 // get the file names (ascending)
303 std::set<std::string> fileNames;
304 for (auto i = fs::recursive_directory_iterator(filePath + "/foo");
305 i != fs::recursive_directory_iterator();
306 ++i) {
307 fileNames.insert(i->path().string());
308 }
309 // verify file by file that the data packets are written correctly
310 auto f_it = fileData.begin();
311 for (auto f : fileNames) {
312 // read file from disk
313 std::vector<uint8_t> file_bytes;
314 fs::ifstream is(f, fs::ifstream::binary | fs::ifstream::in);
315 is >> std::noskipws;
316 std::istream_iterator<uint8_t> start(is), end;
317 file_bytes.insert(file_bytes.end(), start, end);
318 std::vector<uint8_t> data_bytes;
319 // get content from data packets
320 for (const auto& d : *f_it) {
321 auto content = d.getContent();
322 data_bytes.insert(data_bytes.end(), content.value_begin(), content.value_end());
323 }
324 BOOST_CHECK(data_bytes == file_bytes);
325 ++f_it;
326 }
327 fs::remove_all(filePath);
328 fs::remove_all(".appdata");
329}
330
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700331BOOST_AUTO_TEST_CASE(CheckWriteTorrentComplete)
332{
333 const struct {
334 const char *d_directoryPath;
335 const char *d_initialSegmentName;
336 size_t d_namesPerSegment;
337 size_t d_subManifestSize;
338 size_t d_dataPacketSize;
339 } DATA [] = {
340 {"tests/testdata/foo", "/NTORRENT/foo/torrent-file/sha256digest=02c737fd4c6e7de4b4825b089f39700c2dfa8fd2bb2b91f09201e357c4463253", 1024, 1024, 1024},
341 {"tests/testdata/foo", "/NTORRENT/foo/torrent-file/sha256digest=96d900d6788465f9a7b00191581b004c910d74b3762d141ec0e82173731bc9f4", 1, 1, 1024},
342 };
343 enum { NUM_DATA = sizeof DATA / sizeof *DATA };
344 for (int i = 0; i < NUM_DATA; ++i) {
345 auto directoryPath = DATA[i].d_directoryPath;
346 Name initialSegmentName = DATA[i].d_initialSegmentName;
347 auto namesPerSegment = DATA[i].d_namesPerSegment;
348 auto dataPacketSize = DATA[i].d_dataPacketSize;
349 auto subManifestSize = DATA[i].d_subManifestSize;
350
351 vector<TorrentFile> torrentSegments;
352 std::string filePath = "tests/testdata/temp";
353 // get torrent files
354 {
355 auto temp = TorrentFile::generate(directoryPath,
356 namesPerSegment,
357 subManifestSize,
358 dataPacketSize,
359 false);
360 torrentSegments = temp.first;
361 }
362 // Initialize manager
363 TestTorrentManager manager(initialSegmentName,
364 filePath);
365 manager.Initialize();
366 std::string dirPath = ".appdata/foo/";
367 std::string torrentPath = dirPath + "torrent_files/";
368 BOOST_CHECK(manager.torrentSegments().empty());
369 for (const auto& t : torrentSegments) {
370 BOOST_CHECK(manager.writeTorrentSegment(t, torrentPath));
371 }
372 BOOST_CHECK(manager.torrentSegments() == torrentSegments);
373 // check that initializing a new manager also gets all the torrent torrentSegments
374 TestTorrentManager manager2(initialSegmentName,
375 filePath);
376 manager2.Initialize();
377 BOOST_CHECK(manager2.torrentSegments() == torrentSegments);
378
379 // start anew
380 fs::remove_all(torrentPath);
381 fs::create_directories(torrentPath);
382 manager.Initialize();
383 BOOST_CHECK(manager.torrentSegments().empty());
384
385 // check that there is no dependence on the order of torrent segments
386 // randomize the order of the torrent segments
387 auto torrentSegmentsRandom = torrentSegments;
388 std::random_shuffle(torrentSegmentsRandom.begin(), torrentSegmentsRandom.end());
389 for (const auto& t : torrentSegmentsRandom) {
390 BOOST_CHECK(manager.writeTorrentSegment(t, torrentPath));
391 }
392 BOOST_CHECK(manager.torrentSegments() == torrentSegments);
393 fs::remove_all(".appdata");
394 }
395}
396
397BOOST_AUTO_TEST_CASE(CheckWriteManifestComplete)
398{
399 std::string dirPath = ".appdata/foo/";
400 std::string torrentPath = dirPath + "torrent_files/";
401 std::string manifestPath = dirPath + "manifests/";
402
403 const struct {
404 const char *d_directoryPath;
405 const char *d_initialSegmentName;
406 size_t d_namesPerSegment;
407 size_t d_subManifestSize;
408 size_t d_dataPacketSize;
409 } DATA [] = {
410 {"tests/testdata/foo", "/NTORRENT/foo/torrent-file/sha256digest=02c737fd4c6e7de4b4825b089f39700c2dfa8fd2bb2b91f09201e357c4463253", 1024, 1024, 1024},
411 {"tests/testdata/foo", "/NTORRENT/foo/torrent-file/sha256digest=02c737fd4c6e7de4b4825b089f39700c2dfa8fd2bb2b91f09201e357c4463253", 128, 128, 1024},
412 };
413 enum { NUM_DATA = sizeof DATA / sizeof *DATA };
414 for (int i = 0; i < NUM_DATA; ++i) {
415 auto directoryPath = DATA[i].d_directoryPath;
416 Name initialSegmentName = DATA[i].d_initialSegmentName;
417 auto namesPerSegment = DATA[i].d_namesPerSegment;
418 auto dataPacketSize = DATA[i].d_dataPacketSize;
419 auto subManifestSize = DATA[i].d_subManifestSize;
420
421 vector<FileManifest> manifests;
422 vector<TorrentFile> torrentSegments;
423
424 std::string filePath = "tests/testdata/temp";
425 // get torrent files and manifests
426 {
427 auto temp = TorrentFile::generate(directoryPath,
428 namesPerSegment,
429 subManifestSize,
430 dataPacketSize,
431 false);
432 torrentSegments = temp.first;
433 auto temp1 = temp.second;
434 for (const auto& ms : temp1) {
435 manifests.insert(manifests.end(), ms.first.begin(), ms.first.end());
436 }
437 }
438 TestTorrentManager manager(initialSegmentName,
439 filePath);
440 manager.Initialize();
441 for (const auto& t : torrentSegments) {
442 manager.writeTorrentSegment(t, torrentPath);
443 }
444
445 BOOST_CHECK(manager.fileManifests().empty());
446 for (const auto& m : manifests) {
447 BOOST_CHECK(manager.writeFileManifest(m, manifestPath));
448 }
449 BOOST_CHECK(manager.fileManifests() == manifests);
450
451 TestTorrentManager manager2(initialSegmentName,
452 filePath);
453
454 manager2.Initialize();
455 BOOST_CHECK(manager2.fileManifests() == manifests);
456
457 // start anew
458 fs::remove_all(manifestPath);
459 fs::create_directories(manifestPath);
460 manager.Initialize();
461 BOOST_CHECK(manager.fileManifests().empty());
462
463 // check that there is no dependence on the order of torrent segments
464 // randomize the order of the torrent segments
465 auto fileManifestsRandom = manifests;
466 std::random_shuffle(fileManifestsRandom.begin(), fileManifestsRandom.end());
467 for (const auto& m : fileManifestsRandom) {
468 BOOST_CHECK(manager.writeFileManifest(m, manifestPath));
469 }
470 BOOST_CHECK(manager2.fileManifests() == manifests);
471 fs::remove_all(".appdata");
472 }
473}
474
Mickey Sweattafda1f12016-04-04 17:15:11 -0700475
476BOOST_AUTO_TEST_SUITE_END()
477
Mickey Sweatt527b0492016-03-02 11:07:48 -0800478} // namespace tests
479} // namespace nTorrent
480} // namespace ndn