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

gulrak / filesystem / 29306137499

13 Jul 2026 07:09PM UTC coverage: 95.295% (+0.007%) from 95.288%
29306137499

push

github

gulrak
Handle malformed UTF-16 sequences in `toUtf8` conversion and update error handling, refs #209.

16 of 16 new or added lines in 2 files covered. (100.0%)

2 existing lines in 2 files now uncovered.

3646 of 3826 relevant lines covered (95.3%)

417.91 hits per line

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

98.1
/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
#define CATCH_CONFIG_NO_COUNTER
86
#include "catch.hpp"
87

88
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
89
// Behaviour Switches (should match the config in ghc/filesystem.hpp):
90
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
91
// LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories
92
#define TEST_LWG_2682_BEHAVIOUR
93
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
94
// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular
95
// file with that name, it is superceded by P1164R1, so only activate if really needed
96
// #define TEST_LWG_2935_BEHAVIOUR
97
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
98
// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2)
99
#define TEST_LWG_2937_BEHAVIOUR
100
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
101

102
template <typename TP>
103
static std::time_t to_time_t(TP tp)
×
104
{
105
    using namespace std::chrono;
106
    auto sctp = time_point_cast<system_clock::duration>(tp - TP::clock::now() + system_clock::now());
×
107
    return system_clock::to_time_t(sctp);
×
108
}
109

110
template <typename TP>
111
static TP from_time_t(std::time_t t)
8✔
112
{
113
    using namespace std::chrono;
114
    auto sctp = system_clock::from_time_t(t);
8✔
115
    auto tp = time_point_cast<typename TP::duration>(sctp - system_clock::now() + TP::clock::now());
8✔
116
    return tp;
8✔
117
}
118

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

126
template <>
127
struct StringMaker<fs::perms>
128
{
129
    static std::string convert(fs::perms const& value) { return std::to_string(static_cast<unsigned int>(value)); }
×
130
};
131

132
template <>
133
struct StringMaker<fs::file_status>
134
{
135
    static std::string convert(fs::file_status const& value) {
×
136
        return std::string("[") + std::to_string(static_cast<unsigned int>(value.type())) + "," + std::to_string(static_cast<unsigned int>(value.permissions())) + "]";
×
137
    }
138
};
139

140
#ifdef __cpp_lib_char8_t
141
template <>
142
struct StringMaker<char8_t>
143
{
144
    static std::string convert(char8_t const& value) { return std::to_string(static_cast<unsigned int>(value)); }
×
145
};
146
#endif
147

148
template <>
149
struct StringMaker<fs::file_time_type>
150
{
151
    static std::string convert(fs::file_time_type const& value)
×
152
    {
153
        std::time_t t = to_time_t(value);
×
154
        std::tm* ptm = std::localtime(&t);
×
155
        std::ostringstream os;
×
156
        if (ptm) {
×
157
            std::tm ttm = *ptm;
×
158
            os << std::put_time(&ttm, "%Y-%m-%d %H:%M:%S");
×
159
        }
160
        else {
161
            os << "(invalid-time)";
×
162
        }
163
        return os.str();
×
164
    }
×
165
};
166
}  // namespace Catch
167

168
enum class TempOpt { none, change_path };
169
class TemporaryDirectory
170
{
171
public:
172
    TemporaryDirectory(TempOpt opt = TempOpt::none)
100✔
173
    {
100✔
174
        static auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
100✔
175
        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))));
100✔
176
        std::string filename;
100✔
177
        do {
178
            filename = "test_";
100✔
179
            for (int i = 0; i < 8; ++i) {
900✔
180
                filename += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[rng()];
800✔
181
            }
182
            _path = fs::canonical(fs::temp_directory_path()) / filename;
100✔
183
        } while (fs::exists(_path));
100✔
184
        fs::create_directories(_path);
100✔
185
        if (opt == TempOpt::change_path) {
100✔
186
            _orig_dir = fs::current_path();
76✔
187
            fs::current_path(_path);
76✔
188
        }
189
    }
100✔
190

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

199
    const fs::path& path() const { return _path; }
236✔
200

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

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

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

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

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

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

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

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

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

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

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

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

354
TEST_CASE("fs::detail::toUtf8", "[filesystem][fs.detail.utf8]")
2✔
355
{
356
    std::string t;
2✔
357
    CHECK(std::string("\xc3\xa4/\xe2\x82\xac\xf0\x9d\x84\x9e") == fs::detail::toUtf8(std::u16string(u"\u00E4/\u20AC\U0001D11E")));
2✔
358
    const std::u16string highThenAscii{0xd800, u'A'};
2✔
359
    const std::u16string lowThenAscii{0xdc00, u'B'};
2✔
360
    const std::u16string malformedThenPair{0xd800, 0xd834, 0xdd1e, u'C'};
2✔
361
    const std::u16string pairThenMalformed{0xd834, 0xdd1e, 0xdc00, u'D'};
2✔
362
#ifdef GHC_RAISE_UNICODE_ERRORS
363
    CHECK_THROWS_AS(fs::detail::toUtf8(std::u16string(1, 0xd800)), fs::filesystem_error);
364
    CHECK_THROWS_AS(fs::detail::toUtf8(highThenAscii), fs::filesystem_error);
365
    CHECK_THROWS_AS(fs::detail::toUtf8(lowThenAscii), fs::filesystem_error);
366
    CHECK_THROWS_AS(fs::detail::toUtf8(malformedThenPair), fs::filesystem_error);
367
    CHECK_THROWS_AS(fs::detail::toUtf8(pairThenMalformed), fs::filesystem_error);
368
    CHECK_THROWS_AS(fs::detail::appendUTF8(t, 0x200000), fs::filesystem_error);
369
#else
370
    CHECK(std::string("\xEF\xBF\xBD") == fs::detail::toUtf8(std::u16string(1, 0xd800)));
2✔
371
    CHECK(std::string("\xEF\xBF\xBD" "A") == fs::detail::toUtf8(highThenAscii));
2✔
372
    CHECK(std::string("\xEF\xBF\xBD" "B") == fs::detail::toUtf8(lowThenAscii));
2✔
373
    CHECK(std::string("\xEF\xBF\xBD\xF0\x9D\x84\x9E" "C") == fs::detail::toUtf8(malformedThenPair));
2✔
374
    CHECK(std::string("\xF0\x9D\x84\x9E\xEF\xBF\xBD" "D") == fs::detail::toUtf8(pairThenMalformed));
2✔
375
    fs::detail::appendUTF8(t, 0x200000);
2✔
376
    CHECK(std::string("\xEF\xBF\xBD") == t);
2✔
377
#endif
378
}
2✔
379
#endif
380

381
TEST_CASE("fs.path.generic - path::preferred_separator", "[filesystem][path][fs.path.generic]")
2✔
382
{
383
#ifdef GHC_OS_WINDOWS
384
    CHECK(fs::path::preferred_separator == '\\');
385
#else
386
    CHECK(fs::path::preferred_separator == '/');
2✔
387
#endif
388
}
2✔
389

390
#ifndef GHC_OS_WINDOWS
391
TEST_CASE("fs.path.generic - path(\"//host\").has_root_name()", "[filesystem][path][fs.path.generic]")
×
392
{
393
    if (!has_host_root_name_support()) {
×
394
        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.)");
×
395
    }
396
}
×
397
#endif
398

399
TEST_CASE("fs.path.construct - path constructors and destructor", "[filesystem][path][fs.path.construct]")
2✔
400
{
401
    CHECK("/usr/local/bin" == fs::path("/usr/local/bin").generic_string());
2✔
402
    std::string str = "/usr/local/bin";
2✔
403
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
404
    std::u8string u8str = u8"/usr/local/bin";
1✔
405
#endif
406
    std::u16string u16str = u"/usr/local/bin";
2✔
407
    std::u32string u32str = U"/usr/local/bin";
2✔
408
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
409
    CHECK(u8str == fs::path(u8str).generic_u8string());
1✔
410
#endif
411
    CHECK(u16str == fs::path(u16str).generic_u16string());
2✔
412
    CHECK(u32str == fs::path(u32str).generic_u32string());
2✔
413
    CHECK(str == fs::path(str, fs::path::format::generic_format));
2✔
414
    CHECK(str == fs::path(str.begin(), str.end()));
2✔
415
    CHECK(fs::path(std::wstring(3, 67)) == "CCC");
2✔
416
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
417
    CHECK(str == fs::path(u8str.begin(), u8str.end()));
1✔
418
#endif
419
    CHECK(str == fs::path(u16str.begin(), u16str.end()));
2✔
420
    CHECK(str == fs::path(u32str.begin(), u32str.end()));
2✔
421
#ifdef GHC_FILESYSTEM_VERSION
422
    CHECK(fs::path("///foo/bar") == "/foo/bar");
2✔
423
    CHECK(fs::path("//foo//bar") == "//foo/bar");
2✔
424
#endif
425
#ifdef GHC_OS_WINDOWS
426
    CHECK("\\usr\\local\\bin" == fs::path("/usr/local/bin"));
427
    CHECK("C:\\usr\\local\\bin" == fs::path("C:\\usr\\local\\bin"));
428
#else
429
    CHECK("/usr/local/bin" == fs::path("/usr/local/bin"));
2✔
430
#endif
431
    if (has_host_root_name_support()) {
2✔
432
        CHECK("//host/foo/bar" == fs::path("//host/foo/bar"));
2✔
433
    }
434

435
#if !defined(GHC_OS_WINDOWS) && !(defined(__GLIBCXX__) && !(defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 8))) && !defined(USE_STD_FS)
436
    std::locale loc;
2✔
437
    bool testUTF8Locale = false;
2✔
438
    try {
439
        if (const char* lang = std::getenv("LANG")) {
2✔
440
            loc = std::locale(lang);
2✔
441
        }
442
        else {
443
            loc = std::locale("en_US.UTF-8");
×
444
        }
445
        std::string name = loc.name();
2✔
446
        if (name.length() > 5 && (name.substr(name.length() - 5) == "UTF-8" || name.substr(name.length() - 5) == "utf-8")) {
2✔
447
            testUTF8Locale = true;
2✔
448
        }
449
    }
2✔
450
    catch (std::runtime_error&) {
×
451
        WARN("Couldn't create an UTF-8 locale!");
×
452
    }
×
453
    if (testUTF8Locale) {
2✔
454
        CHECK("/usr/local/bin" == fs::path("/usr/local/bin", loc));
2✔
455
        CHECK(str == fs::path(str.begin(), str.end(), loc));
2✔
456
        CHECK(str == fs::path(u16str.begin(), u16str.end(), loc));
2✔
457
        CHECK(str == fs::path(u32str.begin(), u32str.end(), loc));
2✔
458
    }
459
#endif
460
}
2✔
461

462
TEST_CASE("fs.path.assign - path assignments", "[filesystem][path][fs.path.assign]")
2✔
463
{
464
    fs::path p1{"/foo/bar"};
2✔
465
    fs::path p2{"/usr/local"};
2✔
466
    fs::path p3;
2✔
467
    p3 = p1;
2✔
468
    REQUIRE(p1 == p3);
2✔
469
    p3 = fs::path{"/usr/local"};
2✔
470
    REQUIRE(p2 == p3);
2✔
471
    p3 = fs::path{L"/usr/local"};
2✔
472
    REQUIRE(p2 == p3);
2✔
473
    p3.assign(L"/usr/local");
2✔
474
    REQUIRE(p2 == p3);
2✔
475
#if defined(IS_WCHAR_PATH) || defined(GHC_USE_WCHAR_T)
476
    p3 = fs::path::string_type{L"/foo/bar"};
477
    REQUIRE(p1 == p3);
478
    p3.assign(fs::path::string_type{L"/usr/local"});
479
    REQUIRE(p2 == p3);
480
#else
481
    p3 = fs::path::string_type{"/foo/bar"};
2✔
482
    REQUIRE(p1 == p3);
2✔
483
    p3.assign(fs::path::string_type{"/usr/local"});
2✔
484
    REQUIRE(p2 == p3);
2✔
485
#endif
486
    p3 = std::u16string(u"/foo/bar");
2✔
487
    REQUIRE(p1 == p3);
2✔
488
    p3 = U"/usr/local";
2✔
489
    REQUIRE(p2 == p3);
2✔
490
    p3.assign(std::u16string(u"/foo/bar"));
2✔
491
    REQUIRE(p1 == p3);
2✔
492
    std::string s{"/usr/local"};
2✔
493
    p3.assign(s.begin(), s.end());
2✔
494
    REQUIRE(p2 == p3);
2✔
495
}
2✔
496

497
TEST_CASE("fs.path.append - path appends", "[filesystem][path][fs.path.append]")
2✔
498
{
499
#ifdef GHC_OS_WINDOWS
500
    CHECK(fs::path("foo") / "c:/bar" == "c:/bar");
501
    CHECK(fs::path("foo") / "c:" == "c:");
502
    CHECK(fs::path("c:") / "" == "c:");
503
    CHECK(fs::path("c:foo") / "/bar" == "c:/bar");
504
    CHECK(fs::path("c:foo") / "c:bar" == "c:foo/bar");
505
    CHECK(fs::path(R"(\\server)") / "abc" == R"(\\server\abc)");
506
    CHECK(fs::path(R"(\\server)") / "" == R"(\\server\)");
507
    CHECK(fs::path(R"(\\server\)") / "abc" == R"(\\server\abc)");
508
    CHECK(fs::path(R"(\\server\share)") / "abc" == R"(\\server\share\abc)");
509
    CHECK(fs::path(R"(\\server)") / R"(\abc)" == R"(\\server\abc)");
510
    CHECK(fs::path("c:") / "abc" == "c:abc");
511
    CHECK(fs::path(R"(c:\)") / "abc" == R"(c:\abc)");
512
    CHECK(fs::path(R"(\rooted)") / "abc" == R"(\rooted\abc)");
513
#else
514
    CHECK(fs::path("foo") / "" == "foo/");
2✔
515
    CHECK(fs::path("foo") / "/bar" == "/bar");
2✔
516
    CHECK(fs::path("/foo") / "/" == "/");
2✔
517
    if (has_host_root_name_support()) {
2✔
518
        CHECK(fs::path("//host/foo") / "/bar" == "/bar");
2✔
519
        CHECK(fs::path("//host") / "/" == "//host/");
2✔
520
        CHECK(fs::path("//host/foo") / "/" == "/");
2✔
521
    }
522
#endif
523
    CHECK(fs::path("/foo/bar") / "some///other" == "/foo/bar/some/other");
2✔
524
    fs::path p1{"/tmp/test"};
2✔
525
    fs::path p2{"foobar.txt"};
2✔
526
    fs::path p3 = p1 / p2;
2✔
527
    CHECK("/tmp/test/foobar.txt" == p3);
2✔
528
    // TODO: append(first, last)
529
}
2✔
530

531
TEST_CASE("fs.path.concat - path concatenation", "[filesystem][path][fs.path.concat]")
2✔
532
{
533
    CHECK((fs::path("foo") += fs::path("bar")) == "foobar");
2✔
534
    CHECK((fs::path("foo") += fs::path("/bar")) == "foo/bar");
2✔
535

536
    CHECK((fs::path("foo") += std::string("bar")) == "foobar");
2✔
537
    CHECK((fs::path("foo") += std::string("/bar")) == "foo/bar");
2✔
538

539
    CHECK((fs::path("foo") += "bar") == "foobar");
2✔
540
    CHECK((fs::path("foo") += "/bar") == "foo/bar");
2✔
541
    CHECK((fs::path("foo") += L"bar") == "foobar");
2✔
542
    CHECK((fs::path("foo") += L"/bar") == "foo/bar");
2✔
543

544
    CHECK((fs::path("foo") += 'b') == "foob");
2✔
545
    CHECK((fs::path("foo") += '/') == "foo/");
2✔
546
    CHECK((fs::path("foo") += L'b') == "foob");
2✔
547
    CHECK((fs::path("foo") += L'/') == "foo/");
2✔
548

549
    CHECK((fs::path("foo") += std::string("bar")) == "foobar");
2✔
550
    CHECK((fs::path("foo") += std::string("/bar")) == "foo/bar");
2✔
551

552
    CHECK((fs::path("foo") += std::u16string(u"bar")) == "foobar");
2✔
553
    CHECK((fs::path("foo") += std::u16string(u"/bar")) == "foo/bar");
2✔
554

555
    CHECK((fs::path("foo") += std::u32string(U"bar")) == "foobar");
2✔
556
    CHECK((fs::path("foo") += std::u32string(U"/bar")) == "foo/bar");
2✔
557

558
    CHECK(fs::path("foo").concat("bar") == "foobar");
2✔
559
    CHECK(fs::path("foo").concat("/bar") == "foo/bar");
2✔
560
    CHECK(fs::path("foo").concat(L"bar") == "foobar");
2✔
561
    CHECK(fs::path("foo").concat(L"/bar") == "foo/bar");
2✔
562
    std::string bar = "bar";
2✔
563
    CHECK(fs::path("foo").concat(bar.begin(), bar.end()) == "foobar");
2✔
564
#ifndef USE_STD_FS
565
    CHECK((fs::path("/foo/bar") += "/some///other") == "/foo/bar/some/other");
2✔
566
#endif
567
    // TODO: contat(first, last)
568
}
2✔
569

570
TEST_CASE("fs.path.modifiers - path modifiers", "[filesystem][path][fs.path.modifiers]")
2✔
571
{
572
    fs::path p = fs::path("/foo/bar");
2✔
573
    p.clear();
2✔
574
    CHECK(p == "");
2✔
575

576
    // make_preferred() is a no-op
577
#ifdef GHC_OS_WINDOWS
578
    CHECK(fs::path("foo\\bar") == "foo/bar");
579
    CHECK(fs::path("foo\\bar").make_preferred() == "foo/bar");
580
#else
581
    CHECK(fs::path("foo\\bar") == "foo\\bar");
2✔
582
    CHECK(fs::path("foo\\bar").make_preferred() == "foo\\bar");
2✔
583
#endif
584
    CHECK(fs::path("foo/bar").make_preferred() == "foo/bar");
2✔
585

586
    CHECK(fs::path("foo/bar").remove_filename() == "foo/");
2✔
587
    CHECK(fs::path("foo/").remove_filename() == "foo/");
2✔
588
    CHECK(fs::path("/foo").remove_filename() == "/");
2✔
589
    CHECK(fs::path("/").remove_filename() == "/");
2✔
590

591
    CHECK(fs::path("/foo").replace_filename("bar") == "/bar");
2✔
592
    CHECK(fs::path("/").replace_filename("bar") == "/bar");
2✔
593
    CHECK(fs::path("/foo").replace_filename("b//ar") == "/b/ar");
2✔
594

595
    CHECK(fs::path("/foo/bar.txt").replace_extension("odf") == "/foo/bar.odf");
2✔
596
    CHECK(fs::path("/foo/bar.txt").replace_extension() == "/foo/bar");
2✔
597
    CHECK(fs::path("/foo/bar").replace_extension("odf") == "/foo/bar.odf");
2✔
598
    CHECK(fs::path("/foo/bar").replace_extension(".odf") == "/foo/bar.odf");
2✔
599
    CHECK(fs::path("/foo/bar.").replace_extension(".odf") == "/foo/bar.odf");
2✔
600
    CHECK(fs::path("/foo/bar/").replace_extension("odf") == "/foo/bar/.odf");
2✔
601

602
    fs::path p1 = "foo";
2✔
603
    fs::path p2 = "bar";
2✔
604
    p1.swap(p2);
2✔
605
    CHECK(p1 == "bar");
2✔
606
    CHECK(p2 == "foo");
2✔
607
}
2✔
608

609
TEST_CASE("fs.path.native.obs - path native format observers", "[filesystem][path][fs.path.native.obs]")
2✔
610
{
611
#ifdef GHC_OS_WINDOWS
612
#if defined(IS_WCHAR_PATH) || defined(GHC_USE_WCHAR_T)
613
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type(L"\u00E4\\\u20AC"));
614
    // CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("ä\\€")); // MSVCs returns local DBCS encoding
615
#else
616
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type("\xc3\xa4\\\xe2\x82\xac"));
617
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("\xc3\xa4\\\xe2\x82\xac"));
618
    CHECK(!::strcmp(fs::u8path("\xc3\xa4\\\xe2\x82\xac").c_str(), "\xc3\xa4\\\xe2\x82\xac"));
619
    CHECK((std::string)fs::u8path("\xc3\xa4\\\xe2\x82\xac") == std::string("\xc3\xa4\\\xe2\x82\xac"));
