1 /*
2 
3 Copyright (c) 2012, Arvid Norberg
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 
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.
18 
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.
30 
31 */
32 
33 #include "test.hpp"
34 #include "libtorrent/file_storage.hpp"
35 #include "libtorrent/aux_/path.hpp"
36 #include "libtorrent/torrent_info.hpp"
37 #include "libtorrent/create_torrent.hpp"
38 #include "libtorrent/announce_entry.hpp"
39 #include "libtorrent/aux_/escape_string.hpp" // for convert_path_to_posix
40 #include "libtorrent/hex.hpp" // to_hex
41 
42 #include <iostream>
43 
44 using namespace lt;
45 
46 #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
TORRENT_TEST(mutable_torrents)47 TORRENT_TEST(mutable_torrents)
48 {
49 	file_storage fs;
50 
51 	fs.add_file("test/temporary.txt", 0x4000);
52 
53 	lt::create_torrent t(fs, 0x4000);
54 
55 	// calculate the hash for all pieces
56 	sha1_hash ph;
57 	for (auto const i : fs.piece_range())
58 		t.set_hash(i, ph);
59 
60 	t.add_collection("collection1");
61 	t.add_collection("collection2");
62 
63 	t.add_similar_torrent(sha1_hash("abababababababababab"));
64 	t.add_similar_torrent(sha1_hash("babababababababababa"));
65 
66 	std::vector<char> tmp;
67 	std::back_insert_iterator<std::vector<char>> out(tmp);
68 
69 	entry tor = t.generate();
70 	bencode(out, tor);
71 
72 	torrent_info ti(tmp, from_span);
73 
74 	std::vector<sha1_hash> similar;
75 	similar.push_back(sha1_hash("abababababababababab"));
76 	similar.push_back(sha1_hash("babababababababababa"));
77 
78 	std::vector<std::string> collections;
79 	collections.push_back("collection1");
80 	collections.push_back("collection2");
81 
82 	TEST_CHECK(similar == ti.similar_torrents());
83 	TEST_CHECK(collections == ti.collections());
84 }
85 #endif
86 
87 namespace {
88 
89 struct test_torrent_t
90 {
91 	char const* file;
92 };
93 
94 using namespace lt;
95 
96 static test_torrent_t test_torrents[] =
97 {
98 	{ "base.torrent" },
99 	{ "empty_path.torrent" },
100 	{ "parent_path.torrent" },
101 	{ "hidden_parent_path.torrent" },
102 	{ "single_multi_file.torrent" },
103 	{ "slash_path.torrent" },
104 	{ "slash_path2.torrent" },
105 	{ "slash_path3.torrent" },
106 	{ "backslash_path.torrent" },
107 	{ "url_list.torrent" },
108 	{ "url_list2.torrent" },
109 	{ "url_list3.torrent" },
110 	{ "httpseed.torrent" },
111 	{ "empty_httpseed.torrent" },
112 	{ "long_name.torrent" },
113 	{ "whitespace_url.torrent" },
114 	{ "duplicate_files.torrent" },
115 	{ "pad_file.torrent" },
116 	{ "creation_date.torrent" },
117 	{ "no_creation_date.torrent" },
118 	{ "url_seed.torrent" },
119 	{ "url_seed_multi.torrent" },
120 	{ "url_seed_multi_single_file.torrent" },
121 	{ "url_seed_multi_space.torrent" },
122 	{ "url_seed_multi_space_nolist.torrent" },
123 	{ "root_hash.torrent" },
124 	{ "empty_path_multi.torrent" },
125 	{ "duplicate_web_seeds.torrent" },
126 	{ "invalid_name2.torrent" },
127 	{ "invalid_name3.torrent" },
128 	{ "symlink1.torrent" },
129 	{ "symlink2.torrent" },
130 	{ "unordered.torrent" },
131 	{ "symlink_zero_size.torrent" },
132 	{ "pad_file_no_path.torrent" },
133 	{ "large.torrent" },
134 	{ "absolute_filename.torrent" },
135 	{ "invalid_filename.torrent" },
136 	{ "invalid_filename2.torrent" },
137 	{ "overlapping_symlinks.torrent" },
138 };
139 
140 struct test_failing_torrent_t
141 {
142 	char const* file;
143 	error_code error; // the expected error
144 };
145 
146 test_failing_torrent_t test_error_torrents[] =
147 {
148 	{ "missing_piece_len.torrent", errors::torrent_missing_piece_length },
149 	{ "invalid_piece_len.torrent", errors::torrent_missing_piece_length },
150 	{ "negative_piece_len.torrent", errors::torrent_missing_piece_length },
151 	{ "no_name.torrent", errors::torrent_missing_name },
152 	{ "bad_name.torrent", errors::torrent_missing_name },
153 	{ "invalid_name.torrent", errors::torrent_missing_name },
154 	{ "invalid_info.torrent", errors::torrent_missing_info },
155 	{ "string.torrent", errors::torrent_is_no_dict },
156 	{ "negative_size.torrent", errors::torrent_invalid_length },
157 	{ "negative_file_size.torrent", errors::torrent_invalid_length },
158 	{ "invalid_path_list.torrent", errors::torrent_invalid_name},
159 	{ "missing_path_list.torrent", errors::torrent_missing_name },
160 	{ "invalid_pieces.torrent", errors::torrent_missing_pieces },
161 	{ "unaligned_pieces.torrent", errors::torrent_invalid_hashes },
162 	{ "invalid_root_hash.torrent", errors::torrent_invalid_hashes },
163 	{ "invalid_root_hash2.torrent", errors::torrent_missing_pieces },
164 	{ "invalid_merkle.torrent", errors::no_files_in_torrent},
165 	{ "invalid_file_size.torrent", errors::torrent_invalid_length },
166 	{ "invalid_symlink.torrent", errors::torrent_invalid_name },
167 	{ "many_pieces.torrent", errors::too_many_pieces_in_torrent },
168 	{ "no_files.torrent", errors::no_files_in_torrent},
169 	{ "zero.torrent", errors::torrent_invalid_length},
170 	{ "zero2.torrent", errors::torrent_invalid_length},
171 };
172 
173 } // anonymous namespace
174 
175 // TODO: test remap_files
176 // TODO: merkle torrents. specifically torrent_info::add_merkle_nodes and torrent with "root hash"
177 // TODO: torrent with 'p' (padfile) attribute
178 // TODO: torrent with 'h' (hidden) attribute
179 // TODO: torrent with 'x' (executable) attribute
180 // TODO: torrent with 'l' (symlink) attribute
181 // TODO: creating a merkle torrent (torrent_info::build_merkle_list)
182 // TODO: torrent with multiple trackers in multiple tiers, making sure we
183 // shuffle them (how do you test shuffling?, load it multiple times and make
184 // sure it's in different order at least once)
185 // TODO: torrents with a zero-length name
186 // TODO: torrents with a merkle tree and add_merkle_nodes
187 // TODO: torrent with a non-dictionary info-section
188 // TODO: torrents with DHT nodes
189 // TODO: torrent with url-list as a single string
190 // TODO: torrent with http seed as a single string
191 // TODO: torrent with a comment
192 // TODO: torrent with an SSL cert
193 // TODO: torrent with attributes (executable and hidden)
194 // TODO: torrent_info::add_tracker
195 // TODO: torrent_info constructor that takes an invalid bencoded buffer
196 // TODO: verify_encoding with a string that triggers character replacement
197 
TORRENT_TEST(url_list_and_httpseeds)198 TORRENT_TEST(url_list_and_httpseeds)
199 {
200 	entry info;
201 	info["pieces"] = "aaaaaaaaaaaaaaaaaaaa";
202 	info["name.utf-8"] = "test1";
203 	info["name"] = "test__";
204 	info["piece length"] = 16 * 1024;
205 	info["length"] = 3245;
206 	entry::list_type l;
207 	l.push_back(entry("http://foo.com/bar1"));
208 	l.push_back(entry("http://foo.com/bar1"));
209 	l.push_back(entry("http://foo.com/bar2"));
210 	entry const e(l);
211 	entry torrent;
212 	torrent["url-list"] = e;
213 	torrent["httpseeds"] = e;
214 	torrent["info"] = info;
215 	std::vector<char> buf;
216 	bencode(std::back_inserter(buf), torrent);
217 	torrent_info ti(buf, from_span);
218 	TEST_EQUAL(ti.web_seeds().size(), 4);
219 }
220 
TORRENT_TEST(add_url_seed)221 TORRENT_TEST(add_url_seed)
222 {
223 	torrent_info ti(sha1_hash("                   "));
224 	TEST_EQUAL(ti.web_seeds().size(), 0);
225 
226 	ti.add_url_seed("http://test.com");
227 
228 	TEST_EQUAL(ti.web_seeds().size(), 1);
229 	web_seed_entry we = ti.web_seeds()[0];
230 	TEST_EQUAL(we.type, web_seed_entry::url_seed);
231 	TEST_EQUAL(we.url, "http://test.com");
232 }
233 
TORRENT_TEST(add_http_seed)234 TORRENT_TEST(add_http_seed)
235 {
236 	torrent_info ti(sha1_hash("                   "));
237 	TEST_EQUAL(ti.web_seeds().size(), 0);
238 
239 	ti.add_http_seed("http://test.com");
240 
241 	TEST_EQUAL(ti.web_seeds().size(), 1);
242 	web_seed_entry we = ti.web_seeds()[0];
243 	TEST_EQUAL(we.type, web_seed_entry::http_seed);
244 	TEST_EQUAL(we.url, "http://test.com");
245 }
246 
TORRENT_TEST(set_web_seeds)247 TORRENT_TEST(set_web_seeds)
248 {
249 	torrent_info ti(sha1_hash("                   "));
250 	TEST_EQUAL(ti.web_seeds().size(), 0);
251 
252 	std::vector<web_seed_entry> seeds;
253 	web_seed_entry e1("http://test1.com", web_seed_entry::url_seed);
254 	seeds.push_back(e1);
255 	web_seed_entry e2("http://test2com", web_seed_entry::http_seed);
256 	seeds.push_back(e2);
257 
258 	ti.set_web_seeds(seeds);
259 
260 	TEST_EQUAL(ti.web_seeds().size(), 2);
261 	TEST_CHECK(ti.web_seeds() == seeds);
262 }
263 
264 #ifdef TORRENT_WINDOWS
265 #define SEPARATOR "\\"
266 #else
267 #define SEPARATOR "/"
268 #endif
269 
TORRENT_TEST(sanitize_path_truncate)270 TORRENT_TEST(sanitize_path_truncate)
271 {
272 	std::string path;
273 	sanitize_append_path_element(path,
274 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
275 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
276 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
277 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
278 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_");
279 	sanitize_append_path_element(path,
280 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
281 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
282 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
283 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
284 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcde.test");
285 	TEST_EQUAL(path,
286 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
287 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
288 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
289 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
290 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_" SEPARATOR
291 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
292 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
293 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
294 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
295 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_.test");
296 }
297 
TORRENT_TEST(sanitize_path_truncate_utf)298 TORRENT_TEST(sanitize_path_truncate_utf)
299 {
300 	std::string path;
301 	// msvc doesn't like unicode string literals, so we encode it as UTF-8 explicitly
302 	sanitize_append_path_element(path,
303 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
304 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
305 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
306 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
307 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi" "\xE2" "\x80" "\x94" "abcde.jpg");
308 	TEST_EQUAL(path,
309 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
310 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
311 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
312 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi_abcdefghi_"
313 		"abcdefghi_abcdefghi_abcdefghi_abcdefghi" "\xE2" "\x80" "\x94" ".jpg");
314 }
315 
TORRENT_TEST(sanitize_path_trailing_dots)316 TORRENT_TEST(sanitize_path_trailing_dots)
317 {
318 	std::string path;
319 	sanitize_append_path_element(path, "a");
320 	sanitize_append_path_element(path, "abc...");
321 	sanitize_append_path_element(path, "c");
322 #ifdef TORRENT_WINDOWS
323 	TEST_EQUAL(path, "a" SEPARATOR "abc" SEPARATOR "c");
324 #else
325 	TEST_EQUAL(path, "a" SEPARATOR "abc..." SEPARATOR "c");
326 #endif
327 
328 	path.clear();
329 	sanitize_append_path_element(path, "abc...");
330 #ifdef TORRENT_WINDOWS
331 	TEST_EQUAL(path, "abc");
332 #else
333 	TEST_EQUAL(path, "abc...");
334 #endif
335 
336 	path.clear();
337 	sanitize_append_path_element(path, "abc.");
338 #ifdef TORRENT_WINDOWS
339 	TEST_EQUAL(path, "abc");
340 #else
341 	TEST_EQUAL(path, "abc.");
342 #endif
343 
344 
345 	path.clear();
346 	sanitize_append_path_element(path, "a. . .");
347 #ifdef TORRENT_WINDOWS
348 	TEST_EQUAL(path, "a");
349 #else
350 	TEST_EQUAL(path, "a. . .");
351 #endif
352 }
353 
TORRENT_TEST(sanitize_path_trailing_spaces)354 TORRENT_TEST(sanitize_path_trailing_spaces)
355 {
356 	std::string path;
357 	sanitize_append_path_element(path, "a");
358 	sanitize_append_path_element(path, "abc   ");
359 	sanitize_append_path_element(path, "c");
360 #ifdef TORRENT_WINDOWS
361 	TEST_EQUAL(path, "a" SEPARATOR "abc" SEPARATOR "c");
362 #else
363 	TEST_EQUAL(path, "a" SEPARATOR "abc   " SEPARATOR "c");
364 #endif
365 
366 	path.clear();
367 	sanitize_append_path_element(path, "abc   ");
368 #ifdef TORRENT_WINDOWS
369 	TEST_EQUAL(path, "abc");
370 #else
371 	TEST_EQUAL(path, "abc   ");
372 #endif
373 
374 	path.clear();
375 	sanitize_append_path_element(path, "abc ");
376 #ifdef TORRENT_WINDOWS
377 	TEST_EQUAL(path, "abc");
378 #else
379 	TEST_EQUAL(path, "abc ");
380 #endif
381 }
382 
TORRENT_TEST(sanitize_path)383 TORRENT_TEST(sanitize_path)
384 {
385 	std::string path;
386 	sanitize_append_path_element(path, "\0\0\xed\0\x80");
387 	TEST_EQUAL(path, "_");
388 
389 	path.clear();
390 	sanitize_append_path_element(path, "/a/");
391 	sanitize_append_path_element(path, "b");
392 	sanitize_append_path_element(path, "c");
393 	TEST_EQUAL(path, "a" SEPARATOR "b" SEPARATOR "c");
394 
395 	path.clear();
396 	sanitize_append_path_element(path, "a...b");
397 	TEST_EQUAL(path, "a...b");
398 
399 	path.clear();
400 	sanitize_append_path_element(path, "a");
401 	sanitize_append_path_element(path, "..");
402 	sanitize_append_path_element(path, "c");
403 	TEST_EQUAL(path, "a" SEPARATOR "c");
404 
405 	path.clear();
406 	sanitize_append_path_element(path, "a");
407 	sanitize_append_path_element(path, "..");
408 	TEST_EQUAL(path, "a");
409 
410 	path.clear();
411 	sanitize_append_path_element(path, "/..");
412 	sanitize_append_path_element(path, ".");
413 	sanitize_append_path_element(path, "c");
414 	TEST_EQUAL(path, "c");
415 
416 	path.clear();
417 	sanitize_append_path_element(path, "dev:");
418 #ifdef TORRENT_WINDOWS
419 	TEST_EQUAL(path, "dev_");
420 #else
421 	TEST_EQUAL(path, "dev:");
422 #endif
423 
424 	path.clear();
425 	sanitize_append_path_element(path, "c:");
426 	sanitize_append_path_element(path, "b");
427 #ifdef TORRENT_WINDOWS
428 	TEST_EQUAL(path, "c_" SEPARATOR "b");
429 #else
430 	TEST_EQUAL(path, "c:" SEPARATOR "b");
431 #endif
432 
433 	path.clear();
434 	sanitize_append_path_element(path, "c:");
435 	sanitize_append_path_element(path, ".");
436 	sanitize_append_path_element(path, "c");
437 #ifdef TORRENT_WINDOWS
438 	TEST_EQUAL(path, "c_" SEPARATOR "c");
439 #else
440 	TEST_EQUAL(path, "c:" SEPARATOR "c");
441 #endif
442 
443 	path.clear();
444 	sanitize_append_path_element(path, "\\c");
445 	sanitize_append_path_element(path, ".");
446 	sanitize_append_path_element(path, "c");
447 	TEST_EQUAL(path, "c" SEPARATOR "c");
448 
449 	path.clear();
450 	sanitize_append_path_element(path, "\b");
451 	TEST_EQUAL(path, "_");
452 
453 	path.clear();
454 	sanitize_append_path_element(path, "\b");
455 	sanitize_append_path_element(path, "filename");
456 	TEST_EQUAL(path, "_" SEPARATOR "filename");
457 
458 	path.clear();
459 	sanitize_append_path_element(path, "filename");
460 	sanitize_append_path_element(path, "\b");
461 	TEST_EQUAL(path, "filename" SEPARATOR "_");
462 
463 	path.clear();
464 	sanitize_append_path_element(path, "abc");
465 	sanitize_append_path_element(path, "");
466 	TEST_EQUAL(path, "abc" SEPARATOR "_");
467 
468 	path.clear();
469 	sanitize_append_path_element(path, "abc");
470 	sanitize_append_path_element(path, "   ");
471 #ifdef TORRENT_WINDOWS
472 	TEST_EQUAL(path, "abc");
473 #else
474 	TEST_EQUAL(path, "abc" SEPARATOR "   ");
475 #endif
476 
477 	path.clear();
478 	sanitize_append_path_element(path, "");
479 	sanitize_append_path_element(path, "abc");
480 	TEST_EQUAL(path, "_" SEPARATOR "abc");
481 
482 	path.clear();
483 	sanitize_append_path_element(path, "\b?filename=4");
484 #ifdef TORRENT_WINDOWS
485 	TEST_EQUAL(path, "__filename=4");
486 #else
487 	TEST_EQUAL(path, "_?filename=4");
488 #endif
489 
490 	path.clear();
491 	sanitize_append_path_element(path, "filename=4");
492 	TEST_EQUAL(path, "filename=4");
493 
494 	// valid 2-byte sequence
495 	path.clear();
496 	sanitize_append_path_element(path, "filename\xc2\xa1");
497 	TEST_EQUAL(path, "filename\xc2\xa1");
498 
499 	// truncated 2-byte sequence
500 	path.clear();
501 	sanitize_append_path_element(path, "filename\xc2");
502 	TEST_EQUAL(path, "filename_");
503 
504 	// valid 3-byte sequence
505 	path.clear();
506 	sanitize_append_path_element(path, "filename\xe2\x9f\xb9");
507 	TEST_EQUAL(path, "filename\xe2\x9f\xb9");
508 
509 	// truncated 3-byte sequence
510 	path.clear();
511 	sanitize_append_path_element(path, "filename\xe2\x9f");
512 	TEST_EQUAL(path, "filename_");
513 
514 	// truncated 3-byte sequence
515 	path.clear();
516 	sanitize_append_path_element(path, "filename\xe2");
517 	TEST_EQUAL(path, "filename_");
518 
519 	// valid 4-byte sequence
520 	path.clear();
521 	sanitize_append_path_element(path, "filename\xf0\x9f\x92\x88");
522 	TEST_EQUAL(path, "filename\xf0\x9f\x92\x88");
523 
524 	// truncated 4-byte sequence
525 	path.clear();
526 	sanitize_append_path_element(path, "filename\xf0\x9f\x92");
527 	TEST_EQUAL(path, "filename_");
528 
529 	// 5-byte utf-8 sequence (not allowed)
530 	path.clear();
531 	sanitize_append_path_element(path, "filename\xf8\x9f\x9f\x9f\x9f" "foobar");
532 	TEST_EQUAL(path, "filename_foobar");
533 
534 	// redundant (overlong) 2-byte sequence
535 	// ascii code 0x2e encoded with a leading 0
536 	path.clear();
537 	sanitize_append_path_element(path, "filename\xc0\xae");
538 	TEST_EQUAL(path, "filename_");
539 
540 	// redundant (overlong) 3-byte sequence
541 	// ascii code 0x2e encoded with two leading 0s
542 	path.clear();
543 	sanitize_append_path_element(path, "filename\xe0\x80\xae");
544 	TEST_EQUAL(path, "filename_");
545 
546 	// redundant (overlong) 4-byte sequence
547 	// ascii code 0x2e encoded with three leading 0s
548 	path.clear();
549 	sanitize_append_path_element(path, "filename\xf0\x80\x80\xae");
550 	TEST_EQUAL(path, "filename_");
551 
552 	// a filename where every character is filtered is not replaced by an understcore
553 	path.clear();
554 	sanitize_append_path_element(path, "//\\");
555 	TEST_EQUAL(path, "");
556 
557 	// make sure suspicious unicode characters are filtered out
558 	path.clear();
559 	// that's utf-8 for U+200e LEFT-TO-RIGHT MARK
560 	sanitize_append_path_element(path, "foo\xe2\x80\x8e" "bar");
561 	TEST_EQUAL(path, "foobar");
562 
563 	// make sure suspicious unicode characters are filtered out
564 	path.clear();
565 	// that's utf-8 for U+202b RIGHT-TO-LEFT EMBEDDING
566 	sanitize_append_path_element(path, "foo\xe2\x80\xab" "bar");
567 	TEST_EQUAL(path, "foobar");
568 }
569 
TORRENT_TEST(sanitize_path_zeroes)570 TORRENT_TEST(sanitize_path_zeroes)
571 {
572 	std::string path;
573 	sanitize_append_path_element(path, "\0foo");
574 	TEST_EQUAL(path, "_");
575 
576 	path.clear();
577 	sanitize_append_path_element(path, "\0\0\0\0");
578 	TEST_EQUAL(path, "_");
579 }
580 
TORRENT_TEST(sanitize_path_colon)581 TORRENT_TEST(sanitize_path_colon)
582 {
583 	std::string path;
584 	sanitize_append_path_element(path, "foo:bar");
585 #ifdef TORRENT_WINDOWS
586 	TEST_EQUAL(path, "foo_bar");
587 #else
588 	TEST_EQUAL(path, "foo:bar");
589 #endif
590 }
591 
TORRENT_TEST(verify_encoding)592 TORRENT_TEST(verify_encoding)
593 {
594 	// verify_encoding
595 	std::string test = "\b?filename=4";
596 	TEST_CHECK(verify_encoding(test));
597 	TEST_CHECK(test == "\b?filename=4");
598 
599 	test = "filename=4";
600 	TEST_CHECK(verify_encoding(test));
601 	TEST_CHECK(test == "filename=4");
602 
603 	// valid 2-byte sequence
604 	test = "filename\xc2\xa1";
605 	TEST_CHECK(verify_encoding(test));
606 	std::printf("%s\n", test.c_str());
607 	TEST_CHECK(test == "filename\xc2\xa1");
608 
609 	// truncated 2-byte sequence
610 	test = "filename\xc2";
611 	TEST_CHECK(!verify_encoding(test));
612 	std::printf("%s\n", test.c_str());
613 	TEST_CHECK(test == "filename_");
614 
615 	// valid 3-byte sequence
616 	test = "filename\xe2\x9f\xb9";
617 	TEST_CHECK(verify_encoding(test));
618 	std::printf("%s\n", test.c_str());
619 	TEST_CHECK(test == "filename\xe2\x9f\xb9");
620 
621 	// truncated 3-byte sequence
622 	test = "filename\xe2\x9f";
623 	TEST_CHECK(!verify_encoding(test));
624 	std::printf("%s\n", test.c_str());
625 	TEST_CHECK(test == "filename_");
626 
627 	// truncated 3-byte sequence
628 	test = "filename\xe2";
629 	TEST_CHECK(!verify_encoding(test));
630 	std::printf("%s\n", test.c_str());
631 	TEST_CHECK(test == "filename_");
632 
633 	// valid 4-byte sequence
634 	test = "filename\xf0\x9f\x92\x88";
635 	TEST_CHECK(verify_encoding(test));
636 	std::printf("%s\n", test.c_str());
637 	TEST_CHECK(test == "filename\xf0\x9f\x92\x88");
638 
639 	// truncated 4-byte sequence
640 	test = "filename\xf0\x9f\x92";
641 	TEST_CHECK(!verify_encoding(test));
642 	std::printf("%s\n", test.c_str());
643 	TEST_CHECK(test == "filename_");
644 
645 	// 5-byte utf-8 sequence (not allowed)
646 	test = "filename\xf8\x9f\x9f\x9f\x9f""foobar";
647 	TEST_CHECK(!verify_encoding(test));
648 	std::printf("%s\n", test.c_str());
649 	TEST_CHECK(test == "filename_foobar");
650 
651 	// redundant (overlong) 2-byte sequence
652 	// ascii code 0x2e encoded with a leading 0
653 	test = "filename\xc0\xae";
654 	TEST_CHECK(!verify_encoding(test));
655 	std::printf("%s\n", test.c_str());
656 	TEST_CHECK(test == "filename_");
657 
658 	// redundant (overlong) 3-byte sequence
659 	// ascii code 0x2e encoded with two leading 0s
660 	test = "filename\xe0\x80\xae";
661 	TEST_CHECK(!verify_encoding(test));
662 	std::printf("%s\n", test.c_str());
663 	TEST_CHECK(test == "filename_");
664 
665 	// redundant (overlong) 4-byte sequence
666 	// ascii code 0x2e encoded with three leading 0s
667 	test = "filename\xf0\x80\x80\xae";
668 	TEST_CHECK(!verify_encoding(test));
669 	std::printf("%s\n", test.c_str());
670 	TEST_CHECK(test == "filename_");
671 
672 	// missing byte header
673 	test = "filename\xed\0\x80";
674 	TEST_CHECK(!verify_encoding(test));
675 	fprintf(stdout, "%s\n", test.c_str());
676 	TEST_CHECK(test == "filename_");
677 }
678 
TORRENT_TEST(parse_torrents)679 TORRENT_TEST(parse_torrents)
680 {
681 	// test torrent parsing
682 
683 	entry info;
684 	info["pieces"] = "aaaaaaaaaaaaaaaaaaaa";
685 	info["name.utf-8"] = "test1";
686 	info["name"] = "test__";
687 	info["piece length"] = 16 * 1024;
688 	info["length"] = 3245;
689 	entry torrent;
690 	torrent["info"] = info;
691 
692 	std::vector<char> buf;
693 	bencode(std::back_inserter(buf), torrent);
694 	torrent_info ti1(buf, from_span);
695 	std::cout << ti1.name() << std::endl;
696 	TEST_CHECK(ti1.name() == "test1");
697 
698 #ifdef TORRENT_WINDOWS
699 	info["name.utf-8"] = "c:/test1/test2/test3";
700 #else
701 	info["name.utf-8"] = "/test1/test2/test3";
702 #endif
703 	torrent["info"] = info;
704 	buf.clear();
705 	bencode(std::back_inserter(buf), torrent);
706 	torrent_info ti2(buf, from_span);
707 	std::cout << ti2.name() << std::endl;
708 #ifdef TORRENT_WINDOWS
709 	TEST_EQUAL(ti2.name(), "c_test1test2test3");
710 #else
711 	TEST_EQUAL(ti2.name(), "test1test2test3");
712 #endif
713 
714 	info["name.utf-8"] = "test2/../test3/.././../../test4";
715 	torrent["info"] = info;
716 	buf.clear();
717 	bencode(std::back_inserter(buf), torrent);
718 	torrent_info ti3(buf, from_span);
719 	std::cout << ti3.name() << std::endl;
720 	TEST_EQUAL(ti3.name(), "test2..test3.......test4");
721 
722 	std::string root_dir = parent_path(current_working_directory());
723 	for (auto const t : test_torrents)
724 	{
725 		std::printf("loading %s\n", t.file);
726 		std::string filename = combine_path(combine_path(root_dir, "test_torrents")
727 			, t.file);
728 		error_code ec;
729 		auto ti = std::make_shared<torrent_info>(filename, ec);
730 		TEST_CHECK(!ec);
731 		if (ec) std::printf(" loading(\"%s\") -> failed %s\n", filename.c_str()
732 			, ec.message().c_str());
733 
734 		if (t.file == "whitespace_url.torrent"_sv)
735 		{
736 			// make sure we trimmed the url
737 			TEST_CHECK(ti->trackers().size() > 0);
738 			if (ti->trackers().size() > 0)
739 				TEST_CHECK(ti->trackers()[0].url == "udp://test.com/announce");
740 		}
741 		else if (t.file == "duplicate_files.torrent"_sv)
742 		{
743 			// make sure we disambiguated the files
744 			TEST_EQUAL(ti->num_files(), 2);
745 			TEST_CHECK(ti->files().file_path(file_index_t{0}) == combine_path(combine_path("temp", "foo"), "bar.txt"));
746 			TEST_CHECK(ti->files().file_path(file_index_t{1}) == combine_path(combine_path("temp", "foo"), "bar.1.txt"));
747 		}
748 		else if (t.file == "pad_file.torrent"_sv)
749 		{
750 			TEST_EQUAL(ti->num_files(), 2);
751 			TEST_EQUAL(bool(ti->files().file_flags(file_index_t{0}) & file_storage::flag_pad_file), false);
752 			TEST_EQUAL(bool(ti->files().file_flags(file_index_t{1}) & file_storage::flag_pad_file), true);
753 		}
754 		else if (t.file == "creation_date.torrent"_sv)
755 		{
756 			TEST_EQUAL(ti->creation_date(), 1234567);
757 		}
758 		else if (t.file == "duplicate_web_seeds.torrent"_sv)
759 		{
760 			TEST_EQUAL(ti->web_seeds().size(), 3);
761 		}
762 		else if (t.file == "no_creation_date.torrent"_sv)
763 		{
764 			TEST_CHECK(!ti->creation_date());
765 		}
766 		else if (t.file == "url_seed.torrent"_sv)
767 		{
768 			TEST_EQUAL(ti->web_seeds().size(), 1);
769 			TEST_EQUAL(ti->web_seeds()[0].url, "http://test.com/file");
770 #if TORRENT_ABI_VERSION == 1
771 			TEST_EQUAL(ti->http_seeds().size(), 0);
772 			TEST_EQUAL(ti->url_seeds().size(), 1);
773 			TEST_EQUAL(ti->url_seeds()[0], "http://test.com/file");
774 #endif
775 		}
776 		else if (t.file == "url_seed_multi.torrent"_sv)
777 		{
778 			TEST_EQUAL(ti->web_seeds().size(), 1);
779 			TEST_EQUAL(ti->web_seeds()[0].url, "http://test.com/file/");
780 #if TORRENT_ABI_VERSION == 1
781 			TEST_EQUAL(ti->http_seeds().size(), 0);
782 			TEST_EQUAL(ti->url_seeds().size(), 1);
783 			TEST_EQUAL(ti->url_seeds()[0], "http://test.com/file/");
784 #endif
785 		}
786 		else if (t.file == "url_seed_multi_single_file.torrent"_sv)
787 		{
788 			TEST_EQUAL(ti->web_seeds().size(), 1);
789 			TEST_EQUAL(ti->web_seeds()[0].url, "http://test.com/file/temp/foo/bar.txt");
790 		}
791 		else if (t.file == "url_seed_multi_space.torrent"_sv
792 			|| t.file == "url_seed_multi_space_nolist.torrent"_sv)
793 		{
794 			TEST_EQUAL(ti->web_seeds().size(), 1);
795 			TEST_EQUAL(ti->web_seeds()[0].url, "http://test.com/test%20file/foo%20bar/");
796 #if TORRENT_ABI_VERSION == 1
797 			TEST_EQUAL(ti->http_seeds().size(), 0);
798 			TEST_EQUAL(ti->url_seeds().size(), 1);
799 			TEST_EQUAL(ti->url_seeds()[0], "http://test.com/test%20file/foo%20bar/");
800 #endif
801 		}
802 		else if (t.file == "invalid_name2.torrent"_sv)
803 		{
804 			// if, after all invalid characters are removed from the name, it ends up
805 			// being empty, it's set to the info-hash. Some torrents also have an empty name
806 			// in which case it's also set to the info-hash
807 			TEST_EQUAL(ti->name(), "b61560c2918f463768cd122b6d2fdd47b77bdb35");
808 		}
809 		else if (t.file == "invalid_name3.torrent"_sv)
810 		{
811 			// windows does not allow trailing spaces in filenames
812 #ifdef TORRENT_WINDOWS
813 			TEST_EQUAL(ti->name(), "foobar");
814 #else
815 			TEST_EQUAL(ti->name(), "foobar ");
816 #endif
817 		}
818 		else if (t.file == "symlink1.torrent"_sv)
819 		{
820 			TEST_EQUAL(ti->num_files(), 2);
821 			TEST_EQUAL(ti->files().symlink(file_index_t{1}), "temp" SEPARATOR "a" SEPARATOR "b" SEPARATOR "bar");
822 		}
823 		else if (t.file == "symlink2.torrent"_sv)
824 		{
825 			TEST_EQUAL(ti->num_files(), 5);
826 			TEST_EQUAL(ti->files().symlink(file_index_t{0}), "Some.framework" SEPARATOR "Versions" SEPARATOR "A" SEPARATOR "SDL2");
827 			TEST_EQUAL(ti->files().symlink(file_index_t{4}), "Some.framework" SEPARATOR "Versions" SEPARATOR "A");
828 		}
829 		else if (t.file == "slash_path.torrent"_sv)
830 		{
831 			TEST_EQUAL(ti->num_files(), 1);
832 			TEST_EQUAL(ti->files().file_path(file_index_t{0}), "temp" SEPARATOR "bar");
833 		}
834 		else if (t.file == "slash_path2.torrent"_sv)
835 		{
836 			TEST_EQUAL(ti->num_files(), 1);
837 			TEST_EQUAL(ti->files().file_path(file_index_t{0}), "temp" SEPARATOR "abc....def" SEPARATOR "bar");
838 		}
839 		else if (t.file == "slash_path3.torrent"_sv)
840 		{
841 			TEST_EQUAL(ti->num_files(), 1);
842 			TEST_EQUAL(ti->files().file_path(file_index_t{0}), "temp....abc");
843 		}
844 		else if (t.file == "symlink_zero_size.torrent"_sv)
845 		{
846 			TEST_EQUAL(ti->num_files(), 2);
847 			TEST_EQUAL(ti->files().symlink(file_index_t(1)), "temp" SEPARATOR "a" SEPARATOR "b" SEPARATOR "bar");
848 		}
849 		else if (t.file == "pad_file_no_path.torrent"_sv)
850 		{
851 			TEST_EQUAL(ti->num_files(), 2);
852 			TEST_EQUAL(ti->files().file_path(file_index_t{1}), combine_path(".pad", "0"));
853 		}
854 		else if (t.file == "absolute_filename.torrent"_sv)
855 		{
856 			TEST_EQUAL(ti->num_files(), 2);
857 			TEST_EQUAL(ti->files().file_path(file_index_t{0}), combine_path("temp", "abcde"));
858 			TEST_EQUAL(ti->files().file_path(file_index_t{1}), combine_path("temp", "foobar"));
859 		}
860 		else if (t.file == "invalid_filename.torrent"_sv)
861 		{
862 			TEST_EQUAL(ti->num_files(), 2);
863 		}
864 		else if (t.file == "invalid_filename2.torrent"_sv)
865 		{
866 			TEST_EQUAL(ti->num_files(), 3);
867 		}
868 		else if (t.file == "overlapping_symlinks.torrent"_sv)
869 		{
870 			TEST_CHECK(ti->num_files() > 3);
871 			TEST_EQUAL(ti->files().symlink(file_index_t{0}), "SDL2.framework" SEPARATOR "Versions" SEPARATOR "Current" SEPARATOR "Headers");
872 			TEST_EQUAL(ti->files().symlink(file_index_t{1}), "SDL2.framework" SEPARATOR "Versions" SEPARATOR "Current" SEPARATOR "Resources");
873 			TEST_EQUAL(ti->files().symlink(file_index_t{2}), "SDL2.framework" SEPARATOR "Versions" SEPARATOR "Current" SEPARATOR "SDL2");
874 		}
875 
876 		file_storage const& fs = ti->files();
877 		for (file_index_t idx{0}; idx != file_index_t(fs.num_files()); ++idx)
878 		{
879 			piece_index_t const first = ti->map_file(idx, 0, 0).piece;
880 			piece_index_t const last = ti->map_file(idx, std::max(fs.file_size(idx)-1, std::int64_t(0)), 0).piece;
881 			file_flags_t const flags = fs.file_flags(idx);
882 			sha1_hash const ih = fs.hash(idx);
883 			std::printf("  %11" PRId64 " %c%c%c%c [ %4d, %4d ] %7u %s %s %s%s\n"
884 				, fs.file_size(idx)
885 				, (flags & file_storage::flag_pad_file)?'p':'-'
886 				, (flags & file_storage::flag_executable)?'x':'-'
887 				, (flags & file_storage::flag_hidden)?'h':'-'
888 				, (flags & file_storage::flag_symlink)?'l':'-'
889 				, static_cast<int>(first), static_cast<int>(last)
890 				, std::uint32_t(fs.mtime(idx))
891 				, ih != sha1_hash(nullptr) ? aux::to_hex(ih).c_str() : ""
892 				, fs.file_path(idx).c_str()
893 				, flags & file_storage::flag_symlink ? "-> ": ""
894 				, flags & file_storage::flag_symlink ? fs.symlink(idx).c_str() : "");
895 		}
896 	}
897 
898 	for (int i = 0; i < int(sizeof(test_error_torrents)/sizeof(test_error_torrents[0])); ++i)
899 	{
900 		error_code ec;
901 		std::printf("loading %s\n", test_error_torrents[i].file);
902 		auto ti = std::make_shared<torrent_info>(combine_path(
903 			combine_path(root_dir, "test_torrents"), test_error_torrents[i].file), ec);
904 		std::printf("E:        \"%s\"\nexpected: \"%s\"\n", ec.message().c_str()
905 			, test_error_torrents[i].error.message().c_str());
906 		TEST_CHECK(ec.message() == test_error_torrents[i].error.message());
907 		TEST_EQUAL(ti->is_valid(), false);
908 	}
909 }
910 
911 namespace {
912 
913 struct file_t
914 {
915 	std::string filename;
916 	int size;
917 	file_flags_t flags;
918 	string_view expected_filename;
919 };
920 
921 std::vector<lt::aux::vector<file_t, lt::file_index_t>> const test_cases
922 {
923 	{
924 		{"test/temporary.txt", 0x4000, {}, "test/temporary.txt"},
925 		{"test/Temporary.txt", 0x4000, {}, "test/Temporary.1.txt"},
926 		{"test/TeMPorArY.txT", 0x4000, {}, "test/TeMPorArY.2.txT"},
927 		// a file with the same name in a seprate directory is fine
928 		{"test/test/TEMPORARY.TXT", 0x4000, {}, "test/test/TEMPORARY.TXT"},
929 	},
930 	{
931 		{"test/b.exe", 0x4000, {}, "test/b.exe"},
932 		// duplicate of b.exe
933 		{"test/B.ExE", 0x4000, {}, "test/B.1.ExE"},
934 		// duplicate of b.exe
935 		{"test/B.exe", 0x4000, {}, "test/B.2.exe"},
936 		{"test/filler", 0x4000, {}, "test/filler"},
937 	},
938 	{
939 		{"test/a/b/c/d/e/f/g/h/i/j/k/l/m", 0x4000, {}, "test/a/b/c/d/e/f/g/h/i/j/k/l/m"},
940 		{"test/a", 0x4000, {}, "test/a.1"},
941 		{"test/a/b", 0x4000, {}, "test/a/b.1"},
942 		{"test/a/b/c", 0x4000, {}, "test/a/b/c.1"},
943 		{"test/a/b/c/d", 0x4000, {}, "test/a/b/c/d.1"},
944 		{"test/a/b/c/d/e", 0x4000, {}, "test/a/b/c/d/e.1"},
945 		{"test/a/b/c/d/e/f", 0x4000, {}, "test/a/b/c/d/e/f.1"},
946 		{"test/a/b/c/d/e/f/g", 0x4000, {}, "test/a/b/c/d/e/f/g.1"},
947 		{"test/a/b/c/d/e/f/g/h", 0x4000, {}, "test/a/b/c/d/e/f/g/h.1"},
948 		{"test/a/b/c/d/e/f/g/h/i", 0x4000, {}, "test/a/b/c/d/e/f/g/h/i.1"},
949 		{"test/a/b/c/d/e/f/g/h/i/j", 0x4000, {}, "test/a/b/c/d/e/f/g/h/i/j.1"},
950 	},
951 	{
952 		// it doesn't matter whether the file comes before the directory,
953 		// directories take precedence
954 		{"test/a", 0x4000, {}, "test/a.1"},
955 		{"test/a/b", 0x4000, {}, "test/a/b"},
956 	},
957 	{
958 		{"test/A/tmp", 0x4000, {}, "test/A/tmp"},
959 		// a file may not have the same name as a directory
960 		{"test/a", 0x4000, {}, "test/a.1"},
961 		// duplicate of directory a
962 		{"test/A", 0x4000, {}, "test/A.2"},
963 		{"test/filler", 0x4000, {}, "test/filler"},
964 	},
965 	{
966 		// a subset of this path collides with the next filename
967 		{"test/long/path/name/that/collides", 0x4000, {}, "test/long/path/name/that/collides"},
968 		// so this file needs to be renamed, to not collide with the path name
969 		{"test/long/path", 0x4000, {}, "test/long/path.1"},
970 		{"test/filler-1", 0x4000, {}, "test/filler-1"},
971 		{"test/filler-2", 0x4000, {}, "test/filler-2"},
972 	},
973 	{
974 		// pad files are allowed to collide, as long as they have the same size
975 		{"test/.pad/1234", 0x4000, file_storage::flag_pad_file, "test/.pad/1234"},
976 		{"test/filler-1", 0x4000, {}, "test/filler-1"},
977 		{"test/.pad/1234", 0x4000, file_storage::flag_pad_file, "test/.pad/1234"},
978 		{"test/filler-2", 0x4000, {}, "test/filler-2"},
979 	},
980 	{
981 		// pad files of different sizes are NOT allowed to collide
982 		{"test/.pad/1234", 0x8000, file_storage::flag_pad_file, "test/.pad/1234"},
983 		{"test/filler-1", 0x4000, {}, "test/filler-1"},
984 		{"test/.pad/1234", 0x4000, file_storage::flag_pad_file, "test/.pad/1234.1"},
985 		{"test/filler-2", 0x4000, {}, "test/filler-2"},
986 	},
987 	{
988 		// pad files are NOT allowed to collide with normal files
989 		{"test/.pad/1234", 0x4000, {}, "test/.pad/1234"},
990 		{"test/filler-1", 0x4000, {}, "test/filler-1"},
991 		{"test/.pad/1234", 0x4000, file_storage::flag_pad_file, "test/.pad/1234.1"},
992 		{"test/filler-2", 0x4000, {}, "test/filler-2"},
993 	},
994 	{
995 		// normal files are NOT allowed to collide with pad files
996 		{"test/.pad/1234", 0x4000, file_storage::flag_pad_file, "test/.pad/1234"},
997 		{"test/filler-1", 0x4000, {}, "test/filler-1"},
998 		{"test/.pad/1234", 0x4000, {}, "test/.pad/1234.1"},
999 		{"test/filler-2", 0x4000, {}, "test/filler-2"},
1000 	},
1001 	{
1002 		// pad files are NOT allowed to collide with directories
1003 		{"test/.pad/1234", 0x4000, file_storage::flag_pad_file, "test/.pad/1234.1"},
1004 		{"test/filler-1", 0x4000, {}, "test/filler-1"},
1005 		{"test/.pad/1234/filler-2", 0x4000, {}, "test/.pad/1234/filler-2"},
1006 	},
1007 };
1008 
test_resolve_duplicates(aux::vector<file_t,file_index_t> const & test)1009 void test_resolve_duplicates(aux::vector<file_t, file_index_t> const& test)
1010 {
1011 	file_storage fs;
1012 	for (auto const& f : test) fs.add_file(f.filename, f.size, f.flags);
1013 
1014 	lt::create_torrent t(fs, 0x4000);
1015 
1016 	// calculate the hash for all pieces
1017 	sha1_hash ph;
1018 	for (auto const i : fs.piece_range())
1019 		t.set_hash(i, ph);
1020 
1021 	std::vector<char> tmp;
1022 	std::back_insert_iterator<std::vector<char>> out(tmp);
1023 
1024 	entry tor = t.generate();
1025 	bencode(out, tor);
1026 
1027 	torrent_info ti(tmp, from_span);
1028 	for (auto const i : fs.file_range())
1029 	{
1030 		std::string p = ti.files().file_path(i);
1031 		convert_path_to_posix(p);
1032 		std::printf("%s == %s\n", p.c_str(), test[i].expected_filename.to_string().c_str());
1033 
1034 		TEST_EQUAL(p, test[i].expected_filename);
1035 	}
1036 }
1037 
1038 } // anonymous namespace
1039 
TORRENT_TEST(resolve_duplicates)1040 TORRENT_TEST(resolve_duplicates)
1041 {
1042 	for (auto const& t : test_cases)
1043 		test_resolve_duplicates(t);
1044 }
1045 
TORRENT_TEST(empty_file)1046 TORRENT_TEST(empty_file)
1047 {
1048 	error_code ec;
1049 	auto ti = std::make_shared<torrent_info>("", ec, from_span);
1050 	TEST_CHECK(ec);
1051 }
1052 
TORRENT_TEST(empty_file2)1053 TORRENT_TEST(empty_file2)
1054 {
1055 	try
1056 	{
1057 		auto ti = std::make_shared<torrent_info>("", from_span);
1058 		TEST_ERROR("expected exception thrown");
1059 	}
1060 	catch (system_error const& e)
1061 	{
1062 		std::printf("Expected error: %s\n", e.code().message().c_str());
1063 	}
1064 }
1065 
TORRENT_TEST(copy)1066 TORRENT_TEST(copy)
1067 {
1068 	using namespace lt;
1069 
1070 	std::shared_ptr<torrent_info> a = std::make_shared<torrent_info>(
1071 		combine_path(parent_path(current_working_directory())
1072 		, combine_path("test_torrents", "sample.torrent")));
1073 
1074 	aux::vector<char const*, file_index_t> expected_files =
1075 	{
1076 		"sample/text_file2.txt",
1077 		"sample/.____padding_file/0",
1078 		"sample/text_file.txt",
1079 	};
1080 
1081 	aux::vector<sha1_hash, file_index_t> file_hashes =
1082 	{
1083 		sha1_hash(nullptr),
1084 		sha1_hash(nullptr),
1085 		sha1_hash("abababababababababab")
1086 	};
1087 
1088 	file_storage const& fs = a->files();
1089 	for (auto const i : fs.file_range())
1090 	{
1091 		std::string p = fs.file_path(i);
1092 		convert_path_to_posix(p);
1093 		TEST_EQUAL(p, expected_files[i]);
1094 		std::printf("%s\n", p.c_str());
1095 
1096 		TEST_EQUAL(a->files().hash(i), file_hashes[i]);
1097 	}
1098 
1099 	// copy the torrent_info object
1100 	std::shared_ptr<torrent_info> b = std::make_shared<torrent_info>(*a);
1101 
1102 	a.reset();
1103 
1104 	TEST_EQUAL(b->num_files(), 3);
1105 
1106 	file_storage const& fs2 = b->files();
1107 	for (auto const i : fs2.file_range())
1108 	{
1109 		std::string p = fs2.file_path(i);
1110 		convert_path_to_posix(p);
1111 		TEST_EQUAL(p, expected_files[i]);
1112 		std::printf("%s\n", p.c_str());
1113 
1114 		TEST_EQUAL(fs2.hash(i), file_hashes[i]);
1115 	}
1116 }
1117 
1118 struct A
1119 {
1120 	int val;
1121 };
1122 
TORRENT_TEST(copy_ptr)1123 TORRENT_TEST(copy_ptr)
1124 {
1125 	copy_ptr<A> a(new A{4});
1126 	copy_ptr<A> b(a);
1127 
1128 	TEST_EQUAL(a->val, b->val);
1129 	TEST_CHECK(&*a != &*b);
1130 	a->val = 5;
1131 	TEST_EQUAL(b->val, 4);
1132 }
1133