• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gulrak / filesystem / 12844595211

18 Jan 2025 01:54PM UTC coverage: 95.498%. Remained the same
12844595211

Pull #175

github

web-flow
Merge b99c2aebd into 076592ce6
Pull Request #175: Workaround for opening file streams using wide string paths on old versions of MinGW

3564 of 3732 relevant lines covered (95.5%)

852.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.08
/test/filesystem_test.cpp
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>
102
static 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>
110
static TP from_time_t(std::time_t t)
8✔
111
{
112
    using namespace std::chrono;
113
    auto sctp = system_clock::from_time_t(t);
8✔
114
    auto tp = time_point_cast<typename TP::duration>(sctp - system_clock::now() + TP::clock::now());
8✔
115
    return tp;
8✔
116
}
117

118
namespace Catch {
119
template <>
120
struct StringMaker<fs::path>
121
{
122
    static std::string convert(fs::path const& value) { return '"' + value.string() + '"'; }
×
123
};
124

125
template <>
126
struct StringMaker<fs::perms>
127
{
128
    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
{
134
    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
{
143
    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
{
150
    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:
171
    TemporaryDirectory(TempOpt opt = TempOpt::none)
196✔
172
    {
196✔
173
        static auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
196✔
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))));
196✔
175
        std::string filename;
294✔
176
        do {
177
            filename = "test_";
196✔
178
            for (int i = 0; i < 8; ++i) {
1,764✔
179
                filename += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[rng()];
1,568✔
180
            }
181
            _path = fs::canonical(fs::temp_directory_path()) / filename;
196✔
182
        } while (fs::exists(_path));
196✔
183
        fs::create_directories(_path);
196✔
184
        if (opt == TempOpt::change_path) {
196✔
185
            _orig_dir = fs::current_path();
148✔
186
            fs::current_path(_path);
148✔
187
        }
188
    }
196✔
189

190
    ~TemporaryDirectory()
196✔
191
    {
98✔
192
        if (!_orig_dir.empty()) {
196✔
193
            fs::current_path(_orig_dir);
148✔
194
        }
195
        fs::remove_all(_path);
196✔
196
    }
196✔
197

198
    const fs::path& path() const { return _path; }
472✔
199

200
private:
201
    fs::path _path;
202
    fs::path _orig_dir;
203
};
204

205
static void generateFile(const fs::path& pathname, int withSize = -1)
308✔
206
{
207
    fs::ofstream outfile(pathname);
462✔
208
    if (withSize < 0) {
308✔
209
        outfile << "Hello world!" << std::endl;
232✔
210
    }
211
    else {
212
        outfile << std::string(size_t(withSize), '*');
76✔
213
    }
214
}
308✔
215

216
#ifdef GHC_OS_WINDOWS
217
#if !defined(_WIN64) && defined(KEY_WOW64_64KEY)
218
static bool isWow64Proc()
219
{
220
    typedef BOOL(WINAPI * IsWow64Process_t)(HANDLE, PBOOL);
221
    BOOL bIsWow64 = FALSE;
222
    auto fnIsWow64Process = (IsWow64Process_t)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
223
    if (NULL != fnIsWow64Process) {
224
        if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64)) {
225
            bIsWow64 = FALSE;
226
        }
227
    }
228
    return bIsWow64 == TRUE;
229
}
230
#endif
231

232
static bool is_symlink_creation_supported()
233
{
234
    bool result = true;
235
    HKEY key;
236
    REGSAM flags = KEY_READ;
237
#ifdef _WIN64
238
    flags |= KEY_WOW64_64KEY;
239
#elif defined(KEY_WOW64_64KEY)
240
    if (isWow64Proc()) {
241
        flags |= KEY_WOW64_64KEY;
242
    }
243
    else {
244
        flags |= KEY_WOW64_32KEY;
245
    }
246
#else
247
    result = false;
248
#endif
249
    if (result) {
250
        auto err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", 0, flags, &key);
251
        if (err == ERROR_SUCCESS) {
252
            DWORD val = 0, size = sizeof(DWORD);
253
            err = RegQueryValueExW(key, L"AllowDevelopmentWithoutDevLicense", 0, NULL, reinterpret_cast<LPBYTE>(&val), &size);
254
            RegCloseKey(key);
255
            if (err != ERROR_SUCCESS) {
256
                result = false;
257
            }
258
            else {
259
                result = (val != 0);
260
            }
261
        }
262
        else {
263
            result = false;
264
        }
265
    }
266
    if (!result) {
267
        std::clog << "Warning: Symlink creation not supported." << std::endl;
268
    }
269
    return result;
270
}
271
#else
272
static bool is_symlink_creation_supported()
124✔
273
{
274
    return true;
124✔
275
}
276
#endif
277

278
static bool has_host_root_name_support()
32✔
279
{
280
    return fs::path("//host").has_root_name();
32✔
281
}
282

283
template <class T>
284
class TestAllocator
285
{
286
public:
287
    using value_type = T;
288
    using pointer = T*;
289
    using const_pointer = const T*;
290
    using reference = T&;
291
    using const_reference = const T&;
292
    using difference_type = ptrdiff_t;
293
    using size_type = size_t;
294
    TestAllocator() noexcept {}
4✔
295
    template <class U>
296
    TestAllocator(TestAllocator<U> const&) noexcept
297
    {
298
    }
299
    value_type* allocate(std::size_t n) { return static_cast<value_type*>(::operator new(n * sizeof(value_type))); }
×
300
    void deallocate(value_type* p, std::size_t) noexcept { ::operator delete(p); }
×
301
    template<class U>
302
    struct rebind {
303
        typedef TestAllocator<U> other;
304
    };
305
};
306

307
template <class T, class U>
308
static bool operator==(TestAllocator<T> const&, TestAllocator<U> const&) noexcept
309
{
310
    return true;
311
}
312

313
template <class T, class U>
314
static bool operator!=(TestAllocator<T> const& x, TestAllocator<U> const& y) noexcept
315
{
316
    return !(x == y);
317
}
318

319
TEST_CASE("Temporary Directory", "[fs.test.tempdir]")
4✔
320
{
321
    fs::path tempPath;
6✔
322
    {
323
        TemporaryDirectory t;
6✔
324
        tempPath = t.path();
4✔
325
        REQUIRE(fs::exists(fs::path(t.path())));
4✔
326
        REQUIRE(fs::is_directory(t.path()));
4✔
327
    }
2✔
328
    REQUIRE(!fs::exists(tempPath));
4✔
329
}
4✔
330

331
#ifdef GHC_FILESYSTEM_VERSION
332
TEST_CASE("fs::detail::fromUtf8", "[filesystem][fs.detail.utf8]")
4✔
333
{
334
    CHECK(fs::detail::fromUtf8<std::wstring>("foobar").length() == 6);
4✔
335
    CHECK(fs::detail::fromUtf8<std::wstring>("foobar") == L"foobar");
4✔
336
    CHECK(fs::detail::fromUtf8<std::wstring>(u8"föobar").length() == 6);
4✔
337
    CHECK(fs::detail::fromUtf8<std::wstring>(u8"föobar") == L"föobar");
4✔
338

339
    CHECK(fs::detail::toUtf8(std::wstring(L"foobar")).length() == 6);
4✔
340
    CHECK(fs::detail::toUtf8(std::wstring(L"foobar")) == "foobar");
4✔
341
    CHECK(fs::detail::toUtf8(std::wstring(L"föobar")).length() == 7);
4✔
342
    //CHECK(fs::detail::toUtf8(std::wstring(L"föobar")) == u8"föobar");
343

344
#ifdef GHC_RAISE_UNICODE_ERRORS
345
    CHECK_THROWS_AS(fs::detail::fromUtf8<std::u16string>(std::string("\xed\xa0\x80")), fs::filesystem_error);
346
    CHECK_THROWS_AS(fs::detail::fromUtf8<std::u16string>(std::string("\xc3")), fs::filesystem_error);
347
#else
348
    CHECK(std::u16string(2,0xfffd) == fs::detail::fromUtf8<std::u16string>(std::string("\xed\xa0\x80")));
4✔
349
    CHECK(std::u16string(1,0xfffd) == fs::detail::fromUtf8<std::u16string>(std::string("\xc3")));
4✔
350
#endif
351
}
4✔
352

353
TEST_CASE("fs::detail::toUtf8", "[filesystem][fs.detail.utf8]")
4✔
354
{
355
    std::string t;
6✔
356
    CHECK(std::string("\xc3\xa4/\xe2\x82\xac\xf0\x9d\x84\x9e") == fs::detail::toUtf8(std::u16string(u"\u00E4/\u20AC\U0001D11E")));
4✔
357
#ifdef GHC_RAISE_UNICODE_ERRORS
358
    CHECK_THROWS_AS(fs::detail::toUtf8(std::u16string(1, 0xd800)), fs::filesystem_error);
359
    CHECK_THROWS_AS(fs::detail::appendUTF8(t, 0x200000), fs::filesystem_error);
360
#else
361
    CHECK(std::string("\xEF\xBF\xBD") == fs::detail::toUtf8(std::u16string(1, 0xd800)));
4✔
362
    fs::detail::appendUTF8(t, 0x200000);
4✔
363
    CHECK(std::string("\xEF\xBF\xBD") == t);
4✔
364
#endif
365
}
4✔
366
#endif
367

368
TEST_CASE("fs.path.generic - path::preferred_separator", "[filesystem][path][fs.path.generic]")
4✔
369
{
370
#ifdef GHC_OS_WINDOWS
371
    CHECK(fs::path::preferred_separator == '\\');
372
#else
373
    CHECK(fs::path::preferred_separator == '/');
4✔
374
#endif
375
}
4✔
376

377
#ifndef GHC_OS_WINDOWS
378
TEST_CASE("fs.path.generic - path(\"//host\").has_root_name()", "[filesystem][path][fs.path.generic]")
×
379
{
380
    if (!has_host_root_name_support()) {
×
381
        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.)");
×
382
    }
383
}
×
384
#endif
385

386
TEST_CASE("fs.path.construct - path constructors and destructor", "[filesystem][path][fs.path.construct]")
4✔
387
{
388
    CHECK("/usr/local/bin" == fs::path("/usr/local/bin").generic_string());
4✔
389
    std::string str = "/usr/local/bin";
6✔
390
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
391
    std::u8string u8str = u8"/usr/local/bin";
3✔
392
#endif
393
    std::u16string u16str = u"/usr/local/bin";
6✔
394
    std::u32string u32str = U"/usr/local/bin";
6✔
395
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
396
    CHECK(u8str == fs::path(u8str).generic_u8string());
2✔
397
#endif
398
    CHECK(u16str == fs::path(u16str).generic_u16string());
4✔
399
    CHECK(u32str == fs::path(u32str).generic_u32string());
4✔
400
    CHECK(str == fs::path(str, fs::path::format::generic_format));
4✔
401
    CHECK(str == fs::path(str.begin(), str.end()));
4✔
402
    CHECK(fs::path(std::wstring(3, 67)) == "CCC");
4✔
403
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
404
    CHECK(str == fs::path(u8str.begin(), u8str.end()));
2✔
405
#endif
406
    CHECK(str == fs::path(u16str.begin(), u16str.end()));
4✔
407
    CHECK(str == fs::path(u32str.begin(), u32str.end()));
4✔
408
#ifdef GHC_FILESYSTEM_VERSION
409
    CHECK(fs::path("///foo/bar") == "/foo/bar");
4✔
410
    CHECK(fs::path("//foo//bar") == "//foo/bar");
4✔
411
#endif
412
#ifdef GHC_OS_WINDOWS
413
    CHECK("\\usr\\local\\bin" == fs::path("/usr/local/bin"));
414
    CHECK("C:\\usr\\local\\bin" == fs::path("C:\\usr\\local\\bin"));
415
#else
416
    CHECK("/usr/local/bin" == fs::path("/usr/local/bin"));
4✔
417
#endif
418
    if (has_host_root_name_support()) {
4✔
419
        CHECK("//host/foo/bar" == fs::path("//host/foo/bar"));
4✔
420
    }
421

422
#if !defined(GHC_OS_WINDOWS) && !(defined(__GLIBCXX__) && !(defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 8))) && !defined(USE_STD_FS)
423
    std::locale loc;
6✔
424
    bool testUTF8Locale = false;
4✔
425
    try {
426
        if (const char* lang = std::getenv("LANG")) {
4✔
427
            loc = std::locale(lang);
4✔
428
        }
429
        else {
430
            loc = std::locale("en_US.UTF-8");
×
431
        }
432
        std::string name = loc.name();
6✔
433
        if (name.length() > 5 && (name.substr(name.length() - 5) == "UTF-8" || name.substr(name.length() - 5) == "utf-8")) {
4✔
434
            testUTF8Locale = true;
4✔
435
        }
436
    }
2✔
437
    catch (std::runtime_error&) {
×
438
        WARN("Couldn't create an UTF-8 locale!");
×
439
    }
440
    if (testUTF8Locale) {
4✔
441
        CHECK("/usr/local/bin" == fs::path("/usr/local/bin", loc));
4✔
442
        CHECK(str == fs::path(str.begin(), str.end(), loc));
4✔
443
        CHECK(str == fs::path(u16str.begin(), u16str.end(), loc));
4✔
444
        CHECK(str == fs::path(u32str.begin(), u32str.end(), loc));
4✔
445
    }
446
#endif
447
}
4✔
448

449
TEST_CASE("fs.path.assign - path assignments", "[filesystem][path][fs.path.assign]")
4✔
450
{
451
    fs::path p1{"/foo/bar"};
6✔
452
    fs::path p2{"/usr/local"};
6✔
453
    fs::path p3;
6✔
454
    p3 = p1;
4✔
455
    REQUIRE(p1 == p3);
4✔
456
    p3 = fs::path{"/usr/local"};
4✔
457
    REQUIRE(p2 == p3);
4✔
458
    p3 = fs::path{L"/usr/local"};
4✔
459
    REQUIRE(p2 == p3);
4✔
460
    p3.assign(L"/usr/local");
4✔
461
    REQUIRE(p2 == p3);
4✔
462
#if defined(IS_WCHAR_PATH) || defined(GHC_USE_WCHAR_T)
463
    p3 = fs::path::string_type{L"/foo/bar"};
464
    REQUIRE(p1 == p3);
465
    p3.assign(fs::path::string_type{L"/usr/local"});
466
    REQUIRE(p2 == p3);
467
#else
468
    p3 = fs::path::string_type{"/foo/bar"};
4✔
469
    REQUIRE(p1 == p3);
4✔
470
    p3.assign(fs::path::string_type{"/usr/local"});
4✔
471
    REQUIRE(p2 == p3);
4✔
472
#endif
473
    p3 = std::u16string(u"/foo/bar");
4✔
474
    REQUIRE(p1 == p3);
4✔
475
    p3 = U"/usr/local";
4✔
476
    REQUIRE(p2 == p3);
4✔
477
    p3.assign(std::u16string(u"/foo/bar"));
4✔
478
    REQUIRE(p1 == p3);
4✔
479
    std::string s{"/usr/local"};
6✔
480
    p3.assign(s.begin(), s.end());
4✔
481
    REQUIRE(p2 == p3);
4✔
482
}
4✔
483

484
TEST_CASE("fs.path.append - path appends", "[filesystem][path][fs.path.append]")
4✔
485
{
486
#ifdef GHC_OS_WINDOWS
487
    CHECK(fs::path("foo") / "c:/bar" == "c:/bar");
488
    CHECK(fs::path("foo") / "c:" == "c:");
489
    CHECK(fs::path("c:") / "" == "c:");
490
    CHECK(fs::path("c:foo") / "/bar" == "c:/bar");
491
    CHECK(fs::path("c:foo") / "c:bar" == "c:foo/bar");
492
#else
493
    CHECK(fs::path("foo") / "" == "foo/");
4✔
494
    CHECK(fs::path("foo") / "/bar" == "/bar");
4✔
495
    CHECK(fs::path("/foo") / "/" == "/");
4✔
496
    if (has_host_root_name_support()) {
4✔
497
        CHECK(fs::path("//host/foo") / "/bar" == "/bar");
4✔
498
        CHECK(fs::path("//host") / "/" == "//host/");
4✔
499
        CHECK(fs::path("//host/foo") / "/" == "/");
4✔
500
    }
501
#endif
502
    CHECK(fs::path("/foo/bar") / "some///other" == "/foo/bar/some/other");
4✔
503
    fs::path p1{"/tmp/test"};
6✔
504
    fs::path p2{"foobar.txt"};
6✔
505
    fs::path p3 = p1 / p2;
6✔
506
    CHECK("/tmp/test/foobar.txt" == p3);
4✔
507
    // TODO: append(first, last)
508
}
4✔
509

510
TEST_CASE("fs.path.concat - path concatenation", "[filesystem][path][fs.path.concat]")
4✔
511
{
512
    CHECK((fs::path("foo") += fs::path("bar")) == "foobar");
4✔
513
    CHECK((fs::path("foo") += fs::path("/bar")) == "foo/bar");
4✔
514

515
    CHECK((fs::path("foo") += std::string("bar")) == "foobar");
4✔
516
    CHECK((fs::path("foo") += std::string("/bar")) == "foo/bar");
4✔
517

518
    CHECK((fs::path("foo") += "bar") == "foobar");
4✔
519
    CHECK((fs::path("foo") += "/bar") == "foo/bar");
4✔
520
    CHECK((fs::path("foo") += L"bar") == "foobar");
4✔
521
    CHECK((fs::path("foo") += L"/bar") == "foo/bar");
4✔
522

523
    CHECK((fs::path("foo") += 'b') == "foob");
4✔
524
    CHECK((fs::path("foo") += '/') == "foo/");
4✔
525
    CHECK((fs::path("foo") += L'b') == "foob");
4✔
526
    CHECK((fs::path("foo") += L'/') == "foo/");
4✔
527

528
    CHECK((fs::path("foo") += std::string("bar")) == "foobar");
4✔
529
    CHECK((fs::path("foo") += std::string("/bar")) == "foo/bar");
4✔
530

531
    CHECK((fs::path("foo") += std::u16string(u"bar")) == "foobar");
4✔
532
    CHECK((fs::path("foo") += std::u16string(u"/bar")) == "foo/bar");
4✔
533

534
    CHECK((fs::path("foo") += std::u32string(U"bar")) == "foobar");
4✔
535
    CHECK((fs::path("foo") += std::u32string(U"/bar")) == "foo/bar");
4✔
536

537
    CHECK(fs::path("foo").concat("bar") == "foobar");
4✔
538
    CHECK(fs::path("foo").concat("/bar") == "foo/bar");
4✔
539
    CHECK(fs::path("foo").concat(L"bar") == "foobar");
4✔
540
    CHECK(fs::path("foo").concat(L"/bar") == "foo/bar");
4✔
541
    std::string bar = "bar";
6✔
542
    CHECK(fs::path("foo").concat(bar.begin(), bar.end()) == "foobar");
4✔
543
#ifndef USE_STD_FS
544
    CHECK((fs::path("/foo/bar") += "/some///other") == "/foo/bar/some/other");
4✔
545
#endif
546
    // TODO: contat(first, last)
547
}
4✔
548

549
TEST_CASE("fs.path.modifiers - path modifiers", "[filesystem][path][fs.path.modifiers]")
4✔
550
{
551
    fs::path p = fs::path("/foo/bar");
6✔
552
    p.clear();
4✔
553
    CHECK(p == "");
4✔
554

555
    // make_preferred() is a no-op
556
#ifdef GHC_OS_WINDOWS
557
    CHECK(fs::path("foo\\bar") == "foo/bar");
558
    CHECK(fs::path("foo\\bar").make_preferred() == "foo/bar");
559
#else
560
    CHECK(fs::path("foo\\bar") == "foo\\bar");
4✔
561
    CHECK(fs::path("foo\\bar").make_preferred() == "foo\\bar");
4✔
562
#endif
563
    CHECK(fs::path("foo/bar").make_preferred() == "foo/bar");
4✔
564

565
    CHECK(fs::path("foo/bar").remove_filename() == "foo/");
4✔
566
    CHECK(fs::path("foo/").remove_filename() == "foo/");
4✔
567
    CHECK(fs::path("/foo").remove_filename() == "/");
4✔
568
    CHECK(fs::path("/").remove_filename() == "/");
4✔
569

570
    CHECK(fs::path("/foo").replace_filename("bar") == "/bar");
4✔
571
    CHECK(fs::path("/").replace_filename("bar") == "/bar");
4✔
572
    CHECK(fs::path("/foo").replace_filename("b//ar") == "/b/ar");
4✔
573

574
    CHECK(fs::path("/foo/bar.txt").replace_extension("odf") == "/foo/bar.odf");
4✔
575
    CHECK(fs::path("/foo/bar.txt").replace_extension() == "/foo/bar");
4✔
576
    CHECK(fs::path("/foo/bar").replace_extension("odf") == "/foo/bar.odf");
4✔
577
    CHECK(fs::path("/foo/bar").replace_extension(".odf") == "/foo/bar.odf");
4✔
578
    CHECK(fs::path("/foo/bar.").replace_extension(".odf") == "/foo/bar.odf");
4✔
579
    CHECK(fs::path("/foo/bar/").replace_extension("odf") == "/foo/bar/.odf");
4✔
580

581
    fs::path p1 = "foo";
6✔
582
    fs::path p2 = "bar";
6✔
583
    p1.swap(p2);
4✔
584
    CHECK(p1 == "bar");
4✔
585
    CHECK(p2 == "foo");
4✔
586
}
4✔
587