620
#endif
621
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").wstring() == std::wstring(L"\u00E4\\\u20AC"));
622
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
623
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u8string() == std::u8string(u8"\u00E4\\\u20AC"));
624
#else
625
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u8string() == std::string("\xc3\xa4\\\xe2\x82\xac"));
626
#endif
627
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u16string() == std::u16string(u"\u00E4\\\u20AC"));
628
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u32string() == std::u32string(U"\U000000E4\\\U000020AC"));
629
#else
630
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").native() == fs::path::string_type("\xc3\xa4/\xe2\x82\xac"));
2✔
631
    CHECK(!::strcmp(fs::u8path("\xc3\xa4/\xe2\x82\xac").c_str(), "\xc3\xa4/\xe2\x82\xac"));
2✔
632
    CHECK((std::string)fs::u8path("\xc3\xa4/\xe2\x82\xac") == std::string("\xc3\xa4/\xe2\x82\xac"));
2✔
633
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").string() == std::string("\xc3\xa4/\xe2\x82\xac"));
2✔
634
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").wstring() == std::wstring(L"ä/€"));
2✔
635
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
636
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u8string() == std::u8string(u8"\xc3\xa4/\xe2\x82\xac"));
1✔
637
#else
638
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u8string() == std::string("\xc3\xa4/\xe2\x82\xac"));
1✔
639
#endif
640
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u16string() == std::u16string(u"\u00E4/\u20AC"));
2✔
641
    INFO("This check might fail on GCC8 (with \"Illegal byte sequence\") due to not detecting the valid unicode codepoint U+1D11E.");
2✔
642
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac\xf0\x9d\x84\x9e").u16string() == std::u16string(u"\u00E4/\u20AC\U0001D11E"));
2✔
643
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u32string() == std::u32string(U"\U000000E4/\U000020AC"));
2✔
644
#endif
645
}
2✔
646

647
TEST_CASE("fs.path.generic.obs - path generic format observers", "[filesystem][path][fs.path.generic.obs]")
2✔
648
{
649
#ifdef GHC_OS_WINDOWS
650
#ifndef IS_WCHAR_PATH
651
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_string() == std::string("\xc3\xa4/\xe2\x82\xac"));
652
#endif
653
#ifndef USE_STD_FS
654
    auto t = fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_string<char, std::char_traits<char>, TestAllocator<char>>();
655
    CHECK(t.c_str() == std::string("\xc3\xa4/\xe2\x82\xac"));
656
#endif
657
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_wstring() == std::wstring(L"\U000000E4/\U000020AC"));
658
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
659
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u8string() == std::u8string(u8"\u00E4/\u20AC"));
660
#else
661
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u8string() == std::string("\xc3\xa4/\xe2\x82\xac"));
662
#endif
663
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u16string() == std::u16string(u"\u00E4/\u20AC"));
664
    CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u32string() == std::u32string(U"\U000000E4/\U000020AC"));
665
#else
666
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_string() == std::string("\xc3\xa4/\xe2\x82\xac"));
2✔
667
#ifndef USE_STD_FS
668
    auto t = fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_string<char, std::char_traits<char>, TestAllocator<char>>();
2✔
669
    CHECK(t.c_str() == std::string("\xc3\xa4/\xe2\x82\xac"));
2✔
670
#endif
671
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_wstring() == std::wstring(L"ä/€"));
2✔
672
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
673
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u8string() == std::u8string(u8"\xc3\xa4/\xe2\x82\xac"));
1✔
674
#else
675
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u8string() == std::string("\xc3\xa4/\xe2\x82\xac"));
1✔
676
#endif
677
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u16string() == std::u16string(u"\u00E4/\u20AC"));
2✔
678
    CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u32string() == std::u32string(U"\U000000E4/\U000020AC"));
2✔
679
#endif
680
}
2✔
681

682
TEST_CASE("fs.path.compare - path compare", "[filesystem][path][fs.path.compare]")
2✔
683
{
684
    CHECK(fs::path("/foo/b").compare("/foo/a") > 0);
2✔
685
    CHECK(fs::path("/foo/b").compare("/foo/b") == 0);
2✔
686
    CHECK(fs::path("/foo/b").compare("/foo/c") < 0);
2✔
687

688
    CHECK(fs::path("/foo/b").compare(std::string("/foo/a")) > 0);
2✔
689
    CHECK(fs::path("/foo/b").compare(std::string("/foo/b")) == 0);
2✔
690
    CHECK(fs::path("/foo/b").compare(std::string("/foo/c")) < 0);
2✔
691

692
    CHECK(fs::path("/foo/b").compare(fs::path("/foo/a")) > 0);
2✔
693
    CHECK(fs::path("/foo/b").compare(fs::path("/foo/b")) == 0);
2✔
694
    CHECK(fs::path("/foo/b").compare(fs::path("/foo/c")) < 0);
2✔
695

696
#ifdef GHC_OS_WINDOWS
697
    CHECK(fs::path("c:\\a\\b").compare("C:\\a\\b") == 0);
698
    CHECK(fs::path("c:\\a\\b").compare("d:\\a\\b") != 0);
699
    CHECK(fs::path("c:\\a\\b").compare("C:\\A\\b") != 0);
700
#endif
701

702
#ifdef LWG_2936_BEHAVIOUR
703
    CHECK(fs::path("/a/b/").compare("/a/b/c") < 0);
2✔
704
    CHECK(fs::path("/a/b/").compare("a/c") > 0);
2✔
705
#endif // LWG_2936_BEHAVIOUR
706
}
2✔
707

708
TEST_CASE("fs.path.decompose - path decomposition", "[filesystem][path][fs.path.decompose]")
2✔
709
{
710
    // root_name()
711
    CHECK(fs::path("").root_name() == "");
2✔
712
    CHECK(fs::path(".").root_name() == "");
2✔
713
    CHECK(fs::path("..").root_name() == "");
2✔
714
    CHECK(fs::path("foo").root_name() == "");
2✔
715
    CHECK(fs::path("/").root_name() == "");
2✔
716
    CHECK(fs::path("/foo").root_name() == "");
2✔
717
    CHECK(fs::path("foo/").root_name() == "");
2✔
718
    CHECK(fs::path("/foo/").root_name() == "");
2✔
719
    CHECK(fs::path("foo/bar").root_name() == "");
2✔
720
    CHECK(fs::path("/foo/bar").root_name() == "");
2✔
721
    CHECK(fs::path("///foo/bar").root_name() == "");
2✔
722
#ifdef GHC_OS_WINDOWS
723
    CHECK(fs::path("C:/foo").root_name() == "C:");
724
    CHECK(fs::path("C:\\foo").root_name() == "C:");
725
    CHECK(fs::path("C:foo").root_name() == "C:");
726
#endif
727

728
    // root_directory()
729
    CHECK(fs::path("").root_directory() == "");
2✔
730
    CHECK(fs::path(".").root_directory() == "");
2✔
731
    CHECK(fs::path("..").root_directory() == "");
2✔
732
    CHECK(fs::path("foo").root_directory() == "");
2✔
733
    CHECK(fs::path("/").root_directory() == "/");
2✔
734
    CHECK(fs::path("/foo").root_directory() == "/");
2✔
735
    CHECK(fs::path("foo/").root_directory() == "");
2✔
736
    CHECK(fs::path("/foo/").root_directory() == "/");
2✔
737
    CHECK(fs::path("foo/bar").root_directory() == "");
2✔
738
    CHECK(fs::path("/foo/bar").root_directory() == "/");
2✔
739
    CHECK(fs::path("///foo/bar").root_directory() == "/");
2✔
740
#ifdef GHC_OS_WINDOWS
741
    CHECK(fs::path("C:/foo").root_directory() == "/");
742
    CHECK(fs::path("C:\\foo").root_directory() == "/");
743
    CHECK(fs::path("C:foo").root_directory() == "");
744
#endif
745

746
    // root_path()
747
    CHECK(fs::path("").root_path() == "");
2✔
748
    CHECK(fs::path(".").root_path() == "");
2✔
749
    CHECK(fs::path("..").root_path() == "");
2✔
750
    CHECK(fs::path("foo").root_path() == "");
2✔
751
    CHECK(fs::path("/").root_path() == "/");
2✔
752
    CHECK(fs::path("/foo").root_path() == "/");
2✔
753
    CHECK(fs::path("foo/").root_path() == "");
2✔
754
    CHECK(fs::path("/foo/").root_path() == "/");
2✔
755
    CHECK(fs::path("foo/bar").root_path() == "");
2✔
756
    CHECK(fs::path("/foo/bar").root_path() == "/");
2✔
757
    CHECK(fs::path("///foo/bar").root_path() == "/");
2✔
758
#ifdef GHC_OS_WINDOWS
759
    CHECK(fs::path("C:/foo").root_path() == "C:/");
760
    CHECK(fs::path("C:\\foo").root_path() == "C:/");
761
    CHECK(fs::path("C:foo").root_path() == "C:");
762
#endif
763

764
    // relative_path()
765
    CHECK(fs::path("").relative_path() == "");
2✔
766
    CHECK(fs::path(".").relative_path() == ".");
2✔
767
    CHECK(fs::path("..").relative_path() == "..");
2✔
768
    CHECK(fs::path("foo").relative_path() == "foo");
2✔
769
    CHECK(fs::path("/").relative_path() == "");
2✔
770
    CHECK(fs::path("/foo").relative_path() == "foo");
2✔
771
    CHECK(fs::path("foo/").relative_path() == "foo/");
2✔
772
    CHECK(fs::path("/foo/").relative_path() == "foo/");
2✔
773
    CHECK(fs::path("foo/bar").relative_path() == "foo/bar");
2✔
774
    CHECK(fs::path("/foo/bar").relative_path() == "foo/bar");
2✔
775
    CHECK(fs::path("///foo/bar").relative_path() == "foo/bar");
2✔
776
#ifdef GHC_OS_WINDOWS
777
    CHECK(fs::path("C:/foo").relative_path() == "foo");
778
    CHECK(fs::path("C:\\foo").relative_path() == "foo");
779
    CHECK(fs::path("C:foo").relative_path() == "foo");
780
#endif
781

782
    // parent_path()
783
    CHECK(fs::path("").parent_path() == "");
2✔
784
    CHECK(fs::path(".").parent_path() == "");
2✔
785
    CHECK(fs::path("..").parent_path() == "");  // unintuitive but as defined in the standard
2✔
786
    CHECK(fs::path("foo").parent_path() == "");
2✔
787
    CHECK(fs::path("/").parent_path() == "/");
2✔
788
    CHECK(fs::path("/foo").parent_path() == "/");
2✔
789
    CHECK(fs::path("foo/").parent_path() == "foo");
2✔
790
    CHECK(fs::path("/foo/").parent_path() == "/foo");
2✔
791
    CHECK(fs::path("foo/bar").parent_path() == "foo");
2✔
792
    CHECK(fs::path("/foo/bar").parent_path() == "/foo");
2✔
793
    CHECK(fs::path("///foo/bar").parent_path() == "/foo");
2✔
794
#ifdef GHC_OS_WINDOWS
795
    CHECK(fs::path("C:/foo").parent_path() == "C:/");
796
    CHECK(fs::path("C:\\foo").parent_path() == "C:/");
797
    CHECK(fs::path("C:foo").parent_path() == "C:");
798
#endif
799

800
    // filename()
801
    CHECK(fs::path("").filename() == "");
2✔
802
    CHECK(fs::path(".").filename() == ".");
2✔
803
    CHECK(fs::path("..").filename() == "..");
2✔
804
    CHECK(fs::path("foo").filename() == "foo");
2✔
805
    CHECK(fs::path("/").filename() == "");
2✔
806
    CHECK(fs::path("/foo").filename() == "foo");
2✔
807
    CHECK(fs::path("foo/").filename() == "");
2✔
808
    CHECK(fs::path("/foo/").filename() == "");
2✔
809
    CHECK(fs::path("foo/bar").filename() == "bar");
2✔
810
    CHECK(fs::path("/foo/bar").filename() == "bar");
2✔
811
    CHECK(fs::path("///foo/bar").filename() == "bar");
2✔
812
#ifdef GHC_OS_WINDOWS
813
    CHECK(fs::path("C:/foo").filename() == "foo");
814
    CHECK(fs::path("C:\\foo").filename() == "foo");
815
    CHECK(fs::path("C:foo").filename() == "foo");
816
    CHECK(fs::path("t:est.txt").filename() == "est.txt");
817
#else
818
    CHECK(fs::path("t:est.txt").filename() == "t:est.txt");
2✔
819
#endif
820

821
    // stem()
822
    CHECK(fs::path("/foo/bar.txt").stem() == "bar");
2✔
823
    {
824
        fs::path p = "foo.bar.baz.tar";
2✔
825
        CHECK(p.extension() == ".tar");
2✔
826
        p = p.stem();
2✔
827
        CHECK(p.extension() == ".baz");
2✔
828
        p = p.stem();
2✔
829
        CHECK(p.extension() == ".bar");
2✔
830
        p = p.stem();
2✔
831
        CHECK(p == "foo");
2✔
832
    }
2✔
833
    CHECK(fs::path("/foo/.profile").stem() == ".profile");
2✔
834
    CHECK(fs::path(".bar").stem() == ".bar");
2✔
835
    CHECK(fs::path("..bar").stem() == ".");
2✔
836
#ifdef GHC_OS_WINDOWS
837
    CHECK(fs::path("t:est.txt").stem() == "est");
838
#else
839
    CHECK(fs::path("t:est.txt").stem() == "t:est");
2✔
840
#endif
841
    CHECK(fs::path("/foo/.").stem() == ".");
2✔
842
    CHECK(fs::path("/foo/..").stem() == "..");
2✔
843

844
    // extension()
845
    CHECK(fs::path("/foo/bar.txt").extension() == ".txt");
2✔
846
    CHECK(fs::path("/foo/bar").extension() == "");
2✔
847
    CHECK(fs::path("/foo/.profile").extension() == "");
2✔
848
    CHECK(fs::path(".bar").extension() == "");
2✔
849
    CHECK(fs::path("..bar").extension() == ".bar");
2✔
850
    CHECK(fs::path("t:est.txt").extension() == ".txt");
2✔
851
    CHECK(fs::path("/foo/.").extension() == "");
2✔
852
    CHECK(fs::path("/foo/..").extension() == "");
2✔
853

854
    if (has_host_root_name_support()) {
2✔
855
        // //host-based root-names
856
        CHECK(fs::path("//host").root_name() == "//host");
2✔
857
        CHECK(fs::path("//host/foo").root_name() == "//host");
2✔
858
        CHECK(fs::path("//host").root_directory() == "");
2✔
859
        CHECK(fs::path("//host/foo").root_directory() == "/");
2✔
860
        CHECK(fs::path("//host").root_path() == "//host");
2✔
861
        CHECK(fs::path("//host/foo").root_path() == "//host/");
2✔
862
        CHECK(fs::path("//host").relative_path() == "");
2✔
863
        CHECK(fs::path("//host/foo").relative_path() == "foo");
2✔
864
        CHECK(fs::path("//host").parent_path() == "//host");
2✔
865
        CHECK(fs::path("//host/foo").parent_path() == "//host/");
2✔
866
        CHECK(fs::path("//host").filename() == "");
2✔
867
        CHECK(fs::path("//host/foo").filename() == "foo");
2✔
868
    }
869
}
2✔
870

871
TEST_CASE("fs.path.query - path query", "[fielsystem][path][fs.path.query]")
2✔
872
{
873
    // empty
874
    CHECK(fs::path("").empty());
2✔
875
    CHECK(!fs::path("foo").empty());
2✔
876

877
    // has_root_path()
878
    CHECK(!fs::path("foo").has_root_path());
2✔
879
    CHECK(!fs::path("foo/bar").has_root_path());
2✔
880
    CHECK(fs::path("/foo").has_root_path());
2✔
881
#ifdef GHC_OS_WINDOWS
882
    CHECK(fs::path("C:foo").has_root_path());
883
    CHECK(fs::path("C:/foo").has_root_path());
884
#endif
885

886
    // has_root_name()
887
    CHECK(!fs::path("foo").has_root_name());
2✔
888
    CHECK(!fs::path("foo/bar").has_root_name());
2✔
889
    CHECK(!fs::path("/foo").has_root_name());
2✔
890
#ifdef GHC_OS_WINDOWS
891
    CHECK(fs::path("C:foo").has_root_name());
892
    CHECK(fs::path("C:/foo").has_root_name());
893
#endif
894

895
    // has_root_directory()
896
    CHECK(!fs::path("foo").has_root_directory());
2✔
897
    CHECK(!fs::path("foo/bar").has_root_directory());
2✔
898
    CHECK(fs::path("/foo").has_root_directory());
2✔
899
#ifdef GHC_OS_WINDOWS
900
    CHECK(!fs::path("C:foo").has_root_directory());
901
    CHECK(fs::path("C:/foo").has_root_directory());
902
#endif
903

904
    // has_relative_path()
905
    CHECK(!fs::path("").has_relative_path());
2✔
906
    CHECK(!fs::path("/").has_relative_path());
2✔
907
    CHECK(fs::path("/foo").has_relative_path());
2✔
908

909
    // has_parent_path()
910
    CHECK(!fs::path("").has_parent_path());
2✔
911
    CHECK(!fs::path(".").has_parent_path());
2✔
912
    CHECK(!fs::path("..").has_parent_path());  // unintuitive but as defined in the standard
2✔
913
    CHECK(!fs::path("foo").has_parent_path());
2✔
914
    CHECK(fs::path("/").has_parent_path());
2✔
915
    CHECK(fs::path("/foo").has_parent_path());
2✔
916
    CHECK(fs::path("foo/").has_parent_path());
2✔
917
    CHECK(fs::path("/foo/").has_parent_path());
2✔
918

919
    // has_filename()
920
    CHECK(fs::path("foo").has_filename());
2✔
921
    CHECK(fs::path("foo/bar").has_filename());
2✔
922
    CHECK(!fs::path("/foo/bar/").has_filename());
2✔
923

924
    // has_stem()
925
    CHECK(fs::path("foo").has_stem());
2✔
926
    CHECK(fs::path("foo.bar").has_stem());
2✔
927
    CHECK(fs::path(".profile").has_stem());
2✔
928
    CHECK(!fs::path("/foo/").has_stem());
2✔
929

930
    // has_extension()
931
    CHECK(!fs::path("foo").has_extension());
2✔
932
    CHECK(fs::path("foo.bar").has_extension());
2✔
933
    CHECK(!fs::path(".profile").has_extension());
2✔
934

935
    // is_absolute()
936
    CHECK(!fs::path("foo/bar").is_absolute());
2✔
937
#ifdef GHC_OS_WINDOWS
938
    CHECK(!fs::path("/foo").is_absolute());
939
    CHECK(!fs::path("c:foo").is_absolute());
940
    CHECK(fs::path("c:/foo").is_absolute());
941
#else
942
    CHECK(fs::path("/foo").is_absolute());
2✔
943
#endif
944

945
    // is_relative()
946
    CHECK(fs::path("foo/bar").is_relative());
2✔
947
#ifdef GHC_OS_WINDOWS
948
    CHECK(fs::path("/foo").is_relative());
949
    CHECK(fs::path("c:foo").is_relative());
950
    CHECK(!fs::path("c:/foo").is_relative());
951
#else
952
    CHECK(!fs::path("/foo").is_relative());
2✔
953
#endif
954

955
    if (has_host_root_name_support()) {
2✔
956
        CHECK(fs::path("//host").has_root_name());
2✔
957
        CHECK(fs::path("//host/foo").has_root_name());
2✔
958
        CHECK(fs::path("//host").has_root_path());
2✔
959
        CHECK(fs::path("//host/foo").has_root_path());
2✔
960
        CHECK(!fs::path("//host").has_root_directory());
2✔
961
        CHECK(fs::path("//host/foo").has_root_directory());
2✔
962
        CHECK(!fs::path("//host").has_relative_path());
2✔
963
        CHECK(fs::path("//host/foo").has_relative_path());
2✔
964
        CHECK(fs::path("//host/foo").is_absolute());
2✔
965
        CHECK(!fs::path("//host/foo").is_relative());
2✔
966
    }
967
}
2✔
968

