fixed typo
[libtorrent.git] / test / test_storage.cpp
blob9ef18ca1e4fbfd399259391cf50a164922b20d90
1 /*
3 Copyright (c) 2008, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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>
46 #include "test.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;
78 switch (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;
92 switch (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
110 , file_storage& fs
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
123 file_pool fp;
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);
131 // verify piece 1
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));
146 s->release_files();
149 // make sure the piece_manager can identify the pieces
151 file_pool fp;
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);
157 boost::mutex lock;
159 lazy_entry frd;
160 pm->async_check_fastresume(&frd, &on_check_resume_data);
161 ios.reset();
162 ios.run();
164 pm->async_check_files(&on_check_files);
165 for (int i = 0; i < 4; ++i)
167 ios.reset();
168 ios.run_one();
170 ios.reset();
171 ios.poll();
173 // test move_storage
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()));
178 test_sleep(2000);
179 ios.reset();
180 ios.poll();
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()));
186 test_sleep(2000);
187 ios.reset();
188 ios.poll();
190 TEST_CHECK(!exists(test_path / "temp_storage2/temp_storage"));
191 remove_all(test_path / "temp_storage2");
193 // test rename_file
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);
199 test_sleep(2000);
200 ios.reset();
201 ios.poll();
203 TEST_CHECK(!exists(test_path / "temp_storage/test1.tmp"));
204 TEST_CHECK(exists(test_path / "part0"));
206 peer_request r;
207 r.piece = 0;
208 r.start = 0;
209 r.length = piece_size;
210 pm->async_read(r, bind(&on_read_piece, _1, _2, piece0, piece_size));
211 r.piece = 1;
212 pm->async_read(r, bind(&on_read_piece, _1, _2, piece1, piece_size));
213 r.piece = 2;
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);
218 test_sleep(1000);
219 TEST_CHECK(!exists(test_path / "part0"));
221 ios.run();
223 io.join();
227 void test_remove(path const& test_path)
229 file_storage fs;
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()));
243 file_pool fp;
244 boost::scoped_ptr<storage_interface> s(
245 default_storage_constructor(fs, test_path, fp));
247 // allocate the files and create the directories
248 s->initialize(true);
250 TEST_CHECK(exists(test_path / "temp_storage/_folder3/subfolder/test5.tmp"));
251 TEST_CHECK(exists(test_path / "temp_storage/folder2/test3.tmp"));
253 s->delete_files();
255 TEST_CHECK(!exists(test_path / "temp_storage"));
258 namespace
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;
263 if (ret != -1)
265 *done = true;
266 return;
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");
278 file_storage fs;
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");
297 std::ofstream f;
298 f.open((test_path / "temp_storage/test1.tmp").string().c_str());
299 f.write(piece0, sizeof(piece0));
300 f.close();
301 f.open((test_path / "temp_storage/test3.tmp").string().c_str());
302 f.write(piece2, sizeof(piece2));
303 f.close();
305 info = new torrent_info(t.generate());
307 file_pool fp;
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);
313 boost::mutex lock;
315 lazy_entry frd;
316 pm->async_check_fastresume(&frd, &on_check_resume_data);
317 ios.reset();
318 ios.run();
320 bool pieces[4] = {false, false, false, false};
321 bool done = false;
323 pm->async_check_files(bind(&check_files_fill_array, _1, _2, pieces, &done));
324 while (!done)
326 ios.reset();
327 ios.run_one();
329 TEST_CHECK(pieces[0] == true);
330 TEST_CHECK(pieces[1] == false);
331 TEST_CHECK(pieces[2] == false);
332 TEST_CHECK(pieces[3] == true);
333 io.join();
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");
344 file_storage fs;
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 // ==============================================
375 file_storage fs;
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);
424 file.close();
425 TEST_CHECK(exists("tmp1/temporary"));
427 entry resume;
429 session ses;
431 torrent_handle h = ses.add_torrent(boost::intrusive_ptr<torrent_info>(new torrent_info(*t))
432 , "tmp1", entry()
433 , storage_mode_compact);
435 for (int i = 0; i < 10; ++i)
437 test_sleep(1000);
438 torrent_status s = h.status();
439 if (s.progress == 1.0f)
441 std::cout << "progress: 1.0f" << std::endl;
442 break;
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);
452 session ses;
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;
465 break;
467 a = ses.pop_alert();
468 assert(a.get());
469 std::cerr << a->message() << std::endl;
471 TEST_CHECK(dynamic_cast<fastresume_rejected_alert*>(a.get()) != 0);
475 int test_main()
477 std::vector<path> test_paths;
478 char* env = std::getenv("TORRENT_TEST_PATHS");
479 if (env == 0)
481 test_paths.push_back(initial_path());
483 else
485 char* p = std::strtok(env, ";");
486 while (p != 0)
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));
495 test_fastresume();
497 return 0;