588
TEST_CASE("fs.path.native.obs - path native format observers", "[filesystem][path][fs.path.native.obs]")
4✔
589
{
590
#ifdef GHC_OS_WINDOWS
591
#if defined(IS_WCHAR_PATH) || defined(GHC_USE_WCHAR_T)
592
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type(L"\u00E4\\\u20AC"));
593
    // CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("ä\\€")); // MSVCs returns local DBCS encoding
594
#else
595
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type("\xc3\xa4\\\xe2\x82\xac"));
596
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("\xc3\xa4\\\xe2\x82\xac"));
597
    CHECK(!::strcmp(fs::u8path("\xc3\xa4\\\xe2\x82\xac").c_str(), "\xc3\xa4\\\xe2\x82\xac"));
598
    CHECK((std::string)fs::u8path("\xc3\xa4\\\xe2\x82\xac") == std::string("\xc3\xa4\\\xe2\x82\xac"));
599
#endif
600
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").wstring() == std::wstring(L"\u00E4\\\u20AC"));
601
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
602
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u8string() == std::u8string(u8"\u00E4\\\u20AC"));
603
#else
604
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u8string() == std::string("\xc3\xa4\\\xe2\x82\xac"));
605
#endif
606
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u16string() == std::u16string(u"\u00E4\\\u20AC"));
607
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u32string() == std::u32string(U"\U000000E4\\\U000020AC"));
608
#else
609
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").native() == fs::path::string_type("\xc3\xa4/\xe2\x82\xac"));
4✔
610
    CHECK(!::strcmp(fs::u8path("\xc3\xa4/\xe2\x82\xac").c_str(), "\xc3\xa4/\xe2\x82\xac"));
4✔
611
    CHECK((std::string)fs::u8path("\xc3\xa4/\xe2\x82\xac") == std::string("\xc3\xa4/\xe2\x82\xac"));
4✔
612
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").string() == std::string("\xc3\xa4/\xe2\x82\xac"));
4✔
613
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").wstring() == std::wstring(L"ä/€"));
4✔
614
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
615
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u8string() == std::u8string(u8"\xc3\xa4/\xe2\x82\xac"));
2✔
616
#else
617
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u8string() == std::string("\xc3\xa4/\xe2\x82\xac"));
2✔
618
#endif
619
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u16string() == std::u16string(u"\u00E4/\u20AC"));
4✔
620
    INFO("This check might fail on GCC8 (with \"Illegal byte sequence\") due to not detecting the valid unicode codepoint U+1D11E.");
6✔
621
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac\xf0\x9d\x84\x9e").u16string() == std::u16string(u"\u00E4/\u20AC\U0001D11E"));
4✔
622
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u32string() == std::u32string(U"\U000000E4/\U000020AC"));
4✔
623
#endif
624
}
4✔
625

626
TEST_CASE("fs.path.generic.obs - path generic format observers", "[filesystem][path][fs.path.generic.obs]")
4✔
627
{
628
#ifdef GHC_OS_WINDOWS
629
#ifndef IS_WCHAR_PATH
630
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_string() == std::string("\xc3\xa4/\xe2\x82\xac"));
631
#endif
632
#ifndef USE_STD_FS
633
    auto t = fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_string<char, std::char_traits<char>, TestAllocator<char>>();
634
    CHECK(t.c_str() == std::string("\xc3\xa4/\xe2\x82\xac"));
635
#endif
636
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_wstring() == std::wstring(L"\U000000E4/\U000020AC"));
637
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
638
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u8string() == std::u8string(u8"\u00E4/\u20AC"));
639
#else
640
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u8string() == std::string("\xc3\xa4/\xe2\x82\xac"));
641
#endif
642
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u16string() == std::u16string(u"\u00E4/\u20AC"));
643
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u32string() == std::u32string(U"\U000000E4/\U000020AC"));
644
#else
645
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_string() == std::string("\xc3\xa4/\xe2\x82\xac"));
4✔
646
#ifndef USE_STD_FS
647
    auto t = fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_string<char, std::char_traits<char>, TestAllocator<char>>();
6✔
648
    CHECK(t.c_str() == std::string("\xc3\xa4/\xe2\x82\xac"));
4✔
649
#endif
650
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_wstring() == std::wstring(L"ä/€"));
4✔
651
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
652
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u8string() == std::u8string(u8"\xc3\xa4/\xe2\x82\xac"));
2✔
653
#else
654
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u8string() == std::string("\xc3\xa4/\xe2\x82\xac"));
2✔
655
#endif
656
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u16string() == std::u16string(u"\u00E4/\u20AC"));
4✔
657
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u32string() == std::u32string(U"\U000000E4/\U000020AC"));
4✔
658
#endif
659
}
4✔
660

661
TEST_CASE("fs.path.compare - path compare", "[filesystem][path][fs.path.compare]")
4✔
662
{
663
    CHECK(fs::path("/foo/b").compare("/foo/a") > 0);
4✔
664
    CHECK(fs::path("/foo/b").compare("/foo/b") == 0);
4✔
665
    CHECK(fs::path("/foo/b").compare("/foo/c") < 0);
4✔
666

667
    CHECK(fs::path("/foo/b").compare(std::string("/foo/a")) > 0);
4✔
668
    CHECK(fs::path("/foo/b").compare(std::string("/foo/b")) == 0);
4✔
669
    CHECK(fs::path("/foo/b").compare(std::string("/foo/c")) < 0);
4✔
670

671
    CHECK(fs::path("/foo/b").compare(fs::path("/foo/a")) > 0);
4✔
672
    CHECK(fs::path("/foo/b").compare(fs::path("/foo/b")) == 0);
4✔
673
    CHECK(fs::path("/foo/b").compare(fs::path("/foo/c")) < 0);
4✔
674

675
#ifdef GHC_OS_WINDOWS
676
    CHECK(fs::path("c:\\a\\b").compare("C:\\a\\b") == 0);
677
    CHECK(fs::path("c:\\a\\b").compare("d:\\a\\b") != 0);
678
    CHECK(fs::path("c:\\a\\b").compare("C:\\A\\b") != 0);
679
#endif
680

681
#ifdef LWG_2936_BEHAVIOUR
682
    CHECK(fs::path("/a/b/").compare("/a/b/c") < 0);
4✔
683
    CHECK(fs::path("/a/b/").compare("a/c") > 0);
4✔
684
#endif // LWG_2936_BEHAVIOUR
685
}
4✔
686

687
TEST_CASE("fs.path.decompose - path decomposition", "[filesystem][path][fs.path.decompose]")
4✔
688
{
689
    // root_name()
690
    CHECK(fs::path("").root_name() == "");
4✔
691
    CHECK(fs::path(".").root_name() == "");
4✔
692
    CHECK(fs::path("..").root_name() == "");
4✔
693
    CHECK(fs::path("foo").root_name() == "");
4✔
694
    CHECK(fs::path("/").root_name() == "");
4✔
695
    CHECK(fs::path("/foo").root_name() == "");
4✔
696
    CHECK(fs::path("foo/").root_name() == "");
4✔
697
    CHECK(fs::path("/foo/").root_name() == "");
4✔
698
    CHECK(fs::path("foo/bar").root_name() == "");
4✔
699
    CHECK(fs::path("/foo/bar").root_name() == "");
4✔
700
    CHECK(fs::path("///foo/bar").root_name() == "");
4✔
701
#ifdef GHC_OS_WINDOWS
702
    CHECK(fs::path("C:/foo").root_name() == "C:");
703
    CHECK(fs::path("C:\\foo").root_name() == "C:");
704
    CHECK(fs::path("C:foo").root_name() == "C:");
705
#endif
706

707
    // root_directory()
708
    CHECK(fs::path("").root_directory() == "");
4✔
709
    CHECK(fs::path(".").root_directory() == "");
4✔
710
    CHECK(fs::path("..").root_directory() == "");
4✔
711
    CHECK(fs::path("foo").root_directory() == "");
4✔
712
    CHECK(fs::path("/").root_directory() == "/");
4✔
713
    CHECK(fs::path("/foo").root_directory() == "/");
4✔
714
    CHECK(fs::path("foo/").root_directory() == "");
4✔
715
    CHECK(fs::path("/foo/").root_directory() == "/");
4✔
716
    CHECK(fs::path("foo/bar").root_directory() == "");
4✔
717
    CHECK(fs::path("/foo/bar").root_directory() == "/");
4✔
718
    CHECK(fs::path("///foo/bar").root_directory() == "/");
4✔
719
#ifdef GHC_OS_WINDOWS
720
    CHECK(fs::path("C:/foo").root_directory() == "/");
721
    CHECK(fs::path("C:\\foo").root_directory() == "/");
722
    CHECK(fs::path("C:foo").root_directory() == "");
723
#endif
724

725
    // root_path()
726
    CHECK(fs::path("").root_path() == "");
4✔
727
    CHECK(fs::path(".").root_path() == "");
4✔
728
    CHECK(fs::path("..").root_path() == "");
4✔
729
    CHECK(fs::path("foo").root_path() == "");
4✔
730
    CHECK(fs::path("/").root_path() == "/");
4✔
731
    CHECK(fs::path("/foo").root_path() == "/");
4✔
732
    CHECK(fs::path("foo/").root_path() == "");
4✔
733
    CHECK(fs::path("/foo/").root_path() == "/");
4✔
734
    CHECK(fs::path("foo/bar").root_path() == "");
4✔
735
    CHECK(fs::path("/foo/bar").root_path() == "/");
4✔
736
    CHECK(fs::path("///foo/bar").root_path() == "/");
4✔
737
#ifdef GHC_OS_WINDOWS
738
    CHECK(fs::path("C:/foo").root_path() == "C:/");
739
    CHECK(fs::path("C:\\foo").root_path() == "C:/");
740
    CHECK(fs::path("C:foo").root_path() == "C:");
741
#endif
742

743
    // relative_path()
744
    CHECK(fs::path("").relative_path() == "");
4✔
745
    CHECK(fs::path(".").relative_path() == ".");
4✔
746
    CHECK(fs::path("..").relative_path() == "..");
4✔
747
    CHECK(fs::path("foo").relative_path() == "foo");
4✔
748
    CHECK(fs::path("/").relative_path() == "");
4✔
749
    CHECK(fs::path("/foo").relative_path() == "foo");
4✔
750
    CHECK(fs::path("foo/").relative_path() == "foo/");
4✔
751
    CHECK(fs::path("/foo/").relative_path() == "foo/");
4✔
752
    CHECK(fs::path("foo/bar").relative_path() == "foo/bar");
4✔
753
    CHECK(fs::path("/foo/bar").relative_path() == "foo/bar");
4✔
754
    CHECK(fs::path("///foo/bar").relative_path() == "foo/bar");
4✔
755
#ifdef GHC_OS_WINDOWS
756
    CHECK(fs::path("C:/foo").relative_path() == "foo");
757
    CHECK(fs::path("C:\\foo").relative_path() == "foo");
758
    CHECK(fs::path("C:foo").relative_path() == "foo");
759
#endif
760

761
    // parent_path()
762
    CHECK(fs::path("").parent_path() == "");
4✔
763
    CHECK(fs::path(".").parent_path() == "");
4✔
764
    CHECK(fs::path("..").parent_path() == "");  // unintuitive but as defined in the standard
4✔
765
    CHECK(fs::path("foo").parent_path() == "");
4✔
766
    CHECK(fs::path("/").parent_path() == "/");
4✔
767
    CHECK(fs::path("/foo").parent_path() == "/");
4✔
768
    CHECK(fs::path("foo/").parent_path() == "foo");
4✔
769
    CHECK(fs::path("/foo/").parent_path() == "/foo");
4✔
770
    CHECK(fs::path("foo/bar").parent_path() == "foo");
4✔
771
    CHECK(fs::path("/foo/bar").parent_path() == "/foo");
4✔
772
    CHECK(fs::path("///foo/bar").parent_path() == "/foo");
4✔
773
#ifdef GHC_OS_WINDOWS
774
    CHECK(fs::path("C:/foo").parent_path() == "C:/");
775
    CHECK(fs::path("C:\\foo").parent_path() == "C:/");
776
    CHECK(fs::path("C:foo").parent_path() == "C:");
777
#endif
778

779
    // filename()
780
    CHECK(fs::path("").filename() == "");
4✔
781
    CHECK(fs::path(".").filename() == ".");
4✔
782
    CHECK(fs::path("..").filename() == "..");
4✔
783
    CHECK(fs::path("foo").filename() == "foo");
4✔
784
    CHECK(fs::path("/").filename() == "");
4✔
785
    CHECK(fs::path("/foo").filename() == "foo");
4✔
786
    CHECK(fs::path("foo/").filename() == "");
4✔
787
    CHECK(fs::path("/foo/").filename() == "");
4✔
788
    CHECK(fs::path("foo/bar").filename() == "bar");
4✔
789
    CHECK(fs::path("/foo/bar").filename() == "bar");
4✔
790
    CHECK(fs::path("///foo/bar").filename() == "bar");
4✔
791
#ifdef GHC_OS_WINDOWS
792
    CHECK(fs::path("C:/foo").filename() == "foo");
793
    CHECK(fs::path("C:\\foo").filename() == "foo");
794
    CHECK(fs::path("C:foo").filename() == "foo");
795
    CHECK(fs::path("t:est.txt").filename() == "est.txt");
796
#else
797
    CHECK(fs::path("t:est.txt").filename() == "t:est.txt");
4✔
798
#endif
799

800
    // stem()
801
    CHECK(fs::path("/foo/bar.txt").stem() == "bar");
4✔
802
    {
803
        fs::path p = "foo.bar.baz.tar";
6✔
804
        CHECK(p.extension() == ".tar");
4✔
805
        p = p.stem();
4✔
806
        CHECK(p.extension() == ".baz");
4✔
807
        p = p.stem();
4✔
808
        CHECK(p.extension() == ".bar");
4✔
809
        p = p.stem();
4✔
810
        CHECK(p == "foo");
4✔
811
    }
2✔
812
    CHECK(fs::path("/foo/.profile").stem() == ".profile");
4✔
813
    CHECK(fs::path(".bar").stem() == ".bar");
4✔
814
    CHECK(fs::path("..bar").stem() == ".");
4✔
815
#ifdef GHC_OS_WINDOWS
816
    CHECK(fs::path("t:est.txt").stem() == "est");
817
#else
818
    CHECK(fs::path("t:est.txt").stem() == "t:est");
4✔
819
#endif
820
    CHECK(fs::path("/foo/.").stem() == ".");
4✔
821
    CHECK(fs::path("/foo/..").stem() == "..");
4✔
822

823
    // extension()
824
    CHECK(fs::path("/foo/bar.txt").extension() == ".txt");
4✔
825
    CHECK(fs::path("/foo/bar").extension() == "");
4✔
826
    CHECK(fs::path("/foo/.profile").extension() == "");
4✔
827
    CHECK(fs::path(".bar").extension() == "");
4✔
828
    CHECK(fs::path("..bar").extension() == ".bar");
4✔
829
    CHECK(fs::path("t:est.txt").extension() == ".txt");
4✔
830
    CHECK(fs::path("/foo/.").extension() == "");
4✔
831
    CHECK(fs::path("/foo/..").extension() == "");
4✔
832

833
    if (has_host_root_name_support()) {
4✔
834
        // //host-based root-names
835
        CHECK(fs::path("//host").root_name() == "//host");
4✔
836
        CHECK(fs::path("//host/foo").root_name() == "//host");
4✔
837
        CHECK(fs::path("//host").root_directory() == "");
4✔
838
        CHECK(fs::path("//host/foo").root_directory() == "/");
4✔
839
        CHECK(fs::path("//host").root_path() == "//host");
4✔
840
        CHECK(fs::path("//host/foo").root_path() == "//host/");
4✔
841
        CHECK(fs::path("//host").relative_path() == "");
4✔
842
        CHECK(fs::path("//host/foo").relative_path() == "foo");
4✔
843
        CHECK(fs::path("//host").parent_path() == "//host");
4✔
844
        CHECK(fs::path("//host/foo").parent_path() == "//host/");
4✔
845
        CHECK(fs::path("//host").filename() == "");
4✔
846
        CHECK(fs::path("//host/foo").filename() == "foo");
4✔
847
    }
848
}
4✔
849

850
TEST_CASE("fs.path.query - path query", "[fielsystem][path][fs.path.query]")
4✔
851
{
852
    // empty
853
    CHECK(fs::path("").empty());
4✔
854
    CHECK(!fs::path("foo").empty());
4✔
855

856
    // has_root_path()
857
    CHECK(!fs::path("foo").has_root_path());
4✔
858
    CHECK(!fs::path("foo/bar").has_root_path());
4✔
859
    CHECK(fs::path("/foo").has_root_path());
4✔
860
#ifdef GHC_OS_WINDOWS
861
    CHECK(fs::path("C:foo").has_root_path());
862
    CHECK(fs::path("C:/foo").has_root_path());
863
#endif
864

865
    // has_root_name()
866
    CHECK(!fs::path("foo").has_root_name());
4✔
867
    CHECK(!fs::path("foo/bar").has_root_name());
4✔
868
    CHECK(!fs::path("/foo").has_root_name());
4✔
869
#ifdef GHC_OS_WINDOWS
870
    CHECK(fs::path("C:foo").has_root_name());
871
    CHECK(fs::path("C:/foo").has_root_name());
872
#endif
873

874
    // has_root_directory()
875
    CHECK(!fs::path("foo").has_root_directory());
4✔
876
    CHECK(!fs::path("foo/bar").has_root_directory());
4✔
877
    CHECK(fs::path("/foo").has_root_directory());
4✔
878
#ifdef GHC_OS_WINDOWS
879
    CHECK(!fs::path("C:foo").has_root_directory());
880
    CHECK(fs::path("C:/foo").has_root_directory());
881
#endif
882

883
    // has_relative_path()
884
    CHECK(!fs::path("").has_relative_path());
4✔
885
    CHECK(!fs::path("/").has_relative_path());
4✔
886
    CHECK(fs::path("/foo").has_relative_path());
4✔
887

888
    // has_parent_path()
889
    CHECK(!fs::path("").has_parent_path());
4✔
890
    CHECK(!fs::path(".").has_parent_path());
4✔
891
    CHECK(!fs::path("..").has_parent_path());  // unintuitive but as defined in the standard
4✔
892
    CHECK(!fs::path("foo").has_parent_path());
4✔
893
    CHECK(fs::path("/").has_parent_path());
4✔
894
    CHECK(fs::path("/foo").has_parent_path());
4✔
895
    CHECK(fs::path("foo/").has_parent_path());
4✔
896
    CHECK(fs::path("/foo/").has_parent_path());
4✔
897

898
    // has_filename()
899
    CHECK(fs::path("foo").has_filename());
4✔
900
    CHECK(fs::path("foo/bar").has_filename());
4✔
901
    CHECK(!fs::path("/foo/bar/").has_filename());
4✔
902

903
    // has_stem()
904
    CHECK(fs::path("foo").has_stem());
4✔
905
    CHECK(fs::path("foo.bar").has_stem());
4✔
906
    CHECK(fs::path(".profile").has_stem());
4✔
907
    CHECK(!fs::path("/foo/").has_stem());
4✔
908

909
    // has_extension()
910
    CHECK(!fs::path("foo").has_extension());
4✔
911
    CHECK(fs::path("foo.bar").has_extension());
4✔
912
    CHECK(!fs::path(".profile").has_extension());
4✔
913

914
    // is_absolute()
915
    CHECK(!fs::path("foo/bar").is_absolute());
4✔
916
#ifdef GHC_OS_WINDOWS
917
    CHECK(!fs::path("/foo").is_absolute());
918
    CHECK(!fs::path("c:foo").is_absolute());
919
    CHECK(fs::path("c:/foo").is_absolute());
920
#else
921
    CHECK(fs::path("/foo").is_absolute());
4✔
922
#endif
923

924
    // is_relative()
925
    CHECK(fs::path("foo/bar").is_relative());
4✔
926
#ifdef GHC_OS_WINDOWS
927
    CHECK(fs::path("/foo").is_relative());
928
    CHECK(fs::path("c:foo").is_relative());
929
    CHECK(!fs::path("c:/foo").is_relative());
930
#else
931
    CHECK(!fs::path("/foo").is_relative());
4✔
932
#endif
933

934
    if (has_host_root_name_support()) {
4✔
935
        CHECK(fs::path("//host").has_root_name());
4✔
936
        CHECK(fs::path("//host/foo").has_root_name());
4✔
937
        CHECK(fs::path("//host").has_root_path());
4✔
938
        CHECK(fs::path("//host/foo").has_root_path());
4✔
939
        CHECK(!fs::path("//host").has_root_directory());
4✔
940
        CHECK(fs::path("//host/foo").has_root_directory());
4✔
941
        CHECK(!fs::path("//host").has_relative_path());
4✔
942
        CHECK(fs::path("//host/foo").has_relative_path());
4✔
943
        CHECK(fs::path("//host/foo").is_absolute());
4✔
944
        CHECK(!fs::path("//host/foo").is_relative());
4✔
945
    }
946
}
4✔
947

