1 //---------------------------------------------------------------------------------------
2 //
3 // Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 //---------------------------------------------------------------------------------------
24 #include <algorithm>
25 #include <cstdio>
26 #include <cstring>
27 #include <fstream>
28 #include <functional>
29 #include <iomanip>
30 #include <iostream>
31 #include <map>
32 #include <random>
33 #include <set>
34 #include <sstream>
35 #include <thread>
36
37 #if (defined(WIN32) || defined(_WIN32)) && !defined(__GNUC__)
38 #define NOMINMAX 1
39 #endif
40
41 #ifdef USE_STD_FS
42 #include <filesystem>
43 namespace fs {
44 using namespace std::filesystem;
45 using ifstream = std::ifstream;
46 using ofstream = std::ofstream;
47 using fstream = std::fstream;
48 } // namespace fs
49 #ifdef __GNUC__
50 #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
51 #endif
52 #ifdef _MSC_VER
53 #define IS_WCHAR_PATH
54 #endif
55 #ifdef WIN32
56 #define GHC_OS_WINDOWS
57 #endif
58 #else
59 #ifdef GHC_FILESYSTEM_FWD_TEST
60 #include <ghc/fs_fwd.hpp>
61 #else
62 #include <ghc/filesystem.hpp>
63 #endif
64 namespace fs {
65 using namespace ghc::filesystem;
66 using ifstream = ghc::filesystem::ifstream;
67 using ofstream = ghc::filesystem::ofstream;
68 using fstream = ghc::filesystem::fstream;
69 } // namespace fs
70 #endif
71
72 #if defined(WIN32) || defined(_WIN32)
73 #include <windows.h>
74 #else
75 #include <sys/socket.h>
76 #include <sys/stat.h>
77 #include <sys/types.h>
78 #include <sys/un.h>
79 #include <unistd.h>
80 #endif
81
82 #ifndef GHC_FILESYSTEM_FWD_TEST
83 #define CATCH_CONFIG_MAIN
84 #endif
85 #include "catch.hpp"
86
87 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
88 // Behaviour Switches (should match the config in ghc/filesystem.hpp):
89 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
90 // LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories
91 #define TEST_LWG_2682_BEHAVIOUR
92 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
93 // LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular
94 // file with that name, it is superceded by P1164R1, so only activate if really needed
95 // #define TEST_LWG_2935_BEHAVIOUR
96 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
97 // LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2)
98 #define TEST_LWG_2937_BEHAVIOUR
99 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
100
101 template <typename TP>
to_time_t(TP tp)102 std::time_t to_time_t(TP tp)
103 {
104 using namespace std::chrono;
105 auto sctp = time_point_cast<system_clock::duration>(tp - TP::clock::now() + system_clock::now());
106 return system_clock::to_time_t(sctp);
107 }
108
109 template <typename TP>
from_time_t(std::time_t t)110 TP from_time_t(std::time_t t)
111 {
112 using namespace std::chrono;
113 auto sctp = system_clock::from_time_t(t);
114 auto tp = time_point_cast<typename TP::duration>(sctp - system_clock::now() + TP::clock::now());
115 return tp;
116 }
117
118 namespace Catch {
119 template <>
120 struct StringMaker<fs::path>
121 {
convertCatch::StringMaker122 static std::string convert(fs::path const& value) { return '"' + value.string() + '"'; }
123 };
124
125 template <>
126 struct StringMaker<fs::perms>
127 {
convertCatch::StringMaker128 static std::string convert(fs::perms const& value) { return std::to_string(static_cast<unsigned int>(value)); }
129 };
130
131 template <>
132 struct StringMaker<fs::file_status>
133 {
convertCatch::StringMaker134 static std::string convert(fs::file_status const& value) {
135 return std::string("[") + std::to_string(static_cast<unsigned int>(value.type())) + "," + std::to_string(static_cast<unsigned int>(value.permissions())) + "]";
136 }
137 };
138
139 #ifdef __cpp_lib_char8_t
140 template <>
141 struct StringMaker<char8_t>
142 {
convertCatch::StringMaker143 static std::string convert(char8_t const& value) { return std::to_string(static_cast<unsigned int>(value)); }
144 };
145 #endif
146
147 template <>
148 struct StringMaker<fs::file_time_type>
149 {
convertCatch::StringMaker150 static std::string convert(fs::file_time_type const& value)
151 {
152 std::time_t t = to_time_t(value);
153 std::tm* ptm = std::localtime(&t);
154 std::ostringstream os;
155 if (ptm) {
156 std::tm ttm = *ptm;
157 os << std::put_time(&ttm, "%Y-%m-%d %H:%M:%S");
158 }
159 else {
160 os << "(invalid-time)";
161 }
162 return os.str();
163 }
164 };
165 } // namespace Catch
166
167 enum class TempOpt { none, change_path };
168 class TemporaryDirectory
169 {
170 public:
TemporaryDirectory(TempOpt opt=TempOpt::none)171 TemporaryDirectory(TempOpt opt = TempOpt::none)
172 {
173 static auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
174 static auto rng = std::bind(std::uniform_int_distribution<int>(0, 35), std::mt19937(static_cast<unsigned int>(seed) ^ static_cast<unsigned int>(reinterpret_cast<ptrdiff_t>(&opt))));
175 std::string filename;
176 do {
177 filename = "test_";
178 for (int i = 0; i < 8; ++i) {
179 filename += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[rng()];
180 }
181 _path = fs::canonical(fs::temp_directory_path()) / filename;
182 } while (fs::exists(_path));
183 fs::create_directories(_path);
184 if (opt == TempOpt::change_path) {
185 _orig_dir = fs::current_path();
186 fs::current_path(_path);
187 }
188 }
189
~TemporaryDirectory()190 ~TemporaryDirectory()
191 {
192 if (!_orig_dir.empty()) {
193 fs::current_path(_orig_dir);
194 }
195 fs::remove_all(_path);
196 }
197
path() const198 const fs::path& path() const { return _path; }
199
200 private:
201 fs::path _path;
202 fs::path _orig_dir;
203 };
204
generateFile(const fs::path & pathname,int withSize=-1)205 static void generateFile(const fs::path& pathname, int withSize = -1)
206 {
207 fs::ofstream outfile(pathname);
208 if (withSize < 0) {
209 outfile << "Hello world!" << std::endl;
210 }
211 else {
212 outfile << std::string(size_t(withSize), '*');
213 }
214 }
215
216 #ifdef GHC_OS_WINDOWS
isWow64Proc()217 inline bool isWow64Proc()
218 {
219 typedef BOOL(WINAPI * IsWow64Process_t)(HANDLE, PBOOL);
220 BOOL bIsWow64 = FALSE;
221 auto fnIsWow64Process = (IsWow64Process_t)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
222 if (NULL != fnIsWow64Process) {
223 if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64)) {
224 bIsWow64 = FALSE;
225 }
226 }
227 return bIsWow64 == TRUE;
228 }
229
is_symlink_creation_supported()230 static bool is_symlink_creation_supported()
231 {
232 bool result = true;
233 HKEY key;
234 REGSAM flags = KEY_READ;
235 #ifdef _WIN64
236 flags |= KEY_WOW64_64KEY;
237 #elif defined(KEY_WOW64_64KEY)
238 if (isWow64Proc()) {
239 flags |= KEY_WOW64_64KEY;
240 }
241 else {
242 flags |= KEY_WOW64_32KEY;
243 }
244 #else
245 result = false;
246 #endif
247 if (result) {
248 auto err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", 0, flags, &key);
249 if (err == ERROR_SUCCESS) {
250 DWORD val = 0, size = sizeof(DWORD);
251 err = RegQueryValueExW(key, L"AllowDevelopmentWithoutDevLicense", 0, NULL, reinterpret_cast<LPBYTE>(&val), &size);
252 RegCloseKey(key);
253 if (err != ERROR_SUCCESS) {
254 result = false;
255 }
256 else {
257 result = (val != 0);
258 }
259 }
260 else {
261 result = false;
262 }
263 }
264 if (!result) {
265 std::clog << "Warning: Symlink creation not supported." << std::endl;
266 }
267 return result;
268 }
269 #else
is_symlink_creation_supported()270 static bool is_symlink_creation_supported()
271 {
272 return true;
273 }
274 #endif
275
has_host_root_name_support()276 static bool has_host_root_name_support()
277 {
278 return fs::path("//host").has_root_name();
279 }
280
281 template <class T>
282 class TestAllocator
283 {
284 public:
285 using value_type = T;
286 using pointer = T*;
287 using const_pointer = const T*;
288 using reference = T&;
289 using const_reference = const T&;
290 using difference_type = ptrdiff_t;
291 using size_type = size_t;
TestAllocator()292 TestAllocator() noexcept {}
293 template <class U>
TestAllocator(TestAllocator<U> const &)294 TestAllocator(TestAllocator<U> const&) noexcept
295 {
296 }
allocate(std::size_t n)297 value_type* allocate(std::size_t n) { return static_cast<value_type*>(::operator new(n * sizeof(value_type))); }
deallocate(value_type * p,std::size_t)298 void deallocate(value_type* p, std::size_t) noexcept { ::operator delete(p); }
299 template<class U>
300 struct rebind {
301 typedef TestAllocator<U> other;
302 };
303 };
304
305 template <class T, class U>
operator ==(TestAllocator<T> const &,TestAllocator<U> const &)306 bool operator==(TestAllocator<T> const&, TestAllocator<U> const&) noexcept
307 {
308 return true;
309 }
310
311 template <class T, class U>
operator !=(TestAllocator<T> const & x,TestAllocator<U> const & y)312 bool operator!=(TestAllocator<T> const& x, TestAllocator<U> const& y) noexcept
313 {
314 return !(x == y);
315 }
316
317 TEST_CASE("Temporary Directory", "[fs.test.tempdir]")
318 {
319 fs::path tempPath;
320 {
321 TemporaryDirectory t;
322 tempPath = t.path();
323 REQUIRE(fs::exists(fs::path(t.path())));
324 REQUIRE(fs::is_directory(t.path()));
325 }
326 REQUIRE(!fs::exists(tempPath));
327 }
328
329 #ifdef GHC_FILESYSTEM_VERSION
330 TEST_CASE("fs::detail::fromUtf8", "[filesystem][fs.detail.utf8]")
331 {
332 CHECK(fs::detail::fromUtf8<std::wstring>("foobar").length() == 6);
333 CHECK(fs::detail::fromUtf8<std::wstring>("foobar") == L"foobar");
334 CHECK(fs::detail::fromUtf8<std::wstring>(u8"föobar").length() == 6);
335 CHECK(fs::detail::fromUtf8<std::wstring>(u8"föobar") == L"föobar");
336
337 CHECK(fs::detail::toUtf8(std::wstring(L"foobar")).length() == 6);
338 CHECK(fs::detail::toUtf8(std::wstring(L"foobar")) == "foobar");
339 CHECK(fs::detail::toUtf8(std::wstring(L"föobar")).length() == 7);
340 //CHECK(fs::detail::toUtf8(std::wstring(L"föobar")) == u8"föobar");
341
342 #ifdef GHC_RAISE_UNICODE_ERRORS
343 CHECK_THROWS_AS(fs::detail::fromUtf8<std::u16string>(std::string("\xed\xa0\x80")), fs::filesystem_error);
344 CHECK_THROWS_AS(fs::detail::fromUtf8<std::u16string>(std::string("\xc3")), fs::filesystem_error);
345 #else
346 CHECK(std::u16string(2,0xfffd) == fs::detail::fromUtf8<std::u16string>(std::string("\xed\xa0\x80")));
347 CHECK(std::u16string(1,0xfffd) == fs::detail::fromUtf8<std::u16string>(std::string("\xc3")));
348 #endif
349 }
350
351 TEST_CASE("fs::detail::toUtf8", "[filesystem][fs.detail.utf8]")
352 {
353 std::string t;
354 CHECK(std::string("\xc3\xa4/\xe2\x82\xac\xf0\x9d\x84\x9e") == fs::detail::toUtf8(std::u16string(u"\u00E4/\u20AC\U0001D11E")));
355 #ifdef GHC_RAISE_UNICODE_ERRORS
356 CHECK_THROWS_AS(fs::detail::toUtf8(std::u16string(1, 0xd800)), fs::filesystem_error);
357 CHECK_THROWS_AS(fs::detail::appendUTF8(t, 0x200000), fs::filesystem_error);
358 #else
359 CHECK(std::string("\xEF\xBF\xBD") == fs::detail::toUtf8(std::u16string(1, 0xd800)));
360 fs::detail::appendUTF8(t, 0x200000);
361 CHECK(std::string("\xEF\xBF\xBD") == t);
362 #endif
363 }
364 #endif
365
366 TEST_CASE("fs.path.generic - path::preferred_separator", "[filesystem][path][fs.path.generic]")
367 {
368 #ifdef GHC_OS_WINDOWS
369 CHECK(fs::path::preferred_separator == '\\');
370 #else
371 CHECK(fs::path::preferred_separator == '/');
372 #endif
373 }
374
375 #ifndef GHC_OS_WINDOWS
376 TEST_CASE("fs.path.generic - path(\"//host\").has_root_name()", "[filesystem][path][fs.path.generic]")
377 {
378 if (!has_host_root_name_support()) {
379 WARN("This implementation doesn't support path(\"//host\").has_root_name() == true [C++17 30.12.8.1 par. 4] on this platform, tests based on this are skipped. (Should be okay.)");
380 }
381 }
382 #endif
383
384 TEST_CASE("fs.path.construct - path constructors and destructor", "[filesystem][path][fs.path.construct]")
385 {
386 CHECK("/usr/local/bin" == fs::path("/usr/local/bin").generic_string());
387 std::string str = "/usr/local/bin";
388 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
389 std::u8string u8str = u8"/usr/local/bin";
390 #endif
391 std::u16string u16str = u"/usr/local/bin";
392 std::u32string u32str = U"/usr/local/bin";
393 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
394 CHECK(u8str == fs::path(u8str).generic_u8string());
395 #endif
396 CHECK(u16str == fs::path(u16str).generic_u16string());
397 CHECK(u32str == fs::path(u32str).generic_u32string());
398 CHECK(str == fs::path(str, fs::path::format::generic_format));
399 CHECK(str == fs::path(str.begin(), str.end()));
400 CHECK(fs::path(std::wstring(3, 67)) == "CCC");
401 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
402 CHECK(str == fs::path(u8str.begin(), u8str.end()));
403 #endif
404 CHECK(str == fs::path(u16str.begin(), u16str.end()));
405 CHECK(str == fs::path(u32str.begin(), u32str.end()));
406 #ifdef GHC_FILESYSTEM_VERSION
407 CHECK(fs::path("///foo/bar") == "/foo/bar");
408 CHECK(fs::path("//foo//bar") == "//foo/bar");
409 #endif
410 #ifdef GHC_OS_WINDOWS
411 CHECK("\\usr\\local\\bin" == fs::path("/usr/local/bin"));
412 CHECK("C:\\usr\\local\\bin" == fs::path("C:\\usr\\local\\bin"));
413 #else
414 CHECK("/usr/local/bin" == fs::path("/usr/local/bin"));
415 #endif
416 if (has_host_root_name_support()) {
417 CHECK("//host/foo/bar" == fs::path("//host/foo/bar"));
418 }
419
420 #if !defined(GHC_OS_WINDOWS) && !(defined(__GLIBCXX__) && !(defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 8))) && !defined(USE_STD_FS)
421 std::locale loc;
422 bool testUTF8Locale = false;
423 try {
424 if (const char* lang = std::getenv("LANG")) {
425 loc = std::locale(lang);
426 }
427 else {
428 loc = std::locale("en_US.UTF-8");
429 }
430 std::string name = loc.name();
431 if (name.length() > 5 && (name.substr(name.length() - 5) == "UTF-8" || name.substr(name.length() - 5) == "utf-8")) {
432 testUTF8Locale = true;
433 }
434 }
435 catch (std::runtime_error&) {
436 WARN("Couldn't create an UTF-8 locale!");
437 }
438 if (testUTF8Locale) {
439 CHECK("/usr/local/bin" == fs::path("/usr/local/bin", loc));
440 CHECK(str == fs::path(str.begin(), str.end(), loc));
441 CHECK(str == fs::path(u16str.begin(), u16str.end(), loc));
442 CHECK(str == fs::path(u32str.begin(), u32str.end(), loc));
443 }
444 #endif
445 }
446
447 TEST_CASE("fs.path.assign - path assignments", "[filesystem][path][fs.path.assign]")
448 {
449 fs::path p1{"/foo/bar"};
450 fs::path p2{"/usr/local"};
451 fs::path p3;
452 p3 = p1;
453 REQUIRE(p1 == p3);
454 p3 = fs::path{"/usr/local"};
455 REQUIRE(p2 == p3);
456 p3 = fs::path{L"/usr/local"};
457 REQUIRE(p2 == p3);
458 p3.assign(L"/usr/local");
459 REQUIRE(p2 == p3);
460 #if defined(IS_WCHAR_PATH) || defined(GHC_USE_WCHAR_T)
461 p3 = fs::path::string_type{L"/foo/bar"};
462 REQUIRE(p1 == p3);
463 p3.assign(fs::path::string_type{L"/usr/local"});
464 REQUIRE(p2 == p3);
465 #else
466 p3 = fs::path::string_type{"/foo/bar"};
467 REQUIRE(p1 == p3);
468 p3.assign(fs::path::string_type{"/usr/local"});
469 REQUIRE(p2 == p3);
470 #endif
471 p3 = std::u16string(u"/foo/bar");
472 REQUIRE(p1 == p3);
473 p3 = U"/usr/local";
474 REQUIRE(p2 == p3);
475 p3.assign(std::u16string(u"/foo/bar"));
476 REQUIRE(p1 == p3);
477 std::string s{"/usr/local"};
478 p3.assign(s.begin(), s.end());
479 REQUIRE(p2 == p3);
480 }
481
482 TEST_CASE("fs.path.append - path appends", "[filesystem][path][fs.path.append]")
483 {
484 #ifdef GHC_OS_WINDOWS
485 CHECK(fs::path("foo") / "c:/bar" == "c:/bar");
486 CHECK(fs::path("foo") / "c:" == "c:");
487 CHECK(fs::path("c:") / "" == "c:");
488 CHECK(fs::path("c:foo") / "/bar" == "c:/bar");
489 CHECK(fs::path("c:foo") / "c:bar" == "c:foo/bar");
490 #else
491 CHECK(fs::path("foo") / "" == "foo/");
492 CHECK(fs::path("foo") / "/bar" == "/bar");
493 CHECK(fs::path("/foo") / "/" == "/");
494 if (has_host_root_name_support()) {
495 CHECK(fs::path("//host/foo") / "/bar" == "/bar");
496 CHECK(fs::path("//host") / "/" == "//host/");
497 CHECK(fs::path("//host/foo") / "/" == "/");
498 }
499 #endif
500 CHECK(fs::path("/foo/bar") / "some///other" == "/foo/bar/some/other");
501 fs::path p1{"/tmp/test"};
502 fs::path p2{"foobar.txt"};
503 fs::path p3 = p1 / p2;
504 CHECK("/tmp/test/foobar.txt" == p3);
505 // TODO: append(first, last)
506 }
507
508 TEST_CASE("fs.path.concat - path concatenation", "[filesystem][path][fs.path.concat]")
509 {
510 CHECK((fs::path("foo") += fs::path("bar")) == "foobar");
511 CHECK((fs::path("foo") += fs::path("/bar")) == "foo/bar");
512
513 CHECK((fs::path("foo") += std::string("bar")) == "foobar");
514 CHECK((fs::path("foo") += std::string("/bar")) == "foo/bar");
515
516 CHECK((fs::path("foo") += "bar") == "foobar");
517 CHECK((fs::path("foo") += "/bar") == "foo/bar");
518 CHECK((fs::path("foo") += L"bar") == "foobar");
519 CHECK((fs::path("foo") += L"/bar") == "foo/bar");
520
521 CHECK((fs::path("foo") += 'b') == "foob");
522 CHECK((fs::path("foo") += '/') == "foo/");
523 CHECK((fs::path("foo") += L'b') == "foob");
524 CHECK((fs::path("foo") += L'/') == "foo/");
525
526 CHECK((fs::path("foo") += std::string("bar")) == "foobar");
527 CHECK((fs::path("foo") += std::string("/bar")) == "foo/bar");
528
529 CHECK((fs::path("foo") += std::u16string(u"bar")) == "foobar");
530 CHECK((fs::path("foo") += std::u16string(u"/bar")) == "foo/bar");
531
532 CHECK((fs::path("foo") += std::u32string(U"bar")) == "foobar");
533 CHECK((fs::path("foo") += std::u32string(U"/bar")) == "foo/bar");
534
535 CHECK(fs::path("foo").concat("bar") == "foobar");
536 CHECK(fs::path("foo").concat("/bar") == "foo/bar");
537 CHECK(fs::path("foo").concat(L"bar") == "foobar");
538 CHECK(fs::path("foo").concat(L"/bar") == "foo/bar");
539 std::string bar = "bar";
540 CHECK(fs::path("foo").concat(bar.begin(), bar.end()) == "foobar");
541 #ifndef USE_STD_FS
542 CHECK((fs::path("/foo/bar") += "/some///other") == "/foo/bar/some/other");
543 #endif
544 // TODO: contat(first, last)
545 }
546
547 TEST_CASE("fs.path.modifiers - path modifiers", "[filesystem][path][fs.path.modifiers]")
548 {
549 fs::path p = fs::path("/foo/bar");
550 p.clear();
551 CHECK(p == "");
552
553 // make_preferred() is a no-op
554 #ifdef GHC_OS_WINDOWS
555 CHECK(fs::path("foo\\bar") == "foo/bar");
556 CHECK(fs::path("foo\\bar").make_preferred() == "foo/bar");
557 #else
558 CHECK(fs::path("foo\\bar") == "foo\\bar");
559 CHECK(fs::path("foo\\bar").make_preferred() == "foo\\bar");
560 #endif
561 CHECK(fs::path("foo/bar").make_preferred() == "foo/bar");
562
563 CHECK(fs::path("foo/bar").remove_filename() == "foo/");
564 CHECK(fs::path("foo/").remove_filename() == "foo/");
565 CHECK(fs::path("/foo").remove_filename() == "/");
566 CHECK(fs::path("/").remove_filename() == "/");
567
568 CHECK(fs::path("/foo").replace_filename("bar") == "/bar");
569 CHECK(fs::path("/").replace_filename("bar") == "/bar");
570 CHECK(fs::path("/foo").replace_filename("b//ar") == "/b/ar");
571
572 CHECK(fs::path("/foo/bar.txt").replace_extension("odf") == "/foo/bar.odf");
573 CHECK(fs::path("/foo/bar.txt").replace_extension() == "/foo/bar");
574 CHECK(fs::path("/foo/bar").replace_extension("odf") == "/foo/bar.odf");
575 CHECK(fs::path("/foo/bar").replace_extension(".odf") == "/foo/bar.odf");
576 CHECK(fs::path("/foo/bar.").replace_extension(".odf") == "/foo/bar.odf");
577 CHECK(fs::path("/foo/bar/").replace_extension("odf") == "/foo/bar/.odf");
578
579 fs::path p1 = "foo";
580 fs::path p2 = "bar";
581 p1.swap(p2);
582 CHECK(p1 == "bar");
583 CHECK(p2 == "foo");
584 }
585
586 TEST_CASE("fs.path.native.obs - path native format observers", "[filesystem][path][fs.path.native.obs]")
587 {
588 #ifdef GHC_OS_WINDOWS
589 #if defined(IS_WCHAR_PATH) || defined(GHC_USE_WCHAR_T)
590 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type(L"\u00E4\\\u20AC"));
591 // CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("ä\\€")); // MSVCs returns local DBCS encoding
592 #else
593 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type("\xc3\xa4\\\xe2\x82\xac"));
594 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("\xc3\xa4\\\xe2\x82\xac"));
595 CHECK(!::strcmp(fs::u8path("\xc3\xa4\\\xe2\x82\xac").c_str(), "\xc3\xa4\\\xe2\x82\xac"));
596 CHECK((std::string)fs::u8path("\xc3\xa4\\\xe2\x82\xac") == std::string("\xc3\xa4\\\xe2\x82\xac"));
597 #endif
598 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").wstring() == std::wstring(L"\u00E4\\\u20AC"));
599 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
600 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u8string() == std::u8string(u8"\u00E4\\\u20AC"));
601 #else
602 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u8string() == std::string("\xc3\xa4\\\xe2\x82\xac"));
603 #endif
604 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u16string() == std::u16string(u"\u00E4\\\u20AC"));
605 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u32string() == std::u32string(U"\U000000E4\\\U000020AC"));
606 #else
607 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").native() == fs::path::string_type("\xc3\xa4/\xe2\x82\xac"));
608 CHECK(!::strcmp(fs::u8path("\xc3\xa4/\xe2\x82\xac").c_str(), "\xc3\xa4/\xe2\x82\xac"));
609 CHECK((std::string)fs::u8path("\xc3\xa4/\xe2\x82\xac") == std::string("\xc3\xa4/\xe2\x82\xac"));
610 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").string() == std::string("\xc3\xa4/\xe2\x82\xac"));
611 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").wstring() == std::wstring(L"ä/€"));
612 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
613 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u8string() == std::u8string(u8"\xc3\xa4/\xe2\x82\xac"));
614 #else
615 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u8string() == std::string("\xc3\xa4/\xe2\x82\xac"));
616 #endif
617 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u16string() == std::u16string(u"\u00E4/\u20AC"));
618 INFO("This check might fail on GCC8 (with \"Illegal byte sequence\") due to not detecting the valid unicode codepoint U+1D11E.");
619 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac\xf0\x9d\x84\x9e").u16string() == std::u16string(u"\u00E4/\u20AC\U0001D11E"));
620 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u32string() == std::u32string(U"\U000000E4/\U000020AC"));
621 #endif
622 }
623
624 TEST_CASE("fs.path.generic.obs - path generic format observers", "[filesystem][path][fs.path.generic.obs]")
625 {
626 #ifdef GHC_OS_WINDOWS
627 #ifndef IS_WCHAR_PATH
628 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_string() == std::string("\xc3\xa4/\xe2\x82\xac"));
629 #endif
630 #ifndef USE_STD_FS
631 auto t = fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_string<char, std::char_traits<char>, TestAllocator<char>>();
632 CHECK(t.c_str() == std::string("\xc3\xa4/\xe2\x82\xac"));
633 #endif
634 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_wstring() == std::wstring(L"\U000000E4/\U000020AC"));
635 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
636 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u8string() == std::u8string(u8"\u00E4/\u20AC"));
637 #else
638 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u8string() == std::string("\xc3\xa4/\xe2\x82\xac"));
639 #endif
640 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u16string() == std::u16string(u"\u00E4/\u20AC"));
641 CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u32string() == std::u32string(U"\U000000E4/\U000020AC"));
642 #else
643 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_string() == std::string("\xc3\xa4/\xe2\x82\xac"));
644 #ifndef USE_STD_FS
645 auto t = fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_string<char, std::char_traits<char>, TestAllocator<char>>();
646 CHECK(t.c_str() == std::string("\xc3\xa4/\xe2\x82\xac"));
647 #endif
648 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_wstring() == std::wstring(L"ä/€"));
649 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
650 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u8string() == std::u8string(u8"\xc3\xa4/\xe2\x82\xac"));
651 #else
652 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u8string() == std::string("\xc3\xa4/\xe2\x82\xac"));
653 #endif
654 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u16string() == std::u16string(u"\u00E4/\u20AC"));
655 CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u32string() == std::u32string(U"\U000000E4/\U000020AC"));
656 #endif
657 }
658
659 TEST_CASE("fs.path.compare - path compare", "[filesystem][path][fs.path.compare]")
660 {
661 CHECK(fs::path("/foo/b").compare("/foo/a") > 0);
662 CHECK(fs::path("/foo/b").compare("/foo/b") == 0);
663 CHECK(fs::path("/foo/b").compare("/foo/c") < 0);
664
665 CHECK(fs::path("/foo/b").compare(std::string("/foo/a")) > 0);
666 CHECK(fs::path("/foo/b").compare(std::string("/foo/b")) == 0);
667 CHECK(fs::path("/foo/b").compare(std::string("/foo/c")) < 0);
668
669 CHECK(fs::path("/foo/b").compare(fs::path("/foo/a")) > 0);
670 CHECK(fs::path("/foo/b").compare(fs::path("/foo/b")) == 0);
671 CHECK(fs::path("/foo/b").compare(fs::path("/foo/c")) < 0);
672
673 #ifdef GHC_OS_WINDOWS
674 CHECK(fs::path("c:\\a\\b").compare("C:\\a\\b") == 0);
675 CHECK(fs::path("c:\\a\\b").compare("d:\\a\\b") != 0);
676 CHECK(fs::path("c:\\a\\b").compare("C:\\A\\b") != 0);
677 #endif
678
679 #ifdef LWG_2936_BEHAVIOUR
680 CHECK(fs::path("/a/b/").compare("/a/b/c") < 0);
681 CHECK(fs::path("/a/b/").compare("a/c") > 0);
682 #endif // LWG_2936_BEHAVIOUR
683 }
684
685 TEST_CASE("fs.path.decompose - path decomposition", "[filesystem][path][fs.path.decompose]")
686 {
687 // root_name()
688 CHECK(fs::path("").root_name() == "");
689 CHECK(fs::path(".").root_name() == "");
690 CHECK(fs::path("..").root_name() == "");
691 CHECK(fs::path("foo").root_name() == "");
692 CHECK(fs::path("/").root_name() == "");
693 CHECK(fs::path("/foo").root_name() == "");
694 CHECK(fs::path("foo/").root_name() == "");
695 CHECK(fs::path("/foo/").root_name() == "");
696 CHECK(fs::path("foo/bar").root_name() == "");
697 CHECK(fs::path("/foo/bar").root_name() == "");
698 CHECK(fs::path("///foo/bar").root_name() == "");
699 #ifdef GHC_OS_WINDOWS
700 CHECK(fs::path("C:/foo").root_name() == "C:");
701 CHECK(fs::path("C:\\foo").root_name() == "C:");
702 CHECK(fs::path("C:foo").root_name() == "C:");
703 #endif
704
705 // root_directory()
706 CHECK(fs::path("").root_directory() == "");
707 CHECK(fs::path(".").root_directory() == "");
708 CHECK(fs::path("..").root_directory() == "");
709 CHECK(fs::path("foo").root_directory() == "");
710 CHECK(fs::path("/").root_directory() == "/");
711 CHECK(fs::path("/foo").root_directory() == "/");
712 CHECK(fs::path("foo/").root_directory() == "");
713 CHECK(fs::path("/foo/").root_directory() == "/");
714 CHECK(fs::path("foo/bar").root_directory() == "");
715 CHECK(fs::path("/foo/bar").root_directory() == "/");
716 CHECK(fs::path("///foo/bar").root_directory() == "/");
717 #ifdef GHC_OS_WINDOWS
718 CHECK(fs::path("C:/foo").root_directory() == "/");
719 CHECK(fs::path("C:\\foo").root_directory() == "/");
720 CHECK(fs::path("C:foo").root_directory() == "");
721 #endif
722
723 // root_path()
724 CHECK(fs::path("").root_path() == "");
725 CHECK(fs::path(".").root_path() == "");
726 CHECK(fs::path("..").root_path() == "");
727 CHECK(fs::path("foo").root_path() == "");
728 CHECK(fs::path("/").root_path() == "/");
729 CHECK(fs::path("/foo").root_path() == "/");
730 CHECK(fs::path("foo/").root_path() == "");
731 CHECK(fs::path("/foo/").root_path() == "/");
732 CHECK(fs::path("foo/bar").root_path() == "");
733 CHECK(fs::path("/foo/bar").root_path() == "/");
734 CHECK(fs::path("///foo/bar").root_path() == "/");
735 #ifdef GHC_OS_WINDOWS
736 CHECK(fs::path("C:/foo").root_path() == "C:/");
737 CHECK(fs::path("C:\\foo").root_path() == "C:/");
738 CHECK(fs::path("C:foo").root_path() == "C:");
739 #endif
740
741 // relative_path()
742 CHECK(fs::path("").relative_path() == "");
743 CHECK(fs::path(".").relative_path() == ".");
744 CHECK(fs::path("..").relative_path() == "..");
745 CHECK(fs::path("foo").relative_path() == "foo");
746 CHECK(fs::path("/").relative_path() == "");
747 CHECK(fs::path("/foo").relative_path() == "foo");
748 CHECK(fs::path("foo/").relative_path() == "foo/");
749 CHECK(fs::path("/foo/").relative_path() == "foo/");
750 CHECK(fs::path("foo/bar").relative_path() == "foo/bar");
751 CHECK(fs::path("/foo/bar").relative_path() == "foo/bar");
752 CHECK(fs::path("///foo/bar").relative_path() == "foo/bar");
753 #ifdef GHC_OS_WINDOWS
754 CHECK(fs::path("C:/foo").relative_path() == "foo");
755 CHECK(fs::path("C:\\foo").relative_path() == "foo");
756 CHECK(fs::path("C:foo").relative_path() == "foo");
757 #endif
758
759 // parent_path()
760 CHECK(fs::path("").parent_path() == "");
761 CHECK(fs::path(".").parent_path() == "");
762 CHECK(fs::path("..").parent_path() == ""); // unintuitive but as defined in the standard
763 CHECK(fs::path("foo").parent_path() == "");
764 CHECK(fs::path("/").parent_path() == "/");
765 CHECK(fs::path("/foo").parent_path() == "/");
766 CHECK(fs::path("foo/").parent_path() == "foo");
767 CHECK(fs::path("/foo/").parent_path() == "/foo");
768 CHECK(fs::path("foo/bar").parent_path() == "foo");
769 CHECK(fs::path("/foo/bar").parent_path() == "/foo");
770 CHECK(fs::path("///foo/bar").parent_path() == "/foo");
771 #ifdef GHC_OS_WINDOWS
772 CHECK(fs::path("C:/foo").parent_path() == "C:/");
773 CHECK(fs::path("C:\\foo").parent_path() == "C:/");
774 CHECK(fs::path("C:foo").parent_path() == "C:");
775 #endif
776
777 // filename()
778 CHECK(fs::path("").filename() == "");
779 CHECK(fs::path(".").filename() == ".");
780 CHECK(fs::path("..").filename() == "..");
781 CHECK(fs::path("foo").filename() == "foo");
782 CHECK(fs::path("/").filename() == "");
783 CHECK(fs::path("/foo").filename() == "foo");
784 CHECK(fs::path("foo/").filename() == "");
785 CHECK(fs::path("/foo/").filename() == "");
786 CHECK(fs::path("foo/bar").filename() == "bar");
787 CHECK(fs::path("/foo/bar").filename() == "bar");
788 CHECK(fs::path("///foo/bar").filename() == "bar");
789 #ifdef GHC_OS_WINDOWS
790 CHECK(fs::path("C:/foo").filename() == "foo");
791 CHECK(fs::path("C:\\foo").filename() == "foo");
792 CHECK(fs::path("C:foo").filename() == "foo");
793 #endif
794
795 // stem()
796 CHECK(fs::path("/foo/bar.txt").stem() == "bar");
797 {
798 fs::path p = "foo.bar.baz.tar";
799 CHECK(p.extension() == ".tar");
800 p = p.stem();
801 CHECK(p.extension() == ".baz");
802 p = p.stem();
803 CHECK(p.extension() == ".bar");
804 p = p.stem();
805 CHECK(p == "foo");
806 }
807 CHECK(fs::path("/foo/.profile").stem() == ".profile");
808 CHECK(fs::path(".bar").stem() == ".bar");
809 CHECK(fs::path("..bar").stem() == ".");
810
811 // extension()
812 CHECK(fs::path("/foo/bar.txt").extension() == ".txt");
813 CHECK(fs::path("/foo/bar").extension() == "");
814 CHECK(fs::path("/foo/.profile").extension() == "");
815 CHECK(fs::path(".bar").extension() == "");
816 CHECK(fs::path("..bar").extension() == ".bar");
817
818 if (has_host_root_name_support()) {
819 // //host-based root-names
820 CHECK(fs::path("//host").root_name() == "//host");
821 CHECK(fs::path("//host/foo").root_name() == "//host");
822 CHECK(fs::path("//host").root_directory() == "");
823 CHECK(fs::path("//host/foo").root_directory() == "/");
824 CHECK(fs::path("//host").root_path() == "//host");
825 CHECK(fs::path("//host/foo").root_path() == "//host/");
826 CHECK(fs::path("//host").relative_path() == "");
827 CHECK(fs::path("//host/foo").relative_path() == "foo");
828 CHECK(fs::path("//host").parent_path() == "//host");
829 CHECK(fs::path("//host/foo").parent_path() == "//host/");
830 CHECK(fs::path("//host").filename() == "");
831 CHECK(fs::path("//host/foo").filename() == "foo");
832 }
833 }
834
835 TEST_CASE("fs.path.query - path query", "[fielsystem][path][fs.path.query]")
836 {
837 // empty
838 CHECK(fs::path("").empty());
839 CHECK(!fs::path("foo").empty());
840
841 // has_root_path()
842 CHECK(!fs::path("foo").has_root_path());
843 CHECK(!fs::path("foo/bar").has_root_path());
844 CHECK(fs::path("/foo").has_root_path());
845 #ifdef GHC_OS_WINDOWS
846 CHECK(fs::path("C:foo").has_root_path());
847 CHECK(fs::path("C:/foo").has_root_path());
848 #endif
849
850 // has_root_name()
851 CHECK(!fs::path("foo").has_root_name());
852 CHECK(!fs::path("foo/bar").has_root_name());
853 CHECK(!fs::path("/foo").has_root_name());
854 #ifdef GHC_OS_WINDOWS
855 CHECK(fs::path("C:foo").has_root_name());
856 CHECK(fs::path("C:/foo").has_root_name());
857 #endif
858
859 // has_root_directory()
860 CHECK(!fs::path("foo").has_root_directory());
861 CHECK(!fs::path("foo/bar").has_root_directory());
862 CHECK(fs::path("/foo").has_root_directory());
863 #ifdef GHC_OS_WINDOWS
864 CHECK(!fs::path("C:foo").has_root_directory());
865 CHECK(fs::path("C:/foo").has_root_directory());
866 #endif
867
868 // has_relative_path()
869 CHECK(!fs::path("").has_relative_path());
870 CHECK(!fs::path("/").has_relative_path());
871 CHECK(fs::path("/foo").has_relative_path());
872
873 // has_parent_path()
874 CHECK(!fs::path("").has_parent_path());
875 CHECK(!fs::path(".").has_parent_path());
876 CHECK(!fs::path("..").has_parent_path()); // unintuitive but as defined in the standard
877 CHECK(!fs::path("foo").has_parent_path());
878 CHECK(fs::path("/").has_parent_path());
879 CHECK(fs::path("/foo").has_parent_path());
880 CHECK(fs::path("foo/").has_parent_path());
881 CHECK(fs::path("/foo/").has_parent_path());
882
883 // has_filename()
884 CHECK(fs::path("foo").has_filename());
885 CHECK(fs::path("foo/bar").has_filename());
886 CHECK(!fs::path("/foo/bar/").has_filename());
887
888 // has_stem()
889 CHECK(fs::path("foo").has_stem());
890 CHECK(fs::path("foo.bar").has_stem());
891 CHECK(fs::path(".profile").has_stem());
892 CHECK(!fs::path("/foo/").has_stem());
893
894 // has_extension()
895 CHECK(!fs::path("foo").has_extension());
896 CHECK(fs::path("foo.bar").has_extension());
897 CHECK(!fs::path(".profile").has_extension());
898
899 // is_absolute()
900 CHECK(!fs::path("foo/bar").is_absolute());
901 #ifdef GHC_OS_WINDOWS
902 CHECK(!fs::path("/foo").is_absolute());
903 CHECK(!fs::path("c:foo").is_absolute());
904 CHECK(fs::path("c:/foo").is_absolute());
905 #else
906 CHECK(fs::path("/foo").is_absolute());
907 #endif
908
909 // is_relative()
910 CHECK(fs::path("foo/bar").is_relative());
911 #ifdef GHC_OS_WINDOWS
912 CHECK(fs::path("/foo").is_relative());
913 CHECK(fs::path("c:foo").is_relative());
914 CHECK(!fs::path("c:/foo").is_relative());
915 #else
916 CHECK(!fs::path("/foo").is_relative());
917 #endif
918
919 if (has_host_root_name_support()) {
920 CHECK(fs::path("//host").has_root_name());
921 CHECK(fs::path("//host/foo").has_root_name());
922 CHECK(fs::path("//host").has_root_path());
923 CHECK(fs::path("//host/foo").has_root_path());
924 CHECK(!fs::path("//host").has_root_directory());
925 CHECK(fs::path("//host/foo").has_root_directory());
926 CHECK(!fs::path("//host").has_relative_path());
927 CHECK(fs::path("//host/foo").has_relative_path());
928 CHECK(fs::path("//host/foo").is_absolute());
929 CHECK(!fs::path("//host/foo").is_relative());
930 }
931 }
932
933 TEST_CASE("fs.path.gen - path generation", "[filesystem][path][fs.path.gen]")
934 {
935 // lexically_normal()
936 CHECK(fs::path("foo/./bar/..").lexically_normal() == "foo/");
937 CHECK(fs::path("foo/.///bar/../").lexically_normal() == "foo/");
938 CHECK(fs::path("/foo/../..").lexically_normal() == "/");
939 CHECK(fs::path("foo/..").lexically_normal() == ".");
940 CHECK(fs::path("ab/cd/ef/../../qw").lexically_normal() == "ab/qw");
941 CHECK(fs::path("a/b/../../../c").lexically_normal() == "../c");
942 CHECK(fs::path("../").lexically_normal() == "..");
943 #ifdef GHC_OS_WINDOWS
944 CHECK(fs::path("\\/\\///\\/").lexically_normal() == "/");
945 CHECK(fs::path("a/b/..\\//..///\\/../c\\\\/").lexically_normal() == "../c/");
946 CHECK(fs::path("..a/b/..\\//..///\\/../c\\\\/").lexically_normal() == "../c/");
947 CHECK(fs::path("..\\").lexically_normal() == "..");
948 #endif
949
950 // lexically_relative()
951 CHECK(fs::path("/a/d").lexically_relative("/a/b/c") == "../../d");
952 CHECK(fs::path("/a/b/c").lexically_relative("/a/d") == "../b/c");
953 CHECK(fs::path("a/b/c").lexically_relative("a") == "b/c");
954 CHECK(fs::path("a/b/c").lexically_relative("a/b/c/x/y") == "../..");
955 CHECK(fs::path("a/b/c").lexically_relative("a/b/c") == ".");
956 CHECK(fs::path("a/b").lexically_relative("c/d") == "../../a/b");
957 CHECK(fs::path("a/b").lexically_relative("a/") == "b");
958 if (has_host_root_name_support()) {
959 CHECK(fs::path("//host1/foo").lexically_relative("//host2.bar") == "");
960 }
961 #ifdef GHC_OS_WINDOWS
962 CHECK(fs::path("c:/foo").lexically_relative("/bar") == "");
963 CHECK(fs::path("c:foo").lexically_relative("c:/bar") == "");
964 CHECK(fs::path("foo").lexically_relative("/bar") == "");
965 CHECK(fs::path("c:/foo/bar.txt").lexically_relative("c:/foo/") == "bar.txt");
966 CHECK(fs::path("c:/foo/bar.txt").lexically_relative("C:/foo/") == "bar.txt");
967 #else
968 CHECK(fs::path("/foo").lexically_relative("bar") == "");
969 CHECK(fs::path("foo").lexically_relative("/bar") == "");
970 #endif
971
972 // lexically_proximate()
973 CHECK(fs::path("/a/d").lexically_proximate("/a/b/c") == "../../d");
974 if (has_host_root_name_support()) {
975 CHECK(fs::path("//host1/a/d").lexically_proximate("//host2/a/b/c") == "//host1/a/d");
976 }
977 CHECK(fs::path("a/d").lexically_proximate("/a/b/c") == "a/d");
978 #ifdef GHC_OS_WINDOWS
979 CHECK(fs::path("c:/a/d").lexically_proximate("c:/a/b/c") == "../../d");
980 CHECK(fs::path("c:/a/d").lexically_proximate("d:/a/b/c") == "c:/a/d");
981 CHECK(fs::path("c:/foo").lexically_proximate("/bar") == "c:/foo");
982 CHECK(fs::path("c:foo").lexically_proximate("c:/bar") == "c:foo");
983 CHECK(fs::path("foo").lexically_proximate("/bar") == "foo");
984 #else
985 CHECK(fs::path("/foo").lexically_proximate("bar") == "/foo");
986 CHECK(fs::path("foo").lexically_proximate("/bar") == "foo");
987 #endif
988 }
989
iterateResult(const fs::path & path)990 static std::string iterateResult(const fs::path& path)
991 {
992 std::ostringstream result;
993 for (fs::path::const_iterator i = path.begin(); i != path.end(); ++i) {
994 if (i != path.begin()) {
995 result << ",";
996 }
997 result << i->generic_string();
998 }
999 return result.str();
1000 }
1001
reverseIterateResult(const fs::path & path)1002 static std::string reverseIterateResult(const fs::path& path)
1003 {
1004 std::ostringstream result;
1005 fs::path::const_iterator iter = path.end();
1006 bool first = true;
1007 if (iter != path.begin()) {
1008 do {
1009 --iter;
1010 if (!first) {
1011 result << ",";
1012 }
1013 first = false;
1014 result << iter->generic_string();
1015 } while (iter != path.begin());
1016 }
1017 return result.str();
1018 }
1019
1020 TEST_CASE("fs.path.itr - path iterators", "[filesystem][path][fs.path.itr]")
1021 {
1022 CHECK(iterateResult(fs::path()).empty());
1023 CHECK("." == iterateResult(fs::path(".")));
1024 CHECK(".." == iterateResult(fs::path("..")));
1025 CHECK("foo" == iterateResult(fs::path("foo")));
1026 CHECK("/" == iterateResult(fs::path("/")));
1027 CHECK("/,foo" == iterateResult(fs::path("/foo")));
1028 CHECK("foo," == iterateResult(fs::path("foo/")));
1029 CHECK("/,foo," == iterateResult(fs::path("/foo/")));
1030 CHECK("foo,bar" == iterateResult(fs::path("foo/bar")));
1031 CHECK("/,foo,bar" == iterateResult(fs::path("/foo/bar")));
1032 #ifndef USE_STD_FS
1033 // ghc::filesystem enforces redundant slashes to be reduced to one
1034 CHECK("/,foo,bar" == iterateResult(fs::path("///foo/bar")));
1035 #else
1036 // typically std::filesystem keeps them
1037 CHECK("///,foo,bar" == iterateResult(fs::path("///foo/bar")));
1038 #endif
1039 CHECK("/,foo,bar," == iterateResult(fs::path("/foo/bar///")));
1040 CHECK("foo,.,bar,..," == iterateResult(fs::path("foo/.///bar/../")));
1041 #ifdef GHC_OS_WINDOWS
1042 CHECK("C:,/,foo" == iterateResult(fs::path("C:/foo")));
1043 #endif
1044
1045 CHECK(reverseIterateResult(fs::path()).empty());
1046 CHECK("." == reverseIterateResult(fs::path(".")));
1047 CHECK(".." == reverseIterateResult(fs::path("..")));
1048 CHECK("foo" == reverseIterateResult(fs::path("foo")));
1049 CHECK("/" == reverseIterateResult(fs::path("/")));
1050 CHECK("foo,/" == reverseIterateResult(fs::path("/foo")));
1051 CHECK(",foo" == reverseIterateResult(fs::path("foo/")));
1052 CHECK(",foo,/" == reverseIterateResult(fs::path("/foo/")));
1053 CHECK("bar,foo" == reverseIterateResult(fs::path("foo/bar")));
1054 CHECK("bar,foo,/" == reverseIterateResult(fs::path("/foo/bar")));
1055 #ifndef USE_STD_FS
1056 // ghc::filesystem enforces redundant slashes to be reduced to one
1057 CHECK("bar,foo,/" == reverseIterateResult(fs::path("///foo/bar")));
1058 #else
1059 // typically std::filesystem keeps them
1060 CHECK("bar,foo,///" == reverseIterateResult(fs::path("///foo/bar")));
1061 #endif
1062 CHECK(",bar,foo,/" == reverseIterateResult(fs::path("/foo/bar///")));
1063 CHECK(",..,bar,.,foo" == reverseIterateResult(fs::path("foo/.///bar/../")));
1064 #ifdef GHC_OS_WINDOWS
1065 CHECK("foo,/,C:" == reverseIterateResult(fs::path("C:/foo")));
1066 CHECK("foo,C:" == reverseIterateResult(fs::path("C:foo")));
1067 #endif
1068 {
1069 fs::path p1 = "/foo/bar/test.txt";
1070 fs::path p2;
1071 for (auto pe : p1) {
1072 p2 /= pe;
1073 }
1074 CHECK(p1 == p2);
1075 CHECK("bar" == *(--fs::path("/foo/bar").end()));
1076 auto p = fs::path("/foo/bar");
1077 auto pi = p.end();
1078 pi--;
1079 CHECK("bar" == *pi);
1080 }
1081
1082 if (has_host_root_name_support()) {
1083 CHECK("foo" == *(--fs::path("//host/foo").end()));
1084 auto p = fs::path("//host/foo");
1085 auto pi = p.end();
1086 pi--;
1087 CHECK("foo" == *pi);
1088 CHECK("//host" == iterateResult(fs::path("//host")));
1089 CHECK("//host,/,foo" == iterateResult(fs::path("//host/foo")));
1090 CHECK("//host" == reverseIterateResult(fs::path("//host")));
1091 CHECK("foo,/,//host" == reverseIterateResult(fs::path("//host/foo")));
1092 {
1093 fs::path p1 = "//host/foo/bar/test.txt";
1094 fs::path p2;
1095 for (auto pe : p1) {
1096 p2 /= pe;
1097 }
1098 CHECK(p1 == p2);
1099 }
1100 }
1101 }
1102
1103 TEST_CASE("fs.path.nonmember - path non-member functions", "[filesystem][path][fs.path.nonmember]")
1104 {
1105 fs::path p1("foo/bar");
1106 fs::path p2("some/other");
1107 fs::swap(p1, p2);
1108 CHECK(p1 == "some/other");
1109 CHECK(p2 == "foo/bar");
1110 CHECK(hash_value(p1));
1111 CHECK(p2 < p1);
1112 CHECK(p2 <= p1);
1113 CHECK(p1 <= p1);
1114 CHECK(!(p1 < p2));
1115 CHECK(!(p1 <= p2));
1116 CHECK(p1 > p2);
1117 CHECK(p1 >= p2);
1118 CHECK(p1 >= p1);
1119 CHECK(!(p2 > p1));
1120 CHECK(!(p2 >= p1));
1121 CHECK(p1 != p2);
1122 CHECK(p1 / p2 == "some/other/foo/bar");
1123 }
1124
1125 TEST_CASE("fs.path.io - path inserter and extractor", "[filesystem][path][fs.path.io]")
1126 {
1127 {
1128 std::ostringstream os;
1129 os << fs::path("/root/foo bar");
1130 #ifdef GHC_OS_WINDOWS
1131 CHECK(os.str() == "\"\\\\root\\\\foo bar\"");
1132 #else
1133 CHECK(os.str() == "\"/root/foo bar\"");
1134 #endif
1135 }
1136 {
1137 std::ostringstream os;
1138 os << fs::path("/root/foo\"bar");
1139 #ifdef GHC_OS_WINDOWS
1140 CHECK(os.str() == "\"\\\\root\\\\foo\\\"bar\"");
1141 #else
1142 CHECK(os.str() == "\"/root/foo\\\"bar\"");
1143 #endif
1144 }
1145
1146 {
1147 std::istringstream is("\"/root/foo bar\"");
1148 fs::path p;
1149 is >> p;
1150 CHECK(p == fs::path("/root/foo bar"));
1151 CHECK((is.flags() & std::ios_base::skipws) == std::ios_base::skipws);
1152 }
1153 {
1154 std::istringstream is("\"/root/foo bar\"");
1155 is >> std::noskipws;
1156 fs::path p;
1157 is >> p;
1158 CHECK(p == fs::path("/root/foo bar"));
1159 CHECK((is.flags() & std::ios_base::skipws) != std::ios_base::skipws);
1160 }
1161 {
1162 std::istringstream is("\"/root/foo\\\"bar\"");
1163 fs::path p;
1164 is >> p;
1165 CHECK(p == fs::path("/root/foo\"bar"));
1166 }
1167 {
1168 std::istringstream is("/root/foo");
1169 fs::path p;
1170 is >> p;
1171 CHECK(p == fs::path("/root/foo"));
1172 }
1173 }
1174
1175 TEST_CASE("fs.path.factory - path factory functions", "[filesystem][path][fs.path.factory]")
1176 {
1177 CHECK(fs::u8path("foo/bar") == fs::path("foo/bar"));
1178 CHECK(fs::u8path("foo/bar") == fs::path("foo/bar"));
1179 std::string str("/foo/bar/test.txt");
1180 CHECK(fs::u8path(str.begin(), str.end()) == str);
1181 }
1182
1183 TEST_CASE("fs.class.filesystem_error - class filesystem_error", "[filesystem][filesystem_error][fs.class.filesystem_error]")
1184 {
1185 std::error_code ec(1, std::system_category());
1186 fs::filesystem_error fse("None", std::error_code());
1187 fse = fs::filesystem_error("Some error", ec);
1188 CHECK(fse.code().value() == 1);
1189 CHECK(!std::string(fse.what()).empty());
1190 CHECK(fse.path1().empty());
1191 CHECK(fse.path2().empty());
1192 fse = fs::filesystem_error("Some error", fs::path("foo/bar"), ec);
1193 CHECK(!std::string(fse.what()).empty());
1194 CHECK(fse.path1() == "foo/bar");
1195 CHECK(fse.path2().empty());
1196 fse = fs::filesystem_error("Some error", fs::path("foo/bar"), fs::path("some/other"), ec);
1197 CHECK(!std::string(fse.what()).empty());
1198 CHECK(fse.path1() == "foo/bar");
1199 CHECK(fse.path2() == "some/other");
1200 }
1201
constExprOwnerAll()1202 constexpr fs::perms constExprOwnerAll()
1203 {
1204 return fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec;
1205 }
1206
1207 TEST_CASE("fs.enum - enum class perms", "[filesystem][enum][fs.enum]")
1208 {
1209 static_assert(constExprOwnerAll() == fs::perms::owner_all, "constexpr didn't result in owner_all");
1210 CHECK((fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec) == fs::perms::owner_all);
1211 CHECK((fs::perms::group_read | fs::perms::group_write | fs::perms::group_exec) == fs::perms::group_all);
1212 CHECK((fs::perms::others_read | fs::perms::others_write | fs::perms::others_exec) == fs::perms::others_all);
1213 CHECK((fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all) == fs::perms::all);
1214 CHECK((fs::perms::all | fs::perms::set_uid | fs::perms::set_gid | fs::perms::sticky_bit) == fs::perms::mask);
1215 }
1216
1217 TEST_CASE("fs.class.file_status - class file_status", "[filesystem][file_status][fs.class.file_status]")
1218 {
1219 {
1220 fs::file_status fs;
1221 CHECK(fs.type() == fs::file_type::none);
1222 CHECK(fs.permissions() == fs::perms::unknown);
1223 }
1224 {
1225 fs::file_status fs{fs::file_type::regular};
1226 CHECK(fs.type() == fs::file_type::regular);
1227 CHECK(fs.permissions() == fs::perms::unknown);
1228 }
1229 {
1230 fs::file_status fs{fs::file_type::directory, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
1231 CHECK(fs.type() == fs::file_type::directory);
1232 CHECK(fs.permissions() == fs::perms::owner_all);
1233 fs.type(fs::file_type::block);
1234 CHECK(fs.type() == fs::file_type::block);
1235 fs.type(fs::file_type::character);
1236 CHECK(fs.type() == fs::file_type::character);
1237 fs.type(fs::file_type::fifo);
1238 CHECK(fs.type() == fs::file_type::fifo);
1239 fs.type(fs::file_type::symlink);
1240 CHECK(fs.type() == fs::file_type::symlink);
1241 fs.type(fs::file_type::socket);
1242 CHECK(fs.type() == fs::file_type::socket);
1243 fs.permissions(fs.permissions() | fs::perms::group_all | fs::perms::others_all);
1244 CHECK(fs.permissions() == fs::perms::all);
1245 }
1246 {
1247 fs::file_status fst(fs::file_type::regular);
1248 fs::file_status fs(std::move(fst));
1249 CHECK(fs.type() == fs::file_type::regular);
1250 CHECK(fs.permissions() == fs::perms::unknown);
1251 }
1252 #if !defined(USE_STD_FS) || defined(GHC_FILESYSTEM_RUNNING_CPP20)
1253 {
1254 fs::file_status fs1{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
1255 fs::file_status fs2{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
1256 fs::file_status fs3{fs::file_type::directory, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
1257 fs::file_status fs4{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write};
1258 CHECK(fs1 == fs2);
1259 CHECK_FALSE(fs1 == fs3);
1260 CHECK_FALSE(fs1 == fs4);
1261 }
1262 #endif
1263 }
1264
1265 TEST_CASE("fs.dir.entry - class directory_entry", "[filesystem][directory_entry][fs.dir.entry]")
1266 {
1267 TemporaryDirectory t;
1268 std::error_code ec;
1269 auto de = fs::directory_entry(t.path());
1270 CHECK(de.path() == t.path());
1271 CHECK((fs::path)de == t.path());
1272 CHECK(de.exists());
1273 CHECK(!de.is_block_file());
1274 CHECK(!de.is_character_file());
1275 CHECK(de.is_directory());
1276 CHECK(!de.is_fifo());
1277 CHECK(!de.is_other());
1278 CHECK(!de.is_regular_file());
1279 CHECK(!de.is_socket());
1280 CHECK(!de.is_symlink());
1281 CHECK(de.status().type() == fs::file_type::directory);
1282 ec.clear();
1283 CHECK(de.status(ec).type() == fs::file_type::directory);
1284 CHECK(!ec);
1285 CHECK_NOTHROW(de.refresh());
1286 fs::directory_entry none;
1287 CHECK_THROWS_AS(none.refresh(), fs::filesystem_error);
1288 ec.clear();
1289 CHECK_NOTHROW(none.refresh(ec));
1290 CHECK(ec);
1291 CHECK_THROWS_AS(de.assign(""), fs::filesystem_error);
1292 ec.clear();
1293 CHECK_NOTHROW(de.assign("", ec));
1294 CHECK(ec);
1295 generateFile(t.path() / "foo", 1234);
1296 auto now = fs::file_time_type::clock::now();
1297 CHECK_NOTHROW(de.assign(t.path() / "foo"));
1298 CHECK_NOTHROW(de.assign(t.path() / "foo", ec));
1299 CHECK(!ec);
1300 de = fs::directory_entry(t.path() / "foo");
1301 CHECK(de.path() == t.path() / "foo");
1302 CHECK(de.exists());
1303 CHECK(de.exists(ec));
1304 CHECK(!ec);
1305 CHECK(!de.is_block_file());
1306 CHECK(!de.is_block_file(ec));
1307 CHECK(!ec);
1308 CHECK(!de.is_character_file());
1309 CHECK(!de.is_character_file(ec));
1310 CHECK(!ec);
1311 CHECK(!de.is_directory());
1312 CHECK(!de.is_directory(ec));
1313 CHECK(!ec);
1314 CHECK(!de.is_fifo());
1315 CHECK(!de.is_fifo(ec));
1316 CHECK(!ec);
1317 CHECK(!de.is_other());
1318 CHECK(!de.is_other(ec));
1319 CHECK(!ec);
1320 CHECK(de.is_regular_file());
1321 CHECK(de.is_regular_file(ec));
1322 CHECK(!ec);
1323 CHECK(!de.is_socket());
1324 CHECK(!de.is_socket(ec));
1325 CHECK(!ec);
1326 CHECK(!de.is_symlink());
1327 CHECK(!de.is_symlink(ec));
1328 CHECK(!ec);
1329 CHECK(de.file_size() == 1234);
1330 CHECK(de.file_size(ec) == 1234);
1331 CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(de.last_write_time() - now).count()) < 3);
1332 ec.clear();
1333 CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(de.last_write_time(ec) - now).count()) < 3);
1334 CHECK(!ec);
1335 #ifndef GHC_OS_WEB
1336 CHECK(de.hard_link_count() == 1);
1337 CHECK(de.hard_link_count(ec) == 1);
1338 CHECK(!ec);
1339 #endif
1340 CHECK_THROWS_AS(de.replace_filename("bar"), fs::filesystem_error);
1341 CHECK_NOTHROW(de.replace_filename("foo"));
1342 ec.clear();
1343 CHECK_NOTHROW(de.replace_filename("bar", ec));
1344 CHECK(ec);
1345 auto de2none = fs::directory_entry();
1346 ec.clear();
1347 #ifndef GHC_OS_WEB
1348 CHECK(de2none.hard_link_count(ec) == static_cast<uintmax_t>(-1));
1349 CHECK_THROWS_AS(de2none.hard_link_count(), fs::filesystem_error);
1350 CHECK(ec);
1351 #endif
1352 ec.clear();
1353 CHECK_NOTHROW(de2none.last_write_time(ec));
1354 CHECK_THROWS_AS(de2none.last_write_time(), fs::filesystem_error);
1355 CHECK(ec);
1356 ec.clear();
1357 CHECK_THROWS_AS(de2none.file_size(), fs::filesystem_error);
1358 CHECK(de2none.file_size(ec) == static_cast<uintmax_t>(-1));
1359 CHECK(ec);
1360 ec.clear();
1361 CHECK(de2none.status().type() == fs::file_type::not_found);
1362 CHECK(de2none.status(ec).type() == fs::file_type::not_found);
1363 CHECK(ec);
1364 generateFile(t.path() / "a");
1365 generateFile(t.path() / "b");
1366 auto d1 = fs::directory_entry(t.path() / "a");
1367 auto d2 = fs::directory_entry(t.path() / "b");
1368 CHECK(d1 < d2);
1369 CHECK(!(d2 < d1));
1370 CHECK(d1 <= d2);
1371 CHECK(!(d2 <= d1));
1372 CHECK(d2 > d1);
1373 CHECK(!(d1 > d2));
1374 CHECK(d2 >= d1);
1375 CHECK(!(d1 >= d2));
1376 CHECK(d1 != d2);
1377 CHECK(!(d2 != d2));
1378 CHECK(d1 == d1);
1379 CHECK(!(d1 == d2));
1380 }
1381
1382 TEST_CASE("fs.class.directory_iterator - class directory_iterator", "[filesystem][directory_iterator][fs.class.directory_iterator]")
1383 {
1384 {
1385 TemporaryDirectory t;
1386 CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
1387 generateFile(t.path() / "test", 1234);
1388 REQUIRE(fs::directory_iterator(t.path()) != fs::directory_iterator());
1389 auto iter = fs::directory_iterator(t.path());
1390 fs::directory_iterator iter2(iter);
1391 fs::directory_iterator iter3, iter4;
1392 iter3 = iter;
1393 CHECK(iter->path().filename() == "test");
1394 CHECK(iter2->path().filename() == "test");
1395 CHECK(iter3->path().filename() == "test");
1396 iter4 = std::move(iter3);
1397 CHECK(iter4->path().filename() == "test");
1398 CHECK(iter->path() == t.path() / "test");
1399 CHECK(!iter->is_symlink());
1400 CHECK(iter->is_regular_file());
1401 CHECK(!iter->is_directory());
1402 CHECK(iter->file_size() == 1234);
1403 CHECK(++iter == fs::directory_iterator());
1404 CHECK_THROWS_AS(fs::directory_iterator(t.path() / "non-existing"), fs::filesystem_error);
1405 int cnt = 0;
1406 for(auto de : fs::directory_iterator(t.path())) {
1407 ++cnt;
1408 }
1409 CHECK(cnt == 1);
1410 }
1411 if (is_symlink_creation_supported()) {
1412 TemporaryDirectory t;
1413 fs::path td = t.path() / "testdir";
1414 CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
1415 generateFile(t.path() / "test", 1234);
1416 fs::create_directory(td);
1417 REQUIRE_NOTHROW(fs::create_symlink(t.path() / "test", td / "testlink"));
1418 std::error_code ec;
1419 REQUIRE(fs::directory_iterator(td) != fs::directory_iterator());
1420 auto iter = fs::directory_iterator(td);
1421 CHECK(iter->path().filename() == "testlink");
1422 CHECK(iter->path() == td / "testlink");
1423 CHECK(iter->is_symlink());
1424 CHECK(iter->is_regular_file());
1425 CHECK(!iter->is_directory());
1426 CHECK(iter->file_size() == 1234);
1427 CHECK(++iter == fs::directory_iterator());
1428 }
1429 {
1430 // Issue #8: check if resources are freed when iterator reaches end()
1431 TemporaryDirectory t(TempOpt::change_path);
1432 auto p = fs::path("test/");
1433 fs::create_directory(p);
1434 auto iter = fs::directory_iterator(p);
1435 while (iter != fs::directory_iterator()) {
1436 ++iter;
1437 }
1438 CHECK(fs::remove_all(p) == 1);
1439 CHECK_NOTHROW(fs::create_directory(p));
1440 }
1441 }
1442
1443 TEST_CASE("fs.class.rec.dir.itr - class recursive_directory_iterator", "[filesystem][recursive_directory_iterator][fs.class.rec.dir.itr]")
1444 {
1445 {
1446 auto iter = fs::recursive_directory_iterator(".");
1447 iter.pop();
1448 CHECK(iter == fs::recursive_directory_iterator());
1449 }
1450 {
1451 TemporaryDirectory t;
1452 CHECK(fs::recursive_directory_iterator(t.path()) == fs::recursive_directory_iterator());
1453 generateFile(t.path() / "test", 1234);
1454 REQUIRE(fs::recursive_directory_iterator(t.path()) != fs::recursive_directory_iterator());
1455 auto iter = fs::recursive_directory_iterator(t.path());
1456 CHECK(iter->path().filename() == "test");
1457 CHECK(iter->path() == t.path() / "test");
1458 CHECK(!iter->is_symlink());
1459 CHECK(iter->is_regular_file());
1460 CHECK(!iter->is_directory());
1461 CHECK(iter->file_size() == 1234);
1462 CHECK(++iter == fs::recursive_directory_iterator());
1463 }
1464
1465 {
1466 TemporaryDirectory t;
1467 fs::path td = t.path() / "testdir";
1468 fs::create_directories(td);
1469 generateFile(td / "test", 1234);
1470 REQUIRE(fs::recursive_directory_iterator(t.path()) != fs::recursive_directory_iterator());
1471 auto iter = fs::recursive_directory_iterator(t.path());
1472
1473 CHECK(iter->path().filename() == "testdir");
1474 CHECK(iter->path() == td);
1475 CHECK(!iter->is_symlink());
1476 CHECK(!iter->is_regular_file());
1477 CHECK(iter->is_directory());
1478
1479 CHECK(++iter != fs::recursive_directory_iterator());
1480
1481 CHECK(iter->path().filename() == "test");
1482 CHECK(iter->path() == td / "test");
1483 CHECK(!iter->is_symlink());
1484 CHECK(iter->is_regular_file());
1485 CHECK(!iter->is_directory());
1486 CHECK(iter->file_size() == 1234);
1487
1488 CHECK(++iter == fs::recursive_directory_iterator());
1489 }
1490 {
1491 TemporaryDirectory t;
1492 std::error_code ec;
1493 CHECK(fs::recursive_directory_iterator(t.path(), fs::directory_options::none) == fs::recursive_directory_iterator());
1494 CHECK(fs::recursive_directory_iterator(t.path(), fs::directory_options::none, ec) == fs::recursive_directory_iterator());
1495 CHECK(!ec);
1496 CHECK(fs::recursive_directory_iterator(t.path(), ec) == fs::recursive_directory_iterator());
1497 CHECK(!ec);
1498 generateFile(t.path() / "test");
1499 fs::recursive_directory_iterator rd1(t.path());
1500 CHECK(fs::recursive_directory_iterator(rd1) != fs::recursive_directory_iterator());
1501 fs::recursive_directory_iterator rd2(t.path());
1502 CHECK(fs::recursive_directory_iterator(std::move(rd2)) != fs::recursive_directory_iterator());
1503 fs::recursive_directory_iterator rd3(t.path(), fs::directory_options::skip_permission_denied);
1504 CHECK(rd3.options() == fs::directory_options::skip_permission_denied);
1505 fs::recursive_directory_iterator rd4;
1506 rd4 = std::move(rd3);
1507 CHECK(rd4 != fs::recursive_directory_iterator());
1508 CHECK_NOTHROW(++rd4);
1509 CHECK(rd4 == fs::recursive_directory_iterator());
1510 fs::recursive_directory_iterator rd5;
1511 rd5 = rd4;
1512 }
1513 {
1514 TemporaryDirectory t(TempOpt::change_path);
1515 generateFile("a");
1516 fs::create_directory("d1");
1517 fs::create_directory("d1/d2");
1518 generateFile("d1/b");
1519 generateFile("d1/c");
1520 generateFile("d1/d2/d");
1521 generateFile("e");
1522 auto iter = fs::recursive_directory_iterator(".");
1523 std::multimap<std::string, int> result;
1524 while(iter != fs::recursive_directory_iterator()) {
1525 result.insert(std::make_pair(iter->path().generic_string(), iter.depth()));
1526 ++iter;
1527 }
1528 std::stringstream os;
1529 for(auto p : result) {
1530 os << "[" << p.first << "," << p.second << "],";
1531 }
1532 CHECK(os.str() == "[./a,0],[./d1,0],[./d1/b,1],[./d1/c,1],[./d1/d2,1],[./d1/d2/d,2],[./e,0],");
1533 }
1534 {
1535 TemporaryDirectory t(TempOpt::change_path);
1536 generateFile("a");
1537 fs::create_directory("d1");
1538 fs::create_directory("d1/d2");
1539 generateFile("d1/b");
1540 generateFile("d1/c");
1541 generateFile("d1/d2/d");
1542 generateFile("e");
1543 std::multiset<std::string> result;
1544 for(auto de : fs::recursive_directory_iterator(".")) {
1545 result.insert(de.path().generic_string());
1546 }
1547 std::stringstream os;
1548 for(auto p : result) {
1549 os << p << ",";
1550 }
1551 CHECK(os.str() == "./a,./d1,./d1/b,./d1/c,./d1/d2,./d1/d2/d,./e,");
1552 }
1553 {
1554 TemporaryDirectory t(TempOpt::change_path);
1555 generateFile("a");
1556 fs::create_directory("d1");
1557 fs::create_directory("d1/d2");
1558 generateFile("d1/d2/b");
1559 generateFile("e");
1560 auto iter = fs::recursive_directory_iterator(".");
1561 std::multimap<std::string, int> result;
1562 while(iter != fs::recursive_directory_iterator()) {
1563 result.insert(std::make_pair(iter->path().generic_string(), iter.depth()));
1564 if(iter->path() == "./d1/d2") {
1565 iter.disable_recursion_pending();
1566 }
1567 ++iter;
1568 }
1569 std::stringstream os;
1570 for(auto p : result) {
1571 os << "[" << p.first << "," << p.second << "],";
1572 }
1573 CHECK(os.str() == "[./a,0],[./d1,0],[./d1/d2,1],[./e,0],");
1574 }
1575 {
1576 TemporaryDirectory t(TempOpt::change_path);
1577 generateFile("a");
1578 fs::create_directory("d1");
1579 fs::create_directory("d1/d2");
1580 generateFile("d1/d2/b");
1581 generateFile("e");
1582 auto iter = fs::recursive_directory_iterator(".");
1583 std::multimap<std::string, int> result;
1584 while(iter != fs::recursive_directory_iterator()) {
1585 result.insert(std::make_pair(iter->path().generic_string(), iter.depth()));
1586 if(iter->path() == "./d1/d2") {
1587 iter.pop();
1588 }
1589 else {
1590 ++iter;
1591 }
1592 }
1593 std::stringstream os;
1594 for(auto p : result) {
1595 os << "[" << p.first << "," << p.second << "],";
1596 }
1597 CHECK(os.str() == "[./a,0],[./d1,0],[./d1/d2,1],[./e,0],");
1598 }
1599 if (is_symlink_creation_supported()) {
1600 TemporaryDirectory t(TempOpt::change_path);
1601 fs::create_directory("d1");
1602 generateFile("d1/a");
1603 fs::create_directory("d2");
1604 generateFile("d2/b");
1605 fs::create_directory_symlink("../d1", "d2/ds1");
1606 fs::create_directory_symlink("d3", "d2/ds2");
1607 std::multiset<std::string> result;
__anon154eedc40102()1608 REQUIRE_NOTHROW([&](){
1609 for (const auto& de : fs::recursive_directory_iterator("d2", fs::directory_options::follow_directory_symlink)) {
1610 result.insert(de.path().generic_string());
1611 }
1612 }());
1613 std::stringstream os;
1614 for(const auto& p : result) {
1615 os << p << ",";
1616 }
1617 CHECK(os.str() == "d2/b,d2/ds1,d2/ds1/a,d2/ds2,");
1618 os.str("");
1619 result.clear();
__anon154eedc40202()1620 REQUIRE_NOTHROW([&](){
1621 for (const auto& de : fs::recursive_directory_iterator("d2")) {
1622 result.insert(de.path().generic_string());
1623 }
1624 }());
1625 for(const auto& p : result) {
1626 os << p << ",";
1627 }
1628 CHECK(os.str() == "d2/b,d2/ds1,d2/ds2,");
1629 }
1630 }
1631
1632 TEST_CASE("fs.op.absolute - absolute", "[filesystem][operations][fs.op.absolute]")
1633 {
1634 CHECK(fs::absolute("") == fs::current_path() / "");
1635 CHECK(fs::absolute(fs::current_path()) == fs::current_path());
1636 CHECK(fs::absolute(".") == fs::current_path() / ".");
1637 CHECK((fs::absolute("..") == fs::current_path().parent_path() || fs::absolute("..") == fs::current_path() / ".."));
1638 CHECK(fs::absolute("foo") == fs::current_path() / "foo");
1639 std::error_code ec;
1640 CHECK(fs::absolute("", ec) == fs::current_path() / "");
1641 CHECK(!ec);
1642 CHECK(fs::absolute("foo", ec) == fs::current_path() / "foo");
1643 CHECK(!ec);
1644 }
1645
1646 TEST_CASE("fs.op.canonical - canonical", "[filesystem][operations][fs.op.canonical]")
1647 {
1648 CHECK_THROWS_AS(fs::canonical(""), fs::filesystem_error);
1649 {
1650 std::error_code ec;
1651 CHECK(fs::canonical("", ec) == "");
1652 CHECK(ec);
1653 }
1654 CHECK(fs::canonical(fs::current_path()) == fs::current_path());
1655
1656 CHECK(fs::canonical(".") == fs::current_path());
1657 CHECK(fs::canonical("..") == fs::current_path().parent_path());
1658 CHECK(fs::canonical("/") == fs::current_path().root_path());
1659 CHECK_THROWS_AS(fs::canonical("foo"), fs::filesystem_error);
1660 {
1661 std::error_code ec;
1662 CHECK_NOTHROW(fs::canonical("foo", ec));
1663 CHECK(ec);
1664 }
1665 {
1666 TemporaryDirectory t(TempOpt::change_path);
1667 auto dir = t.path() / "d0";
1668 fs::create_directories(dir / "d1");
1669 generateFile(dir / "f0");
1670 fs::path rel(dir.filename());
1671 CHECK(fs::canonical(dir) == dir);
1672 CHECK(fs::canonical(rel) == dir);
1673 CHECK(fs::canonical(dir / "f0") == dir / "f0");
1674 CHECK(fs::canonical(rel / "f0") == dir / "f0");
1675 CHECK(fs::canonical(rel / "./f0") == dir / "f0");
1676 CHECK(fs::canonical(rel / "d1/../f0") == dir / "f0");
1677 }
1678
1679 if (is_symlink_creation_supported()) {
1680 TemporaryDirectory t(TempOpt::change_path);
1681 fs::create_directory(t.path() / "dir1");
1682 generateFile(t.path() / "dir1/test1");
1683 fs::create_directory(t.path() / "dir2");
1684 fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym");
1685 CHECK(fs::canonical(t.path() / "dir2/dirSym/test1") == t.path() / "dir1/test1");
1686 }
1687 }
1688
1689 TEST_CASE("fs.op.copy - copy", "[filesystem][operations][fs.op.copy]")
1690 {
1691 {
1692 TemporaryDirectory t(TempOpt::change_path);
1693 std::error_code ec;
1694 fs::create_directory("dir1");
1695 generateFile("dir1/file1");
1696 generateFile("dir1/file2");
1697 fs::create_directory("dir1/dir2");
1698 generateFile("dir1/dir2/file3");
1699 CHECK_NOTHROW(fs::copy("dir1", "dir3"));
1700 CHECK(fs::exists("dir3/file1"));
1701 CHECK(fs::exists("dir3/file2"));
1702 CHECK(!fs::exists("dir3/dir2"));
1703 CHECK_NOTHROW(fs::copy("dir1", "dir4", fs::copy_options::recursive, ec));
1704 CHECK(!ec);
1705 CHECK(fs::exists("dir4/file1"));
1706 CHECK(fs::exists("dir4/file2"));
1707 CHECK(fs::exists("dir4/dir2/file3"));
1708 fs::create_directory("dir5");
1709 generateFile("dir5/file1");
1710 CHECK_THROWS_AS(fs::copy("dir1/file1", "dir5/file1"), fs::filesystem_error);
1711 CHECK_NOTHROW(fs::copy("dir1/file1", "dir5/file1", fs::copy_options::skip_existing));
1712 }
1713 if (is_symlink_creation_supported()) {
1714 TemporaryDirectory t(TempOpt::change_path);
1715 std::error_code ec;
1716 fs::create_directory("dir1");
1717 generateFile("dir1/file1");
1718 generateFile("dir1/file2");
1719 fs::create_directory("dir1/dir2");
1720 generateFile("dir1/dir2/file3");
1721 #ifdef TEST_LWG_2682_BEHAVIOUR
1722 REQUIRE_THROWS_AS(fs::copy("dir1", "dir3", fs::copy_options::create_symlinks | fs::copy_options::recursive), fs::filesystem_error);
1723 #else
1724 REQUIRE_NOTHROW(fs::copy("dir1", "dir3", fs::copy_options::create_symlinks | fs::copy_options::recursive));
1725 CHECK(!ec);
1726 CHECK(fs::exists("dir3/file1"));
1727 CHECK(fs::is_symlink("dir3/file1"));
1728 CHECK(fs::exists("dir3/file2"));
1729 CHECK(fs::is_symlink("dir3/file2"));
1730 CHECK(fs::exists("dir3/dir2/file3"));
1731 CHECK(fs::is_symlink("dir3/dir2/file3"));
1732 #endif
1733 }
1734 #ifndef GHC_OS_WEB
1735 {
1736 TemporaryDirectory t(TempOpt::change_path);
1737 std::error_code ec;
1738 fs::create_directory("dir1");
1739 generateFile("dir1/file1");
1740 generateFile("dir1/file2");
1741 fs::create_directory("dir1/dir2");
1742 generateFile("dir1/dir2/file3");
1743 auto f1hl = fs::hard_link_count("dir1/file1");
1744 auto f2hl = fs::hard_link_count("dir1/file2");
1745 auto f3hl = fs::hard_link_count("dir1/dir2/file3");
1746 CHECK_NOTHROW(fs::copy("dir1", "dir3", fs::copy_options::create_hard_links | fs::copy_options::recursive, ec));
1747 REQUIRE(!ec);
1748 CHECK(fs::exists("dir3/file1"));
1749 CHECK(fs::hard_link_count("dir1/file1") == f1hl + 1);
1750 CHECK(fs::exists("dir3/file2"));
1751 CHECK(fs::hard_link_count("dir1/file2") == f2hl + 1);
1752 CHECK(fs::exists("dir3/dir2/file3"));
1753 CHECK(fs::hard_link_count("dir1/dir2/file3") == f3hl + 1);
1754 }
1755 #endif
1756 }
1757
1758 TEST_CASE("fs.op.copy_file - copy_file", "[filesystem][operations][fs.op.copy_file]")
1759 {
1760 TemporaryDirectory t(TempOpt::change_path);
1761 std::error_code ec;
1762 generateFile("foo", 100);
1763 CHECK(!fs::exists("bar"));
1764 CHECK(fs::copy_file("foo", "bar"));
1765 CHECK(fs::exists("bar"));
1766 CHECK(fs::file_size("foo") == fs::file_size("bar"));
1767 CHECK(fs::copy_file("foo", "bar2", ec));
1768 CHECK(!ec);
1769 std::this_thread::sleep_for(std::chrono::seconds(1));
1770 generateFile("foo2", 200);
1771 CHECK(fs::copy_file("foo2", "bar", fs::copy_options::update_existing));
1772 CHECK(fs::file_size("bar") == 200);
1773 CHECK(!fs::copy_file("foo", "bar", fs::copy_options::update_existing));
1774 CHECK(fs::file_size("bar") == 200);
1775 CHECK(fs::copy_file("foo", "bar", fs::copy_options::overwrite_existing));
1776 CHECK(fs::file_size("bar") == 100);
1777 CHECK_THROWS_AS(fs::copy_file("foobar", "foobar2"), fs::filesystem_error);
1778 CHECK_NOTHROW(fs::copy_file("foobar", "foobar2", ec));
1779 CHECK(ec);
1780 CHECK(!fs::exists("foobar"));
1781 }
1782
1783 TEST_CASE("fs.op.copy_symlink - copy_symlink", "[filesystem][operations][fs.op.copy_symlink]")
1784 {
1785 TemporaryDirectory t(TempOpt::change_path);
1786 std::error_code ec;
1787 generateFile("foo");
1788 fs::create_directory("dir");
1789 if (is_symlink_creation_supported()) {
1790 fs::create_symlink("foo", "sfoo");
1791 fs::create_directory_symlink("dir", "sdir");
1792 CHECK_NOTHROW(fs::copy_symlink("sfoo", "sfooc"));
1793 CHECK(fs::exists("sfooc"));
1794 CHECK_NOTHROW(fs::copy_symlink("sfoo", "sfooc2", ec));
1795 CHECK(fs::exists("sfooc2"));
1796 CHECK(!ec);
1797 CHECK_NOTHROW(fs::copy_symlink("sdir", "sdirc"));
1798 CHECK(fs::exists("sdirc"));
1799 CHECK_NOTHROW(fs::copy_symlink("sdir", "sdirc2", ec));
1800 CHECK(fs::exists("sdirc2"));
1801 CHECK(!ec);
1802 }
1803 CHECK_THROWS_AS(fs::copy_symlink("bar", "barc"), fs::filesystem_error);
1804 CHECK_NOTHROW(fs::copy_symlink("bar", "barc", ec));
1805 CHECK(ec);
1806 }
1807
1808 TEST_CASE("fs.op.create_directories - create_directories", "[filesystem][operations][fs.op.create_directories]")
1809 {
1810 TemporaryDirectory t;
1811 fs::path p = t.path() / "testdir";
1812 fs::path p2 = p / "nested";
1813 REQUIRE(!fs::exists(p));
1814 REQUIRE(!fs::exists(p2));
1815 CHECK(fs::create_directories(p2));
1816 CHECK(fs::is_directory(p));
1817 CHECK(fs::is_directory(p2));
1818 CHECK(!fs::create_directories(p2));
1819 #ifdef TEST_LWG_2935_BEHAVIOUR
1820 INFO("This test expects LWG #2935 result conformance.");
1821 p = t.path() / "testfile";
1822 generateFile(p);
1823 CHECK(fs::is_regular_file(p));
1824 CHECK(!fs::is_directory(p));
1825 bool created = false;
1826 CHECK_NOTHROW((created = fs::create_directories(p)));
1827 CHECK(!created);
1828 CHECK(fs::is_regular_file(p));
1829 CHECK(!fs::is_directory(p));
1830 std::error_code ec;
1831 CHECK_NOTHROW((created = fs::create_directories(p, ec)));
1832 CHECK(!created);
1833 CHECK(!ec);
1834 CHECK(fs::is_regular_file(p));
1835 CHECK(!fs::is_directory(p));
1836 CHECK(!fs::create_directories(p, ec));
1837 #else
1838 INFO("This test expects conformance with P1164R1. (implemented by GCC with issue #86910.)");
1839 p = t.path() / "testfile";
1840 generateFile(p);
1841 CHECK(fs::is_regular_file(p));
1842 CHECK(!fs::is_directory(p));
1843 CHECK_THROWS_AS(fs::create_directories(p), fs::filesystem_error);
1844 CHECK(fs::is_regular_file(p));
1845 CHECK(!fs::is_directory(p));
1846 std::error_code ec;
1847 CHECK_NOTHROW(fs::create_directories(p, ec));
1848 CHECK(ec);
1849 CHECK(fs::is_regular_file(p));
1850 CHECK(!fs::is_directory(p));
1851 CHECK(!fs::create_directories(p, ec));
1852 #endif
1853 }
1854
1855 TEST_CASE("fs.op.create_directory - create_directory", "[filesystem][operations][fs.op.create_directory]")
1856 {
1857 TemporaryDirectory t;
1858 fs::path p = t.path() / "testdir";
1859 REQUIRE(!fs::exists(p));
1860 CHECK(fs::create_directory(p));
1861 CHECK(fs::is_directory(p));
1862 CHECK(!fs::is_regular_file(p));
1863 CHECK(fs::create_directory(p / "nested", p));
1864 CHECK(fs::is_directory(p / "nested"));
1865 CHECK(!fs::is_regular_file(p / "nested"));
1866 #ifdef TEST_LWG_2935_BEHAVIOUR
1867 INFO("This test expects LWG #2935 result conformance.");
1868 p = t.path() / "testfile";
1869 generateFile(p);
1870 CHECK(fs::is_regular_file(p));
1871 CHECK(!fs::is_directory(p));
1872 bool created = false;
1873 CHECK_NOTHROW((created = fs::create_directory(p)));
1874 CHECK(!created);
1875 CHECK(fs::is_regular_file(p));
1876 CHECK(!fs::is_directory(p));
1877 std::error_code ec;
1878 CHECK_NOTHROW((created = fs::create_directory(p, ec)));
1879 CHECK(!created);
1880 CHECK(!ec);
1881 CHECK(fs::is_regular_file(p));
1882 CHECK(!fs::is_directory(p));
1883 CHECK(!fs::create_directories(p, ec));
1884 #else
1885 INFO("This test expects conformance with P1164R1. (implemented by GCC with issue #86910.)");
1886 p = t.path() / "testfile";
1887 generateFile(p);
1888 CHECK(fs::is_regular_file(p));
1889 CHECK(!fs::is_directory(p));
1890 REQUIRE_THROWS_AS(fs::create_directory(p), fs::filesystem_error);
1891 CHECK(fs::is_regular_file(p));
1892 CHECK(!fs::is_directory(p));
1893 std::error_code ec;
1894 REQUIRE_NOTHROW(fs::create_directory(p, ec));
1895 CHECK(ec);
1896 CHECK(fs::is_regular_file(p));
1897 CHECK(!fs::is_directory(p));
1898 CHECK(!fs::create_directory(p, ec));
1899 #endif
1900 }
1901
1902 TEST_CASE("fs.op.create_directory_symlink - create_directory_symlink", "[filesystem][operations][fs.op.create_directory_symlink]")
1903 {
1904 if (is_symlink_creation_supported()) {
1905 TemporaryDirectory t;
1906 fs::create_directory(t.path() / "dir1");
1907 generateFile(t.path() / "dir1/test1");
1908 fs::create_directory(t.path() / "dir2");
1909 fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym");
1910 CHECK(fs::exists(t.path() / "dir2/dirSym"));
1911 CHECK(fs::is_symlink(t.path() / "dir2/dirSym"));
1912 CHECK(fs::exists(t.path() / "dir2/dirSym/test1"));
1913 CHECK(fs::is_regular_file(t.path() / "dir2/dirSym/test1"));
1914 CHECK_THROWS_AS(fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym"), fs::filesystem_error);
1915 std::error_code ec;
1916 CHECK_NOTHROW(fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym", ec));
1917 CHECK(ec);
1918 }
1919 }
1920
1921 TEST_CASE("fs.op.create_hard_link - create_hard_link", "[filesystem][operations][fs.op.create_hard_link]")
1922 {
1923 #ifndef GHC_OS_WEB
1924 TemporaryDirectory t(TempOpt::change_path);
1925 std::error_code ec;
1926 generateFile("foo", 1234);
1927 CHECK_NOTHROW(fs::create_hard_link("foo", "bar"));
1928 CHECK(fs::exists("bar"));
1929 CHECK(!fs::is_symlink("bar"));
1930 CHECK_NOTHROW(fs::create_hard_link("foo", "bar2", ec));
1931 CHECK(fs::exists("bar2"));
1932 CHECK(!fs::is_symlink("bar2"));
1933 CHECK(!ec);
1934 CHECK_THROWS_AS(fs::create_hard_link("nofoo", "bar"), fs::filesystem_error);
1935 CHECK_NOTHROW(fs::create_hard_link("nofoo", "bar", ec));
1936 CHECK(ec);
1937 #endif
1938 }
1939
1940 TEST_CASE("fs.op.create_symlink - create_symlink", "[filesystem][operations][fs.op.create_symlink]")
1941 {
1942 if (is_symlink_creation_supported()) {
1943 TemporaryDirectory t;
1944 fs::create_directory(t.path() / "dir1");
1945 generateFile(t.path() / "dir1/test1");
1946 fs::create_directory(t.path() / "dir2");
1947 fs::create_symlink(t.path() / "dir1/test1", t.path() / "dir2/fileSym");
1948 CHECK(fs::exists(t.path() / "dir2/fileSym"));
1949 CHECK(fs::is_symlink(t.path() / "dir2/fileSym"));
1950 CHECK(fs::exists(t.path() / "dir2/fileSym"));
1951 CHECK(fs::is_regular_file(t.path() / "dir2/fileSym"));
1952 CHECK_THROWS_AS(fs::create_symlink(t.path() / "dir1", t.path() / "dir2/fileSym"), fs::filesystem_error);
1953 std::error_code ec;
1954 CHECK_NOTHROW(fs::create_symlink(t.path() / "dir1", t.path() / "dir2/fileSym", ec));
1955 CHECK(ec);
1956 }
1957 }
1958
1959 TEST_CASE("fs.op.current_path - current_path", "[filesystem][operations][fs.op.current_path]")
1960 {
1961 TemporaryDirectory t;
1962 std::error_code ec;
1963 fs::path p1 = fs::current_path();
1964 CHECK_NOTHROW(fs::current_path(t.path()));
1965 CHECK(p1 != fs::current_path());
1966 CHECK_NOTHROW(fs::current_path(p1, ec));
1967 CHECK(!ec);
1968 CHECK_THROWS_AS(fs::current_path(t.path() / "foo"), fs::filesystem_error);
1969 CHECK(p1 == fs::current_path());
1970 CHECK_NOTHROW(fs::current_path(t.path() / "foo", ec));
1971 CHECK(ec);
1972 }
1973
1974 TEST_CASE("fs.op.equivalent - equivalent", "[filesystem][operations][fs.op.equivalent]")
1975 {
1976 TemporaryDirectory t(TempOpt::change_path);
1977 generateFile("foo", 1234);
1978 CHECK(fs::equivalent(t.path() / "foo", "foo"));
1979 if (is_symlink_creation_supported()) {
1980 std::error_code ec(42, std::system_category());
1981 fs::create_symlink("foo", "foo2");
1982 CHECK(fs::equivalent("foo", "foo2"));
1983 CHECK(fs::equivalent("foo", "foo2", ec));
1984 CHECK(!ec);
1985 }
1986 #ifdef TEST_LWG_2937_BEHAVIOUR
1987 INFO("This test expects LWG #2937 result conformance.");
1988 std::error_code ec;
1989 bool result = false;
1990 REQUIRE_THROWS_AS(fs::equivalent("foo", "foo3"), fs::filesystem_error);
1991 CHECK_NOTHROW(result = fs::equivalent("foo", "foo3", ec));
1992 CHECK(!result);
1993 CHECK(ec);
1994 ec.clear();
1995 CHECK_THROWS_AS(fs::equivalent("foo3", "foo"), fs::filesystem_error);
1996 CHECK_NOTHROW(result = fs::equivalent("foo3", "foo", ec));
1997 CHECK(!result);
1998 CHECK(ec);
1999 ec.clear();
2000 CHECK_THROWS_AS(fs::equivalent("foo3", "foo4"), fs::filesystem_error);
2001 CHECK_NOTHROW(result = fs::equivalent("foo3", "foo4", ec));
2002 CHECK(!result);
2003 CHECK(ec);
2004 #else
2005 INFO("This test expects conformance predating LWG #2937 result.");
2006 std::error_code ec;
2007 bool result = false;
2008 REQUIRE_NOTHROW(result = fs::equivalent("foo", "foo3"));
2009 CHECK(!result);
2010 CHECK_NOTHROW(result = fs::equivalent("foo", "foo3", ec));
2011 CHECK(!result);
2012 CHECK(!ec);
2013 ec.clear();
2014 CHECK_NOTHROW(result = fs::equivalent("foo3", "foo"));
2015 CHECK(!result);
2016 CHECK_NOTHROW(result = fs::equivalent("foo3", "foo", ec));
2017 CHECK(!result);
2018 CHECK(!ec);
2019 ec.clear();
2020 CHECK_THROWS_AS(result = fs::equivalent("foo4", "foo3"), fs::filesystem_error);
2021 CHECK(!result);
2022 CHECK_NOTHROW(result = fs::equivalent("foo4", "foo3", ec));
2023 CHECK(!result);
2024 CHECK(ec);
2025 #endif
2026 }
2027
2028 TEST_CASE("fs.op.exists - exists", "[filesystem][operations][fs.op.exists]")
2029 {
2030 TemporaryDirectory t(TempOpt::change_path);
2031 std::error_code ec;
2032 CHECK(!fs::exists(""));
2033 CHECK(!fs::exists("foo"));
2034 CHECK(!fs::exists("foo", ec));
2035 CHECK(!ec);
2036 ec = std::error_code(42, std::system_category());
2037 CHECK(!fs::exists("foo", ec));
2038 #if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
2039 CHECK(!fs::exists(u8"foo"));
2040 #endif
2041 CHECK(!ec);
2042 ec.clear();
2043 CHECK(fs::exists(t.path()));
2044 CHECK(fs::exists(t.path(), ec));
2045 CHECK(!ec);
2046 ec = std::error_code(42, std::system_category());
2047 CHECK(fs::exists(t.path(), ec));
2048 CHECK(!ec);
2049 #if defined(GHC_OS_WINDOWS) && !defined(GHC_FILESYSTEM_FWD)
2050 if (::GetFileAttributesW(L"C:\\fs-test") != INVALID_FILE_ATTRIBUTES) {
2051 CHECK(fs::exists("C:\\fs-test"));
2052 }
2053 #endif
2054 }
2055
2056 TEST_CASE("fs.op.file_size - file_size", "[filesystem][operations][fs.op.file_size]")
2057 {
2058 TemporaryDirectory t(TempOpt::change_path);
2059 std::error_code ec;
2060 generateFile("foo", 0);
2061 generateFile("bar", 1234);
2062 CHECK(fs::file_size("foo") == 0);
2063 ec = std::error_code(42, std::system_category());
2064 CHECK(fs::file_size("foo", ec) == 0);
2065 CHECK(!ec);
2066 ec.clear();
2067 CHECK(fs::file_size("bar") == 1234);
2068 ec = std::error_code(42, std::system_category());
2069 CHECK(fs::file_size("bar", ec) == 1234);
2070 CHECK(!ec);
2071 ec.clear();
2072 CHECK_THROWS_AS(fs::file_size("foobar"), fs::filesystem_error);
2073 CHECK(fs::file_size("foobar", ec) == static_cast<uintmax_t>(-1));
2074 CHECK(ec);
2075 ec.clear();
2076 }
2077
2078 #ifndef GHC_OS_WINDOWS
getHardlinkCount(const fs::path & p)2079 static uintmax_t getHardlinkCount(const fs::path& p)
2080 {
2081 struct stat st = {};
2082 auto rc = ::lstat(p.c_str(), &st);
2083 return rc == 0 ? st.st_nlink : ~0u;
2084 }
2085 #endif
2086
2087 TEST_CASE("fs.op.hard_link_count - hard_link_count", "[filesystem][operations][fs.op.hard_link_count]")
2088 {
2089 #ifndef GHC_OS_WEB
2090 TemporaryDirectory t(TempOpt::change_path);
2091 std::error_code ec;
2092 #ifdef GHC_OS_WINDOWS
2093 // windows doesn't implement "."/".." as hardlinks, so it
2094 // starts with 1 and subdirectories don't change the count
2095 CHECK(fs::hard_link_count(t.path()) == 1);
2096 fs::create_directory("dir");
2097 CHECK(fs::hard_link_count(t.path()) == 1);
2098 #else
2099 // unix/bsd/linux typically implements "."/".." as hardlinks
2100 // so an empty dir has 2 (from parent and the ".") and
2101 // adding a subdirectory adds one due to its ".."
2102 CHECK(fs::hard_link_count(t.path()) == getHardlinkCount(t.path()));
2103 fs::create_directory("dir");
2104 CHECK(fs::hard_link_count(t.path()) == getHardlinkCount(t.path()));
2105 #endif
2106 generateFile("foo");
2107 CHECK(fs::hard_link_count(t.path() / "foo") == 1);
2108 ec = std::error_code(42, std::system_category());
2109 CHECK(fs::hard_link_count(t.path() / "foo", ec) == 1);
2110 CHECK(!ec);
2111 CHECK_THROWS_AS(fs::hard_link_count(t.path() / "bar"), fs::filesystem_error);
2112 CHECK_NOTHROW(fs::hard_link_count(t.path() / "bar", ec));
2113 CHECK(ec);
2114 ec.clear();
2115 #else
2116 WARN("Test for unsupportet features are disabled on JS/Wasm target.");
2117 #endif
2118 }
2119
2120 class FileTypeMixFixture
2121 {
2122 public:
FileTypeMixFixture()2123 FileTypeMixFixture()
2124 : _t(TempOpt::change_path)
2125 , _hasFifo(false)
2126 , _hasSocket(false)
2127 {
2128 generateFile("regular");
2129 fs::create_directory("directory");
2130 if (is_symlink_creation_supported()) {
2131 fs::create_symlink("regular", "file_symlink");
2132 fs::create_directory_symlink("directory", "dir_symlink");
2133 }
2134 #if !defined(GHC_OS_WINDOWS) && !defined(GHC_OS_WEB)
2135 REQUIRE(::mkfifo("fifo", 0644) == 0);
2136 _hasFifo = true;
2137 struct ::sockaddr_un addr;
2138 addr.sun_family = AF_UNIX;
2139 std::strncpy(addr.sun_path, "socket", sizeof(addr.sun_path));
2140 int fd = socket(PF_UNIX, SOCK_STREAM, 0);
2141 bind(fd, (struct sockaddr*)&addr, sizeof addr);
2142 _hasSocket = true;
2143 #endif
2144 }
2145
~FileTypeMixFixture()2146 ~FileTypeMixFixture() {}
2147
has_fifo() const2148 bool has_fifo() const { return _hasFifo; }
2149
has_socket() const2150 bool has_socket() const { return _hasSocket; }
2151
block_path() const2152 fs::path block_path() const
2153 {
2154 std::error_code ec;
2155 if (fs::exists("/dev/sda", ec)) {
2156 return "/dev/sda";
2157 }
2158 else if (fs::exists("/dev/disk0", ec)) {
2159 return "/dev/disk0";
2160 }
2161 return fs::path();
2162 }
2163
character_path() const2164 fs::path character_path() const
2165 {
2166 std::error_code ec;
2167 if (fs::exists("/dev/null", ec)) {
2168 return "/dev/null";
2169 }
2170 else if (fs::exists("NUL", ec)) {
2171 return "NUL";
2172 }
2173 return fs::path();
2174 }
temp_path() const2175 fs::path temp_path() const { return _t.path(); }
2176
2177 private:
2178 TemporaryDirectory _t;
2179 bool _hasFifo;
2180 bool _hasSocket;
2181 };
2182
2183 TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_block_file - is_block_file", "[filesystem][operations][fs.op.is_block_file]")
2184 {
2185 std::error_code ec;
2186 CHECK(!fs::is_block_file("directory"));
2187 CHECK(!fs::is_block_file("regular"));
2188 if (is_symlink_creation_supported()) {
2189 CHECK(!fs::is_block_file("dir_symlink"));
2190 CHECK(!fs::is_block_file("file_symlink"));
2191 }
2192 CHECK((has_fifo() ? !fs::is_block_file("fifo") : true));
2193 CHECK((has_socket() ? !fs::is_block_file("socket") : true));
2194 CHECK((block_path().empty() ? true : fs::is_block_file(block_path())));
2195 CHECK((character_path().empty() ? true : !fs::is_block_file(character_path())));
2196 CHECK_NOTHROW(fs::is_block_file("notfound"));
2197 CHECK_NOTHROW(fs::is_block_file("notfound", ec));
2198 CHECK(ec);
2199 ec.clear();
2200 CHECK(!fs::is_block_file(fs::file_status(fs::file_type::none)));
2201 CHECK(!fs::is_block_file(fs::file_status(fs::file_type::not_found)));
2202 CHECK(!fs::is_block_file(fs::file_status(fs::file_type::regular)));
2203 CHECK(!fs::is_block_file(fs::file_status(fs::file_type::directory)));
2204 CHECK(!fs::is_block_file(fs::file_status(fs::file_type::symlink)));
2205 CHECK(fs::is_block_file(fs::file_status(fs::file_type::block)));
2206 CHECK(!fs::is_block_file(fs::file_status(fs::file_type::character)));
2207 CHECK(!fs::is_block_file(fs::file_status(fs::file_type::fifo)));
2208 CHECK(!fs::is_block_file(fs::file_status(fs::file_type::socket)));
2209 CHECK(!fs::is_block_file(fs::file_status(fs::file_type::unknown)));
2210 }
2211
2212 TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_character_file - is_character_file", "[filesystem][operations][fs.op.is_character_file]")
2213 {
2214 std::error_code ec;
2215 CHECK(!fs::is_character_file("directory"));
2216 CHECK(!fs::is_character_file("regular"));
2217 if (is_symlink_creation_supported()) {
2218 CHECK(!fs::is_character_file("dir_symlink"));
2219 CHECK(!fs::is_character_file("file_symlink"));
2220 }
2221 CHECK((has_fifo() ? !fs::is_character_file("fifo") : true));
2222 CHECK((has_socket() ? !fs::is_character_file("socket") : true));
2223 CHECK((block_path().empty() ? true : !fs::is_character_file(block_path())));
2224 CHECK((character_path().empty() ? true : fs::is_character_file(character_path())));
2225 CHECK_NOTHROW(fs::is_character_file("notfound"));
2226 CHECK_NOTHROW(fs::is_character_file("notfound", ec));
2227 CHECK(ec);
2228 ec.clear();
2229 CHECK(!fs::is_character_file(fs::file_status(fs::file_type::none)));
2230 CHECK(!fs::is_character_file(fs::file_status(fs::file_type::not_found)));
2231 CHECK(!fs::is_character_file(fs::file_status(fs::file_type::regular)));
2232 CHECK(!fs::is_character_file(fs::file_status(fs::file_type::directory)));
2233 CHECK(!fs::is_character_file(fs::file_status(fs::file_type::symlink)));
2234 CHECK(!fs::is_character_file(fs::file_status(fs::file_type::block)));
2235 CHECK(fs::is_character_file(fs::file_status(fs::file_type::character)));
2236 CHECK(!fs::is_character_file(fs::file_status(fs::file_type::fifo)));
2237 CHECK(!fs::is_character_file(fs::file_status(fs::file_type::socket)));
2238 CHECK(!fs::is_character_file(fs::file_status(fs::file_type::unknown)));
2239 }
2240
2241 TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_directory - is_directory", "[filesystem][operations][fs.op.is_directory]")
2242 {
2243 std::error_code ec;
2244 CHECK(fs::is_directory("directory"));
2245 CHECK(!fs::is_directory("regular"));
2246 if (is_symlink_creation_supported()) {
2247 CHECK(fs::is_directory("dir_symlink"));
2248 CHECK(!fs::is_directory("file_symlink"));
2249 }
2250 CHECK((has_fifo() ? !fs::is_directory("fifo") : true));
2251 CHECK((has_socket() ? !fs::is_directory("socket") : true));
2252 CHECK((block_path().empty() ? true : !fs::is_directory(block_path())));
2253 CHECK((character_path().empty() ? true : !fs::is_directory(character_path())));
2254 CHECK_NOTHROW(fs::is_directory("notfound"));
2255 CHECK_NOTHROW(fs::is_directory("notfound", ec));
2256 CHECK(ec);
2257 ec.clear();
2258 CHECK(!fs::is_directory(fs::file_status(fs::file_type::none)));
2259 CHECK(!fs::is_directory(fs::file_status(fs::file_type::not_found)));
2260 CHECK(!fs::is_directory(fs::file_status(fs::file_type::regular)));
2261 CHECK(fs::is_directory(fs::file_status(fs::file_type::directory)));
2262 CHECK(!fs::is_directory(fs::file_status(fs::file_type::symlink)));
2263 CHECK(!fs::is_directory(fs::file_status(fs::file_type::block)));
2264 CHECK(!fs::is_directory(fs::file_status(fs::file_type::character)));
2265 CHECK(!fs::is_directory(fs::file_status(fs::file_type::fifo)));
2266 CHECK(!fs::is_directory(fs::file_status(fs::file_type::socket)));
2267 CHECK(!fs::is_directory(fs::file_status(fs::file_type::unknown)));
2268 }
2269
2270 TEST_CASE("fs.op.is_empty - is_empty", "[filesystem][operations][fs.op.is_empty]")
2271 {
2272 TemporaryDirectory t(TempOpt::change_path);
2273 std::error_code ec;
2274 CHECK(fs::is_empty(t.path()));
2275 CHECK(fs::is_empty(t.path(), ec));
2276 CHECK(!ec);
2277 generateFile("foo", 0);
2278 generateFile("bar", 1234);
2279 CHECK(fs::is_empty("foo"));
2280 CHECK(fs::is_empty("foo", ec));
2281 CHECK(!ec);
2282 CHECK(!fs::is_empty("bar"));
2283 CHECK(!fs::is_empty("bar", ec));
2284 CHECK(!ec);
2285 CHECK_THROWS_AS(fs::is_empty("foobar"), fs::filesystem_error);
2286 bool result = false;
2287 CHECK_NOTHROW(result = fs::is_empty("foobar", ec));
2288 CHECK(!result);
2289 CHECK(ec);
2290 }
2291
2292 TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_fifo - is_fifo", "[filesystem][operations][fs.op.is_fifo]")
2293 {
2294 std::error_code ec;
2295 CHECK(!fs::is_fifo("directory"));
2296 CHECK(!fs::is_fifo("regular"));
2297 if (is_symlink_creation_supported()) {
2298 CHECK(!fs::is_fifo("dir_symlink"));
2299 CHECK(!fs::is_fifo("file_symlink"));
2300 }
2301 CHECK((has_fifo() ? fs::is_fifo("fifo") : true));
2302 CHECK((has_socket() ? !fs::is_fifo("socket") : true));
2303 CHECK((block_path().empty() ? true : !fs::is_fifo(block_path())));
2304 CHECK((character_path().empty() ? true : !fs::is_fifo(character_path())));
2305 CHECK_NOTHROW(fs::is_fifo("notfound"));
2306 CHECK_NOTHROW(fs::is_fifo("notfound", ec));
2307 CHECK(ec);
2308 ec.clear();
2309 CHECK(!fs::is_fifo(fs::file_status(fs::file_type::none)));
2310 CHECK(!fs::is_fifo(fs::file_status(fs::file_type::not_found)));
2311 CHECK(!fs::is_fifo(fs::file_status(fs::file_type::regular)));
2312 CHECK(!fs::is_fifo(fs::file_status(fs::file_type::directory)));
2313 CHECK(!fs::is_fifo(fs::file_status(fs::file_type::symlink)));
2314 CHECK(!fs::is_fifo(fs::file_status(fs::file_type::block)));
2315 CHECK(!fs::is_fifo(fs::file_status(fs::file_type::character)));
2316 CHECK(fs::is_fifo(fs::file_status(fs::file_type::fifo)));
2317 CHECK(!fs::is_fifo(fs::file_status(fs::file_type::socket)));
2318 CHECK(!fs::is_fifo(fs::file_status(fs::file_type::unknown)));
2319 }
2320
2321 TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_other - is_other", "[filesystem][operations][fs.op.is_other]")
2322 {
2323 std::error_code ec;
2324 CHECK(!fs::is_other("directory"));
2325 CHECK(!fs::is_other("regular"));
2326 if (is_symlink_creation_supported()) {
2327 CHECK(!fs::is_other("dir_symlink"));
2328 CHECK(!fs::is_other("file_symlink"));
2329 }
2330 CHECK((has_fifo() ? fs::is_other("fifo") : true));
2331 CHECK((has_socket() ? fs::is_other("socket") : true));
2332 CHECK((block_path().empty() ? true : fs::is_other(block_path())));
2333 CHECK((character_path().empty() ? true : fs::is_other(character_path())));
2334 CHECK_NOTHROW(fs::is_other("notfound"));
2335 CHECK_NOTHROW(fs::is_other("notfound", ec));
2336 CHECK(ec);
2337 ec.clear();
2338 CHECK(!fs::is_other(fs::file_status(fs::file_type::none)));
2339 CHECK(!fs::is_other(fs::file_status(fs::file_type::not_found)));
2340 CHECK(!fs::is_other(fs::file_status(fs::file_type::regular)));
2341 CHECK(!fs::is_other(fs::file_status(fs::file_type::directory)));
2342 CHECK(!fs::is_other(fs::file_status(fs::file_type::symlink)));
2343 CHECK(fs::is_other(fs::file_status(fs::file_type::block)));
2344 CHECK(fs::is_other(fs::file_status(fs::file_type::character)));
2345 CHECK(fs::is_other(fs::file_status(fs::file_type::fifo)));
2346 CHECK(fs::is_other(fs::file_status(fs::file_type::socket)));
2347 CHECK(fs::is_other(fs::file_status(fs::file_type::unknown)));
2348 }
2349
2350 TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_regular_file - is_regular_file", "[filesystem][operations][fs.op.is_regular_file]")
2351 {
2352 std::error_code ec;
2353 CHECK(!fs::is_regular_file("directory"));
2354 CHECK(fs::is_regular_file("regular"));
2355 if (is_symlink_creation_supported()) {
2356 CHECK(!fs::is_regular_file("dir_symlink"));
2357 CHECK(fs::is_regular_file("file_symlink"));
2358 }
2359 CHECK((has_fifo() ? !fs::is_regular_file("fifo") : true));
2360 CHECK((has_socket() ? !fs::is_regular_file("socket") : true));
2361 CHECK((block_path().empty() ? true : !fs::is_regular_file(block_path())));
2362 CHECK((character_path().empty() ? true : !fs::is_regular_file(character_path())));
2363 CHECK_NOTHROW(fs::is_regular_file("notfound"));
2364 CHECK_NOTHROW(fs::is_regular_file("notfound", ec));
2365 CHECK(ec);
2366 ec.clear();
2367 CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::none)));
2368 CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::not_found)));
2369 CHECK(fs::is_regular_file(fs::file_status(fs::file_type::regular)));
2370 CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::directory)));
2371 CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::symlink)));
2372 CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::block)));
2373 CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::character)));
2374 CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::fifo)));
2375 CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::socket)));
2376 CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::unknown)));
2377 }
2378
2379 TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_socket - is_socket", "[filesystem][operations][fs.op.is_socket]")
2380 {
2381 std::error_code ec;
2382 CHECK(!fs::is_socket("directory"));
2383 CHECK(!fs::is_socket("regular"));
2384 if (is_symlink_creation_supported()) {
2385 CHECK(!fs::is_socket("dir_symlink"));
2386 CHECK(!fs::is_socket("file_symlink"));
2387 }
2388 CHECK((has_fifo() ? !fs::is_socket("fifo") : true));
2389 CHECK((has_socket() ? fs::is_socket("socket") : true));
2390 CHECK((block_path().empty() ? true : !fs::is_socket(block_path())));
2391 CHECK((character_path().empty() ? true : !fs::is_socket(character_path())));
2392 CHECK_NOTHROW(fs::is_socket("notfound"));
2393 CHECK_NOTHROW(fs::is_socket("notfound", ec));
2394 CHECK(ec);
2395 ec.clear();
2396 CHECK(!fs::is_socket(fs::file_status(fs::file_type::none)));
2397 CHECK(!fs::is_socket(fs::file_status(fs::file_type::not_found)));
2398 CHECK(!fs::is_socket(fs::file_status(fs::file_type::regular)));
2399 CHECK(!fs::is_socket(fs::file_status(fs::file_type::directory)));
2400 CHECK(!fs::is_socket(fs::file_status(fs::file_type::symlink)));
2401 CHECK(!fs::is_socket(fs::file_status(fs::file_type::block)));
2402 CHECK(!fs::is_socket(fs::file_status(fs::file_type::character)));
2403 CHECK(!fs::is_socket(fs::file_status(fs::file_type::fifo)));
2404 CHECK(fs::is_socket(fs::file_status(fs::file_type::socket)));
2405 CHECK(!fs::is_socket(fs::file_status(fs::file_type::unknown)));
2406 }
2407
2408 TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_symlink - is_symlink", "[filesystem][operations][fs.op.is_symlink]")
2409 {
2410 std::error_code ec;
2411 CHECK(!fs::is_symlink("directory"));
2412 CHECK(!fs::is_symlink("regular"));
2413 if (is_symlink_creation_supported()) {
2414 CHECK(fs::is_symlink("dir_symlink"));
2415 CHECK(fs::is_symlink("file_symlink"));
2416 }
2417 CHECK((has_fifo() ? !fs::is_symlink("fifo") : true));
2418 CHECK((has_socket() ? !fs::is_symlink("socket") : true));
2419 CHECK((block_path().empty() ? true : !fs::is_symlink(block_path())));
2420 CHECK((character_path().empty() ? true : !fs::is_symlink(character_path())));
2421 CHECK_NOTHROW(fs::is_symlink("notfound"));
2422 CHECK_NOTHROW(fs::is_symlink("notfound", ec));
2423 CHECK(ec);
2424 ec.clear();
2425 CHECK(!fs::is_symlink(fs::file_status(fs::file_type::none)));
2426 CHECK(!fs::is_symlink(fs::file_status(fs::file_type::not_found)));
2427 CHECK(!fs::is_symlink(fs::file_status(fs::file_type::regular)));
2428 CHECK(!fs::is_symlink(fs::file_status(fs::file_type::directory)));
2429 CHECK(fs::is_symlink(fs::file_status(fs::file_type::symlink)));
2430 CHECK(!fs::is_symlink(fs::file_status(fs::file_type::block)));
2431 CHECK(!fs::is_symlink(fs::file_status(fs::file_type::character)));
2432 CHECK(!fs::is_symlink(fs::file_status(fs::file_type::fifo)));
2433 CHECK(!fs::is_symlink(fs::file_status(fs::file_type::socket)));
2434 CHECK(!fs::is_symlink(fs::file_status(fs::file_type::unknown)));
2435 }
2436
2437 #ifndef GHC_OS_WEB
timeFromString(const std::string & str)2438 static fs::file_time_type timeFromString(const std::string& str)
2439 {
2440 struct ::tm tm;
2441 ::memset(&tm, 0, sizeof(::tm));
2442 std::istringstream is(str);
2443 is >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
2444 if (is.fail()) {
2445 throw std::exception();
2446 }
2447 return from_time_t<fs::file_time_type>(std::mktime(&tm));
2448 }
2449 #endif
2450
2451 TEST_CASE("fs.op.last_write_time - last_write_time", "[filesystem][operations][fs.op.last_write_time]")
2452 {
2453 TemporaryDirectory t(TempOpt::change_path);
2454 std::error_code ec;
2455 fs::file_time_type ft;
2456 generateFile("foo");
2457 auto now = fs::file_time_type::clock::now();
2458 CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time(t.path()) - now).count()) < 3);
2459 CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - now).count()) < 3);
2460 CHECK_THROWS_AS(fs::last_write_time("bar"), fs::filesystem_error);
2461 CHECK_NOTHROW(ft = fs::last_write_time("bar", ec));
2462 CHECK(ft == fs::file_time_type::min());
2463 CHECK(ec);
2464 ec.clear();
2465 if (is_symlink_creation_supported()) {
2466 std::this_thread::sleep_for(std::chrono::seconds(1));
2467 fs::create_symlink("foo", "foo2");
2468 ft = fs::last_write_time("foo");
2469 // checks that the time of the symlink is fetched
2470 CHECK(ft == fs::last_write_time("foo2"));
2471 }
2472 #ifndef GHC_OS_WEB
2473 auto nt = timeFromString("2015-10-21T04:30:00");
2474 CHECK_NOTHROW(fs::last_write_time(t.path() / "foo", nt));
2475 CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
2476 nt = timeFromString("2015-10-21T04:29:00");
2477 CHECK_NOTHROW(fs::last_write_time("foo", nt, ec));
2478 CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
2479 CHECK(!ec);
2480 CHECK_THROWS_AS(fs::last_write_time("bar", nt), fs::filesystem_error);
2481 CHECK_NOTHROW(fs::last_write_time("bar", nt, ec));
2482 CHECK(ec);
2483 #endif
2484 }
2485
2486 TEST_CASE("fs.op.permissions - permissions", "[filesystem][operations][fs.op.permissions]")
2487 {
2488 TemporaryDirectory t(TempOpt::change_path);
2489 std::error_code ec;
2490 generateFile("foo", 512);
2491 auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write;
2492 CHECK_NOTHROW(fs::permissions("foo", allWrite, fs::perm_options::remove));
2493 CHECK((fs::status("foo").permissions() & fs::perms::owner_write) != fs::perms::owner_write);
2494 #if !defined(GHC_OS_WINDOWS)
2495 if (geteuid() != 0)
2496 #endif
2497 {
2498 CHECK_THROWS_AS(fs::resize_file("foo", 1024), fs::filesystem_error);
2499 CHECK(fs::file_size("foo") == 512);
2500 }
2501 CHECK_NOTHROW(fs::permissions("foo", fs::perms::owner_write, fs::perm_options::add));
2502 CHECK((fs::status("foo").permissions() & fs::perms::owner_write) == fs::perms::owner_write);
2503 CHECK_NOTHROW(fs::resize_file("foo", 2048));
2504 CHECK(fs::file_size("foo") == 2048);
2505 CHECK_THROWS_AS(fs::permissions("bar", fs::perms::owner_write, fs::perm_options::add), fs::filesystem_error);
2506 CHECK_NOTHROW(fs::permissions("bar", fs::perms::owner_write, fs::perm_options::add, ec));
2507 CHECK(ec);
2508 CHECK_THROWS_AS(fs::permissions("bar", fs::perms::owner_write, static_cast<fs::perm_options>(0)), fs::filesystem_error);
2509 }
2510
2511 TEST_CASE("fs.op.proximate - proximate", "[filesystem][operations][fs.op.proximate]")
2512 {
2513 std::error_code ec;
2514 CHECK(fs::proximate("/a/d", "/a/b/c") == "../../d");
2515 CHECK(fs::proximate("/a/d", "/a/b/c", ec) == "../../d");
2516 CHECK(!ec);
2517 CHECK(fs::proximate("/a/b/c", "/a/d") == "../b/c");
2518 CHECK(fs::proximate("/a/b/c", "/a/d", ec) == "../b/c");
2519 CHECK(!ec);
2520 CHECK(fs::proximate("a/b/c", "a") == "b/c");
2521 CHECK(fs::proximate("a/b/c", "a", ec) == "b/c");
2522 CHECK(!ec);
2523 CHECK(fs::proximate("a/b/c", "a/b/c/x/y") == "../..");
2524 CHECK(fs::proximate("a/b/c", "a/b/c/x/y", ec) == "../..");
2525 CHECK(!ec);
2526 CHECK(fs::proximate("a/b/c", "a/b/c") == ".");
2527 CHECK(fs::proximate("a/b/c", "a/b/c", ec) == ".");
2528 CHECK(!ec);
2529 CHECK(fs::proximate("a/b", "c/d") == "../../a/b");
2530 CHECK(fs::proximate("a/b", "c/d", ec) == "../../a/b");
2531 CHECK(!ec);
2532 #ifndef GHC_OS_WINDOWS
2533 if (has_host_root_name_support()) {
2534 CHECK(fs::proximate("//host1/a/d", "//host2/a/b/c") == "//host1/a/d");
2535 CHECK(fs::proximate("//host1/a/d", "//host2/a/b/c", ec) == "//host1/a/d");
2536 CHECK(!ec);
2537 }
2538 #endif
2539 }
2540
2541 TEST_CASE("fs.op.read_symlink - read_symlink", "[filesystem][operations][fs.op.read_symlink]")
2542 {
2543 if (is_symlink_creation_supported()) {
2544 TemporaryDirectory t(TempOpt::change_path);
2545 std::error_code ec;
2546 generateFile("foo");
2547 fs::create_symlink(t.path() / "foo", "bar");
2548 CHECK(fs::read_symlink("bar") == t.path() / "foo");
2549 CHECK(fs::read_symlink("bar", ec) == t.path() / "foo");
2550 CHECK(!ec);
2551 CHECK_THROWS_AS(fs::read_symlink("foobar"), fs::filesystem_error);
2552 CHECK(fs::read_symlink("foobar", ec) == fs::path());
2553 CHECK(ec);
2554 }
2555 }
2556
2557 TEST_CASE("fs.op.relative - relative", "[filesystem][operations][fs.op.relative]")
2558 {
2559 CHECK(fs::relative("/a/d", "/a/b/c") == "../../d");
2560 CHECK(fs::relative("/a/b/c", "/a/d") == "../b/c");
2561 CHECK(fs::relative("a/b/c", "a") == "b/c");
2562 CHECK(fs::relative("a/b/c", "a/b/c/x/y") == "../..");
2563 CHECK(fs::relative("a/b/c", "a/b/c") == ".");
2564 CHECK(fs::relative("a/b", "c/d") == "../../a/b");
2565 std::error_code ec;
2566 CHECK(fs::relative(fs::current_path() / "foo", ec) == "foo");
2567 CHECK(!ec);
2568 }
2569
2570 TEST_CASE("fs.op.remove - remove", "[filesystem][operations][fs.op.remove]")
2571 {
2572 TemporaryDirectory t(TempOpt::change_path);
2573 std::error_code ec;
2574 generateFile("foo");
2575 CHECK(fs::remove("foo"));
2576 CHECK(!fs::exists("foo"));
2577 CHECK(!fs::remove("foo"));
2578 generateFile("foo");
2579 CHECK(fs::remove("foo", ec));
2580 CHECK(!fs::exists("foo"));
2581 if (is_symlink_creation_supported()) {
2582 generateFile("foo");
2583 fs::create_symlink("foo", "bar");
2584 CHECK(fs::exists(fs::symlink_status("bar")));
2585 CHECK(fs::remove("bar", ec));
2586 CHECK(fs::exists("foo"));
2587 CHECK(!fs::exists(fs::symlink_status("bar")));
2588 }
2589 CHECK(!fs::remove("bar"));
2590 CHECK(!fs::remove("bar", ec));
2591 CHECK(!ec);
2592 }
2593
2594 TEST_CASE("fs.op.remove_all - remove_all", "[filesystem][operations][fs.op.remove_all]")
2595 {
2596 TemporaryDirectory t(TempOpt::change_path);
2597 std::error_code ec;
2598 generateFile("foo");
2599 CHECK(fs::remove_all("foo", ec) == 1);
2600 CHECK(!ec);
2601 ec.clear();
2602 CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
2603 fs::create_directories("dir1/dir1a");
2604 fs::create_directories("dir1/dir1b");
2605 generateFile("dir1/dir1a/f1");
2606 generateFile("dir1/dir1b/f2");
2607 CHECK_NOTHROW(fs::remove_all("dir1/non-existing", ec));
2608 CHECK(!ec);
2609 CHECK(fs::remove_all("dir1/non-existing", ec) == 0);
2610 CHECK(fs::remove_all("dir1") == 5);
2611 CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
2612 }
2613
2614 TEST_CASE("fs.op.rename - rename", "[filesystem][operations][fs.op.rename]")
2615 {
2616 TemporaryDirectory t(TempOpt::change_path);
2617 std::error_code ec;
2618 generateFile("foo", 123);
2619 fs::create_directory("dir1");
2620 CHECK_NOTHROW(fs::rename("foo", "bar"));
2621 CHECK(!fs::exists("foo"));
2622 CHECK(fs::exists("bar"));
2623 CHECK_NOTHROW(fs::rename("dir1", "dir2"));
2624 CHECK(fs::exists("dir2"));
2625 generateFile("foo2", 42);
2626 CHECK_NOTHROW(fs::rename("bar", "foo2"));
2627 CHECK(fs::exists("foo2"));
2628 CHECK(fs::file_size("foo2") == 123u);
2629 CHECK(!fs::exists("bar"));
2630 CHECK_NOTHROW(fs::rename("foo2", "foo", ec));
2631 CHECK(!ec);
2632 CHECK_THROWS_AS(fs::rename("foobar", "barfoo"), fs::filesystem_error);
2633 CHECK_NOTHROW(fs::rename("foobar", "barfoo", ec));
2634 CHECK(ec);
2635 CHECK(!fs::exists("barfoo"));
2636 }
2637
2638 TEST_CASE("fs.op.resize_file - resize_file", "[filesystem][operations][fs.op.resize_file]")
2639 {
2640 TemporaryDirectory t(TempOpt::change_path);
2641 std::error_code ec;
2642 generateFile("foo", 1024);
2643 CHECK(fs::file_size("foo") == 1024);
2644 CHECK_NOTHROW(fs::resize_file("foo", 2048));
2645 CHECK(fs::file_size("foo") == 2048);
2646 CHECK_NOTHROW(fs::resize_file("foo", 1000, ec));
2647 CHECK(!ec);
2648 CHECK(fs::file_size("foo") == 1000);
2649 CHECK_THROWS_AS(fs::resize_file("bar", 2048), fs::filesystem_error);
2650 CHECK(!fs::exists("bar"));
2651 CHECK_NOTHROW(fs::resize_file("bar", 4096, ec));
2652 CHECK(ec);
2653 CHECK(!fs::exists("bar"));
2654 }
2655
2656 TEST_CASE("fs.op.space - space", "[filesystem][operations][fs.op.space]")
2657 {
2658 {
2659 fs::space_info si;
2660 CHECK_NOTHROW(si = fs::space(fs::current_path()));
2661 CHECK(si.capacity > 1024 * 1024);
2662 CHECK(si.capacity > si.free);
2663 CHECK(si.free >= si.available);
2664 }
2665 {
2666 std::error_code ec;
2667 fs::space_info si;
2668 CHECK_NOTHROW(si = fs::space(fs::current_path(), ec));
2669 CHECK(si.capacity > 1024 * 1024);
2670 CHECK(si.capacity > si.free);
2671 CHECK(si.free >= si.available);
2672 CHECK(!ec);
2673 }
2674 #ifndef GHC_OS_WEB // statvfs under emscripten always returns a result, so this tests would fail
2675 {
2676 std::error_code ec;
2677 fs::space_info si;
2678 CHECK_NOTHROW(si = fs::space("foobar42", ec));
2679 CHECK(si.capacity == static_cast<uintmax_t>(-1));
2680 CHECK(si.free == static_cast<uintmax_t>(-1));
2681 CHECK(si.available == static_cast<uintmax_t>(-1));
2682 CHECK(ec);
2683 }
2684 CHECK_THROWS_AS(fs::space("foobar42"), fs::filesystem_error);
2685 #endif
2686 }
2687
2688 TEST_CASE("fs.op.status - status", "[filesystem][operations][fs.op.status]")
2689 {
2690 TemporaryDirectory t(TempOpt::change_path);
2691 std::error_code ec;
2692 fs::file_status fs;
2693 CHECK_NOTHROW(fs = fs::status("foo"));
2694 CHECK(fs.type() == fs::file_type::not_found);
2695 CHECK(fs.permissions() == fs::perms::unknown);
2696 CHECK_NOTHROW(fs = fs::status("bar", ec));
2697 CHECK(fs.type() == fs::file_type::not_found);
2698 CHECK(fs.permissions() == fs::perms::unknown);
2699 CHECK(ec);
2700 ec.clear();
2701 fs = fs::status(t.path());
2702 CHECK(fs.type() == fs::file_type::directory);
2703 CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
2704 generateFile("foobar");
2705 fs = fs::status(t.path() / "foobar");
2706 CHECK(fs.type() == fs::file_type::regular);
2707 CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
2708 if (is_symlink_creation_supported()) {
2709 fs::create_symlink(t.path() / "foobar", t.path() / "barfoo");
2710 fs = fs::status(t.path() / "barfoo");
2711 CHECK(fs.type() == fs::file_type::regular);
2712 CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
2713 }
2714 }
2715
2716 TEST_CASE("fs.op.status_known - status_known", "[filesystem][operations][fs.op.status_known]")
2717 {
2718 CHECK(!fs::status_known(fs::file_status()));
2719 CHECK(fs::status_known(fs::file_status(fs::file_type::not_found)));
2720 CHECK(fs::status_known(fs::file_status(fs::file_type::regular)));
2721 CHECK(fs::status_known(fs::file_status(fs::file_type::directory)));
2722 CHECK(fs::status_known(fs::file_status(fs::file_type::symlink)));
2723 CHECK(fs::status_known(fs::file_status(fs::file_type::character)));
2724 CHECK(fs::status_known(fs::file_status(fs::file_type::fifo)));
2725 CHECK(fs::status_known(fs::file_status(fs::file_type::socket)));
2726 CHECK(fs::status_known(fs::file_status(fs::file_type::unknown)));
2727 }
2728
2729 TEST_CASE("fs.op.symlink_status - symlink_status", "[filesystem][operations][fs.op.symlink_status]")
2730 {
2731 TemporaryDirectory t(TempOpt::change_path);
2732 std::error_code ec;
2733 fs::file_status fs;
2734 CHECK_NOTHROW(fs = fs::symlink_status("foo"));
2735 CHECK(fs.type() == fs::file_type::not_found);
2736 CHECK(fs.permissions() == fs::perms::unknown);
2737 CHECK_NOTHROW(fs = fs::symlink_status("bar", ec));
2738 CHECK(fs.type() == fs::file_type::not_found);
2739 CHECK(fs.permissions() == fs::perms::unknown);
2740 CHECK(ec);
2741 ec.clear();
2742 fs = fs::symlink_status(t.path());
2743 CHECK(fs.type() == fs::file_type::directory);
2744 CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
2745 generateFile("foobar");
2746 fs = fs::symlink_status(t.path() / "foobar");
2747 CHECK(fs.type() == fs::file_type::regular);
2748 CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
2749 if (is_symlink_creation_supported()) {
2750 fs::create_symlink(t.path() / "foobar", t.path() / "barfoo");
2751 fs = fs::symlink_status(t.path() / "barfoo");
2752 CHECK(fs.type() == fs::file_type::symlink);
2753 }
2754 }
2755
2756 TEST_CASE("fs.op.temp_dir_path - temporary_directory_path", "[filesystem][operations][fs.op.temp_dir_path]")
2757 {
2758 std::error_code ec;
2759 CHECK_NOTHROW(fs::exists(fs::temp_directory_path()));
2760 CHECK_NOTHROW(fs::exists(fs::temp_directory_path(ec)));
2761 CHECK(!fs::temp_directory_path().empty());
2762 CHECK(!ec);
2763 }
2764
2765 TEST_CASE("fs.op.weakly_canonical - weakly_canonical", "[filesystem][operations][fs.op.weakly_canonical]")
2766 {
2767 INFO("This might fail on std::implementations that return fs::current_path() for fs::canonical(\"\")");
2768 CHECK(fs::weakly_canonical("") == ".");
2769 if(fs::weakly_canonical("") == ".") {
2770 CHECK(fs::weakly_canonical("foo/bar") == "foo/bar");
2771 CHECK(fs::weakly_canonical("foo/./bar") == "foo/bar");
2772 CHECK(fs::weakly_canonical("foo/../bar") == "bar");
2773 }
2774 else {
2775 CHECK(fs::weakly_canonical("foo/bar") == fs::current_path() / "foo/bar");
2776 CHECK(fs::weakly_canonical("foo/./bar") == fs::current_path() / "foo/bar");
2777 CHECK(fs::weakly_canonical("foo/../bar") == fs::current_path() / "bar");
2778 }
2779
2780 {
2781 TemporaryDirectory t(TempOpt::change_path);
2782 auto dir = t.path() / "d0";
2783 fs::create_directories(dir / "d1");
2784 generateFile(dir / "f0");
2785 fs::path rel(dir.filename());
2786 CHECK(fs::weakly_canonical(dir) == dir);
2787 CHECK(fs::weakly_canonical(rel) == dir);
2788 CHECK(fs::weakly_canonical(dir / "f0") == dir / "f0");
2789 CHECK(fs::weakly_canonical(dir / "f0/") == dir / "f0/");
2790 CHECK(fs::weakly_canonical(dir / "f1") == dir / "f1");
2791 CHECK(fs::weakly_canonical(rel / "f0") == dir / "f0");
2792 CHECK(fs::weakly_canonical(rel / "f0/") == dir / "f0/");
2793 CHECK(fs::weakly_canonical(rel / "f1") == dir / "f1");
2794 CHECK(fs::weakly_canonical(rel / "./f0") == dir / "f0");
2795 CHECK(fs::weakly_canonical(rel / "./f1") == dir / "f1");
2796 CHECK(fs::weakly_canonical(rel / "d1/../f0") == dir / "f0");
2797 CHECK(fs::weakly_canonical(rel / "d1/../f1") == dir / "f1");
2798 CHECK(fs::weakly_canonical(rel / "d1/../f1/../f2") == dir / "f2");
2799 }
2800 }
2801
2802 TEST_CASE("std::string_view support", "[filesystem][fs.string_view]")
2803 {
2804 #if defined(GHC_HAS_STD_STRING_VIEW) || defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW)
2805
2806 #if defined(GHC_HAS_STD_STRING_VIEW)
2807 using namespace std::literals;
2808 using string_view = std::string_view;
2809 using wstring_view = std::wstring_view;
2810 #elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW)
2811 using string_view = std::experimental::string_view;
2812 using wstring_view = std::experimental::wstring_view;
2813 #endif
2814
2815 {
2816 std::string p("foo/bar");
2817 string_view sv(p);
2818 CHECK(fs::path(sv, fs::path::format::generic_format).generic_string() == "foo/bar");
2819 fs::path p2("fo");
2820 p2 += string_view("o");
2821 CHECK(p2 == "foo");
2822 CHECK(p2.compare(string_view("foo")) == 0);
2823 }
2824 {
2825 auto p = fs::path{"XYZ"};
2826 p /= string_view("Appendix");
2827 CHECK(p == "XYZ/Appendix");
2828 }
2829 {
2830 std::wstring p(L"foo/bar");
2831 wstring_view sv(p);
2832 CHECK(fs::path(sv, fs::path::format::generic_format).generic_string() == "foo/bar");
2833 fs::path p2(L"fo");
2834 p2 += wstring_view(L"o");
2835 CHECK(p2 == "foo");
2836 CHECK(p2.compare(wstring_view(L"foo")) == 0);
2837 }
2838
2839 #else
2840 WARN("std::string_view specific tests are empty without std::string_view.");
2841 #endif
2842 }
2843
2844 TEST_CASE("Windows: Long filename support", "[filesystem][path][fs.path.win.long]")
2845 {
2846 #ifdef GHC_OS_WINDOWS
2847 TemporaryDirectory t(TempOpt::change_path);
2848 char c = 'A';
2849 fs::path dir{"\\\\?\\"};
2850 dir += fs::current_path().u8string();
2851 for (; c <= 'Z'; ++c) {
2852 std::string part = std::string(16, c);
2853 dir /= part;
2854 CHECK_NOTHROW(fs::create_directory(dir));
2855 CHECK(fs::exists(dir));
2856 generateFile(dir / "f0");
2857 CHECK(fs::exists(dir / "f0"));
2858 auto native = dir.u8string();
2859 if (native.substr(0, 4) == u8"\\\\?\\") {
2860 break;
2861 }
2862 }
2863 CHECK(c <= 'Z');
2864 #else
2865 WARN("Windows specific tests are empty on non-Windows systems.");
2866 #endif
2867 }
2868
2869 TEST_CASE("Windows: path namespace handling", "[filesystem][path][fs.path.win.namespaces]")
2870 {
2871 #ifdef GHC_OS_WINDOWS
2872 {
2873 std::error_code ec;
2874 fs::path p(R"(\\localhost\c$\Windows)");
2875 auto symstat = fs::symlink_status(p, ec);
2876 CHECK(!ec);
2877 auto p2 = fs::canonical(p, ec);
2878 CHECK(!ec);
2879 CHECK(p2 == p);
2880 }
2881
2882 struct TestInfo
2883 {
2884 std::string _path;
2885 std::string _string;
2886 std::string _rootName;
2887 std::string _rootPath;
2888 std::string _iterateResult;
2889 };
2890 std::vector<TestInfo> variants = {
2891 {R"(C:\Windows\notepad.exe)", R"(C:\Windows\notepad.exe)", "C:", "C:\\", "C:,/,Windows,notepad.exe"},
2892 #ifdef USE_STD_FS
2893 {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,C:,Windows,notepad.exe"},
2894 {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "\\??", "\\??\\", "/??,/,C:,Windows,notepad.exe"},
2895 #else
2896 {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "C:", "C:\\", "//?/,C:,/,Windows,notepad.exe"},
2897 {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "C:", "C:\\", "/?\?/,C:,/,Windows,notepad.exe"},
2898 #endif
2899 {R"(\\.\C:\Windows\notepad.exe)", R"(\\.\C:\Windows\notepad.exe)", "\\\\.", "\\\\.\\", "//.,/,C:,Windows,notepad.exe"},
2900 {R"(\\?\HarddiskVolume1\Windows\notepad.exe)", R"(\\?\HarddiskVolume1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,HarddiskVolume1,Windows,notepad.exe"},
2901 {R"(\\?\Harddisk0Partition1\Windows\notepad.exe)", R"(\\?\Harddisk0Partition1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,Harddisk0Partition1,Windows,notepad.exe"},
2902 {R"(\\.\GLOBALROOT\Device\HarddiskVolume1\Windows\notepad.exe)", R"(\\.\GLOBALROOT\Device\HarddiskVolume1\Windows\notepad.exe)", "\\\\.", "\\\\.\\", "//.,/,GLOBALROOT,Device,HarddiskVolume1,Windows,notepad.exe"},
2903 {R"(\\?\GLOBALROOT\Device\Harddisk0\Partition1\Windows\notepad.exe)", R"(\\?\GLOBALROOT\Device\Harddisk0\Partition1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,GLOBALROOT,Device,Harddisk0,Partition1,Windows,notepad.exe"},
2904 {R"(\\?\Volume{e8a4a89d-0000-0000-0000-100000000000}\Windows\notepad.exe)", R"(\\?\Volume{e8a4a89d-0000-0000-0000-100000000000}\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,Volume{e8a4a89d-0000-0000-0000-100000000000},Windows,notepad.exe"},
2905 {R"(\\LOCALHOST\C$\Windows\notepad.exe)", R"(\\LOCALHOST\C$\Windows\notepad.exe)", "\\\\LOCALHOST", "\\\\LOCALHOST\\", "//LOCALHOST,/,C$,Windows,notepad.exe"},
2906 {R"(\\?\UNC\C$\Windows\notepad.exe)", R"(\\?\UNC\C$\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,UNC,C$,Windows,notepad.exe"},
2907 {R"(\\?\GLOBALROOT\Device\Mup\C$\Windows\notepad.exe)", R"(\\?\GLOBALROOT\Device\Mup\C$\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,GLOBALROOT,Device,Mup,C$,Windows,notepad.exe"},
2908 };
2909
2910 for (auto ti : variants) {
2911 INFO("Used path: " + ti._path);
2912 auto p = fs::path(ti._path);
2913 CHECK(p.string() == ti._string);
2914 CHECK(p.is_absolute());
2915 CHECK(p.root_name().string() == ti._rootName);
2916 CHECK(p.root_path().string() == ti._rootPath);
2917 CHECK(iterateResult(p) == ti._iterateResult);
2918 }
2919 #else
2920 WARN("Windows specific tests are empty on non-Windows systems.");
2921 #endif
2922 }
2923
2924 TEST_CASE("Windows: Mapped folders handling ", "[filesystem][fs.win][fs.win.mapped]")
2925 {
2926 #ifdef GHC_OS_WINDOWS
2927 // this test expects a mapped volume on C:\\fs-test as is the case on the development test system
2928 // does nothing on other systems
2929 if (fs::exists("C:\\fs-test")) {
2930 CHECK(fs::canonical("C:\\fs-test\\Test.txt").string() == "C:\\fs-test\\Test.txt");
2931 }
2932 #else
2933 WARN("Windows specific tests are empty on non-Windows systems.");
2934 #endif
2935 }
2936
2937 TEST_CASE("Windows: Deletion of Read-only Files", "[filesystem][fs.win][fs.win.remove]")
2938 {
2939 #ifdef GHC_OS_WINDOWS
2940 TemporaryDirectory t(TempOpt::change_path);
2941 std::error_code ec;
2942 generateFile("foo", 512);
2943 auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write;
2944 CHECK_NOTHROW(fs::permissions("foo", allWrite, fs::perm_options::remove));
2945 CHECK_NOTHROW(fs::remove("foo"));
2946 CHECK(!fs::exists("foo"));
2947 #else
2948 WARN("Windows specific tests are empty on non-Windows systems.");
2949 #endif
2950 }
2951