969
TEST_CASE("fs.path.gen - path generation", "[filesystem][path][fs.path.gen]")
2✔
970
{
971
    // lexically_normal()
972
    CHECK(fs::path("foo/./bar/..").lexically_normal() == "foo/");
2✔
973
    CHECK(fs::path("foo/.///bar/../").lexically_normal() == "foo/");
2✔
974
    CHECK(fs::path("/foo/../..").lexically_normal() == "/");
2✔
975
    CHECK(fs::path("foo/..").lexically_normal() == ".");
2✔
976
    CHECK(fs::path("ab/cd/ef/../../qw").lexically_normal() == "ab/qw");
2✔
977
    CHECK(fs::path("a/b/../../../c").lexically_normal() == "../c");
2✔
978
    CHECK(fs::path("../").lexically_normal() == "..");
2✔
979
    CHECK(fs::path("./../foo/../bar").lexically_normal() == "../bar");
2✔
980
    CHECK(fs::path("./../foo/../../bar").lexically_normal() == "../../bar");
2✔
981
    CHECK(fs::path("../../foo/../bar").lexically_normal() == "../../bar");
2✔
982
#ifdef GHC_OS_WINDOWS
983
    CHECK(fs::path("\\/\\///\\/").lexically_normal() == "/");
984
    CHECK(fs::path("a/b/..\\//..///\\/../c\\\\/").lexically_normal() == "../c/");
985
    CHECK(fs::path("..a/b/..\\//..///\\/../c\\\\/").lexically_normal() == "../c/");
986
    CHECK(fs::path("..\\").lexically_normal() == "..");
987
    CHECK(fs::path("//?/UNC/::1/c$/foo").lexically_normal() == R"(\\?\UNC\::1\c$\foo)");
988
    CHECK(fs::path(R"(\\?\UNC\a::1\c$\foo)").lexically_normal() == R"(\\?\UNC\a::1\c$\foo)");
989
    CHECK(fs::path(R"(\\?\UNC\fe80::1\c$\foo)").lexically_normal() == R"(\\?\UNC\fe80::1\c$\foo)");
990
    CHECK(fs::path(R"(\\?\UNC\server\share\foo)").lexically_normal() == R"(\\?\UNC\server\share\foo)");
991
    CHECK(fs::path(R"(\\server\share\foo)").lexically_normal() == R"(\\server\share\foo)");
992
    CHECK(fs::path(R"(C:foo\..\bar)").lexically_normal() == R"(C:bar)");
993
#endif
994

995
    // lexically_relative()
996
    CHECK(fs::path("/a/d").lexically_relative("/a/b/c") == "../../d");
2✔
997
    CHECK(fs::path("/a/b/c").lexically_relative("/a/d") == "../b/c");
2✔
998
    CHECK(fs::path("/a/b/c").lexically_relative("/a/b/c/d/..") == ".");
2✔
999
    CHECK(fs::path("/a/b/c/").lexically_relative("/a/b/c/d/..") == ".");
2✔
1000
    CHECK(fs::path("").lexically_relative("/a/..") == "");
2✔
1001
    CHECK(fs::path("").lexically_relative("a/..") == ".");
2✔
1002
    CHECK(fs::path("a/b/c").lexically_relative("a") == "b/c");
2✔
1003
    CHECK(fs::path("a/b/c").lexically_relative("a/b/c/x/y") == "../..");
2✔
1004
    CHECK(fs::path("a/b/c").lexically_relative("a/b/c") == ".");
2✔
1005
    CHECK(fs::path("a/b").lexically_relative("c/d") == "../../a/b");
2✔
1006
    CHECK(fs::path("a/b").lexically_relative("a/") == "b");
2✔
1007
    if (has_host_root_name_support()) {
2✔
1008
        CHECK(fs::path("//host1/foo").lexically_relative("//host2.bar") == "");
2✔
1009
    }
1010
#ifdef GHC_OS_WINDOWS
1011
    CHECK(fs::path("c:/foo").lexically_relative("/bar") == "");
1012
    CHECK(fs::path("c:foo").lexically_relative("c:/bar") == "");
1013
    CHECK(fs::path("foo").lexically_relative("/bar") == "");
1014
    CHECK(fs::path("c:/foo/bar.txt").lexically_relative("c:/foo/") == "bar.txt");
1015
    CHECK(fs::path("c:/foo/bar.txt").lexically_relative("C:/foo/") == "bar.txt");
1016
#else
1017
    CHECK(fs::path("/foo").lexically_relative("bar") == "");
2✔
1018
    CHECK(fs::path("foo").lexically_relative("/bar") == "");
2✔
1019
#endif
1020

1021
    // lexically_proximate()
1022
    CHECK(fs::path("/a/d").lexically_proximate("/a/b/c") == "../../d");
2✔
1023
    if (has_host_root_name_support()) {
2✔
1024
        CHECK(fs::path("//host1/a/d").lexically_proximate("//host2/a/b/c") == "//host1/a/d");
2✔
1025
    }
1026
    CHECK(fs::path("a/d").lexically_proximate("/a/b/c") == "a/d");
2✔
1027
#ifdef GHC_OS_WINDOWS
1028
    CHECK(fs::path("c:/a/d").lexically_proximate("c:/a/b/c") == "../../d");
1029
    CHECK(fs::path("c:/a/d").lexically_proximate("d:/a/b/c") == "c:/a/d");
1030
    CHECK(fs::path("c:/foo").lexically_proximate("/bar") == "c:/foo");
1031
    CHECK(fs::path("c:foo").lexically_proximate("c:/bar") == "c:foo");
1032
    CHECK(fs::path("foo").lexically_proximate("/bar") == "foo");
1033
#else
1034
    CHECK(fs::path("/foo").lexically_proximate("bar") == "/foo");
2✔
1035
    CHECK(fs::path("foo").lexically_proximate("/bar") == "foo");
2✔
1036
#endif
1037
}
2✔
1038

1039
static std::string iterateResult(const fs::path& path)
30✔
1040
{
1041
    std::ostringstream result;
30✔
1042
    for (fs::path::const_iterator i = path.begin(); i != path.end(); ++i) {
94✔
1043
        if (i != path.begin()) {
64✔
1044
            result << ",";
36✔
1045
        }
1046
        result << i->generic_string();
64✔
1047
    }
30✔
1048
    return result.str();
60✔
1049
}
30✔
1050

1051
static std::string reverseIterateResult(const fs::path& path)
30✔
1052
{
1053
    std::ostringstream result;
30✔
1054
    fs::path::const_iterator iter = path.end();
30✔
1055
    bool first = true;
30✔
1056
    if (iter != path.begin()) {
30✔
1057
        do {
1058
            --iter;
64✔
1059
            if (!first) {
64✔
1060
                result << ",";
36✔
1061
            }
1062
            first = false;
64✔
1063
            result << iter->generic_string();
64✔
1064
        } while (iter != path.begin());
64✔
1065
    }
1066
    return result.str();
60✔
1067
}
30✔
1068

1069
TEST_CASE("fs.path.itr - path iterators", "[filesystem][path][fs.path.itr]")
2✔
1070
{
1071
    CHECK(iterateResult(fs::path()).empty());
2✔
1072
    CHECK("." == iterateResult(fs::path(".")));
2✔
1073
    CHECK(".." == iterateResult(fs::path("..")));
2✔
1074
    CHECK("foo" == iterateResult(fs::path("foo")));
2✔
1075
    CHECK("/" == iterateResult(fs::path("/")));
2✔
1076
    CHECK("/,foo" == iterateResult(fs::path("/foo")));
2✔
1077
    CHECK("foo," == iterateResult(fs::path("foo/")));
2✔
1078
    CHECK("/,foo," == iterateResult(fs::path("/foo/")));
2✔
1079
    CHECK("foo,bar" == iterateResult(fs::path("foo/bar")));
2✔
1080
    CHECK("/,foo,bar" == iterateResult(fs::path("/foo/bar")));
2✔
1081
#ifndef USE_STD_FS
1082
    // ghc::filesystem enforces redundant slashes to be reduced to one
1083
    CHECK("/,foo,bar" == iterateResult(fs::path("///foo/bar")));
2✔
1084
#else
1085
    // typically std::filesystem keeps them
1086
    CHECK("///,foo,bar" == iterateResult(fs::path("///foo/bar")));
1087
#endif
1088
    CHECK("/,foo,bar," == iterateResult(fs::path("/foo/bar///")));
2✔
1089
    CHECK("foo,.,bar,..," == iterateResult(fs::path("foo/.///bar/../")));
2✔
1090
#ifdef GHC_OS_WINDOWS
1091
    CHECK("C:,/,foo" == iterateResult(fs::path("C:/foo")));
1092
#endif
1093

1094
    CHECK(reverseIterateResult(fs::path()).empty());
2✔
1095
    CHECK("." == reverseIterateResult(fs::path(".")));
2✔
1096
    CHECK(".." == reverseIterateResult(fs::path("..")));
2✔
1097
    CHECK("foo" == reverseIterateResult(fs::path("foo")));
2✔
1098
    CHECK("/" == reverseIterateResult(fs::path("/")));
2✔
1099
    CHECK("foo,/" == reverseIterateResult(fs::path("/foo")));
2✔
1100
    CHECK(",foo" == reverseIterateResult(fs::path("foo/")));
2✔
1101
    CHECK(",foo,/" == reverseIterateResult(fs::path("/foo/")));
2✔
1102
    CHECK("bar,foo" == reverseIterateResult(fs::path("foo/bar")));
2✔
1103
    CHECK("bar,foo,/" == reverseIterateResult(fs::path("/foo/bar")));
2✔
1104
#ifndef USE_STD_FS
1105
    // ghc::filesystem enforces redundant slashes to be reduced to one
1106
    CHECK("bar,foo,/" == reverseIterateResult(fs::path("///foo/bar")));
2✔
1107
#else
1108
    // typically std::filesystem keeps them
1109
    CHECK("bar,foo,///" == reverseIterateResult(fs::path("///foo/bar")));
1110
#endif
1111
    CHECK(",bar,foo,/" == reverseIterateResult(fs::path("/foo/bar///")));
2✔
1112
    CHECK(",..,bar,.,foo" == reverseIterateResult(fs::path("foo/.///bar/../")));
2✔
1113
#ifdef GHC_OS_WINDOWS
1114
    CHECK("foo,/,C:" == reverseIterateResult(fs::path("C:/foo")));
1115
    CHECK("foo,C:" == reverseIterateResult(fs::path("C:foo")));
1116
#endif
1117
    {
1118
        fs::path p1 = "/foo/bar/test.txt";
2✔
1119
        fs::path p2;
2✔
1120
        for (auto pe : p1) {
10✔
1121
            p2 /= pe;
8✔
1122
        }
10✔
1123
        CHECK(p1 == p2);
2✔
1124
        CHECK("bar" == *(--fs::path("/foo/bar").end()));
2✔
1125
        auto p = fs::path("/foo/bar");
2✔
1126
        auto pi = p.end();
2✔
1127
        pi--;
2✔
1128
        CHECK("bar" == *pi);
2✔
1129
    }
2✔
1130

1131
    if (has_host_root_name_support()) {
2✔
1132
        CHECK("foo" == *(--fs::path("//host/foo").end()));
2✔
1133
        auto p = fs::path("//host/foo");
2✔
1134
        auto pi = p.end();
2✔
1135
        pi--;
2✔
1136
        CHECK("foo" == *pi);
2✔
1137
        CHECK("//host" == iterateResult(fs::path("//host")));
2✔
1138
        CHECK("//host,/,foo" == iterateResult(fs::path("//host/foo")));
2✔
1139
        CHECK("//host" == reverseIterateResult(fs::path("//host")));
2✔
1140
        CHECK("foo,/,//host" == reverseIterateResult(fs::path("//host/foo")));
2✔
1141
        {
1142
            fs::path p1 = "//host/foo/bar/test.txt";
2✔
1143
            fs::path p2;
2✔
1144
            for (auto pe : p1) {
12✔
1145
                p2 /= pe;
10✔
1146
            }
12✔
1147
            CHECK(p1 == p2);
2✔
1148
        }
2✔
1149
    }
2✔
1150
}
2✔
1151

1152
TEST_CASE("fs.path.nonmember - path non-member functions", "[filesystem][path][fs.path.nonmember]")
2✔
1153
{
1154
    fs::path p1("foo/bar");
2✔
1155
    fs::path p2("some/other");
2✔
1156
    fs::swap(p1, p2);
2✔
1157
    CHECK(p1 == "some/other");
2✔
1158
    CHECK(p2 == "foo/bar");
2✔
1159
    CHECK(hash_value(p1));
2✔
1160
    CHECK(p2 < p1);
2✔
1161
    CHECK(p2 <= p1);
2✔
1162
    CHECK(p1 <= p1);
2✔
1163
    CHECK(!(p1 < p2));
2✔
1164
    CHECK(!(p1 <= p2));
2✔
1165
    CHECK(p1 > p2);
2✔
1166
    CHECK(p1 >= p2);
2✔
1167
    CHECK(p1 >= p1);
2✔
1168
    CHECK(!(p2 > p1));
2✔
1169
    CHECK(!(p2 >= p1));
2✔
1170
    CHECK(p1 != p2);
2✔
1171
    CHECK(p1 / p2 == "some/other/foo/bar");
2✔
1172
}
2✔
1173

1174
TEST_CASE("fs.path.io - path inserter and extractor", "[filesystem][path][fs.path.io]")
2✔
1175
{
1176
    {
1177
        std::ostringstream os;
2✔
1178
        os << fs::path("/root/foo bar");
2✔
1179
#ifdef GHC_OS_WINDOWS
1180
        CHECK(os.str() == "\"\\\\root\\\\foo bar\"");
1181
#else
1182
        CHECK(os.str() == "\"/root/foo bar\"");
2✔
1183
#endif
1184
    }
2✔
1185
    {
1186
        std::ostringstream os;
2✔
1187
        os << fs::path("/root/foo\"bar");
2✔
1188
#ifdef GHC_OS_WINDOWS
1189
        CHECK(os.str() == "\"\\\\root\\\\foo\\\"bar\"");
1190
#else
1191
        CHECK(os.str() == "\"/root/foo\\\"bar\"");
2✔
1192
#endif
1193
    }
2✔
1194

1195
    {
1196
        std::istringstream is("\"/root/foo bar\"");
4✔
1197
        fs::path p;
2✔
1198
        is >> p;
2✔
1199
        CHECK(p == fs::path("/root/foo bar"));
2✔
1200
        CHECK((is.flags() & std::ios_base::skipws) == std::ios_base::skipws);
2✔
1201
    }
2✔
1202
    {
1203
        std::istringstream is("\"/root/foo bar\"");
4✔
1204
        is >> std::noskipws;
2✔
1205
        fs::path p;
2✔
1206
        is >> p;
2✔
1207
        CHECK(p == fs::path("/root/foo bar"));
2✔
1208
        CHECK((is.flags() & std::ios_base::skipws) != std::ios_base::skipws);
2✔
1209
    }
2✔
1210
    {
1211
        std::istringstream is("\"/root/foo\\\"bar\"");
4✔
1212
        fs::path p;
2✔
1213
        is >> p;
2✔
1214
        CHECK(p == fs::path("/root/foo\"bar"));
2✔
1215
    }
2✔
1216
    {
1217
        std::istringstream is("/root/foo");
4✔
1218
        fs::path p;
2✔
1219
        is >> p;
2✔
1220
        CHECK(p == fs::path("/root/foo"));
2✔
1221
    }
2✔
1222
}
2✔
1223

1224
TEST_CASE("fs.path.factory - path factory functions", "[filesystem][path][fs.path.factory]")
2✔
1225
{
1226
    CHECK(fs::u8path("foo/bar") == fs::path("foo/bar"));
2✔
1227
    CHECK(fs::u8path("foo/bar") == fs::path("foo/bar"));
2✔
1228
    std::string str("/foo/bar/test.txt");
2✔
1229
    CHECK(fs::u8path(str.begin(), str.end()) == str);
2✔
1230
}
2✔
1231

1232
TEST_CASE("fs.class.filesystem_error - class filesystem_error", "[filesystem][filesystem_error][fs.class.filesystem_error]")
2✔
1233
{
1234
    std::error_code ec(1, std::system_category());
2✔
1235
    fs::filesystem_error fse("None", std::error_code());
4✔
1236
    fse = fs::filesystem_error("Some error", ec);
2✔
1237
    CHECK(fse.code().value() == 1);
2✔
1238
    CHECK(!std::string(fse.what()).empty());
2✔
1239
    CHECK(fse.path1().empty());
2✔
1240
    CHECK(fse.path2().empty());
2✔
1241
    fse = fs::filesystem_error("Some error", fs::path("foo/bar"), ec);
2✔
1242
    CHECK(!std::string(fse.what()).empty());
2✔
1243
    CHECK(fse.path1() == "foo/bar");
2✔
1244
    CHECK(fse.path2().empty());
2✔
1245
    fse = fs::filesystem_error("Some error", fs::path("foo/bar"), fs::path("some/other"), ec);
2✔
1246
    CHECK(!std::string(fse.what()).empty());
2✔
1247
    CHECK(fse.path1() == "foo/bar");
2✔
1248
    CHECK(fse.path2() == "some/other");
2✔
1249
#if defined(GHC_OS_WINDOWS) && !defined(USE_STD_FS) && !defined(GHC_FILESYSTEM_FWD_TEST)
1250
    CHECK_FALSE(fs::detail::systemErrorText(ERROR_FILE_NOT_FOUND).empty());
1251
    const DWORD unknownError = 0xffffffffu;
1252
    const auto unknownErrorText = fs::detail::systemErrorText(unknownError);
1253
    CHECK_FALSE(unknownErrorText.empty());
1254
    CHECK(unknownErrorText.find(std::to_string(unknownError)) != std::string::npos);
1255
#endif
1256
}
2✔
1257

1258
static constexpr fs::perms constExprOwnerAll()
1259
{
1260
    return fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec;
1261
}
1262

1263
TEST_CASE("fs.enum - enum class perms", "[filesystem][enum][fs.enum]")
2✔
1264
{
1265
    static_assert(constExprOwnerAll() == fs::perms::owner_all, "constexpr didn't result in owner_all");
1266
    CHECK((fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec) == fs::perms::owner_all);
2✔
1267
    CHECK((fs::perms::group_read | fs::perms::group_write | fs::perms::group_exec) == fs::perms::group_all);
2✔
1268
    CHECK((fs::perms::others_read | fs::perms::others_write | fs::perms::others_exec) == fs::perms::others_all);
2✔
1269
    CHECK((fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all) == fs::perms::all);
2✔
1270
    CHECK((fs::perms::all | fs::perms::set_uid | fs::perms::set_gid | fs::perms::sticky_bit) == fs::perms::mask);
2✔
1271
}
2✔
1272