948
TEST_CASE("fs.path.gen - path generation", "[filesystem][path][fs.path.gen]")
4✔
949
{
950
    // lexically_normal()
951
    CHECK(fs::path("foo/./bar/..").lexically_normal() == "foo/");
4✔
952
    CHECK(fs::path("foo/.///bar/../").lexically_normal() == "foo/");
4✔
953
    CHECK(fs::path("/foo/../..").lexically_normal() == "/");
4✔
954
    CHECK(fs::path("foo/..").lexically_normal() == ".");
4✔
955
    CHECK(fs::path("ab/cd/ef/../../qw").lexically_normal() == "ab/qw");
4✔
956
    CHECK(fs::path("a/b/../../../c").lexically_normal() == "../c");
4✔
957
    CHECK(fs::path("../").lexically_normal() == "..");
4✔
958
#ifdef GHC_OS_WINDOWS
959
    CHECK(fs::path("\\/\\///\\/").lexically_normal() == "/");
960
    CHECK(fs::path("a/b/..\\//..///\\/../c\\\\/").lexically_normal() == "../c/");
961
    CHECK(fs::path("..a/b/..\\//..///\\/../c\\\\/").lexically_normal() == "../c/");
962
    CHECK(fs::path("..\\").lexically_normal() == "..");
963
#endif
964

965
    // lexically_relative()
966
    CHECK(fs::path("/a/d").lexically_relative("/a/b/c") == "../../d");
4✔
967
    CHECK(fs::path("/a/b/c").lexically_relative("/a/d") == "../b/c");
4✔
968
    CHECK(fs::path("/a/b/c").lexically_relative("/a/b/c/d/..") == ".");
4✔
969
    CHECK(fs::path("/a/b/c/").lexically_relative("/a/b/c/d/..") == ".");
4✔
970
    CHECK(fs::path("").lexically_relative("/a/..") == "");
4✔
971
    CHECK(fs::path("").lexically_relative("a/..") == ".");
4✔
972
    CHECK(fs::path("a/b/c").lexically_relative("a") == "b/c");
4✔
973
    CHECK(fs::path("a/b/c").lexically_relative("a/b/c/x/y") == "../..");
4✔
974
    CHECK(fs::path("a/b/c").lexically_relative("a/b/c") == ".");
4✔
975
    CHECK(fs::path("a/b").lexically_relative("c/d") == "../../a/b");
4✔
976
    CHECK(fs::path("a/b").lexically_relative("a/") == "b");
4✔
977
    if (has_host_root_name_support()) {
4✔
978
        CHECK(fs::path("//host1/foo").lexically_relative("//host2.bar") == "");
4✔
979
    }
980
#ifdef GHC_OS_WINDOWS
981
    CHECK(fs::path("c:/foo").lexically_relative("/bar") == "");
982
    CHECK(fs::path("c:foo").lexically_relative("c:/bar") == "");
983
    CHECK(fs::path("foo").lexically_relative("/bar") == "");
984
    CHECK(fs::path("c:/foo/bar.txt").lexically_relative("c:/foo/") == "bar.txt");
985
    CHECK(fs::path("c:/foo/bar.txt").lexically_relative("C:/foo/") == "bar.txt");
986
#else
987
    CHECK(fs::path("/foo").lexically_relative("bar") == "");
4✔
988
    CHECK(fs::path("foo").lexically_relative("/bar") == "");
4✔
989
#endif
990

991
    // lexically_proximate()
992
    CHECK(fs::path("/a/d").lexically_proximate("/a/b/c") == "../../d");
4✔
993
    if (has_host_root_name_support()) {
4✔
994
        CHECK(fs::path("//host1/a/d").lexically_proximate("//host2/a/b/c") == "//host1/a/d");
4✔
995
    }
996
    CHECK(fs::path("a/d").lexically_proximate("/a/b/c") == "a/d");
4✔
997
#ifdef GHC_OS_WINDOWS
998
    CHECK(fs::path("c:/a/d").lexically_proximate("c:/a/b/c") == "../../d");
999
    CHECK(fs::path("c:/a/d").lexically_proximate("d:/a/b/c") == "c:/a/d");
1000
    CHECK(fs::path("c:/foo").lexically_proximate("/bar") == "c:/foo");
1001
    CHECK(fs::path("c:foo").lexically_proximate("c:/bar") == "c:foo");
1002
    CHECK(fs::path("foo").lexically_proximate("/bar") == "foo");
1003
#else
1004
    CHECK(fs::path("/foo").lexically_proximate("bar") == "/foo");
4✔
1005
    CHECK(fs::path("foo").lexically_proximate("/bar") == "foo");
4✔
1006
#endif
1007
}
4✔
1008

1009
static std::string iterateResult(const fs::path& path)
60✔
1010
{
1011
    std::ostringstream result;
90✔
1012
    for (fs::path::const_iterator i = path.begin(); i != path.end(); ++i) {
188✔
1013
        if (i != path.begin()) {
128✔
1014
            result << ",";
72✔
1015
        }
1016
        result << i->generic_string();
128✔
1017
    }
30✔
1018
    return result.str();
120✔
1019
}
30✔
1020

1021
static std::string reverseIterateResult(const fs::path& path)
60✔
1022
{
1023
    std::ostringstream result;
90✔
1024
    fs::path::const_iterator iter = path.end();
90✔
1025
    bool first = true;
60✔
1026
    if (iter != path.begin()) {
60✔
1027
        do {
36✔
1028
            --iter;
128✔
1029
            if (!first) {
128✔
1030
                result << ",";
72✔
1031
            }
1032
            first = false;
128✔
1033
            result << iter->generic_string();
128✔
1034
        } while (iter != path.begin());
128✔
1035
    }
1036
    return result.str();
120✔
1037
}
30✔
1038

1039
TEST_CASE("fs.path.itr - path iterators", "[filesystem][path][fs.path.itr]")
4✔
1040
{
1041
    CHECK(iterateResult(fs::path()).empty());
4✔
1042
    CHECK("." == iterateResult(fs::path(".")));
4✔
1043
    CHECK(".." == iterateResult(fs::path("..")));
4✔
1044
    CHECK("foo" == iterateResult(fs::path("foo")));
4✔
1045
    CHECK("/" == iterateResult(fs::path("/")));
4✔
1046
    CHECK("/,foo" == iterateResult(fs::path("/foo")));
4✔
1047
    CHECK("foo," == iterateResult(fs::path("foo/")));
4✔
1048
    CHECK("/,foo," == iterateResult(fs::path("/foo/")));
4✔
1049
    CHECK("foo,bar" == iterateResult(fs::path("foo/bar")));
4✔
1050
    CHECK("/,foo,bar" == iterateResult(fs::path("/foo/bar")));
4✔
1051
#ifndef USE_STD_FS
1052
    // ghc::filesystem enforces redundant slashes to be reduced to one
1053
    CHECK("/,foo,bar" == iterateResult(fs::path("///foo/bar")));
4✔
1054
#else
1055
    // typically std::filesystem keeps them
1056
    CHECK("///,foo,bar" == iterateResult(fs::path("///foo/bar")));
1057
#endif
1058
    CHECK("/,foo,bar," == iterateResult(fs::path("/foo/bar///")));
4✔
1059
    CHECK("foo,.,bar,..," == iterateResult(fs::path("foo/.///bar/../")));
4✔
1060
#ifdef GHC_OS_WINDOWS
1061
    CHECK("C:,/,foo" == iterateResult(fs::path("C:/foo")));
1062
#endif
1063

1064
    CHECK(reverseIterateResult(fs::path()).empty());
4✔
1065
    CHECK("." == reverseIterateResult(fs::path(".")));
4✔
1066
    CHECK(".." == reverseIterateResult(fs::path("..")));
4✔
1067
    CHECK("foo" == reverseIterateResult(fs::path("foo")));
4✔
1068
    CHECK("/" == reverseIterateResult(fs::path("/")));
4✔
1069
    CHECK("foo,/" == reverseIterateResult(fs::path("/foo")));
4✔
1070
    CHECK(",foo" == reverseIterateResult(fs::path("foo/")));
4✔
1071
    CHECK(",foo,/" == reverseIterateResult(fs::path("/foo/")));
4✔
1072
    CHECK("bar,foo" == reverseIterateResult(fs::path("foo/bar")));
4✔
1073
    CHECK("bar,foo,/" == reverseIterateResult(fs::path("/foo/bar")));
4✔
1074
#ifndef USE_STD_FS
1075
    // ghc::filesystem enforces redundant slashes to be reduced to one
1076
    CHECK("bar,foo,/" == reverseIterateResult(fs::path("///foo/bar")));
4✔
1077
#else
1078
    // typically std::filesystem keeps them
1079
    CHECK("bar,foo,///" == reverseIterateResult(fs::path("///foo/bar")));
1080
#endif
1081
    CHECK(",bar,foo,/" == reverseIterateResult(fs::path("/foo/bar///")));
4✔
1082
    CHECK(",..,bar,.,foo" == reverseIterateResult(fs::path("foo/.///bar/../")));
4✔
1083
#ifdef GHC_OS_WINDOWS
1084
    CHECK("foo,/,C:" == reverseIterateResult(fs::path("C:/foo")));
1085
    CHECK("foo,C:" == reverseIterateResult(fs::path("C:foo")));
1086
#endif
1087
    {
1088
        fs::path p1 = "/foo/bar/test.txt";
6✔
1089
        fs::path p2;
6✔
1090
        for (auto pe : p1) {
20✔
1091
            p2 /= pe;
16✔
1092
        }
10✔
1093
        CHECK(p1 == p2);
4✔
1094
        CHECK("bar" == *(--fs::path("/foo/bar").end()));
4✔
1095
        auto p = fs::path("/foo/bar");
6✔
1096
        auto pi = p.end();
6✔
1097
        pi--;
4✔
1098
        CHECK("bar" == *pi);
4✔
1099
    }
2✔
1100

1101
    if (has_host_root_name_support()) {
4✔
1102
        CHECK("foo" == *(--fs::path("//host/foo").end()));
4✔
1103
        auto p = fs::path("//host/foo");
6✔
1104
        auto pi = p.end();
6✔
1105
        pi--;
4✔
1106
        CHECK("foo" == *pi);
4✔
1107
        CHECK("//host" == iterateResult(fs::path("//host")));
4✔
1108
        CHECK("//host,/,foo" == iterateResult(fs::path("//host/foo")));
4✔
1109
        CHECK("//host" == reverseIterateResult(fs::path("//host")));
4✔
1110
        CHECK("foo,/,//host" == reverseIterateResult(fs::path("//host/foo")));
4✔
1111
        {
1112
            fs::path p1 = "//host/foo/bar/test.txt";
6✔
1113
            fs::path p2;
6✔
1114
            for (auto pe : p1) {
24✔
1115
                p2 /= pe;
20✔
1116
            }
12✔
1117
            CHECK(p1 == p2);
4✔
1118
        }
2✔
1119
    }
2✔
1120
}
4✔
1121

1122
TEST_CASE("fs.path.nonmember - path non-member functions", "[filesystem][path][fs.path.nonmember]")
4✔
1123
{
1124
    fs::path p1("foo/bar");
6✔
1125
    fs::path p2("some/other");
6✔
1126
    fs::swap(p1, p2);
4✔
1127
    CHECK(p1 == "some/other");
4✔
1128
    CHECK(p2 == "foo/bar");
4✔
1129
    CHECK(hash_value(p1));
4✔
1130
    CHECK(p2 < p1);
4✔
1131
    CHECK(p2 <= p1);
4✔
1132
    CHECK(p1 <= p1);
4✔
1133
    CHECK(!(p1 < p2));
4✔
1134
    CHECK(!(p1 <= p2));
4✔
1135
    CHECK(p1 > p2);
4✔
1136
    CHECK(p1 >= p2);
4✔
1137
    CHECK(p1 >= p1);
4✔
1138
    CHECK(!(p2 > p1));
4✔
1139
    CHECK(!(p2 >= p1));
4✔
1140
    CHECK(p1 != p2);
4✔
1141
    CHECK(p1 / p2 == "some/other/foo/bar");
4✔
1142
}
4✔
1143

1144
TEST_CASE("fs.path.io - path inserter and extractor", "[filesystem][path][fs.path.io]")
4✔
1145
{
1146
    {
1147
        std::ostringstream os;
6✔
1148
        os << fs::path("/root/foo bar");
4✔
1149
#ifdef GHC_OS_WINDOWS
1150
        CHECK(os.str() == "\"\\\\root\\\\foo bar\"");
1151
#else
1152
        CHECK(os.str() == "\"/root/foo bar\"");
4✔
1153
#endif
1154
    }
2✔
1155
    {
1156
        std::ostringstream os;
6✔
1157
        os << fs::path("/root/foo\"bar");
4✔
1158
#ifdef GHC_OS_WINDOWS
1159
        CHECK(os.str() == "\"\\\\root\\\\foo\\\"bar\"");
1160
#else
1161
        CHECK(os.str() == "\"/root/foo\\\"bar\"");
4✔
1162
#endif
1163
    }
2✔
1164

1165
    {
1166
        std::istringstream is("\"/root/foo bar\"");
10✔
1167
        fs::path p;
6✔
1168
        is >> p;
4✔
1169
        CHECK(p == fs::path("/root/foo bar"));
4✔
1170
        CHECK((is.flags() & std::ios_base::skipws) == std::ios_base::skipws);
4✔
1171
    }
2✔
1172
    {
1173
        std::istringstream is("\"/root/foo bar\"");
10✔
1174
        is >> std::noskipws;
4✔
1175
        fs::path p;
6✔
1176
        is >> p;
4✔
1177
        CHECK(p == fs::path("/root/foo bar"));
4✔
1178
        CHECK((is.flags() & std::ios_base::skipws) != std::ios_base::skipws);
4✔
1179
    }
2✔
1180
    {
1181
        std::istringstream is("\"/root/foo\\\"bar\"");
10✔
1182
        fs::path p;
6✔
1183
        is >> p;
4✔
1184
        CHECK(p == fs::path("/root/foo\"bar"));
4✔
1185
    }
2✔
1186
    {
1187
        std::istringstream is("/root/foo");
10✔
1188
        fs::path p;
6✔
1189
        is >> p;
4✔
1190
        CHECK(p == fs::path("/root/foo"));
4✔
1191
    }
2✔
1192
}
4✔
1193

1194
TEST_CASE("fs.path.factory - path factory functions", "[filesystem][path][fs.path.factory]")
4✔
1195
{
1196
    CHECK(fs::u8path("foo/bar") == fs::path("foo/bar"));
4✔
1197
    CHECK(fs::u8path("foo/bar") == fs::path("foo/bar"));
4✔
1198
    std::string str("/foo/bar/test.txt");
6✔
1199
    CHECK(fs::u8path(str.begin(), str.end()) == str);
4✔
1200
}
4✔
1201

1202
TEST_CASE("fs.class.filesystem_error - class filesystem_error", "[filesystem][filesystem_error][fs.class.filesystem_error]")
4✔
1203
{
1204
    std::error_code ec(1, std::system_category());
4✔
1205
    fs::filesystem_error fse("None", std::error_code());
10✔
1206
    fse = fs::filesystem_error("Some error", ec);
4✔
1207
    CHECK(fse.code().value() == 1);
4✔
1208
    CHECK(!std::string(fse.what()).empty());
4✔
1209
    CHECK(fse.path1().empty());
4✔
1210
    CHECK(fse.path2().empty());
4✔
1211
    fse = fs::filesystem_error("Some error", fs::path("foo/bar"), ec);
4✔
1212
    CHECK(!std::string(fse.what()).empty());
4✔
1213
    CHECK(fse.path1() == "foo/bar");
4✔
1214
    CHECK(fse.path2().empty());
4✔
1215
    fse = fs::filesystem_error("Some error", fs::path("foo/bar"), fs::path("some/other"), ec);
4✔
1216
    CHECK(!std::string(fse.what()).empty());
4✔
1217
    CHECK(fse.path1() == "foo/bar");
4✔
1218
    CHECK(fse.path2() == "some/other");
4✔
1219
}
4✔
1220

1221
static constexpr fs::perms constExprOwnerAll()
1222
{
1223
    return fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec;
1224
}
1225

1226
TEST_CASE("fs.enum - enum class perms", "[filesystem][enum][fs.enum]")
4✔
1227
{
1228
    static_assert(constExprOwnerAll() == fs::perms::owner_all, "constexpr didn't result in owner_all");
1229
    CHECK((fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec) == fs::perms::owner_all);
4✔
1230
    CHECK((fs::perms::group_read | fs::perms::group_write | fs::perms::group_exec) == fs::perms::group_all);
4✔
1231
    CHECK((fs::perms::others_read | fs::perms::others_write | fs::perms::others_exec) == fs::perms::others_all);
4✔
1232
    CHECK((fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all) == fs::perms::all);
4✔
1233
    CHECK((fs::perms::all | fs::perms::set_uid | fs::perms::set_gid | fs::perms::sticky_bit) == fs::perms::mask);
4✔
1234
}
4✔
1235

