Testing std::filesystem on Windows

Hi,

As a prerequisite to being able to implement std::filesystem for windows in libc++ (something that I guess there's more or less desire to do), the first step would be to make the existing tests work on windows.

There already exists two other implementations of std::filesystem on windows; Microsoft's STL and libstdc++ for mingw. While there are certain corner case differences in how they behave, they agree on most of the vital points.

There are a couple things that differ pretty significantly in std::filesystem on Windows on these implementations, compared to other platforms and implementations, and most of these differenes are mandated:

- fs::path::value_type is wchar_t, and fs::path::string_type is std::wstring. This requires a bit of touch-ups here and there; some are pretty straightforward (just using the right fs::path::*_type instead of hardcoding char/string), but some require a bit more conversion, as a lot of the code in the testsuite really wants to work on test data in std::string form. Instead of forcing everything to agnostic form, I'm often using the string() method to specifically request a std::string.

- preferred_separator is \ instead of /. This pops up in a few tests that do path adjustments. When comparing two fs::path objects using operator==, two paths are considered equal even if they have different separators, but some of the tests use a function PathEq(), that check for exact equality. For those cases, I've changed some occurrances of PathEq() into operator==.

- Many tests want to set permission::none on a directory, in order to make accesses fail, to test for different error cases. Setting permission::none doesn't really work in windows, at least not with the two tested implementations

- Tests that look for specific error codes often fail, but I guess this can be seen as a shortcoming in these implementations.

- While Windows does support symlinks, it's rather limited. (Only processes with elevated privileges can create symlinks, or normal processes can create symlinks on Windows 10 if developer mode is enabled and a special flag is included in the function call to create them.) For now I've just ifdeffed out those bits of the tests (which is a pretty big bit), but I guess I should revisit those bits before trying to upstream those parts.

- Windows filesystem timestamps (and both MS STL and libstdc++) use a 100 nanosecond timebase, with a different epoch than the regular time_t epoch used in values returned by stat().

I've got a patchset locally that makes all tests pass, by either fixing things, or disabling tests that assume thigs that the implementations don't provide. I'll start by trying to upstream the more central and less dirty bits, to get the ball rolling.

// Martin

Hi,

As a prerequisite to being able to implement std::filesystem for windows in libc++ (something that I guess there's more or less desire to do), the first step would be to make the existing tests work on windows.

There already exists two other implementations of std::filesystem on windows; Microsoft's STL and libstdc++ for mingw. While there are certain corner case differences in how they behave, they agree on most of the vital points.

There are a couple things that differ pretty significantly in std::filesystem on Windows on these implementations, compared to other platforms and implementations, and most of these differenes are mandated:

- fs::path::value_type is wchar_t, and fs::path::string_type is std::wstring. This requires a bit of touch-ups here and there; some are pretty straightforward (just using the right fs::path::*_type instead of hardcoding char/string), but some require a bit more conversion, as a lot of the code in the testsuite really wants to work on test data in std::string form. Instead of forcing everything to agnostic form, I'm often using the string() method to specifically request a std::string.

Let's fix those.

- preferred_separator is \ instead of /. This pops up in a few tests that do path adjustments. When comparing two fs::path objects using operator==, two paths are considered equal even if they have different separators, but some of the tests use a function PathEq(), that check for exact equality. For those cases, I've changed some occurrances of PathEq() into operator==.

Same.

- Many tests want to set permission::none on a directory, in order to make accesses fail, to test for different error cases. Setting permission::none doesn't really work in windows, at least not with the two tested implementations

Let's find another way to write those tests. They also cause all kinds of issues when running the tests as a root user, and also when running the tests from elsewhere than /tmp. Even after playing around a bit, I don't understand exactly what's special with /tmp that makes these tests pass, but it's really annoying that they're relying on that.

- Tests that look for specific error codes often fail, but I guess this can be seen as a shortcoming in these implementations.

- While Windows does support symlinks, it's rather limited. (Only processes with elevated privileges can create symlinks, or normal processes can create symlinks on Windows 10 if developer mode is enabled and a special flag is included in the function call to create them.) For now I've just ifdeffed out those bits of the tests (which is a pretty big bit), but I guess I should revisit those bits before trying to upstream those parts.

- Windows filesystem timestamps (and both MS STL and libstdc++) use a 100 nanosecond timebase, with a different epoch than the regular time_t epoch used in values returned by stat().

I've got a patchset locally that makes all tests pass, by either fixing things, or disabling tests that assume thigs that the implementations don't provide. I'll start by trying to upstream the more central and less dirty bits, to get the ball rolling.

Excellent, thanks for working on this. If the filesystem tests pass on Windows and with libstdc++, it means they might have an incentive to contribute to the test suite.

Louis

[Martin]

I've got a patchset locally that makes all tests pass, by either fixing things,
or disabling tests that assume thigs that the implementations don't provide.

[Louis]

Excellent, thanks for working on this. If the filesystem tests pass on Windows
and with libstdc++, it means they might have an incentive to contribute to the test suite.

Yeah, that's awesome, thank you! We've been skipping the filesystem tests (see https://github.com/microsoft/STL/blob/39eb812426167fc7955005b53b28d696c50e8b61/tests/libcxx/skipped_tests.txt#L71-L187 ) but we'd love to enable them and report any future Windows-specific issues we encounter.

Our own filesystem tests (Apache-2.0 WITH LLVM-exception, as usual) are here: https://github.com/microsoft/STL/blob/39eb812426167fc7955005b53b28d696c50e8b61/tests/std/tests/P0218R1_filesystem/test.cpp I don't immediately know how we could contribute this to libcxx given the difference in our test strategies (where libcxx typically has one test per section and MSVC typically has one test per paper, leading to large test cases like this one), but perhaps the various parts testing Windows paths could be useful to you?

Thanks,
STL

Thanks for the link! I might have a look at that if I get to that point of having things otherwise mostly working. The libc++ fs tests do have some amount of tests for the path handling operations, that I've got locally modified to match what MS STL returns, but your tests of that aspect do indeed seem more exhaustive.

// Martin

Thanks for the pointer here - those tests did point out a handful of windows specific behaviours that I had missed by just testing my implementation against libc++'s tests (with more yet-unmerged patches to make those tests work with the STL).

Also the test, being just one single file, had the upside of being much faster to compile and test than libc++'s, split up over more files...

At this point, I have an implementation for windows that passes libc++'s tests to the same degree as the STL (i.e. with some test references changed to what's expected for windows, and some bits disabled), and passes most of the STL's filesystem test as well.

I'll start upstreaming the implementation soon. Unfortunately, it's a pretty large change. I have it split up over a number of patches, split by topic/area to keep them on-topic. As the libc++ filesystem implementation doesn't compile for windows as-is, it doesn't actually compile successfully until after the first 20 or so patches.

(From the get-go it wouldn't take that many tweaks to make it compile, but the path class is supposed to be based on wchar_t, and switching that over breaks almost all of the existing code in src/filesystem/operations.cpp. So the following patches fix that up to use corresponding filesystem functions that take wchar_t, and in many cases, using windows native functions instead of posix-lookalike functions that don't behave exactly as needed in many cases, especially relating to symlinks.)

// Martin