1273
TEST_CASE("fs.class.file_status - class file_status", "[filesystem][file_status][fs.class.file_status]")
2✔
1274
{
1275
    {
1276
        fs::file_status fs;
2✔
1277
        CHECK(fs.type() == fs::file_type::none);
2✔
1278
        CHECK(fs.permissions() == fs::perms::unknown);
2✔
1279
    }
2✔
1280
    {
1281
        fs::file_status fs{fs::file_type::regular};
2✔
1282
        CHECK(fs.type() == fs::file_type::regular);
2✔
1283
        CHECK(fs.permissions() == fs::perms::unknown);
2✔
1284
    }
2✔
1285
    {
1286
        fs::file_status fs{fs::file_type::directory, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
2✔
1287
        CHECK(fs.type() == fs::file_type::directory);
2✔
1288
        CHECK(fs.permissions() == fs::perms::owner_all);
2✔
1289
        fs.type(fs::file_type::block);
2✔
1290
        CHECK(fs.type() == fs::file_type::block);
2✔
1291
        fs.type(fs::file_type::character);
2✔
1292
        CHECK(fs.type() == fs::file_type::character);
2✔
1293
        fs.type(fs::file_type::fifo);
2✔
1294
        CHECK(fs.type() == fs::file_type::fifo);
2✔
1295
        fs.type(fs::file_type::symlink);
2✔
1296
        CHECK(fs.type() == fs::file_type::symlink);
2✔
1297
        fs.type(fs::file_type::socket);
2✔
1298
        CHECK(fs.type() == fs::file_type::socket);
2✔
1299
        fs.permissions(fs.permissions() | fs::perms::group_all | fs::perms::others_all);
2✔
1300
        CHECK(fs.permissions() == fs::perms::all);
2✔
1301
    }
2✔
1302
    {
1303
        fs::file_status fst(fs::file_type::regular);
2✔
1304
        fs::file_status fs(std::move(fst));
2✔
1305
        CHECK(fs.type() == fs::file_type::regular);
2✔
1306
        CHECK(fs.permissions() == fs::perms::unknown);
2✔
1307
    }
2✔
1308
#if !defined(USE_STD_FS) || defined(GHC_FILESYSTEM_RUNNING_CPP20)
1309
    {
1310
        fs::file_status fs1{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
2✔
1311
        fs::file_status fs2{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
2✔
1312
        fs::file_status fs3{fs::file_type::directory, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec};
2✔
1313
        fs::file_status fs4{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write};
2✔
1314
        CHECK(fs1 == fs2);
2✔
1315
        CHECK_FALSE(fs1 == fs3);
2✔
1316
        CHECK_FALSE(fs1 == fs4);
2✔
1317
    }
2✔
1318
#endif
1319
}
2✔
1320

1321
TEST_CASE("fs.dir.entry - class directory_entry", "[filesystem][directory_entry][fs.dir.entry]")
2✔
1322
{
1323
    TemporaryDirectory t;
2✔
1324
    std::error_code ec;
2✔
1325
    auto de = fs::directory_entry(t.path());
2✔
1326
    CHECK(de.path() == t.path());
2✔
1327
    CHECK((fs::path)de == t.path());
2✔
1328
    CHECK(de.exists());
2✔
1329
    CHECK(!de.is_block_file());
2✔
1330
    CHECK(!de.is_character_file());
2✔
1331
    CHECK(de.is_directory());
2✔
1332
    CHECK(!de.is_fifo());
2✔
1333
    CHECK(!de.is_other());
2✔
1334
    CHECK(!de.is_regular_file());
2✔
1335
    CHECK(!de.is_socket());
2✔
1336
    CHECK(!de.is_symlink());
2✔
1337
    CHECK(de.status().type() == fs::file_type::directory);
2✔
1338
    ec.clear();
2✔
1339
    CHECK(de.status(ec).type() == fs::file_type::directory);
2✔
1340
    CHECK(!ec);
2✔
1341
    CHECK_NOTHROW(de.refresh());
2✔
1342
    fs::directory_entry none;
2✔
1343
    CHECK_THROWS_AS(none.refresh(), fs::filesystem_error);
2✔
1344
    ec.clear();
2✔
1345
    CHECK_NOTHROW(none.refresh(ec));
2✔
1346
    CHECK(ec);
2✔
1347
    CHECK_THROWS_AS(de.assign(""), fs::filesystem_error);
4✔
1348
    ec.clear();
2✔
1349
    CHECK_NOTHROW(de.assign("", ec));
2✔
1350
    CHECK(ec);
2✔
1351
    generateFile(t.path() / "foo", 1234);
2✔
1352
    auto now = fs::file_time_type::clock::now();
2✔
1353
    CHECK_NOTHROW(de.assign(t.path() / "foo"));
2✔
1354
    CHECK_NOTHROW(de.assign(t.path() / "foo", ec));
2✔
1355
    CHECK(!ec);
2✔
1356
    de = fs::directory_entry(t.path() / "foo");
2✔
1357
    CHECK(de.path() == t.path() / "foo");
2✔
1358
    CHECK(de.exists());
2✔
1359
    CHECK(de.exists(ec));
2✔
1360
    CHECK(!ec);
2✔
1361
    CHECK(!de.is_block_file());
2✔
1362
    CHECK(!de.is_block_file(ec));
2✔
1363
    CHECK(!ec);
2✔
1364
    CHECK(!de.is_character_file());
2✔
1365
    CHECK(!de.is_character_file(ec));
2✔
1366
    CHECK(!ec);
2✔
1367
    CHECK(!de.is_directory());
2✔
1368
    CHECK(!de.is_directory(ec));
2✔
1369
    CHECK(!ec);
2✔
1370
    CHECK(!de.is_fifo());
2✔
1371
    CHECK(!de.is_fifo(ec));
2✔
1372
    CHECK(!ec);
2✔
1373
    CHECK(!de.is_other());
2✔
1374
    CHECK(!de.is_other(ec));
2✔
1375
    CHECK(!ec);
2✔
1376
    CHECK(de.is_regular_file());
2✔
1377
    CHECK(de.is_regular_file(ec));
2✔
1378
    CHECK(!ec);
2✔
1379
    CHECK(!de.is_socket());
2✔
1380
    CHECK(!de.is_socket(ec));
2✔
1381
    CHECK(!ec);
2✔
1382
    CHECK(!de.is_symlink());
2✔
1383
    CHECK(!de.is_symlink(ec));
2✔
1384
    CHECK(!ec);
2✔
1385
    CHECK(de.file_size() == 1234);
2✔
1386
    CHECK(de.file_size(ec) == 1234);
2✔
1387
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(de.last_write_time() - now).count()) < 3);
2✔
1388
    ec.clear();
2✔
1389
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(de.last_write_time(ec) - now).count()) < 3);
2✔
1390
    CHECK(!ec);
2✔
1391
#ifndef GHC_OS_WEB
1392
    CHECK(de.hard_link_count() == 1);
2✔
1393
    CHECK(de.hard_link_count(ec) == 1);
2✔
1394
    CHECK(!ec);
2✔
1395
#endif
1396
    CHECK_THROWS_AS(de.replace_filename("bar"), fs::filesystem_error);
4✔
1397
    CHECK_NOTHROW(de.replace_filename("foo"));
2✔
1398
    ec.clear();
2✔
1399
    CHECK_NOTHROW(de.replace_filename("bar", ec));
2✔
1400
    CHECK(ec);
2✔
1401
    auto de2none = fs::directory_entry();
2✔
1402
    ec.clear();
2✔
1403
#ifndef GHC_OS_WEB
1404
    CHECK(de2none.hard_link_count(ec) == static_cast<uintmax_t>(-1));
2✔
1405
    CHECK_THROWS_AS(de2none.hard_link_count(), fs::filesystem_error);
2✔
1406
    CHECK(ec);
2✔
1407
#endif
1408
    ec.clear();
2✔
1409
    CHECK_NOTHROW(de2none.last_write_time(ec));
2✔
1410
    CHECK_THROWS_AS(de2none.last_write_time(), fs::filesystem_error);
2✔
1411
    CHECK(ec);
2✔
1412
    ec.clear();
2✔
1413
    CHECK_THROWS_AS(de2none.file_size(), fs::filesystem_error);
2✔
1414
    CHECK(de2none.file_size(ec) == static_cast<uintmax_t>(-1));
2✔
1415
    CHECK(ec);
2✔
1416
    ec.clear();
2✔
1417
    CHECK(de2none.status().type() == fs::file_type::not_found);
2✔
1418
    CHECK(de2none.status(ec).type() == fs::file_type::not_found);
2✔
1419
    CHECK(ec);
2✔
1420
    generateFile(t.path() / "a");
2✔
1421
    generateFile(t.path() / "b");
2✔
1422
    auto d1 = fs::directory_entry(t.path() / "a");
4✔
1423
    auto d2 = fs::directory_entry(t.path() / "b");
4✔
1424
    CHECK(d1 < d2);
2✔
1425
    CHECK(!(d2 < d1));
2✔
1426
    CHECK(d1 <= d2);
2✔
1427
    CHECK(!(d2 <= d1));
2✔
1428
    CHECK(d2 > d1);
2✔
1429
    CHECK(!(d1 > d2));
2✔
1430
    CHECK(d2 >= d1);
2✔
1431
    CHECK(!(d1 >= d2));
2✔
1432
    CHECK(d1 != d2);
2✔
1433
    CHECK(!(d2 != d2));
2✔
1434
    CHECK(d1 == d1);
2✔
1435
    CHECK(!(d1 == d2));
2✔
1436
    if(is_symlink_creation_supported()) {
2✔
1437
        fs::create_symlink(t.path() / "nonexistent", t.path() / "broken");
2✔
1438
        for (auto d3 : fs::directory_iterator(t.path())) {
12✔
1439
            CHECK_NOTHROW(d3.symlink_status());
8✔
1440
            CHECK_NOTHROW(d3.status());
8✔
1441
            CHECK_NOTHROW(d3.refresh());
8✔
1442
        }
10✔
1443
        fs::directory_entry entry(t.path() / "broken");
4✔
1444
        CHECK_NOTHROW(entry.refresh());
2✔
1445
    }
2✔
1446
}
2✔
1447

1448
TEST_CASE("fs.class.directory_iterator - class directory_iterator", "[filesystem][directory_iterator][fs.class.directory_iterator]")
2✔
1449
{
1450
    {
1451
        TemporaryDirectory t;
2✔
1452
        CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
2✔
1453
        generateFile(t.path() / "test", 1234);
2✔
1454
        REQUIRE(fs::directory_iterator(t.path()) != fs::directory_iterator());
2✔
1455
        auto iter = fs::directory_iterator(t.path());
2✔
1456
        fs::directory_iterator iter2(iter);
2✔
1457
        fs::directory_iterator iter3, iter4;
2✔
1458
        iter3 = iter;
2✔
1459
        CHECK(iter->path().filename() == "test");
2✔
1460
        CHECK(iter2->path().filename() == "test");
2✔
1461
        CHECK(iter3->path().filename() == "test");
2✔
1462
        iter4 = std::move(iter3);
2✔
1463
        CHECK(iter4->path().filename() == "test");
2✔
1464
        CHECK(iter->path() == t.path() / "test");
2✔
1465
        CHECK(!iter->is_symlink());
2✔
1466
        CHECK(iter->is_regular_file());
2✔
1467
        CHECK(!iter->is_directory());
2✔
1468
        CHECK(iter->file_size() == 1234);
2✔
1469
        CHECK(++iter == fs::directory_iterator());
2✔
1470
        CHECK_THROWS_AS(fs::directory_iterator(t.path() / "non-existing"), fs::filesystem_error);
6✔
1471
        int cnt = 0;
2✔
1472
        for(auto de : fs::directory_iterator(t.path())) {
6✔
1473
            ++cnt;
2✔
1474
        }
4✔
1475
        CHECK(cnt == 1);
2✔
1476
    }
2✔
1477
    if (is_symlink_creation_supported()) {
2✔
1478
        TemporaryDirectory t;
2✔
1479
        fs::path td = t.path() / "testdir";
2✔
1480
        CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
2✔
1481
        generateFile(t.path() / "test", 1234);
2✔
1482
        fs::create_directory(td);
2✔
1483
        REQUIRE_NOTHROW(fs::create_symlink(t.path() / "test", td / "testlink"));
2✔
1484
        std::error_code ec;
2✔
1485
        REQUIRE(fs::directory_iterator(td) != fs::directory_iterator());
2✔
1486
        auto iter = fs::directory_iterator(td);
2✔
1487
        CHECK(iter->path().filename() == "testlink");
2✔
1488
        CHECK(iter->path() == td / "testlink");
2✔
1489
        CHECK(iter->is_symlink());
2✔
1490
        CHECK(iter->is_regular_file());
2✔
1491
        CHECK(!iter->is_directory());
2✔
1492
        CHECK(iter->file_size() == 1234);
2✔
1493
        CHECK(++iter == fs::directory_iterator());
2✔
1494
    }
2✔
1495
    {
1496
        // Issue #8: check if resources are freed when iterator reaches end()
1497
        TemporaryDirectory t(TempOpt::change_path);
2✔
1498
        auto p = fs::path("test/");
2✔
1499
        fs::create_directory(p);
2✔
1500
        auto iter = fs::directory_iterator(p);
2✔
1501
        while (iter != fs::directory_iterator()) {
2✔
1502
            ++iter;
×
1503
        }
1504
        CHECK(fs::remove_all(p) == 1);
2✔
1505
        CHECK_NOTHROW(fs::create_directory(p));
2✔
1506
    }
2✔
1507
}
2✔
1508

1509
TEST_CASE("fs.class.rec.dir.itr - class recursive_directory_iterator", "[filesystem][recursive_directory_iterator][fs.class.rec.dir.itr]")
2✔
1510
{
1511
    {
1512
        auto iter = fs::recursive_directory_iterator(".");
2✔
1513
        iter.pop();
2✔
1514
        CHECK(iter == fs::recursive_directory_iterator());
2✔
1515
    }
2✔
1516
    {
1517
        TemporaryDirectory t;
2✔
1518
        CHECK(fs::recursive_directory_iterator(t.path()) == fs::recursive_directory_iterator());
2✔
1519
        generateFile(t.path() / "test", 1234);
2✔
1520
        REQUIRE(fs::recursive_directory_iterator(t.path()) != fs::recursive_directory_iterator());
2✔
1521
        auto iter = fs::recursive_directory_iterator(t.path());
2✔
1522
        CHECK(iter->path().filename() == "test");
2✔
1523
        CHECK(iter->path() == t.path() / "test");
2✔
1524
        CHECK(!iter->is_symlink());
2✔
1525
        CHECK(iter->is_regular_file());
2✔
1526
        CHECK(!iter->is_directory());
2✔
1527
        CHECK(iter->file_size() == 1234);
2✔
1528
        CHECK(++iter == fs::recursive_directory_iterator());
2✔
1529
    }
2✔
1530

1531
    {
1532
        TemporaryDirectory t;
2✔
1533
        fs::path td = t.path() / "testdir";
2✔
1534
        fs::create_directories(td);
2✔
1535
        generateFile(td / "test", 1234);
2✔
1536
        REQUIRE(fs::recursive_directory_iterator(t.path()) != fs::recursive_directory_iterator());
2✔
1537
        auto iter = fs::recursive_directory_iterator(t.path());
2✔
1538

1539
        CHECK(iter->path().filename() == "testdir");
2✔
1540
        CHECK(iter->path() == td);
2✔
1541
        CHECK(!iter->is_symlink());
2✔
1542
        CHECK(!iter->is_regular_file());
2✔
1543
        CHECK(iter->is_directory());
2✔
1544

1545
        CHECK(++iter != fs::recursive_directory_iterator());
2✔
1546

1547
        CHECK(iter->path().filename() == "test");
2✔
1548
        CHECK(iter->path() == td / "test");
2✔
1549
        CHECK(!iter->is_symlink());
2✔
1550
        CHECK(iter->is_regular_file());
2✔
1551
        CHECK(!iter->is_directory());
2✔
1552
        CHECK(iter->file_size() == 1234);
2✔
1553

1554
        CHECK(++iter == fs::recursive_directory_iterator());
2✔
1555
    }
2✔
1556
    {
1557
        TemporaryDirectory t;
2✔
1558
        std::error_code ec;
2✔
1559
        CHECK(fs::recursive_directory_iterator(t.path(), fs::directory_options::none) == fs::recursive_directory_iterator());
2✔
1560
        CHECK(fs::recursive_directory_iterator(t.path(), fs::directory_options::none, ec) == fs::recursive_directory_iterator());
2✔
1561
        CHECK(!ec);
2✔
1562
        CHECK(fs::recursive_directory_iterator(t.path(), ec) == fs::recursive_directory_iterator());
2✔
1563
        CHECK(!ec);
2✔
1564
        generateFile(t.path() / "test");
2✔
1565
        fs::recursive_directory_iterator rd1(t.path());
2✔
1566
        CHECK(fs::recursive_directory_iterator(rd1) != fs::recursive_directory_iterator());
2✔
1567
        fs::recursive_directory_iterator rd2(t.path());
2✔
1568
        CHECK(fs::recursive_directory_iterator(std::move(rd2)) != fs::recursive_directory_iterator());
2✔
1569
        fs::recursive_directory_iterator rd3(t.path(), fs::directory_options::skip_permission_denied);
2✔
1570
        CHECK(rd3.options() == fs::directory_options::skip_permission_denied);
2✔
1571
        fs::recursive_directory_iterator rd4;
2✔
1572
        rd4 = std::move(rd3);
2✔
1573
        CHECK(rd4 != fs::recursive_directory_iterator());
2✔
1574
        CHECK_NOTHROW(++rd4);
2✔
1575
        CHECK(rd4 == fs::recursive_directory_iterator());
2✔
1576
        fs::recursive_directory_iterator rd5;
2✔
1577
        rd5 = rd4;
2✔
1578
    }
2✔
1579
    {
1580
        TemporaryDirectory t(TempOpt::change_path);
2✔
1581
        generateFile("a");
2✔
1582
        fs::create_directory("d1");
2✔
1583
        fs::create_directory("d1/d2");
2✔
1584
        generateFile("d1/b");
2✔
1585
        generateFile("d1/c");
2✔
1586
        generateFile("d1/d2/d");
2✔
1587
        generateFile("e");
2✔
1588
        auto iter = fs::recursive_directory_iterator(".");
2✔
1589
        std::multimap<std::string, int> result;
2✔
1590
        while(iter != fs::recursive_directory_iterator()) {
16✔
1591
            result.insert(std::make_pair(iter->path().generic_string(), iter.depth()));
14✔
1592
            ++iter;
14✔
1593
        }
1594
        std::stringstream os;
2✔
1595
        for(auto p : result) {
16✔
1596
            os << "[" << p.first << "," << p.second << "],";
14✔
1597
        }
14✔
1598
        CHECK(os.str() == "[./a,0],[./d1,0],[./d1/b,1],[./d1/c,1],[./d1/d2,1],[./d1/d2/d,2],[./e,0],");
2✔
1599
    }
2✔
1600
    {
1601
        TemporaryDirectory t(TempOpt::change_path);
2✔
1602
        generateFile("a");
2✔
1603
        fs::create_directory("d1");
2✔
1604
        fs::create_directory("d1/d2");
2✔
1605
        generateFile("d1/b");
2✔
1606
        generateFile("d1/c");
2✔
1607
        generateFile("d1/d2/d");
2✔
1608
        generateFile("e");
2✔
1609
        std::multiset<std::string> result;
2✔
1610
        for(auto de : fs::recursive_directory_iterator(".")) {
18✔
1611
            result.insert(de.path().generic_string());
14✔
1612
        }
16✔
1613
        std::stringstream os;
2✔
1614
        for(auto p : result) {
16✔
1615
            os << p << ",";
14✔
1616
        }
14✔
1617
        CHECK(os.str() == "./a,./d1,./d1/b,./d1/c,./d1/d2,./d1/d2/d,./e,");
2✔
1618
    }
2✔
1619
    {
1620
        TemporaryDirectory t(TempOpt::change_path);
2✔
1621
        generateFile("a");
2✔
1622
        fs::create_directory("d1");
2✔
1623
        fs::create_directory("d1/d2");
2✔
1624
        generateFile("d1/d2/b");
2✔
1625
        generateFile("e");
2✔
1626
        auto iter = fs::recursive_directory_iterator(".");
2✔
1627
        std::multimap<std::string, int> result;
2✔
1628
        while(iter != fs::recursive_directory_iterator()) {
10✔
1629
            result.insert(std::make_pair(iter->path().generic_string(), iter.depth()));
8✔
1630
            if(iter->path() == "./d1/d2") {
8✔
1631
                iter.disable_recursion_pending();
2✔
1632
            }
1633
            ++iter;
8✔
1634
        }
1635
        std::stringstream os;
2✔
1636
        for(auto p : result) {
10✔
1637
            os << "[" << p.first << "," << p.second << "],";
8✔
1638
        }
8✔
1639
        CHECK(os.str() == "[./a,0],[./d1,0],[./d1/d2,1],[./e,0],");
2✔
1640
    }
2✔
1641
    {
1642
        TemporaryDirectory t(TempOpt::change_path);
2✔
1643
        generateFile("a");
2✔
1644
        fs::create_directory("d1");
2✔
1645
        fs::create_directory("d1/d2");
2✔
1646
        generateFile("d1/d2/b");
2✔
1647
        generateFile("e");
2✔
1648
        auto iter = fs::recursive_directory_iterator(".");
2✔
1649
        std::multimap<std::string, int> result;
2✔
1650
        while(iter != fs::recursive_directory_iterator()) {
10✔
1651
            result.insert(std::make_pair(iter->path().generic_string(), iter.depth()));
8✔
1652
            if(iter->path() == "./d1/d2") {
8✔
1653
                iter.pop();
2✔
1654
            }
1655
            else {
1656
                ++iter;
6✔
1657
            }
1658
        }
1659
        std::stringstream os;
2✔
1660
        for(auto p : result) {
10✔
1661
            os << "[" << p.first << "," << p.second << "],";
8✔
1662
        }
8✔
1663
        CHECK(os.str() == "[./a,0],[./d1,0],[./d1/d2,1],[./e,0],");
2✔
1664
    }
2✔
1665
    if (is_symlink_creation_supported()) {
2✔
1666
        TemporaryDirectory t(TempOpt::change_path);
2✔
1667
        fs::create_directory("d1");
2✔
1668
        generateFile("d1/a");
2✔
1669
        fs::create_directory("d2");
2✔
1670
        generateFile("d2/b");
2✔
1671
        fs::create_directory_symlink("../d1", "d2/ds1");
2✔
1672
        fs::create_directory_symlink("d3", "d2/ds2");
2✔
1673
        std::multiset<std::string> result;
2✔
1674
        REQUIRE_NOTHROW([&](){
14✔
1675
            for (const auto& de : fs::recursive_directory_iterator("d2", fs::directory_options::follow_directory_symlink)) {
1676
                result.insert(de.path().generic_string());
1677
            }
1678
        }());
1679
        std::stringstream os;
2✔
1680
        for(const auto& p : result) {
10✔
1681
            os << p << ",";
8✔
1682
        }
1683
        CHECK(os.str() == "d2/b,d2/ds1,d2/ds1/a,d2/ds2,");
2✔
1684
        os.str("");
2✔
1685
        result.clear();
2✔
1686
        REQUIRE_NOTHROW([&](){
12✔
1687
          for (const auto& de : fs::recursive_directory_iterator("d2")) {
1688
              result.insert(de.path().generic_string());
1689
          }
1690
        }());
1691
         for(const auto& p : result) {
8✔
1692
            os << p << ",";
6✔
1693
        }
1694
        CHECK(os.str() == "d2/b,d2/ds1,d2/ds2,");
2✔
1695
    }
2✔
1696
    if (is_symlink_creation_supported()) {
2✔
1697
        TemporaryDirectory t(TempOpt::change_path);
2✔
1698
        generateFile("regular");
2✔
1699
        fs::create_symlink("self", "self");
2✔
1700
        std::multiset<std::string> result;
2✔
1701
        REQUIRE_NOTHROW([&]() {
10✔
1702
            for (const auto& de : fs::recursive_directory_iterator(".")) {
1703
                result.insert(de.path().generic_string());
1704
            }
1705
        }());
1706
        CHECK(result.size() == 2);
2✔
1707
        CHECK(result.count("./regular") == 1);
2✔
1708
        CHECK(result.count("./self") == 1);
2✔
1709
        CHECK(fs::remove("self"));
2✔
1710
    }
2✔
1711
}
2✔
1712