1236
TEST_CASE("fs.class.file_status - class file_status", "[filesystem][file_status][fs.class.file_status]")
4✔
1237
{
1238
    {
1239
        fs::file_status fs;
6✔
1240
        CHECK(fs.type() == fs::file_type::none);
4✔
1241
        CHECK(fs.permissions() == fs::perms::unknown);
4✔
1242
    }
2✔
1243
    {
1244
        fs::file_status fs{fs::file_type::regular};
6✔
1245
        CHECK(fs.type() == fs::file_type::regular);
4✔
1246
        CHECK(fs.permissions() == fs::perms::unknown);
4✔
1247
    }
2✔
1248
    {
1249
        fs::file_status fs{fs::file_type::directory, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
6✔
1250
        CHECK(fs.type() == fs::file_type::directory);
4✔
1251
        CHECK(fs.permissions() == fs::perms::owner_all);
4✔
1252
        fs.type(fs::file_type::block);
4✔
1253
        CHECK(fs.type() == fs::file_type::block);
4✔
1254
        fs.type(fs::file_type::character);
4✔
1255
        CHECK(fs.type() == fs::file_type::character);
4✔
1256
        fs.type(fs::file_type::fifo);
4✔
1257
        CHECK(fs.type() == fs::file_type::fifo);
4✔
1258
        fs.type(fs::file_type::symlink);
4✔
1259
        CHECK(fs.type() == fs::file_type::symlink);
4✔
1260
        fs.type(fs::file_type::socket);
4✔
1261
        CHECK(fs.type() == fs::file_type::socket);
4✔
1262
        fs.permissions(fs.permissions() | fs::perms::group_all | fs::perms::others_all);
4✔
1263
        CHECK(fs.permissions() == fs::perms::all);
4✔
1264
    }
2✔
1265
    {
1266
        fs::file_status fst(fs::file_type::regular);
6✔
1267
        fs::file_status fs(std::move(fst));
6✔
1268
        CHECK(fs.type() == fs::file_type::regular);
4✔
1269
        CHECK(fs.permissions() == fs::perms::unknown);
4✔
1270
    }
2✔
1271
#if !defined(USE_STD_FS) || defined(GHC_FILESYSTEM_RUNNING_CPP20)
1272
    {
1273
        fs::file_status fs1{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
6✔
1274
        fs::file_status fs2{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
6✔
1275
        fs::file_status fs3{fs::file_type::directory, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
6✔
1276
        fs::file_status fs4{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write};
6✔
1277
        CHECK(fs1 == fs2);
4✔
1278
        CHECK_FALSE(fs1 == fs3);
4✔
1279
        CHECK_FALSE(fs1 == fs4);
4✔
1280
    }
2✔
1281
#endif
1282
}
4✔
1283

1284
TEST_CASE("fs.dir.entry - class directory_entry", "[filesystem][directory_entry][fs.dir.entry]")
4✔
1285
{
1286
    TemporaryDirectory t;
6✔
1287
    std::error_code ec;
4✔
1288
    auto de = fs::directory_entry(t.path());
6✔
1289
    CHECK(de.path() == t.path());
4✔
1290
    CHECK((fs::path)de == t.path());
4✔
1291
    CHECK(de.exists());
4✔
1292
    CHECK(!de.is_block_file());
4✔
1293
    CHECK(!de.is_character_file());
4✔
1294
    CHECK(de.is_directory());
4✔
1295
    CHECK(!de.is_fifo());
4✔
1296
    CHECK(!de.is_other());
4✔
1297
    CHECK(!de.is_regular_file());
4✔
1298
    CHECK(!de.is_socket());
4✔
1299
    CHECK(!de.is_symlink());
4✔
1300
    CHECK(de.status().type() == fs::file_type::directory);
4✔
1301
    ec.clear();
4✔
1302
    CHECK(de.status(ec).type() == fs::file_type::directory);
4✔
1303
    CHECK(!ec);
4✔
1304
    CHECK_NOTHROW(de.refresh());
4✔
1305
    fs::directory_entry none;
6✔
1306
    CHECK_THROWS_AS(none.refresh(), fs::filesystem_error);
6✔
1307
    ec.clear();
4✔
1308
    CHECK_NOTHROW(none.refresh(ec));
4✔
1309
    CHECK(ec);
4✔
1310
    CHECK_THROWS_AS(de.assign(""), fs::filesystem_error);
10✔
1311
    ec.clear();
4✔
1312
    CHECK_NOTHROW(de.assign("", ec));
4✔
1313
    CHECK(ec);
4✔
1314
    generateFile(t.path() / "foo", 1234);
4✔
1315
    auto now = fs::file_time_type::clock::now();
4✔
1316
    CHECK_NOTHROW(de.assign(t.path() / "foo"));
4✔
1317
    CHECK_NOTHROW(de.assign(t.path() / "foo", ec));
4✔
1318
    CHECK(!ec);
4✔
1319
    de = fs::directory_entry(t.path() / "foo");
4✔
1320
    CHECK(de.path() == t.path() / "foo");
4✔
1321
    CHECK(de.exists());
4✔
1322
    CHECK(de.exists(ec));
4✔
1323
    CHECK(!ec);
4✔
1324
    CHECK(!de.is_block_file());
4✔
1325
    CHECK(!de.is_block_file(ec));
4✔
1326
    CHECK(!ec);
4✔
1327
    CHECK(!de.is_character_file());
4✔
1328
    CHECK(!de.is_character_file(ec));
4✔
1329
    CHECK(!ec);
4✔
1330
    CHECK(!de.is_directory());
4✔
1331
    CHECK(!de.is_directory(ec));
4✔
1332
    CHECK(!ec);
4✔
1333
    CHECK(!de.is_fifo());
4✔
1334
    CHECK(!de.is_fifo(ec));
4✔
1335
    CHECK(!ec);
4✔
1336
    CHECK(!de.is_other());
4✔
1337
    CHECK(!de.is_other(ec));
4✔
1338
    CHECK(!ec);
4✔
1339
    CHECK(de.is_regular_file());
4✔
1340
    CHECK(de.is_regular_file(ec));
4✔
1341
    CHECK(!ec);
4✔
1342
    CHECK(!de.is_socket());
4✔
1343
    CHECK(!de.is_socket(ec));
4✔
1344
    CHECK(!ec);
4✔
1345
    CHECK(!de.is_symlink());
4✔
1346
    CHECK(!de.is_symlink(ec));
4✔
1347
    CHECK(!ec);
4✔
1348
    CHECK(de.file_size() == 1234);
4✔
1349
    CHECK(de.file_size(ec) == 1234);
4✔
1350
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(de.last_write_time() - now).count()) < 3);
4✔
1351
    ec.clear();
4✔
1352
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(de.last_write_time(ec) - now).count()) < 3);
4✔
1353
    CHECK(!ec);
4✔
1354
#ifndef GHC_OS_WEB
1355
    CHECK(de.hard_link_count() == 1);
4✔
1356
    CHECK(de.hard_link_count(ec) == 1);
4✔
1357
    CHECK(!ec);
4✔
1358
#endif
1359
    CHECK_THROWS_AS(de.replace_filename("bar"), fs::filesystem_error);
10✔
1360
    CHECK_NOTHROW(de.replace_filename("foo"));
4✔
1361
    ec.clear();
4✔
1362
    CHECK_NOTHROW(de.replace_filename("bar", ec));
4✔
1363
    CHECK(ec);
4✔
1364
    auto de2none = fs::directory_entry();
6✔
1365
    ec.clear();
4✔
1366
#ifndef GHC_OS_WEB
1367
    CHECK(de2none.hard_link_count(ec) == static_cast<uintmax_t>(-1));
4✔
1368
    CHECK_THROWS_AS(de2none.hard_link_count(), fs::filesystem_error);
6✔
1369
    CHECK(ec);
4✔
1370
#endif
1371
    ec.clear();
4✔
1372
    CHECK_NOTHROW(de2none.last_write_time(ec));
4✔
1373
    CHECK_THROWS_AS(de2none.last_write_time(), fs::filesystem_error);
6✔
1374
    CHECK(ec);
4✔
1375
    ec.clear();
4✔
1376
    CHECK_THROWS_AS(de2none.file_size(), fs::filesystem_error);
6✔
1377
    CHECK(de2none.file_size(ec) == static_cast<uintmax_t>(-1));
4✔
1378
    CHECK(ec);
4✔
1379
    ec.clear();
4✔
1380
    CHECK(de2none.status().type() == fs::file_type::not_found);
4✔
1381
    CHECK(de2none.status(ec).type() == fs::file_type::not_found);
4✔
1382
    CHECK(ec);
4✔
1383
    generateFile(t.path() / "a");
4✔
1384
    generateFile(t.path() / "b");
4✔
1385
    auto d1 = fs::directory_entry(t.path() / "a");
10✔
1386
    auto d2 = fs::directory_entry(t.path() / "b");
10✔
1387
    CHECK(d1 < d2);
4✔
1388
    CHECK(!(d2 < d1));
4✔
1389
    CHECK(d1 <= d2);
4✔
1390
    CHECK(!(d2 <= d1));
4✔
1391
    CHECK(d2 > d1);
4✔
1392
    CHECK(!(d1 > d2));
4✔
1393
    CHECK(d2 >= d1);
4✔
1394
    CHECK(!(d1 >= d2));
4✔
1395
    CHECK(d1 != d2);
4✔
1396
    CHECK(!(d2 != d2));
4✔
1397
    CHECK(d1 == d1);
4✔
1398
    CHECK(!(d1 == d2));
4✔
1399
    if(is_symlink_creation_supported()) {
4✔
1400
        fs::create_symlink(t.path() / "nonexistent", t.path() / "broken");
4✔
1401
        for (auto d3 : fs::directory_iterator(t.path())) {
24✔
1402
            CHECK_NOTHROW(d3.symlink_status());
16✔
1403
            CHECK_NOTHROW(d3.status());
16✔
1404
            CHECK_NOTHROW(d3.refresh());
16✔
1405
        }
10✔
1406
        fs::directory_entry entry(t.path() / "broken");
10✔
1407
        CHECK_NOTHROW(entry.refresh());
4✔
1408
    }
2✔
1409
}
4✔
1410

1411
TEST_CASE("fs.class.directory_iterator - class directory_iterator", "[filesystem][directory_iterator][fs.class.directory_iterator]")
4✔
1412
{
1413
    {
1414
        TemporaryDirectory t;
6✔
1415
        CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
4✔
1416
        generateFile(t.path() / "test", 1234);
4✔
1417
        REQUIRE(fs::directory_iterator(t.path()) != fs::directory_iterator());
4✔
1418
        auto iter = fs::directory_iterator(t.path());
6✔
1419
        fs::directory_iterator iter2(iter);
6✔
1420
        fs::directory_iterator iter3, iter4;
6✔
1421
        iter3 = iter;
4✔
1422
        CHECK(iter->path().filename() == "test");
4✔
1423
        CHECK(iter2->path().filename() == "test");
4✔
1424
        CHECK(iter3->path().filename() == "test");
4✔
1425
        iter4 = std::move(iter3);
4✔
1426
        CHECK(iter4->path().filename() == "test");
4✔
1427
        CHECK(iter->path() == t.path() / "test");
4✔
1428
        CHECK(!iter->is_symlink());
4✔
1429
        CHECK(iter->is_regular_file());
4✔
1430
        CHECK(!iter->is_directory());
4✔
1431
        CHECK(iter->file_size() == 1234);
4✔
1432
        CHECK(++iter == fs::directory_iterator());
4✔
1433
        CHECK_THROWS_AS(fs::directory_iterator(t.path() / "non-existing"), fs::filesystem_error);
14✔
1434
        int cnt = 0;
4✔
1435
        for(auto de : fs::directory_iterator(t.path())) {
12✔
1436
            ++cnt;
4✔
1437
        }
4✔
1438
        CHECK(cnt == 1);
4✔
1439
    }
2✔
1440
    if (is_symlink_creation_supported()) {
4✔
1441
        TemporaryDirectory t;
6✔
1442
        fs::path td = t.path() / "testdir";
6✔
1443
        CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
4✔
1444
        generateFile(t.path() / "test", 1234);
4✔
1445
        fs::create_directory(td);
4✔
1446
        REQUIRE_NOTHROW(fs::create_symlink(t.path() / "test", td / "testlink"));
4✔
1447
        std::error_code ec;
4✔
1448
        REQUIRE(fs::directory_iterator(td) != fs::directory_iterator());
4✔
1449
        auto iter = fs::directory_iterator(td);
6✔
1450
        CHECK(iter->path().filename() == "testlink");
4✔
1451
        CHECK(iter->path() == td / "testlink");
4✔
1452
        CHECK(iter->is_symlink());
4✔
1453
        CHECK(iter->is_regular_file());
4✔
1454
        CHECK(!iter->is_directory());
4✔
1455
        CHECK(iter->file_size() == 1234);
4✔
1456
        CHECK(++iter == fs::directory_iterator());
4✔
1457
    }
2✔
1458
    {
1459
        // Issue #8: check if resources are freed when iterator reaches end()
1460
        TemporaryDirectory t(TempOpt::change_path);
6✔
1461
        auto p = fs::path("test/");
6✔
1462
        fs::create_directory(p);
4✔
1463
        auto iter = fs::directory_iterator(p);
6✔
1464
        while (iter != fs::directory_iterator()) {
4✔
1465
            ++iter;
×
1466
        }
1467
        CHECK(fs::remove_all(p) == 1);
4✔
1468
        CHECK_NOTHROW(fs::create_directory(p));
4✔
1469
    }
2✔
1470
}
4✔
1471

1472
TEST_CASE("fs.class.rec.dir.itr - class recursive_directory_iterator", "[filesystem][recursive_directory_iterator][fs.class.rec.dir.itr]")
4✔
1473
{
1474
    {
1475
        auto iter = fs::recursive_directory_iterator(".");
6✔
1476
        iter.pop();
4✔
1477
        CHECK(iter == fs::recursive_directory_iterator());
4✔
1478
    }
2✔
1479
    {
1480
        TemporaryDirectory t;
6✔
1481
        CHECK(fs::recursive_directory_iterator(t.path()) == fs::recursive_directory_iterator());
4✔
1482
        generateFile(t.path() / "test", 1234);
4✔
1483
        REQUIRE(fs::recursive_directory_iterator(t.path()) != fs::recursive_directory_iterator());
4✔
1484
        auto iter = fs::recursive_directory_iterator(t.path());
6✔
1485
        CHECK(iter->path().filename() == "test");
4✔
1486
        CHECK(iter->path() == t.path() / "test");
4✔
1487
        CHECK(!iter->is_symlink());
4✔
1488
        CHECK(iter->is_regular_file());
4✔
1489
        CHECK(!iter->is_directory());
4✔
1490
        CHECK(iter->file_size() == 1234);
4✔
1491
        CHECK(++iter == fs::recursive_directory_iterator());
4✔
1492
    }
2✔
1493

1494
    {
1495
        TemporaryDirectory t;
6✔
1496
        fs::path td = t.path() / "testdir";
6✔
1497
        fs::create_directories(td);
4✔
1498
        generateFile(td / "test", 1234);
4✔
1499
        REQUIRE(fs::recursive_directory_iterator(t.path()) != fs::recursive_directory_iterator());
4✔
1500
        auto iter = fs::recursive_directory_iterator(t.path());
6✔
1501

1502
        CHECK(iter->path().filename() == "testdir");
4✔
1503
        CHECK(iter->path() == td);
4✔
1504
        CHECK(!iter->is_symlink());
4✔
1505
        CHECK(!iter->is_regular_file());
4✔
1506
        CHECK(iter->is_directory());
4✔
1507

1508
        CHECK(++iter != fs::recursive_directory_iterator());
4✔
1509

1510
        CHECK(iter->path().filename() == "test");
4✔
1511
        CHECK(iter->path() == td / "test");
4✔
1512
        CHECK(!iter->is_symlink());
4✔
1513
        CHECK(iter->is_regular_file());
4✔
1514
        CHECK(!iter->is_directory());
4✔
1515
        CHECK(iter->file_size() == 1234);
4✔
1516

1517
        CHECK(++iter == fs::recursive_directory_iterator());
4✔
1518
    }
2✔
1519
    {
1520
        TemporaryDirectory t;
6✔
1521
        std::error_code ec;
4✔
1522
        CHECK(fs::recursive_directory_iterator(t.path(), fs::directory_options::none) == fs::recursive_directory_iterator());
4✔
1523
        CHECK(fs::recursive_directory_iterator(t.path(), fs::directory_options::none, ec) == fs::recursive_directory_iterator());
4✔
1524
        CHECK(!ec);
4✔
1525
        CHECK(fs::recursive_directory_iterator(t.path(), ec) == fs::recursive_directory_iterator());
4✔
1526
        CHECK(!ec);
4✔
1527
        generateFile(t.path() / "test");
4✔
1528
        fs::recursive_directory_iterator rd1(t.path());
6✔
1529
        CHECK(fs::recursive_directory_iterator(rd1) != fs::recursive_directory_iterator());
4✔
1530
        fs::recursive_directory_iterator rd2(t.path());
6✔
1531
        CHECK(fs::recursive_directory_iterator(std::move(rd2)) != fs::recursive_directory_iterator());
4✔
1532
        fs::recursive_directory_iterator rd3(t.path(), fs::directory_options::skip_permission_denied);
6✔
1533
        CHECK(rd3.options() == fs::directory_options::skip_permission_denied);
4✔
1534
        fs::recursive_directory_iterator rd4;
6✔
1535
        rd4 = std::move(rd3);
4✔
1536
        CHECK(rd4 != fs::recursive_directory_iterator());
4✔
1537
        CHECK_NOTHROW(++rd4);
4✔
1538
        CHECK(rd4 == fs::recursive_directory_iterator());
4✔
1539
        fs::recursive_directory_iterator rd5;
6✔
1540
        rd5 = rd4;
4✔
1541
    }
2✔
1542
    {
1543
        TemporaryDirectory t(TempOpt::change_path);
6✔
1544
        generateFile("a");
4✔
1545
        fs::create_directory("d1");
4✔
1546
        fs::create_directory("d1/d2");
4✔
1547
        generateFile("d1/b");
4✔
1548
        generateFile("d1/c");
4✔
1549
        generateFile("d1/d2/d");
4✔
1550
        generateFile("e");
4✔
1551
        auto iter = fs::recursive_directory_iterator(".");
6✔
1552
        std::multimap<std::string, int> result;
6✔
1553
        while(iter != fs::recursive_directory_iterator()) {
32✔
1554
            result.insert(std::make_pair(iter->path().generic_string(), iter.depth()));
28✔
1555
            ++iter;
28✔
1556
        }
1557
        std::stringstream os;
6✔
1558
        for(auto p : result) {
32✔
1559
            os << "[" << p.first << "," << p.second << "],";
28✔
1560
        }
14✔
1561
        CHECK(os.str() == "[./a,0],[./d1,0],[./d1/b,1],[./d1/c,1],[./d1/d2,1],[./d1/d2/d,2],[./e,0],");
4✔
1562
    }
2✔
1563
    {
1564
        TemporaryDirectory t(TempOpt::change_path);
6✔
1565
        generateFile("a");
4✔
1566
        fs::create_directory("d1");
4✔
1567
        fs::create_directory("d1/d2");
4✔
1568
        generateFile("d1/b");
4✔
1569
        generateFile("d1/c");
4✔
1570
        generateFile("d1/d2/d");
4✔
1571
        generateFile("e");
4✔
1572
        std::multiset<std::string> result;
6✔
1573
        for(auto de : fs::recursive_directory_iterator(".")) {
36✔
1574
            result.insert(de.path().generic_string());
28✔
1575
        }
16✔
1576
        std::stringstream os;
6✔
1577
        for(auto p : result) {
32✔
1578
            os << p << ",";
28✔
1579
        }
14✔
1580
        CHECK(os.str() == "./a,./d1,./d1/b,./d1/c,./d1/d2,./d1/d2/d,./e,");
4✔
1581
    }
2✔
1582
    {
1583
        TemporaryDirectory t(TempOpt::change_path);
6✔
1584
        generateFile("a");
4✔
1585
        fs::create_directory("d1");
4✔
1586
        fs::create_directory("d1/d2");
4✔
1587
        generateFile("d1/d2/b");
4✔
1588
        generateFile("e");
4✔
1589
        auto iter = fs::recursive_directory_iterator(".");
6✔
1590
        std::multimap<std::string, int> result;
6✔
1591
        while(iter != fs::recursive_directory_iterator()) {
20✔
1592
            result.insert(std::make_pair(iter->path().generic_string(), iter.depth()));
16✔
1593
            if(iter->path() == "./d1/d2") {
16✔
1594
                iter.disable_recursion_pending();
4✔
1595
            }
1596
            ++iter;
16✔
1597
        }
1598
        std::stringstream os;
6✔
1599
        for(auto p : result) {
20✔
1600
            os << "[" << p.first << "," << p.second << "],";
16✔
1601
        }
8✔
1602
        CHECK(os.str() == "[./a,0],[./d1,0],[./d1/d2,1],[./e,0],");
4✔
1603
    }
2✔
1604
    {
1605
        TemporaryDirectory t(TempOpt::change_path);
6✔
1606
        generateFile("a");
4✔
1607
        fs::create_directory("d1");
4✔
1608
        fs::create_directory("d1/d2");
4✔
1609
        generateFile("d1/d2/b");
4✔
1610
        generateFile("e");
4✔
1611
        auto iter = fs::recursive_directory_iterator(".");
6✔
1612
        std::multimap<std::string, int> result;
6✔
1613
        while(iter != fs::recursive_directory_iterator()) {
20✔
1614
            result.insert(std::make_pair(iter->path().generic_string(), iter.depth()));
16✔
1615
            if(iter->path() == "./d1/d2") {
16✔
1616
                iter.pop();
4✔
1617
            }
1618
            else {
1619
                ++iter;
12✔
1620
            }
1621
        }
1622
        std::stringstream os;
6✔
1623
        for(auto p : result) {
20✔
1624
            os << "[" << p.first << "," << p.second << "],";
16✔
1625
        }
8✔
1626
        CHECK(os.str() == "[./a,0],[./d1,0],[./d1/d2,1],[./e,0],");
4✔
1627
    }
2✔
1628
    if (is_symlink_creation_supported()) {
4✔
1629
        TemporaryDirectory t(TempOpt::change_path);
6✔
1630
        fs::create_directory("d1");
4✔
1631
        generateFile("d1/a");
4✔
1632
        fs::create_directory("d2");
4✔
1633
        generateFile("d2/b");
4✔
1634
        fs::create_directory_symlink("../d1", "d2/ds1");
4✔
1635
        fs::create_directory_symlink("d3", "d2/ds2");
4✔
1636
        std::multiset<std::string> result;
6✔
1637
        REQUIRE_NOTHROW([&](){
28✔
1638
            for (const auto& de : fs::recursive_directory_iterator("d2", fs::directory_options::follow_directory_symlink)) {
1639
                result.insert(de.path().generic_string());
1640
            }
1641
        }());
1642
        std::stringstream os;
6✔
1643
        for(const auto& p : result) {
20✔
1644
            os << p << ",";
16✔
1645
        }
1646
        CHECK(os.str() == "d2/b,d2/ds1,d2/ds1/a,d2/ds2,");
4✔
1647
        os.str("");
4✔
1648
        result.clear();
4✔
1649
        REQUIRE_NOTHROW([&](){
24✔
1650
          for (const auto& de : fs::recursive_directory_iterator("d2")) {
1651
              result.insert(de.path().generic_string());
1652
          }
1653
        }());
1654
         for(const auto& p : result) {
16✔
1655
            os << p << ",";
12✔
1656
        }
1657
        CHECK(os.str() == "d2/b,d2/ds1,d2/ds2,");
4✔
1658
    }
2✔
1659
}
4✔
1660

