3 Copyright (c) 2008, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/storage.hpp"
34 #include "libtorrent/file_pool.hpp"
35 #include "libtorrent/hasher.hpp"
36 #include "libtorrent/session.hpp"
37 #include "libtorrent/alert_types.hpp"
38 #include "libtorrent/aux_/session_impl.hpp"
39 #include "libtorrent/create_torrent.hpp"
41 #include <boost/utility.hpp>
42 #include <boost/filesystem/operations.hpp>
43 #include <boost/filesystem/convenience.hpp>
44 #include <boost/thread/mutex.hpp>
47 #include "setup_transfer.hpp"
49 using namespace libtorrent
;
50 using namespace boost::filesystem
;
52 const int piece_size
= 16;
54 const int half
= piece_size
/ 2;
56 char piece0
[piece_size
] =
57 { 6, 6, 6, 6, 6, 6, 6, 6
58 , 9, 9, 9, 9, 9, 9, 9, 9};
60 char piece1
[piece_size
] =
61 { 0, 0, 0, 0, 0, 0, 0, 0
62 , 1, 1, 1, 1, 1, 1, 1, 1};
64 char piece2
[piece_size
] =
65 { 0, 0, 1, 0, 0, 0, 0, 0
66 , 1, 1, 1, 1, 1, 1, 1, 1};
68 void on_read_piece(int ret
, disk_io_job
const& j
, char const* data
, int size
)
70 std::cerr
<< "on_read_piece piece: " << j
.piece
<< std::endl
;
71 TEST_CHECK(ret
== size
);
72 TEST_CHECK(std::equal(j
.buffer
, j
.buffer
+ ret
, data
));
75 void on_check_resume_data(int ret
, disk_io_job
const& j
)
77 std::cerr
<< "on_check_resume_data ret: " << ret
;
80 case 0: std::cerr
<< " success" << std::endl
; break;
81 case -1: std::cerr
<< " need full check" << std::endl
; break;
82 case -2: std::cerr
<< " disk error: " << j
.str
83 << " file: " << j
.error_file
<< std::endl
; break;
84 case -3: std::cerr
<< " aborted" << std::endl
; break;
88 void on_check_files(int ret
, disk_io_job
const& j
)
90 std::cerr
<< "on_check_files ret: " << ret
;
94 case 0: std::cerr
<< " done" << std::endl
; break;
95 case -1: std::cerr
<< " current slot: " << j
.piece
<< " have: " << j
.offset
<< std::endl
; break;
96 case -2: std::cerr
<< " disk error: " << j
.str
97 << " file: " << j
.error_file
<< std::endl
; break;
98 case -3: std::cerr
<< " aborted" << std::endl
; break;
102 void on_move_storage(int ret
, disk_io_job
const& j
, std::string path
)
104 std::cerr
<< "on_move_storage ret: " << ret
<< " path: " << j
.str
<< std::endl
;
105 TEST_CHECK(ret
== 0);
106 TEST_CHECK(j
.str
== path
);
109 void run_storage_tests(boost::intrusive_ptr
<torrent_info
> info
111 , path
const& test_path
112 , libtorrent::storage_mode_t storage_mode
)
114 TORRENT_ASSERT(fs
.num_files() > 0);
115 create_directory(test_path
/ "temp_storage");
117 int num_pieces
= (1 + 612 + 17 + piece_size
- 1) / piece_size
;
118 TEST_CHECK(info
->num_pieces() == num_pieces
);
120 char piece
[piece_size
];
122 { // avoid having two storages use the same files
124 boost::scoped_ptr
<storage_interface
> s(
125 default_storage_constructor(fs
, test_path
, fp
));
127 // write piece 1 (in slot 0)
128 s
->write(piece1
, 0, 0, half
);
129 s
->write(piece1
+ half
, 0, half
, half
);
132 TEST_CHECK(s
->read(piece
, 0, 0, piece_size
) == piece_size
);
133 TEST_CHECK(std::equal(piece
, piece
+ piece_size
, piece1
));
135 // do the same with piece 0 and 2 (in slot 1 and 2)
136 s
->write(piece0
, 1, 0, piece_size
);
137 s
->write(piece2
, 2, 0, piece_size
);
139 // verify piece 0 and 2
140 TEST_CHECK(s
->read(piece
, 1, 0, piece_size
) == piece_size
);
141 TEST_CHECK(std::equal(piece
, piece
+ piece_size
, piece0
));
143 s
->read(piece
, 2, 0, piece_size
);
144 TEST_CHECK(std::equal(piece
, piece
+ piece_size
, piece2
));
149 // make sure the piece_manager can identify the pieces
152 libtorrent::asio::io_service ios
;
153 disk_io_thread
io(ios
);
154 boost::shared_ptr
<int> dummy(new int);
155 boost::intrusive_ptr
<piece_manager
> pm
= new piece_manager(dummy
, info
156 , test_path
, fp
, io
, default_storage_constructor
, storage_mode
);
160 pm
->async_check_fastresume(&frd
, &on_check_resume_data
);
164 pm
->async_check_files(&on_check_files
);
165 for (int i
= 0; i
< 4; ++i
)
174 boost::function
<void(int, disk_io_job
const&)> none
;
175 TEST_CHECK(exists(test_path
/ "temp_storage"));
176 pm
->async_move_storage(test_path
/ "temp_storage2", bind(on_move_storage
, _1
, _2
, (test_path
/ "temp_storage2").string()));
182 TEST_CHECK(!exists(test_path
/ "temp_storage"));
183 TEST_CHECK(exists(test_path
/ "temp_storage2/temp_storage"));
184 pm
->async_move_storage(test_path
, bind(on_move_storage
, _1
, _2
, test_path
.string()));
190 TEST_CHECK(!exists(test_path
/ "temp_storage2/temp_storage"));
191 remove_all(test_path
/ "temp_storage2");
194 remove(test_path
/ "part0");
195 TEST_CHECK(exists(test_path
/ "temp_storage/test1.tmp"));
196 TEST_CHECK(!exists(test_path
/ "part0"));
197 pm
->async_rename_file(0, "part0", none
);
203 TEST_CHECK(!exists(test_path
/ "temp_storage/test1.tmp"));
204 TEST_CHECK(exists(test_path
/ "part0"));
209 r
.length
= piece_size
;
210 pm
->async_read(r
, bind(&on_read_piece
, _1
, _2
, piece0
, piece_size
));
212 pm
->async_read(r
, bind(&on_read_piece
, _1
, _2
, piece1
, piece_size
));
214 pm
->async_read(r
, bind(&on_read_piece
, _1
, _2
, piece2
, piece_size
));
215 pm
->async_release_files(none
);
217 pm
->async_rename_file(0, "temp_storage/test1.tmp", none
);
219 TEST_CHECK(!exists(test_path
/ "part0"));
227 void test_remove(path
const& test_path
)
230 fs
.add_file("temp_storage/test1.tmp", 8);
231 fs
.add_file("temp_storage/folder1/test2.tmp", 8);
232 fs
.add_file("temp_storage/folder2/test3.tmp", 0);
233 fs
.add_file("temp_storage/_folder3/test4.tmp", 0);
234 fs
.add_file("temp_storage/_folder3/subfolder/test5.tmp", 8);
235 libtorrent::create_torrent
t(fs
, 4);
237 char buf
[4] = {0, 0, 0, 0};
238 sha1_hash h
= hasher(buf
, 4).final();
239 for (int i
= 0; i
< 6; ++i
) t
.set_hash(i
, h
);
241 boost::intrusive_ptr
<torrent_info
> info(new torrent_info(t
.generate()));
244 boost::scoped_ptr
<storage_interface
> s(
245 default_storage_constructor(fs
, test_path
, fp
));
247 // allocate the files and create the directories
250 TEST_CHECK(exists(test_path
/ "temp_storage/_folder3/subfolder/test5.tmp"));
251 TEST_CHECK(exists(test_path
/ "temp_storage/folder2/test3.tmp"));
255 TEST_CHECK(!exists(test_path
/ "temp_storage"));
260 void check_files_fill_array(int ret
, disk_io_job
const& j
, bool* array
, bool* done
)
262 if (j
.offset
>= 0) array
[j
.offset
] = true;
271 void test_check_files(path
const& test_path
272 , libtorrent::storage_mode_t storage_mode
)
274 boost::intrusive_ptr
<torrent_info
> info
;
276 const int piece_size
= 16 * 1024;
277 remove_all(test_path
/ "temp_storage");
279 fs
.add_file("temp_storage/test1.tmp", piece_size
);
280 fs
.add_file("temp_storage/test2.tmp", piece_size
* 2);
281 fs
.add_file("temp_storage/test3.tmp", piece_size
);
283 char piece0
[piece_size
];
284 char piece2
[piece_size
];
286 std::generate(piece0
, piece0
+ piece_size
, std::rand
);
287 std::generate(piece2
, piece2
+ piece_size
, std::rand
);
289 libtorrent::create_torrent
t(fs
, piece_size
);
290 t
.set_hash(0, hasher(piece0
, piece_size
).final());
291 t
.set_hash(1, sha1_hash(0));
292 t
.set_hash(2, sha1_hash(0));
293 t
.set_hash(3, hasher(piece2
, piece_size
).final());
295 create_directory(test_path
/ "temp_storage");
298 f
.open((test_path
/ "temp_storage/test1.tmp").string().c_str());
299 f
.write(piece0
, sizeof(piece0
));
301 f
.open((test_path
/ "temp_storage/test3.tmp").string().c_str());
302 f
.write(piece2
, sizeof(piece2
));
305 info
= new torrent_info(t
.generate());
308 libtorrent::asio::io_service ios
;
309 disk_io_thread
io(ios
);
310 boost::shared_ptr
<int> dummy(new int);
311 boost::intrusive_ptr
<piece_manager
> pm
= new piece_manager(dummy
, info
312 , test_path
, fp
, io
, default_storage_constructor
, storage_mode
);
316 pm
->async_check_fastresume(&frd
, &on_check_resume_data
);
320 bool pieces
[4] = {false, false, false, false};
323 pm
->async_check_files(bind(&check_files_fill_array
, _1
, _2
, pieces
, &done
));
329 TEST_CHECK(pieces
[0] == true);
330 TEST_CHECK(pieces
[1] == false);
331 TEST_CHECK(pieces
[2] == false);
332 TEST_CHECK(pieces
[3] == true);
336 void run_test(path
const& test_path
)
338 std::cerr
<< "\n=== " << test_path
.string() << " ===\n" << std::endl
;
340 boost::intrusive_ptr
<torrent_info
> info
;
343 remove_all(test_path
/ "temp_storage");
345 fs
.add_file("temp_storage/test1.tmp", 17);
346 fs
.add_file("temp_storage/test2.tmp", 612);
347 fs
.add_file("temp_storage/test3.tmp", 0);
348 fs
.add_file("temp_storage/test4.tmp", 0);
349 fs
.add_file("temp_storage/test5.tmp", 1);
351 libtorrent::create_torrent
t(fs
, piece_size
);
352 t
.set_hash(0, hasher(piece0
, piece_size
).final());
353 t
.set_hash(1, hasher(piece1
, piece_size
).final());
354 t
.set_hash(2, hasher(piece2
, piece_size
).final());
357 info
= new torrent_info(t
.generate());
358 std::cerr
<< "=== test 1 ===" << std::endl
;
360 run_storage_tests(info
, fs
, test_path
, storage_mode_compact
);
362 // make sure the files have the correct size
363 std::cerr
<< file_size(test_path
/ "temp_storage" / "test1.tmp") << std::endl
;
364 TEST_CHECK(file_size(test_path
/ "temp_storage" / "test1.tmp") == 17);
365 std::cerr
<< file_size(test_path
/ "temp_storage" / "test2.tmp") << std::endl
;
366 TEST_CHECK(file_size(test_path
/ "temp_storage" / "test2.tmp") == 31);
367 TEST_CHECK(exists(test_path
/ "temp_storage/test3.tmp"));
368 TEST_CHECK(exists(test_path
/ "temp_storage/test4.tmp"));
369 remove_all(test_path
/ "temp_storage");
372 // ==============================================
376 fs
.add_file("temp_storage/test1.tmp", 17 + 612 + 1);
377 libtorrent::create_torrent
t(fs
, piece_size
);
378 TEST_CHECK(fs
.begin()->path
== "temp_storage/test1.tmp");
379 t
.set_hash(0, hasher(piece0
, piece_size
).final());
380 t
.set_hash(1, hasher(piece1
, piece_size
).final());
381 t
.set_hash(2, hasher(piece2
, piece_size
).final());
383 info
= new torrent_info(t
.generate());
385 std::cerr
<< "=== test 3 ===" << std::endl
;
387 run_storage_tests(info
, fs
, test_path
, storage_mode_compact
);
389 // 48 = piece_size * 3
390 TEST_CHECK(file_size(test_path
/ "temp_storage" / "test1.tmp") == 48);
391 remove_all(test_path
/ "temp_storage");
393 // ==============================================
395 std::cerr
<< "=== test 4 ===" << std::endl
;
397 run_storage_tests(info
, fs
, test_path
, storage_mode_allocate
);
399 std::cerr
<< file_size(test_path
/ "temp_storage" / "test1.tmp") << std::endl
;
400 TEST_CHECK(file_size(test_path
/ "temp_storage" / "test1.tmp") == 17 + 612 + 1);
402 remove_all(test_path
/ "temp_storage");
406 // ==============================================
408 std::cerr
<< "=== test 5 ===" << std::endl
;
409 test_remove(test_path
);
411 // ==============================================
413 std::cerr
<< "=== test 6 ===" << std::endl
;
414 test_check_files(test_path
, storage_mode_sparse
);
415 test_check_files(test_path
, storage_mode_compact
);
418 void test_fastresume()
420 std::cout
<< "=== test fastresume ===" << std::endl
;
421 create_directory("tmp1");
422 std::ofstream
file("tmp1/temporary");
423 boost::intrusive_ptr
<torrent_info
> t
= ::create_torrent(&file
);
425 TEST_CHECK(exists("tmp1/temporary"));
431 torrent_handle h
= ses
.add_torrent(boost::intrusive_ptr
<torrent_info
>(new torrent_info(*t
))
433 , storage_mode_compact
);
435 for (int i
= 0; i
< 10; ++i
)
438 torrent_status s
= h
.status();
439 if (s
.progress
== 1.0f
)
441 std::cout
<< "progress: 1.0f" << std::endl
;
445 resume
= h
.write_resume_data();
446 ses
.remove_torrent(h
, session::delete_files
);
448 TEST_CHECK(!exists("tmp1/temporary"));
449 resume
.print(std::cout
);
453 ses
.set_alert_mask(alert::all_categories
);
454 torrent_handle h
= ses
.add_torrent(t
, "tmp1", resume
455 , storage_mode_compact
);
458 std::auto_ptr
<alert
> a
= ses
.pop_alert();
459 ptime end
= time_now() + seconds(20);
460 while (a
.get() == 0 || dynamic_cast<fastresume_rejected_alert
*>(a
.get()) == 0)
462 if (ses
.wait_for_alert(end
- time_now()) == 0)
464 std::cerr
<< "wait_for_alert() expired" << std::endl
;
469 std::cerr
<< a
->message() << std::endl
;
471 TEST_CHECK(dynamic_cast<fastresume_rejected_alert
*>(a
.get()) != 0);
477 std::vector
<path
> test_paths
;
478 char* env
= std::getenv("TORRENT_TEST_PATHS");
481 test_paths
.push_back(initial_path());
485 char* p
= std::strtok(env
, ";");
488 test_paths
.push_back(complete(p
));
489 p
= std::strtok(0, ";");
493 std::for_each(test_paths
.begin(), test_paths
.end(), bind(&run_test
, _1
));