1713
TEST_CASE("fs.op.absolute - absolute", "[filesystem][operations][fs.op.absolute]")
2✔
1714
{
1715
    CHECK(fs::absolute("") == fs::current_path() / "");
2✔
1716
    CHECK(fs::absolute(fs::current_path()) == fs::current_path());
2✔
1717
    CHECK(fs::absolute(".") == fs::current_path() / ".");
2✔
1718
    CHECK((fs::absolute("..") == fs::current_path().parent_path() || fs::absolute("..") == fs::current_path() / ".."));
2✔
1719
    CHECK(fs::absolute("foo") == fs::current_path() / "foo");
2✔
1720
    std::error_code ec;
2✔
1721
    CHECK(fs::absolute("", ec) == fs::current_path() / "");
2✔
1722
    CHECK(!ec);
2✔
1723
    CHECK(fs::absolute("foo", ec) == fs::current_path() / "foo");
2✔
1724
    CHECK(!ec);
2✔
1725
}
2✔
1726

1727
TEST_CASE("fs.op.canonical - canonical", "[filesystem][operations][fs.op.canonical]")
2✔
1728
{
1729
    CHECK_THROWS_AS(fs::canonical(""), fs::filesystem_error);
4✔
1730
    {
1731
        std::error_code ec;
2✔
1732
        CHECK(fs::canonical("", ec) == "");
2✔
1733
        CHECK(ec);
2✔
1734
    }
1735
    CHECK(fs::canonical(fs::current_path()) == fs::current_path());
2✔
1736

1737
    CHECK(fs::canonical(".") == fs::current_path());
2✔
1738
    CHECK(fs::canonical("..") == fs::current_path().parent_path());
2✔
1739
    CHECK(fs::canonical("/") == fs::current_path().root_path());
2✔
1740
    CHECK_THROWS_AS(fs::canonical("foo"), fs::filesystem_error);
4✔
1741
    {
1742
        std::error_code ec;
2✔
1743
        CHECK_NOTHROW(fs::canonical("foo", ec));
2✔
1744
        CHECK(ec);
2✔
1745
    }
1746
    {
1747
        TemporaryDirectory t(TempOpt::change_path);
2✔
1748
        auto dir = t.path() / "d0";
2✔
1749
        fs::create_directories(dir / "d1");
2✔
1750
        generateFile(dir / "f0");
2✔
1751
        fs::path rel(dir.filename());
2✔
1752
        CHECK(fs::canonical(dir) == dir);
2✔
1753
        CHECK(fs::canonical(rel) == dir);
2✔
1754
        CHECK(fs::canonical(dir / "f0") == dir / "f0");
2✔
1755
        CHECK(fs::canonical(rel / "f0") == dir / "f0");
2✔
1756
        CHECK(fs::canonical(rel / "./f0") == dir / "f0");
2✔
1757
        CHECK(fs::canonical(rel / "d1/../f0") == dir / "f0");
2✔
1758
    }
2✔
1759

1760
    if (is_symlink_creation_supported()) {
2✔
1761
        TemporaryDirectory t(TempOpt::change_path);
2✔
1762
        fs::create_directory(t.path() / "dir1");
2✔
1763
        generateFile(t.path() / "dir1/test1");
2✔
1764
        fs::create_directory(t.path() / "dir2");
2✔
1765
        fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym");
2✔
1766
        CHECK(fs::canonical(t.path() / "dir2/dirSym/test1") == t.path() / "dir1/test1");
2✔
1767
    }
2✔
1768
}
2✔
1769

1770
TEST_CASE("fs.op.copy - copy", "[filesystem][operations][fs.op.copy]")
2✔
1771
{
1772
    {
1773
        TemporaryDirectory t(TempOpt::change_path);
2✔
1774
        std::error_code ec;
2✔
1775
        fs::create_directory("dir1");
2✔
1776
        generateFile("dir1/file1");
2✔
1777
        generateFile("dir1/file2");
2✔
1778
        fs::create_directory("dir1/dir2");
2✔
1779
        generateFile("dir1/dir2/file3");
2✔
1780
        CHECK_NOTHROW(fs::copy("dir1", "dir3"));
2✔
1781
        CHECK(fs::exists("dir3/file1"));
2✔
1782
        CHECK(fs::exists("dir3/file2"));
2✔
1783
        CHECK(!fs::exists("dir3/dir2"));
2✔
1784
        CHECK_NOTHROW(fs::copy("dir1", "dir4", fs::copy_options::recursive, ec));
2✔
1785
        CHECK(!ec);
2✔
1786
        CHECK(fs::exists("dir4/file1"));
2✔
1787
        CHECK(fs::exists("dir4/file2"));
2✔
1788
        CHECK(fs::exists("dir4/dir2/file3"));
2✔
1789
        fs::create_directory("dir5");
2✔
1790
        generateFile("dir5/file1");
2✔
1791
        CHECK_THROWS_AS(fs::copy("dir1/file1", "dir5/file1"), fs::filesystem_error);
6✔
1792
        CHECK_NOTHROW(fs::copy("dir1/file1", "dir5/file1", fs::copy_options::skip_existing));
2✔
1793
    }
2✔
1794
    if (is_symlink_creation_supported()) {
2✔
1795
        TemporaryDirectory t(TempOpt::change_path);
2✔
1796
        std::error_code ec;
2✔
1797
        fs::create_directory("dir1");
2✔
1798
        generateFile("dir1/file1");
2✔
1799
        generateFile("dir1/file2");
2✔
1800
        fs::create_directory("dir1/dir2");
2✔
1801
        generateFile("dir1/dir2/file3");
2✔
1802
#ifdef TEST_LWG_2682_BEHAVIOUR
1803
        REQUIRE_THROWS_AS(fs::copy("dir1", "dir3", fs::copy_options::create_symlinks | fs::copy_options::recursive), fs::filesystem_error);
6✔
1804
#else
1805
        REQUIRE_NOTHROW(fs::copy("dir1", "dir3", fs::copy_options::create_symlinks | fs::copy_options::recursive));
1806
        CHECK(!ec);
1807
        CHECK(fs::exists("dir3/file1"));
1808
        CHECK(fs::is_symlink("dir3/file1"));
1809
        CHECK(fs::exists("dir3/file2"));
1810
        CHECK(fs::is_symlink("dir3/file2"));
1811
        CHECK(fs::exists("dir3/dir2/file3"));
1812
        CHECK(fs::is_symlink("dir3/dir2/file3"));
1813
#endif
1814
    }
2✔
1815
#ifndef GHC_OS_WEB
1816
    {
1817
        TemporaryDirectory t(TempOpt::change_path);
2✔
1818
        std::error_code ec;
2✔
1819
        fs::create_directory("dir1");
2✔
1820
        generateFile("dir1/file1");
2✔
1821
        generateFile("dir1/file2");
2✔
1822
        fs::create_directory("dir1/dir2");
2✔
1823
        generateFile("dir1/dir2/file3");
2✔
1824
        auto f1hl = fs::hard_link_count("dir1/file1");
2✔
1825
        auto f2hl = fs::hard_link_count("dir1/file2");
2✔
1826
        auto f3hl = fs::hard_link_count("dir1/dir2/file3");
2✔
1827
        CHECK_NOTHROW(fs::copy("dir1", "dir3", fs::copy_options::create_hard_links | fs::copy_options::recursive, ec));
2✔
1828
        REQUIRE(!ec);
2✔
1829
        CHECK(fs::exists("dir3/file1"));
2✔
1830
        CHECK(fs::hard_link_count("dir1/file1") == f1hl + 1);
2✔
1831
        CHECK(fs::exists("dir3/file2"));
2✔
1832
        CHECK(fs::hard_link_count("dir1/file2") == f2hl + 1);
2✔
1833
        CHECK(fs::exists("dir3/dir2/file3"));
2✔
1834
        CHECK(fs::hard_link_count("dir1/dir2/file3") == f3hl + 1);
2✔
1835
    }
2✔
1836
#endif
1837
}
2✔
1838

1839
TEST_CASE("fs.op.copy_file - copy_file", "[filesystem][operations][fs.op.copy_file]")
2✔
1840
{
1841
    TemporaryDirectory t(TempOpt::change_path);
2✔
1842
    std::error_code ec;
2✔
1843
    generateFile("foo", 100);
2✔
1844
    CHECK(!fs::exists("bar"));
2✔
1845
    CHECK(fs::copy_file("foo", "bar"));
2✔
1846
    CHECK(fs::exists("bar"));
2✔
1847
    CHECK(fs::file_size("foo") == fs::file_size("bar"));
2✔
1848
    CHECK(fs::copy_file("foo", "bar2", ec));
2✔
1849
    CHECK(!ec);
2✔
1850
    generateFile("foo2", 200);
2✔
1851
    const auto copyTime = fs::file_time_type(std::chrono::seconds(1445401800));
2✔
1852
    fs::last_write_time("foo", copyTime);
2✔
1853
    fs::last_write_time("bar", copyTime + std::chrono::milliseconds(100));
2✔
1854
    fs::last_write_time("foo2", copyTime + std::chrono::milliseconds(200));
2✔
1855
    CHECK(fs::copy_file("foo2", "bar", fs::copy_options::update_existing));
2✔
1856
    CHECK(fs::file_size("bar") == 200);
2✔
1857
    CHECK(!fs::copy_file("foo", "bar", fs::copy_options::update_existing));
2✔
1858
    CHECK(fs::file_size("bar") == 200);
2✔
1859
    CHECK(fs::copy_file("foo", "bar", fs::copy_options::overwrite_existing));
2✔
1860
    CHECK(fs::file_size("bar") == 100);
2✔
1861
    CHECK_THROWS_AS(fs::copy_file("foobar", "foobar2"), fs::filesystem_error);
6✔
1862
    CHECK_NOTHROW(fs::copy_file("foobar", "foobar2", ec));
2✔
1863
    CHECK(ec);
2✔
1864
    CHECK(!fs::exists("foobar"));
2✔
1865
    CHECK(!fs::exists("foobar2"));
2✔
1866

1867
    fs::create_directory("source_dir");
2✔
1868
    CHECK_FALSE(fs::copy_file("source_dir", "dir_copy", ec));
2✔
1869
    CHECK(ec);
2✔
1870
    CHECK_FALSE(fs::exists("dir_copy"));
2✔
1871
    CHECK_THROWS_AS(fs::copy_file("source_dir", "dir_copy"), fs::filesystem_error);
6✔
1872
    CHECK_FALSE(fs::exists("dir_copy"));
2✔
1873

1874
#if !defined(GHC_OS_WINDOWS) && !defined(GHC_OS_WEB)
1875
    REQUIRE(::mkfifo("source_fifo", 0644) == 0);
2✔
1876
    CHECK_FALSE(fs::copy_file("source_fifo", "fifo_copy", ec));
2✔
1877
    CHECK(ec);
2✔
1878
    CHECK_FALSE(fs::exists("fifo_copy"));
2✔
1879
    CHECK_THROWS_AS(fs::copy_file("source_fifo", "fifo_copy"), fs::filesystem_error);
6✔
1880
    CHECK_FALSE(fs::exists("fifo_copy"));
2✔
1881
#endif
1882

1883
    fs::path file1("temp1.txt");
2✔
1884
    fs::path file2("temp2.txt");
2✔
1885
    generateFile(file1, 200);
2✔
1886
    generateFile(file2, 200);
2✔
1887
    auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write;
2✔
1888
    CHECK_NOTHROW(fs::permissions(file1, allWrite, fs::perm_options::remove));
2✔
1889
    CHECK((fs::status(file1).permissions() & fs::perms::owner_write) != fs::perms::owner_write);
2✔
1890
    CHECK_NOTHROW(fs::permissions(file2, allWrite, fs::perm_options::add));
2✔
1891
    CHECK((fs::status(file2).permissions() & fs::perms::owner_write) == fs::perms::owner_write);
2✔
1892
    fs::copy_file(file1, file2, fs::copy_options::overwrite_existing);
2✔
1893
    CHECK((fs::status(file2).permissions() & fs::perms::owner_write) != fs::perms::owner_write);
2✔
1894
    CHECK_NOTHROW(fs::permissions(file1, allWrite, fs::perm_options::add));
2✔
1895
    CHECK_NOTHROW(fs::permissions(file2, allWrite, fs::perm_options::add));
2✔
1896

1897
    ec = std::make_error_code(std::errc::invalid_argument);
2✔
1898
    CHECK(fs::copy_file("foo", "bar3", ec));
2✔
1899
    CHECK(!ec);
2✔
1900
}
2✔
1901

1902
TEST_CASE("fs.op.copy_symlink - copy_symlink", "[filesystem][operations][fs.op.copy_symlink]")
2✔
1903
{
1904
    TemporaryDirectory t(TempOpt::change_path);
2✔
1905
    std::error_code ec;
2✔
1906
    generateFile("foo");
2✔
1907
    fs::create_directory("dir");
2✔
1908
    if (is_symlink_creation_supported()) {
2✔
1909
        fs::create_symlink("foo", "sfoo");
2✔
1910
        fs::create_directory_symlink("dir", "sdir");
2✔
1911
        CHECK_NOTHROW(fs::copy_symlink("sfoo", "sfooc"));
2✔
1912
        CHECK(fs::exists("sfooc"));
2✔
1913
        CHECK_NOTHROW(fs::copy_symlink("sfoo", "sfooc2", ec));
2✔
1914
        CHECK(fs::exists("sfooc2"));
2✔
1915
        CHECK(!ec);
2✔
1916
        CHECK_NOTHROW(fs::copy_symlink("sdir", "sdirc"));
2✔
1917
        CHECK(fs::exists("sdirc"));
2✔
1918
        CHECK_NOTHROW(fs::copy_symlink("sdir", "sdirc2", ec));
2✔
1919
        CHECK(fs::exists("sdirc2"));
2✔
1920
        CHECK(!ec);
2✔
1921
    }
1922
    CHECK_THROWS_AS(fs::copy_symlink("bar", "barc"), fs::filesystem_error);
6✔
1923
    CHECK_NOTHROW(fs::copy_symlink("bar", "barc", ec));
2✔
1924
    CHECK(ec);
2✔
1925
}
2✔
1926

1927
TEST_CASE("fs.op.create_directories - create_directories", "[filesystem][operations][fs.op.create_directories]")
2✔
1928
{
1929
    TemporaryDirectory t;
2✔
1930
    fs::path p = t.path() / "testdir";
2✔
1931
    fs::path p2 = p / "nested";
2✔
1932
    REQUIRE(!fs::exists(p));
2✔
1933
    REQUIRE(!fs::exists(p2));
2✔
1934
    CHECK(fs::create_directories(p2));
2✔
1935
    CHECK(fs::is_directory(p));
2✔
1936
    CHECK(fs::is_directory(p2));
2✔
1937
    CHECK(!fs::create_directories(p2));
2✔
1938
#ifdef TEST_LWG_2935_BEHAVIOUR
1939
    INFO("This test expects LWG #2935 result conformance.");
1940
    p = t.path() / "testfile";
1941
    generateFile(p);
1942
    CHECK(fs::is_regular_file(p));
1943
    CHECK(!fs::is_directory(p));
1944
    bool created = false;
1945
    CHECK_NOTHROW((created = fs::create_directories(p)));
1946
    CHECK(!created);
1947
    CHECK(fs::is_regular_file(p));
1948
    CHECK(!fs::is_directory(p));
1949
    std::error_code ec;
1950
    CHECK_NOTHROW((created = fs::create_directories(p, ec)));
1951
    CHECK(!created);
1952
    CHECK(!ec);
1953
    CHECK(fs::is_regular_file(p));
1954
    CHECK(!fs::is_directory(p));
1955
    CHECK(!fs::create_directories(p, ec));
1956
#else
1957
    INFO("This test expects conformance with P1164R1. (implemented by GCC with issue #86910.)");
2✔
1958
    p = t.path() / "testfile";
2✔
1959
    generateFile(p);
2✔
1960
    CHECK(fs::is_regular_file(p));
2✔
1961
    CHECK(!fs::is_directory(p));
2✔
1962
    CHECK_THROWS_AS(fs::create_directories(p), fs::filesystem_error);
2✔
1963
    CHECK(fs::is_regular_file(p));
2✔
1964
    CHECK(!fs::is_directory(p));
2✔
1965
    std::error_code ec;
2✔
1966
    CHECK_NOTHROW(fs::create_directories(p, ec));
2✔
1967
    CHECK(ec);
2✔
1968
    CHECK(fs::is_regular_file(p));
2✔
1969
    CHECK(!fs::is_directory(p));
2✔
1970
    CHECK(!fs::create_directories(p, ec));
2✔
1971
#endif
1972
}
2✔
1973

1974
TEST_CASE("fs.op.create_directory - create_directory", "[filesystem][operations][fs.op.create_directory]")
2✔
1975
{
1976
    TemporaryDirectory t;
2✔
1977
    fs::path p = t.path() / "testdir";
2✔
1978
    REQUIRE(!fs::exists(p));
2✔
1979
    CHECK(fs::create_directory(p));
2✔
1980
    CHECK(fs::is_directory(p));
2✔
1981
    CHECK(!fs::is_regular_file(p));
2✔
1982
    CHECK(fs::create_directory(p / "nested", p));
2✔
1983
    CHECK(fs::is_directory(p / "nested"));
2✔
1984
    CHECK(!fs::is_regular_file(p / "nested"));
2✔
1985
#ifdef TEST_LWG_2935_BEHAVIOUR
1986
    INFO("This test expects LWG #2935 result conformance.");
1987
    p = t.path() / "testfile";
1988
    generateFile(p);
1989
    CHECK(fs::is_regular_file(p));
1990
    CHECK(!fs::is_directory(p));
1991
    bool created = false;
1992
    CHECK_NOTHROW((created = fs::create_directory(p)));
1993
    CHECK(!created);
1994
    CHECK(fs::is_regular_file(p));
1995
    CHECK(!fs::is_directory(p));
1996
    std::error_code ec;
1997
    CHECK_NOTHROW((created = fs::create_directory(p, ec)));
1998
    CHECK(!created);
1999
    CHECK(!ec);
2000
    CHECK(fs::is_regular_file(p));
2001
    CHECK(!fs::is_directory(p));
2002
    CHECK(!fs::create_directories(p, ec));
2003
#else
2004
    INFO("This test expects conformance with P1164R1. (implemented by GCC with issue #86910.)");
2✔
2005
    p = t.path() / "testfile";
2✔
2006
    generateFile(p);
2✔
2007
    CHECK(fs::is_regular_file(p));
2✔
2008
    CHECK(!fs::is_directory(p));
2✔
2009
    REQUIRE_THROWS_AS(fs::create_directory(p), fs::filesystem_error);
2✔
2010
    CHECK(fs::is_regular_file(p));
2✔
2011
    CHECK(!fs::is_directory(p));
2✔
2012
    std::error_code ec;
2✔
2013
    REQUIRE_NOTHROW(fs::create_directory(p, ec));
2✔
2014
    CHECK(ec);
2✔
2015
    CHECK(fs::is_regular_file(p));
2✔
2016
    CHECK(!fs::is_directory(p));
2✔
2017
    CHECK(!fs::create_directory(p, ec));
2✔
2018
#endif
2019
}
2✔
2020