1661
TEST_CASE("fs.op.absolute - absolute", "[filesystem][operations][fs.op.absolute]")
4✔
1662
{
1663
    CHECK(fs::absolute("") == fs::current_path() / "");
4✔
1664
    CHECK(fs::absolute(fs::current_path()) == fs::current_path());
4✔
1665
    CHECK(fs::absolute(".") == fs::current_path() / ".");
4✔
1666
    CHECK((fs::absolute("..") == fs::current_path().parent_path() || fs::absolute("..") == fs::current_path() / ".."));
4✔
1667
    CHECK(fs::absolute("foo") == fs::current_path() / "foo");
4✔
1668
    std::error_code ec;
4✔
1669
    CHECK(fs::absolute("", ec) == fs::current_path() / "");
4✔
1670
    CHECK(!ec);
4✔
1671
    CHECK(fs::absolute("foo", ec) == fs::current_path() / "foo");
4✔
1672
    CHECK(!ec);
4✔
1673
}
4✔
1674

1675
TEST_CASE("fs.op.canonical - canonical", "[filesystem][operations][fs.op.canonical]")
4✔
1676
{
1677
    CHECK_THROWS_AS(fs::canonical(""), fs::filesystem_error);
10✔
1678
    {
1679
        std::error_code ec;
4✔
1680
        CHECK(fs::canonical("", ec) == "");
4✔
1681
        CHECK(ec);
4✔
1682
    }
1683
    CHECK(fs::canonical(fs::current_path()) == fs::current_path());
4✔
1684

1685
    CHECK(fs::canonical(".") == fs::current_path());
4✔
1686
    CHECK(fs::canonical("..") == fs::current_path().parent_path());
4✔
1687
    CHECK(fs::canonical("/") == fs::current_path().root_path());
4✔
1688
    CHECK_THROWS_AS(fs::canonical("foo"), fs::filesystem_error);
10✔
1689
    {
1690
        std::error_code ec;
4✔
1691
        CHECK_NOTHROW(fs::canonical("foo", ec));
4✔
1692
        CHECK(ec);
4✔
1693
    }
1694
    {
1695
        TemporaryDirectory t(TempOpt::change_path);
6✔
1696
        auto dir = t.path() / "d0";
6✔
1697
        fs::create_directories(dir / "d1");
4✔
1698
        generateFile(dir / "f0");
4✔
1699
        fs::path rel(dir.filename());
6✔
1700
        CHECK(fs::canonical(dir) == dir);
4✔
1701
        CHECK(fs::canonical(rel) == dir);
4✔
1702
        CHECK(fs::canonical(dir / "f0") == dir / "f0");
4✔
1703
        CHECK(fs::canonical(rel / "f0") == dir / "f0");
4✔
1704
        CHECK(fs::canonical(rel / "./f0") == dir / "f0");
4✔
1705
        CHECK(fs::canonical(rel / "d1/../f0") == dir / "f0");
4✔
1706
    }
2✔
1707

1708
    if (is_symlink_creation_supported()) {
4✔
1709
        TemporaryDirectory t(TempOpt::change_path);
6✔
1710
        fs::create_directory(t.path() / "dir1");
4✔
1711
        generateFile(t.path() / "dir1/test1");
4✔
1712
        fs::create_directory(t.path() / "dir2");
4✔
1713
        fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym");
4✔
1714
        CHECK(fs::canonical(t.path() / "dir2/dirSym/test1") == t.path() / "dir1/test1");
4✔
1715
    }
2✔
1716
}
4✔
1717

1718
TEST_CASE("fs.op.copy - copy", "[filesystem][operations][fs.op.copy]")
4✔
1719
{
1720
    {
1721
        TemporaryDirectory t(TempOpt::change_path);
6✔
1722
        std::error_code ec;
4✔
1723
        fs::create_directory("dir1");
4✔
1724
        generateFile("dir1/file1");
4✔
1725
        generateFile("dir1/file2");
4✔
1726
        fs::create_directory("dir1/dir2");
4✔
1727
        generateFile("dir1/dir2/file3");
4✔
1728
        CHECK_NOTHROW(fs::copy("dir1", "dir3"));
4✔
1729
        CHECK(fs::exists("dir3/file1"));
4✔
1730
        CHECK(fs::exists("dir3/file2"));
4✔
1731
        CHECK(!fs::exists("dir3/dir2"));
4✔
1732
        CHECK_NOTHROW(fs::copy("dir1", "dir4", fs::copy_options::recursive, ec));
4✔
1733
        CHECK(!ec);
4✔
1734
        CHECK(fs::exists("dir4/file1"));
4✔
1735
        CHECK(fs::exists("dir4/file2"));
4✔
1736
        CHECK(fs::exists("dir4/dir2/file3"));
4✔
1737
        fs::create_directory("dir5");
4✔
1738
        generateFile("dir5/file1");
4✔
1739
        CHECK_THROWS_AS(fs::copy("dir1/file1", "dir5/file1"), fs::filesystem_error);
14✔
1740
        CHECK_NOTHROW(fs::copy("dir1/file1", "dir5/file1", fs::copy_options::skip_existing));
4✔
1741
    }
2✔
1742
    if (is_symlink_creation_supported()) {
4✔
1743
        TemporaryDirectory t(TempOpt::change_path);
6✔
1744
        std::error_code ec;
4✔
1745
        fs::create_directory("dir1");
4✔
1746
        generateFile("dir1/file1");
4✔
1747
        generateFile("dir1/file2");
4✔
1748
        fs::create_directory("dir1/dir2");
4✔
1749
        generateFile("dir1/dir2/file3");
4✔
1750
#ifdef TEST_LWG_2682_BEHAVIOUR
1751
        REQUIRE_THROWS_AS(fs::copy("dir1", "dir3", fs::copy_options::create_symlinks | fs::copy_options::recursive), fs::filesystem_error);
14✔
1752
#else
1753
        REQUIRE_NOTHROW(fs::copy("dir1", "dir3", fs::copy_options::create_symlinks | fs::copy_options::recursive));
1754
        CHECK(!ec);
1755
        CHECK(fs::exists("dir3/file1"));
1756
        CHECK(fs::is_symlink("dir3/file1"));
1757
        CHECK(fs::exists("dir3/file2"));
1758
        CHECK(fs::is_symlink("dir3/file2"));
1759
        CHECK(fs::exists("dir3/dir2/file3"));
1760
        CHECK(fs::is_symlink("dir3/dir2/file3"));
1761
#endif
1762
    }
2✔
1763
#ifndef GHC_OS_WEB
1764
    {
1765
        TemporaryDirectory t(TempOpt::change_path);
6✔
1766
        std::error_code ec;
4✔
1767
        fs::create_directory("dir1");
4✔
1768
        generateFile("dir1/file1");
4✔
1769
        generateFile("dir1/file2");
4✔
1770
        fs::create_directory("dir1/dir2");
4✔
1771
        generateFile("dir1/dir2/file3");
4✔
1772
        auto f1hl = fs::hard_link_count("dir1/file1");
4✔
1773
        auto f2hl = fs::hard_link_count("dir1/file2");
4✔
1774
        auto f3hl = fs::hard_link_count("dir1/dir2/file3");
4✔
1775
        CHECK_NOTHROW(fs::copy("dir1", "dir3", fs::copy_options::create_hard_links | fs::copy_options::recursive, ec));
4✔
1776
        REQUIRE(!ec);
4✔
1777
        CHECK(fs::exists("dir3/file1"));
4✔
1778
        CHECK(fs::hard_link_count("dir1/file1") == f1hl + 1);
4✔
1779
        CHECK(fs::exists("dir3/file2"));
4✔
1780
        CHECK(fs::hard_link_count("dir1/file2") == f2hl + 1);
4✔
1781
        CHECK(fs::exists("dir3/dir2/file3"));
4✔
1782
        CHECK(fs::hard_link_count("dir1/dir2/file3") == f3hl + 1);
4✔
1783
    }
2✔
1784
#endif
1785
}
4✔
1786

1787
TEST_CASE("fs.op.copy_file - copy_file", "[filesystem][operations][fs.op.copy_file]")
4✔
1788
{
1789
    TemporaryDirectory t(TempOpt::change_path);
6✔
1790
    std::error_code ec;
4✔
1791
    generateFile("foo", 100);
4✔
1792
    CHECK(!fs::exists("bar"));
4✔
1793
    CHECK(fs::copy_file("foo", "bar"));
4✔
1794
    CHECK(fs::exists("bar"));
4✔
1795
    CHECK(fs::file_size("foo") == fs::file_size("bar"));
4✔
1796
    CHECK(fs::copy_file("foo", "bar2", ec));
4✔
1797
    CHECK(!ec);
4✔
1798
    std::this_thread::sleep_for(std::chrono::seconds(1));
4✔
1799
    generateFile("foo2", 200);
4✔
1800
    CHECK(fs::copy_file("foo2", "bar", fs::copy_options::update_existing));
4✔
1801
    CHECK(fs::file_size("bar") == 200);
4✔
1802
    CHECK(!fs::copy_file("foo", "bar", fs::copy_options::update_existing));
4✔
1803
    CHECK(fs::file_size("bar") == 200);
4✔
1804
    CHECK(fs::copy_file("foo", "bar", fs::copy_options::overwrite_existing));
4✔
1805
    CHECK(fs::file_size("bar") == 100);
4✔
1806
    CHECK_THROWS_AS(fs::copy_file("foobar", "foobar2"), fs::filesystem_error);
14✔
1807
    CHECK_NOTHROW(fs::copy_file("foobar", "foobar2", ec));
4✔
1808
    CHECK(ec);
4✔
1809
    CHECK(!fs::exists("foobar"));
4✔
1810
    fs::path file1("temp1.txt");
6✔
1811
    fs::path file2("temp2.txt");
6✔
1812
    generateFile(file1, 200);
4✔
1813
    generateFile(file2, 200);
4✔
1814
    auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write;
4✔
1815
    CHECK_NOTHROW(fs::permissions(file1, allWrite, fs::perm_options::remove));
4✔
1816
    CHECK((fs::status(file1).permissions() & fs::perms::owner_write) != fs::perms::owner_write);
4✔
1817
    CHECK_NOTHROW(fs::permissions(file2, allWrite, fs::perm_options::add));
4✔
1818
    CHECK((fs::status(file2).permissions() & fs::perms::owner_write) == fs::perms::owner_write);
4✔
1819
    fs::copy_file(file1, file2, fs::copy_options::overwrite_existing);
4✔
1820
    CHECK((fs::status(file2).permissions() & fs::perms::owner_write) != fs::perms::owner_write);
4✔
1821
    CHECK_NOTHROW(fs::permissions(file1, allWrite, fs::perm_options::add));
4✔
1822
    CHECK_NOTHROW(fs::permissions(file2, allWrite, fs::perm_options::add));
4✔
1823
}
4✔
1824

1825
TEST_CASE("fs.op.copy_symlink - copy_symlink", "[filesystem][operations][fs.op.copy_symlink]")
4✔
1826
{
1827
    TemporaryDirectory t(TempOpt::change_path);
6✔
1828
    std::error_code ec;
4✔
1829
    generateFile("foo");
4✔
1830
    fs::create_directory("dir");
4✔
1831
    if (is_symlink_creation_supported()) {
4✔
1832
        fs::create_symlink("foo", "sfoo");
4✔
1833
        fs::create_directory_symlink("dir", "sdir");
4✔
1834
        CHECK_NOTHROW(fs::copy_symlink("sfoo", "sfooc"));
4✔
1835
        CHECK(fs::exists("sfooc"));
4✔
1836
        CHECK_NOTHROW(fs::copy_symlink("sfoo", "sfooc2", ec));
4✔
1837
        CHECK(fs::exists("sfooc2"));
4✔
1838
        CHECK(!ec);
4✔
1839
        CHECK_NOTHROW(fs::copy_symlink("sdir", "sdirc"));
4✔
1840
        CHECK(fs::exists("sdirc"));
4✔
1841
        CHECK_NOTHROW(fs::copy_symlink("sdir", "sdirc2", ec));
4✔
1842
        CHECK(fs::exists("sdirc2"));
4✔
1843
        CHECK(!ec);
4✔
1844
    }
1845
    CHECK_THROWS_AS(fs::copy_symlink("bar", "barc"), fs::filesystem_error);
14✔
1846
    CHECK_NOTHROW(fs::copy_symlink("bar", "barc", ec));
4✔
1847
    CHECK(ec);
4✔
1848
}
4✔
1849

1850
TEST_CASE("fs.op.create_directories - create_directories", "[filesystem][operations][fs.op.create_directories]")
4✔
1851
{
1852
    TemporaryDirectory t;
6✔
1853
    fs::path p = t.path() / "testdir";
6✔
1854
    fs::path p2 = p / "nested";
6✔
1855
    REQUIRE(!fs::exists(p));
4✔
1856
    REQUIRE(!fs::exists(p2));
4✔
1857
    CHECK(fs::create_directories(p2));
4✔
1858
    CHECK(fs::is_directory(p));
4✔
1859
    CHECK(fs::is_directory(p2));
4✔
1860
    CHECK(!fs::create_directories(p2));
4✔
1861
#ifdef TEST_LWG_2935_BEHAVIOUR
1862
    INFO("This test expects LWG #2935 result conformance.");
1863
    p = t.path() / "testfile";
1864
    generateFile(p);
1865
    CHECK(fs::is_regular_file(p));
1866
    CHECK(!fs::is_directory(p));
1867
    bool created = false;
1868
    CHECK_NOTHROW((created = fs::create_directories(p)));
1869
    CHECK(!created);
1870
    CHECK(fs::is_regular_file(p));
1871
    CHECK(!fs::is_directory(p));
1872
    std::error_code ec;
1873
    CHECK_NOTHROW((created = fs::create_directories(p, ec)));
1874
    CHECK(!created);
1875
    CHECK(!ec);
1876
    CHECK(fs::is_regular_file(p));
1877
    CHECK(!fs::is_directory(p));
1878
    CHECK(!fs::create_directories(p, ec));
1879
#else
1880
    INFO("This test expects conformance with P1164R1. (implemented by GCC with issue #86910.)");
6✔
1881
    p = t.path() / "testfile";
4✔
1882
    generateFile(p);
4✔
1883
    CHECK(fs::is_regular_file(p));
4✔
1884
    CHECK(!fs::is_directory(p));
4✔
1885
    CHECK_THROWS_AS(fs::create_directories(p), fs::filesystem_error);
6✔
1886
    CHECK(fs::is_regular_file(p));
4✔
1887
    CHECK(!fs::is_directory(p));
4✔
1888
    std::error_code ec;
4✔
1889
    CHECK_NOTHROW(fs::create_directories(p, ec));
4✔
1890
    CHECK(ec);
4✔
1891
    CHECK(fs::is_regular_file(p));
4✔
1892
    CHECK(!fs::is_directory(p));
4✔
1893
    CHECK(!fs::create_directories(p, ec));
4✔
1894
#endif
1895
}
4✔
1896

1897
TEST_CASE("fs.op.create_directory - create_directory", "[filesystem][operations][fs.op.create_directory]")
4✔
1898
{
1899
    TemporaryDirectory t;
6✔
1900
    fs::path p = t.path() / "testdir";
6✔
1901
    REQUIRE(!fs::exists(p));
4✔
1902
    CHECK(fs::create_directory(p));
4✔
1903
    CHECK(fs::is_directory(p));
4✔
1904
    CHECK(!fs::is_regular_file(p));
4✔
1905
    CHECK(fs::create_directory(p / "nested", p));
4✔
1906
    CHECK(fs::is_directory(p / "nested"));
4✔
1907
    CHECK(!fs::is_regular_file(p / "nested"));
4✔
1908
#ifdef TEST_LWG_2935_BEHAVIOUR
1909
    INFO("This test expects LWG #2935 result conformance.");
1910
    p = t.path() / "testfile";
1911
    generateFile(p);
1912
    CHECK(fs::is_regular_file(p));
1913
    CHECK(!fs::is_directory(p));
1914
    bool created = false;
1915
    CHECK_NOTHROW((created = fs::create_directory(p)));
1916
    CHECK(!created);
1917
    CHECK(fs::is_regular_file(p));
1918
    CHECK(!fs::is_directory(p));
1919
    std::error_code ec;
1920
    CHECK_NOTHROW((created = fs::create_directory(p, ec)));
1921
    CHECK(!created);
1922
    CHECK(!ec);
1923
    CHECK(fs::is_regular_file(p));
1924
    CHECK(!fs::is_directory(p));
1925
    CHECK(!fs::create_directories(p, ec));
1926
#else
1927
    INFO("This test expects conformance with P1164R1. (implemented by GCC with issue #86910.)");
6✔
1928
    p = t.path() / "testfile";
4✔
1929
    generateFile(p);
4✔
1930
    CHECK(fs::is_regular_file(p));
4✔
1931
    CHECK(!fs::is_directory(p));
4✔
1932
    REQUIRE_THROWS_AS(fs::create_directory(p), fs::filesystem_error);
6✔
1933
    CHECK(fs::is_regular_file(p));
4✔
1934
    CHECK(!fs::is_directory(p));
4✔
1935
    std::error_code ec;
4✔
1936
    REQUIRE_NOTHROW(fs::create_directory(p, ec));
4✔
1937
    CHECK(ec);
4✔
1938
    CHECK(fs::is_regular_file(p));
4✔
1939
    CHECK(!fs::is_directory(p));
4✔
1940
    CHECK(!fs::create_directory(p, ec));
4✔
1941
#endif
1942
}
4✔
1943

