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 "libtorrent/file.hpp"
34 #include "libtorrent/aux_/path.hpp"
35 #include "libtorrent/aux_/numeric_cast.hpp"
36 #include "libtorrent/string_view.hpp"
37 #include "test.hpp"
38 #include <vector>
39 #include <set>
40 #include <thread>
41 #include <iostream>
42
43 using namespace lt;
44
45 namespace {
46
touch_file(std::string const & filename,int size)47 int touch_file(std::string const& filename, int size)
48 {
49 using namespace lt;
50
51 std::vector<char> v;
52 v.resize(aux::numeric_cast<std::size_t>(size));
53 for (int i = 0; i < size; ++i)
54 v[std::size_t(i)] = char(i & 255);
55
56 file f;
57 error_code ec;
58 if (!f.open(filename, open_mode::write_only, ec)) return -1;
59 TEST_EQUAL(ec, error_code());
60 if (ec) return -1;
61 iovec_t b = {v};
62 std::int64_t written = f.writev(0, b, ec);
63 if (written != int(v.size())) return -3;
64 if (ec) return -3;
65 TEST_EQUAL(ec, error_code());
66 return 0;
67 }
68
69 } // anonymous namespace
70
TORRENT_TEST(create_directory)71 TORRENT_TEST(create_directory)
72 {
73 error_code ec;
74 create_directory("__foobar__", ec);
75 if (ec) std::printf("ERROR: create_directory: (%d) %s\n"
76 , ec.value(), ec.message().c_str());
77 TEST_CHECK(!ec);
78
79 file_status st;
80 stat_file("__foobar__", &st, ec);
81 if (ec) std::printf("ERROR: stat_file: (%d) %s\n"
82 , ec.value(), ec.message().c_str());
83 TEST_CHECK(!ec);
84
85 TEST_CHECK(st.mode & file_status::directory);
86
87 remove("__foobar__", ec);
88 if (ec) std::printf("ERROR: remove: (%d) %s\n"
89 , ec.value(), ec.message().c_str());
90 TEST_CHECK(!ec);
91 }
92
TORRENT_TEST(file_status)93 TORRENT_TEST(file_status)
94 {
95 error_code ec;
96
97 // test that the modification timestamps
98 touch_file("__test_timestamp__", 10);
99
100 file_status st1;
101 stat_file("__test_timestamp__", &st1, ec);
102 TEST_CHECK(!ec);
103
104 // sleep for 3 seconds and then make sure the difference in timestamp is
105 // between 2-4 seconds after touching it again
106 std::this_thread::sleep_for(lt::milliseconds(3000));
107
108 touch_file("__test_timestamp__", 10);
109
110 file_status st2;
111 stat_file("__test_timestamp__", &st2, ec);
112 TEST_CHECK(!ec);
113
114 int diff = int(st2.mtime - st1.mtime);
115 std::printf("timestamp difference: %d seconds. expected approx. 3 seconds\n"
116 , diff);
117
118 TEST_CHECK(diff >= 2 && diff <= 4);
119 }
120
TORRENT_TEST(directory)121 TORRENT_TEST(directory)
122 {
123 error_code ec;
124
125 create_directory("file_test_dir", ec);
126 if (ec) std::printf("create_directory: %s\n", ec.message().c_str());
127 TEST_CHECK(!ec);
128
129 std::string cwd = current_working_directory();
130
131 touch_file(combine_path("file_test_dir", "abc"), 10);
132 touch_file(combine_path("file_test_dir", "def"), 100);
133 touch_file(combine_path("file_test_dir", "ghi"), 1000);
134
135 std::set<std::string> files;
136 for (directory i("file_test_dir", ec); !i.done(); i.next(ec))
137 {
138 std::string f = i.file();
139 TEST_CHECK(files.count(f) == 0);
140 files.insert(f);
141 std::printf(" %s\n", f.c_str());
142 }
143
144 TEST_CHECK(files.count("abc") == 1);
145 TEST_CHECK(files.count("def") == 1);
146 TEST_CHECK(files.count("ghi") == 1);
147 TEST_CHECK(files.count("..") == 1);
148 TEST_CHECK(files.count(".") == 1);
149 files.clear();
150
151 recursive_copy("file_test_dir", "file_test_dir2", ec);
152
153 for (directory i("file_test_dir2", ec); !i.done(); i.next(ec))
154 {
155 std::string f = i.file();
156 TEST_CHECK(files.count(f) == 0);
157 files.insert(f);
158 std::printf(" %s\n", f.c_str());
159 }
160
161 remove_all("file_test_dir", ec);
162 if (ec) std::printf("remove_all: %s\n", ec.message().c_str());
163 remove_all("file_test_dir2", ec);
164 if (ec) std::printf("remove_all: %s\n", ec.message().c_str());
165 }
166
167 // test path functions
TORRENT_TEST(paths)168 TORRENT_TEST(paths)
169 {
170 TEST_EQUAL(combine_path("test1/", "test2"), "test1/test2");
171 TEST_EQUAL(combine_path("test1", "."), "test1");
172 TEST_EQUAL(combine_path(".", "test1"), "test1");
173 #ifdef TORRENT_WINDOWS
174 TEST_EQUAL(combine_path("test1\\", "test2"), "test1\\test2");
175 TEST_EQUAL(combine_path("test1", "test2"), "test1\\test2");
176 #else
177 TEST_EQUAL(combine_path("test1", "test2"), "test1/test2");
178 #endif
179
180 TEST_EQUAL(extension("blah"), "");
181 TEST_EQUAL(extension("blah.exe"), ".exe");
182 TEST_EQUAL(extension("blah.foo.bar"), ".bar");
183 TEST_EQUAL(extension("blah.foo."), ".");
184 TEST_EQUAL(extension("blah.foo/bar"), "");
185
186 TEST_EQUAL(remove_extension("blah"), "blah");
187 TEST_EQUAL(remove_extension("blah.exe"), "blah");
188 TEST_EQUAL(remove_extension("blah.foo.bar"), "blah.foo");
189 TEST_EQUAL(remove_extension("blah.foo."), "blah.foo");
190
191 #ifdef TORRENT_WINDOWS
192 TEST_EQUAL(is_root_path("c:\\blah"), false);
193 TEST_EQUAL(is_root_path("c:\\"), true);
194 TEST_EQUAL(is_root_path("\\\\"), true);
195 TEST_EQUAL(is_root_path("\\\\foobar"), true);
196 TEST_EQUAL(is_root_path("\\\\foobar\\"), true);
197 TEST_EQUAL(is_root_path("\\\\foobar/"), true);
198 TEST_EQUAL(is_root_path("\\\\foo/bar"), false);
199 TEST_EQUAL(is_root_path("\\\\foo\\bar\\"), false);
200 #else
201 TEST_EQUAL(is_root_path("/blah"), false);
202 TEST_EQUAL(is_root_path("/"), true);
203 #endif
204
205 #ifdef TORRENT_WINDOWS
206 TEST_CHECK(compare_path("c:\\blah\\", "c:\\blah"));
207 TEST_CHECK(compare_path("c:\\blah", "c:\\blah"));
208 TEST_CHECK(compare_path("c:\\blah/", "c:\\blah"));
209 TEST_CHECK(compare_path("c:\\blah", "c:\\blah\\"));
210 TEST_CHECK(compare_path("c:\\blah", "c:\\blah"));
211 TEST_CHECK(compare_path("c:\\blah", "c:\\blah/"));
212
213 TEST_CHECK(!compare_path("c:\\bla", "c:\\blah/"));
214 TEST_CHECK(!compare_path("c:\\bla", "c:\\blah"));
215 TEST_CHECK(!compare_path("c:\\blah", "c:\\bla"));
216 TEST_CHECK(!compare_path("c:\\blah\\sdf", "c:\\blah"));
217 #else
218 TEST_CHECK(compare_path("/blah", "/blah"));
219 TEST_CHECK(compare_path("/blah/", "/blah"));
220 TEST_CHECK(compare_path("/blah", "/blah"));
221 TEST_CHECK(compare_path("/blah", "/blah/"));
222
223 TEST_CHECK(!compare_path("/bla", "/blah/"));
224 TEST_CHECK(!compare_path("/bla", "/blah"));
225 TEST_CHECK(!compare_path("/blah", "/bla"));
226 TEST_CHECK(!compare_path("/blah/sdf", "/blah"));
227 #endif
228
229 // if has_parent_path() returns false
230 // parent_path() should return the empty string
231 TEST_EQUAL(parent_path("blah"), "");
232 TEST_EQUAL(has_parent_path("blah"), false);
233 TEST_EQUAL(parent_path("/blah/foo/bar"), "/blah/foo/");
234 TEST_EQUAL(has_parent_path("/blah/foo/bar"), true);
235 TEST_EQUAL(parent_path("/blah/foo/bar/"), "/blah/foo/");
236 TEST_EQUAL(has_parent_path("/blah/foo/bar/"), true);
237 TEST_EQUAL(parent_path("/a"), "/");
238 TEST_EQUAL(has_parent_path("/a"), true);
239 TEST_EQUAL(parent_path("/"), "");
240 TEST_EQUAL(has_parent_path("/"), false);
241 TEST_EQUAL(parent_path(""), "");
242 TEST_EQUAL(has_parent_path(""), false);
243 #ifdef TORRENT_WINDOWS
244 TEST_EQUAL(parent_path("\\\\"), "");
245 TEST_EQUAL(has_parent_path("\\\\"), false);
246 TEST_EQUAL(parent_path("c:\\"), "");
247 TEST_EQUAL(has_parent_path("c:\\"), false);
248 TEST_EQUAL(parent_path("c:\\a"), "c:\\");
249 TEST_EQUAL(has_parent_path("c:\\a"), true);
250 TEST_EQUAL(has_parent_path("\\\\a"), false);
251 TEST_EQUAL(has_parent_path("\\\\foobar/"), false);
252 TEST_EQUAL(has_parent_path("\\\\foobar\\"), false);
253 TEST_EQUAL(has_parent_path("\\\\foo/bar\\"), true);
254 #endif
255
256 #ifdef TORRENT_WINDOWS
257 TEST_EQUAL(is_complete("c:\\"), true);
258 TEST_EQUAL(is_complete("c:\\foo\\bar"), true);
259 TEST_EQUAL(is_complete("\\\\foo\\bar"), true);
260 TEST_EQUAL(is_complete("foo/bar"), false);
261 TEST_EQUAL(is_complete("\\\\"), true);
262 #else
263 TEST_EQUAL(is_complete("/foo/bar"), true);
264 TEST_EQUAL(is_complete("foo/bar"), false);
265 TEST_EQUAL(is_complete("/"), true);
266 TEST_EQUAL(is_complete(""), false);
267 #endif
268
269 TEST_EQUAL(complete("."), current_working_directory());
270
271 #ifdef TORRENT_WINDOWS
272 TEST_EQUAL(complete(".\\foobar"), current_working_directory() + "\\foobar");
273 #else
274 TEST_EQUAL(complete("./foobar"), current_working_directory() + "/foobar");
275 #endif
276 }
277
TORRENT_TEST(filename)278 TORRENT_TEST(filename)
279 {
280 #ifdef TORRENT_WINDOWS
281 TEST_EQUAL(filename("blah"), "blah");
282 TEST_EQUAL(filename("\\blah\\foo\\bar"), "bar");
283 TEST_EQUAL(filename("\\blah\\foo\\bar\\"), "bar");
284 TEST_EQUAL(filename("blah\\"), "blah");
285 #endif
286 TEST_EQUAL(filename("blah"), "blah");
287 TEST_EQUAL(filename("/blah/foo/bar"), "bar");
288 TEST_EQUAL(filename("/blah/foo/bar/"), "bar");
289 TEST_EQUAL(filename("blah/"), "blah");
290 }
291
TORRENT_TEST(split_path)292 TORRENT_TEST(split_path)
293 {
294 using r = std::pair<string_view, string_view>;
295
296 #ifdef TORRENT_WINDOWS
297 TEST_CHECK(lsplit_path("\\b\\c\\d") == r("b", "c\\d"));
298 TEST_CHECK(lsplit_path("a\\b\\c\\d") == r("a", "b\\c\\d"));
299 TEST_CHECK(lsplit_path("a") == r("a", ""));
300 TEST_CHECK(lsplit_path("") == r("", ""));
301
302 TEST_CHECK(lsplit_path("a\\b/c\\d") == r("a", "b/c\\d"));
303 TEST_CHECK(lsplit_path("a/b\\c\\d") == r("a", "b\\c\\d"));
304
305 TEST_CHECK(rsplit_path("a\\b\\c\\d\\") == r("a\\b\\c", "d"));
306 TEST_CHECK(rsplit_path("\\a\\b\\c\\d") == r("\\a\\b\\c", "d"));
307 TEST_CHECK(rsplit_path("\\a") == r("", "a"));
308 TEST_CHECK(rsplit_path("a") == r("", "a"));
309 TEST_CHECK(rsplit_path("") == r("", ""));
310
311 TEST_CHECK(rsplit_path("a\\b/c\\d\\") == r("a\\b/c", "d"));
312 TEST_CHECK(rsplit_path("a\\b\\c/d\\") == r("a\\b\\c", "d"));
313 #endif
314 TEST_CHECK(lsplit_path("/b/c/d") == r("b", "c/d"));
315 TEST_CHECK(lsplit_path("a/b/c/d") == r("a", "b/c/d"));
316 TEST_CHECK(lsplit_path("a") == r("a", ""));
317 TEST_CHECK(lsplit_path("") == r("", ""));
318
319 TEST_CHECK(rsplit_path("a/b/c/d/") == r("a/b/c", "d"));
320 TEST_CHECK(rsplit_path("/a/b/c/d") == r("/a/b/c", "d"));
321 TEST_CHECK(rsplit_path("/a") == r("", "a"));
322 TEST_CHECK(rsplit_path("a") == r("", "a"));
323 TEST_CHECK(rsplit_path("") == r("", ""));
324 }
325
TORRENT_TEST(split_path_pos)326 TORRENT_TEST(split_path_pos)
327 {
328 using r = std::pair<string_view, string_view>;
329
330 #ifdef TORRENT_WINDOWS
331 TEST_CHECK(lsplit_path("\\b\\c\\d", 0) == r("b", "c\\d"));
332 TEST_CHECK(lsplit_path("\\b\\c\\d", 1) == r("b", "c\\d"));
333 TEST_CHECK(lsplit_path("\\b\\c\\d", 2) == r("b", "c\\d"));
334 TEST_CHECK(lsplit_path("\\b\\c\\d", 3) == r("b\\c", "d"));
335 TEST_CHECK(lsplit_path("\\b\\c\\d", 4) == r("b\\c", "d"));
336 TEST_CHECK(lsplit_path("\\b\\c\\d", 5) == r("b\\c\\d", ""));
337 TEST_CHECK(lsplit_path("\\b\\c\\d", 6) == r("b\\c\\d", ""));
338
339 TEST_CHECK(lsplit_path("b\\c\\d", 0) == r("b", "c\\d"));
340 TEST_CHECK(lsplit_path("b\\c\\d", 1) == r("b", "c\\d"));
341 TEST_CHECK(lsplit_path("b\\c\\d", 2) == r("b\\c", "d"));
342 TEST_CHECK(lsplit_path("b\\c\\d", 3) == r("b\\c", "d"));
343 TEST_CHECK(lsplit_path("b\\c\\d", 4) == r("b\\c\\d", ""));
344 TEST_CHECK(lsplit_path("b\\c\\d", 5) == r("b\\c\\d", ""));
345 #endif
346 TEST_CHECK(lsplit_path("/b/c/d", 0) == r("b", "c/d"));
347 TEST_CHECK(lsplit_path("/b/c/d", 1) == r("b", "c/d"));
348 TEST_CHECK(lsplit_path("/b/c/d", 2) == r("b", "c/d"));
349 TEST_CHECK(lsplit_path("/b/c/d", 3) == r("b/c", "d"));
350 TEST_CHECK(lsplit_path("/b/c/d", 4) == r("b/c", "d"));
351 TEST_CHECK(lsplit_path("/b/c/d", 5) == r("b/c/d", ""));
352 TEST_CHECK(lsplit_path("/b/c/d", 6) == r("b/c/d", ""));
353
354 TEST_CHECK(lsplit_path("b/c/d", 0) == r("b", "c/d"));
355 TEST_CHECK(lsplit_path("b/c/d", 1) == r("b", "c/d"));
356 TEST_CHECK(lsplit_path("b/c/d", 2) == r("b/c", "d"));
357 TEST_CHECK(lsplit_path("b/c/d", 3) == r("b/c", "d"));
358 TEST_CHECK(lsplit_path("b/c/d", 4) == r("b/c/d", ""));
359 TEST_CHECK(lsplit_path("b/c/d", 5) == r("b/c/d", ""));
360 }
361
362 // file class
TORRENT_TEST(file)363 TORRENT_TEST(file)
364 {
365 error_code ec;
366 file f;
367 std::string test_file_name = "test_file";
368 TEST_CHECK(f.open(test_file_name, open_mode::read_write, ec));
369 if (ec)
370 std::printf("open failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
371 TEST_EQUAL(ec, error_code());
372 if (ec) std::printf("%s\n", ec.message().c_str());
373 char test[] = "test";
374 int const test_word_size = int(sizeof(test)) - 1;
375 iovec_t b = {test, test_word_size};
376 TEST_EQUAL(f.writev(0, b, ec), test_word_size);
377 if (ec)
378 std::printf("writev failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
379 TEST_CHECK(!ec);
380 char test_buf[test_word_size + 1] = {0};
381 b = { test_buf, test_word_size };
382 TEST_EQUAL(f.readv(0, b, ec), test_word_size);
383 if (ec)
384 std::printf("readv failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
385 TEST_EQUAL(ec, error_code());
386 TEST_CHECK(test_buf == "test"_sv);
387 f.close();
388
389 TEST_CHECK(f.open(test_file_name, open_mode::read_only, ec));
390 if (ec)
391 std::printf("open failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
392 TEST_EQUAL(ec, error_code());
393 if (ec) std::printf("%s\n", ec.message().c_str());
394
395 char test_buf2[test_word_size + 1] = {0};
396 std::memset(test_buf, 0, sizeof(test_buf));
397 iovec_t two_buffers[2] {
398 {test_buf, test_word_size},
399 {test_buf2, test_word_size}
400 };
401
402 TEST_EQUAL(f.readv(0, two_buffers, ec), test_word_size);
403 if (ec)
404 std::printf("readv failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
405 TEST_EQUAL(ec, error_code());
406 TEST_CHECK(test_buf == "test"_sv);
407 f.close();
408 }
409
TORRENT_TEST(hard_link)410 TORRENT_TEST(hard_link)
411 {
412 // try to create a hard link to see what happens
413 // first create a regular file to then add another link to.
414
415 // create a file, write some stuff to it, create a hard link to that file,
416 // read that file and assert we get the same stuff we wrote to the first file
417 error_code ec;
418 file f;
419 TEST_CHECK(f.open("original_file", open_mode::read_write, ec));
420 if (ec)
421 std::printf("open failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
422 TEST_EQUAL(ec, error_code());
423
424 char str[] = "abcdefghijklmnopqrstuvwxyz";
425 iovec_t b = { str, 26 };
426 TEST_EQUAL(f.writev(0, b, ec), 26);
427 if (ec)
428 std::printf("writev failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
429 TEST_EQUAL(ec, error_code());
430 f.close();
431
432 hard_link("original_file", "second_link", ec);
433
434 if (ec)
435 std::printf("hard_link failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
436 TEST_EQUAL(ec, error_code());
437
438
439 TEST_CHECK(f.open("second_link", open_mode::read_write, ec));
440 if (ec)
441 std::printf("open failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
442 TEST_EQUAL(ec, error_code());
443
444 char test_buf[27] = {};
445 b = { test_buf, 27 };
446 TEST_EQUAL(f.readv(0, b, ec), 26);
447 if (ec)
448 std::printf("readv failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
449 TEST_EQUAL(ec, error_code());
450 TEST_CHECK(test_buf == "abcdefghijklmnopqrstuvwxyz"_sv);
451 f.close();
452
453 remove("original_file", ec);
454 if (ec)
455 std::printf("remove failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
456
457 remove("second_link", ec);
458 if (ec)
459 std::printf("remove failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
460 }
461
TORRENT_TEST(coalesce_buffer)462 TORRENT_TEST(coalesce_buffer)
463 {
464 error_code ec;
465 file f;
466 TEST_CHECK(f.open("test_file", open_mode::read_write, ec));
467 if (ec)
468 std::printf("open failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
469 TEST_EQUAL(ec, error_code());
470 if (ec) std::printf("%s\n", ec.message().c_str());
471 char test[] = "test";
472 char foobar[] = "foobar";
473 iovec_t b[2] = {{test, 4}, {foobar, 6}};
474 TEST_EQUAL(f.writev(0, b, ec, open_mode::coalesce_buffers), 4 + 6);
475 if (ec)
476 std::printf("writev failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
477 TEST_CHECK(!ec);
478 char test_buf1[5] = {0};
479 char test_buf2[7] = {0};
480 b[0] = { test_buf1, 4 };
481 b[1] = { test_buf2, 6 };
482 TEST_EQUAL(f.readv(0, b, ec), 4 + 6);
483 if (ec)
484 {
485 std::printf("readv failed: [%s] %s\n"
486 , ec.category().name(), ec.message().c_str());
487 }
488 TEST_EQUAL(ec, error_code());
489 TEST_CHECK(test_buf1 == "test"_sv);
490 TEST_CHECK(test_buf2 == "foobar"_sv);
491 f.close();
492 }
493
TORRENT_TEST(stat_file)494 TORRENT_TEST(stat_file)
495 {
496 file_status st;
497 error_code ec;
498 stat_file("no_such_file_or_directory.file", &st, ec);
499 TEST_CHECK(ec);
500 TEST_EQUAL(ec, boost::system::errc::no_such_file_or_directory);
501 }
502
TORRENT_TEST(relative_path)503 TORRENT_TEST(relative_path)
504 {
505 #ifdef TORRENT_WINDOWS
506 #define S "\\"
507 #else
508 #define S "/"
509 #endif
510 TEST_EQUAL(lexically_relative("A" S "B" S "C", "A" S "C" S "B")
511 , ".." S ".." S "C" S "B");
512
513 TEST_EQUAL(lexically_relative("A" S "B" S "C" S, "A" S "C" S "B")
514 , ".." S ".." S "C" S "B");
515
516 TEST_EQUAL(lexically_relative("A" S "B" S "C" S, "A" S "C" S "B" S)
517 , ".." S ".." S "C" S "B");
518
519 TEST_EQUAL(lexically_relative("A" S "B" S "C", "A" S "B" S "B")
520 , ".." S "B");
521
522 TEST_EQUAL(lexically_relative("A" S "B" S "C", "A" S "B" S "C")
523 , "");
524
525 TEST_EQUAL(lexically_relative("A" S "B", "A" S "B")
526 , "");
527
528 TEST_EQUAL(lexically_relative("A" S "B", "A" S "B" S "C")
529 , "C");
530
531 TEST_EQUAL(lexically_relative("A" S, "A" S)
532 , "");
533
534 TEST_EQUAL(lexically_relative("", "A" S "B" S "C")
535 , "A" S "B" S "C");
536
537 TEST_EQUAL(lexically_relative("A" S "B" S "C", "")
538 , ".." S ".." S ".." S);
539
540 TEST_EQUAL(lexically_relative("", ""), "");
541 }
542
543 // UNC tests
544 #if TORRENT_USE_UNC_PATHS
545
546 namespace {
current_directory_caps()547 std::tuple<int, bool> current_directory_caps()
548 {
549 #ifdef TORRENT_WINDOWS
550 DWORD maximum_component_length = FILENAME_MAX;
551 DWORD file_system_flags = 0;
552 if (GetVolumeInformation(nullptr
553 , nullptr, 0, nullptr
554 , &maximum_component_length, &file_system_flags, nullptr, 0) == 0)
555 {
556 error_code ec(GetLastError(), system_category());
557 std::printf("GetVolumeInformation: [%s : %d] %s\n"
558 , ec.category().name(), ec.value(), ec.message().c_str());
559 // ignore errors, this is best-effort
560 }
561 bool const support_hard_links = ((file_system_flags & FILE_SUPPORTS_HARD_LINKS) != 0);
562 return std::make_tuple(int(maximum_component_length), support_hard_links);
563 #else
564 return std::make_tuple(255, true);
565 #endif
566 }
567 } // anonymous namespace
568
TORRENT_TEST(unc_tests)569 TORRENT_TEST(unc_tests)
570 {
571 using lt::canonicalize_path;
572 TEST_EQUAL(canonicalize_path("c:\\a\\..\\b"), "c:\\b");
573 TEST_EQUAL(canonicalize_path("a\\..\\b"), "b");
574 TEST_EQUAL(canonicalize_path("a\\..\\.\\b"), "b");
575 TEST_EQUAL(canonicalize_path("\\.\\a"), "\\a");
576 TEST_EQUAL(canonicalize_path("\\\\bla\\.\\a"), "\\\\bla\\a");
577 TEST_EQUAL(canonicalize_path("c:\\bla\\a"), "c:\\bla\\a");
578
579 error_code ec;
580
581 std::vector<std::string> special_names
582 {
583 "CON", "PRN", "AUX", "NUL",
584 "COM1", "COM2", "COM3", "COM4",
585 "COM5", "COM6", "COM7", "COM8",
586 "COM9", "LPT1", "LPT2", "LPT3",
587 "LPT4", "LPT5", "LPT6", "LPT7",
588 "LPT8", "LPT9"
589 };
590
591 for (std::string special_name : special_names)
592 {
593 touch_file(special_name, 10);
594 TEST_CHECK(lt::exists(special_name));
595 lt::remove(special_name, ec);
596 TEST_EQUAL(ec, error_code());
597 TEST_CHECK(!lt::exists(special_name));
598 }
599
600 int maximum_component_length;
601 bool support_hard_links;
602 std::tie(maximum_component_length, support_hard_links) = current_directory_caps();
603
604 std::cout << "max file path component length: " << maximum_component_length << "\n"
605 << "support hard links: " << (support_hard_links?"yes":"no") << "\n";
606
607 std::string long_dir_name;
608 maximum_component_length -= 12;
609 long_dir_name.resize(size_t(maximum_component_length));
610 for (int i = 0; i < maximum_component_length; ++i)
611 long_dir_name[i] = static_cast<char>((i % 26) + 'A');
612
613 std::string long_file_name1 = combine_path(long_dir_name, long_dir_name);
614 long_file_name1.back() = '1';
615 std::string long_file_name2 = long_file_name1;
616 long_file_name2.back() = '2';
617
618 lt::create_directory(long_dir_name, ec);
619 TEST_EQUAL(ec, error_code());
620 if (ec)
621 {
622 std::cout << "create_directory \"" << long_dir_name << "\" failed: " << ec.message() << "\n";
623 std::wcout << convert_to_native_path_string(long_dir_name) << L"\n";
624 }
625 TEST_CHECK(lt::exists(long_dir_name));
626 TEST_CHECK(lt::is_directory(long_dir_name, ec));
627 TEST_EQUAL(ec, error_code());
628 if (ec)
629 {
630 std::cout << "is_directory \"" << long_dir_name << "\" failed " << ec.message() << "\n";
631 std::wcout << convert_to_native_path_string(long_dir_name) << L"\n";
632 }
633
634 touch_file(long_file_name1, 10);
635 TEST_CHECK(lt::exists(long_file_name1));
636
637 lt::rename(long_file_name1, long_file_name2, ec);
638 TEST_EQUAL(ec, error_code());
639 if (ec)
640 {
641 std::cout << "rename \"" << long_file_name1 << "\" failed " << ec.message() << "\n";
642 std::wcout << convert_to_native_path_string(long_file_name1) << L"\n";
643 }
644 TEST_CHECK(!lt::exists(long_file_name1));
645 TEST_CHECK(lt::exists(long_file_name2));
646
647 lt::copy_file(long_file_name2, long_file_name1, ec);
648 TEST_EQUAL(ec, error_code());
649 if (ec)
650 {
651 std::cout << "copy_file \"" << long_file_name2 << "\" failed " << ec.message() << "\n";
652 std::wcout << convert_to_native_path_string(long_file_name2) << L"\n";
653 }
654 TEST_CHECK(lt::exists(long_file_name1));
655
656 std::set<std::string> files;
657
658 for (lt::directory i(long_dir_name, ec); !i.done(); i.next(ec))
659 {
660 std::string f = i.file();
661 files.insert(f);
662 }
663
664 TEST_EQUAL(files.size(), 4);
665
666 lt::remove(long_file_name1, ec);
667 TEST_EQUAL(ec, error_code());
668 if (ec)
669 {
670 std::cout << "remove \"" << long_file_name1 << "\" failed " << ec.message() << "\n";
671 std::wcout << convert_to_native_path_string(long_file_name1) << L"\n";
672 }
673 TEST_CHECK(!lt::exists(long_file_name1));
674
675 if (support_hard_links)
676 {
677 lt::hard_link(long_file_name2, long_file_name1, ec);
678 TEST_EQUAL(ec, error_code());
679 TEST_CHECK(lt::exists(long_file_name1));
680
681 lt::remove(long_file_name1, ec);
682 TEST_EQUAL(ec, error_code());
683 TEST_CHECK(!lt::exists(long_file_name1));
684 }
685 }
686
TORRENT_TEST(unc_paths)687 TORRENT_TEST(unc_paths)
688 {
689 std::string const reserved_name = "con";
690 error_code ec;
691 {
692 file f;
693 TEST_CHECK(f.open(reserved_name, open_mode::read_write, ec) && !ec);
694 }
695 remove(reserved_name, ec);
696 TEST_CHECK(!ec);
697 }
698
699 #endif
700