2021
TEST_CASE("fs.op.create_directory_symlink - create_directory_symlink", "[filesystem][operations][fs.op.create_directory_symlink]")
2✔
2022
{
2023
    if (is_symlink_creation_supported()) {
2✔
2024
        TemporaryDirectory t;
2✔
2025
        fs::create_directory(t.path() / "dir1");
2✔
2026
        generateFile(t.path() / "dir1/test1");
2✔
2027
        fs::create_directory(t.path() / "dir2");
2✔
2028
        fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym");
2✔
2029
        CHECK(fs::exists(t.path() / "dir2/dirSym"));
2✔
2030
        CHECK(fs::is_symlink(t.path() / "dir2/dirSym"));
2✔
2031
        CHECK(fs::exists(t.path() / "dir2/dirSym/test1"));
2✔
2032
        CHECK(fs::is_regular_file(t.path() / "dir2/dirSym/test1"));
2✔
2033
        CHECK_THROWS_AS(fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym"), fs::filesystem_error);
10✔
2034
        std::error_code ec;
2✔
2035
        CHECK_NOTHROW(fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym", ec));
2✔
2036
        CHECK(ec);
2✔
2037
    }
2✔
2038
}
2✔
2039

2040
TEST_CASE("fs.op.create_hard_link - create_hard_link", "[filesystem][operations][fs.op.create_hard_link]")
2✔
2041
{
2042
#ifndef GHC_OS_WEB
2043
    TemporaryDirectory t(TempOpt::change_path);
2✔
2044
    std::error_code ec;
2✔
2045
    generateFile("foo", 1234);
2✔
2046
    CHECK_NOTHROW(fs::create_hard_link("foo", "bar"));
2✔
2047
    CHECK(fs::exists("bar"));
2✔
2048
    CHECK(!fs::is_symlink("bar"));
2✔
2049
    CHECK_NOTHROW(fs::create_hard_link("foo", "bar2", ec));
2✔
2050
    CHECK(fs::exists("bar2"));
2✔
2051
    CHECK(!fs::is_symlink("bar2"));
2✔
2052
    CHECK(!ec);
2✔
2053
    CHECK_THROWS_AS(fs::create_hard_link("nofoo", "bar"), fs::filesystem_error);
6✔
2054
    CHECK_NOTHROW(fs::create_hard_link("nofoo", "bar", ec));
2✔
2055
    CHECK(ec);
2✔
2056
#endif
2057
}
2✔
2058

2059
TEST_CASE("fs.op.create_symlink - create_symlink", "[filesystem][operations][fs.op.create_symlink]")
2✔
2060
{
2061
    if (is_symlink_creation_supported()) {
2✔
2062
        TemporaryDirectory t;
2✔
2063
        fs::create_directory(t.path() / "dir1");
2✔
2064
        generateFile(t.path() / "dir1/test1");
2✔
2065
        fs::create_directory(t.path() / "dir2");
2✔
2066
        fs::create_symlink(t.path() / "dir1/test1", t.path() / "dir2/fileSym");
2✔
2067
        CHECK(fs::exists(t.path() / "dir2/fileSym"));
2✔
2068
        CHECK(fs::is_symlink(t.path() / "dir2/fileSym"));
2✔
2069
        CHECK(fs::exists(t.path() / "dir2/fileSym"));
2✔
2070
        CHECK(fs::is_regular_file(t.path() / "dir2/fileSym"));
2✔
2071
        CHECK_THROWS_AS(fs::create_symlink(t.path() / "dir1", t.path() / "dir2/fileSym"), fs::filesystem_error);
10✔
2072
        std::error_code ec;
2✔
2073
        CHECK_NOTHROW(fs::create_symlink(t.path() / "dir1", t.path() / "dir2/fileSym", ec));
2✔
2074
        CHECK(ec);
2✔
2075
    }
2✔
2076
}
2✔
2077

2078
TEST_CASE("fs.op.current_path - current_path", "[filesystem][operations][fs.op.current_path]")
2✔
2079
{
2080
    TemporaryDirectory t;
2✔
2081
    std::error_code ec;
2✔
2082
    fs::path p1 = fs::current_path();
2✔
2083
    CHECK_NOTHROW(fs::current_path(t.path()));
2✔
2084
    CHECK(p1 != fs::current_path());
2✔
2085
    CHECK_NOTHROW(fs::current_path(p1, ec));
2✔
2086
    CHECK(!ec);
2✔
2087
    CHECK_THROWS_AS(fs::current_path(t.path() / "foo"), fs::filesystem_error);
6✔
2088
    CHECK(p1 == fs::current_path());
2✔
2089
    CHECK_NOTHROW(fs::current_path(t.path() / "foo", ec));
2✔
2090
    CHECK(ec);
2✔
2091
}
2✔
2092

2093
TEST_CASE("fs.op.equivalent - equivalent", "[filesystem][operations][fs.op.equivalent]")
2✔
2094
{
2095
    TemporaryDirectory t(TempOpt::change_path);
2✔
2096
    generateFile("foo", 1234);
2✔
2097
    CHECK(fs::equivalent(t.path() / "foo", "foo"));
2✔
2098
    if (is_symlink_creation_supported()) {
2✔
2099
        std::error_code ec(42, std::system_category());
2✔
2100
        fs::create_symlink("foo", "foo2");
2✔
2101
        CHECK(fs::equivalent("foo", "foo2"));
2✔
2102
        CHECK(fs::equivalent("foo", "foo2", ec));
2✔
2103
        CHECK(!ec);
2✔
2104
    }
2105
#ifdef TEST_LWG_2937_BEHAVIOUR
2106
    INFO("This test expects LWG #2937 result conformance.");
2✔
2107
    std::error_code ec;
2✔
2108
    bool result = false;
2✔
2109
    REQUIRE_THROWS_AS(fs::equivalent("foo", "foo3"), fs::filesystem_error);
6✔
2110
    CHECK_NOTHROW(result = fs::equivalent("foo", "foo3", ec));
2✔
2111
    CHECK(!result);
2✔
2112
    CHECK(ec);
2✔
2113
    ec.clear();
2✔
2114
    CHECK_THROWS_AS(fs::equivalent("foo3", "foo"), fs::filesystem_error);
6✔
2115
    CHECK_NOTHROW(result = fs::equivalent("foo3", "foo", ec));
2✔
2116
    CHECK(!result);
2✔
2117
    CHECK(ec);
2✔
2118
    ec.clear();
2✔
2119
    CHECK_THROWS_AS(fs::equivalent("foo3", "foo4"), fs::filesystem_error);
6✔
2120
    CHECK_NOTHROW(result = fs::equivalent("foo3", "foo4", ec));
2✔
2121
    CHECK(!result);
2✔
2122
    CHECK(ec);
2✔
2123
#else
2124
    INFO("This test expects conformance predating LWG #2937 result.");
2125
    std::error_code ec;
2126
    bool result = false;
2127
    REQUIRE_NOTHROW(result = fs::equivalent("foo", "foo3"));
2128
    CHECK(!result);
2129
    CHECK_NOTHROW(result = fs::equivalent("foo", "foo3", ec));
2130
    CHECK(!result);
2131
    CHECK(!ec);
2132
    ec.clear();
2133
    CHECK_NOTHROW(result = fs::equivalent("foo3", "foo"));
2134
    CHECK(!result);
2135
    CHECK_NOTHROW(result = fs::equivalent("foo3", "foo", ec));
2136
    CHECK(!result);
2137
    CHECK(!ec);
2138
    ec.clear();
2139
    CHECK_THROWS_AS(result = fs::equivalent("foo4", "foo3"), fs::filesystem_error);
2140
    CHECK(!result);
2141
    CHECK_NOTHROW(result = fs::equivalent("foo4", "foo3", ec));
2142
    CHECK(!result);
2143
    CHECK(ec);
2144
#endif
2145
}
2✔
2146

2147
TEST_CASE("fs.op.exists - exists", "[filesystem][operations][fs.op.exists]")
2✔
2148
{
2149
    TemporaryDirectory t(TempOpt::change_path);
2✔
2150
    std::error_code ec;
2✔
2151
    CHECK(!fs::exists(""));
2✔
2152
    CHECK(!fs::exists("foo"));
2✔
2153
    CHECK(!fs::exists("foo", ec));
2✔
2154
    CHECK(!ec);
2✔
2155
    ec = std::error_code(42, std::system_category());
2✔
2156
    CHECK(!fs::exists("foo", ec));
2✔
2157
#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API)
2158
    CHECK(!fs::exists(u8"foo"));
1✔
2159
#endif
2160
    CHECK(!ec);
2✔
2161
    ec.clear();
2✔
2162
    CHECK(fs::exists(t.path()));
2✔
2163
    CHECK(fs::exists(t.path(), ec));
2✔
2164
    CHECK(!ec);
2✔
2165
    ec = std::error_code(42, std::system_category());
2✔
2166
    CHECK(fs::exists(t.path(), ec));
2✔
2167
    CHECK(!ec);
2✔
2168
#if defined(GHC_OS_WINDOWS) && !defined(GHC_FILESYSTEM_FWD)
2169
    if (::GetFileAttributesW(L"C:\\fs-test") != INVALID_FILE_ATTRIBUTES) {
2170
        CHECK(fs::exists("C:\\fs-test"));    
2171
    }
2172
#endif
2173
}
2✔
2174

2175
TEST_CASE("fs.op.file_size - file_size", "[filesystem][operations][fs.op.file_size]")
2✔
2176
{
2177
    TemporaryDirectory t(TempOpt::change_path);
2✔
2178
    std::error_code ec;
2✔
2179
    generateFile("foo", 0);
2✔
2180
    generateFile("bar", 1234);
2✔
2181
    CHECK(fs::file_size("foo") == 0);
2✔
2182
    ec = std::error_code(42, std::system_category());
2✔
2183
    CHECK(fs::file_size("foo", ec) == 0);
2✔
2184
    CHECK(!ec);
2✔
2185
    ec.clear();
2✔
2186
    CHECK(fs::file_size("bar") == 1234);
2✔
2187
    ec = std::error_code(42, std::system_category());
2✔
2188
    CHECK(fs::file_size("bar", ec) == 1234);
2✔
2189
    CHECK(!ec);
2✔
2190
    ec.clear();
2✔
2191
    CHECK_THROWS_AS(fs::file_size("foobar"), fs::filesystem_error);
4✔
2192
    CHECK(fs::file_size("foobar", ec) == static_cast<uintmax_t>(-1));
2✔
2193
    CHECK(ec);
2✔
2194
    ec.clear();
2✔
2195
}
2✔
2196

2197
#ifndef GHC_OS_WINDOWS
2198
static uintmax_t getHardlinkCount(const fs::path& p)
4✔
2199
{
2200
    struct stat st = {};
4✔
2201
    auto rc = ::lstat(p.c_str(), &st);
4✔
2202
    return rc == 0 ? st.st_nlink : ~0u;
4✔
2203
}
2204
#endif
2205

2206
TEST_CASE("fs.op.hard_link_count - hard_link_count", "[filesystem][operations][fs.op.hard_link_count]")
2✔
2207
{
2208
#ifndef GHC_OS_WEB
2209
    TemporaryDirectory t(TempOpt::change_path);
2✔
2210
    std::error_code ec;
2✔
2211
#ifdef GHC_OS_WINDOWS
2212
    // windows doesn't implement "."/".." as hardlinks, so it
2213
    // starts with 1 and subdirectories don't change the count
2214
    CHECK(fs::hard_link_count(t.path()) == 1);
2215
    fs::create_directory("dir");
2216
    CHECK(fs::hard_link_count(t.path()) == 1);
2217
#else
2218
    // unix/bsd/linux typically implements "."/".." as hardlinks
2219
    // so an empty dir has 2 (from parent and the ".") and
2220
    // adding a subdirectory adds one due to its ".."
2221
    CHECK(fs::hard_link_count(t.path()) == getHardlinkCount(t.path()));
2✔
2222
    fs::create_directory("dir");
2✔
2223
    CHECK(fs::hard_link_count(t.path()) == getHardlinkCount(t.path()));
2✔
2224
#endif
2225
    generateFile("foo");
2✔
2226
    CHECK(fs::hard_link_count(t.path() / "foo") == 1);
2✔
2227
    ec = std::error_code(42, std::system_category());
2✔
2228
    CHECK(fs::hard_link_count(t.path() / "foo", ec) == 1);
2✔
2229
    CHECK(!ec);
2✔
2230
    CHECK_THROWS_AS(fs::hard_link_count(t.path() / "bar"), fs::filesystem_error);
6✔
2231
    CHECK_NOTHROW(fs::hard_link_count(t.path() / "bar", ec));
2✔
2232
    CHECK(ec);
2✔
2233
    ec.clear();
2✔
2234
#else
2235
    WARN("Test for unsupportet features are disabled on JS/Wasm target.");
2236
#endif
2237
}
2✔
2238

2239
class FileTypeMixFixture
2240
{
2241
public:
2242
    FileTypeMixFixture()
16✔
2243
        : _t(TempOpt::change_path)
16✔
2244
        , _hasFifo(false)
16✔
2245
        , _hasSocket(false)
16✔
2246
    {
2247
        generateFile("regular");
16✔
2248
        fs::create_directory("directory");
16✔
2249
        if (is_symlink_creation_supported()) {
16✔
2250
            fs::create_symlink("regular", "file_symlink");
16✔
2251
            fs::create_directory_symlink("directory", "dir_symlink");
16✔
2252
        }
2253
#if !defined(GHC_OS_WINDOWS) && !defined(GHC_OS_WEB)
2254
        REQUIRE(::mkfifo("fifo", 0644) == 0);
16✔
2255
        _hasFifo = true;
16✔
2256
        struct ::sockaddr_un addr;
2257
        addr.sun_family = AF_UNIX;
16✔
2258
        std::strncpy(addr.sun_path, "socket", sizeof(addr.sun_path));
16✔
2259
        int fd = socket(PF_UNIX, SOCK_STREAM, 0);
16✔
2260
        bind(fd, (struct sockaddr*)&addr, sizeof addr);
16✔
2261
        _hasSocket = true;
16✔
2262
#endif
2263
    }
16✔
2264

2265
    ~FileTypeMixFixture() {}
16✔
2266

2267
    bool has_fifo() const { return _hasFifo; }
16✔
2268

2269
    bool has_socket() const { return _hasSocket; }
16✔
2270

2271
    fs::path block_path() const
16✔
2272
    {
2273
        std::error_code ec;
16✔
2274
        if (fs::exists("/dev/sda", ec)) {
16✔
UNCOV
2275
            return "/dev/sda";
×
2276
        }
2277
        else if (fs::exists("/dev/disk0", ec)) {
16✔
2278
            return "/dev/disk0";
×
2279
        }
2280
        return fs::path();
16✔
2281
    }
2282

2283
    fs::path character_path() const
32✔
2284
    {
2285
#ifndef GHC_OS_SOLARIS
2286
        std::error_code ec;
32✔
2287
        if (fs::exists("/dev/null", ec)) {
32✔
2288
            return "/dev/null";
32✔
2289
        }
2290
        else if (fs::exists("NUL", ec)) {
×
2291
            return "NUL";
×
2292
        }
2293
#endif
2294
        return fs::path();
×
2295
    }
2296
    fs::path temp_path() const { return _t.path(); }
2297

2298
private:
2299
    TemporaryDirectory _t;
2300
    bool _hasFifo;
2301
    bool _hasSocket;
2302
};
2303

2304
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_block_file - is_block_file", "[filesystem][operations][fs.op.is_block_file]")
2✔
2305
{
2306
    std::error_code ec;
2✔
2307
    CHECK(!fs::is_block_file("directory"));
2✔
2308
    CHECK(!fs::is_block_file("regular"));
2✔
2309
    if (is_symlink_creation_supported()) {
2✔
2310
        CHECK(!fs::is_block_file("dir_symlink"));
2✔
2311
        CHECK(!fs::is_block_file("file_symlink"));
2✔
2312
    }
2313
    CHECK((has_fifo() ? !fs::is_block_file("fifo") : true));
2✔
2314
    CHECK((has_socket() ? !fs::is_block_file("socket") : true));
2✔
2315
    CHECK((block_path().empty() ? true : fs::is_block_file(block_path())));
2✔
2316
    CHECK((character_path().empty() ? true : !fs::is_block_file(character_path())));
2✔
2317
    CHECK_NOTHROW(fs::is_block_file("notfound"));
2✔
2318
    CHECK_NOTHROW(fs::is_block_file("notfound", ec));
2✔
2319
    CHECK(ec);
2✔
2320
    ec.clear();
2✔
2321
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::none)));
2✔
2322
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::not_found)));
2✔
2323
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::regular)));
2✔
2324
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::directory)));
2✔
2325
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::symlink)));
2✔
2326
    CHECK(fs::is_block_file(fs::file_status(fs::file_type::block)));
2✔
2327
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::character)));
2✔
2328
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::fifo)));
2✔
2329
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::socket)));
2✔
2330
    CHECK(!fs::is_block_file(fs::file_status(fs::file_type::unknown)));
2✔
2331
}
2✔
2332

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

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

2391
TEST_CASE("fs.op.is_empty - is_empty", "[filesystem][operations][fs.op.is_empty]")
2✔
2392
{
2393
    TemporaryDirectory t(TempOpt::change_path);
2✔
2394
    std::error_code ec;
2✔
2395
    CHECK(fs::is_empty(t.path()));
2✔
2396
    CHECK(fs::is_empty(t.path(), ec));
2✔
2397
    CHECK(!ec);
2✔
2398
    generateFile("foo", 0);
2✔
2399
    generateFile("bar", 1234);
2✔
2400
    CHECK(fs::is_empty("foo"));
2✔
2401
    CHECK(fs::is_empty("foo", ec));
2✔
2402
    CHECK(!ec);
2✔
2403
    CHECK(!fs::is_empty("bar"));
2✔
2404
    CHECK(!fs::is_empty("bar", ec));
2✔
2405
    CHECK(!ec);
2✔
2406
    CHECK_THROWS_AS(fs::is_empty("foobar"), fs::filesystem_error);
4✔
2407
    bool result = false;
2✔
2408
    CHECK_NOTHROW(result = fs::is_empty("foobar", ec));
2✔
2409
    CHECK(!result);
2✔
2410
    CHECK(ec);
2✔
2411
}
2✔
2412

2413
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_fifo - is_fifo", "[filesystem][operations][fs.op.is_fifo]")
2✔
2414
{
2415
    std::error_code ec;
2✔
2416
    CHECK(!fs::is_fifo("directory"));
2✔
2417
    CHECK(!fs::is_fifo("regular"));
2✔
2418
    if (is_symlink_creation_supported()) {
2✔
2419
        CHECK(!fs::is_fifo("dir_symlink"));
2✔
2420
        CHECK(!fs::is_fifo("file_symlink"));
2✔
2421
    }
2422
    CHECK((has_fifo() ? fs::is_fifo("fifo") : true));
2✔
2423
    CHECK((has_socket() ? !fs::is_fifo("socket") : true));
2✔
2424
    CHECK((block_path().empty() ? true : !fs::is_fifo(block_path())));
2✔
2425
    CHECK((character_path().empty() ? true : !fs::is_fifo(character_path())));
2✔
2426
    CHECK_NOTHROW(fs::is_fifo("notfound"));
2✔
2427
    CHECK_NOTHROW(fs::is_fifo("notfound", ec));
2✔
2428
    CHECK(ec);
2✔
2429
    ec.clear();
2✔
2430
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::none)));
2✔
2431
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::not_found)));
2✔
2432
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::regular)));
2✔
2433
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::directory)));
2✔
2434
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::symlink)));
2✔
2435
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::block)));
2✔
2436
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::character)));
2✔
2437
    CHECK(fs::is_fifo(fs::file_status(fs::file_type::fifo)));
2✔
2438
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::socket)));
2✔
2439
    CHECK(!fs::is_fifo(fs::file_status(fs::file_type::unknown)));
2✔
2440
}
2✔
2441