1944
TEST_CASE("fs.op.create_directory_symlink - create_directory_symlink", "[filesystem][operations][fs.op.create_directory_symlink]")
4✔
1945
{
1946
    if (is_symlink_creation_supported()) {
4✔
1947
        TemporaryDirectory t;
6✔
1948
        fs::create_directory(t.path() / "dir1");
4✔
1949
        generateFile(t.path() / "dir1/test1");
4✔
1950
        fs::create_directory(t.path() / "dir2");
4✔
1951
        fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym");
4✔
1952
        CHECK(fs::exists(t.path() / "dir2/dirSym"));
4✔
1953
        CHECK(fs::is_symlink(t.path() / "dir2/dirSym"));
4✔
1954
        CHECK(fs::exists(t.path() / "dir2/dirSym/test1"));
4✔
1955
        CHECK(fs::is_regular_file(t.path() / "dir2/dirSym/test1"));
4✔
1956
        CHECK_THROWS_AS(fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym"), fs::filesystem_error);
22✔
1957
        std::error_code ec;
4✔
1958
        CHECK_NOTHROW(fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym", ec));
4✔
1959
        CHECK(ec);
4✔
1960
    }
2✔
1961
}
4✔
1962

1963
TEST_CASE("fs.op.create_hard_link - create_hard_link", "[filesystem][operations][fs.op.create_hard_link]")
4✔
1964
{
1965
#ifndef GHC_OS_WEB
1966
    TemporaryDirectory t(TempOpt::change_path);
6✔
1967
    std::error_code ec;
4✔
1968
    generateFile("foo", 1234);
4✔
1969
    CHECK_NOTHROW(fs::create_hard_link("foo", "bar"));
4✔
1970
    CHECK(fs::exists("bar"));
4✔
1971
    CHECK(!fs::is_symlink("bar"));
4✔
1972
    CHECK_NOTHROW(fs::create_hard_link("foo", "bar2", ec));
4✔
1973
    CHECK(fs::exists("bar2"));
4✔
1974
    CHECK(!fs::is_symlink("bar2"));
4✔
1975
    CHECK(!ec);
4✔
1976
    CHECK_THROWS_AS(fs::create_hard_link("nofoo", "bar"), fs::filesystem_error);
14✔
1977
    CHECK_NOTHROW(fs::create_hard_link("nofoo", "bar", ec));
4✔
1978
    CHECK(ec);
4✔
1979
#endif
1980
}
4✔
1981

1982
TEST_CASE("fs.op.create_symlink - create_symlink", "[filesystem][operations][fs.op.create_symlink]")
4✔
1983
{
1984
    if (is_symlink_creation_supported()) {
4✔
1985
        TemporaryDirectory t;
6✔
1986
        fs::create_directory(t.path() / "dir1");
4✔
1987
        generateFile(t.path() / "dir1/test1");
4✔
1988
        fs::create_directory(t.path() / "dir2");
4✔
1989
        fs::create_symlink(t.path() / "dir1/test1", t.path() / "dir2/fileSym");
4✔
1990
        CHECK(fs::exists(t.path() / "dir2/fileSym"));
4✔
1991
        CHECK(fs::is_symlink(t.path() / "dir2/fileSym"));
4✔
1992
        CHECK(fs::exists(t.path() / "dir2/fileSym"));
4✔
1993
        CHECK(fs::is_regular_file(t.path() / "dir2/fileSym"));
4✔
1994
        CHECK_THROWS_AS(fs::create_symlink(t.path() / "dir1", t.path() / "dir2/fileSym"), fs::filesystem_error);
22✔
1995
        std::error_code ec;
4✔
1996
        CHECK_NOTHROW(fs::create_symlink(t.path() / "dir1", t.path() / "dir2/fileSym", ec));
4✔
1997
        CHECK(ec);
4✔
1998
    }
2✔
1999
}
4✔
2000

2001
TEST_CASE("fs.op.current_path - current_path", "[filesystem][operations][fs.op.current_path]")
4✔
2002
{
2003
    TemporaryDirectory t;
6✔
2004
    std::error_code ec;
4✔
2005
    fs::path p1 = fs::current_path();
6✔
2006
    CHECK_NOTHROW(fs::current_path(t.path()));
4✔
2007
    CHECK(p1 != fs::current_path());
4✔
2008
    CHECK_NOTHROW(fs::current_path(p1, ec));
4✔
2009
    CHECK(!ec);
4✔
2010
    CHECK_THROWS_AS(fs::current_path(t.path() / "foo"), fs::filesystem_error);
14✔
2011
    CHECK(p1 == fs::current_path());
4✔
2012
    CHECK_NOTHROW(fs::current_path(t.path() / "foo", ec));
4✔
2013
    CHECK(ec);
4✔
2014
}
4✔
2015

2016
TEST_CASE("fs.op.equivalent - equivalent", "[filesystem][operations][fs.op.equivalent]")
4✔
2017
{
2018
    TemporaryDirectory t(TempOpt::change_path);
6✔
2019
    generateFile("foo", 1234);
4✔
2020
    CHECK(fs::equivalent(t.path() / "foo", "foo"));
4✔
2021
    if (is_symlink_creation_supported()) {
4✔
2022
        std::error_code ec(42, std::system_category());
4✔
2023
        fs::create_symlink("foo", "foo2");
4✔
2024
        CHECK(fs::equivalent("foo", "foo2"));
4✔
2025
        CHECK(fs::equivalent("foo", "foo2", ec));
4✔
2026
        CHECK(!ec);
4✔
2027
    }
2028
#ifdef TEST_LWG_2937_BEHAVIOUR
2029
    INFO("This test expects LWG #2937 result conformance.");
6✔
2030
    std::error_code ec;
4✔
2031
    bool result = false;
4✔
2032
    REQUIRE_THROWS_AS(fs::equivalent("foo", "foo3"), fs::filesystem_error);
14✔
2033
    CHECK_NOTHROW(result = fs::equivalent("foo", "foo3", ec));
4✔
2034
    CHECK(!result);
4✔
2035
    CHECK(ec);
4✔
2036
    ec.clear();
4✔
2037
    CHECK_THROWS_AS(fs::equivalent("foo3", "foo"), fs::filesystem_error);
14✔
2038
    CHECK_NOTHROW(result = fs::equivalent("foo3", "foo", ec));
4✔
2039
    CHECK(!result);
4✔
2040
    CHECK(ec);
4✔
2041
    ec.clear();
4✔
2042
    CHECK_THROWS_AS(fs::equivalent("foo3", "foo4"), fs::filesystem_error);
14✔
2043
    CHECK_NOTHROW(result = fs::equivalent("foo3", "foo4", ec));
4✔
2044
    CHECK(!result);
4✔
2045
    CHECK(ec);
4✔
2046
#else
2047
    INFO("This test expects conformance predating LWG #2937 result.");
2048
    std::error_code ec;
2049
    bool result = false;
2050
    REQUIRE_NOTHROW(result = fs::equivalent("foo", "foo3"));
2051
    CHECK(!result);
2052
    CHECK_NOTHROW(result = fs::equivalent("foo", "foo3", ec));
2053
    CHECK(!result);
2054
    CHECK(!ec);
2055
    ec.clear();
2056
    CHECK_NOTHROW(result = fs::equivalent("foo3", "foo"));
2057
    CHECK(!result);
2058
    CHECK_NOTHROW(result = fs::equivalent("foo3", "foo", ec));
2059
    CHECK(!result);
2060
    CHECK(!ec);
2061
    ec.clear();
2062
    CHECK_THROWS_AS(result = fs::equivalent("foo4", "foo3"), fs::filesystem_error);
2063
    CHECK(!result);
2064
    CHECK_NOTHROW(result = fs::equivalent("foo4", "foo3", ec));
2065
    CHECK(!result);
2066
    CHECK(ec);
2067
#endif
2068
}
4✔
2069

2070
TEST_CASE("fs.op.exists - exists", "[filesystem][operations][fs.op.exists]")
4✔
2071
{
2072
    TemporaryDirectory t(TempOpt::change_path);
6✔
2073
    std::error_code ec;
4✔
2074
    CHECK(!fs::exists(""));
4✔
2075
    CHECK(!fs::exists("foo"));
4✔
2076
    CHECK(!fs::exists("foo", ec));
4✔
2077
    CHECK(!ec);
4✔
2078
    ec = std::error_code(42, std::system_category());
4✔
2079
    CHECK(!fs::exists("foo", ec));
4✔
2080
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
2081
    CHECK(!fs::exists(u8"foo"));
2✔
2082
#endif
2083
    CHECK(!ec);
4✔
2084
    ec.clear();
4✔
2085
    CHECK(fs::exists(t.path()));
4✔
2086
    CHECK(fs::exists(t.path(), ec));
4✔
2087
    CHECK(!ec);
4✔
2088
    ec = std::error_code(42, std::system_category());
4✔
2089
    CHECK(fs::exists(t.path(), ec));
4✔
2090
    CHECK(!ec);
4✔
2091
#if defined(GHC_OS_WINDOWS) && !defined(GHC_FILESYSTEM_FWD)
2092
    if (::GetFileAttributesW(L"C:\\fs-test") != INVALID_FILE_ATTRIBUTES) {
2093
        CHECK(fs::exists("C:\\fs-test"));    
2094
    }
2095
#endif
2096
}
4✔
2097

2098
TEST_CASE("fs.op.file_size - file_size", "[filesystem][operations][fs.op.file_size]")
4✔
2099
{
2100
    TemporaryDirectory t(TempOpt::change_path);
6✔
2101
    std::error_code ec;
4✔
2102
    generateFile("foo", 0);
4✔
2103
    generateFile("bar", 1234);
4✔
2104
    CHECK(fs::file_size("foo") == 0);
4✔
2105
    ec = std::error_code(42, std::system_category());
4✔
2106
    CHECK(fs::file_size("foo", ec) == 0);
4✔
2107
    CHECK(!ec);
4✔
2108
    ec.clear();
4✔
2109
    CHECK(fs::file_size("bar") == 1234);
4✔
2110
    ec = std::error_code(42, std::system_category());
4✔
2111
    CHECK(fs::file_size("bar", ec) == 1234);
4✔
2112
    CHECK(!ec);
4✔
2113
    ec.clear();
4✔
2114
    CHECK_THROWS_AS(fs::file_size("foobar"), fs::filesystem_error);
10✔
2115
    CHECK(fs::file_size("foobar", ec) == static_cast<uintmax_t>(-1));
4✔
2116
    CHECK(ec);
4✔
2117
    ec.clear();
4✔
2118
}
4✔
2119

2120
#ifndef GHC_OS_WINDOWS
2121
static uintmax_t getHardlinkCount(const fs::path& p)
8✔
2122
{
2123
    struct stat st = {};
8✔
2124
    auto rc = ::lstat(p.c_str(), &st);
8✔
2125
    return rc == 0 ? st.st_nlink : ~0u;
8✔
2126
}
2127
#endif
2128

2129
TEST_CASE("fs.op.hard_link_count - hard_link_count", "[filesystem][operations][fs.op.hard_link_count]")
4✔
2130
{
2131
#ifndef GHC_OS_WEB
2132
    TemporaryDirectory t(TempOpt::change_path);
6✔
2133
    std::error_code ec;
4✔
2134
#ifdef GHC_OS_WINDOWS
2135
    // windows doesn't implement "."/".." as hardlinks, so it
2136
    // starts with 1 and subdirectories don't change the count
2137
    CHECK(fs::hard_link_count(t.path()) == 1);
2138
    fs::create_directory("dir");
2139
    CHECK(fs::hard_link_count(t.path()) == 1);
2140
#else
2141
    // unix/bsd/linux typically implements "."/".." as hardlinks
2142
    // so an empty dir has 2 (from parent and the ".") and
2143
    // adding a subdirectory adds one due to its ".."
2144
    CHECK(fs::hard_link_count(t.path()) == getHardlinkCount(t.path()));
4✔
2145
    fs::create_directory("dir");
4✔
2146
    CHECK(fs::hard_link_count(t.path()) == getHardlinkCount(t.path()));
4✔
2147
#endif
2148
    generateFile("foo");
4✔
2149
    CHECK(fs::hard_link_count(t.path() / "foo") == 1);
4✔
2150
    ec = std::error_code(42, std::system_category());
4✔
2151
    CHECK(fs::hard_link_count(t.path() / "foo", ec) == 1);
4✔
2152
    CHECK(!ec);
4✔
2153
    CHECK_THROWS_AS(fs::hard_link_count(t.path() / "bar"), fs::filesystem_error);
14✔
2154
    CHECK_NOTHROW(fs::hard_link_count(t.path() / "bar", ec));
4✔
2155
    CHECK(ec);
4✔
2156
    ec.clear();
4✔
2157
#else
2158
    WARN("Test for unsupportet features are disabled on JS/Wasm target.");
2159
#endif
2160
}
4✔
2161

2162
class FileTypeMixFixture
2163
{
2164
public:
2165
    FileTypeMixFixture()
32✔
2166
        : _t(TempOpt::change_path)
32✔
2167
        , _hasFifo(false)
16✔
2168
        , _hasSocket(false)
32✔
2169
    {
2170
        generateFile("regular");
32✔
2171
        fs::create_directory("directory");
32✔
2172
        if (is_symlink_creation_supported()) {
32✔
2173
            fs::create_symlink("regular", "file_symlink");
32✔
2174
            fs::create_directory_symlink("directory", "dir_symlink");
32✔
2175
        }
2176
#if !defined(GHC_OS_WINDOWS) && !defined(GHC_OS_WEB)
2177
        REQUIRE(::mkfifo("fifo", 0644) == 0);
32✔
2178
        _hasFifo = true;
32✔
2179
        struct ::sockaddr_un addr;
2180
        addr.sun_family = AF_UNIX;
32✔
2181
        std::strncpy(addr.sun_path, "socket", sizeof(addr.sun_path));
32✔
2182
        int fd = socket(PF_UNIX, SOCK_STREAM, 0);
32✔
2183
        bind(fd, (struct sockaddr*)&addr, sizeof addr);
32✔
2184
        _hasSocket = true;
32✔
2185
#endif
2186
    }
32✔
2187

2188
    ~FileTypeMixFixture() {}
32✔
2189

2190
    bool has_fifo() const { return _hasFifo; }
32✔
2191

2192
    bool has_socket() const { return _hasSocket; }
32✔
2193

2194
    fs::path block_path() const
64✔
2195
    {
2196
        std::error_code ec;
64✔
2197
        if (fs::exists("/dev/sda", ec)) {
64✔
2198
            return "/dev/sda";
64✔
2199
        }
2200
        else if (fs::exists("/dev/disk0", ec)) {
×
2201
            return "/dev/disk0";
×
2202
        }
2203
        return fs::path();
×
2204
    }
2205

2206
    fs::path character_path() const
64✔
2207
    {
2208
#ifndef GHC_OS_SOLARIS
2209
        std::error_code ec;
64✔
2210
        if (fs::exists("/dev/null", ec)) {
64✔
2211
            return "/dev/null";
64✔
2212
        }
2213
        else if (fs::exists("NUL", ec)) {
×
2214
            return "NUL";
×
2215
        }
2216
#endif
2217
        return fs::path();
×
2218
    }
2219
    fs::path temp_path() const { return _t.path(); }
2220

2221
private:
2222
    TemporaryDirectory _t;
2223
    bool _hasFifo;
2224
    bool _hasSocket;
2225
};
2226

2227
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_block_file - is_block_file", "[filesystem][operations][fs.op.is_block_file]")
4✔
2228
{
2229
    std::error_code ec;
4✔
2230
    CHECK(!fs::is_block_file("directory"));
4✔
2231
    CHECK(!fs::is_block_file("regular"));
4✔
2232
    if (is_symlink_creation_supported()) {
4✔
2233
        CHECK(!fs::is_block_file("dir_symlink"));
4✔
2234
        CHECK(!fs::is_block_file("file_symlink"));
4✔
2235
    }
2236
    CHECK((has_fifo() ? !fs::is_block_file("fifo") : true));
4✔
2237
    CHECK((has_socket() ? !fs::is_block_file("socket") : true));
4✔
2238
    CHECK((block_path().empty() ? true : fs::is_block_file(block_path())));
4✔
2239
    CHECK((character_path().empty() ? true : !fs::is_block_file(character_path())));
4✔
2240
    CHECK_NOTHROW(fs::is_block_file("notfound"));
4✔
2241
    CHECK_NOTHROW(fs::is_block_file("notfound", ec));
4✔
2242
    CHECK(ec);
4✔
2243
    ec.clear();
4✔
2244
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::none)));
4✔
2245
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::not_found)));
4✔
2246
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::regular)));
4✔
2247
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::directory)));
4✔
2248
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::symlink)));
4✔
2249
    CHECK(fs::is_block_file(fs::file_status(fs::file_type::block)));
4✔
2250
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::character)));
4✔
2251
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::fifo)));
4✔
2252
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::socket)));
4✔
2253
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::unknown)));
4✔
2254
}
4✔
2255

2256
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_character_file - is_character_file", "[filesystem][operations][fs.op.is_character_file]")
4✔
2257
{
2258
    std::error_code ec;
4✔
2259
    CHECK(!fs::is_character_file("directory"));
4✔
2260
    CHECK(!fs::is_character_file("regular"));
4✔
2261
    if (is_symlink_creation_supported()) {
4✔
2262
        CHECK(!fs::is_character_file("dir_symlink"));
4✔
2263
        CHECK(!fs::is_character_file("file_symlink"));
4✔
2264
    }
2265
    CHECK((has_fifo() ? !fs::is_character_file("fifo") : true));
4✔
2266
    CHECK((has_socket() ? !fs::is_character_file("socket") : true));
4✔
2267
    CHECK((block_path().empty() ? true : !fs::is_character_file(block_path())));
4✔
2268
    CHECK((character_path().empty() ? true : fs::is_character_file(character_path())));
4✔
2269
    CHECK_NOTHROW(fs::is_character_file("notfound"));
4✔
2270
    CHECK_NOTHROW(fs::is_character_file("notfound", ec));
4✔
2271
    CHECK(ec);
4✔
2272
    ec.clear();
4✔
2273
    CHECK(!fs::is_character_file(fs::file_status(fs::file_type::none)));
4✔
2274
    CHECK(!fs::is_character_file(fs::file_status(fs::file_type::not_found)));
4✔
2275
    CHECK(!fs::is_character_file(fs::file_status(fs::file_type::regular)));
4✔
2276
    CHECK(!fs::is_character_file(fs::file_status(fs::file_type::directory)));
4✔
2277
    CHECK(!fs::is_character_file(fs::file_status(fs::file_type::symlink)));
4✔
2278
    CHECK(!fs::is_character_file(fs::file_status(fs::file_type::block)));
4✔
2279
    CHECK(fs::is_character_file(fs::file_status(fs::file_type::character)));
4✔
2280
    CHECK(!fs::is_character_file(fs::file_status(fs::file_type::fifo)));
4✔
2281
    CHECK(!fs::is_character_file(fs::file_status(fs::file_type::socket)));
4✔
2282
    CHECK(!fs::is_character_file(fs::file_status(fs::file_type::unknown)));
4✔
2283
}
4✔
2284

2285
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_directory - is_directory", "[filesystem][operations][fs.op.is_directory]")
4✔
2286
{
2287
    std::error_code ec;
4✔
2288
    CHECK(fs::is_directory("directory"));
4✔
2289
    CHECK(!fs::is_directory("regular"));
4✔
2290
    if (is_symlink_creation_supported()) {
4✔
2291
        CHECK(fs::is_directory("dir_symlink"));
4✔
2292
        CHECK(!fs::is_directory("file_symlink"));
4✔
2293
    }
2294
    CHECK((has_fifo() ? !fs::is_directory("fifo") : true));
4✔
2295
    CHECK((has_socket() ? !fs::is_directory("socket") : true));
4✔
2296
    CHECK((block_path().empty() ? true : !fs::is_directory(block_path())));
4✔
2297
    CHECK((character_path().empty() ? true : !fs::is_directory(character_path())));
4✔
2298
    CHECK_NOTHROW(fs::is_directory("notfound"));
4✔
2299
    CHECK_NOTHROW(fs::is_directory("notfound", ec));
4✔
2300
    CHECK(ec);
4✔
2301
    ec.clear();
4✔
2302
    CHECK(!fs::is_directory(fs::file_status(fs::file_type::none)));
4✔
2303
    CHECK(!fs::is_directory(fs::file_status(fs::file_type::not_found)));
4✔
2304
    CHECK(!fs::is_directory(fs::file_status(fs::file_type::regular)));
4✔
2305
    CHECK(fs::is_directory(fs::file_status(fs::file_type::directory)));
4✔
2306
    CHECK(!fs::is_directory(fs::file_status(fs::file_type::symlink)));
4✔
2307
    CHECK(!fs::is_directory(fs::file_status(fs::file_type::block)));
4✔
2308
    CHECK(!fs::is_directory(fs::file_status(fs::file_type::character)));
4✔
2309
    CHECK(!fs::is_directory(fs::file_status(fs::file_type::fifo)));
4✔
2310
    CHECK(!fs::is_directory(fs::file_status(fs::file_type::socket)));
4✔
2311
    CHECK(!fs::is_directory(fs::file_status(fs::file_type::unknown)));
4✔
2312
}
4✔
2313

