From 2265f412717de6deefeef15e248d42598afe93e7 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 1 Oct 2025 08:22:42 -0700 Subject: [PATCH 1/7] test_filesystem_support.hpp: Avoid dragging in ``. Standard P0218R1_filesystem includes test_filesystem_support.hpp, and we don't want `` there. get_test_directory_subname() is the important thing to centralize. Explicitly calling fs::temp_directory_path() makes what we're doing clearer. --- tests/std/include/test_filesystem_support.hpp | 7 ------- .../tests/Dev11_1066931_filesystem_rename_noop/test.cpp | 2 +- tests/std/tests/VSO_0000000_path_stream_parameter/test.cpp | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/std/include/test_filesystem_support.hpp b/tests/std/include/test_filesystem_support.hpp index 34c338b960c..bed01e6524d 100644 --- a/tests/std/include/test_filesystem_support.hpp +++ b/tests/std/include/test_filesystem_support.hpp @@ -3,10 +3,7 @@ #pragma once -#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING - #include -#include #include #include #include @@ -23,10 +20,6 @@ std::string get_test_directory_subname(const T& testName) { return subName; } -inline std::experimental::filesystem::path get_experimental_test_directory(const char* const testName) { - return std::experimental::filesystem::temp_directory_path() / get_test_directory_subname(testName); -} - #if _HAS_CXX17 #include diff --git a/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp b/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp index dd727839dbe..486cae0093b 100644 --- a/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp +++ b/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp @@ -345,7 +345,7 @@ int main() { error_code ec; const auto previousCd = fs::current_path(ec); assert_success(ec); - const auto testDir = get_experimental_test_directory("filesystem_rename_noop"); + const auto testDir = fs::temp_directory_path() / get_test_directory_subname("filesystem_rename_noop"); printf("changing directory to \"%ls\"\n", testDir.native().c_str()); fs::create_directory(testDir, ec); assert_success(ec); diff --git a/tests/std/tests/VSO_0000000_path_stream_parameter/test.cpp b/tests/std/tests/VSO_0000000_path_stream_parameter/test.cpp index 41910ec0ccf..4054508c8b2 100644 --- a/tests/std/tests/VSO_0000000_path_stream_parameter/test.cpp +++ b/tests/std/tests/VSO_0000000_path_stream_parameter/test.cpp @@ -18,7 +18,7 @@ int main() { error_code ec; { - const auto testDir = get_experimental_test_directory("path_stream_parameter"); + const auto testDir = fs::temp_directory_path() / get_test_directory_subname("path_stream_parameter"); fs::create_directories(testDir, ec); assert(!ec); From a39910bdc06c6c718bb22ca49318a0de79269e9f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 1 Oct 2025 09:00:20 -0700 Subject: [PATCH 2/7] Fix confusion in VSO_0000000_path_stream_parameter, make it dual-mode. This test intended to cover the Experimental and Standard implementations, but never got around to finishing the latter. In the `_HAS_CXX17` region, we called get_test_directory() to get a Standard std::filesystem::path, but then everything else called fs::meow() (i.e. std::experimental::filesystem::meow()) and had to use .native() to adapt the Standard path to the Experimental functions. At one point, these were commented TODO, but it looks like I purged those comments during the cleanup for GitHub, without realizing that the test was unfinished. As Standard filesystem has long been completed, we can fix this test. Now the Experimental and Standard regions properly mirror each other. This also noticed a missing assertion in the Experimental part, that the final fs::remove_all() should succeed. Finally, to defend against further confusion, restrict the Experimental namespace alias to the scope where it's needed. --- .../VSO_0000000_path_stream_parameter/test.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/std/tests/VSO_0000000_path_stream_parameter/test.cpp b/tests/std/tests/VSO_0000000_path_stream_parameter/test.cpp index 4054508c8b2..dcdbc072202 100644 --- a/tests/std/tests/VSO_0000000_path_stream_parameter/test.cpp +++ b/tests/std/tests/VSO_0000000_path_stream_parameter/test.cpp @@ -12,12 +12,13 @@ #include using namespace std; -namespace fs = experimental::filesystem; int main() { error_code ec; { + namespace fs = experimental::filesystem; + const auto testDir = fs::temp_directory_path() / get_test_directory_subname("path_stream_parameter"); fs::create_directories(testDir, ec); assert(!ec); @@ -77,12 +78,13 @@ int main() { } fs::remove_all(testDir, ec); + assert(!ec); } #if _HAS_CXX17 { const auto testDir = get_test_directory("path_stream_parameter"); - fs::create_directories(testDir.native(), ec); + filesystem::create_directories(testDir, ec); assert(!ec); { @@ -90,7 +92,7 @@ int main() { filebuf buf; buf.open(filebufPath, ios::out | ios::trunc); buf.close(); - assert(fs::exists(filebufPath.native(), ec)); + assert(filesystem::exists(filebufPath, ec)); assert(!ec); } @@ -100,7 +102,7 @@ int main() { ofstream ostr(ofstreamFile); assert(ostr); ostr << 1729; - assert(fs::exists(ofstreamFile.native(), ec)); + assert(filesystem::exists(ofstreamFile, ec)); assert(!ec); ostr.close(); ostr.open(ofstreamOpenFile); @@ -108,7 +110,7 @@ int main() { assert(ostr); } - assert(fs::exists(ofstreamOpenFile.native(), ec)); + assert(filesystem::exists(ofstreamOpenFile, ec)); assert(!ec); const auto fstreamFile = testDir / L"fstream_file"; @@ -116,14 +118,14 @@ int main() { { fstream fstr(fstreamFile, ios::out | ios::trunc); assert(fstr); - assert(fs::exists(fstreamFile.native(), ec)); + assert(filesystem::exists(fstreamFile, ec)); assert(!ec); fstr.close(); fstr.open(fstreamOpenFile, ios::out | ios::trunc); assert(fstr); } - assert(fs::exists(fstreamOpenFile.native(), ec)); + assert(filesystem::exists(fstreamOpenFile, ec)); assert(!ec); { @@ -139,7 +141,7 @@ int main() { assert(temp == 42); } - fs::remove_all(testDir.native(), ec); + filesystem::remove_all(testDir, ec); assert(!ec); } #endif // _HAS_CXX17 From 7a9e1fd762d4fcef9ca08d2994f76bdf9f27739c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 1 Oct 2025 09:54:57 -0700 Subject: [PATCH 3/7] Fix VSO_0000000_instantiate_iterators_misc to be dual-mode. This test was including both Standard and Experimental filesystem, but exercising only Experimental. Gather `` and the commented-out ``, ``, `` into a `_HAS_CXX17` block, to avoid emitting "this does nothing" messages in C++14 mode. Include `` because I'll need `is_same_v`. Rename the existing coverage to experimental_filesystem_test_impl and experimental_filesystem_test. Within `_HAS_CXX17`, add standard_filesystem_test_impl and standard_filesystem_test. Differences: * `using namespace experimental::filesystem;` => `using namespace filesystem;` * Guard the `default_locale` constructions with `is_same_v` due to N5014 [fs.path.construct]/5: "Mandates: The value type of Source and InputIterator is char." * Now that p3, p4, and p5 are scoped, change following usage to always use p0. We don't need to worry about unused variable warnings (for now). * Drop `p0.concat(*c_str);`. N5014 [fs.path.concat] specifies `concat(const Source& x)` and N5014 [fs.path.req]/2 doesn't permit individual characters to be a Source. --- .../test.compile.pass.cpp | 87 ++++++++++++++++--- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/tests/std/tests/VSO_0000000_instantiate_iterators_misc/test.compile.pass.cpp b/tests/std/tests/VSO_0000000_instantiate_iterators_misc/test.compile.pass.cpp index d73e84653fc..ff5f35e3424 100644 --- a/tests/std/tests/VSO_0000000_instantiate_iterators_misc/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0000000_instantiate_iterators_misc/test.compile.pass.cpp @@ -24,7 +24,6 @@ #define _USE_NAMED_IDL_NAMESPACE 1 #include -// #include // All templates instantiated in P0220R1_any #include #include #include @@ -55,7 +54,6 @@ #include #include #include -#include #include #include #include @@ -73,7 +71,6 @@ #include #include #include -// #include // All templates instantiated in P0220R1_optional #include #include #include @@ -86,12 +83,19 @@ #include #include #include +#include #include #include #include -// #include // All templates instantiated in P0088R3_variant #include +#if _HAS_CXX17 +#include +// #include // All templates instantiated in P0220R1_any +// #include // All templates instantiated in P0220R1_optional +// #include // All templates instantiated in P0088R3_variant +#endif // _HAS_CXX17 + // Headers not allowed with /clr:pure #ifndef _M_CEE_PURE #include @@ -345,7 +349,7 @@ void exception_test() { } template -void filesystem_test_impl(const CharType* c_str) { +void experimental_filesystem_test_impl(const CharType* c_str) { using namespace experimental::filesystem; basic_string str = c_str; @@ -390,17 +394,78 @@ void filesystem_test_impl(const CharType* c_str) { (void) u8path(u8str.c_str()); } -void filesystem_test() { - filesystem_test_impl("narrow"); - filesystem_test_impl(L"wide"); +void experimental_filesystem_test() { + experimental_filesystem_test_impl("narrow"); + experimental_filesystem_test_impl(L"wide"); +#ifndef _M_CEE_PURE +#ifdef __cpp_char8_t + experimental_filesystem_test_impl(u8"utf8"); +#endif // __cpp_char8_t + experimental_filesystem_test_impl(u"utf16"); + experimental_filesystem_test_impl(U"utf32"); +#endif // _M_CEE_PURE +} + +#if _HAS_CXX17 +template +void standard_filesystem_test_impl(const CharType* c_str) { + using namespace filesystem; + + basic_string str = c_str; + + path p0(str.begin(), str.end()); + path p1(c_str); + path p2(str); + + if constexpr (is_same_v) { + locale default_locale{}; + path p3(str.begin(), str.end(), default_locale); + path p4(c_str, default_locale); + path p5(str, default_locale); + } + + p0 = c_str; + p0 = str; + p0.assign(str.begin(), str.end()); + p0.assign(c_str); + p0.assign(str); + p0 /= c_str; + p0 /= str; + p0.append(str.begin(), str.end()); + p0.append(c_str); + p0 += *c_str; + p0 += str; + p0 += c_str; + p0.concat(str.begin(), str.end()); + p0.concat(str.begin()); + p0.concat(c_str); + p0.concat(str); + + (void) p0.string(str.get_allocator()); + (void) p0.generic_string(str.get_allocator()); + + stringstream ss{}; + + ss << p0; + ss >> p0; + + auto u8str = p0.u8string(); + (void) u8path(u8str.begin(), u8str.end()); + (void) u8path(u8str.c_str()); +} + +void standard_filesystem_test() { + standard_filesystem_test_impl("narrow"); + standard_filesystem_test_impl(L"wide"); #ifndef _M_CEE_PURE #ifdef __cpp_char8_t - filesystem_test_impl(u8"utf8"); + standard_filesystem_test_impl(u8"utf8"); #endif // __cpp_char8_t - filesystem_test_impl(u"utf16"); - filesystem_test_impl(U"utf32"); + standard_filesystem_test_impl(u"utf16"); + standard_filesystem_test_impl(U"utf32"); #endif // _M_CEE_PURE } +#endif // _HAS_CXX17 template void fstream_test_impl() { From a60ae6d95bafd1be15f7be28cf497445ed8874dc Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 1 Oct 2025 10:14:01 -0700 Subject: [PATCH 4/7] Remove dead code from VSO_0121275_filesystem_canonical_should_handle_many_double_dots. This Experimental-only test was defining exec_test_output_path_too_long_behavior() but never calling it. As this involves creating symlinks, it couldn't run for PR/CI checks anyways. We have some Standard test coverage for symlinks that warns (without failing) when symlinks can't be created, making it possible for manual runs to laboriously exercise that code. That isn't worth the effort for the Experimental implementation, which we aren't messing with anymore. This function was the only user of MAX_PATH and ``, which we can now drop from the test too. Also, cleanup an outdated test comment. VSO-158882 ": canonical() should handle symlinks and other OS behavior" was fixed by MSVC-PR-107664 on 2018-02-23, making the Standard implementation call GetFinalPathNameByHandleW. But this is a test for the Experimental implementation, which never calls GetFinalPathNameByHandleW, so the comment should be cauterized. --- .../test.cpp | 46 +------------------ 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/tests/std/tests/VSO_0121275_filesystem_canonical_should_handle_many_double_dots/test.cpp b/tests/std/tests/VSO_0121275_filesystem_canonical_should_handle_many_double_dots/test.cpp index f5012baa30e..a35303fde30 100644 --- a/tests/std/tests/VSO_0121275_filesystem_canonical_should_handle_many_double_dots/test.cpp +++ b/tests/std/tests/VSO_0121275_filesystem_canonical_should_handle_many_double_dots/test.cpp @@ -12,11 +12,6 @@ #include #include -#pragma warning(push) // TRANSITION, OS-23694920 -#pragma warning(disable : 4668) // 'MEOW' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' -#include -#pragma warning(pop) - using namespace std; namespace fs = std::experimental::filesystem; @@ -68,10 +63,7 @@ void exec_test_case(const fs::path& expectedAnswer, const fs::path& input) { } void exec_test_case_string(const fs::path& expectedAnswer, const fs::path& input) { - // This tests calling _Ugly stuff because we want to test the string based - // fallback which is used if GetFinalPathNameByHandleW cannot be called. - // Note that GetFinalPathNameByHandleW isn't being called at all until - // VSO-158882 gets fixed in Dev15 (or later). + // This tests calling _Ugly stuff because we want to test the string based fallback. fs::path canonicalPath; fs::path absPath = fs::absolute(input); _Canonicalize_string_only(canonicalPath, absPath); @@ -97,42 +89,6 @@ void exec_test_input_path_too_long_behavior() { assert_canonical_path_too_long(input, "input 261"); } -STATIC_ASSERT(260 == MAX_PATH); - -void exec_test_output_path_too_long_behavior() { - // Make a long (> MAX_PATH) file, make a short (< MAX_PATH) symlink to it, - // and verify that canonical() fails with filename_too_long - wstring longName(LR"(\\?\)"); - longName.append(fs::current_path().native()); - longName.append(LR"(\path_longer_than_MAX_PATH)"); - while (longName.size() < MAX_PATH) { - longName.append(L"_component"); - } - - longName.append(L".txt"); - - HANDLE hTouched = ::CreateFileW(longName.c_str(), FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr); - - if (hTouched == INVALID_HANDLE_VALUE) { - const auto lastError = static_cast(::GetLastError()); - printf("Test failed because long target file could not be created. (Error 0x%08X)\n", lastError); - abort(); - } - - const wchar_t* const hardLinkFileName = L"Link_to_long_path.txt"; - if (::CreateSymbolicLinkW(hardLinkFileName, longName.c_str(), 0) == 0) { - const auto lastError = static_cast(::GetLastError()); - printf("Test failed because symlink to long name could not be created. (Error 0x%08X)\n", lastError); - ::CloseHandle(hTouched); - abort(); - } - - assert_canonical_path_too_long(hardLinkFileName, "output"); - assert(::DeleteFileW(hardLinkFileName) != 0); - ::CloseHandle(hTouched); -} - struct test_case_example { fs::path expected; fs::path actual; From af6c045e8bd159ffc4e3e06980cbf06cc6944147 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 1 Oct 2025 12:48:38 -0700 Subject: [PATCH 5/7] Extend Dev11_1180290_filesystem_error_code to be dual-mode. In C++17 mode, include Standard ``. Move the existing code into `namespace test_experimental`, including its Experimental namespace alias. Rephrase `system_clock::now()` to `fs::file_time_type::clock::now()`, which works across Experimental, Standard 17, and Standard 20. In C++17 mode, introduce `namespace test_standard`, which exercises the Standard `filesystem` namespace. Note that we need to expect `filesystem::remove_all("DELME_dir", ec) == 4`. It appears that our Standard implementation is correct to return 4, and Experimental incorrectly returned 3. --- .../test.cpp | 203 +++++++++++++----- 1 file changed, 148 insertions(+), 55 deletions(-) diff --git a/tests/std/tests/Dev11_1180290_filesystem_error_code/test.cpp b/tests/std/tests/Dev11_1180290_filesystem_error_code/test.cpp index d6c7d2d200d..24abba9e72b 100644 --- a/tests/std/tests/Dev11_1180290_filesystem_error_code/test.cpp +++ b/tests/std/tests/Dev11_1180290_filesystem_error_code/test.cpp @@ -10,9 +10,12 @@ #include #include +#if _HAS_CXX17 +#include +#endif // _HAS_CXX17 + using namespace std; using namespace std::chrono; -namespace fs = std::experimental::filesystem; void assert_success(const error_code& ec) { assert(!ec); @@ -22,69 +25,159 @@ void assert_failure(const error_code& ec) { assert(!!ec); } -void create_file_containing(const char* const filename, const char* const contents) { - assert(!fs::exists(filename)); +namespace test_experimental { + namespace fs = std::experimental::filesystem; - ofstream f(filename); - f << contents << endl; - f.close(); + void create_file_containing(const char* const filename, const char* const contents) { + assert(!fs::exists(filename)); - assert(fs::is_regular_file(filename)); -} + ofstream f(filename); + f << contents << endl; + f.close(); -int main() { - (void) fs::remove_all("DELME_dir"); - assert(!fs::exists("DELME_dir")); - assert(fs::create_directory("DELME_dir")); - assert(fs::is_directory("DELME_dir")); - - create_file_containing("DELME_dir/DELME_file.txt", "meow"); - create_file_containing("DELME_dir/DELME_old.txt", "disco"); - create_file_containing("DELME_dir/DELME_new.txt", "DoYouWantToBuildASnowman"); - - { - const auto now = system_clock::now(); - fs::last_write_time("DELME_dir/DELME_old.txt", now - 1h); - fs::last_write_time("DELME_dir/DELME_new.txt", now); - assert(fs::last_write_time("DELME_dir/DELME_old.txt") < fs::last_write_time("DELME_dir/DELME_new.txt")); + assert(fs::is_regular_file(filename)); } - error_code ec; - - fs::copy("DELME_dir", "NONEXISTENT_dir1/NONEXISTENT_dir2", fs::copy_options::recursive, ec); - assert_failure(ec); - assert(!fs::exists("NONEXISTENT_dir1")); - - assert(!fs::copy_file("DELME_dir/DELME_file.txt", "DELME_dir/DELME_file.txt", fs::copy_options::skip_existing, ec)); - assert_failure(ec); - assert(fs::is_regular_file("DELME_dir/DELME_file.txt")); - - assert(!fs::copy_file("DELME_dir/DELME_old.txt", "DELME_dir/DELME_new.txt", fs::copy_options::update_existing, ec)); - assert_success(ec); - assert(fs::file_size("DELME_dir/DELME_old.txt") != fs::file_size("DELME_dir/DELME_new.txt")); + void run_tests() { + (void) fs::remove_all("DELME_dir"); + assert(!fs::exists("DELME_dir")); + assert(fs::create_directory("DELME_dir")); + assert(fs::is_directory("DELME_dir")); + + create_file_containing("DELME_dir/DELME_file.txt", "meow"); + create_file_containing("DELME_dir/DELME_old.txt", "disco"); + create_file_containing("DELME_dir/DELME_new.txt", "DoYouWantToBuildASnowman"); + + { + const auto now = fs::file_time_type::clock::now(); + fs::last_write_time("DELME_dir/DELME_old.txt", now - 1h); + fs::last_write_time("DELME_dir/DELME_new.txt", now); + assert(fs::last_write_time("DELME_dir/DELME_old.txt") < fs::last_write_time("DELME_dir/DELME_new.txt")); + } + + error_code ec; + + fs::copy("DELME_dir", "NONEXISTENT_dir1/NONEXISTENT_dir2", fs::copy_options::recursive, ec); + assert_failure(ec); + assert(!fs::exists("NONEXISTENT_dir1")); + + assert(!fs::copy_file( + "DELME_dir/DELME_file.txt", "DELME_dir/DELME_file.txt", fs::copy_options::skip_existing, ec)); + assert_failure(ec); + assert(fs::is_regular_file("DELME_dir/DELME_file.txt")); + + assert(!fs::copy_file( + "DELME_dir/DELME_old.txt", "DELME_dir/DELME_new.txt", fs::copy_options::update_existing, ec)); + assert_success(ec); + assert(fs::file_size("DELME_dir/DELME_old.txt") != fs::file_size("DELME_dir/DELME_new.txt")); + + assert( + fs::copy_file("DELME_dir/DELME_new.txt", "DELME_dir/DELME_old.txt", fs::copy_options::update_existing, ec)); + assert_success(ec); + assert(fs::file_size("DELME_dir/DELME_old.txt") == fs::file_size("DELME_dir/DELME_new.txt")); + + assert(!fs::create_directories("DELME_dir/DELME_file.txt/blah/blah/blah", ec)); + assert_failure(ec); + assert(fs::is_regular_file("DELME_dir/DELME_file.txt")); + + assert(!fs::is_empty("NONEXISTENT_file.txt", ec)); + assert_failure(ec); + assert(!fs::exists("NONEXISTENT_file.txt")); + + assert(fs::last_write_time("NONEXISTENT_file.txt", ec) == fs::file_time_type::min()); + assert_failure(ec); + assert(!fs::exists("NONEXISTENT_file.txt")); + + assert(_putenv_s("TMP", "NONEXISTENT_dir1") == 0); + assert(fs::temp_directory_path(ec) == fs::path()); + assert_failure(ec); + assert(!fs::exists("NONEXISTENT_dir1")); + + assert(fs::remove_all("DELME_dir", ec) == 3); + assert_success(ec); + assert(!fs::exists("DELME_dir")); + } +} // namespace test_experimental - assert(fs::copy_file("DELME_dir/DELME_new.txt", "DELME_dir/DELME_old.txt", fs::copy_options::update_existing, ec)); - assert_success(ec); - assert(fs::file_size("DELME_dir/DELME_old.txt") == fs::file_size("DELME_dir/DELME_new.txt")); +#if _HAS_CXX17 +namespace test_standard { + void create_file_containing(const char* const filename, const char* const contents) { + assert(!filesystem::exists(filename)); - assert(!fs::create_directories("DELME_dir/DELME_file.txt/blah/blah/blah", ec)); - assert_failure(ec); - assert(fs::is_regular_file("DELME_dir/DELME_file.txt")); + ofstream f(filename); + f << contents << endl; + f.close(); - assert(!fs::is_empty("NONEXISTENT_file.txt", ec)); - assert_failure(ec); - assert(!fs::exists("NONEXISTENT_file.txt")); + assert(filesystem::is_regular_file(filename)); + } - assert(fs::last_write_time("NONEXISTENT_file.txt", ec) == fs::file_time_type::min()); - assert_failure(ec); - assert(!fs::exists("NONEXISTENT_file.txt")); + void run_tests() { + (void) filesystem::remove_all("DELME_dir"); + assert(!filesystem::exists("DELME_dir")); + assert(filesystem::create_directory("DELME_dir")); + assert(filesystem::is_directory("DELME_dir")); + + create_file_containing("DELME_dir/DELME_file.txt", "meow"); + create_file_containing("DELME_dir/DELME_old.txt", "disco"); + create_file_containing("DELME_dir/DELME_new.txt", "DoYouWantToBuildASnowman"); + + { + const auto now = filesystem::file_time_type::clock::now(); + filesystem::last_write_time("DELME_dir/DELME_old.txt", now - 1h); + filesystem::last_write_time("DELME_dir/DELME_new.txt", now); + assert(filesystem::last_write_time("DELME_dir/DELME_old.txt") + < filesystem::last_write_time("DELME_dir/DELME_new.txt")); + } + + error_code ec; + + filesystem::copy("DELME_dir", "NONEXISTENT_dir1/NONEXISTENT_dir2", filesystem::copy_options::recursive, ec); + assert_failure(ec); + assert(!filesystem::exists("NONEXISTENT_dir1")); + + assert(!filesystem::copy_file( + "DELME_dir/DELME_file.txt", "DELME_dir/DELME_file.txt", filesystem::copy_options::skip_existing, ec)); + assert_failure(ec); + assert(filesystem::is_regular_file("DELME_dir/DELME_file.txt")); + + assert(!filesystem::copy_file( + "DELME_dir/DELME_old.txt", "DELME_dir/DELME_new.txt", filesystem::copy_options::update_existing, ec)); + assert_success(ec); + assert(filesystem::file_size("DELME_dir/DELME_old.txt") != filesystem::file_size("DELME_dir/DELME_new.txt")); + + assert(filesystem::copy_file( + "DELME_dir/DELME_new.txt", "DELME_dir/DELME_old.txt", filesystem::copy_options::update_existing, ec)); + assert_success(ec); + assert(filesystem::file_size("DELME_dir/DELME_old.txt") == filesystem::file_size("DELME_dir/DELME_new.txt")); + + assert(!filesystem::create_directories("DELME_dir/DELME_file.txt/blah/blah/blah", ec)); + assert_failure(ec); + assert(filesystem::is_regular_file("DELME_dir/DELME_file.txt")); + + assert(!filesystem::is_empty("NONEXISTENT_file.txt", ec)); + assert_failure(ec); + assert(!filesystem::exists("NONEXISTENT_file.txt")); + + assert(filesystem::last_write_time("NONEXISTENT_file.txt", ec) == filesystem::file_time_type::min()); + assert_failure(ec); + assert(!filesystem::exists("NONEXISTENT_file.txt")); + + assert(_putenv_s("TMP", "NONEXISTENT_dir1") == 0); + assert(filesystem::temp_directory_path(ec) == filesystem::path()); + assert_failure(ec); + assert(!filesystem::exists("NONEXISTENT_dir1")); + + assert(filesystem::remove_all("DELME_dir", ec) == 4); + assert_success(ec); + assert(!filesystem::exists("DELME_dir")); + } +} // namespace test_standard +#endif // _HAS_CXX17 - assert(_putenv_s("TMP", "NONEXISTENT_dir1") == 0); - assert(fs::temp_directory_path(ec) == fs::path()); - assert_failure(ec); - assert(!fs::exists("NONEXISTENT_dir1")); +int main() { + test_experimental::run_tests(); - assert(fs::remove_all("DELME_dir", ec) == 3); - assert_success(ec); - assert(!fs::exists("DELME_dir")); +#if _HAS_CXX17 + test_standard::run_tests(); +#endif // _HAS_CXX17 } From d38d89f30a222dc5771036f98a722c8d0a1e9883 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 1 Oct 2025 13:11:03 -0700 Subject: [PATCH 6/7] Extend Dev11_1066931_filesystem_rename_noop to be fully dual-mode, part 1. Guard the inclusion of `` with `_HAS_CXX17`. Introduce `namespace test_experimental` and scope the Experimental namespace alias within it. Extract `Get_child_dir_name()` as it's used by both the Experimental and partial Standard coverage. Remove an unnecessary newline. Change `chrono::system_clock::now()` to `fs::file_time_type::clock::now()`, which works across Experimental, Standard 17, and Standard 20. Adjust the comment to talk about "the file clock". The partial Standard coverage in test_experimental::Test_VSO_171729_disable_recursion_pending_should_not_be_permanent() will be untangled in the next commit. --- .../test.cpp | 603 +++++++++--------- 1 file changed, 306 insertions(+), 297 deletions(-) diff --git a/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp b/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp index 486cae0093b..28132747e3f 100644 --- a/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp +++ b/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp @@ -8,15 +8,17 @@ #include #include #include -#include #include #include #include +#if _HAS_CXX17 +#include +#endif // _HAS_CXX17 + #include using namespace std; -namespace fs = experimental::filesystem; void assert_success(const error_code& ec) { assert(!ec); @@ -52,330 +54,337 @@ void TouchFile(const char* const filename) { WriteToFile("", filename); } -void Cleanup() { - printf("cleaning up... "); - error_code ec; - fs::remove("missing.txt", ec); - assert_success(ec); - fs::remove("missing2.txt", ec); - assert_success(ec); - fs::remove("innocent.txt", ec); - assert_success(ec); - fs::remove("meow_old.txt", ec); - assert_success(ec); - fs::remove("meow_new.txt", ec); - assert_success(ec); - fs::remove("same.txt", ec); - assert_success(ec); - fs::remove("cats.txt", ec); - assert_success(ec); - fs::remove("dogs.txt", ec); - assert_success(ec); - fs::remove_all("subdir", ec); - assert_success(ec); - fs::remove_all("subdir_copied", ec); - assert_success(ec); - puts("done."); +template +PathIsh Get_child_dir_name(const PathIsh& p) { + auto filenameStr = p.filename().native(); + filenameStr.push_back(L'c'); + return p / filenameStr; } -error_code TestRename(const char* const old_p, const char* const new_p) { - printf("attempting to rename \"%s\" to \"%s\"...\n", old_p, new_p); - error_code ec; - fs::rename(old_p, new_p, ec); - return ec; -} +namespace test_experimental { + namespace fs = experimental::filesystem; + + void Cleanup() { + printf("cleaning up... "); + error_code ec; + fs::remove("missing.txt", ec); + assert_success(ec); + fs::remove("missing2.txt", ec); + assert_success(ec); + fs::remove("innocent.txt", ec); + assert_success(ec); + fs::remove("meow_old.txt", ec); + assert_success(ec); + fs::remove("meow_new.txt", ec); + assert_success(ec); + fs::remove("same.txt", ec); + assert_success(ec); + fs::remove("cats.txt", ec); + assert_success(ec); + fs::remove("dogs.txt", ec); + assert_success(ec); + fs::remove_all("subdir", ec); + assert_success(ec); + fs::remove_all("subdir_copied", ec); + assert_success(ec); + puts("done."); + } -void Test_Dev11_1066931_filesystem_rename_noop() { - WriteToFile("innocent", "innocent.txt"); - WriteToFile("meow", "meow_old.txt"); - WriteToFile("same", "same.txt"); - WriteToFile("cats", "cats.txt"); - WriteToFile("dogs", "dogs.txt"); - - - assert(ReadFile("missing.txt") == "NONEXISTENT"); - assert_failure(TestRename("missing.txt", "missing.txt")); - assert(ReadFile("missing.txt") == "NONEXISTENT"); - - assert(ReadFile("missing.txt") == "NONEXISTENT"); - assert(ReadFile("missing2.txt") == "NONEXISTENT"); - assert_failure(TestRename("missing.txt", "missing2.txt")); - assert(ReadFile("missing.txt") == "NONEXISTENT"); - assert(ReadFile("missing2.txt") == "NONEXISTENT"); - - assert(ReadFile("missing.txt") == "NONEXISTENT"); - assert(ReadFile("innocent.txt") == "innocent"); - assert_failure(TestRename("missing.txt", "innocent.txt")); - assert(ReadFile("missing.txt") == "NONEXISTENT"); - assert(ReadFile("innocent.txt") == "innocent"); - - assert(ReadFile("meow_old.txt") == "meow"); - assert(ReadFile("meow_new.txt") == "NONEXISTENT"); - assert_success(TestRename("meow_old.txt", "meow_new.txt")); - assert(ReadFile("meow_old.txt") == "NONEXISTENT"); - assert(ReadFile("meow_new.txt") == "meow"); - - assert(ReadFile("same.txt") == "same"); - assert_success(TestRename("same.txt", "same.txt")); - assert(ReadFile("same.txt") == "same"); - - assert(ReadFile("cats.txt") == "cats"); - assert(ReadFile("dogs.txt") == "dogs"); - assert_success(TestRename("cats.txt", "dogs.txt")); - assert(ReadFile("cats.txt") == "NONEXISTENT"); - assert(ReadFile("dogs.txt") == "cats"); -} + error_code TestRename(const char* const old_p, const char* const new_p) { + printf("attempting to rename \"%s\" to \"%s\"...\n", old_p, new_p); + error_code ec; + fs::rename(old_p, new_p, ec); + return ec; + } -void Test_VSO_121387_filesystem_equivalent_should_accept_directories() { - error_code ec; - fs::create_directory("subdir", ec); - assert_success(ec); - fs::create_directory("subdir/dir1", ec); - assert_success(ec); - fs::create_directory("subdir/dir2", ec); - assert_success(ec); - TouchFile("subdir/file.txt"); - fs::create_hard_link("subdir/file.txt", "subdir/file_link.txt", ec); - assert_success(ec); - - assert(fs::equivalent("subdir/dir2", "subdir/dir2", ec)); - assert_success(ec); - assert(fs::equivalent("subdir/file.txt", "subdir/file.txt", ec)); - assert_success(ec); - - assert(!fs::equivalent("subdir/dir1", "subdir", ec)); - assert_success(ec); - assert(!fs::equivalent("subdir/dir1", "subdir/dir2", ec)); - assert_success(ec); - assert(!fs::equivalent("subdir/dir1", "subdir/file.txt", ec)); - assert_success(ec); - assert(!fs::equivalent("subdir/file.txt", "subdir/dir1", ec)); - assert_success(ec); - - assert(fs::equivalent("subdir/file.txt", "subdir/.././subdir/file.txt", ec)); - assert_success(ec); - assert(fs::equivalent("subdir/dir1", "subdir/.././subdir/dir1", ec)); - assert_success(ec); - - assert(fs::equivalent("subdir/file.txt", "subdir/file_link.txt", ec)); - assert_success(ec); -} + void Test_Dev11_1066931_filesystem_rename_noop() { + WriteToFile("innocent", "innocent.txt"); + WriteToFile("meow", "meow_old.txt"); + WriteToFile("same", "same.txt"); + WriteToFile("cats", "cats.txt"); + WriteToFile("dogs", "dogs.txt"); + + assert(ReadFile("missing.txt") == "NONEXISTENT"); + assert_failure(TestRename("missing.txt", "missing.txt")); + assert(ReadFile("missing.txt") == "NONEXISTENT"); + + assert(ReadFile("missing.txt") == "NONEXISTENT"); + assert(ReadFile("missing2.txt") == "NONEXISTENT"); + assert_failure(TestRename("missing.txt", "missing2.txt")); + assert(ReadFile("missing.txt") == "NONEXISTENT"); + assert(ReadFile("missing2.txt") == "NONEXISTENT"); + + assert(ReadFile("missing.txt") == "NONEXISTENT"); + assert(ReadFile("innocent.txt") == "innocent"); + assert_failure(TestRename("missing.txt", "innocent.txt")); + assert(ReadFile("missing.txt") == "NONEXISTENT"); + assert(ReadFile("innocent.txt") == "innocent"); + + assert(ReadFile("meow_old.txt") == "meow"); + assert(ReadFile("meow_new.txt") == "NONEXISTENT"); + assert_success(TestRename("meow_old.txt", "meow_new.txt")); + assert(ReadFile("meow_old.txt") == "NONEXISTENT"); + assert(ReadFile("meow_new.txt") == "meow"); + + assert(ReadFile("same.txt") == "same"); + assert_success(TestRename("same.txt", "same.txt")); + assert(ReadFile("same.txt") == "same"); + + assert(ReadFile("cats.txt") == "cats"); + assert(ReadFile("dogs.txt") == "dogs"); + assert_success(TestRename("cats.txt", "dogs.txt")); + assert(ReadFile("cats.txt") == "NONEXISTENT"); + assert(ReadFile("dogs.txt") == "cats"); + } -void Test_VSO_121387_filesystem_hard_link_count_should_accept_directories() { - error_code ec; - fs::create_directory("subdir", ec); - assert_success(ec); - fs::create_directory("subdir/dir1", ec); - assert_success(ec); - TouchFile("meow_old.txt"); - fs::create_hard_link("meow_old.txt", "meow_new.txt", ec); - assert_success(ec); - - fs::create_hard_link("subdir/dir1", "subdir/dir2", ec); - assert_failure(ec); // hard links aren't supported for directories on Windows - - assert(fs::hard_link_count("subdir", ec) == 1u); - assert_success(ec); - assert(fs::hard_link_count("meow_old.txt", ec) == 2u); - assert_success(ec); - assert(fs::hard_link_count("meow_new.txt", ec) == 2u); - assert_success(ec); - - assert(fs::hard_link_count("missing.txt", ec) == static_cast(-1)); - assert_failure(ec); -} + void Test_VSO_121387_filesystem_equivalent_should_accept_directories() { + error_code ec; + fs::create_directory("subdir", ec); + assert_success(ec); + fs::create_directory("subdir/dir1", ec); + assert_success(ec); + fs::create_directory("subdir/dir2", ec); + assert_success(ec); + TouchFile("subdir/file.txt"); + fs::create_hard_link("subdir/file.txt", "subdir/file_link.txt", ec); + assert_success(ec); + + assert(fs::equivalent("subdir/dir2", "subdir/dir2", ec)); + assert_success(ec); + assert(fs::equivalent("subdir/file.txt", "subdir/file.txt", ec)); + assert_success(ec); + + assert(!fs::equivalent("subdir/dir1", "subdir", ec)); + assert_success(ec); + assert(!fs::equivalent("subdir/dir1", "subdir/dir2", ec)); + assert_success(ec); + assert(!fs::equivalent("subdir/dir1", "subdir/file.txt", ec)); + assert_success(ec); + assert(!fs::equivalent("subdir/file.txt", "subdir/dir1", ec)); + assert_success(ec); + + assert(fs::equivalent("subdir/file.txt", "subdir/.././subdir/file.txt", ec)); + assert_success(ec); + assert(fs::equivalent("subdir/dir1", "subdir/.././subdir/dir1", ec)); + assert_success(ec); + + assert(fs::equivalent("subdir/file.txt", "subdir/file_link.txt", ec)); + assert_success(ec); + } -void Test_VSO_121387_filesystem_set_write_time_should_accept_directories() { - error_code ec; - fs::create_directory("subdir", ec); - assert_success(ec); - - // Technically speaking the underlying file system may not have a high - // enough resolution for the equality tests here; but in practice our - // target is going to be NTFS or ReFS which have 100ns increment resolution, - // and is >= that which system_clock offers. - auto const expectedTime = chrono::system_clock::now() - 48h; - fs::last_write_time("subdir", expectedTime, ec); - assert_success(ec); - assert(fs::last_write_time("subdir", ec) == expectedTime); - assert_success(ec); - - TouchFile("meow_old.txt"); - fs::last_write_time("meow_old.txt", expectedTime, ec); - assert_success(ec); - assert(fs::last_write_time("meow_old.txt", ec) == expectedTime); - assert_success(ec); - - fs::last_write_time("missing.txt", expectedTime, ec); - assert_failure(ec); - assert(fs::last_write_time("missing.txt", ec) == fs::file_time_type::time_point::min()); - assert_failure(ec); -} + void Test_VSO_121387_filesystem_hard_link_count_should_accept_directories() { + error_code ec; + fs::create_directory("subdir", ec); + assert_success(ec); + fs::create_directory("subdir/dir1", ec); + assert_success(ec); + TouchFile("meow_old.txt"); + fs::create_hard_link("meow_old.txt", "meow_new.txt", ec); + assert_success(ec); + + fs::create_hard_link("subdir/dir1", "subdir/dir2", ec); + assert_failure(ec); // hard links aren't supported for directories on Windows + + assert(fs::hard_link_count("subdir", ec) == 1u); + assert_success(ec); + assert(fs::hard_link_count("meow_old.txt", ec) == 2u); + assert_success(ec); + assert(fs::hard_link_count("meow_new.txt", ec) == 2u); + assert_success(ec); + + assert(fs::hard_link_count("missing.txt", ec) == static_cast(-1)); + assert_failure(ec); + } -void Create_copy_source() { - error_code ec; - fs::create_directory("subdir", ec); - assert_success(ec); - TouchFile("subdir/cats.txt"); - TouchFile("subdir/dogs.txt"); - fs::create_directory("subdir/subsubdir", ec); - assert_success(ec); - TouchFile("subdir/subsubdir/meow.txt"); -} + void Test_VSO_121387_filesystem_set_write_time_should_accept_directories() { + error_code ec; + fs::create_directory("subdir", ec); + assert_success(ec); + + // Technically speaking the underlying file system may not have a high + // enough resolution for the equality tests here; but in practice our + // target is going to be NTFS or ReFS which have 100ns increment resolution, + // and is >= that which the file clock offers. + auto const expectedTime = fs::file_time_type::clock::now() - 48h; + fs::last_write_time("subdir", expectedTime, ec); + assert_success(ec); + assert(fs::last_write_time("subdir", ec) == expectedTime); + assert_success(ec); + + TouchFile("meow_old.txt"); + fs::last_write_time("meow_old.txt", expectedTime, ec); + assert_success(ec); + assert(fs::last_write_time("meow_old.txt", ec) == expectedTime); + assert_success(ec); + + fs::last_write_time("missing.txt", expectedTime, ec); + assert_failure(ec); + assert(fs::last_write_time("missing.txt", ec) == fs::file_time_type::time_point::min()); + assert_failure(ec); + } -void Test_VSO_153113_copy_filename() { - Create_copy_source(); - error_code ec; - fs::create_directory("subdir_copied", ec); - assert_success(ec); - fs::copy("subdir/cats.txt", "subdir_copied", ec); - assert_success(ec); - assert(fs::exists("subdir_copied/cats.txt", ec)); - assert_success(ec); -} + void Create_copy_source() { + error_code ec; + fs::create_directory("subdir", ec); + assert_success(ec); + TouchFile("subdir/cats.txt"); + TouchFile("subdir/dogs.txt"); + fs::create_directory("subdir/subsubdir", ec); + assert_success(ec); + TouchFile("subdir/subsubdir/meow.txt"); + } -void Test_VSO_153113_copy_examples() { - // This example is taken directly from N4100 15.3 [fs.ops.copy]/32 - // (This tests copy_options::_Unspecified_recursion_prevention_tag) - Create_copy_source(); - error_code ec; - fs::copy("subdir", "subdir_copied", ec); - assert_success(ec); - assert(fs::exists("subdir_copied/cats.txt", ec)); - assert_success(ec); - assert(fs::exists("subdir_copied/dogs.txt", ec)); - assert_success(ec); - assert(!fs::exists("subdir_copied/subsubdir", ec)); - assert_success(ec); - - // N4100 15.3 [fs.ops.copy]/34 - fs::remove_all("subdir_copied", ec); - assert_success(ec); - fs::copy("subdir", "subdir_copied", fs::copy_options::recursive, ec); - assert_success(ec); - assert(fs::exists("subdir_copied/cats.txt", ec)); - assert_success(ec); - assert(fs::exists("subdir_copied/dogs.txt", ec)); - assert_success(ec); - assert(fs::exists("subdir_copied/subsubdir/meow.txt", ec)); - assert_success(ec); -} + void Test_VSO_153113_copy_filename() { + Create_copy_source(); + error_code ec; + fs::create_directory("subdir_copied", ec); + assert_success(ec); + fs::copy("subdir/cats.txt", "subdir_copied", ec); + assert_success(ec); + assert(fs::exists("subdir_copied/cats.txt", ec)); + assert_success(ec); + } -template -PathIsh Get_child_dir_name(const PathIsh& p) { - auto filenameStr = p.filename().native(); - filenameStr.push_back(L'c'); - return p / filenameStr; -} + void Test_VSO_153113_copy_examples() { + // This example is taken directly from N4100 15.3 [fs.ops.copy]/32 + // (This tests copy_options::_Unspecified_recursion_prevention_tag) + Create_copy_source(); + error_code ec; + fs::copy("subdir", "subdir_copied", ec); + assert_success(ec); + assert(fs::exists("subdir_copied/cats.txt", ec)); + assert_success(ec); + assert(fs::exists("subdir_copied/dogs.txt", ec)); + assert_success(ec); + assert(!fs::exists("subdir_copied/subsubdir", ec)); + assert_success(ec); + + // N4100 15.3 [fs.ops.copy]/34 + fs::remove_all("subdir_copied", ec); + assert_success(ec); + fs::copy("subdir", "subdir_copied", fs::copy_options::recursive, ec); + assert_success(ec); + assert(fs::exists("subdir_copied/cats.txt", ec)); + assert_success(ec); + assert(fs::exists("subdir_copied/dogs.txt", ec)); + assert_success(ec); + assert(fs::exists("subdir_copied/subsubdir/meow.txt", ec)); + assert_success(ec); + } -void Test_VSO_171729_disable_recursion_pending_should_not_be_permanent() { - error_code ec; - fs::create_directories("subdir/subdir1/subdir1c", ec); - assert_success(ec); - fs::create_directories("subdir/subdir2/subdir2c", ec); - assert_success(ec); - fs::create_directories("subdir/subdir3/subdir3c", ec); - assert_success(ec); + void Test_VSO_171729_disable_recursion_pending_should_not_be_permanent() { + error_code ec; + fs::create_directories("subdir/subdir1/subdir1c", ec); + assert_success(ec); + fs::create_directories("subdir/subdir2/subdir2c", ec); + assert_success(ec); + fs::create_directories("subdir/subdir3/subdir3c", ec); + assert_success(ec); - { - fs::recursive_directory_iterator iter("subdir"); - fs::recursive_directory_iterator end; + { + fs::recursive_directory_iterator iter("subdir"); + fs::recursive_directory_iterator end; - assert("recursive_directory_iterator had no entries" && iter != end); - fs::path badDirName1 = Get_child_dir_name(iter->path()); - iter.disable_recursion_pending(); + assert("recursive_directory_iterator had no entries" && iter != end); + fs::path badDirName1 = Get_child_dir_name(iter->path()); + iter.disable_recursion_pending(); - ++iter; - assert("recursive_directory_iterator had only 1 entry" && iter != end); - assert("disable_recursion_pending didn't work" && iter->path() != badDirName1); - fs::path goodDirName2 = Get_child_dir_name(iter->path()); + ++iter; + assert("recursive_directory_iterator had only 1 entry" && iter != end); + assert("disable_recursion_pending didn't work" && iter->path() != badDirName1); + fs::path goodDirName2 = Get_child_dir_name(iter->path()); - ++iter; - assert("recursive_directory_iterator had only 2 entries" && iter != end); - assert("disable_recursion_pending was permanent" && iter->path() == goodDirName2); + ++iter; + assert("recursive_directory_iterator had only 2 entries" && iter != end); + assert("disable_recursion_pending was permanent" && iter->path() == goodDirName2); - iter.disable_recursion_pending(); - ++iter; + iter.disable_recursion_pending(); + ++iter; - assert("recursive_directory_iterator increment didn't reset recursion_pending" && iter.recursion_pending()); - assert("recursive_directory_iterator didn't have enough entries" && iter != end); + assert("recursive_directory_iterator increment didn't reset recursion_pending" && iter.recursion_pending()); + assert("recursive_directory_iterator didn't have enough entries" && iter != end); - iter.disable_recursion_pending(); - ++iter; + iter.disable_recursion_pending(); + ++iter; - assert("recursive_directory_iterator had too many entries" && iter == end); - } + assert("recursive_directory_iterator had too many entries" && iter == end); + } #if _HAS_CXX17 - { - filesystem::recursive_directory_iterator iter("subdir"); - filesystem::recursive_directory_iterator end; + { + filesystem::recursive_directory_iterator iter("subdir"); + filesystem::recursive_directory_iterator end; - assert("std recursive_directory_iterator had no entries" && iter != end); - filesystem::path badDirName1 = Get_child_dir_name(iter->path()); - iter.disable_recursion_pending(); + assert("std recursive_directory_iterator had no entries" && iter != end); + filesystem::path badDirName1 = Get_child_dir_name(iter->path()); + iter.disable_recursion_pending(); - ++iter; - assert("std recursive_directory_iterator had only 1 entry" && iter != end); - assert("disable_recursion_pending didn't work" && iter->path() != badDirName1); - filesystem::path goodDirName2 = Get_child_dir_name(iter->path()); + ++iter; + assert("std recursive_directory_iterator had only 1 entry" && iter != end); + assert("disable_recursion_pending didn't work" && iter->path() != badDirName1); + filesystem::path goodDirName2 = Get_child_dir_name(iter->path()); - ++iter; - assert("std recursive_directory_iterator had only 2 entries" && iter != end); - assert("disable_recursion_pending was permanent" && iter->path() == goodDirName2); + ++iter; + assert("std recursive_directory_iterator had only 2 entries" && iter != end); + assert("disable_recursion_pending was permanent" && iter->path() == goodDirName2); - iter.disable_recursion_pending(); - iter.pop(); + iter.disable_recursion_pending(); + iter.pop(); - assert("std recursive_directory_iterator pop didn't reset recursion_pending" && iter.recursion_pending()); - assert("std recursive_directory_iterator at end after pop" && iter != end); - filesystem::path goodDirName3 = Get_child_dir_name(iter->path()); + assert("std recursive_directory_iterator pop didn't reset recursion_pending" && iter.recursion_pending()); + assert("std recursive_directory_iterator at end after pop" && iter != end); + filesystem::path goodDirName3 = Get_child_dir_name(iter->path()); - ++iter; - assert("std recursive_directory_iterator at end in pop subdirectory" && iter != end); - assert("disable_recursion_pending was permanent" && iter->path() == goodDirName3); + ++iter; + assert("std recursive_directory_iterator at end in pop subdirectory" && iter != end); + assert("disable_recursion_pending was permanent" && iter->path() == goodDirName3); - ++iter; - assert("std recursive_directory_iterator had too many entries" && iter == end); - } + ++iter; + assert("std recursive_directory_iterator had too many entries" && iter == end); + } #endif // _HAS_CXX17 -} + } + + void run_tests() { + error_code ec; + const auto previousCd = fs::current_path(ec); + assert_success(ec); + const auto testDir = fs::temp_directory_path() / get_test_directory_subname("filesystem_rename_noop"); + printf("changing directory to \"%ls\"\n", testDir.native().c_str()); + fs::create_directory(testDir, ec); + assert_success(ec); + fs::current_path(testDir, ec); + assert_success(ec); + + puts("running test Test_Dev11_1066931_filesystem_rename_noop"); + Test_Dev11_1066931_filesystem_rename_noop(); + Cleanup(); + puts("running test Test_VSO_121387_filesystem_equivalent_should_accept_directories"); + Test_VSO_121387_filesystem_equivalent_should_accept_directories(); + Cleanup(); + puts("running test Test_VSO_121387_filesystem_hard_link_count_should_accept_directories"); + Test_VSO_121387_filesystem_hard_link_count_should_accept_directories(); + Cleanup(); + puts("running test Test_VSO_121387_filesystem_set_write_time_should_accept_directories"); + Test_VSO_121387_filesystem_set_write_time_should_accept_directories(); + Cleanup(); + puts("running test Test_VSO_153113_copy_filename"); + Test_VSO_153113_copy_filename(); + Cleanup(); + puts("running test Test_VSO_153113_copy_examples"); + Test_VSO_153113_copy_examples(); + Cleanup(); + puts("running test Test_VSO_171729_disable_recursion_pending_should_not_be_permanent"); + Test_VSO_171729_disable_recursion_pending_should_not_be_permanent(); + Cleanup(); + + fs::current_path(previousCd, ec); + assert_success(ec); + fs::remove_all(testDir, ec); + assert_success(ec); + } +} // namespace test_experimental int main() { - error_code ec; - const auto previousCd = fs::current_path(ec); - assert_success(ec); - const auto testDir = fs::temp_directory_path() / get_test_directory_subname("filesystem_rename_noop"); - printf("changing directory to \"%ls\"\n", testDir.native().c_str()); - fs::create_directory(testDir, ec); - assert_success(ec); - fs::current_path(testDir, ec); - assert_success(ec); - - puts("running test Test_Dev11_1066931_filesystem_rename_noop"); - Test_Dev11_1066931_filesystem_rename_noop(); - Cleanup(); - puts("running test Test_VSO_121387_filesystem_equivalent_should_accept_directories"); - Test_VSO_121387_filesystem_equivalent_should_accept_directories(); - Cleanup(); - puts("running test Test_VSO_121387_filesystem_hard_link_count_should_accept_directories"); - Test_VSO_121387_filesystem_hard_link_count_should_accept_directories(); - Cleanup(); - puts("running test Test_VSO_121387_filesystem_set_write_time_should_accept_directories"); - Test_VSO_121387_filesystem_set_write_time_should_accept_directories(); - Cleanup(); - puts("running test Test_VSO_153113_copy_filename"); - Test_VSO_153113_copy_filename(); - Cleanup(); - puts("running test Test_VSO_153113_copy_examples"); - Test_VSO_153113_copy_examples(); - Cleanup(); - puts("running test Test_VSO_171729_disable_recursion_pending_should_not_be_permanent"); - Test_VSO_171729_disable_recursion_pending_should_not_be_permanent(); - Cleanup(); - - fs::current_path(previousCd, ec); - assert_success(ec); - fs::remove_all(testDir, ec); - assert_success(ec); + test_experimental::run_tests(); } From 1690764a0d844cff2b48224e4a12b197b38457b1 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 1 Oct 2025 13:37:00 -0700 Subject: [PATCH 7/7] Extend Dev11_1066931_filesystem_rename_noop to be fully dual-mode, part 2. Add `namespace test_standard`, exercising the Standard namespace. Transfer the Standard `recursive_directory_iterator` case here, removing it from `namespace test_experimental`. (Apparently they differ slightly in semantics.) Update the citations in Test_VSO_153113_copy_examples. Drop the mention of "This tests copy_options::_Unspecified_recursion_prevention_tag" as that doesn't apply to our current Standard implementation. --- .../test.cpp | 278 +++++++++++++++++- 1 file changed, 270 insertions(+), 8 deletions(-) diff --git a/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp b/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp index 28132747e3f..a21fb335a6f 100644 --- a/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp +++ b/tests/std/tests/Dev11_1066931_filesystem_rename_noop/test.cpp @@ -309,8 +309,266 @@ namespace test_experimental { assert("recursive_directory_iterator had too many entries" && iter == end); } + } + + void run_tests() { + error_code ec; + const auto previousCd = fs::current_path(ec); + assert_success(ec); + const auto testDir = fs::temp_directory_path() / get_test_directory_subname("filesystem_rename_noop"); + printf("changing directory to \"%ls\"\n", testDir.native().c_str()); + fs::create_directory(testDir, ec); + assert_success(ec); + fs::current_path(testDir, ec); + assert_success(ec); + + puts("running test Test_Dev11_1066931_filesystem_rename_noop"); + Test_Dev11_1066931_filesystem_rename_noop(); + Cleanup(); + puts("running test Test_VSO_121387_filesystem_equivalent_should_accept_directories"); + Test_VSO_121387_filesystem_equivalent_should_accept_directories(); + Cleanup(); + puts("running test Test_VSO_121387_filesystem_hard_link_count_should_accept_directories"); + Test_VSO_121387_filesystem_hard_link_count_should_accept_directories(); + Cleanup(); + puts("running test Test_VSO_121387_filesystem_set_write_time_should_accept_directories"); + Test_VSO_121387_filesystem_set_write_time_should_accept_directories(); + Cleanup(); + puts("running test Test_VSO_153113_copy_filename"); + Test_VSO_153113_copy_filename(); + Cleanup(); + puts("running test Test_VSO_153113_copy_examples"); + Test_VSO_153113_copy_examples(); + Cleanup(); + puts("running test Test_VSO_171729_disable_recursion_pending_should_not_be_permanent"); + Test_VSO_171729_disable_recursion_pending_should_not_be_permanent(); + Cleanup(); + + fs::current_path(previousCd, ec); + assert_success(ec); + fs::remove_all(testDir, ec); + assert_success(ec); + } +} // namespace test_experimental #if _HAS_CXX17 +namespace test_standard { + void Cleanup() { + printf("cleaning up... "); + error_code ec; + filesystem::remove("missing.txt", ec); + assert_success(ec); + filesystem::remove("missing2.txt", ec); + assert_success(ec); + filesystem::remove("innocent.txt", ec); + assert_success(ec); + filesystem::remove("meow_old.txt", ec); + assert_success(ec); + filesystem::remove("meow_new.txt", ec); + assert_success(ec); + filesystem::remove("same.txt", ec); + assert_success(ec); + filesystem::remove("cats.txt", ec); + assert_success(ec); + filesystem::remove("dogs.txt", ec); + assert_success(ec); + filesystem::remove_all("subdir", ec); + assert_success(ec); + filesystem::remove_all("subdir_copied", ec); + assert_success(ec); + puts("done."); + } + + error_code TestRename(const char* const old_p, const char* const new_p) { + printf("attempting to rename \"%s\" to \"%s\"...\n", old_p, new_p); + error_code ec; + filesystem::rename(old_p, new_p, ec); + return ec; + } + + void Test_Dev11_1066931_filesystem_rename_noop() { + WriteToFile("innocent", "innocent.txt"); + WriteToFile("meow", "meow_old.txt"); + WriteToFile("same", "same.txt"); + WriteToFile("cats", "cats.txt"); + WriteToFile("dogs", "dogs.txt"); + + assert(ReadFile("missing.txt") == "NONEXISTENT"); + assert_failure(TestRename("missing.txt", "missing.txt")); + assert(ReadFile("missing.txt") == "NONEXISTENT"); + + assert(ReadFile("missing.txt") == "NONEXISTENT"); + assert(ReadFile("missing2.txt") == "NONEXISTENT"); + assert_failure(TestRename("missing.txt", "missing2.txt")); + assert(ReadFile("missing.txt") == "NONEXISTENT"); + assert(ReadFile("missing2.txt") == "NONEXISTENT"); + + assert(ReadFile("missing.txt") == "NONEXISTENT"); + assert(ReadFile("innocent.txt") == "innocent"); + assert_failure(TestRename("missing.txt", "innocent.txt")); + assert(ReadFile("missing.txt") == "NONEXISTENT"); + assert(ReadFile("innocent.txt") == "innocent"); + + assert(ReadFile("meow_old.txt") == "meow"); + assert(ReadFile("meow_new.txt") == "NONEXISTENT"); + assert_success(TestRename("meow_old.txt", "meow_new.txt")); + assert(ReadFile("meow_old.txt") == "NONEXISTENT"); + assert(ReadFile("meow_new.txt") == "meow"); + + assert(ReadFile("same.txt") == "same"); + assert_success(TestRename("same.txt", "same.txt")); + assert(ReadFile("same.txt") == "same"); + + assert(ReadFile("cats.txt") == "cats"); + assert(ReadFile("dogs.txt") == "dogs"); + assert_success(TestRename("cats.txt", "dogs.txt")); + assert(ReadFile("cats.txt") == "NONEXISTENT"); + assert(ReadFile("dogs.txt") == "cats"); + } + + void Test_VSO_121387_filesystem_equivalent_should_accept_directories() { + error_code ec; + filesystem::create_directory("subdir", ec); + assert_success(ec); + filesystem::create_directory("subdir/dir1", ec); + assert_success(ec); + filesystem::create_directory("subdir/dir2", ec); + assert_success(ec); + TouchFile("subdir/file.txt"); + filesystem::create_hard_link("subdir/file.txt", "subdir/file_link.txt", ec); + assert_success(ec); + + assert(filesystem::equivalent("subdir/dir2", "subdir/dir2", ec)); + assert_success(ec); + assert(filesystem::equivalent("subdir/file.txt", "subdir/file.txt", ec)); + assert_success(ec); + + assert(!filesystem::equivalent("subdir/dir1", "subdir", ec)); + assert_success(ec); + assert(!filesystem::equivalent("subdir/dir1", "subdir/dir2", ec)); + assert_success(ec); + assert(!filesystem::equivalent("subdir/dir1", "subdir/file.txt", ec)); + assert_success(ec); + assert(!filesystem::equivalent("subdir/file.txt", "subdir/dir1", ec)); + assert_success(ec); + + assert(filesystem::equivalent("subdir/file.txt", "subdir/.././subdir/file.txt", ec)); + assert_success(ec); + assert(filesystem::equivalent("subdir/dir1", "subdir/.././subdir/dir1", ec)); + assert_success(ec); + + assert(filesystem::equivalent("subdir/file.txt", "subdir/file_link.txt", ec)); + assert_success(ec); + } + + void Test_VSO_121387_filesystem_hard_link_count_should_accept_directories() { + error_code ec; + filesystem::create_directory("subdir", ec); + assert_success(ec); + filesystem::create_directory("subdir/dir1", ec); + assert_success(ec); + TouchFile("meow_old.txt"); + filesystem::create_hard_link("meow_old.txt", "meow_new.txt", ec); + assert_success(ec); + + filesystem::create_hard_link("subdir/dir1", "subdir/dir2", ec); + assert_failure(ec); // hard links aren't supported for directories on Windows + + assert(filesystem::hard_link_count("subdir", ec) == 1u); + assert_success(ec); + assert(filesystem::hard_link_count("meow_old.txt", ec) == 2u); + assert_success(ec); + assert(filesystem::hard_link_count("meow_new.txt", ec) == 2u); + assert_success(ec); + + assert(filesystem::hard_link_count("missing.txt", ec) == static_cast(-1)); + assert_failure(ec); + } + + void Test_VSO_121387_filesystem_set_write_time_should_accept_directories() { + error_code ec; + filesystem::create_directory("subdir", ec); + assert_success(ec); + + // Technically speaking the underlying file system may not have a high + // enough resolution for the equality tests here; but in practice our + // target is going to be NTFS or ReFS which have 100ns increment resolution, + // and is >= that which the file clock offers. + auto const expectedTime = filesystem::file_time_type::clock::now() - 48h; + filesystem::last_write_time("subdir", expectedTime, ec); + assert_success(ec); + assert(filesystem::last_write_time("subdir", ec) == expectedTime); + assert_success(ec); + + TouchFile("meow_old.txt"); + filesystem::last_write_time("meow_old.txt", expectedTime, ec); + assert_success(ec); + assert(filesystem::last_write_time("meow_old.txt", ec) == expectedTime); + assert_success(ec); + + filesystem::last_write_time("missing.txt", expectedTime, ec); + assert_failure(ec); + assert(filesystem::last_write_time("missing.txt", ec) == filesystem::file_time_type::time_point::min()); + assert_failure(ec); + } + + void Create_copy_source() { + error_code ec; + filesystem::create_directory("subdir", ec); + assert_success(ec); + TouchFile("subdir/cats.txt"); + TouchFile("subdir/dogs.txt"); + filesystem::create_directory("subdir/subsubdir", ec); + assert_success(ec); + TouchFile("subdir/subsubdir/meow.txt"); + } + + void Test_VSO_153113_copy_filename() { + Create_copy_source(); + error_code ec; + filesystem::create_directory("subdir_copied", ec); + assert_success(ec); + filesystem::copy("subdir/cats.txt", "subdir_copied", ec); + assert_success(ec); + assert(filesystem::exists("subdir_copied/cats.txt", ec)); + assert_success(ec); + } + + void Test_VSO_153113_copy_examples() { + // This example is taken directly from N5014 [fs.op.copy]/7: + Create_copy_source(); + error_code ec; + filesystem::copy("subdir", "subdir_copied", ec); + assert_success(ec); + assert(filesystem::exists("subdir_copied/cats.txt", ec)); + assert_success(ec); + assert(filesystem::exists("subdir_copied/dogs.txt", ec)); + assert_success(ec); + assert(!filesystem::exists("subdir_copied/subsubdir", ec)); + assert_success(ec); + + // Also from N5014 [fs.op.copy]/7: + filesystem::remove_all("subdir_copied", ec); + assert_success(ec); + filesystem::copy("subdir", "subdir_copied", filesystem::copy_options::recursive, ec); + assert_success(ec); + assert(filesystem::exists("subdir_copied/cats.txt", ec)); + assert_success(ec); + assert(filesystem::exists("subdir_copied/dogs.txt", ec)); + assert_success(ec); + assert(filesystem::exists("subdir_copied/subsubdir/meow.txt", ec)); + assert_success(ec); + } + + void Test_VSO_171729_disable_recursion_pending_should_not_be_permanent() { + error_code ec; + filesystem::create_directories("subdir/subdir1/subdir1c", ec); + assert_success(ec); + filesystem::create_directories("subdir/subdir2/subdir2c", ec); + assert_success(ec); + filesystem::create_directories("subdir/subdir3/subdir3c", ec); + assert_success(ec); + { filesystem::recursive_directory_iterator iter("subdir"); filesystem::recursive_directory_iterator end; @@ -342,18 +600,17 @@ namespace test_experimental { ++iter; assert("std recursive_directory_iterator had too many entries" && iter == end); } -#endif // _HAS_CXX17 } void run_tests() { error_code ec; - const auto previousCd = fs::current_path(ec); + const auto previousCd = filesystem::current_path(ec); assert_success(ec); - const auto testDir = fs::temp_directory_path() / get_test_directory_subname("filesystem_rename_noop"); + const auto testDir = filesystem::temp_directory_path() / get_test_directory_subname("filesystem_rename_noop"); printf("changing directory to \"%ls\"\n", testDir.native().c_str()); - fs::create_directory(testDir, ec); + filesystem::create_directory(testDir, ec); assert_success(ec); - fs::current_path(testDir, ec); + filesystem::current_path(testDir, ec); assert_success(ec); puts("running test Test_Dev11_1066931_filesystem_rename_noop"); @@ -378,13 +635,18 @@ namespace test_experimental { Test_VSO_171729_disable_recursion_pending_should_not_be_permanent(); Cleanup(); - fs::current_path(previousCd, ec); + filesystem::current_path(previousCd, ec); assert_success(ec); - fs::remove_all(testDir, ec); + filesystem::remove_all(testDir, ec); assert_success(ec); } -} // namespace test_experimental +} // namespace test_standard +#endif // _HAS_CXX17 int main() { test_experimental::run_tests(); + +#if _HAS_CXX17 + test_standard::run_tests(); +#endif // _HAS_CXX17 }