2442
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_other - is_other", "[filesystem][operations][fs.op.is_other]")
2✔
2443
{
2444
    std::error_code ec;
2✔
2445
    CHECK(!fs::is_other("directory"));
2✔
2446
    CHECK(!fs::is_other("regular"));
2✔
2447
    if (is_symlink_creation_supported()) {
2✔
2448
        CHECK(!fs::is_other("dir_symlink"));
2✔
2449
        CHECK(!fs::is_other("file_symlink"));
2✔
2450
    }
2451
    CHECK((has_fifo() ? fs::is_other("fifo") : true));
2✔
2452
    CHECK((has_socket() ? fs::is_other("socket") : true));
2✔
2453
    CHECK((block_path().empty() ? true : fs::is_other(block_path())));
2✔
2454
    CHECK((character_path().empty() ? true : fs::is_other(character_path())));
2✔
2455
    CHECK_NOTHROW(fs::is_other("notfound"));
2✔
2456
    CHECK_NOTHROW(fs::is_other("notfound", ec));
2✔
2457
    CHECK(ec);
2✔
2458
    ec.clear();
2✔
2459
    CHECK(!fs::is_other(fs::file_status(fs::file_type::none)));
2✔
2460
    CHECK(!fs::is_other(fs::file_status(fs::file_type::not_found)));
2✔
2461
    CHECK(!fs::is_other(fs::file_status(fs::file_type::regular)));
2✔
2462
    CHECK(!fs::is_other(fs::file_status(fs::file_type::directory)));
2✔
2463
    CHECK(!fs::is_other(fs::file_status(fs::file_type::symlink)));
2✔
2464
    CHECK(fs::is_other(fs::file_status(fs::file_type::block)));
2✔
2465
    CHECK(fs::is_other(fs::file_status(fs::file_type::character)));
2✔
2466
    CHECK(fs::is_other(fs::file_status(fs::file_type::fifo)));
2✔
2467
    CHECK(fs::is_other(fs::file_status(fs::file_type::socket)));
2✔
2468
    CHECK(fs::is_other(fs::file_status(fs::file_type::unknown)));
2✔
2469
}
2✔
2470

2471
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_regular_file - is_regular_file", "[filesystem][operations][fs.op.is_regular_file]")
2✔
2472
{
2473
    std::error_code ec;
2✔
2474
    CHECK(!fs::is_regular_file("directory"));
2✔
2475
    CHECK(fs::is_regular_file("regular"));
2✔
2476
    if (is_symlink_creation_supported()) {
2✔
2477
        CHECK(!fs::is_regular_file("dir_symlink"));
2✔
2478
        CHECK(fs::is_regular_file("file_symlink"));
2✔
2479
    }
2480
    CHECK((has_fifo() ? !fs::is_regular_file("fifo") : true));
2✔
2481
    CHECK((has_socket() ? !fs::is_regular_file("socket") : true));
2✔
2482
    CHECK((block_path().empty() ? true : !fs::is_regular_file(block_path())));
2✔
2483
    CHECK((character_path().empty() ? true : !fs::is_regular_file(character_path())));
2✔
2484
    CHECK_NOTHROW(fs::is_regular_file("notfound"));
2✔
2485
    CHECK_NOTHROW(fs::is_regular_file("notfound", ec));
2✔
2486
    CHECK(ec);
2✔
2487
    ec.clear();
2✔
2488
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::none)));
2✔
2489
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::not_found)));
2✔
2490
    CHECK(fs::is_regular_file(fs::file_status(fs::file_type::regular)));
2✔
2491
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::directory)));
2✔
2492
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::symlink)));
2✔
2493
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::block)));
2✔
2494
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::character)));
2✔
2495
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::fifo)));
2✔
2496
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::socket)));
2✔
2497
    CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::unknown)));
2✔
2498
}
2✔
2499

2500
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_socket - is_socket", "[filesystem][operations][fs.op.is_socket]")
2✔
2501
{
2502
    std::error_code ec;
2✔
2503
    CHECK(!fs::is_socket("directory"));
2✔
2504
    CHECK(!fs::is_socket("regular"));
2✔
2505
    if (is_symlink_creation_supported()) {
2✔
2506
        CHECK(!fs::is_socket("dir_symlink"));
2✔
2507
        CHECK(!fs::is_socket("file_symlink"));
2✔
2508
    }
2509
    CHECK((has_fifo() ? !fs::is_socket("fifo") : true));
2✔
2510
    CHECK((has_socket() ? fs::is_socket("socket") : true));
2✔
2511
    CHECK((block_path().empty() ? true : !fs::is_socket(block_path())));
2✔
2512
    CHECK((character_path().empty() ? true : !fs::is_socket(character_path())));
2✔
2513
    CHECK_NOTHROW(fs::is_socket("notfound"));
2✔
2514
    CHECK_NOTHROW(fs::is_socket("notfound", ec));
2✔
2515
    CHECK(ec);
2✔
2516
    ec.clear();
2✔
2517
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::none)));
2✔
2518
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::not_found)));
2✔
2519
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::regular)));
2✔
2520
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::directory)));
2✔
2521
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::symlink)));
2✔
2522
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::block)));
2✔
2523
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::character)));
2✔
2524
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::fifo)));
2✔
2525
    CHECK(fs::is_socket(fs::file_status(fs::file_type::socket)));
2✔
2526
    CHECK(!fs::is_socket(fs::file_status(fs::file_type::unknown)));
2✔
2527
}
2✔
2528

2529
TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_symlink - is_symlink", "[filesystem][operations][fs.op.is_symlink]")
2✔
2530
{
2531
    std::error_code ec;
2✔
2532
    CHECK(!fs::is_symlink("directory"));
2✔
2533
    CHECK(!fs::is_symlink("regular"));
2✔
2534
    if (is_symlink_creation_supported()) {
2✔
2535
        CHECK(fs::is_symlink("dir_symlink"));
2✔
2536
        CHECK(fs::is_symlink("file_symlink"));
2✔
2537
    }
2538
    CHECK((has_fifo() ? !fs::is_symlink("fifo") : true));
2✔
2539
    CHECK((has_socket() ? !fs::is_symlink("socket") : true));
2✔
2540
    CHECK((block_path().empty() ? true : !fs::is_symlink(block_path())));
2✔
2541
    CHECK((character_path().empty() ? true : !fs::is_symlink(character_path())));
2✔
2542
    CHECK_NOTHROW(fs::is_symlink("notfound"));
2✔
2543
    CHECK_NOTHROW(fs::is_symlink("notfound", ec));
2✔
2544
    CHECK(ec);
2✔
2545
    ec.clear();
2✔
2546
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::none)));
2✔
2547
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::not_found)));
2✔
2548
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::regular)));
2✔
2549
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::directory)));
2✔
2550
    CHECK(fs::is_symlink(fs::file_status(fs::file_type::symlink)));
2✔
2551
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::block)));
2✔
2552
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::character)));
2✔
2553
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::fifo)));
2✔
2554
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::socket)));
2✔
2555
    CHECK(!fs::is_symlink(fs::file_status(fs::file_type::unknown)));
2✔
2556
}
2✔
2557

2558
#ifndef GHC_OS_WEB
2559
static fs::file_time_type timeFromString(const std::string& str)
8✔
2560
{
2561
    struct ::tm tm;
2562
    ::memset(&tm, 0, sizeof(::tm));
8✔
2563
    std::istringstream is(str);
8✔
2564
    is >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
8✔
2565
    if (is.fail()) {
8✔
2566
        throw std::exception();
×
2567
    }
2568
    return from_time_t<fs::file_time_type>(std::mktime(&tm));
16✔
2569
}
8✔
2570
#endif
2571

2572
TEST_CASE("fs.op.last_write_time - last_write_time", "[filesystem][operations][fs.op.last_write_time]")
2✔
2573
{
2574
    TemporaryDirectory t(TempOpt::change_path);
2✔
2575
    std::error_code ec;
2✔
2576
    fs::file_time_type ft;
2✔
2577
    generateFile("foo");
2✔
2578
    auto now = fs::file_time_type::clock::now();
2✔
2579
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time(t.path()) - now).count()) < 3);
2✔
2580
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - now).count()) < 3);
2✔
2581
    CHECK_THROWS_AS(fs::last_write_time("bar"), fs::filesystem_error);
4✔
2582
    CHECK_NOTHROW(ft = fs::last_write_time("bar", ec));
2✔
2583
    CHECK(ft == fs::file_time_type::min());
2✔
2584
    CHECK(ec);
2✔
2585
    ec.clear();
2✔
2586
    if (is_symlink_creation_supported()) {
2✔
2587
        std::this_thread::sleep_for(std::chrono::seconds(1));
2✔
2588
        fs::create_symlink("foo", "foo2");
2✔
2589
        ft = fs::last_write_time("foo");
2✔
2590
        // checks that the time of the symlink is fetched
2591
        CHECK(ft == fs::last_write_time("foo2"));
2✔
2592
    }
2593
#ifndef GHC_OS_WEB
2594
    auto nt = timeFromString("2015-10-21T04:30:00");
2✔
2595
    CHECK_NOTHROW(fs::last_write_time(t.path() / "foo", nt));
2✔
2596
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
2✔
2597
    nt = timeFromString("2015-10-21T04:29:00");
2✔
2598
    CHECK_NOTHROW(fs::last_write_time("foo", nt, ec));
2✔
2599
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
2✔
2600
    CHECK(!ec);
2✔
2601
    const auto preciseTime = nt + std::chrono::milliseconds(123);
2✔
2602
    CHECK_NOTHROW(fs::last_write_time("foo", preciseTime, ec));
2✔
2603
    REQUIRE(!ec);
2✔
2604
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::nanoseconds>(fs::last_write_time("foo") - preciseTime).count()) < 1000000);
2✔
2605
    const fs::directory_entry preciseEntry("foo");
2✔
2606
    CHECK(std::abs(std::chrono::duration_cast<std::chrono::nanoseconds>(preciseEntry.last_write_time() - preciseTime).count()) < 1000000);
2✔
2607
    if (is_symlink_creation_supported()) {
2✔
2608
#if !defined(GHC_OS_WINDOWS) && !defined(GHC_OS_WEB)
2609
        struct ::stat linkStatBefore;
2610
        REQUIRE(::lstat("foo2", &linkStatBefore) == 0);
2✔
2611
#endif
2612
        nt = timeFromString("2015-10-21T04:28:00");
2✔
2613
        CHECK_NOTHROW(fs::last_write_time("foo2", nt));
2✔
2614
        CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
2✔
2615
        nt = timeFromString("2015-10-21T04:27:00");
2✔
2616
        CHECK_NOTHROW(fs::last_write_time("foo2", nt, ec));
2✔
2617
        CHECK(!ec);
2✔
2618
        CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
2✔
2619
#if !defined(GHC_OS_WINDOWS) && !defined(GHC_OS_WEB)
2620
        struct ::stat linkStatAfter;
2621
        REQUIRE(::lstat("foo2", &linkStatAfter) == 0);
2✔
2622
        CHECK(linkStatAfter.st_mtime == linkStatBefore.st_mtime);
2✔
2623
#endif
2624
    }
2625
    CHECK_THROWS_AS(fs::last_write_time("bar", nt), fs::filesystem_error);
4✔
2626
    CHECK_NOTHROW(fs::last_write_time("bar", nt, ec));
2✔
2627
    CHECK(ec);
2✔
2628
#endif
2629
}
2✔
2630

2631
TEST_CASE("fs.op.permissions - permissions", "[filesystem][operations][fs.op.permissions]")
2✔
2632
{
2633
    TemporaryDirectory t(TempOpt::change_path);
2✔
2634
    std::error_code ec;
2✔
2635
    generateFile("foo", 512);
2✔
2636
    auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write;
2✔
2637
    CHECK_NOTHROW(fs::permissions("foo", allWrite, fs::perm_options::remove));
2✔
2638
    CHECK((fs::status("foo").permissions() & fs::perms::owner_write) != fs::perms::owner_write);
2✔
2639
#if !defined(GHC_OS_WINDOWS)
2640
    if (geteuid() != 0)
2✔
2641
#endif
2642
    {
2643
        CHECK_THROWS_AS(fs::resize_file("foo", 1024), fs::filesystem_error);
4✔
2644
        CHECK(fs::file_size("foo") == 512);
2✔
2645
    }
2646
    CHECK_NOTHROW(fs::permissions("foo", fs::perms::owner_write, fs::perm_options::add));
2✔
2647
    CHECK((fs::status("foo").permissions() & fs::perms::owner_write) == fs::perms::owner_write);
2✔
2648
    CHECK_NOTHROW(fs::resize_file("foo", 2048));
2✔
2649
    CHECK(fs::file_size("foo") == 2048);
2✔
2650
    CHECK_THROWS_AS(fs::permissions("bar", fs::perms::owner_write, fs::perm_options::add), fs::filesystem_error);
4✔
2651
    CHECK_NOTHROW(fs::permissions("bar", fs::perms::owner_write, fs::perm_options::add, ec));
2✔
2652
    CHECK(ec);
2✔
2653
    CHECK_THROWS_AS(fs::permissions("bar", fs::perms::owner_write, static_cast<fs::perm_options>(0)), fs::filesystem_error);
4✔
2654
}
2✔
2655

2656
TEST_CASE("fs.op.proximate - proximate", "[filesystem][operations][fs.op.proximate]")
2✔
2657
{
2658
    std::error_code ec;
2✔
2659
    CHECK(fs::proximate("/a/d", "/a/b/c") == "../../d");
2✔
2660
    CHECK(fs::proximate("/a/d", "/a/b/c", ec) == "../../d");
2✔
2661
    CHECK(!ec);
2✔
2662
    CHECK(fs::proximate("/a/b/c", "/a/d") == "../b/c");
2✔
2663
    CHECK(fs::proximate("/a/b/c", "/a/d", ec) == "../b/c");
2✔
2664
    CHECK(!ec);
2✔
2665
    CHECK(fs::proximate("a/b/c", "a") == "b/c");
2✔
2666
    CHECK(fs::proximate("a/b/c", "a", ec) == "b/c");
2✔
2667
    CHECK(!ec);
2✔
2668
    CHECK(fs::proximate("a/b/c", "a/b/c/x/y") == "../..");
2✔
2669
    CHECK(fs::proximate("a/b/c", "a/b/c/x/y", ec) == "../..");
2✔
2670
    CHECK(!ec);
2✔
2671
    CHECK(fs::proximate("a/b/c", "a/b/c") == ".");
2✔
2672
    CHECK(fs::proximate("a/b/c", "a/b/c", ec) == ".");
2✔
2673
    CHECK(!ec);
2✔
2674
    CHECK(fs::proximate("a/b", "c/d") == "../../a/b");
2✔
2675
    CHECK(fs::proximate("a/b", "c/d", ec) == "../../a/b");
2✔
2676
    CHECK(!ec);
2✔
2677
#ifndef GHC_OS_WINDOWS
2678
    if (has_host_root_name_support()) {
2✔
2679
        CHECK(fs::proximate("//host1/a/d", "//host2/a/b/c") == "//host1/a/d");
2✔
2680
        CHECK(fs::proximate("//host1/a/d", "//host2/a/b/c", ec) == "//host1/a/d");
2✔
2681
        CHECK(!ec);
2✔
2682
    }
2683
#endif
2684
}
2✔
2685

2686
TEST_CASE("fs.op.read_symlink - read_symlink", "[filesystem][operations][fs.op.read_symlink]")
2✔
2687
{
2688
    if (is_symlink_creation_supported()) {
2✔
2689
        TemporaryDirectory t(TempOpt::change_path);
2✔
2690
        std::error_code ec;
2✔
2691
        generateFile("foo");
2✔
2692
        fs::create_symlink(t.path() / "foo", "bar");
2✔
2693
        CHECK(fs::read_symlink("bar") == t.path() / "foo");
2✔
2694
        CHECK(fs::read_symlink("bar", ec) == t.path() / "foo");
2✔
2695
        CHECK(!ec);
2✔
2696
        CHECK_THROWS_AS(fs::read_symlink("foobar"), fs::filesystem_error);
4✔
2697
        CHECK(fs::read_symlink("foobar", ec) == fs::path());
2✔
2698
        CHECK(ec);
2✔
2699
    }
2✔
2700
}
2✔
2701

2702
TEST_CASE("fs.op.relative - relative", "[filesystem][operations][fs.op.relative]")
2✔
2703
{
2704
    CHECK(fs::relative("/a/d", "/a/b/c") == "../../d");
2✔
2705
    CHECK(fs::relative("/a/b/c", "/a/d") == "../b/c");
2✔
2706
    CHECK(fs::relative("a/b/c", "a") == "b/c");
2✔
2707
    CHECK(fs::relative("a/b/c", "a/b/c/x/y") == "../..");
2✔
2708
    CHECK(fs::relative("a/b/c", "a/b/c") == ".");
2✔
2709
    CHECK(fs::relative("a/b", "c/d") == "../../a/b");
2✔
2710
    std::error_code ec;
2✔
2711
    CHECK(fs::relative(fs::current_path() / "foo", ec) == "foo");
2✔
2712
    CHECK(!ec);
2✔
2713
}
2✔
2714

2715
TEST_CASE("fs.op.remove - remove", "[filesystem][operations][fs.op.remove]")
2✔
2716
{
2717
    TemporaryDirectory t(TempOpt::change_path);
2✔
2718
    std::error_code ec;
2✔
2719
    generateFile("foo");
2✔
2720
    CHECK(fs::remove("foo"));
2✔
2721
    CHECK(!fs::exists("foo"));
2✔
2722
    CHECK(!fs::remove("foo"));
2✔
2723
    generateFile("foo");
2✔
2724
    CHECK(fs::remove("foo", ec));
2✔
2725
    CHECK(!fs::exists("foo"));
2✔
2726
    if (is_symlink_creation_supported()) {
2✔
2727
        generateFile("foo");
2✔
2728
        fs::create_symlink("foo", "bar");
2✔
2729
        CHECK(fs::exists(fs::symlink_status("bar")));
2✔
2730
        CHECK(fs::remove("bar", ec));
2✔
2731
        CHECK(fs::exists("foo"));
2✔
2732
        CHECK(!fs::exists(fs::symlink_status("bar")));
2✔
2733
    }
2734
    CHECK(!fs::remove("bar"));
2✔
2735
    CHECK(!fs::remove("bar", ec));
2✔
2736
    CHECK(!ec);
2✔
2737
}
2✔
2738

2739
TEST_CASE("fs.op.remove_all - remove_all", "[filesystem][operations][fs.op.remove_all]")
2✔
2740
{
2741
    TemporaryDirectory t(TempOpt::change_path);
2✔
2742
    std::error_code ec;
2✔
2743
    generateFile("foo");
2✔
2744
    CHECK(fs::remove_all("foo", ec) == 1);
2✔
2745
    CHECK(!ec);
2✔
2746
    ec.clear();
2✔
2747
    CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
2✔
2748
    fs::create_directories("dir1/dir1a");
2✔
2749
    fs::create_directories("dir1/dir1b");
2✔
2750
    generateFile("dir1/dir1a/f1");
2✔
2751
    generateFile("dir1/dir1b/f2");
2✔
2752
    CHECK_NOTHROW(fs::remove_all("dir1/non-existing", ec));
2✔
2753
    CHECK(!ec);
2✔
2754
    CHECK(fs::remove_all("dir1/non-existing", ec) == 0);
2✔
2755
    if (is_symlink_creation_supported()) {
2✔
2756
        fs::create_directory_symlink("dir1", "dir1link");
2✔
2757
        CHECK(fs::remove_all("dir1link") == 1);
2✔
2758
    }
2759
    CHECK(fs::remove_all("dir1") == 5);
2✔
2760
    CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
2✔
2761
}
2✔
2762

2763
TEST_CASE("fs.op.rename - rename", "[filesystem][operations][fs.op.rename]")
2✔
2764
{
2765
    TemporaryDirectory t(TempOpt::change_path);
2✔
2766
    std::error_code ec;
2✔
2767
    generateFile("foo", 123);
2✔
2768
    fs::create_directory("dir1");
2✔
2769
    CHECK_NOTHROW(fs::rename("foo", "bar"));
2✔
2770
    CHECK(!fs::exists("foo"));
2✔
2771
    CHECK(fs::exists("bar"));
2✔
2772
    CHECK_NOTHROW(fs::rename("dir1", "dir2"));
2✔
2773
    CHECK(fs::exists("dir2"));
2✔
2774
    generateFile("foo2", 42);
2✔
2775
    CHECK_NOTHROW(fs::rename("bar", "foo2"));
2✔
2776
    CHECK(fs::exists("foo2"));
2✔
2777
    CHECK(fs::file_size("foo2") == 123u);
2✔
2778
    CHECK(!fs::exists("bar"));
2✔
2779
    CHECK_NOTHROW(fs::rename("foo2", "foo", ec));
2✔
2780
    CHECK(!ec);
2✔
2781
    CHECK_THROWS_AS(fs::rename("foobar", "barfoo"), fs::filesystem_error);
6✔
2782
    CHECK_NOTHROW(fs::rename("foobar", "barfoo", ec));
2✔
2783
    CHECK(ec);
2✔
2784
    CHECK(!fs::exists("barfoo"));
2✔
2785
}
2✔
2786