2314
TEST_CASE("fs.op.is_empty - is_empty", "[filesystem][operations][fs.op.is_empty]")
4✔
2315
{
2316
    TemporaryDirectory t(TempOpt::change_path);
6✔
2317
    std::error_code ec;
4✔
2318
    CHECK(fs::is_empty(t.path()));
4✔
2319
    CHECK(fs::is_empty(t.path(), ec));
4✔
2320
    CHECK(!ec);
4✔
2321
    generateFile("foo", 0);
4✔
2322
    generateFile("bar", 1234);
4✔
2323
    CHECK(fs::is_empty("foo"));
4✔
2324
    CHECK(fs::is_empty("foo", ec));
4✔
2325
    CHECK(!ec);
4✔
2326
    CHECK(!fs::is_empty("bar"));
4✔
2327
    CHECK(!fs::is_empty("bar", ec));
4✔
2328
    CHECK(!ec);
4✔
2329
    CHECK_THROWS_AS(fs::is_empty("foobar"), fs::filesystem_error);
10✔
2330
    bool result = false;
4✔
2331
    CHECK_NOTHROW(result = fs::is_empty("foobar", ec));
4✔
2332
    CHECK(!result);
4✔
2333
    CHECK(ec);
4✔
2334
}
4✔
2335

2336
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_fifo - is_fifo", "[filesystem][operations][fs.op.is_fifo]")
4✔
2337
{
2338
    std::error_code ec;
4✔
2339
    CHECK(!fs::is_fifo("directory"));
4✔
2340
    CHECK(!fs::is_fifo("regular"));
4✔
2341
    if (is_symlink_creation_supported()) {
4✔
2342
        CHECK(!fs::is_fifo("dir_symlink"));
4✔
2343
        CHECK(!fs::is_fifo("file_symlink"));
4✔
2344
    }
2345
    CHECK((has_fifo() ? fs::is_fifo("fifo") : true));
4✔
2346
    CHECK((has_socket() ? !fs::is_fifo("socket") : true));
4✔
2347
    CHECK((block_path().empty() ? true : !fs::is_fifo(block_path())));
4✔
2348
    CHECK((character_path().empty() ? true : !fs::is_fifo(character_path())));
4✔
2349
    CHECK_NOTHROW(fs::is_fifo("notfound"));
4✔
2350
    CHECK_NOTHROW(fs::is_fifo("notfound", ec));
4✔
2351
    CHECK(ec);
4✔
2352
    ec.clear();
4✔
2353
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::none)));
4✔
2354
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::not_found)));
4✔
2355
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::regular)));
4✔
2356
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::directory)));
4✔
2357
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::symlink)));
4✔
2358
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::block)));
4✔
2359
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::character)));
4✔
2360
    CHECK(fs::is_fifo(fs::file_status(fs::file_type::fifo)));
4✔
2361
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::socket)));
4✔
2362
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::unknown)));
4✔
2363
}
4✔
2364

2365
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_other - is_other", "[filesystem][operations][fs.op.is_other]")
4✔
2366
{
2367
    std::error_code ec;
4✔
2368
    CHECK(!fs::is_other("directory"));
4✔
2369
    CHECK(!fs::is_other("regular"));
4✔
2370
    if (is_symlink_creation_supported()) {
4✔
2371
        CHECK(!fs::is_other("dir_symlink"));
4✔
2372
        CHECK(!fs::is_other("file_symlink"));
4✔
2373
    }
2374
    CHECK((has_fifo() ? fs::is_other("fifo") : true));
4✔
2375
    CHECK((has_socket() ? fs::is_other("socket") : true));
4✔
2376
    CHECK((block_path().empty() ? true : fs::is_other(block_path())));
4✔
2377
    CHECK((character_path().empty() ? true : fs::is_other(character_path())));
4✔
2378
    CHECK_NOTHROW(fs::is_other("notfound"));
4✔
2379
    CHECK_NOTHROW(fs::is_other("notfound", ec));
4✔
2380
    CHECK(ec);
4✔
2381
    ec.clear();
4✔
2382
    CHECK(!fs::is_other(fs::file_status(fs::file_type::none)));
4✔
2383
    CHECK(!fs::is_other(fs::file_status(fs::file_type::not_found)));
4✔
2384
    CHECK(!fs::is_other(fs::file_status(fs::file_type::regular)));
4✔
2385
    CHECK(!fs::is_other(fs::file_status(fs::file_type::directory)));
4✔
2386
    CHECK(!fs::is_other(fs::file_status(fs::file_type::symlink)));
4✔
2387
    CHECK(fs::is_other(fs::file_status(fs::file_type::block)));
4✔
2388
    CHECK(fs::is_other(fs::file_status(fs::file_type::character)));
4✔
2389
    CHECK(fs::is_other(fs::file_status(fs::file_type::fifo)));
4✔
2390
    CHECK(fs::is_other(fs::file_status(fs::file_type::socket)));
4✔
2391
    CHECK(fs::is_other(fs::file_status(fs::file_type::unknown)));
4✔
2392
}
4✔
2393

2394
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_regular_file - is_regular_file", "[filesystem][operations][fs.op.is_regular_file]")
4✔
2395
{
2396
    std::error_code ec;
4✔
2397
    CHECK(!fs::is_regular_file("directory"));
4✔
2398
    CHECK(fs::is_regular_file("regular"));
4✔
2399
    if (is_symlink_creation_supported()) {
4✔
2400
        CHECK(!fs::is_regular_file("dir_symlink"));
4✔
2401
        CHECK(fs::is_regular_file("file_symlink"));
4✔
2402
    }
2403
    CHECK((has_fifo() ? !fs::is_regular_file("fifo") : true));
4✔
2404
    CHECK((has_socket() ? !fs::is_regular_file("socket") : true));
4✔
2405
    CHECK((block_path().empty() ? true : !fs::is_regular_file(block_path())));
4✔
2406
    CHECK((character_path().empty() ? true : !fs::is_regular_file(character_path())));
4✔
2407
    CHECK_NOTHROW(fs::is_regular_file("notfound"));
4✔
2408
    CHECK_NOTHROW(fs::is_regular_file("notfound", ec));
4✔
2409
    CHECK(ec);
4✔
2410
    ec.clear();
4✔
2411
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::none)));
4✔
2412
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::not_found)));
4✔
2413
    CHECK(fs::is_regular_file(fs::file_status(fs::file_type::regular)));
4✔
2414
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::directory)));
4✔
2415
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::symlink)));
4✔
2416
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::block)));
4✔
2417
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::character)));
4✔
2418
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::fifo)));
4✔
2419
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::socket)));
4✔
2420
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::unknown)));
4✔
2421
}
4✔
2422

2423
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_socket - is_socket", "[filesystem][operations][fs.op.is_socket]")
4✔
2424
{
2425
    std::error_code ec;
4✔
2426
    CHECK(!fs::is_socket("directory"));
4✔
2427
    CHECK(!fs::is_socket("regular"));
4✔
2428
    if (is_symlink_creation_supported()) {
4✔
2429
        CHECK(!fs::is_socket("dir_symlink"));
4✔
2430
        CHECK(!fs::is_socket("file_symlink"));
4✔
2431
    }
2432
    CHECK((has_fifo() ? !fs::is_socket("fifo") : true));
4✔
2433
    CHECK((has_socket() ? fs::is_socket("socket") : true));
4✔
2434
    CHECK((block_path().empty() ? true : !fs::is_socket(block_path())));
4✔
2435
    CHECK((character_path().empty() ? true : !fs::is_socket(character_path())));
4✔
2436
    CHECK_NOTHROW(fs::is_socket("notfound"));
4✔
2437
    CHECK_NOTHROW(fs::is_socket("notfound", ec));
4✔
2438
    CHECK(ec);
4✔
2439
    ec.clear();
4✔
2440
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::none)));
4✔
2441
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::not_found)));
4✔
2442
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::regular)));
4✔
2443
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::directory)));
4✔
2444
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::symlink)));
4✔
2445
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::block)));
4✔
2446
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::character)));
4✔
2447
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::fifo)));
4✔
2448
    CHECK(fs::is_socket(fs::file_status(fs::file_type::socket)));
4✔
2449
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::unknown)));
4✔
2450
}
4✔
2451

2452
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_symlink - is_symlink", "[filesystem][operations][fs.op.is_symlink]")
4✔
2453
{
2454
    std::error_code ec;
4✔
2455
    CHECK(!fs::is_symlink("directory"));
4✔
2456
    CHECK(!fs::is_symlink("regular"));
4✔
2457
    if (is_symlink_creation_supported()) {
4✔
2458
        CHECK(fs::is_symlink("dir_symlink"));
4✔
2459
        CHECK(fs::is_symlink("file_symlink"));
4✔
2460
    }
2461
    CHECK((has_fifo() ? !fs::is_symlink("fifo") : true));
4✔
2462
    CHECK((has_socket() ? !fs::is_symlink("socket") : true));
4✔
2463
    CHECK((block_path().empty() ? true : !fs::is_symlink(block_path())));
4✔
2464
    CHECK((character_path().empty() ? true : !fs::is_symlink(character_path())));
4✔
2465
    CHECK_NOTHROW(fs::is_symlink("notfound"));
4✔
2466
    CHECK_NOTHROW(fs::is_symlink("notfound", ec));
4✔
2467
    CHECK(ec);
4✔
2468
    ec.clear();
4✔
2469
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::none)));
4✔
2470
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::not_found)));
4✔
2471
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::regular)));
4✔
2472
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::directory)));
4✔
2473
    CHECK(fs::is_symlink(fs::file_status(fs::file_type::symlink)));
4✔
2474
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::block)));
4✔
2475
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::character)));
4✔
2476
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::fifo)));
4✔
2477
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::socket)));
4✔
2478
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::unknown)));
4✔
2479
}
4✔
2480

2481
#ifndef GHC_OS_WEB
2482
static fs::file_time_type timeFromString(const std::string& str)
8✔
2483
{
2484
    struct ::tm tm;
2485
    ::memset(&tm, 0, sizeof(::tm));
8✔
2486
    std::istringstream is(str);
12✔
2487
    is >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
8✔
2488
    if (is.fail()) {
8✔
2489
        throw std::exception();
×
2490
    }
2491
    return from_time_t<fs::file_time_type>(std::mktime(&tm));
16✔
2492
}
4✔
2493
#endif
2494

2495
TEST_CASE("fs.op.last_write_time - last_write_time", "[filesystem][operations][fs.op.last_write_time]")
4✔
2496
{
2497
    TemporaryDirectory t(TempOpt::change_path);
6✔
2498
    std::error_code ec;
4✔
2499
    fs::file_time_type ft;
4✔
2500
    generateFile("foo");
4✔
2501
    auto now = fs::file_time_type::clock::now();
4✔
2502
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time(t.path()) - now).count()) < 3);
4✔
2503
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - now).count()) < 3);
4✔
2504
    CHECK_THROWS_AS(fs::last_write_time("bar"), fs::filesystem_error);
10✔
2505
    CHECK_NOTHROW(ft = fs::last_write_time("bar", ec));
4✔
2506
    CHECK(ft == fs::file_time_type::min());
4✔
2507
    CHECK(ec);
4✔
2508
    ec.clear();
4✔
2509
    if (is_symlink_creation_supported()) {
4✔
2510
        std::this_thread::sleep_for(std::chrono::seconds(1));
4✔
2511
        fs::create_symlink("foo", "foo2");
4✔
2512
        ft = fs::last_write_time("foo");
4✔
2513
        // checks that the time of the symlink is fetched
2514
        CHECK(ft == fs::last_write_time("foo2"));
4✔
2515
    }
2516
#ifndef GHC_OS_WEB
2517
    auto nt = timeFromString("2015-10-21T04:30:00");
4✔
2518
    CHECK_NOTHROW(fs::last_write_time(t.path() / "foo", nt));
4✔
2519
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
4✔
2520
    nt = timeFromString("2015-10-21T04:29:00");
4✔
2521
    CHECK_NOTHROW(fs::last_write_time("foo", nt, ec));
4✔
2522
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
4✔
2523
    CHECK(!ec);
4✔
2524
    CHECK_THROWS_AS(fs::last_write_time("bar", nt), fs::filesystem_error);
10✔
2525
    CHECK_NOTHROW(fs::last_write_time("bar", nt, ec));
4✔
2526
    CHECK(ec);
4✔
2527
#endif
2528
}
4✔
2529

2530
TEST_CASE("fs.op.permissions - permissions", "[filesystem][operations][fs.op.permissions]")
4✔
2531
{
2532
    TemporaryDirectory t(TempOpt::change_path);
6✔
2533
    std::error_code ec;
4✔
2534
    generateFile("foo", 512);
4✔
2535
    auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write;
4✔
2536
    CHECK_NOTHROW(fs::permissions("foo", allWrite, fs::perm_options::remove));
4✔
2537
    CHECK((fs::status("foo").permissions() & fs::perms::owner_write) != fs::perms::owner_write);
4✔
2538
#if !defined(GHC_OS_WINDOWS)
2539
    if (geteuid() != 0)
4✔
2540
#endif
2541
    {
2542
        CHECK_THROWS_AS(fs::resize_file("foo", 1024), fs::filesystem_error);
10✔
2543
        CHECK(fs::file_size("foo") == 512);
4✔
2544
    }
2545
    CHECK_NOTHROW(fs::permissions("foo", fs::perms::owner_write, fs::perm_options::add));
4✔
2546
    CHECK((fs::status("foo").permissions() & fs::perms::owner_write) == fs::perms::owner_write);
4✔
2547
    CHECK_NOTHROW(fs::resize_file("foo", 2048));
4✔
2548
    CHECK(fs::file_size("foo") == 2048);
4✔
2549
    CHECK_THROWS_AS(fs::permissions("bar", fs::perms::owner_write, fs::perm_options::add), fs::filesystem_error);
10✔
2550
    CHECK_NOTHROW(fs::permissions("bar", fs::perms::owner_write, fs::perm_options::add, ec));
4✔
2551
    CHECK(ec);
4✔
2552
    CHECK_THROWS_AS(fs::permissions("bar", fs::perms::owner_write, static_cast<fs::perm_options>(0)), fs::filesystem_error);
10✔
2553
}
4✔
2554

2555
TEST_CASE("fs.op.proximate - proximate", "[filesystem][operations][fs.op.proximate]")
4✔
2556
{
2557
    std::error_code ec;
4✔
2558
    CHECK(fs::proximate("/a/d", "/a/b/c") == "../../d");
4✔
2559
    CHECK(fs::proximate("/a/d", "/a/b/c", ec) == "../../d");
4✔
2560
    CHECK(!ec);
4✔
2561
    CHECK(fs::proximate("/a/b/c", "/a/d") == "../b/c");
4✔
2562
    CHECK(fs::proximate("/a/b/c", "/a/d", ec) == "../b/c");
4✔
2563
    CHECK(!ec);
4✔
2564
    CHECK(fs::proximate("a/b/c", "a") == "b/c");
4✔
2565
    CHECK(fs::proximate("a/b/c", "a", ec) == "b/c");
4✔
2566
    CHECK(!ec);
4✔
2567
    CHECK(fs::proximate("a/b/c", "a/b/c/x/y") == "../..");
4✔
2568
    CHECK(fs::proximate("a/b/c", "a/b/c/x/y", ec) == "../..");
4✔
2569
    CHECK(!ec);
4✔
2570
    CHECK(fs::proximate("a/b/c", "a/b/c") == ".");
4✔
2571
    CHECK(fs::proximate("a/b/c", "a/b/c", ec) == ".");
4✔
2572
    CHECK(!ec);
4✔
2573
    CHECK(fs::proximate("a/b", "c/d") == "../../a/b");
4✔
2574
    CHECK(fs::proximate("a/b", "c/d", ec) == "../../a/b");
4✔
2575
    CHECK(!ec);
4✔
2576
#ifndef GHC_OS_WINDOWS
2577
    if (has_host_root_name_support()) {
4✔
2578
        CHECK(fs::proximate("//host1/a/d", "//host2/a/b/c") == "//host1/a/d");
4✔
2579
        CHECK(fs::proximate("//host1/a/d", "//host2/a/b/c", ec) == "//host1/a/d");
4✔
2580
        CHECK(!ec);
4✔
2581
    }
2582
#endif
2583
}
4✔
2584

2585
TEST_CASE("fs.op.read_symlink - read_symlink", "[filesystem][operations][fs.op.read_symlink]")
4✔
2586
{
2587
    if (is_symlink_creation_supported()) {
4✔
2588
        TemporaryDirectory t(TempOpt::change_path);
6✔
2589
        std::error_code ec;
4✔
2590
        generateFile("foo");
4✔
2591
        fs::create_symlink(t.path() / "foo", "bar");
4✔
2592
        CHECK(fs::read_symlink("bar") == t.path() / "foo");
4✔
2593
        CHECK(fs::read_symlink("bar", ec) == t.path() / "foo");
4✔
2594
        CHECK(!ec);
4✔
2595
        CHECK_THROWS_AS(fs::read_symlink("foobar"), fs::filesystem_error);
10✔
2596
        CHECK(fs::read_symlink("foobar", ec) == fs::path());
4✔
2597
        CHECK(ec);
4✔
2598
    }
2✔
2599
}
4✔
2600

2601
TEST_CASE("fs.op.relative - relative", "[filesystem][operations][fs.op.relative]")
4✔
2602
{
2603
    CHECK(fs::relative("/a/d", "/a/b/c") == "../../d");
4✔
2604
    CHECK(fs::relative("/a/b/c", "/a/d") == "../b/c");
4✔
2605
    CHECK(fs::relative("a/b/c", "a") == "b/c");
4✔
2606
    CHECK(fs::relative("a/b/c", "a/b/c/x/y") == "../..");
4✔
2607
    CHECK(fs::relative("a/b/c", "a/b/c") == ".");
4✔
2608
    CHECK(fs::relative("a/b", "c/d") == "../../a/b");
4✔
2609
    std::error_code ec;
4✔
2610
    CHECK(fs::relative(fs::current_path() / "foo", ec) == "foo");
4✔
2611
    CHECK(!ec);
4✔
2612
}
4✔
2613

2614
TEST_CASE("fs.op.remove - remove", "[filesystem][operations][fs.op.remove]")
4✔
2615
{
2616
    TemporaryDirectory t(TempOpt::change_path);
6✔
2617
    std::error_code ec;
4✔
2618
    generateFile("foo");
4✔
2619
    CHECK(fs::remove("foo"));
4✔
2620
    CHECK(!fs::exists("foo"));
4✔
2621
    CHECK(!fs::remove("foo"));
4✔
2622
    generateFile("foo");
4✔
2623
    CHECK(fs::remove("foo", ec));
4✔
2624
    CHECK(!fs::exists("foo"));
4✔
2625
    if (is_symlink_creation_supported()) {
4✔
2626
        generateFile("foo");
4✔
2627
        fs::create_symlink("foo", "bar");
4✔
2628
        CHECK(fs::exists(fs::symlink_status("bar")));
4✔
2629
        CHECK(fs::remove("bar", ec));
4✔
2630
        CHECK(fs::exists("foo"));
4✔
2631
        CHECK(!fs::exists(fs::symlink_status("bar")));
4✔
2632
    }
2633
    CHECK(!fs::remove("bar"));
4✔
2634
    CHECK(!fs::remove("bar", ec));
4✔
2635
    CHECK(!ec);
4✔
2636
}
4✔
2637

2638
TEST_CASE("fs.op.remove_all - remove_all", "[filesystem][operations][fs.op.remove_all]")
4✔
2639
{
2640
    TemporaryDirectory t(TempOpt::change_path);
6✔
2641
    std::error_code ec;
4✔
2642
    generateFile("foo");
4✔
2643
    CHECK(fs::remove_all("foo", ec) == 1);
4✔
2644
    CHECK(!ec);
4✔
2645
    ec.clear();
4✔
2646
    CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
4✔
2647
    fs::create_directories("dir1/dir1a");
4✔
2648
    fs::create_directories("dir1/dir1b");
4✔
2649
    generateFile("dir1/dir1a/f1");
4✔
2650
    generateFile("dir1/dir1b/f2");
4✔
2651
    CHECK_NOTHROW(fs::remove_all("dir1/non-existing", ec));
4✔
2652
    CHECK(!ec);
4✔
2653
    CHECK(fs::remove_all("dir1/non-existing", ec) == 0);
4✔
2654
    if (is_symlink_creation_supported()) {
4✔
2655
        fs::create_directory_symlink("dir1", "dir1link");
4✔
2656
        CHECK(fs::remove_all("dir1link") == 1);
4✔
2657
    }
2658
    CHECK(fs::remove_all("dir1") == 5);
4✔
2659
    CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
4✔
2660
}
4✔
2661

