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