2787
TEST_CASE("fs.op.resize_file - resize_file", "[filesystem][operations][fs.op.resize_file]")
2✔
2788
{
2789
    TemporaryDirectory t(TempOpt::change_path);
2✔
2790
    std::error_code ec;
2✔
2791
    generateFile("foo", 1024);
2✔
2792
    CHECK(fs::file_size("foo") == 1024);
2✔
2793
    CHECK_NOTHROW(fs::resize_file("foo", 2048));
2✔
2794
    CHECK(fs::file_size("foo") == 2048);
2✔
2795
    CHECK_NOTHROW(fs::resize_file("foo", 1000, ec));
2✔
2796
    CHECK(!ec);
2✔
2797
    CHECK(fs::file_size("foo") == 1000);
2✔
2798
    CHECK_THROWS_AS(fs::resize_file("bar", 2048), fs::filesystem_error);
4✔
2799
    CHECK(!fs::exists("bar"));
2✔
2800
    CHECK_NOTHROW(fs::resize_file("bar", 4096, ec));
2✔
2801
    CHECK(ec);
2✔
2802
    CHECK(!fs::exists("bar"));
2✔
2803
}
2✔
2804

2805
TEST_CASE("fs.op.space - space", "[filesystem][operations][fs.op.space]")
2✔
2806
{
2807
    {
2808
        fs::space_info si;
2809
        CHECK_NOTHROW(si = fs::space(fs::current_path()));
2✔
2810
        CHECK(si.capacity > 1024 * 1024);
2✔
2811
        CHECK(si.capacity > si.free);
2✔
2812
        CHECK(si.free >= si.available);
2✔
2813
    }
2814
    {
2815
        std::error_code ec;
2✔
2816
        fs::space_info si;
2817
        CHECK_NOTHROW(si = fs::space(fs::current_path(), ec));
2✔
2818
        CHECK(si.capacity > 1024 * 1024);
2✔
2819
        CHECK(si.capacity > si.free);
2✔
2820
        CHECK(si.free >= si.available);
2✔
2821
        CHECK(!ec);
2✔
2822
    }
2823
#ifndef GHC_OS_WEB // statvfs under emscripten always returns a result, so this tests would fail
2824
    {
2825
        std::error_code ec;
2✔
2826
        fs::space_info si;
2827
        CHECK_NOTHROW(si = fs::space("foobar42", ec));
2✔
2828
        CHECK(si.capacity == static_cast<uintmax_t>(-1));
2✔
2829
        CHECK(si.free == static_cast<uintmax_t>(-1));
2✔
2830
        CHECK(si.available == static_cast<uintmax_t>(-1));
2✔
2831
        CHECK(ec);
2✔
2832
    }
2833
    CHECK_THROWS_AS(fs::space("foobar42"), fs::filesystem_error);
4✔
2834
#endif
2835
}
2✔
2836

2837
TEST_CASE("fs.op.status - status", "[filesystem][operations][fs.op.status]")
2✔
2838
{
2839
    TemporaryDirectory t(TempOpt::change_path);
2✔
2840
    std::error_code ec;
2✔
2841
    fs::file_status fs;
2✔
2842
    CHECK_NOTHROW(fs = fs::status("foo"));
2✔
2843
    CHECK(fs.type() == fs::file_type::not_found);
2✔
2844
    CHECK(fs.permissions() == fs::perms::unknown);
2✔
2845
    CHECK_NOTHROW(fs = fs::status("bar", ec));
2✔
2846
    CHECK(fs.type() == fs::file_type::not_found);
2✔
2847
    CHECK(fs.permissions() == fs::perms::unknown);
2✔
2848
    CHECK(ec);
2✔
2849
    ec.clear();
2✔
2850
    fs = fs::status(t.path());
2✔
2851
    CHECK(fs.type() == fs::file_type::directory);
2✔
2852
    CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
2✔
2853
    generateFile("foobar");
2✔
2854
    fs = fs::status(t.path() / "foobar");
2✔
2855
    CHECK(fs.type() == fs::file_type::regular);
2✔
2856
    CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
2✔
2857
    if (is_symlink_creation_supported()) {
2✔
2858
        fs::create_symlink(t.path() / "foobar", t.path() / "barfoo");
2✔
2859
        fs = fs::status(t.path() / "barfoo");
2✔
2860
        CHECK(fs.type() == fs::file_type::regular);
2✔
2861
        CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
2✔
2862
    }
2863
}
2✔
2864

2865
TEST_CASE("fs.op.status_known - status_known", "[filesystem][operations][fs.op.status_known]")
2✔
2866
{
2867
    CHECK(!fs::status_known(fs::file_status()));
2✔
2868
    CHECK(fs::status_known(fs::file_status(fs::file_type::not_found)));
2✔
2869
    CHECK(fs::status_known(fs::file_status(fs::file_type::regular)));
2✔
2870
    CHECK(fs::status_known(fs::file_status(fs::file_type::directory)));
2✔
2871
    CHECK(fs::status_known(fs::file_status(fs::file_type::symlink)));
2✔
2872
    CHECK(fs::status_known(fs::file_status(fs::file_type::character)));
2✔
2873
    CHECK(fs::status_known(fs::file_status(fs::file_type::fifo)));
2✔
2874
    CHECK(fs::status_known(fs::file_status(fs::file_type::socket)));
2✔
2875
    CHECK(fs::status_known(fs::file_status(fs::file_type::unknown)));
2✔
2876
}
2✔
2877

2878
TEST_CASE("fs.op.symlink_status - symlink_status", "[filesystem][operations][fs.op.symlink_status]")
2✔
2879
{
2880
    TemporaryDirectory t(TempOpt::change_path);
2✔
2881
    std::error_code ec;
2✔
2882
    fs::file_status fs;
2✔
2883
    CHECK_NOTHROW(fs = fs::symlink_status("foo"));
2✔
2884
    CHECK(fs.type() == fs::file_type::not_found);
2✔
2885
    CHECK(fs.permissions() == fs::perms::unknown);
2✔
2886
    CHECK_NOTHROW(fs = fs::symlink_status("bar", ec));
2✔
2887
    CHECK(fs.type() == fs::file_type::not_found);
2✔
2888
    CHECK(fs.permissions() == fs::perms::unknown);
2✔
2889
    CHECK(ec);
2✔
2890
    ec.clear();
2✔
2891
    fs = fs::symlink_status(t.path());
2✔
2892
    CHECK(fs.type() == fs::file_type::directory);
2✔
2893
    CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
2✔
2894
    generateFile("foobar");
2✔
2895
    fs = fs::symlink_status(t.path() / "foobar");
2✔
2896
    CHECK(fs.type() == fs::file_type::regular);
2✔
2897
    CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write));
2✔
2898
    if (is_symlink_creation_supported()) {
2✔
2899
        fs::create_symlink(t.path() / "foobar", t.path() / "barfoo");
2✔
2900
        fs = fs::symlink_status(t.path() / "barfoo");
2✔
2901
        CHECK(fs.type() == fs::file_type::symlink);
2✔
2902
    }
2903
}
2✔
2904

2905
TEST_CASE("fs.op.temp_dir_path - temporary_directory_path", "[filesystem][operations][fs.op.temp_dir_path]")
2✔
2906
{
2907
    std::error_code ec;
2✔
2908
    CHECK_NOTHROW(fs::exists(fs::temp_directory_path()));
2✔
2909
    CHECK_NOTHROW(fs::exists(fs::temp_directory_path(ec)));
2✔
2910
    CHECK(!fs::temp_directory_path().empty());
2✔
2911
    CHECK(!ec);
2✔
2912
}
2✔
2913

2914
TEST_CASE("fs.op.weakly_canonical - weakly_canonical", "[filesystem][operations][fs.op.weakly_canonical]")
2✔
2915
{
2916
    INFO("This might fail on std::implementations that return fs::current_path() for fs::canonical(\"\")");
2✔
2917
    CHECK(fs::weakly_canonical("") == ".");
2✔
2918
    if(fs::weakly_canonical("") == ".") {
2✔
2919
        CHECK(fs::weakly_canonical("foo/bar") == "foo/bar");
2✔
2920
        CHECK(fs::weakly_canonical("foo/./bar") == "foo/bar");
2✔
2921
        CHECK(fs::weakly_canonical("foo/../bar") == "bar");
2✔
2922
    }
2923
    else {
2924
        CHECK(fs::weakly_canonical("foo/bar") == fs::current_path() / "foo/bar");
×
2925
        CHECK(fs::weakly_canonical("foo/./bar") == fs::current_path() / "foo/bar");
×
2926
        CHECK(fs::weakly_canonical("foo/../bar") == fs::current_path() / "bar");
×
2927
    }
2928

2929
    {
2930
        TemporaryDirectory t(TempOpt::change_path);
2✔
2931
        auto dir = t.path() / "d0";
2✔
2932
        fs::create_directories(dir / "d1");
2✔
2933
        generateFile(dir / "f0");
2✔
2934
        fs::path rel(dir.filename());
2✔
2935
        CHECK(fs::weakly_canonical(dir) == dir);
2✔
2936
        CHECK(fs::weakly_canonical(rel) == dir);
2✔
2937
        CHECK(fs::weakly_canonical(dir / "f0") == dir / "f0");
2✔
2938
        CHECK(fs::weakly_canonical(dir / "f0/") == dir / "f0/");
2✔
2939
        CHECK(fs::weakly_canonical(dir / "f1") == dir / "f1");
2✔
2940
        CHECK(fs::weakly_canonical(rel / "f0") == dir / "f0");
2✔
2941
        CHECK(fs::weakly_canonical(rel / "f0/") == dir / "f0/");
2✔
2942
        CHECK(fs::weakly_canonical(rel / "f1") == dir / "f1");
2✔
2943
        CHECK(fs::weakly_canonical(rel / "./f0") == dir / "f0");
2✔
2944
        CHECK(fs::weakly_canonical(rel / "./f1") == dir / "f1");
2✔
2945
        CHECK(fs::weakly_canonical(rel / "d1/../f0") == dir / "f0");
2✔
2946
        CHECK(fs::weakly_canonical(rel / "d1/../f1") == dir / "f1");
2✔
2947
        CHECK(fs::weakly_canonical(rel / "d1/../f1/../f2") == dir / "f2");
2✔
2948

2949
        std::error_code ec(42, std::system_category());
2✔
2950
        CHECK(fs::weakly_canonical(dir / "missing/child", ec) == dir / "missing/child");
2✔
2951
        CHECK(!ec);
2✔
2952

2953
        const auto invalid = dir / std::string(1024, 'x');
4✔
2954
        std::error_code lookupError;
2✔
2955
        CHECK_FALSE(fs::exists(invalid, lookupError));
2✔
2956
#ifndef GHC_OS_WINDOWS
2957
        REQUIRE(lookupError);
2✔
2958
#endif
2959
        if (lookupError) {
2✔
2960
            CHECK(fs::weakly_canonical(invalid, ec).empty());
2✔
2961
            CHECK(ec == lookupError);
2✔
2962
#ifdef GHC_WITH_EXCEPTIONS
2963
            CHECK_THROWS_AS(fs::weakly_canonical(invalid), fs::filesystem_error);
2✔
2964
#endif
2965
        }
2966
    }
2✔
2967
}
2✔
2968

2969
TEST_CASE("std::string_view support", "[filesystem][fs.string_view]")
2✔
2970
{
2971
#if defined(GHC_HAS_STD_STRING_VIEW) || defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW)
2972

2973
#if defined(GHC_HAS_STD_STRING_VIEW)
2974
    using namespace std::literals;
2975
    using string_view = std::string_view;
2976
    using wstring_view = std::wstring_view;
2977
#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW)
2978
    using string_view = std::experimental::string_view;
2979
    using wstring_view = std::experimental::wstring_view;
2980
#endif
2981

2982
    {
2983
        std::string p("foo/bar");
2✔
2984
        string_view sv(p);
2✔
2985
        CHECK(fs::path(sv, fs::path::format::generic_format).generic_string() == "foo/bar");
2✔
2986
        fs::path p2("fo");
2✔
2987
        p2 += string_view("o");
2✔
2988
        CHECK(p2 == "foo");
2✔
2989
        CHECK(p2.compare(string_view("foo")) == 0);
2✔
2990
    }
2✔
2991
    {
2992
        auto p = fs::path{"XYZ"};
2✔
2993
        p /= string_view("Appendix");
2✔
2994
        CHECK(p == "XYZ/Appendix");
2✔
2995
    }
2✔
2996
    {
2997
        std::wstring p(L"foo/bar");
2✔
2998
        wstring_view sv(p);
2✔
2999
        CHECK(fs::path(sv, fs::path::format::generic_format).generic_string() == "foo/bar");
2✔
3000
        fs::path p2(L"fo");
2✔
3001
        p2 += wstring_view(L"o");
2✔
3002
        CHECK(p2 == "foo");
2✔
3003
        CHECK(p2.compare(wstring_view(L"foo")) == 0);
2✔
3004
    }
2✔
3005

3006
#else
3007
    WARN("std::string_view specific tests are empty without std::string_view.");
3008
#endif
3009
}
2✔
3010

3011
TEST_CASE("Windows: Long filename support", "[filesystem][path][fs.path.win.long]")
2✔
3012
{
3013
#ifdef GHC_OS_WINDOWS
3014
    TemporaryDirectory t(TempOpt::change_path);
3015
    char c = 'A';
3016
    fs::path dir{"\\\\?\\"};
3017
    dir += fs::current_path().u8string();
3018
    for (; c <= 'Z'; ++c) {
3019
        std::string part = std::string(16, c);
3020
        dir /= part;
3021
        CHECK_NOTHROW(fs::create_directory(dir));
3022
        CHECK(fs::exists(dir));
3023
        generateFile(dir / "f0");
3024
        REQUIRE(fs::exists(dir / "f0"));
3025
    }
3026
    CHECK(c > 'Z');
3027
    fs::remove_all(fs::current_path() / std::string(16, 'A'));
3028
    CHECK(!fs::exists(fs::current_path() / std::string(16, 'A')));
3029
    CHECK_NOTHROW(fs::create_directories(dir));
3030
    CHECK(fs::exists(dir));
3031
    generateFile(dir / "f0");
3032
    CHECK(fs::exists(dir / "f0"));
3033
#else
3034
    WARN("Windows specific tests are empty on non-Windows systems.");
2✔
3035
#endif
3036
}
2✔
3037

3038
TEST_CASE("Windows: path namespace handling", "[filesystem][path][fs.path.win.namespaces]")
2✔
3039
{
3040
#ifdef GHC_OS_WINDOWS
3041
    {
3042
        std::error_code ec;
3043
        fs::path p(R"(\\localhost\c$\Windows)");
3044
        auto symstat = fs::symlink_status(p, ec);
3045
        CHECK(!ec);
3046
        auto p2 = fs::canonical(p, ec);
3047
        CHECK(!ec);
3048
        CHECK(p2 == p);
3049

3050
        auto p3 = fs::canonical(R"(\\.\UNC\localhost\c$\Windows)", ec);
3051
        CHECK(!ec);
3052
        CHECK(p3 == p);
3053

3054
        auto p4 = fs::canonical(R"(\\?\UNC\localhost\c$\Windows)", ec);
3055
        CHECK(!ec);
3056
        CHECK(p4 == p);
3057
    }
3058
    
3059
    struct TestInfo
3060
    {
3061
        std::string _path;
3062
        std::string _string;
3063
        std::string _rootName;
3064
        std::string _rootPath;
3065
        std::string _iterateResult;
3066
    };
3067
    std::vector<TestInfo> variants = {
3068
        {R"(C:\Windows\notepad.exe)", R"(C:\Windows\notepad.exe)", "C:", "C:\\", "C:,/,Windows,notepad.exe"},
3069
#ifdef USE_STD_FS
3070
        {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,C:,Windows,notepad.exe"},
3071
        {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "\\??", "\\??\\", "/??,/,C:,Windows,notepad.exe"},
3072
#else
3073
        {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "C:", "C:\\", "//?/,C:,/,Windows,notepad.exe"},
3074
        {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "C:", "C:\\", "/?\?/,C:,/,Windows,notepad.exe"},
3075
#endif
3076
        {R"(\\.\C:\Windows\notepad.exe)", R"(\\.\C:\Windows\notepad.exe)", "\\\\.", "\\\\.\\", "//.,/,C:,Windows,notepad.exe"},
3077
        {R"(\\?\HarddiskVolume1\Windows\notepad.exe)", R"(\\?\HarddiskVolume1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,HarddiskVolume1,Windows,notepad.exe"},
3078
        {R"(\\?\Harddisk0Partition1\Windows\notepad.exe)", R"(\\?\Harddisk0Partition1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,Harddisk0Partition1,Windows,notepad.exe"},
3079
        {R"(\\.\GLOBALROOT\Device\HarddiskVolume1\Windows\notepad.exe)", R"(\\.\GLOBALROOT\Device\HarddiskVolume1\Windows\notepad.exe)", "\\\\.", "\\\\.\\", "//.,/,GLOBALROOT,Device,HarddiskVolume1,Windows,notepad.exe"},
3080
        {R"(\\?\GLOBALROOT\Device\Harddisk0\Partition1\Windows\notepad.exe)", R"(\\?\GLOBALROOT\Device\Harddisk0\Partition1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,GLOBALROOT,Device,Harddisk0,Partition1,Windows,notepad.exe"},
3081
        {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"},
3082
        {R"(\\LOCALHOST\C$\Windows\notepad.exe)", R"(\\LOCALHOST\C$\Windows\notepad.exe)", "\\\\LOCALHOST", "\\\\LOCALHOST\\", "//LOCALHOST,/,C$,Windows,notepad.exe"},
3083
        {R"(\\?\UNC\C$\Windows\notepad.exe)", R"(\\?\UNC\C$\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,UNC,C$,Windows,notepad.exe"},
3084
        {R"(\\?\GLOBALROOT\Device\Mup\C$\Windows\notepad.exe)", R"(\\?\GLOBALROOT\Device\Mup\C$\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,GLOBALROOT,Device,Mup,C$,Windows,notepad.exe"},
3085
    };
3086

3087
    for (auto ti : variants) {
3088
        INFO("Used path: " + ti._path);
3089
        auto p = fs::path(ti._path);
3090
        CHECK(p.string() == ti._string);
3091
        CHECK(p.is_absolute());
3092
        CHECK(p.root_name().string() == ti._rootName);
3093
        CHECK(p.root_path().string() == ti._rootPath);
3094
        CHECK(iterateResult(p) == ti._iterateResult);
3095
    }
3096
#else
3097
    WARN("Windows specific tests are empty on non-Windows systems.");
2✔
3098
#endif
3099
}
2✔
3100

3101
TEST_CASE("Windows: Mapped folders handling ", "[filesystem][fs.win][fs.win.mapped]")
2✔
3102
{
3103
#ifdef GHC_OS_WINDOWS
3104
    // this test expects a mapped volume on C:\\fs-test as is the case on the development test system
3105
    // does nothing on other systems
3106
    if (fs::exists("C:\\fs-test")) {
3107
        CHECK(fs::canonical("C:\\fs-test\\Test.txt").string() == "C:\\fs-test\\Test.txt");
3108
    }
3109
#else
3110
    WARN("Windows specific tests are empty on non-Windows systems.");
2✔
3111
#endif
3112
}
2✔
3113

3114
TEST_CASE("Windows: Deletion of Read-only Files", "[filesystem][fs.win][fs.win.remove]")
2✔
3115
{
3116
#ifdef GHC_OS_WINDOWS
3117
    TemporaryDirectory t(TempOpt::change_path);
3118
    std::error_code ec;
3119
    generateFile("foo", 512);
3120
    auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write;
3121
    CHECK_NOTHROW(fs::permissions("foo", allWrite, fs::perm_options::remove));
3122
    CHECK_NOTHROW(fs::remove("foo"));
3123
    CHECK(!fs::exists("foo"));
3124
#else
3125
    WARN("Windows specific tests are empty on non-Windows systems.");
2✔
3126
#endif
3127
}
2✔
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

© 2026 Coveralls, Inc