2662
TEST_CASE("fs.op.rename - rename", "[filesystem][operations][fs.op.rename]")
4✔
2663
{
2664
    TemporaryDirectory t(TempOpt::change_path);
6✔
2665
    std::error_code ec;
4✔
2666
    generateFile("foo", 123);
4✔
2667
    fs::create_directory("dir1");
4✔
2668
    CHECK_NOTHROW(fs::rename("foo", "bar"));
4✔
2669
    CHECK(!fs::exists("foo"));
4✔
2670
    CHECK(fs::exists("bar"));
4✔
2671
    CHECK_NOTHROW(fs::rename("dir1", "dir2"));
4✔
2672
    CHECK(fs::exists("dir2"));
4✔
2673
    generateFile("foo2", 42);
4✔
2674
    CHECK_NOTHROW(fs::rename("bar", "foo2"));
4✔
2675
    CHECK(fs::exists("foo2"));
4✔
2676
    CHECK(fs::file_size("foo2") == 123u);
4✔
2677
    CHECK(!fs::exists("bar"));
4✔
2678
    CHECK_NOTHROW(fs::rename("foo2", "foo", ec));
4✔
2679
    CHECK(!ec);
4✔
2680
    CHECK_THROWS_AS(fs::rename("foobar", "barfoo"), fs::filesystem_error);
14✔
2681
    CHECK_NOTHROW(fs::rename("foobar", "barfoo", ec));
4✔
2682
    CHECK(ec);
4✔
2683
    CHECK(!fs::exists("barfoo"));
4✔
2684
}
4✔
2685

2686
TEST_CASE("fs.op.resize_file - resize_file", "[filesystem][operations][fs.op.resize_file]")
4✔
2687
{
2688
    TemporaryDirectory t(TempOpt::change_path);
6✔
2689
    std::error_code ec;
4✔
2690
    generateFile("foo", 1024);
4✔
2691
    CHECK(fs::file_size("foo") == 1024);
4✔
2692
    CHECK_NOTHROW(fs::resize_file("foo", 2048));
4✔
2693
    CHECK(fs::file_size("foo") == 2048);
4✔
2694
    CHECK_NOTHROW(fs::resize_file("foo", 1000, ec));
4✔
2695
    CHECK(!ec);
4✔
2696
    CHECK(fs::file_size("foo") == 1000);
4✔
2697
    CHECK_THROWS_AS(fs::resize_file("bar", 2048), fs::filesystem_error);
10✔
2698
    CHECK(!fs::exists("bar"));
4✔
2699
    CHECK_NOTHROW(fs::resize_file("bar", 4096, ec));
4✔
2700
    CHECK(ec);
4✔
2701
    CHECK(!fs::exists("bar"));
4✔
2702
}
4✔
2703

2704
TEST_CASE("fs.op.space - space", "[filesystem][operations][fs.op.space]")
4✔
2705
{
2706
    {
2707
        fs::space_info si;
2708
        CHECK_NOTHROW(si = fs::space(fs::current_path()));
4✔
2709
        CHECK(si.capacity > 1024 * 1024);
4✔
2710
        CHECK(si.capacity > si.free);
4✔
2711
        CHECK(si.free >= si.available);
4✔
2712
    }
2713
    {
2714
        std::error_code ec;
4✔
2715
        fs::space_info si;
2716
        CHECK_NOTHROW(si = fs::space(fs::current_path(), ec));
4✔
2717
        CHECK(si.capacity > 1024 * 1024);
4✔
2718
        CHECK(si.capacity > si.free);
4✔
2719
        CHECK(si.free >= si.available);
4✔
2720
        CHECK(!ec);
4✔
2721
    }
2722
#ifndef GHC_OS_WEB // statvfs under emscripten always returns a result, so this tests would fail
2723
    {
2724
        std::error_code ec;
4✔
2725
        fs::space_info si;
2726
        CHECK_NOTHROW(si = fs::space("foobar42", ec));
4✔
2727
        CHECK(si.capacity == static_cast<uintmax_t>(-1));
4✔
2728
        CHECK(si.free == static_cast<uintmax_t>(-1));
4✔
2729
        CHECK(si.available == static_cast<uintmax_t>(-1));
4✔
2730
        CHECK(ec);
4✔
2731
    }
2732
    CHECK_THROWS_AS(fs::space("foobar42"), fs::filesystem_error);
10✔
2733
#endif
2734
}
4✔
2735

2736
TEST_CASE("fs.op.status - status", "[filesystem][operations][fs.op.status]")
4✔
2737
{
2738
    TemporaryDirectory t(TempOpt::change_path);
6✔
2739
    std::error_code ec;
4✔
2740
    fs::file_status fs;
6✔
2741
    CHECK_NOTHROW(fs = fs::status("foo"));
4✔
2742
    CHECK(fs.type() == fs::file_type::not_found);
4✔
2743
    CHECK(fs.permissions() == fs::perms::unknown);
4✔
2744
    CHECK_NOTHROW(fs = fs::status("bar", ec));
4✔
2745
    CHECK(fs.type() == fs::file_type::not_found);
4✔
2746
    CHECK(fs.permissions() == fs::perms::unknown);
4✔
2747
    CHECK(ec);
4✔
2748
    ec.clear();
4✔
2749
    fs = fs::status(t.path());
4✔
2750
    CHECK(fs.type() == fs::file_type::directory);
4✔
2751
    CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
4✔
2752
    generateFile("foobar");
4✔
2753
    fs = fs::status(t.path() / "foobar");
4✔
2754
    CHECK(fs.type() == fs::file_type::regular);
4✔
2755
    CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
4✔
2756
    if (is_symlink_creation_supported()) {
4✔
2757
        fs::create_symlink(t.path() / "foobar", t.path() / "barfoo");
4✔
2758
        fs = fs::status(t.path() / "barfoo");
4✔
2759
        CHECK(fs.type() == fs::file_type::regular);
4✔
2760
        CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
4✔
2761
    }
2762
}
4✔
2763

2764
TEST_CASE("fs.op.status_known - status_known", "[filesystem][operations][fs.op.status_known]")
4✔
2765
{
2766
    CHECK(!fs::status_known(fs::file_status()));
4✔
2767
    CHECK(fs::status_known(fs::file_status(fs::file_type::not_found)));
4✔
2768
    CHECK(fs::status_known(fs::file_status(fs::file_type::regular)));
4✔
2769
    CHECK(fs::status_known(fs::file_status(fs::file_type::directory)));
4✔
2770
    CHECK(fs::status_known(fs::file_status(fs::file_type::symlink)));
4✔
2771
    CHECK(fs::status_known(fs::file_status(fs::file_type::character)));
4✔
2772
    CHECK(fs::status_known(fs::file_status(fs::file_type::fifo)));
4✔
2773
    CHECK(fs::status_known(fs::file_status(fs::file_type::socket)));
4✔
2774
    CHECK(fs::status_known(fs::file_status(fs::file_type::unknown)));
4✔
2775
}
4✔
2776

2777
TEST_CASE("fs.op.symlink_status - symlink_status", "[filesystem][operations][fs.op.symlink_status]")
4✔
2778
{
2779
    TemporaryDirectory t(TempOpt::change_path);
6✔
2780
    std::error_code ec;
4✔
2781
    fs::file_status fs;
6✔
2782
    CHECK_NOTHROW(fs = fs::symlink_status("foo"));
4✔
2783
    CHECK(fs.type() == fs::file_type::not_found);
4✔
2784
    CHECK(fs.permissions() == fs::perms::unknown);
4✔
2785
    CHECK_NOTHROW(fs = fs::symlink_status("bar", ec));
4✔
2786
    CHECK(fs.type() == fs::file_type::not_found);
4✔
2787
    CHECK(fs.permissions() == fs::perms::unknown);
4✔
2788
    CHECK(ec);
4✔
2789
    ec.clear();
4✔
2790
    fs = fs::symlink_status(t.path());
4✔
2791
    CHECK(fs.type() == fs::file_type::directory);
4✔
2792
    CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
4✔
2793
    generateFile("foobar");
4✔
2794
    fs = fs::symlink_status(t.path() / "foobar");
4✔
2795
    CHECK(fs.type() == fs::file_type::regular);
4✔
2796
    CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
4✔
2797
    if (is_symlink_creation_supported()) {
4✔
2798
        fs::create_symlink(t.path() / "foobar", t.path() / "barfoo");
4✔
2799
        fs = fs::symlink_status(t.path() / "barfoo");
4✔
2800
        CHECK(fs.type() == fs::file_type::symlink);
4✔
2801
    }
2802
}
4✔
2803

2804
TEST_CASE("fs.op.temp_dir_path - temporary_directory_path", "[filesystem][operations][fs.op.temp_dir_path]")
4✔
2805
{
2806
    std::error_code ec;
4✔
2807
    CHECK_NOTHROW(fs::exists(fs::temp_directory_path()));
4✔
2808
    CHECK_NOTHROW(fs::exists(fs::temp_directory_path(ec)));
4✔
2809
    CHECK(!fs::temp_directory_path().empty());
4✔
2810
    CHECK(!ec);
4✔
2811
}
4✔
2812

2813
TEST_CASE("fs.op.weakly_canonical - weakly_canonical", "[filesystem][operations][fs.op.weakly_canonical]")
4✔
2814
{
2815
    INFO("This might fail on std::implementations that return fs::current_path() for fs::canonical(\"\")");
6✔
2816
    CHECK(fs::weakly_canonical("") == ".");
4✔
2817
    if(fs::weakly_canonical("") == ".") {
4✔
2818
        CHECK(fs::weakly_canonical("foo/bar") == "foo/bar");
4✔
2819
        CHECK(fs::weakly_canonical("foo/./bar") == "foo/bar");
4✔
2820
        CHECK(fs::weakly_canonical("foo/../bar") == "bar");
4✔
2821
    }
2822
    else {
2823
        CHECK(fs::weakly_canonical("foo/bar") == fs::current_path() / "foo/bar");
×
2824
        CHECK(fs::weakly_canonical("foo/./bar") == fs::current_path() / "foo/bar");
×
2825
        CHECK(fs::weakly_canonical("foo/../bar") == fs::current_path() / "bar");
×
2826
    }
2827

2828
    {
2829
        TemporaryDirectory t(TempOpt::change_path);
6✔
2830
        auto dir = t.path() / "d0";
6✔
2831
        fs::create_directories(dir / "d1");
4✔
2832
        generateFile(dir / "f0");
4✔
2833
        fs::path rel(dir.filename());
6✔
2834
        CHECK(fs::weakly_canonical(dir) == dir);
4✔
2835
        CHECK(fs::weakly_canonical(rel) == dir);
4✔
2836
        CHECK(fs::weakly_canonical(dir / "f0") == dir / "f0");
4✔
2837
        CHECK(fs::weakly_canonical(dir / "f0/") == dir / "f0/");
4✔
2838
        CHECK(fs::weakly_canonical(dir / "f1") == dir / "f1");
4✔
2839
        CHECK(fs::weakly_canonical(rel / "f0") == dir / "f0");
4✔
2840
        CHECK(fs::weakly_canonical(rel / "f0/") == dir / "f0/");
4✔
2841
        CHECK(fs::weakly_canonical(rel / "f1") == dir / "f1");
4✔
2842
        CHECK(fs::weakly_canonical(rel / "./f0") == dir / "f0");
4✔
2843
        CHECK(fs::weakly_canonical(rel / "./f1") == dir / "f1");
4✔
2844
        CHECK(fs::weakly_canonical(rel / "d1/../f0") == dir / "f0");
4✔
2845
        CHECK(fs::weakly_canonical(rel / "d1/../f1") == dir / "f1");
4✔
2846
        CHECK(fs::weakly_canonical(rel / "d1/../f1/../f2") == dir / "f2");
4✔
2847
    }
2✔
2848
}
4✔
2849

2850
TEST_CASE("std::string_view support", "[filesystem][fs.string_view]")
4✔
2851
{
2852
#if defined(GHC_HAS_STD_STRING_VIEW) || defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW)
2853

2854
#if defined(GHC_HAS_STD_STRING_VIEW)
2855
    using namespace std::literals;
2856
    using string_view = std::string_view;
2857
    using wstring_view = std::wstring_view;
2858
#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW)
2859
    using string_view = std::experimental::string_view;
2860
    using wstring_view = std::experimental::wstring_view;
2861
#endif
2862

2863
    {
2864
        std::string p("foo/bar");
6✔
2865
        string_view sv(p);
4✔
2866
        CHECK(fs::path(sv, fs::path::format::generic_format).generic_string() == "foo/bar");
4✔
2867
        fs::path p2("fo");
6✔
2868
        p2 += string_view("o");
4✔
2869
        CHECK(p2 == "foo");
4✔
2870
        CHECK(p2.compare(string_view("foo")) == 0);
4✔
2871
    }
2✔
2872
    {
2873
        auto p = fs::path{"XYZ"};
6✔
2874
        p /= string_view("Appendix");
4✔
2875
        CHECK(p == "XYZ/Appendix");
4✔
2876
    }
2✔
2877
    {
2878
        std::wstring p(L"foo/bar");
6✔
2879
        wstring_view sv(p);
4✔
2880
        CHECK(fs::path(sv, fs::path::format::generic_format).generic_string() == "foo/bar");
4✔
2881
        fs::path p2(L"fo");
6✔
2882
        p2 += wstring_view(L"o");
4✔
2883
        CHECK(p2 == "foo");
4✔
2884
        CHECK(p2.compare(wstring_view(L"foo")) == 0);
4✔
2885
    }
2✔
2886

2887
#else
2888
    WARN("std::string_view specific tests are empty without std::string_view.");
2889
#endif
2890
}
4✔
2891

2892
TEST_CASE("Windows: Long filename support", "[filesystem][path][fs.path.win.long]")
4✔
2893
{
2894
#ifdef GHC_OS_WINDOWS
2895
    TemporaryDirectory t(TempOpt::change_path);
2896
    char c = 'A';
2897
    fs::path dir{"\\\\?\\"};
2898
    dir += fs::current_path().u8string();
2899
    for (; c <= 'Z'; ++c) {
2900
        std::string part = std::string(16, c);
2901
        dir /= part;
2902
        CHECK_NOTHROW(fs::create_directory(dir));
2903
        CHECK(fs::exists(dir));
2904
        generateFile(dir / "f0");
2905
        REQUIRE(fs::exists(dir / "f0"));
2906
    }
2907
    CHECK(c > 'Z');
2908
    fs::remove_all(fs::current_path() / std::string(16, 'A'));
2909
    CHECK(!fs::exists(fs::current_path() / std::string(16, 'A')));
2910
    CHECK_NOTHROW(fs::create_directories(dir));
2911
    CHECK(fs::exists(dir));
2912
    generateFile(dir / "f0");
2913
    CHECK(fs::exists(dir / "f0"));
2914
#else
2915
    WARN("Windows specific tests are empty on non-Windows systems.");
4✔
2916
#endif
2917
}
4✔
2918

2919
TEST_CASE("Windows: path namespace handling", "[filesystem][path][fs.path.win.namespaces]")
4✔
2920
{
2921
#ifdef GHC_OS_WINDOWS
2922
    {
2923
        std::error_code ec;
2924
        fs::path p(R"(\\localhost\c$\Windows)");
2925
        auto symstat = fs::symlink_status(p, ec);
2926
        CHECK(!ec);
2927
        auto p2 = fs::canonical(p, ec);
2928
        CHECK(!ec);
2929
        CHECK(p2 == p);
2930
    }
2931
    
2932
    struct TestInfo
2933
    {
2934
        std::string _path;
2935
        std::string _string;
2936
        std::string _rootName;
2937
        std::string _rootPath;
2938
        std::string _iterateResult;
2939
    };
2940
    std::vector<TestInfo> variants = {
2941
        {R"(C:\Windows\notepad.exe)", R"(C:\Windows\notepad.exe)", "C:", "C:\\", "C:,/,Windows,notepad.exe"},
2942
#ifdef USE_STD_FS
2943
        {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,C:,Windows,notepad.exe"},
2944
        {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "\\??", "\\??\\", "/??,/,C:,Windows,notepad.exe"},
2945
#else
2946
        {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "C:", "C:\\", "//?/,C:,/,Windows,notepad.exe"},
2947
        {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "C:", "C:\\", "/?\?/,C:,/,Windows,notepad.exe"},
2948
#endif
2949
        {R"(\\.\C:\Windows\notepad.exe)", R"(\\.\C:\Windows\notepad.exe)", "\\\\.", "\\\\.\\", "//.,/,C:,Windows,notepad.exe"},
2950
        {R"(\\?\HarddiskVolume1\Windows\notepad.exe)", R"(\\?\HarddiskVolume1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,HarddiskVolume1,Windows,notepad.exe"},
2951
        {R"(\\?\Harddisk0Partition1\Windows\notepad.exe)", R"(\\?\Harddisk0Partition1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,Harddisk0Partition1,Windows,notepad.exe"},
2952
        {R"(\\.\GLOBALROOT\Device\HarddiskVolume1\Windows\notepad.exe)", R"(\\.\GLOBALROOT\Device\HarddiskVolume1\Windows\notepad.exe)", "\\\\.", "\\\\.\\", "//.,/,GLOBALROOT,Device,HarddiskVolume1,Windows,notepad.exe"},
2953
        {R"(\\?\GLOBALROOT\Device\Harddisk0\Partition1\Windows\notepad.exe)", R"(\\?\GLOBALROOT\Device\Harddisk0\Partition1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,GLOBALROOT,Device,Harddisk0,Partition1,Windows,notepad.exe"},
2954
        {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"},
2955
        {R"(\\LOCALHOST\C$\Windows\notepad.exe)", R"(\\LOCALHOST\C$\Windows\notepad.exe)", "\\\\LOCALHOST", "\\\\LOCALHOST\\", "//LOCALHOST,/,C$,Windows,notepad.exe"},
2956
        {R"(\\?\UNC\C$\Windows\notepad.exe)", R"(\\?\UNC\C$\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,UNC,C$,Windows,notepad.exe"},
2957
        {R"(\\?\GLOBALROOT\Device\Mup\C$\Windows\notepad.exe)", R"(\\?\GLOBALROOT\Device\Mup\C$\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,GLOBALROOT,Device,Mup,C$,Windows,notepad.exe"},
2958
    };
2959

2960
    for (auto ti : variants) {
2961
        INFO("Used path: " + ti._path);
2962
        auto p = fs::path(ti._path);
2963
        CHECK(p.string() == ti._string);
2964
        CHECK(p.is_absolute());
2965
        CHECK(p.root_name().string() == ti._rootName);
2966
        CHECK(p.root_path().string() == ti._rootPath);
2967
        CHECK(iterateResult(p) == ti._iterateResult);
2968
    }
2969
#else
2970
    WARN("Windows specific tests are empty on non-Windows systems.");
4✔
2971
#endif
2972
}
4✔
2973

2974
TEST_CASE("Windows: Mapped folders handling ", "[filesystem][fs.win][fs.win.mapped]")
4✔
2975
{
2976
#ifdef GHC_OS_WINDOWS
2977
    // this test expects a mapped volume on C:\\fs-test as is the case on the development test system
2978
    // does nothing on other systems
2979
    if (fs::exists("C:\\fs-test")) {
2980
        CHECK(fs::canonical("C:\\fs-test\\Test.txt").string() == "C:\\fs-test\\Test.txt");
2981
    }
2982
#else
2983
    WARN("Windows specific tests are empty on non-Windows systems.");
4✔
2984
#endif
2985
}
4✔
2986

2987
TEST_CASE("Windows: Deletion of Read-only Files", "[filesystem][fs.win][fs.win.remove]")
4✔
2988
{
2989
#ifdef GHC_OS_WINDOWS
2990
    TemporaryDirectory t(TempOpt::change_path);
2991
    std::error_code ec;
2992
    generateFile("foo", 512);
2993
    auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write;
2994
    CHECK_NOTHROW(fs::permissions("foo", allWrite, fs::perm_options::remove));
2995
    CHECK_NOTHROW(fs::remove("foo"));
2996
    CHECK(!fs::exists("foo"));
2997
#else
2998
    WARN("Windows specific tests are empty on non-Windows systems.");
4✔
2999
#endif
3000
}
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc