diff options
author | Eric Fiselier <eric@efcs.ca> | 2018-07-27 03:07:09 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2018-07-27 03:07:09 +0000 |
commit | 998a5c88312066fcc2b2de1358edc76587611354 (patch) | |
tree | f3e74cc7ce9e55f30e7019dd6e379571c91aaa1f /libcxx/test/std/input.output/filesystems | |
parent | 567485a72ff71e34c9e25aaab8eecfda3aa6cd62 (diff) | |
download | bcm5719-llvm-998a5c88312066fcc2b2de1358edc76587611354.tar.gz bcm5719-llvm-998a5c88312066fcc2b2de1358edc76587611354.zip |
Implement <filesystem>
This patch implements the <filesystem> header and uses that
to provide <experimental/filesystem>.
Unlike other standard headers, the symbols needed for <filesystem>
have not yet been placed in libc++.so. Instead they live in the
new libc++fs.a library. Users of filesystem are required to link this
library. (Also note that libc++experimental no longer contains the
definition of <experimental/filesystem>, which now requires linking libc++fs).
The reason for keeping <filesystem> out of the dylib for now is that
it's still somewhat experimental, and the possibility of requiring an
ABI breaking change is very real. In the future the symbols will likely
be moved into the dylib, or the dylib will be made to link libc++fs automagically).
Note that moving the symbols out of libc++experimental may break user builds
until they update to -lc++fs. This should be OK, because the experimental
library provides no stability guarantees. However, I plan on looking into
ways we can force libc++experimental to automagically link libc++fs.
In order to use a single implementation and set of tests for <filesystem>, it
has been placed in a special `__fs` namespace. This namespace is inline in
C++17 onward, but not before that. As such implementation is available
in C++11 onward, but no filesystem namespace is present "directly", and
as such name conflicts shouldn't occur in C++11 or C++14.
llvm-svn: 338093
Diffstat (limited to 'libcxx/test/std/input.output/filesystems')
145 files changed, 14043 insertions, 0 deletions
diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/bad_symlink b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/bad_symlink new file mode 120000 index 00000000000..76646beed5e --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/bad_symlink @@ -0,0 +1 @@ +dne
\ No newline at end of file diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/afile3 b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/afile3 new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/afile3 diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/dir3/file5 b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/dir3/file5 new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/dir3/file5 diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/file4 b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/file4 new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/file4 diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/symlink_to_dir3 b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/symlink_to_dir3 new file mode 120000 index 00000000000..39791395262 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/symlink_to_dir3 @@ -0,0 +1 @@ +dir3
\ No newline at end of file diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/file1 b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/file1 new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/file1 diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/file2 b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/file2 new file mode 100644 index 00000000000..44834e58673 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/file2 @@ -0,0 +1 @@ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
\ No newline at end of file diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/empty_file b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/empty_file new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/empty_file diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/non_empty_file b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/non_empty_file new file mode 100644 index 00000000000..44834e58673 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/non_empty_file @@ -0,0 +1 @@ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
\ No newline at end of file diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_dir b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_dir new file mode 120000 index 00000000000..df490f837a8 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_dir @@ -0,0 +1 @@ +dir1
\ No newline at end of file diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_empty_file b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_empty_file new file mode 120000 index 00000000000..b79b689fc85 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_empty_file @@ -0,0 +1 @@ +empty_file
\ No newline at end of file diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/copy.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/copy.pass.cpp new file mode 100644 index 00000000000..a81d5ea7960 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/copy.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// directory_entry(const directory_entry&) = default; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" +#include "test_convertible.hpp" + +TEST_SUITE(directory_entry_path_ctor_suite) + +TEST_CASE(copy_ctor) { + using namespace fs; + // Copy + { + static_assert(std::is_copy_constructible<directory_entry>::value, + "directory_entry must be copy constructible"); + static_assert(!std::is_nothrow_copy_constructible<directory_entry>::value, + "directory_entry's copy constructor cannot be noexcept"); + const path p("foo/bar/baz"); + const directory_entry e(p); + assert(e.path() == p); + directory_entry e2(e); + assert(e.path() == p); + assert(e2.path() == p); + } +} + +TEST_CASE(copy_ctor_copies_cache) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path sym = env.create_symlink("dir/file", "sym"); + + { + directory_entry ent(sym); + + fs::remove(sym); + + directory_entry ent_cp(ent); + TEST_CHECK(ent_cp.path() == sym); + TEST_CHECK(ent_cp.is_symlink()); + } + + { + directory_entry ent(file); + + fs::remove(file); + + directory_entry ent_cp(ent); + TEST_CHECK(ent_cp.path() == file); + TEST_CHECK(ent_cp.is_regular_file()); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/copy_assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/copy_assign.pass.cpp new file mode 100644 index 00000000000..204207276cb --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/copy_assign.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// directory_entry& operator=(directory_entry const&) = default; +// directory_entry& operator=(directory_entry&&) noexcept = default; +// void assign(path const&); +// void replace_filename(path const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +TEST_SUITE(directory_entry_ctor_suite) + +TEST_CASE(test_copy_assign_operator) { + using namespace fs; + // Copy + { + static_assert(std::is_copy_assignable<directory_entry>::value, + "directory_entry must be copy assignable"); + static_assert(!std::is_nothrow_copy_assignable<directory_entry>::value, + "directory_entry's copy assignment cannot be noexcept"); + const path p("foo/bar/baz"); + const path p2("abc"); + const directory_entry e(p); + directory_entry e2; + assert(e.path() == p && e2.path() == path()); + e2 = e; + assert(e.path() == p && e2.path() == p); + directory_entry e3(p2); + e2 = e3; + assert(e2.path() == p2 && e3.path() == p2); + } +} + +TEST_CASE(copy_assign_copies_cache) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path sym = env.create_symlink("dir/file", "sym"); + + { + directory_entry ent(sym); + + fs::remove(sym); + + directory_entry ent_cp; + ent_cp = ent; + TEST_CHECK(ent_cp.path() == sym); + TEST_CHECK(ent_cp.is_symlink()); + } + + { + directory_entry ent(file); + + fs::remove(file); + + directory_entry ent_cp; + ent_cp = ent; + TEST_CHECK(ent_cp.path() == file); + TEST_CHECK(ent_cp.is_regular_file()); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/default.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/default.pass.cpp new file mode 100644 index 00000000000..1d83cc69d6c --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/default.pass.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// directory_entry() noexcept = default; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +int main() { + using namespace fs; + // Default + { + static_assert(std::is_nothrow_default_constructible<directory_entry>::value, + "directory_entry must have a nothrow default constructor"); + directory_entry e; + assert(e.path() == path()); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/default_const.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/default_const.pass.cpp new file mode 100644 index 00000000000..240541d203d --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/default_const.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 +// XFAIL: apple-clang-7, clang-3.7, clang-3.8 + +// <filesystem> + +// class directory_entry + +// directory_entry() noexcept = default; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +int main() { + using namespace fs; + // Default + { + static_assert(std::is_nothrow_default_constructible<directory_entry>::value, + "directory_entry must have a nothrow default constructor"); + const directory_entry e; + assert(e.path() == path()); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/move.pass.cpp new file mode 100644 index 00000000000..238a8549ea3 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/move.pass.cpp @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// directory_entry(directory_entry&&) noexcept = default; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" +#include "test_convertible.hpp" + +TEST_SUITE(directory_entry_path_ctor_suite) + +TEST_CASE(move_ctor) { + using namespace fs; + // Move + { + static_assert(std::is_nothrow_move_constructible<directory_entry>::value, + "directory_entry must be nothrow move constructible"); + const path p("foo/bar/baz"); + directory_entry e(p); + assert(e.path() == p); + directory_entry e2(std::move(e)); + assert(e2.path() == p); + assert(e.path() != p); // Testing moved from state. + } +} + +TEST_CASE(move_ctor_copies_cache) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path sym = env.create_symlink("dir/file", "sym"); + + { + directory_entry ent(sym); + + fs::remove(sym); + + directory_entry ent_cp(std::move(ent)); + TEST_CHECK(ent_cp.path() == sym); + TEST_CHECK(ent_cp.is_symlink()); + } + + { + directory_entry ent(file); + + fs::remove(file); + + directory_entry ent_cp(std::move(ent)); + TEST_CHECK(ent_cp.path() == file); + TEST_CHECK(ent_cp.is_regular_file()); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/move_assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/move_assign.pass.cpp new file mode 100644 index 00000000000..f104980c9c4 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/move_assign.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// directory_entry& operator=(directory_entry const&) = default; +// directory_entry& operator=(directory_entry&&) noexcept = default; +// void assign(path const&); +// void replace_filename(path const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +TEST_SUITE(directory_entry_ctor_suite) + +TEST_CASE(test_move_assign_operator) { + using namespace fs; + // Copy + { + static_assert(std::is_nothrow_move_assignable<directory_entry>::value, + "directory_entry is noexcept move assignable"); + const path p("foo/bar/baz"); + const path p2("abc"); + directory_entry e(p); + directory_entry e2(p2); + assert(e.path() == p && e2.path() == p2); + e2 = std::move(e); + assert(e2.path() == p); + assert(e.path() != p); // testing moved from state + } +} + +TEST_CASE(move_assign_copies_cache) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path sym = env.create_symlink("dir/file", "sym"); + + { + directory_entry ent(sym); + + fs::remove(sym); + + directory_entry ent_cp; + ent_cp = std::move(ent); + TEST_CHECK(ent_cp.path() == sym); + TEST_CHECK(ent_cp.is_symlink()); + } + + { + directory_entry ent(file); + + fs::remove(file); + + directory_entry ent_cp; + ent_cp = std::move(ent); + TEST_CHECK(ent_cp.path() == file); + TEST_CHECK(ent_cp.is_regular_file()); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp new file mode 100644 index 00000000000..b06bd129384 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp @@ -0,0 +1,182 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// explicit directory_entry(const path); +// directory_entry(const path&, error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" +#include "test_convertible.hpp" + +TEST_SUITE(directory_entry_path_ctor_suite) + +TEST_CASE(path_ctor) { + using namespace fs; + { + static_assert(std::is_constructible<directory_entry, const path&>::value, + "directory_entry must be constructible from path"); + static_assert( + !std::is_nothrow_constructible<directory_entry, const path&>::value, + "directory_entry constructor should not be noexcept"); + static_assert(!std::is_convertible<path const&, directory_entry>::value, + "directory_entry constructor should be explicit"); + } + { + const path p("foo/bar/baz"); + const directory_entry e(p); + TEST_CHECK(e.path() == p); + } +} + +TEST_CASE(path_ec_ctor) { + using namespace fs; + { + static_assert( + std::is_constructible<directory_entry, const path&, + std::error_code&>::value, + "directory_entry must be constructible from path and error_code"); + static_assert(!std::is_nothrow_constructible<directory_entry, const path&, + std::error_code&>::value, + "directory_entry constructor should not be noexcept"); + static_assert( + test_convertible<directory_entry, const path&, std::error_code&>(), + "directory_entry constructor should not be explicit"); + } + { + std::error_code ec = GetTestEC(); + const directory_entry e(StaticEnv::File, ec); + TEST_CHECK(e.path() == StaticEnv::File); + TEST_CHECK(!ec); + } + { + const path p("foo/bar/baz"); + std::error_code ec = GetTestEC(); + const directory_entry e(p, ec); + TEST_CHECK(e.path() == p); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + } +} + +TEST_CASE(path_ctor_calls_refresh) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path sym = env.create_symlink("dir/file", "sym"); + + { + directory_entry ent(file); + std::error_code ec = GetTestEC(); + directory_entry ent_ec(file, ec); + TEST_CHECK(!ec); + + LIBCPP_ONLY(remove(file)); + + TEST_CHECK(ent.exists()); + TEST_CHECK(ent_ec.exists()); + + TEST_CHECK(ent.file_size() == 42); + TEST_CHECK(ent_ec.file_size() == 42); + } + + env.create_file("dir/file", 101); + + { + directory_entry ent(sym); + std::error_code ec = GetTestEC(); + directory_entry ent_ec(sym, ec); + TEST_CHECK(!ec); + + LIBCPP_ONLY(remove(file)); + LIBCPP_ONLY(remove(sym)); + + TEST_CHECK(ent.is_symlink()); + TEST_CHECK(ent_ec.is_symlink()); + + TEST_CHECK(ent.is_regular_file()); + TEST_CHECK(ent_ec.is_regular_file()); + + TEST_CHECK(ent.file_size() == 101); + TEST_CHECK(ent_ec.file_size() == 101); + } +} + +TEST_CASE(path_ctor_dne) { + using namespace fs; + + { + std::error_code ec = GetTestEC(); + directory_entry ent(StaticEnv::DNE, ec); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + TEST_CHECK(ent.path() == StaticEnv::DNE); + } + // don't report dead symlinks as an error. + { + std::error_code ec = GetTestEC(); + directory_entry ent(StaticEnv::BadSymlink, ec); + TEST_CHECK(!ec); + TEST_CHECK(ent.path() == StaticEnv::BadSymlink); + } + // DNE does not cause the constructor to throw + { + directory_entry ent(StaticEnv::DNE); + TEST_CHECK(ent.path() == StaticEnv::DNE); + + directory_entry ent_two(StaticEnv::BadSymlink); + TEST_CHECK(ent_two.path() == StaticEnv::BadSymlink); + } +} + +TEST_CASE(path_ctor_cannot_resolve) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path file_out_of_dir = env.create_file("file1", 101); + const path sym_out_of_dir = env.create_symlink("dir/file", "sym"); + const path sym_in_dir = env.create_symlink("dir/file1", "dir/sym2"); + permissions(dir, perms::none); + + { + std::error_code ec = GetTestEC(); + directory_entry ent(file, ec); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + TEST_CHECK(ent.path() == file); + } + { + std::error_code ec = GetTestEC(); + directory_entry ent(sym_in_dir, ec); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + TEST_CHECK(ent.path() == sym_in_dir); + } + { + std::error_code ec = GetTestEC(); + directory_entry ent(sym_out_of_dir, ec); + TEST_CHECK(!ec); + TEST_CHECK(ent.path() == sym_out_of_dir); + } + { + TEST_CHECK_NO_THROW(directory_entry(file)); + TEST_CHECK_NO_THROW(directory_entry(sym_in_dir)); + TEST_CHECK_NO_THROW(directory_entry(sym_out_of_dir)); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp new file mode 100644 index 00000000000..d19c8c2ad87 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// directory_entry& operator=(directory_entry const&) = default; +// directory_entry& operator=(directory_entry&&) noexcept = default; +// void assign(path const&); +// void replace_filename(path const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +TEST_SUITE(directory_entry_mods_suite) + +TEST_CASE(test_path_assign_method) { + using namespace fs; + const path p("foo/bar/baz"); + const path p2("abc"); + directory_entry e(p); + { + static_assert(std::is_same<decltype(e.assign(p)), void>::value, + "return type should be void"); + static_assert(noexcept(e.assign(p)) == false, + "operation must not be noexcept"); + } + { + TEST_CHECK(e.path() == p); + e.assign(p2); + TEST_CHECK(e.path() == p2 && e.path() != p); + e.assign(p); + TEST_CHECK(e.path() == p && e.path() != p2); + } +} + +TEST_CASE(test_path_assign_ec_method) { + using namespace fs; + const path p("foo/bar/baz"); + const path p2("abc"); + { + std::error_code ec; + directory_entry e(p); + static_assert(std::is_same<decltype(e.assign(p, ec)), void>::value, + "return type should be void"); + static_assert(noexcept(e.assign(p, ec)) == false, + "operation must not be noexcept"); + } + { + directory_entry ent(p); + std::error_code ec = GetTestEC(); + ent.assign(p2, ec); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + TEST_CHECK(ent.path() == p2); + } +} + +TEST_CASE(test_assign_calls_refresh) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path sym = env.create_symlink("dir/file", "sym"); + + { + directory_entry ent; + ent.assign(file); + + // removing the file demonstrates that the values where cached previously. + LIBCPP_ONLY(remove(file)); + + TEST_CHECK(ent.is_regular_file()); + } + env.create_file("dir/file", 101); + { + directory_entry ent; + ent.assign(sym); + + LIBCPP_ONLY(remove(file)); + LIBCPP_ONLY(remove(sym)); + + TEST_CHECK(ent.is_symlink()); + TEST_CHECK(ent.is_regular_file()); + } +} + +TEST_CASE(test_assign_propagates_error) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path sym_out_of_dir = env.create_symlink("dir/file", "sym"); + const path file_out_of_dir = env.create_file("file1"); + const path sym_in_dir = env.create_symlink("file1", "dir/sym1"); + + permissions(dir, perms::none); + + { + directory_entry ent; + std::error_code ec = GetTestEC(); + ent.assign(file, ec); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + } + { + directory_entry ent; + std::error_code ec = GetTestEC(); + ent.assign(sym_in_dir, ec); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + } + { + directory_entry ent; + std::error_code ec = GetTestEC(); + ent.assign(sym_out_of_dir, ec); + TEST_CHECK(!ec); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp new file mode 100644 index 00000000000..575f0d59b4c --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp @@ -0,0 +1,341 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// directory_entry& operator=(directory_entry const&) = default; +// directory_entry& operator=(directory_entry&&) noexcept = default; +// void assign(path const&); +// void replace_filename(path const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +TEST_SUITE(directory_entry_mods_suite) + +TEST_CASE(test_refresh_method) { + using namespace fs; + { + directory_entry e; + static_assert(noexcept(e.refresh()) == false, + "operation cannot be noexcept"); + static_assert(std::is_same<decltype(e.refresh()), void>::value, + "operation must return void"); + } + { + directory_entry e; + e.refresh(); + TEST_CHECK(!e.exists()); + } +} + +TEST_CASE(test_refresh_ec_method) { + using namespace fs; + { + directory_entry e; + std::error_code ec; + static_assert(noexcept(e.refresh(ec)), "operation should be noexcept"); + static_assert(std::is_same<decltype(e.refresh(ec)), void>::value, + "operation must return void"); + } + { + directory_entry e; + std::error_code ec = GetTestEC(); + e.refresh(ec); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + } +} + +TEST_CASE(refresh_on_file_dne) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + + const perms old_perms = status(dir).permissions(); + + // test file doesn't exist + { + directory_entry ent(file); + remove(file); + TEST_CHECK(ent.exists()); + + ent.refresh(); + + permissions(dir, perms::none); + TEST_CHECK(!ent.exists()); + } + permissions(dir, old_perms); + env.create_file("dir/file", 101); + { + directory_entry ent(file); + remove(file); + TEST_CHECK(ent.exists()); + + std::error_code ec = GetTestEC(); + ent.refresh(ec); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + permissions(dir, perms::none); + TEST_CHECK(!ent.exists()); + } +} + +void remove_if_exists(const fs::path& p) { + std::error_code ec; + remove(p, ec); +} + +TEST_CASE(refresh_on_bad_symlink) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path sym = env.create_symlink("dir/file", "sym"); + + const perms old_perms = status(dir).permissions(); + + // test file doesn't exist + { + directory_entry ent(sym); + LIBCPP_ONLY(remove(file)); + TEST_CHECK(ent.is_symlink()); + TEST_CHECK(ent.is_regular_file()); + TEST_CHECK(ent.exists()); + + remove_if_exists(file); + ent.refresh(); + + LIBCPP_ONLY(permissions(dir, perms::none)); + TEST_CHECK(ent.is_symlink()); + TEST_CHECK(!ent.is_regular_file()); + TEST_CHECK(!ent.exists()); + } + permissions(dir, old_perms); + env.create_file("dir/file", 101); + { + directory_entry ent(sym); + LIBCPP_ONLY(remove(file)); + TEST_CHECK(ent.is_symlink()); + TEST_CHECK(ent.is_regular_file()); + TEST_CHECK(ent.exists()); + + remove_if_exists(file); + + std::error_code ec = GetTestEC(); + ent.refresh(ec); + TEST_CHECK(!ec); // we don't report bad symlinks as an error. + + LIBCPP_ONLY(permissions(dir, perms::none)); + TEST_CHECK(!ent.exists()); + } +} + +TEST_CASE(refresh_cannot_resolve) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path file_out_of_dir = env.create_file("file1", 99); + const path sym_out_of_dir = env.create_symlink("dir/file", "sym"); + const path sym_in_dir = env.create_symlink("file1", "dir/sym1"); + perms old_perms = status(dir).permissions(); + + { + directory_entry ent(file); + permissions(dir, perms::none); + + TEST_CHECK(ent.is_regular_file()); + + std::error_code ec = GetTestEC(); + ent.refresh(ec); + + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + TEST_CHECK(ent.path() == file); + + ExceptionChecker Checker(file, std::errc::permission_denied, + "directory_entry::refresh"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh()); + } + permissions(dir, old_perms); + { + directory_entry ent(sym_in_dir); + permissions(dir, perms::none); + TEST_CHECK(ent.is_symlink()); + + std::error_code ec = GetTestEC(); + ent.refresh(ec); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + TEST_CHECK(ent.path() == sym_in_dir); + + ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied, + "directory_entry::refresh"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh()); + } + permissions(dir, old_perms); + { + directory_entry ent(sym_out_of_dir); + permissions(dir, perms::none); + TEST_CHECK(ent.is_symlink()); + + // Failure to resolve the linked entity due to permissions is not + // reported as an error. + std::error_code ec = GetTestEC(); + ent.refresh(ec); + TEST_CHECK(!ec); + TEST_CHECK(ent.is_symlink()); + + ec = GetTestEC(); + TEST_CHECK(ent.exists(ec) == false); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + TEST_CHECK(ent.path() == sym_out_of_dir); + } + permissions(dir, old_perms); + { + directory_entry ent_file(file); + directory_entry ent_sym(sym_in_dir); + directory_entry ent_sym2(sym_out_of_dir); + permissions(dir, perms::none); + ((void)ent_file); + ((void)ent_sym); + + TEST_CHECK_THROW(filesystem_error, ent_file.refresh()); + TEST_CHECK_THROW(filesystem_error, ent_sym.refresh()); + TEST_CHECK_NO_THROW(ent_sym2); + } +} + +TEST_CASE(refresh_doesnt_throw_on_dne_but_reports_it) { + using namespace fs; + scoped_test_env env; + + const path file = env.create_file("file1", 42); + const path sym = env.create_symlink("file1", "sym"); + + { + directory_entry ent(file); + TEST_CHECK(ent.file_size() == 42); + + remove(file); + + std::error_code ec = GetTestEC(); + ent.refresh(ec); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + TEST_CHECK_NO_THROW(ent.refresh()); + + ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + // doesn't throw! + TEST_CHECK_THROW(filesystem_error, ent.file_size()); + } + env.create_file("file1", 99); + { + directory_entry ent(sym); + TEST_CHECK(ent.is_symlink()); + TEST_CHECK(ent.is_regular_file()); + TEST_CHECK(ent.file_size() == 99); + + remove(file); + + std::error_code ec = GetTestEC(); + ent.refresh(ec); + TEST_CHECK(!ec); + + ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + TEST_CHECK_THROW(filesystem_error, ent.file_size()); + } +} + +TEST_CASE(access_cache_after_refresh_fails) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path file_out_of_dir = env.create_file("file1", 101); + const path sym = env.create_symlink("dir/file", "sym"); + const path sym_in_dir = env.create_symlink("dir/file", "dir/sym2"); + + const perms old_perms = status(dir).permissions(); + +#define CHECK_ACCESS(func, expect) \ + ec = GetTestEC(); \ + TEST_CHECK(ent.func(ec) == expect); \ + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)) + + // test file doesn't exist + { + directory_entry ent(file); + + TEST_CHECK(!ent.is_symlink()); + TEST_CHECK(ent.is_regular_file()); + TEST_CHECK(ent.exists()); + + permissions(dir, perms::none); + std::error_code ec = GetTestEC(); + ent.refresh(ec); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + CHECK_ACCESS(exists, false); + CHECK_ACCESS(is_symlink, false); + CHECK_ACCESS(last_write_time, file_time_type::min()); + CHECK_ACCESS(hard_link_count, uintmax_t(-1)); + } + permissions(dir, old_perms); + { + directory_entry ent(sym_in_dir); + TEST_CHECK(ent.is_symlink()); + TEST_CHECK(ent.is_regular_file()); + TEST_CHECK(ent.exists()); + + permissions(dir, perms::none); + std::error_code ec = GetTestEC(); + ent.refresh(ec); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + CHECK_ACCESS(exists, false); + CHECK_ACCESS(is_symlink, false); + CHECK_ACCESS(last_write_time, file_time_type::min()); + CHECK_ACCESS(hard_link_count, uintmax_t(-1)); + } + permissions(dir, old_perms); + { + directory_entry ent(sym); + TEST_CHECK(ent.is_symlink()); + TEST_CHECK(ent.is_regular_file()); + TEST_CHECK(ent.exists()); + + permissions(dir, perms::none); + std::error_code ec = GetTestEC(); + ent.refresh(ec); + TEST_CHECK(!ec); + TEST_CHECK(ent.is_symlink()); + + CHECK_ACCESS(exists, false); + CHECK_ACCESS(is_regular_file, false); + CHECK_ACCESS(last_write_time, file_time_type::min()); + CHECK_ACCESS(hard_link_count, uintmax_t(-1)); + } +#undef CHECK_ACCESS +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp new file mode 100644 index 00000000000..4a9b76d6375 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp @@ -0,0 +1,169 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// directory_entry& operator=(directory_entry const&) = default; +// directory_entry& operator=(directory_entry&&) noexcept = default; +// void assign(path const&); +// void replace_filename(path const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +TEST_SUITE(directory_entry_mods_suite) + +TEST_CASE(test_replace_filename_method) { + using namespace fs; + + { + directory_entry e; + path replace; + static_assert(noexcept(e.replace_filename(replace)) == false, + "operation cannot be noexcept"); + static_assert( + std::is_same<decltype(e.replace_filename(replace)), void>::value, + "operation must return void"); + } + { + const path p("/path/to/foo.exe"); + const path replace("bar.out"); + const path expect("/path/to/bar.out"); + directory_entry e(p); + TEST_CHECK(e.path() == p); + e.replace_filename(replace); + TEST_CHECK(e.path() == expect); + } +} + +TEST_CASE(test_replace_filename_ec_method) { + using namespace fs; + + { + directory_entry e; + path replace; + std::error_code ec; + static_assert(noexcept(e.replace_filename(replace, ec)) == false, + "operation cannot be noexcept"); + static_assert( + std::is_same<decltype(e.replace_filename(replace, ec)), void>::value, + "operation must return void"); + } + { + const path p("/path/to/foo.exe"); + const path replace("bar.out"); + const path expect("/path/to/bar.out"); + directory_entry e(p); + TEST_CHECK(e.path() == p); + std::error_code ec = GetTestEC(); + e.replace_filename(replace, ec); + TEST_CHECK(e.path() == expect); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + } + { + const path p = StaticEnv::EmptyFile; + const path expect = StaticEnv::NonEmptyFile; + const path replace = StaticEnv::NonEmptyFile.filename(); + TEST_REQUIRE(expect.parent_path() == p.parent_path()); + directory_entry e(p); + TEST_CHECK(e.path() == p); + std::error_code ec = GetTestEC(); + e.replace_filename(replace, ec); + TEST_CHECK(e.path() == expect); + TEST_CHECK(!ec); + } +} + +TEST_CASE(test_replace_filename_calls_refresh) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path file_two = env.create_file("dir/file_two", 101); + const path sym = env.create_symlink("dir/file", "sym"); + const path sym_two = env.create_symlink("dir/file_two", "sym_two"); + + { + directory_entry ent(file); + ent.replace_filename(file_two.filename()); + TEST_REQUIRE(ent.path() == file_two); + + // removing the file demonstrates that the values where cached previously. + LIBCPP_ONLY(remove(file_two)); + + TEST_CHECK(ent.file_size() == 101); + } + env.create_file("dir/file_two", 99); + { + directory_entry ent(sym); + ent.replace_filename(sym_two.filename()); + TEST_REQUIRE(ent.path() == sym_two); + + LIBCPP_ONLY(remove(file_two)); + LIBCPP_ONLY(remove(sym_two)); + + TEST_CHECK(ent.is_symlink()); + TEST_CHECK(ent.is_regular_file()); + TEST_CHECK(ent.file_size() == 99); + } +} + +TEST_CASE(test_replace_filename_propagates_error) { + using namespace fs; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path file_two = env.create_file("dir/file_two", 99); + const path file_out_of_dir = env.create_file("file_three", 101); + const path sym_out_of_dir = env.create_symlink("dir/file", "sym"); + const path sym_out_of_dir_two = env.create_symlink("dir/file", "sym_two"); + const path sym_in_dir = env.create_symlink("file_two", "dir/sym_three"); + const path sym_in_dir_two = env.create_symlink("file_two", "dir/sym_four"); + + const perms old_perms = status(dir).permissions(); + + { + directory_entry ent(file); + permissions(dir, perms::none); + std::error_code ec = GetTestEC(); + ent.replace_filename(file_two.filename(), ec); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + } + permissions(dir, old_perms); + { + directory_entry ent(sym_in_dir); + permissions(dir, perms::none); + std::error_code ec = GetTestEC(); + ent.replace_filename(sym_in_dir_two.filename(), ec); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + } + permissions(dir, old_perms); + { + directory_entry ent(sym_out_of_dir); + permissions(dir, perms::none); + std::error_code ec = GetTestEC(); + ent.replace_filename(sym_out_of_dir_two.filename(), ec); + TEST_CHECK(!ec); + TEST_CHECK(ent.is_symlink()); + ec = GetTestEC(); + TEST_CHECK(!ent.exists(ec)); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/comparisons.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/comparisons.pass.cpp new file mode 100644 index 00000000000..7df36fed3a6 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/comparisons.pass.cpp @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// bool operator==(directory_entry const&) const noexcept; +// bool operator!=(directory_entry const&) const noexcept; +// bool operator< (directory_entry const&) const noexcept; +// bool operator<=(directory_entry const&) const noexcept; +// bool operator> (directory_entry const&) const noexcept; +// bool operator>=(directory_entry const&) const noexcept; + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + + +#define CHECK_OP(Op) \ + static_assert(std::is_same<decltype(ce. operator Op (ce)), bool>::value, ""); \ + static_assert(noexcept(ce.operator Op (ce)), "Operation must be noexcept" ) + +void test_comparison_signatures() { + using namespace fs; + path const p("foo/bar/baz"); + // Check that the operators are member functions with the correct signatures. + { + directory_entry const ce(p); + CHECK_OP(==); + CHECK_OP(!=); + CHECK_OP(< ); + CHECK_OP(<=); + CHECK_OP(> ); + CHECK_OP(>=); + } +} +#undef CHECK_OP + +// The actual semantics of the comparisons are testing via paths operators. +void test_comparisons_simple() { + using namespace fs; + typedef std::pair<path, path> TestType; + TestType TestCases[] = + { + {"", ""}, + {"", "a"}, + {"a", "a"}, + {"a", "b"}, + {"foo/bar/baz", "foo/bar/baz/"} + }; + auto TestFn = [](path const& LHS, const directory_entry& LHSE, + path const& RHS, const directory_entry& RHSE) { + assert((LHS == RHS) == (LHSE == RHSE)); + assert((LHS != RHS) == (LHSE != RHSE)); + assert((LHS < RHS) == (LHSE < RHSE)); + assert((LHS <= RHS) == (LHSE <= RHSE)); + assert((LHS > RHS) == (LHSE > RHSE)); + assert((LHS >= RHS) == (LHSE >= RHSE)); + }; + for (auto const& TC : TestCases) { + const directory_entry L(TC.first); + const directory_entry R(TC.second); + TestFn(TC.first, L, TC.second, R); + TestFn(TC.second, R, TC.first, L); + } +} + +int main() { + test_comparison_signatures(); + test_comparisons_simple(); +} diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp new file mode 100644 index 00000000000..04dec92c9c9 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp @@ -0,0 +1,242 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// uintmax_t file_size() const; +// uintmax_t file_size(error_code const&) const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "filesystem_test_helper.hpp" +#include "rapid-cxx-test.hpp" + +#include <iostream> + +TEST_SUITE(directory_entry_obs_testsuite) + +TEST_CASE(signatures) { + using namespace fs; + { + const fs::directory_entry e = {}; + std::error_code ec; + static_assert(std::is_same<decltype(e.file_size()), uintmax_t>::value, ""); + static_assert(std::is_same<decltype(e.file_size(ec)), uintmax_t>::value, + ""); + static_assert(noexcept(e.file_size()) == false, ""); + static_assert(noexcept(e.file_size(ec)) == true, ""); + } +} + +TEST_CASE(basic) { + using namespace fs; + + scoped_test_env env; + const path file = env.create_file("file", 42); + const path dir = env.create_dir("dir"); + const path sym = env.create_symlink("file", "sym"); + + { + directory_entry ent(file); + uintmax_t expect = file_size(ent); + TEST_CHECK(expect == 42); + + // Remove the file to show that the results were already in the cache. + LIBCPP_ONLY(remove(file)); + + std::error_code ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == expect); + TEST_CHECK(!ec); + } + env.create_file("file", 99); + { + directory_entry ent(sym); + + uintmax_t expect = file_size(ent); + TEST_CHECK(expect == 99); + + LIBCPP_ONLY(remove(ent)); + + std::error_code ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == 99); + TEST_CHECK(!ec); + } +} + +TEST_CASE(not_regular_file) { + using namespace fs; + + scoped_test_env env; + struct { + const path p; + std::errc expected_err; + } TestCases[] = { + {env.create_dir("dir"), std::errc::is_a_directory}, + {env.create_fifo("fifo"), std::errc::not_supported}, + {env.create_symlink("dir", "sym"), std::errc::is_a_directory}}; + + for (auto const& TC : TestCases) { + const path& p = TC.p; + directory_entry ent(p); + TEST_CHECK(ent.path() == p); + std::error_code ec = GetTestEC(0); + + std::error_code other_ec = GetTestEC(1); + uintmax_t expect = file_size(p, other_ec); + + uintmax_t got = ent.file_size(ec); + TEST_CHECK(got == expect); + TEST_CHECK(got == uintmax_t(-1)); + TEST_CHECK(ec == other_ec); + TEST_CHECK(ErrorIs(ec, TC.expected_err)); + + ExceptionChecker Checker(p, TC.expected_err, "directory_entry::file_size"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); + } +} + +TEST_CASE(error_reporting) { + using namespace fs; + + scoped_test_env env; + + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path file_out_of_dir = env.create_file("file2", 101); + const path sym_out_of_dir = env.create_symlink("dir/file", "sym"); + const path sym_in_dir = env.create_symlink("file2", "dir/sym2"); + + const perms old_perms = status(dir).permissions(); + + // test a file which doesn't exist + { + directory_entry ent; + + std::error_code ec = GetTestEC(); + ent.assign(StaticEnv::DNE, ec); + TEST_REQUIRE(ent.path() == StaticEnv::DNE); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ExceptionChecker Checker(StaticEnv::DNE, + std::errc::no_such_file_or_directory, + "directory_entry::file_size"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); + } + // test a dead symlink + { + directory_entry ent; + + std::error_code ec = GetTestEC(); + uintmax_t expect_bad = file_size(StaticEnv::BadSymlink, ec); + TEST_CHECK(expect_bad == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ec = GetTestEC(); + ent.assign(StaticEnv::BadSymlink, ec); + TEST_REQUIRE(ent.path() == StaticEnv::BadSymlink); + TEST_CHECK(!ec); + + ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == expect_bad); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ExceptionChecker Checker(StaticEnv::BadSymlink, + std::errc::no_such_file_or_directory, + "directory_entry::file_size"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); + } + // test a file w/o appropriate permissions. + { + directory_entry ent; + uintmax_t expect_good = file_size(file); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + ent.assign(file, ec); + TEST_REQUIRE(ent.path() == file); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ExceptionChecker Checker(file, std::errc::permission_denied, "file_size"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); + + permissions(dir, old_perms); + ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == expect_good); + TEST_CHECK(!ec); + TEST_CHECK_NO_THROW(ent.file_size()); + } + permissions(dir, old_perms); + // test a symlink w/o appropriate permissions. + { + directory_entry ent; + uintmax_t expect_good = file_size(sym_in_dir); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + ent.assign(sym_in_dir, ec); + TEST_REQUIRE(ent.path() == sym_in_dir); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied, + "file_size"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); + + permissions(dir, old_perms); + ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == expect_good); + TEST_CHECK(!ec); + TEST_CHECK_NO_THROW(ent.file_size()); + } + permissions(dir, old_perms); + // test a symlink to a file w/o appropriate permissions + { + directory_entry ent; + uintmax_t expect_good = file_size(sym_out_of_dir); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + ent.assign(sym_out_of_dir, ec); + TEST_REQUIRE(ent.path() == sym_out_of_dir); + TEST_CHECK(!ec); + + ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied, + "file_size"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size()); + + permissions(dir, old_perms); + ec = GetTestEC(); + TEST_CHECK(ent.file_size(ec) == expect_good); + TEST_CHECK(!ec); + TEST_CHECK_NO_THROW(ent.file_size()); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp new file mode 100644 index 00000000000..2a0d80ca626 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp @@ -0,0 +1,258 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// file_status status() const; +// file_status status(error_code const&) const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "filesystem_test_helper.hpp" +#include "rapid-cxx-test.hpp" + +TEST_SUITE(directory_entry_obs_testsuite) + +TEST_CASE(file_dne) { + using namespace fs; + directory_entry p("dne"); +} + +TEST_CASE(signatures) { + using namespace fs; + const directory_entry e = {}; + std::error_code ec; +#define TEST_FUNC(name) \ + static_assert(std::is_same<decltype(e.name()), bool>::value, \ + "wrong return type"); \ + static_assert(noexcept(e.name()) == false, "should not be noexcept"); \ + static_assert(std::is_same<decltype(e.name(ec)), bool>::value, \ + "wrong return type"); \ + static_assert(noexcept(e.name(ec)) == true, "should be noexcept") + + TEST_FUNC(exists); + TEST_FUNC(is_block_file); + TEST_FUNC(is_character_file); + TEST_FUNC(is_directory); + TEST_FUNC(is_fifo); + TEST_FUNC(is_other); + TEST_FUNC(is_regular_file); + TEST_FUNC(is_socket); + TEST_FUNC(is_symlink); + +#undef TEST_FUNC +} + +TEST_CASE(test_without_ec) { + using namespace fs; + using fs::directory_entry; + using fs::file_status; + using fs::path; + + scoped_test_env env; + path f = env.create_file("foo", 42); + path d = env.create_dir("dir"); + path fifo = env.create_fifo("fifo"); + path hl = env.create_hardlink("foo", "hl"); + for (auto p : {hl, f, d, fifo}) { + directory_entry e(p); + file_status st = status(p); + file_status sym_st = symlink_status(p); + fs::remove(p); + TEST_REQUIRE(e.exists()); + TEST_REQUIRE(!exists(p)); + TEST_CHECK(e.exists() == exists(st)); + TEST_CHECK(e.is_block_file() == is_block_file(st)); + TEST_CHECK(e.is_character_file() == is_character_file(st)); + TEST_CHECK(e.is_directory() == is_directory(st)); + TEST_CHECK(e.is_fifo() == is_fifo(st)); + TEST_CHECK(e.is_other() == is_other(st)); + TEST_CHECK(e.is_regular_file() == is_regular_file(st)); + TEST_CHECK(e.is_socket() == is_socket(st)); + TEST_CHECK(e.is_symlink() == is_symlink(sym_st)); + } +} + +TEST_CASE(test_with_ec) { + using namespace fs; + using fs::directory_entry; + using fs::file_status; + using fs::path; + + scoped_test_env env; + path f = env.create_file("foo", 42); + path d = env.create_dir("dir"); + path fifo = env.create_fifo("fifo"); + path hl = env.create_hardlink("foo", "hl"); + for (auto p : {hl, f, d, fifo}) { + directory_entry e(p); + std::error_code status_ec = GetTestEC(); + std::error_code sym_status_ec = GetTestEC(1); + file_status st = status(p, status_ec); + file_status sym_st = symlink_status(p, sym_status_ec); + fs::remove(p); + std::error_code ec = GetTestEC(2); + auto CheckEC = [&](std::error_code const& other_ec) { + bool res = ec == other_ec; + ec = GetTestEC(2); + return res; + }; + + TEST_REQUIRE(e.exists(ec)); + TEST_CHECK(CheckEC(status_ec)); + TEST_REQUIRE(!exists(p)); + + TEST_CHECK(e.exists(ec) == exists(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_block_file(ec) == is_block_file(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_character_file(ec) == is_character_file(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_directory(ec) == is_directory(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_fifo(ec) == is_fifo(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_other(ec) == is_other(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_regular_file(ec) == is_regular_file(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_socket(ec) == is_socket(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_symlink(ec) == is_symlink(sym_st)); + TEST_CHECK(CheckEC(sym_status_ec)); + } +} + +TEST_CASE(test_with_ec_dne) { + using namespace fs; + using fs::directory_entry; + using fs::file_status; + using fs::path; + + for (auto p : {StaticEnv::DNE, StaticEnv::BadSymlink}) { + + directory_entry e(p); + std::error_code status_ec = GetTestEC(); + std::error_code sym_status_ec = GetTestEC(1); + file_status st = status(p, status_ec); + file_status sym_st = symlink_status(p, sym_status_ec); + std::error_code ec = GetTestEC(2); + auto CheckEC = [&](std::error_code const& other_ec) { + bool res = ec == other_ec; + ec = GetTestEC(2); + return res; + }; + + TEST_CHECK(e.exists(ec) == exists(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_block_file(ec) == is_block_file(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_character_file(ec) == is_character_file(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_directory(ec) == is_directory(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_fifo(ec) == is_fifo(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_other(ec) == is_other(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_regular_file(ec) == is_regular_file(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_socket(ec) == is_socket(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_symlink(ec) == is_symlink(sym_st)); + TEST_CHECK(CheckEC(sym_status_ec)); + } +} + +TEST_CASE(test_with_ec_cannot_resolve) { + using namespace fs; + using fs::directory_entry; + using fs::file_status; + using fs::path; + + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path file_out_of_dir = env.create_file("file2", 99); + const path sym = env.create_symlink("file2", "dir/sym"); + + perms old_perms = fs::status(dir).permissions(); + + for (auto p : {file, sym}) { + permissions(dir, old_perms); + directory_entry e(p); + + permissions(dir, perms::none); + std::error_code dummy_ec; + e.refresh(dummy_ec); + TEST_REQUIRE(dummy_ec); + + std::error_code status_ec = GetTestEC(); + std::error_code sym_status_ec = GetTestEC(1); + file_status st = status(p, status_ec); + file_status sym_st = symlink_status(p, sym_status_ec); + std::error_code ec = GetTestEC(2); + auto CheckEC = [&](std::error_code const& other_ec) { + bool res = ec == other_ec; + ec = GetTestEC(2); + return res; + }; + + TEST_CHECK(e.exists(ec) == exists(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_block_file(ec) == is_block_file(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_character_file(ec) == is_character_file(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_directory(ec) == is_directory(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_fifo(ec) == is_fifo(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_other(ec) == is_other(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_regular_file(ec) == is_regular_file(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_socket(ec) == is_socket(st)); + TEST_CHECK(CheckEC(status_ec)); + + TEST_CHECK(e.is_symlink(ec) == is_symlink(sym_st)); + TEST_CHECK(CheckEC(sym_status_ec)); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp new file mode 100644 index 00000000000..17124c132cb --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp @@ -0,0 +1,242 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// uintmax_t hard_link_count() const; +// uintmax_t hard_link_count(error_code const&) const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "filesystem_test_helper.hpp" +#include "rapid-cxx-test.hpp" + +TEST_SUITE(directory_entry_obs_testsuite) + +TEST_CASE(signatures) { + using namespace fs; + { + const directory_entry e = {}; + std::error_code ec; + static_assert(std::is_same<decltype(e.hard_link_count()), uintmax_t>::value, ""); + static_assert(std::is_same<decltype(e.hard_link_count(ec)), uintmax_t>::value, + ""); + static_assert(noexcept(e.hard_link_count()) == false, ""); + static_assert(noexcept(e.hard_link_count(ec)) == true, ""); + } +} + +TEST_CASE(basic) { + using namespace fs; + + scoped_test_env env; + const path file = env.create_file("file", 42); + const path dir = env.create_dir("dir"); + const path sym = env.create_symlink("file", "sym"); + + { + directory_entry ent(file); + uintmax_t expect = hard_link_count(ent); + + // Remove the file to show that the results were already in the cache. + LIBCPP_ONLY(remove(file)); + + std::error_code ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == expect); + TEST_CHECK(!ec); + } + { + directory_entry ent(dir); + uintmax_t expect = hard_link_count(ent); + + LIBCPP_ONLY(remove(dir)); + + std::error_code ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == expect); + TEST_CHECK(!ec); + } + env.create_file("file", 99); + env.create_hardlink("file", "hl"); + { + directory_entry ent(sym); + std::error_code ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == 2); + TEST_CHECK(!ec); + } +} + +TEST_CASE(not_regular_file) { + using namespace fs; + + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path dir2 = env.create_dir("dir/dir2"); + const path fifo = env.create_fifo("dir/fifo"); + const path sym_to_fifo = env.create_symlink("dir/fifo", "dir/sym"); + + const perms old_perms = status(dir).permissions(); + + for (auto p : {dir2, fifo, sym_to_fifo}) { + permissions(dir, old_perms); + std::error_code dummy_ec = GetTestEC(); + directory_entry ent(p, dummy_ec); + TEST_CHECK(!dummy_ec); + + uintmax_t expect = hard_link_count(p); + + LIBCPP_ONLY(permissions(dir, perms::none)); + + std::error_code ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == expect); + TEST_CHECK(!ec); + TEST_CHECK_NO_THROW(ent.hard_link_count()); + } +} + +TEST_CASE(error_reporting) { + using namespace fs; + + scoped_test_env env; + + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path file_out_of_dir = env.create_file("file2", 101); + const path sym_out_of_dir = env.create_symlink("dir/file", "sym"); + const path sym_in_dir = env.create_symlink("file2", "dir/sym2"); + + const perms old_perms = status(dir).permissions(); + + // test a file which doesn't exist + { + directory_entry ent; + + std::error_code ec = GetTestEC(); + ent.assign(StaticEnv::DNE, ec); + TEST_CHECK(ec); + TEST_REQUIRE(ent.path() == StaticEnv::DNE); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ExceptionChecker Checker(StaticEnv::DNE, + std::errc::no_such_file_or_directory, + "directory_entry::hard_link_count"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count()); + } + // test a dead symlink + { + directory_entry ent; + + std::error_code ec = GetTestEC(); + uintmax_t expect_bad = hard_link_count(StaticEnv::BadSymlink, ec); + TEST_CHECK(expect_bad == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ec = GetTestEC(); + ent.assign(StaticEnv::BadSymlink, ec); + TEST_REQUIRE(ent.path() == StaticEnv::BadSymlink); + TEST_CHECK(!ec); + + ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == expect_bad); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ExceptionChecker Checker(StaticEnv::BadSymlink, + std::errc::no_such_file_or_directory, + "directory_entry::hard_link_count"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count()); + } + // test a file w/o appropriate permissions. + { + directory_entry ent; + uintmax_t expect_good = hard_link_count(file); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + ent.assign(file, ec); + TEST_REQUIRE(ent.path() == file); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ExceptionChecker Checker(file, std::errc::permission_denied, + "hard_link_count"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count()); + + permissions(dir, old_perms); + ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == expect_good); + TEST_CHECK(!ec); + TEST_CHECK_NO_THROW(ent.hard_link_count()); + } + permissions(dir, old_perms); + // test a symlink w/o appropriate permissions. + { + directory_entry ent; + uintmax_t expect_good = hard_link_count(sym_in_dir); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + ent.assign(sym_in_dir, ec); + TEST_REQUIRE(ent.path() == sym_in_dir); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied, + "hard_link_count"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count()); + + permissions(dir, old_perms); + ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == expect_good); + TEST_CHECK(!ec); + TEST_CHECK_NO_THROW(ent.hard_link_count()); + } + permissions(dir, old_perms); + // test a symlink to a file w/o appropriate permissions + { + directory_entry ent; + uintmax_t expect_good = hard_link_count(sym_out_of_dir); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + ent.assign(sym_out_of_dir, ec); + TEST_REQUIRE(ent.path() == sym_out_of_dir); + TEST_CHECK(!ec); + + ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1)); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied, + "hard_link_count"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count()); + + permissions(dir, old_perms); + ec = GetTestEC(); + TEST_CHECK(ent.hard_link_count(ec) == expect_good); + TEST_CHECK(!ec); + TEST_CHECK_NO_THROW(ent.hard_link_count()); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp new file mode 100644 index 00000000000..afdd47a513c --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp @@ -0,0 +1,215 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// file_time_type last_write_time() const; +// file_time_type last_write_time(error_code const&) const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "filesystem_test_helper.hpp" +#include "rapid-cxx-test.hpp" + +TEST_SUITE(directory_entry_obs_testsuite) + +TEST_CASE(signatures) { + using namespace fs; + { + const fs::directory_entry e = {}; + std::error_code ec; + static_assert(std::is_same<decltype(e.last_write_time()), file_time_type>::value, + ""); + static_assert(std::is_same<decltype(e.last_write_time(ec)), file_time_type>::value, + ""); + static_assert(noexcept(e.last_write_time()) == false, ""); + static_assert(noexcept(e.last_write_time(ec)) == true, ""); + } +} + +TEST_CASE(basic) { + using namespace fs; + + scoped_test_env env; + const path file = env.create_file("file", 42); + const path dir = env.create_dir("dir"); + const path sym = env.create_symlink("file", "sym"); + + { + directory_entry ent(file); + file_time_type expect = last_write_time(ent); + + // Remove the file to show that the results were already in the cache. + LIBCPP_ONLY(remove(file)); + + std::error_code ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == expect); + TEST_CHECK(!ec); + } + { + directory_entry ent(dir); + file_time_type expect = last_write_time(ent); + + LIBCPP_ONLY(remove(dir)); + + std::error_code ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == expect); + TEST_CHECK(!ec); + } + env.create_file("file", 99); + { + directory_entry ent(sym); + file_time_type expect = last_write_time(sym); + + std::error_code ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == expect); + TEST_CHECK(!ec); + } +} + +TEST_CASE(error_reporting) { + using namespace fs; + + scoped_test_env env; + + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path file_out_of_dir = env.create_file("file2", 101); + const path sym_out_of_dir = env.create_symlink("dir/file", "sym"); + const path sym_in_dir = env.create_symlink("file2", "dir/sym2"); + + const perms old_perms = status(dir).permissions(); + + // test a file which doesn't exist + { + directory_entry ent; + + std::error_code ec = GetTestEC(); + ent.assign(StaticEnv::DNE, ec); + TEST_REQUIRE(ent.path() == StaticEnv::DNE); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == file_time_type::min()); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ExceptionChecker Checker(StaticEnv::DNE, + std::errc::no_such_file_or_directory, + "directory_entry::last_write_time"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time()); + } + // test a dead symlink + { + directory_entry ent; + + std::error_code ec = GetTestEC(); + file_time_type expect_bad = last_write_time(StaticEnv::BadSymlink, ec); + TEST_CHECK(expect_bad == file_time_type::min()); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ec = GetTestEC(); + ent.assign(StaticEnv::BadSymlink, ec); + TEST_REQUIRE(ent.path() == StaticEnv::BadSymlink); + TEST_CHECK(!ec); + + ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == expect_bad); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + + ExceptionChecker Checker(StaticEnv::BadSymlink, + std::errc::no_such_file_or_directory, + "directory_entry::last_write_time"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time()); + } + // test a file w/o appropriate permissions. + { + directory_entry ent; + file_time_type expect_good = last_write_time(file); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + ent.assign(file, ec); + TEST_REQUIRE(ent.path() == file); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == file_time_type::min()); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ExceptionChecker Checker(file, std::errc::permission_denied, + "last_write_time"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time()); + + permissions(dir, old_perms); + ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == expect_good); + TEST_CHECK(!ec); + TEST_CHECK_NO_THROW(ent.last_write_time()); + } + permissions(dir, old_perms); + // test a symlink w/o appropriate permissions. + { + directory_entry ent; + file_time_type expect_good = last_write_time(sym_in_dir); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + ent.assign(sym_in_dir, ec); + TEST_REQUIRE(ent.path() == sym_in_dir); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == file_time_type::min()); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied, + "last_write_time"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time()); + + permissions(dir, old_perms); + ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == expect_good); + TEST_CHECK(!ec); + TEST_CHECK_NO_THROW(ent.last_write_time()); + } + permissions(dir, old_perms); + // test a symlink to a file w/o appropriate permissions + { + directory_entry ent; + file_time_type expect_good = last_write_time(sym_out_of_dir); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + ent.assign(sym_out_of_dir, ec); + TEST_REQUIRE(ent.path() == sym_out_of_dir); + TEST_CHECK(!ec); + + ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == file_time_type::min()); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied, + "last_write_time"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time()); + + permissions(dir, old_perms); + ec = GetTestEC(); + TEST_CHECK(ent.last_write_time(ec) == expect_good); + TEST_CHECK(!ec); + TEST_CHECK_NO_THROW(ent.last_write_time()); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/path.pass.cpp new file mode 100644 index 00000000000..2abb13df1f8 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/path.pass.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// const path& path() const noexcept; +// operator const path&() const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + + +void test_path_method() { + using namespace fs; + const path p("foo/bar/baz.exe"); + const path p2("abc"); + { + directory_entry nce; + const directory_entry e(""); + static_assert(std::is_same<decltype(e.path()), const path&>::value, ""); + static_assert(std::is_same<decltype(nce.path()), const path&>::value, ""); + static_assert(noexcept(e.path()) && noexcept(nce.path()), ""); + } + { + directory_entry e(p); + path const& pref = e.path(); + assert(pref == p); + assert(&pref == &e.path()); + e.assign(p2); + assert(pref == p2); + assert(&pref == &e.path()); + } +} + +void test_path_conversion() { + using namespace fs; + const path p("foo/bar/baz.exe"); + const path p2("abc"); + { + directory_entry nce; + const directory_entry e(""); + // Check conversions exist + static_assert(std::is_convertible<directory_entry&, path const&>::value, ""); + static_assert(std::is_convertible<directory_entry const&, path const&>::value, ""); + static_assert(std::is_convertible<directory_entry &&, path const&>::value, ""); + static_assert(std::is_convertible<directory_entry const&&, path const&>::value, ""); + // Not convertible to non-const + static_assert(!std::is_convertible<directory_entry&, path&>::value, ""); + static_assert(!std::is_convertible<directory_entry const&, path&>::value, ""); + static_assert(!std::is_convertible<directory_entry &&, path&>::value, ""); + static_assert(!std::is_convertible<directory_entry const&&, path&>::value, ""); + // conversions are noexcept + static_assert(noexcept(e.operator fs::path const&()) && + noexcept(e.operator fs::path const&()), ""); + } + // const + { + directory_entry const e(p); + path const& pref = e; + assert(&pref == &e.path()); + } + // non-const + { + directory_entry e(p); + path const& pref = e; + assert(&pref == &e.path()); + + e.assign(p2); + assert(pref == p2); + assert(&pref == &e.path()); + } +} + +int main() { + test_path_method(); + test_path_conversion(); +} diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/status.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/status.pass.cpp new file mode 100644 index 00000000000..ec654468b9a --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/status.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// file_status status() const; +// file_status status(error_code const&) const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "filesystem_test_helper.hpp" +#include "rapid-cxx-test.hpp" + +TEST_SUITE(directory_entry_status_testsuite) + +TEST_CASE(test_basic) { + using namespace fs; + { + const fs::directory_entry e("foo"); + std::error_code ec; + static_assert(std::is_same<decltype(e.status()), fs::file_status>::value, ""); + static_assert(std::is_same<decltype(e.status(ec)), fs::file_status>::value, ""); + static_assert(noexcept(e.status()) == false, ""); + static_assert(noexcept(e.status(ec)) == true, ""); + } + path TestCases[] = {StaticEnv::File, StaticEnv::Dir, StaticEnv::SymlinkToFile, + StaticEnv::DNE}; + for (const auto& p : TestCases) { + const directory_entry e(p); + std::error_code pec = GetTestEC(), eec = GetTestEC(1); + file_status ps = fs::status(p, pec); + file_status es = e.status(eec); + TEST_CHECK(ps.type() == es.type()); + TEST_CHECK(ps.permissions() == es.permissions()); + TEST_CHECK(pec == eec); + } + for (const auto& p : TestCases) { + const directory_entry e(p); + file_status ps = fs::status(p); + file_status es = e.status(); + TEST_CHECK(ps.type() == es.type()); + TEST_CHECK(ps.permissions() == es.permissions()); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp new file mode 100644 index 00000000000..e8850c3a7b1 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_entry + +// file_status symlink_status() const; +// file_status symlink_status(error_code&) const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "filesystem_test_helper.hpp" +#include "rapid-cxx-test.hpp" + +TEST_SUITE(directory_entry_obs_suite) + +TEST_CASE(test_signature) { + using namespace fs; + { + const directory_entry e("foo"); + std::error_code ec; + static_assert(std::is_same<decltype(e.symlink_status()), file_status>::value, ""); + static_assert(std::is_same<decltype(e.symlink_status(ec)), file_status>::value, ""); + static_assert(noexcept(e.symlink_status()) == false, ""); + static_assert(noexcept(e.symlink_status(ec)) == true, ""); + } + path TestCases[] = {StaticEnv::File, StaticEnv::Dir, StaticEnv::SymlinkToFile, + StaticEnv::DNE}; + for (const auto& p : TestCases) { + const directory_entry e(p); + std::error_code pec = GetTestEC(), eec = GetTestEC(1); + file_status ps = fs::symlink_status(p, pec); + file_status es = e.symlink_status(eec); + TEST_CHECK(ps.type() == es.type()); + TEST_CHECK(ps.permissions() == es.permissions()); + TEST_CHECK(pec == eec); + } + for (const auto& p : TestCases) { + const directory_entry e(p); + file_status ps = fs::symlink_status(p); + file_status es = e.symlink_status(); + TEST_CHECK(ps.type() == es.type()); + TEST_CHECK(ps.permissions() == es.permissions()); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/copy.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/copy.pass.cpp new file mode 100644 index 00000000000..ac224ac7ae6 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/copy.pass.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_iterator + +// directory_iterator(directory_iterator const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(directory_iterator_copy_construct_tests) + +TEST_CASE(test_constructor_signature) +{ + using D = directory_iterator; + static_assert(std::is_copy_constructible<D>::value, ""); +} + +TEST_CASE(test_copy_end_iterator) +{ + const directory_iterator endIt; + directory_iterator it(endIt); + TEST_CHECK(it == endIt); +} + +TEST_CASE(test_copy_valid_iterator) +{ + const path testDir = StaticEnv::Dir; + const directory_iterator endIt{}; + + const directory_iterator it(testDir); + TEST_REQUIRE(it != endIt); + const path entry = *it; + + const directory_iterator it2(it); + TEST_REQUIRE(it2 == it); + TEST_CHECK(*it2 == entry); + TEST_CHECK(*it == entry); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/copy_assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/copy_assign.pass.cpp new file mode 100644 index 00000000000..3f08e4024de --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/copy_assign.pass.cpp @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_iterator + +// directory_iterator& operator=(directory_iterator const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(directory_iterator_copy_assign_tests) + +TEST_CASE(test_assignment_signature) +{ + using D = directory_iterator; + static_assert(std::is_copy_assignable<D>::value, ""); +} + +TEST_CASE(test_copy_to_end_iterator) +{ + const path testDir = StaticEnv::Dir; + + const directory_iterator from(testDir); + TEST_REQUIRE(from != directory_iterator{}); + const path entry = *from; + + directory_iterator to{}; + to = from; + TEST_REQUIRE(to == from); + TEST_CHECK(*to == entry); + TEST_CHECK(*from == entry); +} + + +TEST_CASE(test_copy_from_end_iterator) +{ + const path testDir = StaticEnv::Dir; + + const directory_iterator from{}; + + directory_iterator to(testDir); + TEST_REQUIRE(to != directory_iterator{}); + + to = from; + TEST_REQUIRE(to == from); + TEST_CHECK(to == directory_iterator{}); +} + +TEST_CASE(test_copy_valid_iterator) +{ + const path testDir = StaticEnv::Dir; + const directory_iterator endIt{}; + + directory_iterator it_obj(testDir); + const directory_iterator& it = it_obj; + TEST_REQUIRE(it != endIt); + ++it_obj; + TEST_REQUIRE(it != endIt); + const path entry = *it; + + directory_iterator it2(testDir); + TEST_REQUIRE(it2 != it); + const path entry2 = *it2; + TEST_CHECK(entry2 != entry); + + it2 = it; + TEST_REQUIRE(it2 == it); + TEST_CHECK(*it2 == entry); +} + +TEST_CASE(test_returns_reference_to_self) +{ + const directory_iterator it; + directory_iterator it2; + directory_iterator& ref = (it2 = it); + TEST_CHECK(&ref == &it2); +} + + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp new file mode 100644 index 00000000000..0a33544c547 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp @@ -0,0 +1,254 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_iterator + +// explicit directory_iterator(const path& p); +// directory_iterator(const path& p, directory_options options); +// directory_iterator(const path& p, error_code& ec); +// directory_iterator(const path& p, directory_options options, error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(directory_iterator_constructor_tests) + +TEST_CASE(test_constructor_signatures) +{ + using D = directory_iterator; + + // explicit directory_iterator(path const&); + static_assert(!std::is_convertible<path, D>::value, ""); + static_assert(std::is_constructible<D, path>::value, ""); + static_assert(!std::is_nothrow_constructible<D, path>::value, ""); + + // directory_iterator(path const&, error_code&) + static_assert(std::is_constructible<D, path, + std::error_code&>::value, ""); + static_assert(!std::is_nothrow_constructible<D, path, + std::error_code&>::value, ""); + + // directory_iterator(path const&, directory_options); + static_assert(std::is_constructible<D, path, directory_options>::value, ""); + static_assert(!std::is_nothrow_constructible<D, path, directory_options>::value, ""); + + // directory_iterator(path const&, directory_options, error_code&) + static_assert(std::is_constructible<D, path, directory_options, + std::error_code&>::value, ""); + static_assert(!std::is_nothrow_constructible<D, path, directory_options, + std::error_code&>::value, ""); + +} + +TEST_CASE(test_construction_from_bad_path) +{ + std::error_code ec; + directory_options opts = directory_options::none; + const directory_iterator endIt; + + const path testPaths[] = { StaticEnv::DNE, StaticEnv::BadSymlink }; + for (path const& testPath : testPaths) + { + { + directory_iterator it(testPath, ec); + TEST_CHECK(ec); + TEST_CHECK(it == endIt); + } + { + directory_iterator it(testPath, opts, ec); + TEST_CHECK(ec); + TEST_CHECK(it == endIt); + } + { + TEST_CHECK_THROW(filesystem_error, directory_iterator(testPath)); + TEST_CHECK_THROW(filesystem_error, directory_iterator(testPath, opts)); + } + } +} + +TEST_CASE(access_denied_test_case) +{ + using namespace fs; + scoped_test_env env; + path const testDir = env.make_env_path("dir1"); + path const testFile = testDir / "testFile"; + env.create_dir(testDir); + env.create_file(testFile, 42); + + // Test that we can iterator over the directory before changing the perms + { + directory_iterator it(testDir); + TEST_REQUIRE(it != directory_iterator{}); + } + // Change the permissions so we can no longer iterate + permissions(testDir, perms::none); + + // Check that the construction fails when skip_permissions_denied is + // not given. + { + std::error_code ec; + directory_iterator it(testDir, ec); + TEST_REQUIRE(ec); + TEST_CHECK(it == directory_iterator{}); + } + // Check that construction does not report an error when + // 'skip_permissions_denied' is given. + { + std::error_code ec; + directory_iterator it(testDir, directory_options::skip_permission_denied, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(it == directory_iterator{}); + } +} + + +TEST_CASE(access_denied_to_file_test_case) +{ + using namespace fs; + scoped_test_env env; + path const testFile = env.make_env_path("file1"); + env.create_file(testFile, 42); + + // Change the permissions so we can no longer iterate + permissions(testFile, perms::none); + + // Check that the construction fails when skip_permissions_denied is + // not given. + { + std::error_code ec; + directory_iterator it(testFile, ec); + TEST_REQUIRE(ec); + TEST_CHECK(it == directory_iterator{}); + } + // Check that construction still fails when 'skip_permissions_denied' is given + // because we tried to open a file and not a directory. + { + std::error_code ec; + directory_iterator it(testFile, directory_options::skip_permission_denied, ec); + TEST_REQUIRE(ec); + TEST_CHECK(it == directory_iterator{}); + } +} + +TEST_CASE(test_open_on_empty_directory_equals_end) +{ + scoped_test_env env; + const path testDir = env.make_env_path("dir1"); + env.create_dir(testDir); + + const directory_iterator endIt; + { + std::error_code ec; + directory_iterator it(testDir, ec); + TEST_CHECK(!ec); + TEST_CHECK(it == endIt); + } + { + directory_iterator it(testDir); + TEST_CHECK(it == endIt); + } +} + +TEST_CASE(test_open_on_directory_succeeds) +{ + const path testDir = StaticEnv::Dir; + std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList), + std::end( StaticEnv::DirIterationList)); + const directory_iterator endIt{}; + + { + std::error_code ec; + directory_iterator it(testDir, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(it != endIt); + TEST_CHECK(dir_contents.count(*it)); + } + { + directory_iterator it(testDir); + TEST_CHECK(it != endIt); + TEST_CHECK(dir_contents.count(*it)); + } +} + +TEST_CASE(test_open_on_file_fails) +{ + const path testFile = StaticEnv::File; + const directory_iterator endIt{}; + { + std::error_code ec; + directory_iterator it(testFile, ec); + TEST_REQUIRE(ec); + TEST_CHECK(it == endIt); + } + { + TEST_CHECK_THROW(filesystem_error, directory_iterator(testFile)); + } +} + +TEST_CASE(test_open_on_empty_string) +{ + const path testPath = ""; + const directory_iterator endIt{}; + + std::error_code ec; + directory_iterator it(testPath, ec); + TEST_CHECK(ec); + TEST_CHECK(it == endIt); +} + +TEST_CASE(test_open_on_dot_dir) +{ + const path testPath = "."; + + std::error_code ec; + directory_iterator it(testPath, ec); + TEST_CHECK(!ec); +} + +TEST_CASE(test_open_on_symlink) +{ + const path symlinkToDir = StaticEnv::SymlinkToDir; + std::set<path> dir_contents; + for (path const& p : StaticEnv::DirIterationList) { + dir_contents.insert(p.filename()); + } + const directory_iterator endIt{}; + + { + std::error_code ec; + directory_iterator it(symlinkToDir, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(it != endIt); + path const& entry = *it; + TEST_CHECK(dir_contents.count(entry.filename())); + } + { + std::error_code ec; + directory_iterator it(symlinkToDir, + directory_options::follow_directory_symlink, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(it != endIt); + path const& entry = *it; + TEST_CHECK(dir_contents.count(entry.filename())); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/default_ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/default_ctor.pass.cpp new file mode 100644 index 00000000000..21c4ed3b723 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/default_ctor.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_iterator + +// directory_iterator::directory_iterator() noexcept + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" + + +int main() { + { + static_assert(std::is_nothrow_default_constructible<fs::directory_iterator>::value, ""); + } + { + fs::directory_iterator d1; + const fs::directory_iterator d2; + assert(d1 == d2); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/increment.pass.cpp new file mode 100644 index 00000000000..7ec814b214e --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/increment.pass.cpp @@ -0,0 +1,117 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_iterator + +// directory_iterator& operator++(); +// directory_iterator& increment(error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" +#include <iostream> + +using namespace fs; + +TEST_SUITE(directory_iterator_increment_tests) + +TEST_CASE(test_increment_signatures) +{ + using D = directory_iterator; + directory_iterator d; ((void)d); + std::error_code ec; ((void)ec); + + ASSERT_SAME_TYPE(decltype(++d), directory_iterator&); + ASSERT_NOT_NOEXCEPT(++d); + + ASSERT_SAME_TYPE(decltype(d.increment(ec)), directory_iterator&); + ASSERT_NOT_NOEXCEPT(d.increment(ec)); +} + +TEST_CASE(test_prefix_increment) +{ + const path testDir = StaticEnv::Dir; + const std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList), + std::end( StaticEnv::DirIterationList)); + const directory_iterator endIt{}; + + std::error_code ec; + directory_iterator it(testDir, ec); + TEST_REQUIRE(!ec); + + std::set<path> unseen_entries = dir_contents; + while (!unseen_entries.empty()) { + TEST_REQUIRE(it != endIt); + const path entry = *it; + TEST_REQUIRE(unseen_entries.erase(entry) == 1); + directory_iterator& it_ref = ++it; + TEST_CHECK(&it_ref == &it); + } + + TEST_CHECK(it == endIt); +} + +TEST_CASE(test_postfix_increment) +{ + const path testDir = StaticEnv::Dir; + const std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList), + std::end( StaticEnv::DirIterationList)); + const directory_iterator endIt{}; + + std::error_code ec; + directory_iterator it(testDir, ec); + TEST_REQUIRE(!ec); + + std::set<path> unseen_entries = dir_contents; + while (!unseen_entries.empty()) { + TEST_REQUIRE(it != endIt); + const path entry = *it; + TEST_REQUIRE(unseen_entries.erase(entry) == 1); + const path entry2 = *it++; + TEST_CHECK(entry2 == entry); + } + + TEST_CHECK(it == endIt); +} + + +TEST_CASE(test_increment_method) +{ + const path testDir = StaticEnv::Dir; + const std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList), + std::end( StaticEnv::DirIterationList)); + const directory_iterator endIt{}; + + std::error_code ec; + directory_iterator it(testDir, ec); + TEST_REQUIRE(!ec); + + std::set<path> unseen_entries = dir_contents; + while (!unseen_entries.empty()) { + TEST_REQUIRE(it != endIt); + const path entry = *it; + TEST_REQUIRE(unseen_entries.erase(entry) == 1); + directory_iterator& it_ref = it.increment(ec); + TEST_REQUIRE(!ec); + TEST_CHECK(&it_ref == &it); + } + + TEST_CHECK(it == endIt); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/move.pass.cpp new file mode 100644 index 00000000000..a2bf2ac9f2c --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/move.pass.cpp @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_iterator + +// directory_iterator(directory_iterator&&) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(directory_iterator_move_construct_tests) + +TEST_CASE(test_constructor_signature) +{ + using D = directory_iterator; + static_assert(std::is_nothrow_move_constructible<D>::value, ""); +} + +TEST_CASE(test_move_end_iterator) +{ + const directory_iterator endIt; + directory_iterator endIt2{}; + + directory_iterator it(std::move(endIt2)); + TEST_CHECK(it == endIt); + TEST_CHECK(endIt2 == endIt); +} + +TEST_CASE(test_move_valid_iterator) +{ + const path testDir = StaticEnv::Dir; + const directory_iterator endIt{}; + + directory_iterator it(testDir); + TEST_REQUIRE(it != endIt); + const path entry = *it; + + const directory_iterator it2(std::move(it)); + TEST_CHECK(*it2 == entry); + + TEST_CHECK(it == it2 || it == endIt); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/move_assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/move_assign.pass.cpp new file mode 100644 index 00000000000..1c722434402 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/move_assign.pass.cpp @@ -0,0 +1,116 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_iterator + +// directory_iterator& operator=(directory_iterator const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +// The filesystem specification explicitly allows for self-move on +// the directory iterators. Turn off this warning so we can test it. +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wself-move" +#endif + +using namespace fs; + +TEST_SUITE(directory_iterator_move_assign_tests) + +TEST_CASE(test_assignment_signature) +{ + using D = directory_iterator; + static_assert(std::is_nothrow_move_assignable<D>::value, ""); +} + +TEST_CASE(test_move_to_end_iterator) +{ + const path testDir = StaticEnv::Dir; + + directory_iterator from(testDir); + TEST_REQUIRE(from != directory_iterator{}); + const path entry = *from; + + directory_iterator to{}; + to = std::move(from); + TEST_REQUIRE(to != directory_iterator{}); + TEST_CHECK(*to == entry); +} + + +TEST_CASE(test_move_from_end_iterator) +{ + const path testDir = StaticEnv::Dir; + + directory_iterator from{}; + + directory_iterator to(testDir); + TEST_REQUIRE(to != from); + + to = std::move(from); + TEST_REQUIRE(to == directory_iterator{}); + TEST_REQUIRE(from == directory_iterator{}); +} + +TEST_CASE(test_move_valid_iterator) +{ + const path testDir = StaticEnv::Dir; + const directory_iterator endIt{}; + + directory_iterator it(testDir); + TEST_REQUIRE(it != endIt); + ++it; + TEST_REQUIRE(it != endIt); + const path entry = *it; + + directory_iterator it2(testDir); + TEST_REQUIRE(it2 != it); + const path entry2 = *it2; + TEST_CHECK(entry2 != entry); + + it2 = std::move(it); + TEST_REQUIRE(it2 != directory_iterator{}); + TEST_CHECK(*it2 == entry); +} + +TEST_CASE(test_returns_reference_to_self) +{ + directory_iterator it; + directory_iterator it2; + directory_iterator& ref = (it2 = it); + TEST_CHECK(&ref == &it2); +} + + +TEST_CASE(test_self_move) +{ + // Create two non-equal iterators that have exactly the same state. + directory_iterator it(StaticEnv::Dir); + directory_iterator it2(StaticEnv::Dir); + ++it; ++it2; + TEST_CHECK(it != it2); + TEST_CHECK(*it2 == *it); + + it = std::move(it); + TEST_CHECK(*it2 == *it); +} + + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.nonmembers/begin_end.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.nonmembers/begin_end.pass.cpp new file mode 100644 index 00000000000..71e8e55ae03 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.nonmembers/begin_end.pass.cpp @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_iterator + +// directory_iterator begin(directory_iterator iter) noexcept; +// directory_iterator end(directory_iterator iter) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" +#include <iostream> + +using namespace fs; + +TEST_SUITE(directory_iterator_begin_end_tests) + +TEST_CASE(test_function_signatures) +{ + using D = directory_iterator; + directory_iterator d; ((void)d); + + ASSERT_SAME_TYPE(decltype(begin(d)), directory_iterator); + ASSERT_NOEXCEPT(begin(std::move(d))); + + ASSERT_SAME_TYPE(decltype(end(d)), directory_iterator); + ASSERT_NOEXCEPT(end(std::move(d))); +} + +TEST_CASE(test_ranged_for_loop) +{ + const path testDir = StaticEnv::Dir; + std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList), + std::end( StaticEnv::DirIterationList)); + + std::error_code ec; + directory_iterator it(testDir, ec); + TEST_REQUIRE(!ec); + + for (auto& elem : it) { + TEST_CHECK(dir_contents.erase(elem) == 1); + } + TEST_CHECK(dir_contents.empty()); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/types.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/types.pass.cpp new file mode 100644 index 00000000000..4619083e8c3 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/types.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_iterator + +// typedef ... value_type; +// typedef ... difference_type; +// typedef ... pointer; +// typedef ... reference; +// typedef ... iterator_category + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" + + +int main() { + using namespace fs; + using D = directory_iterator; + ASSERT_SAME_TYPE(D::value_type, directory_entry); + ASSERT_SAME_TYPE(D::difference_type, std::ptrdiff_t); + ASSERT_SAME_TYPE(D::pointer, const directory_entry*); + ASSERT_SAME_TYPE(D::reference, const directory_entry&); + ASSERT_SAME_TYPE(D::iterator_category, std::input_iterator_tag); +} diff --git a/libcxx/test/std/input.output/filesystems/class.file_status/file_status.cons.pass.cpp b/libcxx/test/std/input.output/filesystems/class.file_status/file_status.cons.pass.cpp new file mode 100644 index 00000000000..6676c7b7d6a --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.file_status/file_status.cons.pass.cpp @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class file_status + +// explicit file_status() noexcept; +// explicit file_status(file_type, perms prms = perms::unknown) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_convertible.hpp" + + +int main() { + using namespace fs; + // Default ctor + { + static_assert(std::is_nothrow_default_constructible<file_status>::value, + "The default constructor must be noexcept"); + static_assert(test_convertible<file_status>(), + "The default constructor must not be explicit"); + const file_status f; + assert(f.type() == file_type::none); + assert(f.permissions() == perms::unknown); + } + + // Unary ctor + { + static_assert(std::is_nothrow_constructible<file_status, file_type>::value, + "This constructor must be noexcept"); + static_assert(!test_convertible<file_status, file_type>(), + "This constructor must be explicit"); + + const file_status f(file_type::not_found); + assert(f.type() == file_type::not_found); + assert(f.permissions() == perms::unknown); + } + // Binary ctor + { + static_assert(std::is_nothrow_constructible<file_status, file_type, perms>::value, + "This constructor must be noexcept"); + static_assert(!test_convertible<file_status, file_type, perms>(), + "This constructor must b explicit"); + const file_status f(file_type::regular, perms::owner_read); + assert(f.type() == file_type::regular); + assert(f.permissions() == perms::owner_read); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.file_status/file_status.mods.pass.cpp b/libcxx/test/std/input.output/filesystems/class.file_status/file_status.mods.pass.cpp new file mode 100644 index 00000000000..4cbf2062a1f --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.file_status/file_status.mods.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class file_status + +// void type(file_type) noexcept; +// void permissions(perms) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + + +int main() { + using namespace fs; + + file_status st; + + // type test + { + static_assert(noexcept(st.type(file_type::regular)), + "operation must be noexcept"); + static_assert(std::is_same<decltype(st.type(file_type::regular)), void>::value, + "operation must return void"); + assert(st.type() != file_type::regular); + st.type(file_type::regular); + assert(st.type() == file_type::regular); + } + // permissions test + { + static_assert(noexcept(st.permissions(perms::owner_read)), + "operation must be noexcept"); + static_assert(std::is_same<decltype(st.permissions(perms::owner_read)), void>::value, + "operation must return void"); + assert(st.permissions() != perms::owner_read); + st.permissions(perms::owner_read); + assert(st.permissions() == perms::owner_read); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.file_status/file_status.obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.file_status/file_status.obs.pass.cpp new file mode 100644 index 00000000000..89739b77df5 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.file_status/file_status.obs.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class file_status + +// file_type type() const noexcept; +// perms permissions(p) const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + + +int main() { + using namespace fs; + + const file_status st(file_type::regular, perms::owner_read); + + // type test + { + static_assert(noexcept(st.type()), + "operation must be noexcept"); + static_assert(std::is_same<decltype(st.type()), file_type>::value, + "operation must return file_type"); + assert(st.type() == file_type::regular); + } + // permissions test + { + static_assert(noexcept(st.permissions()), + "operation must be noexcept"); + static_assert(std::is_same<decltype(st.permissions()), perms>::value, + "operation must return perms"); + assert(st.permissions() == perms::owner_read); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.filesystem_error/filesystem_error.members.pass.cpp b/libcxx/test/std/input.output/filesystems/class.filesystem_error/filesystem_error.members.pass.cpp new file mode 100644 index 00000000000..b7484df5cc7 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.filesystem_error/filesystem_error.members.pass.cpp @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class filesystem_error + +// filesystem_error(const string& what_arg, error_code ec); +// filesystem_error(const string& what_arg, const path& p1, error_code ec); +// filesystem_error(const string& what_arg, const path& p1, const path& p2, error_code ec); +// const std::error_code& code() const; +// const char* what() const noexcept; +// const path& path1() const noexcept; +// const path& path2() const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" + + +void test_constructors() { + using namespace fs; + + // The string returned by "filesystem_error::what() must contain runtime_error::what() + const std::string what_arg = "Hello World"; + const std::string what_contains = std::runtime_error(what_arg).what(); + assert(what_contains.find(what_arg) != std::string::npos); + auto CheckWhat = [what_contains](filesystem_error const& e) { + std::string s = e.what(); + assert(s.find(what_contains) != std::string::npos); + }; + + std::error_code ec = std::make_error_code(std::errc::file_exists); + const path p1("foo"); + const path p2("bar"); + + // filesystem_error(const string& what_arg, error_code ec); + { + ASSERT_NOT_NOEXCEPT(filesystem_error(what_arg, ec)); + filesystem_error e(what_arg, ec); + CheckWhat(e); + assert(e.code() == ec); + assert(e.path1().empty() && e.path2().empty()); + } + // filesystem_error(const string& what_arg, const path&, error_code ec); + { + ASSERT_NOT_NOEXCEPT(filesystem_error(what_arg, p1, ec)); + filesystem_error e(what_arg, p1, ec); + CheckWhat(e); + assert(e.code() == ec); + assert(e.path1() == p1); + assert(e.path2().empty()); + } + // filesystem_error(const string& what_arg, const path&, const path&, error_code ec); + { + ASSERT_NOT_NOEXCEPT(filesystem_error(what_arg, p1, p2, ec)); + filesystem_error e(what_arg, p1, p2, ec); + CheckWhat(e); + assert(e.code() == ec); + assert(e.path1() == p1); + assert(e.path2() == p2); + } +} + +void test_signatures() +{ + using namespace fs; + const path p; + std::error_code ec; + const filesystem_error e("lala", ec); + // const path& path1() const noexcept; + { + ASSERT_SAME_TYPE(path const&, decltype(e.path1())); + ASSERT_NOEXCEPT(e.path1()); + } + // const path& path2() const noexcept + { + ASSERT_SAME_TYPE(path const&, decltype(e.path2())); + ASSERT_NOEXCEPT(e.path2()); + } + // const char* what() const noexcept + { + ASSERT_SAME_TYPE(const char*, decltype(e.what())); + ASSERT_NOEXCEPT(e.what()); + } +} + +int main() { + static_assert(std::is_base_of<std::system_error, fs::filesystem_error>::value, ""); + test_constructors(); + test_signatures(); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.itr/iterator.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.itr/iterator.pass.cpp new file mode 100644 index 00000000000..b7a0383e047 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.itr/iterator.pass.cpp @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// template <class Source> +// path(const Source& source); +// template <class InputIterator> +// path(InputIterator first, InputIterator last); + + +#include "filesystem_include.hpp" +#include <iterator> +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "filesystem_test_helper.hpp" + + + +template <class It> +std::reverse_iterator<It> mkRev(It it) { + return std::reverse_iterator<It>(it); +} + +void checkIteratorConcepts() { + using namespace fs; + using It = path::iterator; + using Traits = std::iterator_traits<It>; + ASSERT_SAME_TYPE(Traits::iterator_category, std::bidirectional_iterator_tag); + ASSERT_SAME_TYPE(Traits::value_type, path); + ASSERT_SAME_TYPE(Traits::pointer, path const*); + ASSERT_SAME_TYPE(Traits::reference, path const&); + { + It it; + ASSERT_SAME_TYPE(It&, decltype(++it)); + ASSERT_SAME_TYPE(It, decltype(it++)); + ASSERT_SAME_TYPE(It&, decltype(--it)); + ASSERT_SAME_TYPE(It, decltype(it--)); + ASSERT_SAME_TYPE(Traits::reference, decltype(*it)); + ASSERT_SAME_TYPE(Traits::pointer, decltype(it.operator->())); + ASSERT_SAME_TYPE(std::string const&, decltype(it->native())); + ASSERT_SAME_TYPE(bool, decltype(it == it)); + ASSERT_SAME_TYPE(bool, decltype(it != it)); + } + { + path const p; + ASSERT_SAME_TYPE(It, decltype(p.begin())); + ASSERT_SAME_TYPE(It, decltype(p.end())); + assert(p.begin() == p.end()); + } +} + +void checkBeginEndBasic() { + using namespace fs; + using It = path::iterator; + { + path const p; + ASSERT_SAME_TYPE(It, decltype(p.begin())); + ASSERT_SAME_TYPE(It, decltype(p.end())); + assert(p.begin() == p.end()); + } + { + path const p("foo"); + It default_constructed; + default_constructed = p.begin(); + assert(default_constructed == p.begin()); + assert(default_constructed != p.end()); + default_constructed = p.end(); + assert(default_constructed == p.end()); + assert(default_constructed != p.begin()); + } + { + path p("//root_name//first_dir////second_dir"); + const path expect[] = {"/", "root_name", "first_dir", "second_dir"}; + assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect))); + assert(checkCollectionsEqualBackwards(p.begin(), p.end(), std::begin(expect), std::end(expect))); + + } + { + path p("////foo/bar/baz///"); + const path expect[] = {"/", "foo", "bar", "baz", ""}; + assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect))); + assert(checkCollectionsEqualBackwards(p.begin(), p.end(), std::begin(expect), std::end(expect))); + + } + +} + +int main() { + using namespace fs; + checkIteratorConcepts(); + checkBeginEndBasic(); // See path.decompose.pass.cpp for more tests. +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp new file mode 100644 index 00000000000..a02ef18c453 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.append.pass.cpp @@ -0,0 +1,339 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path& operator/=(path const&) +// template <class Source> +// path& operator/=(Source const&); +// template <class Source> +// path& append(Source const&); +// template <class InputIterator> +// path& append(InputIterator first, InputIterator last); + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <string_view> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" +#include "verbose_assert.h" + + +struct AppendOperatorTestcase { + MultiStringType lhs; + MultiStringType rhs; + MultiStringType expect; +}; + +#define S(Str) MKSTR(Str) +const AppendOperatorTestcase Cases[] = + { + {S(""), S(""), S("")} + , {S("p1"), S("p2"), S("p1/p2")} + , {S("p1/"), S("p2"), S("p1/p2")} + , {S("p1"), S("/p2"), S("/p2")} + , {S("p1/"), S("/p2"), S("/p2")} + , {S("p1"), S("\\p2"), S("p1/\\p2")} + , {S("p1\\"), S("p2"), S("p1\\/p2")} + , {S("p1\\"), S("\\p2"), S("p1\\/\\p2")} + , {S(""), S("p2"), S("p2")} + , {S("/p1"), S("p2"), S("/p1/p2")} + , {S("/p1"), S("/p2"), S("/p2")} + , {S("/p1/p3"), S("p2"), S("/p1/p3/p2")} + , {S("/p1/p3/"), S("p2"), S("/p1/p3/p2")} + , {S("/p1/"), S("p2"), S("/p1/p2")} + , {S("/p1/p3/"), S("/p2/p4"), S("/p2/p4")} + , {S("/"), S(""), S("/")} + , {S("/p1"), S("/p2/"), S("/p2/")} + , {S("p1"), S(""), S("p1/")} + , {S("p1/"), S(""), S("p1/")} + }; + + +const AppendOperatorTestcase LongLHSCases[] = + { + {S("p1"), S("p2"), S("p1/p2")} + , {S("p1/"), S("p2"), S("p1/p2")} + , {S("p1"), S("/p2"), S("/p2")} + , {S("/p1"), S("p2"), S("/p1/p2")} + }; +#undef S + + +// The append operator may need to allocate a temporary buffer before a code_cvt +// conversion. Test if this allocation occurs by: +// 1. Create a path, `LHS`, and reserve enough space to append `RHS`. +// This prevents `LHS` from allocating during the actual appending. +// 2. Create a `Source` object `RHS`, which represents a "large" string. +// (The string must not trigger the SSO) +// 3. Append `RHS` to `LHS` and check for the expected allocation behavior. +template <class CharT> +void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) +{ + using namespace fs; + using Ptr = CharT const*; + using Str = std::basic_string<CharT>; + using StrView = std::basic_string_view<CharT>; + using InputIter = input_iterator<Ptr>; + + const Ptr L = TC.lhs; + Str RShort = (Ptr)TC.rhs; + Str EShort = (Ptr)TC.expect; + assert(RShort.size() >= 2); + CharT c = RShort.back(); + RShort.append(100, c); + EShort.append(100, c); + const Ptr R = RShort.data(); + const Str& E = EShort; + std::size_t ReserveSize = E.size() + 3; + // basic_string + { + path LHS(L); PathReserve(LHS, ReserveSize); + Str RHS(R); + { + DisableAllocationGuard g; + LHS /= RHS; + } + ASSERT_PRED(PathEq, LHS , E); + } + // basic_string_view + { + path LHS(L); PathReserve(LHS, ReserveSize); + StrView RHS(R); + { + DisableAllocationGuard g; + LHS /= RHS; + } + assert(PathEq(LHS, E)); + } + // CharT* + { + path LHS(L); PathReserve(LHS, ReserveSize); + Ptr RHS(R); + { + DisableAllocationGuard g; + LHS /= RHS; + } + assert(PathEq(LHS, E)); + } + { + path LHS(L); PathReserve(LHS, ReserveSize); + Ptr RHS(R); + { + DisableAllocationGuard g; + LHS.append(RHS, StrEnd(RHS)); + } + assert(PathEq(LHS, E)); + } + // input iterator - For non-native char types, appends needs to copy the + // iterator range into a contiguous block of memory before it can perform the + // code_cvt conversions. + // For "char" no allocations will be performed because no conversion is + // required. + bool DisableAllocations = std::is_same<CharT, char>::value; + { + path LHS(L); PathReserve(LHS, ReserveSize); + InputIter RHS(R); + { + RequireAllocationGuard g; // requires 1 or more allocations occur by default + if (DisableAllocations) g.requireExactly(0); + LHS /= RHS; + } + assert(PathEq(LHS, E)); + } + { + path LHS(L); PathReserve(LHS, ReserveSize); + InputIter RHS(R); + InputIter REnd(StrEnd(R)); + { + RequireAllocationGuard g; + if (DisableAllocations) g.requireExactly(0); + LHS.append(RHS, REnd); + } + assert(PathEq(LHS, E)); + } +} + +template <class CharT> +void doAppendSourceTest(AppendOperatorTestcase const& TC) +{ + using namespace fs; + using Ptr = CharT const*; + using Str = std::basic_string<CharT>; + using StrView = std::basic_string_view<CharT>; + using InputIter = input_iterator<Ptr>; + const Ptr L = TC.lhs; + const Ptr R = TC.rhs; + const Ptr E = TC.expect; + // basic_string + { + path Result(L); + Str RHS(R); + path& Ref = (Result /= RHS); + ASSERT_EQ(Result, E) + << DISPLAY(L) << DISPLAY(R); + assert(&Ref == &Result); + } + { + path LHS(L); + Str RHS(R); + path& Ref = LHS.append(RHS); + assert(PathEq(LHS, E)); + assert(&Ref == &LHS); + } + // basic_string_view + { + path LHS(L); + StrView RHS(R); + path& Ref = (LHS /= RHS); + assert(PathEq(LHS, E)); + assert(&Ref == &LHS); + } + { + path LHS(L); + StrView RHS(R); + path& Ref = LHS.append(RHS); + assert(PathEq(LHS, E)); + assert(&Ref == &LHS); + } + // Char* + { + path LHS(L); + Str RHS(R); + path& Ref = (LHS /= RHS); + assert(PathEq(LHS, E)); + assert(&Ref == &LHS); + } + { + path LHS(L); + Ptr RHS(R); + path& Ref = LHS.append(RHS); + assert(PathEq(LHS, E)); + assert(&Ref == &LHS); + } + { + path LHS(L); + Ptr RHS(R); + path& Ref = LHS.append(RHS, StrEnd(RHS)); + ASSERT_PRED(PathEq, LHS, E) + << DISPLAY(L) << DISPLAY(R); + assert(&Ref == &LHS); + } + // iterators + { + path LHS(L); + InputIter RHS(R); + path& Ref = (LHS /= RHS); + assert(PathEq(LHS, E)); + assert(&Ref == &LHS); + } + { + path LHS(L); InputIter RHS(R); + path& Ref = LHS.append(RHS); + assert(PathEq(LHS, E)); + assert(&Ref == &LHS); + } + { + path LHS(L); + InputIter RHS(R); + InputIter REnd(StrEnd(R)); + path& Ref = LHS.append(RHS, REnd); + assert(PathEq(LHS, E)); + assert(&Ref == &LHS); + } +} + + + +template <class It, class = decltype(fs::path{}.append(std::declval<It>()))> +constexpr bool has_append(int) { return true; } +template <class It> +constexpr bool has_append(long) { return false; } + +template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))> +constexpr bool has_append_op(int) { return true; } +template <class It> +constexpr bool has_append_op(long) { return false; } + +template <class It> +constexpr bool has_append() { + static_assert(has_append<It>(0) == has_append_op<It>(0), "must be same"); + return has_append<It>(0) && has_append_op<It>(0); +} + +void test_sfinae() +{ + using namespace fs; + { + using It = const char* const; + static_assert(has_append<It>(), ""); + } + { + using It = input_iterator<const char*>; + static_assert(has_append<It>(), ""); + } + { + struct Traits { + using iterator_category = std::input_iterator_tag; + using value_type = const char; + using pointer = const char*; + using reference = const char&; + using difference_type = std::ptrdiff_t; + }; + using It = input_iterator<const char*, Traits>; + static_assert(has_append<It>(), ""); + } + { + using It = output_iterator<const char*>; + static_assert(!has_append<It>(), ""); + + } + { + static_assert(!has_append<int*>(), ""); + } + { + static_assert(!has_append<char>(), ""); + static_assert(!has_append<const char>(), ""); + } +} + +int main() +{ + using namespace fs; + for (auto const & TC : Cases) { + { + const char* LHS_In = TC.lhs; + const char* RHS_In = TC.rhs; + path LHS(LHS_In); + path RHS(RHS_In); + path& Res = (LHS /= RHS); + ASSERT_PRED(PathEq, Res, (const char*)TC.expect) + << DISPLAY(LHS_In) << DISPLAY(RHS_In); + assert(&Res == &LHS); + } + doAppendSourceTest<char> (TC); + doAppendSourceTest<wchar_t> (TC); + doAppendSourceTest<char16_t>(TC); + doAppendSourceTest<char32_t>(TC); + } + for (auto const & TC : LongLHSCases) { + doAppendSourceAllocTest<char>(TC); + doAppendSourceAllocTest<wchar_t>(TC); + } + test_sfinae(); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/braced_init.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/braced_init.pass.cpp new file mode 100644 index 00000000000..fe677c3e654 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/braced_init.pass.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path& operator=(path const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "count_new.hpp" + + +int main() { + using namespace fs; + path p("abc"); + p = {}; + assert(p.native() == ""); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/copy.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/copy.pass.cpp new file mode 100644 index 00000000000..a0575e649d6 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/copy.pass.cpp @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path& operator=(path const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" + + +int main() { + using namespace fs; + static_assert(std::is_copy_assignable<path>::value, ""); + static_assert(!std::is_nothrow_copy_assignable<path>::value, "should not be noexcept"); + const std::string s("foo"); + const path p(s); + path p2; + path& pref = (p2 = p); + assert(p.native() == s); + assert(p2.native() == s); + assert(&pref == &p2); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp new file mode 100644 index 00000000000..71fa47d1b9d --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path& operator=(path&&) noexcept + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "count_new.hpp" + + +int main() { + using namespace fs; + static_assert(std::is_nothrow_move_assignable<path>::value, ""); + assert(globalMemCounter.checkOutstandingNewEq(0)); + const std::string s("we really really really really really really really " + "really really long string so that we allocate"); + assert(globalMemCounter.checkOutstandingNewEq(1)); + path p(s); + { + DisableAllocationGuard g; + path p2; + path& pref = (p2 = std::move(p)); + assert(p2.native() == s); + assert(p.native() != s); // Testing moved from state + assert(&pref == &p2); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/source.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/source.pass.cpp new file mode 100644 index 00000000000..92eff6e078a --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/source.pass.cpp @@ -0,0 +1,241 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// template <class Source> +// path& operator=(Source const&); +// path& operator=(string_type&&); +// template <class Source> +// path& assign(Source const&); +// template <class InputIterator> +// path& assign(InputIterator first, InputIterator last); + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <string_view> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" +#include <iostream> + + +template <class CharT> +void RunTestCase(MultiStringType const& MS) { + using namespace fs; + const char* Expect = MS; + const CharT* TestPath = MS; + const CharT* TestPathEnd = StrEnd(TestPath); + const std::size_t Size = TestPathEnd - TestPath; + const std::size_t SSize = StrEnd(Expect) - Expect; + assert(Size == SSize); + ////////////////////////////////////////////////////////////////////////////// + // basic_string<Char, Traits, Alloc> + { + const std::basic_string<CharT> S(TestPath); + path p; PathReserve(p, S.length() + 1); + { + // string provides a contiguous iterator. No allocation needed. + DisableAllocationGuard g; + path& pref = (p = S); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + assert(p.string<CharT>() == S); + } + { + const std::basic_string<CharT> S(TestPath); + path p; PathReserve(p, S.length() + 1); + { + DisableAllocationGuard g; + path& pref = p.assign(S); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + assert(p.string<CharT>() == S); + } + // basic_string<Char, Traits, Alloc> + { + const std::basic_string_view<CharT> S(TestPath); + path p; PathReserve(p, S.length() + 1); + { + // string provides a contiguous iterator. No allocation needed. + DisableAllocationGuard g; + path& pref = (p = S); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + assert(p.string<CharT>() == S); + } + { + const std::basic_string_view<CharT> S(TestPath); + path p; PathReserve(p, S.length() + 1); + { + DisableAllocationGuard g; + path& pref = p.assign(S); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + assert(p.string<CharT>() == S); + } + ////////////////////////////////////////////////////////////////////////////// + // Char* pointers + { + path p; PathReserve(p, Size + 1); + { + // char* pointers are contiguous and can be used with code_cvt directly. + // no allocations needed. + DisableAllocationGuard g; + path& pref = (p = TestPath); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + } + { + path p; PathReserve(p, Size + 1); + { + DisableAllocationGuard g; + path& pref = p.assign(TestPath); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + } + { + path p; PathReserve(p, Size + 1); + { + DisableAllocationGuard g; + path& pref = p.assign(TestPath, TestPathEnd); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + } + ////////////////////////////////////////////////////////////////////////////// + // Iterators + { + using It = input_iterator<const CharT*>; + path p; PathReserve(p, Size + 1); + It it(TestPath); + { + // Iterators cannot be used with code_cvt directly. This assignment + // may allocate if it's larger than a "short-string". + path& pref = (p = it); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + } + { + using It = input_iterator<const CharT*>; + path p; PathReserve(p, Size + 1); + It it(TestPath); + { + path& pref = p.assign(it); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + } + { + using It = input_iterator<const CharT*>; + path p; PathReserve(p, Size + 1); + It it(TestPath); + It e(TestPathEnd); + { + path& pref = p.assign(it, e); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + } +} + +template <class It, class = decltype(fs::path{}.assign(std::declval<It>()))> +constexpr bool has_assign(int) { return true; } +template <class It> +constexpr bool has_assign(long) { return false; } +template <class It> +constexpr bool has_assign() { return has_assign<It>(0); } + +void test_sfinae() { + using namespace fs; + { + using It = const char* const; + static_assert(std::is_assignable<path, It>::value, ""); + static_assert(has_assign<It>(), ""); + } + { + using It = input_iterator<const char*>; + static_assert(std::is_assignable<path, It>::value, ""); + static_assert(has_assign<It>(), ""); + } + { + struct Traits { + using iterator_category = std::input_iterator_tag; + using value_type = const char; + using pointer = const char*; + using reference = const char&; + using difference_type = std::ptrdiff_t; + }; + using It = input_iterator<const char*, Traits>; + static_assert(std::is_assignable<path, It>::value, ""); + static_assert(has_assign<It>(), ""); + } + { + using It = output_iterator<const char*>; + static_assert(!std::is_assignable<path, It>::value, ""); + static_assert(!has_assign<It>(), ""); + + } + { + static_assert(!std::is_assignable<path, int*>::value, ""); + static_assert(!has_assign<int*>(), ""); + } +} + +void RunStringMoveTest(const char* Expect) { + using namespace fs; + std::string ss(Expect); + path p; + { + DisableAllocationGuard g; ((void)g); + path& pr = (p = std::move(ss)); + assert(&pr == &p); + } + assert(p == Expect); + { + // Signature test + ASSERT_NOEXCEPT(p = std::move(ss)); + } +} + +int main() { + for (auto const& MS : PathList) { + RunTestCase<char>(MS); + RunTestCase<wchar_t>(MS); + RunTestCase<char16_t>(MS); + RunTestCase<char32_t>(MS); + RunStringMoveTest(MS); + } + test_sfinae(); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.compare.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.compare.pass.cpp new file mode 100644 index 00000000000..7791097dcd1 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.compare.pass.cpp @@ -0,0 +1,138 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// int compare(path const&) const noexcept; +// int compare(string_type const&) const; +// int compare(value_type const*) const; +// +// bool operator==(path const&, path const&) noexcept; +// bool operator!=(path const&, path const&) noexcept; +// bool operator< (path const&, path const&) noexcept; +// bool operator<=(path const&, path const&) noexcept; +// bool operator> (path const&, path const&) noexcept; +// bool operator>=(path const&, path const&) noexcept; +// +// size_t hash_value(path const&) noexcept; + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" +#include "verbose_assert.h" + +struct PathCompareTest { + const char* LHS; + const char* RHS; + int expect; +}; + +#define LONGA "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +#define LONGB "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" +#define LONGC "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" +#define LONGD "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" +const PathCompareTest CompareTestCases[] = +{ + {"", "", 0}, + {"a", "", 1}, + {"", "a", -1}, + {"a/b/c", "a/b/c", 0}, + {"b/a/c", "a/b/c", 1}, + {"a/b/c", "b/a/c", -1}, + {"a/b", "a/b/c", -1}, + {"a/b/c", "a/b", 1}, + {"a/b/", "a/b/.", -1}, + {"a/b/", "a/b", 1}, + {"a/b//////", "a/b/////.", -1}, + {"a/.././b", "a///..//.////b", 0}, + {"//foo//bar///baz////", "//foo/bar/baz/", 0}, // duplicate separators + {"///foo/bar", "/foo/bar", 0}, // "///" is not a root directory + {"/foo/bar/", "/foo/bar", 1}, // trailing separator + {"//" LONGA "////" LONGB "/" LONGC "///" LONGD, "//" LONGA "/" LONGB "/" LONGC "/" LONGD, 0}, + { LONGA "/" LONGB "/" LONGC, LONGA "/" LONGB "/" LONGB, 1} + +}; +#undef LONGA +#undef LONGB +#undef LONGC +#undef LONGD + +static inline int normalize_ret(int ret) +{ + return ret < 0 ? -1 : (ret > 0 ? 1 : 0); +} + +int main() +{ + using namespace fs; + for (auto const & TC : CompareTestCases) { + const path p1(TC.LHS); + const path p2(TC.RHS); + const std::string R(TC.RHS); + const std::string_view RV(TC.RHS); + const int E = TC.expect; + { // compare(...) functions + DisableAllocationGuard g; // none of these operations should allocate + + // check runtime results + int ret1 = normalize_ret(p1.compare(p2)); + int ret2 = normalize_ret(p1.compare(R)); + int ret3 = normalize_ret(p1.compare(TC.RHS)); + int ret4 = normalize_ret(p1.compare(RV)); + + g.release(); + ASSERT_EQ(ret1, ret2); + ASSERT_EQ(ret1, ret3); + ASSERT_EQ(ret1, ret4); + ASSERT_EQ(ret1, E) + << DISPLAY(TC.LHS) << DISPLAY(TC.RHS); + + // check signatures + ASSERT_NOEXCEPT(p1.compare(p2)); + } + { // comparison operators + DisableAllocationGuard g; // none of these operations should allocate + + // Check runtime result + assert((p1 == p2) == (E == 0)); + assert((p1 != p2) == (E != 0)); + assert((p1 < p2) == (E < 0)); + assert((p1 <= p2) == (E <= 0)); + assert((p1 > p2) == (E > 0)); + assert((p1 >= p2) == (E >= 0)); + + // Check signatures + ASSERT_NOEXCEPT(p1 == p2); + ASSERT_NOEXCEPT(p1 != p2); + ASSERT_NOEXCEPT(p1 < p2); + ASSERT_NOEXCEPT(p1 <= p2); + ASSERT_NOEXCEPT(p1 > p2); + ASSERT_NOEXCEPT(p1 >= p2); + } + { // check hash values + auto h1 = hash_value(p1); + auto h2 = hash_value(p2); + assert((h1 == h2) == (p1 == p2)); + // check signature + ASSERT_SAME_TYPE(size_t, decltype(hash_value(p1))); + ASSERT_NOEXCEPT(hash_value(p1)); + } + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.concat.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.concat.pass.cpp new file mode 100644 index 00000000000..03d5134fe09 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.concat.pass.cpp @@ -0,0 +1,388 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path& operator+=(const path& x); +// path& operator+=(const string_type& x); +// path& operator+=(string_view x); +// path& operator+=(const value_type* x); +// path& operator+=(value_type x); +// template <class Source> +// path& operator+=(const Source& x); +// template <class EcharT> +// path& operator+=(EcharT x); +// template <class Source> +// path& concat(const Source& x); +// template <class InputIterator> +// path& concat(InputIterator first, InputIterator last); + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <string> +#include <string_view> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +struct ConcatOperatorTestcase { + MultiStringType lhs; + MultiStringType rhs; + MultiStringType expect; +}; + +#define LONGSTR "LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR" +#define S(Str) MKSTR(Str) +const ConcatOperatorTestcase Cases[] = + { + {S(""), S(""), S("")} + , {S("p1"), S("p2"), S("p1p2")} + , {S("p1/"), S("/p2"), S("p1//p2")} + , {S(""), S("\\foo/bar/baz"), S("\\foo/bar/baz")} + , {S("c:\\foo"), S(""), S("c:\\foo")} + , {S(LONGSTR), S("foo"), S(LONGSTR "foo")} + , {S("abcdefghijklmnopqrstuvwxyz/\\"), S("/\\123456789"), S("abcdefghijklmnopqrstuvwxyz/\\/\\123456789")} + }; +const ConcatOperatorTestcase LongLHSCases[] = + { + {S(""), S(LONGSTR), S(LONGSTR)} + , {S("p1/"), S(LONGSTR), S("p1/" LONGSTR)} + }; +const ConcatOperatorTestcase CharTestCases[] = + { + {S(""), S("P"), S("P")} + , {S("/fooba"), S("r"), S("/foobar")} + }; +#undef S +#undef LONGSTR + +// The concat operator may need to allocate a temporary buffer before a code_cvt +// conversion. Test if this allocation occurs by: +// 1. Create a path, `LHS`, and reserve enough space to append `RHS`. +// This prevents `LHS` from allocating during the actual appending. +// 2. Create a `Source` object `RHS`, which represents a "large" string. +// (The string must not trigger the SSO) +// 3. Concat `RHS` to `LHS` and check for the expected allocation behavior. +template <class CharT> +void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC) +{ + using namespace fs; + using Ptr = CharT const*; + using Str = std::basic_string<CharT>; + using StrView = std::basic_string_view<CharT>; + using InputIter = input_iterator<Ptr>; + + const Ptr L = TC.lhs; + const Ptr R = TC.rhs; + const Ptr E = TC.expect; + std::size_t ReserveSize = StrLen(E) + 1; + // basic_string + { + path LHS(L); PathReserve(LHS, ReserveSize); + Str RHS(R); + { + DisableAllocationGuard g; + LHS += RHS; + } + assert(LHS == E); + } + // basic_string_view + { + path LHS(L); PathReserve(LHS, ReserveSize); + StrView RHS(R); + { + DisableAllocationGuard g; + LHS += RHS; + } + assert(LHS == E); + } + // CharT* + { + path LHS(L); PathReserve(LHS, ReserveSize); + Ptr RHS(R); + { + DisableAllocationGuard g; + LHS += RHS; + } + assert(LHS == E); + } + { + path LHS(L); PathReserve(LHS, ReserveSize); + Ptr RHS(R); + { + DisableAllocationGuard g; + LHS.concat(RHS, StrEnd(RHS)); + } + assert(LHS == E); + } + // input iterator - For non-native char types, appends needs to copy the + // iterator range into a contiguous block of memory before it can perform the + // code_cvt conversions. + // For "char" no allocations will be performed because no conversion is + // required. + bool DisableAllocations = std::is_same<CharT, char>::value; + { + path LHS(L); PathReserve(LHS, ReserveSize); + InputIter RHS(R); + { + RequireAllocationGuard g; // requires 1 or more allocations occur by default + if (DisableAllocations) g.requireExactly(0); + LHS += RHS; + } + assert(LHS == E); + } + { + path LHS(L); PathReserve(LHS, ReserveSize); + InputIter RHS(R); + InputIter REnd(StrEnd(R)); + { + RequireAllocationGuard g; + if (DisableAllocations) g.requireExactly(0); + LHS.concat(RHS, REnd); + } + assert(LHS == E); + } +} + +template <class CharT> +void doConcatSourceTest(ConcatOperatorTestcase const& TC) +{ + using namespace fs; + using Ptr = CharT const*; + using Str = std::basic_string<CharT>; + using StrView = std::basic_string_view<CharT>; + using InputIter = input_iterator<Ptr>; + const Ptr L = TC.lhs; + const Ptr R = TC.rhs; + const Ptr E = TC.expect; + // basic_string + { + path LHS(L); + Str RHS(R); + path& Ref = (LHS += RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } + { + path LHS(L); + Str RHS(R); + path& Ref = LHS.concat(RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } + // basic_string_view + { + path LHS(L); + StrView RHS(R); + path& Ref = (LHS += RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } + { + path LHS(L); + StrView RHS(R); + path& Ref = LHS.concat(RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } + // Char* + { + path LHS(L); + Str RHS(R); + path& Ref = (LHS += RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } + { + path LHS(L); + Ptr RHS(R); + path& Ref = LHS.concat(RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } + { + path LHS(L); + Ptr RHS(R); + path& Ref = LHS.concat(RHS, StrEnd(RHS)); + assert(LHS == E); + assert(&Ref == &LHS); + } + // iterators + { + path LHS(L); + InputIter RHS(R); + path& Ref = (LHS += RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } + { + path LHS(L); InputIter RHS(R); + path& Ref = LHS.concat(RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } + { + path LHS(L); + InputIter RHS(R); + InputIter REnd(StrEnd(R)); + path& Ref = LHS.concat(RHS, REnd); + assert(LHS == E); + assert(&Ref == &LHS); + } +} + +template <class CharT> +void doConcatECharTest(ConcatOperatorTestcase const& TC) +{ + using namespace fs; + using Ptr = CharT const*; + const Ptr RStr = TC.rhs; + assert(StrLen(RStr) == 1); + const Ptr L = TC.lhs; + const CharT R = RStr[0]; + const Ptr E = TC.expect; + { + path LHS(L); + path& Ref = (LHS += R); + assert(LHS == E); + assert(&Ref == &LHS); + } +} + + +template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))> +constexpr bool has_concat(int) { return true; } +template <class It> +constexpr bool has_concat(long) { return false; } + +template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))> +constexpr bool has_concat_op(int) { return true; } +template <class It> +constexpr bool has_concat_op(long) { return false; } +template <class It> +constexpr bool has_concat_op() { return has_concat_op<It>(0); } + +template <class It> +constexpr bool has_concat() { + static_assert(has_concat<It>(0) == has_concat_op<It>(0), "must be same"); + return has_concat<It>(0) && has_concat_op<It>(0); +} + +void test_sfinae() { + using namespace fs; + { + static_assert(has_concat_op<char>(), ""); + static_assert(has_concat_op<const char>(), ""); + static_assert(has_concat_op<char16_t>(), ""); + static_assert(has_concat_op<const char16_t>(), ""); + } + { + using It = const char* const; + static_assert(has_concat<It>(), ""); + } + { + using It = input_iterator<const char*>; + static_assert(has_concat<It>(), ""); + } + { + struct Traits { + using iterator_category = std::input_iterator_tag; + using value_type = const char; + using pointer = const char*; + using reference = const char&; + using difference_type = std::ptrdiff_t; + }; + using It = input_iterator<const char*, Traits>; + static_assert(has_concat<It>(), ""); + } + { + using It = output_iterator<const char*>; + static_assert(!has_concat<It>(), ""); + } + { + static_assert(!has_concat<int>(0), ""); + // operator+=(int) is well formed since it converts to operator+=(value_type) + // but concat(int) isn't valid because there is no concat(value_type). + // This should probably be addressed by a LWG issue. + static_assert(has_concat_op<int>(), ""); + } + { + static_assert(!has_concat<int*>(), ""); + } +} + +int main() +{ + using namespace fs; + for (auto const & TC : Cases) { + { + path LHS((const char*)TC.lhs); + path RHS((const char*)TC.rhs); + path& Ref = (LHS += RHS); + assert(LHS == (const char*)TC.expect); + assert(&Ref == &LHS); + } + { + path LHS((const char*)TC.lhs); + std::string_view RHS((const char*)TC.rhs); + path& Ref = (LHS += RHS); + assert(LHS == (const char*)TC.expect); + assert(&Ref == &LHS); + } + doConcatSourceTest<char> (TC); + doConcatSourceTest<wchar_t> (TC); + doConcatSourceTest<char16_t>(TC); + doConcatSourceTest<char32_t>(TC); + } + for (auto const & TC : LongLHSCases) { + // Do path test + { + path LHS((const char*)TC.lhs); + path RHS((const char*)TC.rhs); + const char* E = TC.expect; + PathReserve(LHS, StrLen(E) + 5); + { + DisableAllocationGuard g; + path& Ref = (LHS += RHS); + assert(&Ref == &LHS); + } + assert(LHS == E); + } + { + path LHS((const char*)TC.lhs); + std::string_view RHS((const char*)TC.rhs); + const char* E = TC.expect; + PathReserve(LHS, StrLen(E) + 5); + { + DisableAllocationGuard g; + path& Ref = (LHS += RHS); + assert(&Ref == &LHS); + } + assert(LHS == E); + } + doConcatSourceAllocTest<char>(TC); + doConcatSourceAllocTest<wchar_t>(TC); + } + for (auto const& TC : CharTestCases) { + doConcatECharTest<char>(TC); + doConcatECharTest<wchar_t>(TC); + doConcatECharTest<char16_t>(TC); + doConcatECharTest<char32_t>(TC); + } + test_sfinae(); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/copy.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/copy.pass.cpp new file mode 100644 index 00000000000..69bdb74159f --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/copy.pass.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path(path const&) + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" + + +int main() { + using namespace fs; + static_assert(std::is_copy_constructible<path>::value, ""); + static_assert(!std::is_nothrow_copy_constructible<path>::value, "should not be noexcept"); + const std::string s("foo"); + const path p(s); + path p2(p); + assert(p.native() == s); + assert(p2.native() == s); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/default.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/default.pass.cpp new file mode 100644 index 00000000000..aa4b03e9bc3 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/default.pass.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path() noexcept + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" + + +int main() { + using namespace fs; + static_assert(std::is_nothrow_default_constructible<path>::value, ""); + const path p; + assert(p.empty()); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp new file mode 100644 index 00000000000..2e8cce22880 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path(path&&) noexcept + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "count_new.hpp" + + +int main() { + using namespace fs; + static_assert(std::is_nothrow_move_constructible<path>::value, ""); + assert(globalMemCounter.checkOutstandingNewEq(0)); + const std::string s("we really really really really really really really " + "really really long string so that we allocate"); + assert(globalMemCounter.checkOutstandingNewEq(1)); + path p(s); + { + DisableAllocationGuard g; + path p2(std::move(p)); + assert(p2.native() == s); + assert(p.native() != s); // Testing moved from state + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source.pass.cpp new file mode 100644 index 00000000000..39d63f456e9 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source.pass.cpp @@ -0,0 +1,129 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// template <class Source> +// path(const Source& source); +// template <class InputIterator> +// path(InputIterator first, InputIterator last); + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "min_allocator.h" +#include "filesystem_test_helper.hpp" + + +template <class CharT, class ...Args> +void RunTestCaseImpl(MultiStringType const& MS, Args... args) { + using namespace fs; + const char* Expect = MS; + const CharT* TestPath = MS; + const CharT* TestPathEnd = StrEnd(TestPath); + const std::size_t Size = TestPathEnd - TestPath; + const std::size_t SSize = StrEnd(Expect) - Expect; + assert(Size == SSize); + // StringTypes + { + const std::basic_string<CharT> S(TestPath); + path p(S, args...); + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + assert(p.string<CharT>() == S); + } + { + const std::basic_string_view<CharT> S(TestPath); + path p(S, args...); + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + assert(p.string<CharT>() == S); + } + // Char* pointers + { + path p(TestPath, args...); + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + } + { + path p(TestPath, TestPathEnd, args...); + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + } + // Iterators + { + using It = input_iterator<const CharT*>; + path p(It{TestPath}, args...); + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + } + { + using It = input_iterator<const CharT*>; + path p(It{TestPath}, It{TestPathEnd}, args...); + assert(p.native() == Expect); + assert(p.string<CharT>() == TestPath); + } +} + +template <class CharT, class ...Args> +void RunTestCase(MultiStringType const& MS) { + RunTestCaseImpl<CharT>(MS); + RunTestCaseImpl<CharT>(MS, fs::path::format::auto_format); + RunTestCaseImpl<CharT>(MS, fs::path::format::native_format); + RunTestCaseImpl<CharT>(MS, fs::path::format::generic_format); +} + +void test_sfinae() { + using namespace fs; + { + using It = const char* const; + static_assert(std::is_constructible<path, It>::value, ""); + } + { + using It = input_iterator<const char*>; + static_assert(std::is_constructible<path, It>::value, ""); + } + { + struct Traits { + using iterator_category = std::input_iterator_tag; + using value_type = const char; + using pointer = const char*; + using reference = const char&; + using difference_type = std::ptrdiff_t; + }; + using It = input_iterator<const char*, Traits>; + static_assert(std::is_constructible<path, It>::value, ""); + } + { + using It = output_iterator<const char*>; + static_assert(!std::is_constructible<path, It>::value, ""); + + } + { + static_assert(!std::is_constructible<path, int*>::value, ""); + } +} + +int main() { + for (auto const& MS : PathList) { + RunTestCase<char>(MS); + RunTestCase<wchar_t>(MS); + RunTestCase<char16_t>(MS); + RunTestCase<char32_t>(MS); + } + test_sfinae(); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.decompose/empty.fail.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.decompose/empty.fail.cpp new file mode 100644 index 00000000000..1a722d7a2a0 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.decompose/empty.fail.cpp @@ -0,0 +1,28 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// <filesystem> + +// class path + +// bool empty() const noexcept; + +// UNSUPPORTED: c++98, c++03, c++11, c++14, c++17 +// UNSUPPORTED: clang-3.3, clang-3.4, clang-3.5, clang-3.6, clang-3.7, clang-3.8 + +#include "filesystem_include.hpp" + +#include "test_macros.h" + +int main () +{ + fs::path c; + c.empty(); // expected-error {{ignoring return value of function declared with 'nodiscard' attribute}} +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.decompose/path.decompose.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.decompose/path.decompose.pass.cpp new file mode 100644 index 00000000000..4eb15cd0bf2 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.decompose/path.decompose.pass.cpp @@ -0,0 +1,216 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// 8.4.9 path decomposition [path.decompose] +//------------------------------------------ +// path root_name() const; +// path root_directory() const; +// path root_path() const; +// path relative_path() const; +// path parent_path() const; +// path filename() const; +// path stem() const; +// path extension() const; +//------------------------------- +// 8.4.10 path query [path.query] +//------------------------------- +// bool empty() const noexcept; +// bool has_root_path() const; +// bool has_root_name() const; +// bool has_root_directory() const; +// bool has_relative_path() const; +// bool has_parent_path() const; +// bool has_filename() const; +// bool has_stem() const; +// bool has_extension() const; +// bool is_absolute() const; +// bool is_relative() const; +//------------------------------- +// 8.5 path iterators [path.itr] +//------------------------------- +// iterator begin() const; +// iterator end() const; + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" +#include "assert_checkpoint.h" +#include "verbose_assert.h" + +struct ComparePathExact { + bool operator()(std::string const& LHS, std::string const& RHS) const { + return LHS == RHS; + } +}; + +struct PathDecomposeTestcase +{ + std::string raw; + std::vector<std::string> elements; + std::string root_path; + std::string root_name; + std::string root_directory; + std::string relative_path; + std::string parent_path; + std::string filename; +}; + +const PathDecomposeTestcase PathTestCases[] = + { + {"", {}, "", "", "", "", "", ""} + , {".", {"."}, "", "", "", ".", "", "."} + , {"..", {".."}, "", "", "", "..", "", ".."} + , {"foo", {"foo"}, "", "", "", "foo", "", "foo"} + , {"/", {"/"}, "/", "", "/", "", "/", ""} + , {"/foo", {"/", "foo"}, "/", "", "/", "foo", "/", "foo"} + , {"foo/", {"foo", ""}, "", "", "", "foo/", "foo", ""} + , {"/foo/", {"/", "foo", ""}, "/", "", "/", "foo/", "/foo", ""} + , {"foo/bar", {"foo","bar"}, "", "", "", "foo/bar", "foo", "bar"} + , {"/foo//bar", {"/","foo","bar"}, "/", "", "/", "foo/bar", "/foo", "bar"} + , {"//net", {"/", "net"}, "/", "", "/", "net", "/", "net"} + , {"//net/foo", {"/", "net", "foo"}, "/", "", "/", "net/foo", "/net", "foo"} + , {"///foo///", {"/", "foo", ""}, "/", "", "/", "foo///", "///foo", ""} + , {"///foo///bar", {"/", "foo", "bar"}, "/", "", "/", "foo///bar", "///foo", "bar"} + , {"/.", {"/", "."}, "/", "", "/", ".", "/", "."} + , {"./", {".", ""}, "", "", "", "./", ".", ""} + , {"/..", {"/", ".."}, "/", "", "/", "..", "/", ".."} + , {"../", {"..", ""}, "", "", "", "../", "..", ""} + , {"foo/.", {"foo", "."}, "", "", "", "foo/.", "foo", "."} + , {"foo/..", {"foo", ".."}, "", "", "", "foo/..", "foo", ".."} + , {"foo/./", {"foo", ".", ""}, "", "", "", "foo/./", "foo/.", ""} + , {"foo/./bar", {"foo", ".", "bar"}, "", "", "", "foo/./bar", "foo/.", "bar"} + , {"foo/../", {"foo", "..", ""}, "", "", "", "foo/../", "foo/..", ""} + , {"foo/../bar", {"foo", "..", "bar"}, "", "", "", "foo/../bar", "foo/..", "bar"} + , {"c:", {"c:"}, "", "", "", "c:", "", "c:"} + , {"c:/", {"c:", ""}, "", "", "", "c:/", "c:", ""} + , {"c:foo", {"c:foo"}, "", "", "", "c:foo", "", "c:foo"} + , {"c:/foo", {"c:", "foo"}, "", "", "", "c:/foo", "c:", "foo"} + , {"c:foo/", {"c:foo", ""}, "", "", "", "c:foo/", "c:foo", ""} + , {"c:/foo/", {"c:", "foo", ""}, "", "", "", "c:/foo/", "c:/foo", ""} + , {"c:/foo/bar", {"c:", "foo", "bar"}, "", "", "", "c:/foo/bar", "c:/foo", "bar"} + , {"prn:", {"prn:"}, "", "", "", "prn:", "", "prn:"} + , {"c:\\", {"c:\\"}, "", "", "", "c:\\", "", "c:\\"} + , {"c:\\foo", {"c:\\foo"}, "", "", "", "c:\\foo", "", "c:\\foo"} + , {"c:foo\\", {"c:foo\\"}, "", "", "", "c:foo\\", "", "c:foo\\"} + , {"c:\\foo\\", {"c:\\foo\\"}, "", "", "", "c:\\foo\\", "", "c:\\foo\\"} + , {"c:\\foo/", {"c:\\foo", ""}, "", "", "", "c:\\foo/", "c:\\foo", ""} + , {"c:/foo\\bar", {"c:", "foo\\bar"}, "", "", "", "c:/foo\\bar", "c:", "foo\\bar"} + , {"//", {"/"}, "/", "", "/", "", "/", ""} + }; + +void decompPathTest() +{ + using namespace fs; + for (auto const & TC : PathTestCases) { + CHECKPOINT(TC.raw.c_str()); + fs::path p(TC.raw); + ASSERT(p == TC.raw); + + ASSERT_EQ(p.root_path(), TC.root_path); + ASSERT_NEQ(p.has_root_path(), TC.root_path.empty()); + + ASSERT(p.root_name().native().empty()) + << DISPLAY(p.root_name()); + ASSERT_EQ(p.root_name(),TC.root_name); + ASSERT_NEQ(p.has_root_name(), TC.root_name.empty()); + + ASSERT_EQ(p.root_directory(), TC.root_directory); + ASSERT_NEQ(p.has_root_directory(), TC.root_directory.empty()); + + ASSERT_EQ(p.relative_path(), TC.relative_path); + ASSERT_NEQ(p.has_relative_path(), TC.relative_path.empty()); + + ASSERT_EQ(p.parent_path(), TC.parent_path); + ASSERT_NEQ(p.has_parent_path(), TC.parent_path.empty()); + + ASSERT_EQ(p.filename(), TC.filename); + ASSERT_NEQ(p.has_filename(), TC.filename.empty()); + + ASSERT_EQ(p.is_absolute(), p.has_root_directory()); + ASSERT_NEQ(p.is_relative(), p.is_absolute()); + if (p.empty()) + ASSERT(p.is_relative()); + + ASSERT_COLLECTION_EQ_COMP( + p.begin(), p.end(), + TC.elements.begin(), TC.elements.end(), + ComparePathExact() + ); + // check backwards + + std::vector<fs::path> Parts; + for (auto it = p.end(); it != p.begin(); ) + Parts.push_back(*--it); + ASSERT_COLLECTION_EQ_COMP(Parts.begin(), Parts.end(), + TC.elements.rbegin(), TC.elements.rend(), + ComparePathExact()); + } +} + + +struct FilenameDecompTestcase +{ + std::string raw; + std::string filename; + std::string stem; + std::string extension; +}; + +const FilenameDecompTestcase FilenameTestCases[] = +{ + {"", "", "", ""} + , {".", ".", ".", ""} + , {"..", "..", "..", ""} + , {"/", "", "", ""} + , {"foo", "foo", "foo", ""} + , {"/foo/bar.txt", "bar.txt", "bar", ".txt"} + , {"foo..txt", "foo..txt", "foo.", ".txt"} + , {".profile", ".profile", ".profile", ""} + , {".profile.txt", ".profile.txt", ".profile", ".txt"} +}; + + +void decompFilenameTest() +{ + using namespace fs; + for (auto const & TC : FilenameTestCases) { + CHECKPOINT(TC.raw.c_str()); + fs::path p(TC.raw); + ASSERT_EQ(p, TC.raw); + ASSERT_NOEXCEPT(p.empty()); + + ASSERT_EQ(p.filename(), TC.filename); + ASSERT_NEQ(p.has_filename(), TC.filename.empty()); + + ASSERT_EQ(p.stem(), TC.stem); + ASSERT_NEQ(p.has_stem(), TC.stem.empty()); + + ASSERT_EQ(p.extension(), TC.extension); + ASSERT_NEQ(p.has_extension(), TC.extension.empty()); + } +} + +int main() +{ + decompPathTest(); + decompFilenameTest(); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_normal.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_normal.pass.cpp new file mode 100644 index 00000000000..3e2fdd08de1 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_normal.pass.cpp @@ -0,0 +1,142 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path lexically_normal() const; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <iostream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +int main() { + // clang-format off + struct { + std::string input; + std::string expect; + } TestCases[] = { + {"", ""}, + {"/a/b/c", "/a/b/c"}, + {"/a/b//c", "/a/b/c"}, + {"foo/./bar/..", "foo/"}, + {"foo/.///bar/../", "foo/"}, + {"/a/b/", "/a/b/"}, + {"a/b", "a/b"}, + {"a/b/.", "a/b/"}, + {"a/b/./", "a/b/"}, + {"a/..", "."}, + {".", "."}, + {"./", "."}, + {"./.", "."}, + {"./..", ".."}, + {"..", ".."}, + {"../..", "../.."}, + {"/../", "/"}, + {"/../..", "/"}, + {"/../../", "/"}, + {"..", ".."}, + {"../", ".."}, + {"/a/b/c/../", "/a/b/"}, + {"/a/b/./", "/a/b/"}, + {"/a/b/c/../d", "/a/b/d"}, + {"/a/b/c/../d/", "/a/b/d/"}, + {"//a/", "/a/"}, + {"//a/b/", "/a/b/"}, + {"//a/b/.", "/a/b/"}, + {"//a/..", "/"}, + ///===---------------------------------------------------------------===// + /// Tests specifically for the clauses under [fs.path.generic]p6 + ///===---------------------------------------------------------------===// + // p1: If the path is empty, stop. + {"", ""}, + // p2: Replace each slash character in the root-name with a preferred + // separator. + {"NO_ROOT_NAME_ON_LINUX", "NO_ROOT_NAME_ON_LINUX"}, + // p3: Replace each directory-separator with a preferred-separator. + // [ Note: The generic pathname grammar ([fs.path.generic]) defines + // directory-separator as one or more slashes and preferred-separators. + // — end note ] + {"/", "/"}, + {"//", "/"}, + {"///", "/"}, + {"a/b", "a/b"}, + {"a//b", "a/b"}, + {"a///b", "a/b"}, + {"a/b/", "a/b/"}, + {"a/b//", "a/b/"}, + {"a/b///", "a/b/"}, + {"///a////b//////", "/a/b/"}, + // p4: Remove each dot filename and any immediately following directory + // separators + {"foo/.", "foo/"}, + {"foo/./bar/.", "foo/bar/"}, + {"./foo/././bar/./", "foo/bar/"}, + {".///foo//.////./bar/.///", "foo/bar/"}, + // p5: As long as any appear, remove a non-dot-dot filename immediately + // followed by a directory-separator and a dot-dot filename, along with + // any immediately following directory separator. + {"foo/..", "."}, + {"foo/../", "."}, + {"foo/bar/..", "foo/"}, + {"foo/bar/../", "foo/"}, + {"foo/bar/../..", "."}, + {"foo/bar/../../", "."}, + {"foo/bar/baz/../..", "foo/"}, + {"foo/bar/baz/../../", "foo/"}, + {"foo/bar/./..", "foo/"}, + {"foo/bar/./../", "foo/"}, + // p6: If there is a root-directory, remove all dot-dot filenames and any + // directory-separators immediately following them. [ Note: These dot-dot + // filenames attempt to refer to nonexistent parent directories. — end note ] + {"/..", "/"}, + {"/../", "/"}, + {"/foo/../..", "/"}, + {"/../foo", "/foo"}, + {"/../foo/../..", "/"}, + // p7: If the last filename is dot-dot, remove any trailing + // directory-separator. + {"../", ".."}, + {"../../", "../.."}, + {"foo/../bar/../..///", ".."}, + {"foo/../bar/..//..///../", "../.."}, + // p8: If the path is empty, add a dot + {".", "."}, + {"./", "."}, + {"foo/..", "."} + }; + // clang-format on + int ID = 0; + bool Failed = false; + for (auto& TC : TestCases) { + ++ID; + fs::path p(TC.input); + const fs::path output = p.lexically_normal(); + if (!PathEq(output, TC.expect)) { + Failed = true; + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Expected: '" << TC.expect << "'\n"; + std::cerr << " Output: '" << output.native() << "'"; + std::cerr << std::endl; + } + } + return Failed; +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp new file mode 100644 index 00000000000..52c577bc8a7 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path lexically_relative(const path& p) const; +// path lexically_proximate(const path& p) const; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <iostream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +int main() { + // clang-format off + struct { + std::string input; + std::string base; + std::string expect; + } TestCases[] = { + {"", "", "."}, + {"/", "a", ""}, + {"a", "/", ""}, + {"//net", "a", ""}, + {"a", "//net", ""}, + {"//net/", "//net", ""}, + {"//net", "//net/", ".."}, + {"//base", "a", ""}, + {"a", "a", "."}, + {"a/b", "a/b", "."}, + {"a/b/c/", "a/b/c/", "."}, + {"//net", "//net", "."}, + {"//net/", "//net/", "."}, + {"//net/a/b", "//net/a/b", "."}, + {"/a/d", "/a/b/c", "../../d"}, + {"/a/b/c", "/a/d", "../b/c"}, + {"a/b/c", "a", "b/c"}, + {"a/b/c", "a/b/c/x/y", "../.."}, + {"a/b/c", "a/b/c", "."}, + {"a/b", "c/d", "../../a/b"} + }; + // clang-format on + int ID = 0; + bool Failed = false; + for (auto& TC : TestCases) { + ++ID; + const fs::path p(TC.input); + const fs::path output = p.lexically_relative(TC.base); + auto ReportErr = [&](const char* Testing, fs::path const& Output, + fs::path const& Expected) { + Failed = true; + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Testing: " << Testing << "\n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Base: '" << TC.base << "'\n"; + std::cerr << " Expected: '" << Expected << "'\n"; + std::cerr << " Output: '" << Output.native() << "'"; + std::cerr << std::endl; + }; + if (!PathEq(output, TC.expect)) + ReportErr("path::lexically_relative", output, TC.expect); + const fs::path proximate_output = p.lexically_proximate(TC.base); + // [path.gen] lexically_proximate + // Returns: If the value of lexically_relative(base) is not an empty path, + // return it.Otherwise return *this. + const fs::path proximate_expected = output.native().empty() ? p + : output; + if (!PathEq(proximate_expected, proximate_output)) + ReportErr("path::lexically_proximate", proximate_output, proximate_expected); + } + return Failed; +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp new file mode 100644 index 00000000000..93c49a6ab61 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.generic.obs/generic_string_alloc.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// template <class ECharT, class Traits = char_traits<ECharT>, +// class Allocator = allocator<ECharT>> +// basic_string<ECharT, Traits, Allocator> +// generic_string(const Allocator& a = Allocator()) const; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "min_allocator.h" +#include "filesystem_test_helper.hpp" + +MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + +// generic_string<C, T, A> forwards to string<C, T, A>. Tests for +// string<C, T, A>() are in "path.native.op/string_alloc.pass.cpp". +// generic_string is minimally tested here. +int main() +{ + using namespace fs; + using CharT = wchar_t; + using Traits = std::char_traits<CharT>; + using Alloc = malloc_allocator<CharT>; + using Str = std::basic_string<CharT, Traits, Alloc>; + const wchar_t* expect = longString; + const path p((const char*)longString); + { + DisableAllocationGuard g; + Alloc a; + Alloc::disable_default_constructor = true; + Str s = p.generic_string<wchar_t, Traits, Alloc>(a); + assert(s == expect); + assert(Alloc::alloc_count > 0); + assert(Alloc::outstanding_alloc() == 1); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.generic.obs/named_overloads.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.generic.obs/named_overloads.pass.cpp new file mode 100644 index 00000000000..d41ab619a89 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.generic.obs/named_overloads.pass.cpp @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// std::string generic_string() const; +// std::wstring generic_wstring() const; +// std::u8string generic_u8string() const; +// std::u16string generic_u16string() const; +// std::u32string generic_u32string() const; + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "min_allocator.h" +#include "filesystem_test_helper.hpp" + +MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +int main() +{ + using namespace fs; + auto const& MS = longString; + const char* value = longString; + const path p(value); + { + std::string s = p.generic_string(); + assert(s == value); + } + { + std::string s = p.generic_u8string(); + assert(s == (const char*)MS); + } + { + std::wstring s = p.generic_wstring(); + assert(s == (const wchar_t*)MS); + } + { + std::u16string s = p.generic_u16string(); + assert(s == (const char16_t*)MS); + } + { + std::u32string s = p.generic_u32string(); + assert(s == (const char32_t*)MS); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/clear.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/clear.pass.cpp new file mode 100644 index 00000000000..fca642647b8 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/clear.pass.cpp @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// void clear() noexcept + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +int main() { + using namespace fs; + { + path p; + ASSERT_NOEXCEPT(p.clear()); + ASSERT_SAME_TYPE(void, decltype(p.clear())); + p.clear(); + assert(p.empty()); + } + { + const path p("/foo/bar/baz"); + path p2(p); + assert(p == p2); + p2.clear(); + assert(p2.empty()); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/make_preferred.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/make_preferred.pass.cpp new file mode 100644 index 00000000000..ec914b1bc48 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/make_preferred.pass.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path& make_preferred() + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +struct MakePreferredTestcase { + const char* value; +}; + +const MakePreferredTestcase TestCases[] = + { + {""} + , {"hello_world"} + , {"/"} + , {"/foo/bar/baz/"} + , {"\\"} + , {"\\foo\\bar\\baz\\"} + , {"\\foo\\/bar\\/baz\\"} + }; + +int main() +{ + // This operation is an identity operation on linux. + using namespace fs; + for (auto const & TC : TestCases) { + path p(TC.value); + assert(p == TC.value); + path& Ref = (p.make_preferred()); + assert(p.native() == TC.value); + assert(&Ref == &p); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/remove_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/remove_filename.pass.cpp new file mode 100644 index 00000000000..b4019e7f228 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/remove_filename.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path& remove_filename() + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" +#include "verbose_assert.h" + +struct RemoveFilenameTestcase { + const char* value; + const char* expect; +}; + +const RemoveFilenameTestcase TestCases[] = + { + {"", ""} + , {"/", "/"} + , {"//", "//"} + , {"///", "///"} + , {"\\", ""} + , {".", ""} + , {"..", ""} + , {"/foo", "/"} + , {"foo/bar", "foo/"} + , {"foo/", "foo/"} + , {"//foo", "//"} + , {"//foo/", "//foo/"} + , {"//foo///", "//foo///"} + , {"///foo", "///"} + , {"///foo/", "///foo/"} + , {"/foo/", "/foo/"} + , {"/foo/.", "/foo/"} + , {"/foo/..", "/foo/"} + , {"/foo/////", "/foo/////"} + , {"/foo\\\\", "/"} + , {"/foo//\\/", "/foo//\\/"} + , {"///foo", "///"} + , {"file.txt", ""} + , {"bar/../baz/./file.txt", "bar/../baz/./"} + }; + +int main() +{ + using namespace fs; + for (auto const & TC : TestCases) { + path const p_orig(TC.value); + path p(p_orig); + assert(p == TC.value); + path& Ref = (p.remove_filename()); + ASSERT_EQ(p, TC.expect) << DISPLAY(p_orig); + assert(&Ref == &p); + assert(!p.has_filename()); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/replace_extension.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/replace_extension.pass.cpp new file mode 100644 index 00000000000..c118b87142a --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/replace_extension.pass.cpp @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path& replace_extension(path const& p = path()) + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +struct ReplaceExtensionTestcase { + const char* value; + const char* expect; + const char* extension; +}; + +const ReplaceExtensionTestcase TestCases[] = + { + {"", "", ""} + , {"foo.cpp", "foo", ""} + , {"foo.cpp", "foo.", "."} + , {"foo..cpp", "foo..txt", "txt"} + , {"", ".txt", "txt"} + , {"", ".txt", ".txt"} + , {"/foo", "/foo.txt", ".txt"} + , {"/foo", "/foo.txt", "txt"} + , {"/foo.cpp", "/foo.txt", ".txt"} + , {"/foo.cpp", "/foo.txt", "txt"} + }; +const ReplaceExtensionTestcase NoArgCases[] = + { + {"", "", ""} + , {"foo", "foo", ""} + , {"foo.cpp", "foo", ""} + , {"foo..cpp", "foo.", ""} +}; + +int main() +{ + using namespace fs; + for (auto const & TC : TestCases) { + path p(TC.value); + assert(p == TC.value); + path& Ref = (p.replace_extension(TC.extension)); + assert(p == TC.expect); + assert(&Ref == &p); + } + for (auto const& TC : NoArgCases) { + path p(TC.value); + assert(p == TC.value); + path& Ref = (p.replace_extension()); + assert(p == TC.expect); + assert(&Ref == &p); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/replace_filename.pass.cpp new file mode 100644 index 00000000000..07cd327728f --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/replace_filename.pass.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// path& replace_filename() + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" +#include "assert_checkpoint.h" +#include "verbose_assert.h" + +struct ReplaceFilenameTestcase { + const char* value; + const char* expect; + const char* filename; +}; + +const ReplaceFilenameTestcase TestCases[] = + { + {"/foo", "/bar", "bar"} + , {"/foo", "/", ""} + , {"foo", "bar", "bar"} + , {"/", "/bar", "bar"} + , {"\\", "bar", "bar"} + , {"///", "///bar", "bar"} + , {"\\\\", "bar", "bar"} + , {"\\/\\", "\\/bar", "bar"} + , {".", "bar", "bar"} + , {"..", "bar", "bar"} + , {"/foo\\baz/bong/", "/foo\\baz/bong/bar", "bar"} + , {"/foo\\baz/bong", "/foo\\baz/bar", "bar"} + }; + +int main() +{ + using namespace fs; + for (auto const & TC : TestCases) { + path p(TC.value); + ASSERT_EQ(p, TC.value); + path& Ref = (p.replace_filename(TC.filename)); + ASSERT_EQ(p, TC.expect) + << DISPLAY(TC.value) + << DISPLAY(TC.filename); + assert(&Ref == &p); + // Tests Effects "as-if": remove_filename() append(filename) + { + path p2(TC.value); + path replace(TC.filename); + p2.remove_filename(); + p2 /= replace; + ASSERT_EQ(p, p2); + } + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/swap.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/swap.pass.cpp new file mode 100644 index 00000000000..eecfe425251 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.modifiers/swap.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// void swap(path& rhs) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +struct SwapTestcase { + const char* value1; + const char* value2; +}; + +#define LONG_STR1 "_THIS_IS_LONG_THIS_IS_LONG_THIS_IS_LONG_THIS_IS_LONG_THIS_IS_LONG_THIS_IS_LONG_THIS_IS_LONG" +#define LONG_STR2 "_THIS_IS_LONG2_THIS_IS_LONG2_THIS_IS_LONG2_THIS_IS_LONG2_THIS_IS_LONG2_THIS_IS_LONG2_THIS_IS_LONG2" +const SwapTestcase TestCases[] = + { + {"", ""} + , {"shortstr", LONG_STR1} + , {LONG_STR1, "shortstr"} + , {LONG_STR1, LONG_STR2} + }; +#undef LONG_STR1 +#undef LONG_STR2 + +int main() +{ + using namespace fs; + { + path p; + ASSERT_NOEXCEPT(p.swap(p)); + ASSERT_SAME_TYPE(void, decltype(p.swap(p))); + } + for (auto const & TC : TestCases) { + path p1(TC.value1); + path p2(TC.value2); + { + DisableAllocationGuard g; + p1.swap(p2); + } + assert(p1 == TC.value2); + assert(p2 == TC.value1); + { + DisableAllocationGuard g; + p1.swap(p2); + } + assert(p1 == TC.value1); + assert(p2 == TC.value2); + } + // self-swap + { + const char* Val = "aoeuaoeuaoeuaoeuaoeuaoeuaoeuaoeuaoeu"; + path p1(Val); + assert(p1 == Val); + { + DisableAllocationGuard g; + p1.swap(p1); + } + assert(p1 == Val); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/c_str.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/c_str.pass.cpp new file mode 100644 index 00000000000..e035764d2c2 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/c_str.pass.cpp @@ -0,0 +1,42 @@ + +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// const value_type* c_str() const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "filesystem_test_helper.hpp" + + +int main() +{ + using namespace fs; + const char* const value = "hello world"; + const std::string str_value = value; + { // Check signature + path p(value); + ASSERT_SAME_TYPE(path::value_type const*, decltype(p.c_str())); + ASSERT_NOEXCEPT(p.c_str()); + } + { + path p(value); + assert(p.c_str() == str_value); + assert(p.native().c_str() == p.c_str()); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/named_overloads.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/named_overloads.pass.cpp new file mode 100644 index 00000000000..bb0f58cab75 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/named_overloads.pass.cpp @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// std::string string() const; +// std::wstring wstring() const; +// std::u8string u8string() const; +// std::u16string u16string() const; +// std::u32string u32string() const; + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "min_allocator.h" +#include "filesystem_test_helper.hpp" + + +MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +int main() +{ + using namespace fs; + auto const& MS = longString; + const char* value = longString; + const path p(value); + { + std::string s = p.string(); + assert(s == value); + } + { + std::string s = p.u8string(); + assert(s == (const char*)MS); + } + { + std::wstring s = p.wstring(); + assert(s == (const wchar_t*)MS); + } + { + std::u16string s = p.u16string(); + assert(s == (const char16_t*)MS); + } + { + std::u32string s = p.u32string(); + assert(s == (const char32_t*)MS); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/native.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/native.pass.cpp new file mode 100644 index 00000000000..ca02b8680b0 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/native.pass.cpp @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// const string_type& native() const noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "filesystem_test_helper.hpp" + + +int main() +{ + using namespace fs; + const char* const value = "hello world"; + { // Check signature + path p(value); + ASSERT_SAME_TYPE(path::string_type const&, decltype(p.native())); + ASSERT_NOEXCEPT(p.native()); + } + { // native() is tested elsewhere + path p(value); + assert(p.native() == value); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/operator_string.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/operator_string.pass.cpp new file mode 100644 index 00000000000..4f5f77df403 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/operator_string.pass.cpp @@ -0,0 +1,46 @@ + +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// operator string_type() const; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "filesystem_test_helper.hpp" + + +int main() +{ + using namespace fs; + using string_type = path::string_type; + const char* const value = "hello world"; + { // Check signature + path p(value); + static_assert(std::is_convertible<path, string_type>::value, ""); + static_assert(std::is_constructible<string_type, path>::value, ""); + ASSERT_SAME_TYPE(string_type, decltype(p.operator string_type())); + ASSERT_NOT_NOEXCEPT(p.operator string_type()); + } + { + path p(value); + assert(p.native() == value); + string_type s = p; + assert(s == value); + assert(p == value); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/string_alloc.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/string_alloc.pass.cpp new file mode 100644 index 00000000000..744ad4df817 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.native.obs/string_alloc.pass.cpp @@ -0,0 +1,137 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// template <class ECharT, class Traits = char_traits<ECharT>, +// class Allocator = allocator<ECharT>> +// basic_string<ECharT, Traits, Allocator> +// string(const Allocator& a = Allocator()) const; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "min_allocator.h" +#include "filesystem_test_helper.hpp" + + +// the SSO is always triggered for strings of size 2. +MultiStringType shortString = MKSTR("a"); +MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +template <class CharT> +void doShortStringTest(MultiStringType const& MS) { + using namespace fs; + using Ptr = CharT const*; + using Str = std::basic_string<CharT>; + using Alloc = std::allocator<CharT>; + Ptr value = MS; + const path p((const char*)MS); + { + DisableAllocationGuard g; + Str s = p.string<CharT>(); + assert(s == value); + Str s2 = p.string<CharT>(Alloc{}); + assert(s2 == value); + } + using MAlloc = malloc_allocator<CharT>; + MAlloc::reset(); + { + using Traits = std::char_traits<CharT>; + using AStr = std::basic_string<CharT, Traits, MAlloc>; + DisableAllocationGuard g; + AStr s = p.string<CharT, Traits, MAlloc>(); + assert(s == value); + assert(MAlloc::alloc_count == 0); + assert(MAlloc::outstanding_alloc() == 0); + } + MAlloc::reset(); + { // Other allocator - provided copy + using Traits = std::char_traits<CharT>; + using AStr = std::basic_string<CharT, Traits, MAlloc>; + DisableAllocationGuard g; + MAlloc a; + // don't allow another allocator to be default constructed. + MAlloc::disable_default_constructor = true; + AStr s = p.string<CharT, Traits, MAlloc>(a); + assert(s == value); + assert(MAlloc::alloc_count == 0); + assert(MAlloc::outstanding_alloc() == 0); + } + MAlloc::reset(); +} + +template <class CharT> +void doLongStringTest(MultiStringType const& MS) { + using namespace fs; + using Ptr = CharT const*; + using Str = std::basic_string<CharT>; + Ptr value = MS; + const path p((const char*)MS); + { // Default allocator + using Alloc = std::allocator<CharT>; + Str s = p.string<CharT>(); + assert(s == value); + Str s2 = p.string<CharT>(Alloc{}); + assert(s2 == value); + } + using MAlloc = malloc_allocator<CharT>; + MAlloc::reset(); + { // Other allocator - default construct + using Traits = std::char_traits<CharT>; + using AStr = std::basic_string<CharT, Traits, MAlloc>; + DisableAllocationGuard g; + AStr s = p.string<CharT, Traits, MAlloc>(); + assert(s == value); + assert(MAlloc::alloc_count > 0); + assert(MAlloc::outstanding_alloc() == 1); + } + MAlloc::reset(); + { // Other allocator - provided copy + using Traits = std::char_traits<CharT>; + using AStr = std::basic_string<CharT, Traits, MAlloc>; + DisableAllocationGuard g; + MAlloc a; + // don't allow another allocator to be default constructed. + MAlloc::disable_default_constructor = true; + AStr s = p.string<CharT, Traits, MAlloc>(a); + assert(s == value); + assert(MAlloc::alloc_count > 0); + assert(MAlloc::outstanding_alloc() == 1); + } + MAlloc::reset(); + ///////////////////////////////////////////////////////////////////////////// +} + +int main() +{ + using namespace fs; + { + auto const& S = shortString; + doShortStringTest<char>(S); + doShortStringTest<wchar_t>(S); + doShortStringTest<char16_t>(S); + doShortStringTest<char32_t>(S); + } + { + auto const& S = longString; + doLongStringTest<char>(S); + doLongStringTest<wchar_t>(S); + doLongStringTest<char16_t>(S); + doLongStringTest<char32_t>(S); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.query/tested_in_path_decompose.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.query/tested_in_path_decompose.pass.cpp new file mode 100644 index 00000000000..be662fcb16c --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.query/tested_in_path_decompose.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +//------------------------------- +// 8.4.10 path query [path.query] +//------------------------------- +// bool empty() const noexcept; +// bool has_root_path() const; +// bool has_root_name() const; +// bool has_root_directory() const; +// bool has_relative_path() const; +// bool has_parent_path() const; +// bool has_filename() const; +// bool has_stem() const; +// bool has_extension() const; +// bool is_absolute() const; +// bool is_relative() const; + +// tested in path.decompose +int main() {} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/append_op.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/append_op.pass.cpp new file mode 100644 index 00000000000..09498bf2135 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/append_op.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// path operator/(path const&, path const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "filesystem_test_helper.hpp" + + +// This is mainly tested via the member append functions. +int main() +{ + using namespace fs; + path p1("abc"); + path p2("def"); + path p3 = p1 / p2; + assert(p3 == "abc/def"); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/comparison_ops_tested_elsewhere.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/comparison_ops_tested_elsewhere.pass.cpp new file mode 100644 index 00000000000..28867432c61 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/comparison_ops_tested_elsewhere.pass.cpp @@ -0,0 +1,14 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// The comparison operators are tested as part of [path.compare] +// in class.path/path.members/path.compare.pass.cpp +int main() {} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/hash_value_tested_elswhere.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/hash_value_tested_elswhere.pass.cpp new file mode 100644 index 00000000000..b03b8008b62 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/hash_value_tested_elswhere.pass.cpp @@ -0,0 +1,14 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// The "hash_value" function is tested as part of [path.compare] +// in class.path/path.members/path.compare.pass.cpp +int main() {} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/path.factory.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/path.factory.pass.cpp new file mode 100644 index 00000000000..96b8286d5e1 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/path.factory.pass.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// template <class Source> +// path u8path(Source const&); +// template <class InputIter> +// path u8path(InputIter, InputIter); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +int main() +{ + using namespace fs; + const char* In1 = "abcd/efg"; + const std::string In2(In1); + const auto In3 = In2.begin(); + const auto In3End = In2.end(); + { + path p = fs::u8path(In1); + assert(p == In1); + } + { + path p = fs::u8path(In2); + assert(p == In1); + } + { + path p = fs::u8path(In3); + assert(p == In1); + } + { + path p = fs::u8path(In3, In3End); + assert(p == In1); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/path.io.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/path.io.pass.cpp new file mode 100644 index 00000000000..7902289edd8 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/path.io.pass.cpp @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// template <class charT, class traits> +// basic_ostream<charT, traits>& +// operator<<(basic_ostream<charT, traits>& os, const path& p); +// +// template <class charT, class traits> +// basic_istream<charT, traits>& +// operator>>(basic_istream<charT, traits>& is, path& p) +// + +#include "filesystem_include.hpp" +#include <type_traits> +#include <sstream> +#include <cassert> +#include <iostream> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + +MultiStringType InStr = MKSTR("abcdefg/\"hijklmnop\"/qrstuvwxyz/123456789"); +MultiStringType OutStr = MKSTR("\"abcdefg/\\\"hijklmnop\\\"/qrstuvwxyz/123456789\""); + + + +template <class CharT> +void doIOTest() { + using namespace fs; + using Ptr = const CharT*; + using StrStream = std::basic_stringstream<CharT>; + const Ptr E = OutStr; + const path p((const char*)InStr); + StrStream ss; + { // test output + auto& ret = (ss << p); + assert(ss.str() == E); + assert(&ret == &ss); + } + { // test input + path p_in; + auto& ret = ss >> p_in; + assert(p_in.native() == (const char*)InStr); + assert(&ret == &ss); + } +} + +namespace impl { +using namespace fs; + +template <class Stream, class Tp, class = decltype(std::declval<Stream&>() << std::declval<Tp&>())> +std::true_type is_ostreamable_imp(int); + +template <class Stream, class Tp> +std::false_type is_ostreamable_imp(long); + +template <class Stream, class Tp, class = decltype(std::declval<Stream&>() >> std::declval<Tp&>())> +std::true_type is_istreamable_imp(int); + +template <class Stream, class Tp> +std::false_type is_istreamable_imp(long); + + +} // namespace impl + +template <class Stream, class Tp> +struct is_ostreamable : decltype(impl::is_ostreamable_imp<Stream, Tp>(0)) {}; +template <class Stream, class Tp> +struct is_istreamable : decltype(impl::is_istreamable_imp<Stream, Tp>(0)) {}; + +void test_LWG2989() { + static_assert(!is_ostreamable<decltype(std::cout), std::wstring>::value, ""); + static_assert(!is_ostreamable<decltype(std::wcout), std::string>::value, ""); + static_assert(!is_istreamable<decltype(std::cin), std::wstring>::value, ""); + static_assert(!is_istreamable<decltype(std::wcin), std::string>::value, ""); +} + +int main() { + doIOTest<char>(); + doIOTest<wchar_t>(); + //doIOTest<char16_t>(); + //doIOTest<char32_t>(); + test_LWG2989(); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/path.io.unicode_bug.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/path.io.unicode_bug.pass.cpp new file mode 100644 index 00000000000..65be19fdc4c --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/path.io.unicode_bug.pass.cpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// template <class charT, class traits> +// basic_ostream<charT, traits>& +// operator<<(basic_ostream<charT, traits>& os, const path& p); +// +// template <class charT, class traits> +// basic_istream<charT, traits>& +// operator>>(basic_istream<charT, traits>& is, path& p) +// + +// TODO(EricWF) This test fails because "std::quoted" fails to compile +// for char16_t and char32_t types. Combine with path.io.pass.cpp when this +// passes. +// XFAIL: * + +#include "filesystem_include.hpp" +#include <type_traits> +#include <sstream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + +MultiStringType InStr = MKSTR("abcdefg/\"hijklmnop\"/qrstuvwxyz/123456789"); +MultiStringType OutStr = MKSTR("\"abcdefg/\\\"hijklmnop\\\"/qrstuvwxyz/123456789\""); + +template <class CharT> +void doIOTest() { + using namespace fs; + using Ptr = const CharT*; + using StrStream = std::basic_stringstream<CharT>; + const char* const InCStr = InStr; + const Ptr E = OutStr; + const path p((const char*)InStr); + StrStream ss; + { // test output + auto& ret = (ss << p); + assert(ss.str() == E); + assert(&ret == &ss); + } + { // test input + path p_in; + auto& ret = ss >> p_in; + assert(p_in.native() == (const char*)InStr); + assert(&ret == &ss); + } +} + + +int main() { + doIOTest<char16_t>(); + doIOTest<char32_t>(); +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/swap.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/swap.pass.cpp new file mode 100644 index 00000000000..554b5129cc9 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.nonmember/swap.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// void swap(path& lhs, path& rhs) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +// NOTE: this is tested in path.members/path.modifiers via the member swap. +int main() +{ + using namespace fs; + const char* value1 = "foo/bar/baz"; + const char* value2 = "_THIS_IS_LONG_THIS_IS_LONG_THIS_IS_LONG_THIS_IS_LONG_THIS_IS_LONG_THIS_IS_LONG_THIS_IS_LONG"; + path p1(value1); + path p2(value2); + { + using namespace std; using namespace fs; + ASSERT_NOEXCEPT(swap(p1, p2)); + ASSERT_SAME_TYPE(void, decltype(swap(p1, p2))); + } + { + DisableAllocationGuard g; + using namespace std; + using namespace fs; + swap(p1, p2); + assert(p1.native() == value2); + assert(p2.native() == value1); + swap(p1, p2); + assert(p1.native() == value1); + assert(p2.native() == value2); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.path/synop.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/synop.pass.cpp new file mode 100644 index 00000000000..875f92fb68a --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/synop.pass.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path + +// typedef ... value_type; +// typedef basic_string<value_type> string_type; +// static constexpr value_type preferred_separator = ...; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" + + +int main() { + using namespace fs; + ASSERT_SAME_TYPE(path::value_type, char); + ASSERT_SAME_TYPE(path::string_type, std::basic_string<path::value_type>); + { + ASSERT_SAME_TYPE(const path::value_type, decltype(path::preferred_separator)); + static_assert(path::preferred_separator == '/', ""); + // Make preferred_separator ODR used by taking its address. + const char* dummy = &path::preferred_separator; + ((void)dummy); + } +} diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/copy.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/copy.pass.cpp new file mode 100644 index 00000000000..40ce491b718 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/copy.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class recursive_directory_iterator + +// recursive_recursive_directory_iterator(recursive_recursive_directory_iterator const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(recursive_directory_iterator_copy_construct_tests) + +TEST_CASE(test_constructor_signature) +{ + using D = recursive_directory_iterator; + static_assert(std::is_copy_constructible<D>::value, ""); + //static_assert(!std::is_nothrow_copy_constructible<D>::value, ""); +} + +TEST_CASE(test_copy_end_iterator) +{ + const recursive_directory_iterator endIt; + recursive_directory_iterator it(endIt); + TEST_CHECK(it == endIt); +} + +TEST_CASE(test_copy_valid_iterator) +{ + const path testDir = StaticEnv::Dir; + const recursive_directory_iterator endIt{}; + + // build 'it' up with "interesting" non-default state so we can test + // that it gets copied. We want to get 'it' into a state such that: + // it.options() != directory_options::none + // it.depth() != 0 + // it.recursion_pending() != true + const directory_options opts = directory_options::skip_permission_denied; + recursive_directory_iterator it(testDir, opts); + TEST_REQUIRE(it != endIt); + while (it.depth() == 0) { + ++it; + TEST_REQUIRE(it != endIt); + } + it.disable_recursion_pending(); + TEST_CHECK(it.options() == opts); + TEST_CHECK(it.depth() == 1); + TEST_CHECK(it.recursion_pending() == false); + const path entry = *it; + + // OPERATION UNDER TEST // + const recursive_directory_iterator it2(it); + // ------------------- // + + TEST_REQUIRE(it2 == it); + TEST_CHECK(*it2 == entry); + TEST_CHECK(it2.depth() == 1); + TEST_CHECK(it2.recursion_pending() == false); + TEST_CHECK(it != endIt); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/copy_assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/copy_assign.pass.cpp new file mode 100644 index 00000000000..00c8c35fc0b --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/copy_assign.pass.cpp @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class recursive_directory_iterator + +// recursive_directory_iterator& operator=(recursive_directory_iterator const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(recursive_directory_iterator_copy_assign_tests) + +recursive_directory_iterator createInterestingIterator() + // Create an "interesting" iterator where all fields are + // in a non-default state. The returned 'it' is in a + // state such that: + // it.options() == directory_options::skip_permission_denied + // it.depth() == 1 + // it.recursion_pending() == true +{ + const path testDir = StaticEnv::Dir; + const recursive_directory_iterator endIt; + recursive_directory_iterator it(testDir, + directory_options::skip_permission_denied); + TEST_ASSERT(it != endIt); + while (it.depth() != 1) { + ++it; + TEST_ASSERT(it != endIt); + } + TEST_ASSERT(it.depth() == 1); + it.disable_recursion_pending(); + return it; +} + + +recursive_directory_iterator createDifferentInterestingIterator() + // Create an "interesting" iterator where all fields are + // in a non-default state. The returned 'it' is in a + // state such that: + // it.options() == directory_options::follow_directory_symlink + // it.depth() == 2 + // it.recursion_pending() == false +{ + const path testDir = StaticEnv::Dir; + const recursive_directory_iterator endIt; + recursive_directory_iterator it(testDir, + directory_options::follow_directory_symlink); + TEST_ASSERT(it != endIt); + while (it.depth() != 2) { + ++it; + TEST_ASSERT(it != endIt); + } + TEST_ASSERT(it.depth() == 2); + return it; +} + +TEST_CASE(test_assignment_signature) { + using D = recursive_directory_iterator; + static_assert(std::is_copy_assignable<D>::value, ""); +} + +TEST_CASE(test_copy_to_end_iterator) +{ + const recursive_directory_iterator endIt; + + const recursive_directory_iterator from = createInterestingIterator(); + const path entry = *from; + + recursive_directory_iterator to; + to = from; + TEST_REQUIRE(to == from); + TEST_CHECK(*to == entry); + TEST_CHECK(to.options() == from.options()); + TEST_CHECK(to.depth() == from.depth()); + TEST_CHECK(to.recursion_pending() == from.recursion_pending()); +} + + +TEST_CASE(test_copy_from_end_iterator) +{ + const recursive_directory_iterator from; + recursive_directory_iterator to = createInterestingIterator(); + + to = from; + TEST_REQUIRE(to == from); + TEST_CHECK(to == recursive_directory_iterator{}); +} + +TEST_CASE(test_copy_valid_iterator) +{ + const recursive_directory_iterator endIt; + + const recursive_directory_iterator it = createInterestingIterator(); + const path entry = *it; + + recursive_directory_iterator it2 = createDifferentInterestingIterator(); + TEST_REQUIRE(it2 != it); + TEST_CHECK(it2.options() != it.options()); + TEST_CHECK(it2.depth() != it.depth()); + TEST_CHECK(it2.recursion_pending() != it.recursion_pending()); + TEST_CHECK(*it2 != entry); + + it2 = it; + TEST_REQUIRE(it2 == it); + TEST_CHECK(it2.options() == it.options()); + TEST_CHECK(it2.depth() == it.depth()); + TEST_CHECK(it2.recursion_pending() == it.recursion_pending()); + TEST_CHECK(*it2 == entry); +} + +TEST_CASE(test_returns_reference_to_self) +{ + const recursive_directory_iterator it; + recursive_directory_iterator it2; + recursive_directory_iterator& ref = (it2 = it); + TEST_CHECK(&ref == &it2); +} + +TEST_CASE(test_self_copy) +{ + // Create two non-equal iterators that have exactly the same state. + recursive_directory_iterator it = createInterestingIterator(); + recursive_directory_iterator it2 = createInterestingIterator(); + TEST_CHECK(it != it2); + TEST_CHECK(it2.options() == it.options()); + TEST_CHECK(it2.depth() == it.depth()); + TEST_CHECK(it2.recursion_pending() == it.recursion_pending()); + TEST_CHECK(*it2 == *it); + + // perform a self-copy and check that the state still matches the + // other unmodified iterator. + recursive_directory_iterator const& cit = it; + it = cit; + TEST_CHECK(it2.options() == it.options()); + TEST_CHECK(it2.depth() == it.depth()); + TEST_CHECK(it2.recursion_pending() == it.recursion_pending()); + TEST_CHECK(*it2 == *it); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp new file mode 100644 index 00000000000..e02d6679c0e --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp @@ -0,0 +1,246 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class directory_iterator + +// +// explicit recursive_directory_iterator(const path& p); +// recursive_directory_iterator(const path& p, directory_options options); +// recursive_directory_iterator(const path& p, error_code& ec); +// recursive_directory_iterator(const path& p, directory_options options, error_code& ec); + + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +using RDI = recursive_directory_iterator; + +TEST_SUITE(recursive_directory_iterator_constructor_tests) + +TEST_CASE(test_constructor_signatures) +{ + using D = recursive_directory_iterator; + + // explicit directory_iterator(path const&); + static_assert(!std::is_convertible<path, D>::value, ""); + static_assert(std::is_constructible<D, path>::value, ""); + static_assert(!std::is_nothrow_constructible<D, path>::value, ""); + + // directory_iterator(path const&, error_code&) + static_assert(std::is_constructible<D, path, + std::error_code&>::value, ""); + static_assert(!std::is_nothrow_constructible<D, path, + std::error_code&>::value, ""); + + // directory_iterator(path const&, directory_options); + static_assert(std::is_constructible<D, path, directory_options>::value, ""); + static_assert(!std::is_nothrow_constructible<D, path, directory_options>::value, ""); + + // directory_iterator(path const&, directory_options, error_code&) + static_assert(std::is_constructible<D, path, directory_options, std::error_code&>::value, ""); + static_assert(!std::is_nothrow_constructible<D, path, directory_options, std::error_code&>::value, ""); +} + +TEST_CASE(test_construction_from_bad_path) +{ + std::error_code ec; + directory_options opts = directory_options::none; + const RDI endIt; + + const path testPaths[] = { StaticEnv::DNE, StaticEnv::BadSymlink }; + for (path const& testPath : testPaths) + { + { + RDI it(testPath, ec); + TEST_CHECK(ec); + TEST_CHECK(it == endIt); + } + { + RDI it(testPath, opts, ec); + TEST_CHECK(ec); + TEST_CHECK(it == endIt); + } + { + TEST_CHECK_THROW(filesystem_error, RDI(testPath)); + TEST_CHECK_THROW(filesystem_error, RDI(testPath, opts)); + } + } +} + +TEST_CASE(access_denied_test_case) +{ + using namespace fs; + scoped_test_env env; + path const testDir = env.make_env_path("dir1"); + path const testFile = testDir / "testFile"; + env.create_dir(testDir); + env.create_file(testFile, 42); + + // Test that we can iterator over the directory before changing the perms + { + RDI it(testDir); + TEST_REQUIRE(it != RDI{}); + } + + // Change the permissions so we can no longer iterate + permissions(testDir, perms::none); + + // Check that the construction fails when skip_permissions_denied is + // not given. + { + std::error_code ec; + RDI it(testDir, ec); + TEST_REQUIRE(ec); + TEST_CHECK(it == RDI{}); + } + // Check that construction does not report an error when + // 'skip_permissions_denied' is given. + { + std::error_code ec; + RDI it(testDir, directory_options::skip_permission_denied, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(it == RDI{}); + } +} + + +TEST_CASE(access_denied_to_file_test_case) +{ + using namespace fs; + scoped_test_env env; + path const testFile = env.make_env_path("file1"); + env.create_file(testFile, 42); + + // Change the permissions so we can no longer iterate + permissions(testFile, perms::none); + + // Check that the construction fails when skip_permissions_denied is + // not given. + { + std::error_code ec; + RDI it(testFile, ec); + TEST_REQUIRE(ec); + TEST_CHECK(it == RDI{}); + } + // Check that construction still fails when 'skip_permissions_denied' is given + // because we tried to open a file and not a directory. + { + std::error_code ec; + RDI it(testFile, directory_options::skip_permission_denied, ec); + TEST_REQUIRE(ec); + TEST_CHECK(it == RDI{}); + } +} + +TEST_CASE(test_open_on_empty_directory_equals_end) +{ + scoped_test_env env; + const path testDir = env.make_env_path("dir1"); + env.create_dir(testDir); + + const RDI endIt; + { + std::error_code ec; + RDI it(testDir, ec); + TEST_CHECK(!ec); + TEST_CHECK(it == endIt); + } + { + RDI it(testDir); + TEST_CHECK(it == endIt); + } +} + +TEST_CASE(test_open_on_directory_succeeds) +{ + const path testDir = StaticEnv::Dir; + std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList), + std::end( StaticEnv::DirIterationList)); + const RDI endIt{}; + + { + std::error_code ec; + RDI it(testDir, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(it != endIt); + TEST_CHECK(dir_contents.count(*it)); + } + { + RDI it(testDir); + TEST_CHECK(it != endIt); + TEST_CHECK(dir_contents.count(*it)); + } +} + +TEST_CASE(test_open_on_file_fails) +{ + const path testFile = StaticEnv::File; + const RDI endIt{}; + { + std::error_code ec; + RDI it(testFile, ec); + TEST_REQUIRE(ec); + TEST_CHECK(it == endIt); + } + { + TEST_CHECK_THROW(filesystem_error, RDI(testFile)); + } +} + +TEST_CASE(test_options_post_conditions) +{ + const path goodDir = StaticEnv::Dir; + const path badDir = StaticEnv::DNE; + + { + std::error_code ec; + + RDI it1(goodDir, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(it1.options() == directory_options::none); + + RDI it2(badDir, ec); + TEST_REQUIRE(ec); + TEST_REQUIRE(it2 == RDI{}); + } + { + std::error_code ec; + const directory_options opts = directory_options::skip_permission_denied; + + RDI it1(goodDir, opts, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(it1.options() == opts); + + RDI it2(badDir, opts, ec); + TEST_REQUIRE(ec); + TEST_REQUIRE(it2 == RDI{}); + } + { + RDI it(goodDir); + TEST_CHECK(it.options() == directory_options::none); + } + { + const directory_options opts = directory_options::follow_directory_symlink; + RDI it(goodDir, opts); + TEST_CHECK(it.options() == opts); + } +} +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/depth.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/depth.pass.cpp new file mode 100644 index 00000000000..9b239c6bf44 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/depth.pass.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class recursive_directory_iterator + +// int depth() const + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(recursive_directory_iterator_depth_tests) + +TEST_CASE(test_depth) +{ + const path testDir = StaticEnv::Dir; + const path DirDepth1 = StaticEnv::Dir2; + const path DirDepth2 = StaticEnv::Dir3; + const recursive_directory_iterator endIt{}; + + std::error_code ec; + recursive_directory_iterator it(testDir, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(it.depth() == 0); + + bool seen_d1, seen_d2; + seen_d1 = seen_d2 = false; + + while (it != endIt) { + const path entry = *it; + const path parent = entry.parent_path(); + if (parent == testDir) { + TEST_CHECK(it.depth() == 0); + } else if (parent == DirDepth1) { + TEST_CHECK(it.depth() == 1); + seen_d1 = true; + } else if (parent == DirDepth2) { + TEST_CHECK(it.depth() == 2); + seen_d2 = true; + } else { + TEST_CHECK(!"Unexpected depth while iterating over static env"); + } + ++it; + } + TEST_REQUIRE(seen_d1 && seen_d2); + TEST_CHECK(it == endIt); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/disable_recursion_pending.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/disable_recursion_pending.pass.cpp new file mode 100644 index 00000000000..799494f1be4 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/disable_recursion_pending.pass.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class recursive_directory_iterator + +// void disable_recursion_pending(); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(recursive_directory_iterator_disable_recursion_pending_tests) + +// NOTE: The main semantics of disable_recursion_pending are tested +// in the 'recursion_pending()' tests. +TEST_CASE(basic_test) +{ + recursive_directory_iterator it(StaticEnv::Dir); + TEST_REQUIRE(it.recursion_pending() == true); + it.disable_recursion_pending(); + TEST_CHECK(it.recursion_pending() == false); + it.disable_recursion_pending(); + TEST_CHECK(it.recursion_pending() == false); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp new file mode 100644 index 00000000000..bfc818924d1 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp @@ -0,0 +1,496 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class recursive_directory_iterator + +// recursive_directory_iterator& operator++(); +// recursive_directory_iterator& increment(error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(recursive_directory_iterator_increment_tests) + +TEST_CASE(test_increment_signatures) +{ + using D = recursive_directory_iterator; + recursive_directory_iterator d; ((void)d); + std::error_code ec; ((void)ec); + + ASSERT_SAME_TYPE(decltype(++d), recursive_directory_iterator&); + ASSERT_NOT_NOEXCEPT(++d); + + ASSERT_SAME_TYPE(decltype(d.increment(ec)), recursive_directory_iterator&); + ASSERT_NOT_NOEXCEPT(d.increment(ec)); +} + +TEST_CASE(test_prefix_increment) +{ + const path testDir = StaticEnv::Dir; + const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList), + std::end( StaticEnv::RecDirIterationList)); + const recursive_directory_iterator endIt{}; + + std::error_code ec; + recursive_directory_iterator it(testDir, ec); + TEST_REQUIRE(!ec); + + std::set<path> unseen_entries = dir_contents; + while (!unseen_entries.empty()) { + TEST_REQUIRE(it != endIt); + const path entry = *it; + TEST_REQUIRE(unseen_entries.erase(entry) == 1); + recursive_directory_iterator& it_ref = ++it; + TEST_CHECK(&it_ref == &it); + } + + TEST_CHECK(it == endIt); +} + +TEST_CASE(test_postfix_increment) +{ + const path testDir = StaticEnv::Dir; + const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList), + std::end( StaticEnv::RecDirIterationList)); + const recursive_directory_iterator endIt{}; + + std::error_code ec; + recursive_directory_iterator it(testDir, ec); + TEST_REQUIRE(!ec); + + std::set<path> unseen_entries = dir_contents; + while (!unseen_entries.empty()) { + TEST_REQUIRE(it != endIt); + const path entry = *it; + TEST_REQUIRE(unseen_entries.erase(entry) == 1); + const path entry2 = *it++; + TEST_CHECK(entry2 == entry); + } + TEST_CHECK(it == endIt); +} + + +TEST_CASE(test_increment_method) +{ + const path testDir = StaticEnv::Dir; + const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList), + std::end( StaticEnv::RecDirIterationList)); + const recursive_directory_iterator endIt{}; + + std::error_code ec; + recursive_directory_iterator it(testDir, ec); + TEST_REQUIRE(!ec); + + std::set<path> unseen_entries = dir_contents; + while (!unseen_entries.empty()) { + TEST_REQUIRE(it != endIt); + const path entry = *it; + TEST_REQUIRE(unseen_entries.erase(entry) == 1); + recursive_directory_iterator& it_ref = it.increment(ec); + TEST_REQUIRE(!ec); + TEST_CHECK(&it_ref == &it); + } + + TEST_CHECK(it == endIt); +} + +TEST_CASE(test_follow_symlinks) +{ + const path testDir = StaticEnv::Dir; + auto const& IterList = StaticEnv::RecDirFollowSymlinksIterationList; + + const std::set<path> dir_contents(std::begin(IterList), std::end(IterList)); + const recursive_directory_iterator endIt{}; + + std::error_code ec; + recursive_directory_iterator it(testDir, + directory_options::follow_directory_symlink, ec); + TEST_REQUIRE(!ec); + + std::set<path> unseen_entries = dir_contents; + while (!unseen_entries.empty()) { + TEST_REQUIRE(it != endIt); + const path entry = *it; + + TEST_REQUIRE(unseen_entries.erase(entry) == 1); + recursive_directory_iterator& it_ref = it.increment(ec); + TEST_REQUIRE(!ec); + TEST_CHECK(&it_ref == &it); + } + TEST_CHECK(it == endIt); +} + +TEST_CASE(access_denied_on_recursion_test_case) +{ + using namespace fs; + scoped_test_env env; + const path testFiles[] = { + env.create_dir("dir1"), + env.create_dir("dir1/dir2"), + env.create_file("dir1/dir2/file1"), + env.create_file("dir1/file2") + }; + const path startDir = testFiles[0]; + const path permDeniedDir = testFiles[1]; + const path otherFile = testFiles[3]; + auto SkipEPerm = directory_options::skip_permission_denied; + + // Change the permissions so we can no longer iterate + permissions(permDeniedDir, perms::none); + + const recursive_directory_iterator endIt; + + // Test that recursion resulting in a "EACCESS" error is not ignored + // by default. + { + std::error_code ec = GetTestEC(); + recursive_directory_iterator it(startDir, ec); + TEST_REQUIRE(ec != GetTestEC()); + TEST_REQUIRE(!ec); + while (it != endIt && it->path() != permDeniedDir) + ++it; + TEST_REQUIRE(it != endIt); + TEST_REQUIRE(*it == permDeniedDir); + + it.increment(ec); + TEST_CHECK(ec); + TEST_CHECK(it == endIt); + } + // Same as above but test operator++(). + { + std::error_code ec = GetTestEC(); + recursive_directory_iterator it(startDir, ec); + TEST_REQUIRE(!ec); + while (it != endIt && it->path() != permDeniedDir) + ++it; + TEST_REQUIRE(it != endIt); + TEST_REQUIRE(*it == permDeniedDir); + + TEST_REQUIRE_THROW(filesystem_error, ++it); + } + // Test that recursion resulting in a "EACCESS" error is ignored when the + // correct options are given to the constructor. + { + std::error_code ec = GetTestEC(); + recursive_directory_iterator it(startDir, SkipEPerm, ec); + TEST_REQUIRE(!ec); + TEST_REQUIRE(it != endIt); + + bool seenOtherFile = false; + if (*it == otherFile) { + ++it; + seenOtherFile = true; + TEST_REQUIRE (it != endIt); + } + TEST_REQUIRE(*it == permDeniedDir); + + ec = GetTestEC(); + it.increment(ec); + TEST_REQUIRE(!ec); + + if (seenOtherFile) { + TEST_CHECK(it == endIt); + } else { + TEST_CHECK(it != endIt); + TEST_CHECK(*it == otherFile); + } + } + // Test that construction resulting in a "EACCESS" error is not ignored + // by default. + { + std::error_code ec; + recursive_directory_iterator it(permDeniedDir, ec); + TEST_REQUIRE(ec); + TEST_REQUIRE(it == endIt); + } + // Same as above but testing the throwing constructors + { + TEST_REQUIRE_THROW(filesystem_error, + recursive_directory_iterator(permDeniedDir)); + } + // Test that construction resulting in a "EACCESS" error constructs the + // end iterator when the correct options are given. + { + std::error_code ec = GetTestEC(); + recursive_directory_iterator it(permDeniedDir, SkipEPerm, ec); + TEST_REQUIRE(!ec); + TEST_REQUIRE(it == endIt); + } +} + +// See llvm.org/PR35078 +TEST_CASE(test_PR35078) +{ + using namespace fs; + scoped_test_env env; + const path testFiles[] = { + env.create_dir("dir1"), + env.create_dir("dir1/dir2"), + env.create_dir("dir1/dir2/dir3"), + env.create_file("dir1/file1"), + env.create_file("dir1/dir2/dir3/file2") + }; + const path startDir = testFiles[0]; + const path permDeniedDir = testFiles[1]; + const path nestedDir = testFiles[2]; + const path nestedFile = testFiles[3]; + + // Change the permissions so we can no longer iterate + permissions(permDeniedDir, + perms::group_exec|perms::owner_exec|perms::others_exec, + perm_options::remove); + + const std::error_code eacess_ec = + std::make_error_code(std::errc::permission_denied); + std::error_code ec = GetTestEC(); + + const recursive_directory_iterator endIt; + + auto SetupState = [&](bool AllowEAccess, bool& SeenFile3) { + SeenFile3 = false; + auto Opts = AllowEAccess ? directory_options::skip_permission_denied + : directory_options::none; + recursive_directory_iterator it(startDir, Opts, ec); + while (!ec && it != endIt && *it != nestedDir) { + if (*it == nestedFile) + SeenFile3 = true; + it.increment(ec); + } + return it; + }; + + { + bool SeenNestedFile = false; + recursive_directory_iterator it = SetupState(false, SeenNestedFile); + TEST_REQUIRE(it != endIt); + TEST_REQUIRE(*it == nestedDir); + ec = GetTestEC(); + it.increment(ec); + TEST_CHECK(ec); + TEST_CHECK(ec == eacess_ec); + TEST_CHECK(it == endIt); + } + { + bool SeenNestedFile = false; + recursive_directory_iterator it = SetupState(true, SeenNestedFile); + TEST_REQUIRE(it != endIt); + TEST_REQUIRE(*it == nestedDir); + ec = GetTestEC(); + it.increment(ec); + TEST_CHECK(!ec); + if (SeenNestedFile) { + TEST_CHECK(it == endIt); + } else { + TEST_REQUIRE(it != endIt); + TEST_CHECK(*it == nestedFile); + } + } + { + bool SeenNestedFile = false; + recursive_directory_iterator it = SetupState(false, SeenNestedFile); + TEST_REQUIRE(it != endIt); + TEST_REQUIRE(*it == nestedDir); + + ExceptionChecker Checker(std::errc::permission_denied, + "recursive_directory_iterator::operator++()", + format_string("attempting recursion into \"%s\"", + nestedDir.native())); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ++it); + } +} + + +// See llvm.org/PR35078 +TEST_CASE(test_PR35078_with_symlink) +{ + using namespace fs; + scoped_test_env env; + const path testFiles[] = { + env.create_dir("dir1"), + env.create_file("dir1/file1"), + env.create_dir("sym_dir"), + env.create_dir("sym_dir/nested_sym_dir"), + env.create_symlink("sym_dir/nested_sym_dir", "dir1/dir2"), + env.create_dir("sym_dir/dir1"), + env.create_dir("sym_dir/dir1/dir2"), + + }; + // const unsigned TestFilesSize = sizeof(testFiles) / sizeof(testFiles[0]); + const path startDir = testFiles[0]; + const path nestedFile = testFiles[1]; + const path permDeniedDir = testFiles[2]; + const path symDir = testFiles[4]; + + // Change the permissions so we can no longer iterate + permissions(permDeniedDir, + perms::group_exec|perms::owner_exec|perms::others_exec, + perm_options::remove); + + const std::error_code eacess_ec = + std::make_error_code(std::errc::permission_denied); + std::error_code ec = GetTestEC(); + + const recursive_directory_iterator endIt; + + auto SetupState = [&](bool AllowEAccess, bool FollowSym, bool& SeenFile3) { + SeenFile3 = false; + auto Opts = AllowEAccess ? directory_options::skip_permission_denied + : directory_options::none; + if (FollowSym) + Opts |= directory_options::follow_directory_symlink; + recursive_directory_iterator it(startDir, Opts, ec); + while (!ec && it != endIt && *it != symDir) { + if (*it == nestedFile) + SeenFile3 = true; + it.increment(ec); + } + return it; + }; + + struct { + bool SkipPermDenied; + bool FollowSymlinks; + bool ExpectSuccess; + } TestCases[] = { + // Passing cases + {false, false, true}, {true, true, true}, {true, false, true}, + // Failing cases + {false, true, false} + }; + for (auto TC : TestCases) { + bool SeenNestedFile = false; + recursive_directory_iterator it = SetupState(TC.SkipPermDenied, + TC.FollowSymlinks, + SeenNestedFile); + TEST_REQUIRE(!ec); + TEST_REQUIRE(it != endIt); + TEST_REQUIRE(*it == symDir); + ec = GetTestEC(); + it.increment(ec); + if (TC.ExpectSuccess) { + TEST_CHECK(!ec); + if (SeenNestedFile) { + TEST_CHECK(it == endIt); + } else { + TEST_REQUIRE(it != endIt); + TEST_CHECK(*it == nestedFile); + } + } else { + TEST_CHECK(ec); + TEST_CHECK(ec == eacess_ec); + TEST_CHECK(it == endIt); + } + } +} + + +// See llvm.org/PR35078 +TEST_CASE(test_PR35078_with_symlink_file) +{ + using namespace fs; + scoped_test_env env; + const path testFiles[] = { + env.create_dir("dir1"), + env.create_dir("dir1/dir2"), + env.create_file("dir1/file2"), + env.create_dir("sym_dir"), + env.create_dir("sym_dir/sdir1"), + env.create_file("sym_dir/sdir1/sfile1"), + env.create_symlink("sym_dir/sdir1/sfile1", "dir1/dir2/file1") + }; + const unsigned TestFilesSize = sizeof(testFiles) / sizeof(testFiles[0]); + const path startDir = testFiles[0]; + const path nestedDir = testFiles[1]; + const path nestedFile = testFiles[2]; + const path permDeniedDir = testFiles[3]; + const path symFile = testFiles[TestFilesSize - 1]; + + // Change the permissions so we can no longer iterate + permissions(permDeniedDir, + perms::group_exec|perms::owner_exec|perms::others_exec, + perm_options::remove); + + const std::error_code eacess_ec = + std::make_error_code(std::errc::permission_denied); + std::error_code ec = GetTestEC(); + + const recursive_directory_iterator EndIt; + + auto SetupState = [&](bool AllowEAccess, bool FollowSym, bool& SeenNestedFile) { + SeenNestedFile = false; + auto Opts = AllowEAccess ? directory_options::skip_permission_denied + : directory_options::none; + if (FollowSym) + Opts |= directory_options::follow_directory_symlink; + recursive_directory_iterator it(startDir, Opts, ec); + while (!ec && it != EndIt && *it != nestedDir) { + if (*it == nestedFile) + SeenNestedFile = true; + it.increment(ec); + } + return it; + }; + + struct { + bool SkipPermDenied; + bool FollowSymlinks; + bool ExpectSuccess; + } TestCases[] = { + // Passing cases + {false, false, true}, {true, true, true}, {true, false, true}, + // Failing cases + {false, true, false} + }; + for (auto TC : TestCases){ + bool SeenNestedFile = false; + recursive_directory_iterator it = SetupState(TC.SkipPermDenied, + TC.FollowSymlinks, + SeenNestedFile); + TEST_REQUIRE(!ec); + TEST_REQUIRE(it != EndIt); + TEST_REQUIRE(*it == nestedDir); + ec = GetTestEC(); + it.increment(ec); + TEST_REQUIRE(it != EndIt); + TEST_CHECK(!ec); + TEST_CHECK(*it == symFile); + ec = GetTestEC(); + it.increment(ec); + if (TC.ExpectSuccess) { + if (!SeenNestedFile) { + TEST_CHECK(!ec); + TEST_REQUIRE(it != EndIt); + TEST_CHECK(*it == nestedFile); + ec = GetTestEC(); + it.increment(ec); + } + TEST_CHECK(!ec); + TEST_CHECK(it == EndIt); + } else { + TEST_CHECK(ec); + TEST_CHECK(ec == eacess_ec); + TEST_CHECK(it == EndIt); + } + } +} + + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/move.pass.cpp new file mode 100644 index 00000000000..e258c45e343 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/move.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class recursive_directory_iterator + +// recursive_directory_iterator(recursive_directory_iterator&&) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(recursive_directory_iterator_move_construct_tests) + +TEST_CASE(test_constructor_signature) +{ + using D = recursive_directory_iterator; + static_assert(std::is_nothrow_move_constructible<D>::value, ""); +} + +TEST_CASE(test_move_end_iterator) +{ + const recursive_directory_iterator endIt; + recursive_directory_iterator endIt2{}; + + recursive_directory_iterator it(std::move(endIt2)); + TEST_CHECK(it == endIt); + TEST_CHECK(endIt2 == endIt); +} + +TEST_CASE(test_move_valid_iterator) +{ + const path testDir = StaticEnv::Dir; + const recursive_directory_iterator endIt{}; + + // build 'it' up with "interesting" non-default state so we can test + // that it gets copied. We want to get 'it' into a state such that: + // it.options() != directory_options::none + // it.depth() != 0 + // it.recursion_pending() != true + const directory_options opts = directory_options::skip_permission_denied; + recursive_directory_iterator it(testDir, opts); + TEST_REQUIRE(it != endIt); + while (it.depth() == 0) { + ++it; + TEST_REQUIRE(it != endIt); + } + it.disable_recursion_pending(); + TEST_CHECK(it.options() == opts); + TEST_CHECK(it.depth() == 1); + TEST_CHECK(it.recursion_pending() == false); + const path entry = *it; + + // OPERATION UNDER TEST // + const recursive_directory_iterator it2(std::move(it)); + // ------------------- // + + TEST_REQUIRE(it2 != endIt); + TEST_CHECK(*it2 == entry); + TEST_CHECK(it2.depth() == 1); + TEST_CHECK(it2.recursion_pending() == false); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/move_assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/move_assign.pass.cpp new file mode 100644 index 00000000000..61bb8724bde --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/move_assign.pass.cpp @@ -0,0 +1,169 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class recursive_directory_iterator + +// recursive_directory_iterator& operator=(recursive_directory_iterator const&); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +// The filesystem specification explicitly allows for self-move on +// the directory iterators. Turn off this warning so we can test it. +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wself-move" +#endif + +using namespace fs; + +TEST_SUITE(recursive_directory_iterator_move_assign_tests) + +recursive_directory_iterator createInterestingIterator() + // Create an "interesting" iterator where all fields are + // in a non-default state. The returned 'it' is in a + // state such that: + // it.options() == directory_options::skip_permission_denied + // it.depth() == 1 + // it.recursion_pending() == true +{ + const path testDir = StaticEnv::Dir; + const recursive_directory_iterator endIt; + recursive_directory_iterator it(testDir, + directory_options::skip_permission_denied); + TEST_ASSERT(it != endIt); + while (it.depth() != 1) { + ++it; + TEST_ASSERT(it != endIt); + } + TEST_ASSERT(it.depth() == 1); + it.disable_recursion_pending(); + return it; +} + +recursive_directory_iterator createDifferentInterestingIterator() + // Create an "interesting" iterator where all fields are + // in a non-default state. The returned 'it' is in a + // state such that: + // it.options() == directory_options::follow_directory_symlink + // it.depth() == 2 + // it.recursion_pending() == false +{ + const path testDir = StaticEnv::Dir; + const recursive_directory_iterator endIt; + recursive_directory_iterator it(testDir, + directory_options::follow_directory_symlink); + TEST_ASSERT(it != endIt); + while (it.depth() != 2) { + ++it; + TEST_ASSERT(it != endIt); + } + TEST_ASSERT(it.depth() == 2); + return it; +} + + +TEST_CASE(test_assignment_signature) +{ + using D = recursive_directory_iterator; + static_assert(std::is_nothrow_move_assignable<D>::value, ""); +} + + +TEST_CASE(test_move_to_end_iterator) +{ + const recursive_directory_iterator endIt; + + recursive_directory_iterator from = createInterestingIterator(); + const recursive_directory_iterator from_copy(from); + const path entry = *from; + + recursive_directory_iterator to; + to = std::move(from); + TEST_REQUIRE(to != endIt); + TEST_CHECK(*to == entry); + TEST_CHECK(to.options() == from_copy.options()); + TEST_CHECK(to.depth() == from_copy.depth()); + TEST_CHECK(to.recursion_pending() == from_copy.recursion_pending()); + TEST_CHECK(from == endIt || from == to); +} + + +TEST_CASE(test_move_from_end_iterator) +{ + recursive_directory_iterator from; + recursive_directory_iterator to = createInterestingIterator(); + + to = std::move(from); + TEST_REQUIRE(to == from); + TEST_CHECK(to == recursive_directory_iterator{}); +} + +TEST_CASE(test_move_valid_iterator) +{ + const recursive_directory_iterator endIt; + + recursive_directory_iterator it = createInterestingIterator(); + const recursive_directory_iterator it_copy(it); + const path entry = *it; + + recursive_directory_iterator it2 = createDifferentInterestingIterator(); + const recursive_directory_iterator it2_copy(it2); + TEST_REQUIRE(it2 != it); + TEST_CHECK(it2.options() != it.options()); + TEST_CHECK(it2.depth() != it.depth()); + TEST_CHECK(it2.recursion_pending() != it.recursion_pending()); + TEST_CHECK(*it2 != entry); + + it2 = std::move(it); + TEST_REQUIRE(it2 != it2_copy && it2 != endIt); + TEST_CHECK(it2.options() == it_copy.options()); + TEST_CHECK(it2.depth() == it_copy.depth()); + TEST_CHECK(it2.recursion_pending() == it_copy.recursion_pending()); + TEST_CHECK(*it2 == entry); + TEST_CHECK(it == endIt || it == it2); +} + +TEST_CASE(test_returns_reference_to_self) +{ + recursive_directory_iterator it; + recursive_directory_iterator it2; + recursive_directory_iterator& ref = (it2 = std::move(it)); + TEST_CHECK(&ref == &it2); +} + +TEST_CASE(test_self_move) +{ + // Create two non-equal iterators that have exactly the same state. + recursive_directory_iterator it = createInterestingIterator(); + recursive_directory_iterator it2 = createInterestingIterator(); + TEST_CHECK(it != it2); + TEST_CHECK(it2.options() == it.options()); + TEST_CHECK(it2.depth() == it.depth()); + TEST_CHECK(it2.recursion_pending() == it.recursion_pending()); + TEST_CHECK(*it2 == *it); + + it = std::move(it); + TEST_CHECK(it2.options() == it.options()); + TEST_CHECK(it2.depth() == it.depth()); + TEST_CHECK(it2.recursion_pending() == it.recursion_pending()); + TEST_CHECK(*it2 == *it); +} + + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/pop.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/pop.pass.cpp new file mode 100644 index 00000000000..fe6ce5ff36e --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/pop.pass.cpp @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class recursive_directory_iterator + +// void pop(); +// void pop(error_code& ec); + +#include "filesystem_include.hpp" +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(recursive_directory_iterator_pop_tests) + +TEST_CASE(signature_tests) +{ + recursive_directory_iterator it{}; ((void)it); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(it.pop()); + ASSERT_NOT_NOEXCEPT(it.pop(ec)); // may require allocation or other things +} + +// NOTE: Since the order of iteration is unspecified we use a list of +// seen files at each depth to determine the new depth after a 'pop()' operation. +TEST_CASE(test_depth) +{ + const recursive_directory_iterator endIt{}; + + auto& DE0 = StaticEnv::DirIterationList; + std::set<path> notSeenDepth0(std::begin(DE0), std::end(DE0)); + + auto& DE1 = StaticEnv::DirIterationListDepth1; + std::set<path> notSeenDepth1(std::begin(DE1), std::end(DE1)); + + std::error_code ec; + recursive_directory_iterator it(StaticEnv::Dir, ec); + TEST_REQUIRE(it != endIt); + TEST_CHECK(it.depth() == 0); + + while (it.depth() != 2) { + if (it.depth() == 0) + notSeenDepth0.erase(it->path()); + else + notSeenDepth1.erase(it->path()); + ++it; + TEST_REQUIRE(it != endIt); + } + + while (true) { + auto set_ec = std::make_error_code(std::errc::address_in_use); + it.pop(set_ec); + TEST_REQUIRE(!set_ec); + + if (it == endIt) { + // We must have seen every entry at depth 0 and 1. + TEST_REQUIRE(notSeenDepth0.empty() && notSeenDepth1.empty()); + break; + } + else if (it.depth() == 1) { + // If we popped to depth 1 then there must be unseen entries + // at this level. + TEST_REQUIRE(!notSeenDepth1.empty()); + TEST_CHECK(notSeenDepth1.count(it->path())); + notSeenDepth1.clear(); + } + else if (it.depth() == 0) { + // If we popped to depth 0 there must be unseen entries at this + // level. There should also be no unseen entries at depth 1. + TEST_REQUIRE(!notSeenDepth0.empty()); + TEST_REQUIRE(notSeenDepth1.empty()); + TEST_CHECK(notSeenDepth0.count(it->path())); + notSeenDepth0.clear(); + } + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/recursion_pending.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/recursion_pending.pass.cpp new file mode 100644 index 00000000000..662c11446a2 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/recursion_pending.pass.cpp @@ -0,0 +1,162 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class recursive_directory_iterator + +// bool recursion_pending() const; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(recursive_directory_iterator_recursion_pending_tests) + +TEST_CASE(initial_value_test) +{ + recursive_directory_iterator it(StaticEnv::Dir); + TEST_REQUIRE(it.recursion_pending() == true); +} + +TEST_CASE(value_after_copy_construction_and_assignment_test) +{ + recursive_directory_iterator rec_pending_it(StaticEnv::Dir); + recursive_directory_iterator no_rec_pending_it(StaticEnv::Dir); + no_rec_pending_it.disable_recursion_pending(); + + { // copy construction + recursive_directory_iterator it(rec_pending_it); + TEST_CHECK(it.recursion_pending() == true); + it.disable_recursion_pending(); + TEST_REQUIRE(rec_pending_it.recursion_pending() == true); + + recursive_directory_iterator it2(no_rec_pending_it); + TEST_CHECK(it2.recursion_pending() == false); + } + { // copy assignment + recursive_directory_iterator it(StaticEnv::Dir); + it.disable_recursion_pending(); + it = rec_pending_it; + TEST_CHECK(it.recursion_pending() == true); + it.disable_recursion_pending(); + TEST_REQUIRE(rec_pending_it.recursion_pending() == true); + + recursive_directory_iterator it2(StaticEnv::Dir); + it2 = no_rec_pending_it; + TEST_CHECK(it2.recursion_pending() == false); + } + TEST_CHECK(rec_pending_it.recursion_pending() == true); + TEST_CHECK(no_rec_pending_it.recursion_pending() == false); +} + + +TEST_CASE(value_after_move_construction_and_assignment_test) +{ + recursive_directory_iterator rec_pending_it(StaticEnv::Dir); + recursive_directory_iterator no_rec_pending_it(StaticEnv::Dir); + no_rec_pending_it.disable_recursion_pending(); + + { // move construction + recursive_directory_iterator it_cp(rec_pending_it); + recursive_directory_iterator it(std::move(it_cp)); + TEST_CHECK(it.recursion_pending() == true); + + recursive_directory_iterator it_cp2(no_rec_pending_it); + recursive_directory_iterator it2(std::move(it_cp2)); + TEST_CHECK(it2.recursion_pending() == false); + } + { // copy assignment + recursive_directory_iterator it(StaticEnv::Dir); + it.disable_recursion_pending(); + recursive_directory_iterator it_cp(rec_pending_it); + it = std::move(it_cp); + TEST_CHECK(it.recursion_pending() == true); + + recursive_directory_iterator it2(StaticEnv::Dir); + recursive_directory_iterator it_cp2(no_rec_pending_it); + it2 = std::move(it_cp2); + TEST_CHECK(it2.recursion_pending() == false); + } + TEST_CHECK(rec_pending_it.recursion_pending() == true); + TEST_CHECK(no_rec_pending_it.recursion_pending() == false); +} + +TEST_CASE(increment_resets_value) +{ + const recursive_directory_iterator endIt; + { + recursive_directory_iterator it(StaticEnv::Dir); + it.disable_recursion_pending(); + TEST_CHECK(it.recursion_pending() == false); + ++it; + TEST_CHECK(it.recursion_pending() == true); + TEST_CHECK(it.depth() == 0); + } + { + recursive_directory_iterator it(StaticEnv::Dir); + it.disable_recursion_pending(); + TEST_CHECK(it.recursion_pending() == false); + it++; + TEST_CHECK(it.recursion_pending() == true); + TEST_CHECK(it.depth() == 0); + } + { + recursive_directory_iterator it(StaticEnv::Dir); + it.disable_recursion_pending(); + TEST_CHECK(it.recursion_pending() == false); + std::error_code ec; + it.increment(ec); + TEST_CHECK(it.recursion_pending() == true); + TEST_CHECK(it.depth() == 0); + } +} + +TEST_CASE(pop_does_not_reset_value) +{ + const recursive_directory_iterator endIt; + + auto& DE0 = StaticEnv::DirIterationList; + std::set<path> notSeenDepth0(std::begin(DE0), std::end(DE0)); + + recursive_directory_iterator it(StaticEnv::Dir); + TEST_REQUIRE(it != endIt); + + while (it.depth() == 0) { + notSeenDepth0.erase(it->path()); + ++it; + TEST_REQUIRE(it != endIt); + } + TEST_REQUIRE(it.depth() == 1); + it.disable_recursion_pending(); + it.pop(); + // Since the order of iteration is unspecified the pop() could result + // in the end iterator. When this is the case it is undefined behavior + // to call recursion_pending(). + if (it == endIt) { + TEST_CHECK(notSeenDepth0.empty()); +#if defined(_LIBCPP_VERSION) + TEST_CHECK(it.recursion_pending() == false); +#endif + } else { + TEST_CHECK(! notSeenDepth0.empty()); + TEST_CHECK(it.recursion_pending() == false); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.nonmembers/begin_end.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.nonmembers/begin_end.pass.cpp new file mode 100644 index 00000000000..04bc2dd1d96 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.nonmembers/begin_end.pass.cpp @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class recursive_directory_iterator + +// recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept; +// recursive_directory_iterator end(recursive_directory_iterator iter) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <set> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" +#include <iostream> + +using namespace fs; + +TEST_SUITE(recursive_directory_iterator_begin_end_tests) + +TEST_CASE(test_function_signatures) +{ + using D = recursive_directory_iterator; + recursive_directory_iterator d; ((void)d); + + ASSERT_SAME_TYPE(decltype(begin(d)), recursive_directory_iterator); + ASSERT_NOEXCEPT(begin(std::move(d))); + + ASSERT_SAME_TYPE(decltype(end(d)), recursive_directory_iterator); + ASSERT_NOEXCEPT(end(std::move(d))); +} + +TEST_CASE(test_ranged_for_loop) +{ + const path testDir = StaticEnv::Dir; + std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList), + std::end( StaticEnv::RecDirIterationList)); + + std::error_code ec; + recursive_directory_iterator it(testDir, ec); + TEST_REQUIRE(!ec); + + for (auto& elem : it) { + TEST_CHECK(dir_contents.erase(elem) == 1); + } + TEST_CHECK(dir_contents.empty()); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.enum/check_bitmask_types.hpp b/libcxx/test/std/input.output/filesystems/fs.enum/check_bitmask_types.hpp new file mode 100644 index 00000000000..77b136f3fca --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.enum/check_bitmask_types.hpp @@ -0,0 +1,75 @@ +#ifndef TEST_BITMASK_TYPE_HPP +#define TEST_BITMASK_TYPE_HPP + +#include <type_traits> +#include <cassert> + +#include "test_macros.h" + + +template <class EnumType, EnumType Val1, EnumType Val2, + class UT = typename std::underlying_type<EnumType>::type, + UT UVal1 = static_cast<UT>(Val1), + UT UVal2 = static_cast<UT>(Val2), + UT UZero = static_cast<UT>(0), + EnumType Zero = static_cast<EnumType>(0) + > +struct check_bitmask_type { + + static constexpr UT dcast(EnumType e) { return static_cast<UT>(e); } + static constexpr UT unpromote(decltype((~UZero)) promoted) { return static_cast<UT>(promoted); } + // We need two values that are non-zero and share at least one bit. + static_assert(Val1 != Zero && Val2 != Zero, ""); + static_assert(Val1 != Val2, ""); + static_assert((UVal1 & UVal2) == 0, ""); + + + static bool check() + { + { + EnumType ValRef = Val1; + ASSERT_SAME_TYPE(EnumType, decltype(Val1 & Val2)); + ASSERT_SAME_TYPE(EnumType, decltype(Val1 | Val2)); + ASSERT_SAME_TYPE(EnumType, decltype(Val1 ^ Val2)); + ASSERT_SAME_TYPE(EnumType, decltype((~Val1))); + ASSERT_SAME_TYPE(EnumType&, decltype(ValRef &= Val2)); + ASSERT_SAME_TYPE(EnumType&, decltype(ValRef |= Val2)); + ASSERT_SAME_TYPE(EnumType&, decltype(ValRef ^= Val2)); + } + + static_assert((Val1 & Zero) == Zero, ""); + static_assert((Val1 & Val1) == Val1, ""); + static_assert(dcast(Val1 & Val2) == (UVal1 & UVal2), ""); + + static_assert((Val1 | Zero) == Val1, ""); + static_assert(dcast(Val1 | Val2) == (UVal1 | UVal2), ""); + + static_assert((Val1 ^ Zero) == Val1, ""); + static_assert(dcast(Val1 ^ Val2) == (UVal1 ^ UVal2), ""); + + static_assert(dcast(~Zero) == unpromote(~UZero), ""); + static_assert(dcast(~Val1) == unpromote(~UVal1), ""); + + { + EnumType e = Val1; + EnumType& eref = (e &= Val2); + assert(&eref == &e); + assert(dcast(eref) == (UVal1 & UVal2)); + } + { + EnumType e = Val1; + EnumType& eref = (e |= Val2); + assert(&eref == &e); + assert(dcast(eref) == (UVal1 | UVal2)); + } + { + EnumType e = Val1; + EnumType& eref = (e ^= Val2); + assert(&eref == &e); + assert(dcast(eref) == (UVal1 ^ UVal2)); + } + return true; + } +}; + +#endif // TEST_BITMASK_TYPE diff --git a/libcxx/test/std/input.output/filesystems/fs.enum/enum.copy_options.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.enum/enum.copy_options.pass.cpp new file mode 100644 index 00000000000..75477309fae --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.enum/enum.copy_options.pass.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// enum class copy_options; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "check_bitmask_types.hpp" +#include "test_macros.h" + + +constexpr fs::copy_options ME(int val) { return static_cast<fs::copy_options>(val); } + +int main() { + typedef fs::copy_options E; + static_assert(std::is_enum<E>::value, ""); + + // Check that E is a scoped enum by checking for conversions. + typedef std::underlying_type<E>::type UT; + static_assert(!std::is_convertible<E, UT>::value, ""); + + static_assert(std::is_same<UT, unsigned short>::value, ""); // Implementation detail + + typedef check_bitmask_type<E, E::skip_existing, E::update_existing> BitmaskTester; + assert(BitmaskTester::check()); + + static_assert( + E::none == ME(0), + "Expected enumeration values do not match"); + // Option group for copy_file + static_assert( + E::skip_existing == ME(1) && + E::overwrite_existing == ME(2) && + E::update_existing == ME(4), + "Expected enumeration values do not match"); + // Option group for copy on directories + static_assert( + E::recursive == ME(8), + "Expected enumeration values do not match"); + // Option group for copy on symlinks + static_assert( + E::copy_symlinks == ME(16) && + E::skip_symlinks == ME(32), + "Expected enumeration values do not match"); + // Option group for changing form of copy + static_assert( + E::directories_only == ME(64) && + E::create_symlinks == ME(128) && + E::create_hard_links == ME(256), + "Expected enumeration values do not match"); +} diff --git a/libcxx/test/std/input.output/filesystems/fs.enum/enum.directory_options.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.enum/enum.directory_options.pass.cpp new file mode 100644 index 00000000000..db40f6de91b --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.enum/enum.directory_options.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// enum class directory_options; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> +#include <sys/stat.h> + +#include "test_macros.h" +#include "check_bitmask_types.hpp" + + +constexpr fs::directory_options ME(int val) { return static_cast<fs::directory_options>(val); } + +int main() { + typedef fs::directory_options E; + static_assert(std::is_enum<E>::value, ""); + + // Check that E is a scoped enum by checking for conversions. + typedef std::underlying_type<E>::type UT; + static_assert(!std::is_convertible<E, UT>::value, ""); + static_assert(std::is_same<UT, unsigned char>::value, ""); + + typedef check_bitmask_type<E, E::follow_directory_symlink, E::skip_permission_denied> BitmaskTester; + assert(BitmaskTester::check()); + + static_assert( + E::none == ME(0) && + E::follow_directory_symlink == ME(1) && + E::skip_permission_denied == ME(2), + "Expected enumeration values do not match"); + +} diff --git a/libcxx/test/std/input.output/filesystems/fs.enum/enum.file_type.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.enum/enum.file_type.pass.cpp new file mode 100644 index 00000000000..899ab682042 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.enum/enum.file_type.pass.cpp @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// enum class file_type; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" + + +constexpr fs::file_type ME(int val) { return static_cast<fs::file_type>(val); } + +int main() { + typedef fs::file_type E; + static_assert(std::is_enum<E>::value, ""); + + // Check that E is a scoped enum by checking for conversions. + typedef std::underlying_type<E>::type UT; + static_assert(!std::is_convertible<E, UT>::value, ""); + + static_assert(std::is_same<UT, signed char>::value, ""); // Implementation detail + + static_assert( + E::none == ME(0) && + E::not_found == ME(-1) && + E::regular == ME(1) && + E::directory == ME(2) && + E::symlink == ME(3) && + E::block == ME(4) && + E::character == ME(5) && + E::fifo == ME(6) && + E::socket == ME(7) && + E::unknown == ME(8), + "Expected enumeration values do not match"); +} diff --git a/libcxx/test/std/input.output/filesystems/fs.enum/enum.path.format.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.enum/enum.path.format.pass.cpp new file mode 100644 index 00000000000..d4ec48e8480 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.enum/enum.path.format.pass.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// class path; +// enum class format; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" + +int main() { + typedef fs::path::format E; + static_assert(std::is_enum<E>::value, ""); + + // Check that E is a scoped enum by checking for conversions. + typedef std::underlying_type<E>::type UT; + static_assert(!std::is_convertible<E, UT>::value, ""); + + LIBCPP_ONLY(static_assert(std::is_same<UT, unsigned char>::value, "")); // Implementation detail + + static_assert( + E::auto_format != E::native_format && + E::auto_format != E::generic_format && + E::native_format != E::generic_format, + "Expected enumeration values are not unique"); +} diff --git a/libcxx/test/std/input.output/filesystems/fs.enum/enum.perm_options.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.enum/enum.perm_options.pass.cpp new file mode 100644 index 00000000000..5af504530de --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.enum/enum.perm_options.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// enum class perm_options; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> +#include <sys/stat.h> + +#include "test_macros.h" +#include "check_bitmask_types.hpp" + + +constexpr fs::perm_options ME(int val) { + return static_cast<fs::perm_options>(val); +} + +int main() { + typedef fs::perm_options E; + static_assert(std::is_enum<E>::value, ""); + + // Check that E is a scoped enum by checking for conversions. + typedef std::underlying_type<E>::type UT; + static_assert(!std::is_convertible<E, UT>::value, ""); + + static_assert(std::is_same<UT, unsigned char >::value, ""); // Implementation detail + + typedef check_bitmask_type<E, E::replace, E::nofollow> BitmaskTester; + assert(BitmaskTester::check()); + + static_assert( + E::replace == ME(1) && + E::add == ME(2) && + E::remove == ME(4) && + E::nofollow == ME(8), + "Expected enumeration values do not match"); +} diff --git a/libcxx/test/std/input.output/filesystems/fs.enum/enum.perms.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.enum/enum.perms.pass.cpp new file mode 100644 index 00000000000..9f2e4e214be --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.enum/enum.perms.pass.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// enum class perms; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> +#include <sys/stat.h> + +#include "test_macros.h" +#include "check_bitmask_types.hpp" + + +constexpr fs::perms ME(int val) { return static_cast<fs::perms>(val); } + +int main() { + typedef fs::perms E; + static_assert(std::is_enum<E>::value, ""); + + // Check that E is a scoped enum by checking for conversions. + typedef std::underlying_type<E>::type UT; + static_assert(!std::is_convertible<E, UT>::value, ""); + + static_assert(std::is_same<UT, unsigned >::value, ""); // Implementation detail + + typedef check_bitmask_type<E, E::group_all, E::owner_all> BitmaskTester; + assert(BitmaskTester::check()); + + static_assert( + E::none == ME(0) && + + E::owner_read == ME(0400) && + E::owner_write == ME(0200) && + E::owner_exec == ME(0100) && + E::owner_all == ME(0700) && + + E::group_read == ME(040) && + E::group_write == ME(020) && + E::group_exec == ME(010) && + E::group_all == ME(070) && + + E::others_read == ME(04) && + E::others_write == ME(02) && + E::others_exec == ME(01) && + E::others_all == ME(07) && + E::all == ME(0777) && + E::set_uid == ME(04000) && + E::set_gid == ME(02000) && + E::sticky_bit == ME(01000) && + E::mask == ME(07777) && + E::unknown == ME(0xFFFF), + "Expected enumeration values do not match"); +} diff --git a/libcxx/test/std/input.output/filesystems/fs.error.report/tested_elsewhere.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.error.report/tested_elsewhere.pass.cpp new file mode 100644 index 00000000000..b58f5c55b64 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.error.report/tested_elsewhere.pass.cpp @@ -0,0 +1,12 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +int main() +{ +} diff --git a/libcxx/test/std/input.output/filesystems/fs.filesystem.synopsis/file_time_type.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.filesystem.synopsis/file_time_type.pass.cpp new file mode 100644 index 00000000000..157083a3094 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.filesystem.synopsis/file_time_type.pass.cpp @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// typedef TrivialClock file_time_type; + +#include "filesystem_include.hpp" +#include <chrono> +#include <type_traits> + +#include "test_macros.h" + +// system_clock is used because it meets the requirements of TrivialClock, +// and the resolution and range of system_clock should match the operating +// system's file time type. + +void test_trivial_clock() { + using namespace fs; + using Clock = file_time_type::clock; + ASSERT_NOEXCEPT(Clock::now()); + ASSERT_SAME_TYPE(decltype(Clock::now()), file_time_type); + ASSERT_SAME_TYPE(Clock::time_point, file_time_type); + volatile auto* odr_use = &Clock::is_steady; + ((void)odr_use); +} + +void test_time_point_resolution_and_range() { + using namespace fs; + using Dur = file_time_type::duration; + using Period = Dur::period; + ASSERT_SAME_TYPE(Period, std::nano); +} + +int main() { + test_trivial_clock(); + test_time_point_resolution_and_range(); +} diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.absolute/absolute.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.absolute/absolute.pass.cpp new file mode 100644 index 00000000000..b2a5794a5dd --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.absolute/absolute.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// path absolute(const path& p, const path& base=current_path()); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_absolute_path_test_suite) + +TEST_CASE(absolute_signature_test) +{ + const path p; ((void)p); + std::error_code ec; + ASSERT_NOT_NOEXCEPT(absolute(p)); + ASSERT_NOT_NOEXCEPT(absolute(p, ec)); +} + + +TEST_CASE(basic_test) +{ + const fs::path cwd = fs::current_path(); + const struct { + std::string input; + std::string expect; + } TestCases [] = { + {"", cwd / ""}, + {"foo", cwd / "foo"}, + {"foo/", cwd / "foo/"}, + {"/already_absolute", "/already_absolute"} + }; + for (auto& TC : TestCases) { + std::error_code ec = GetTestEC(); + const path ret = absolute(TC.input, ec); + TEST_CHECK(!ec); + TEST_CHECK(ret.is_absolute()); + TEST_CHECK(PathEq(ret, TC.expect)); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp new file mode 100644 index 00000000000..eb10d9816c6 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp @@ -0,0 +1,124 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// path canonical(const path& p); +// path canonical(const path& p, error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +struct CWDGuard { + path OldCWD; + CWDGuard() : OldCWD(fs::current_path()) { } + ~CWDGuard() { fs::current_path(OldCWD); } + + CWDGuard(CWDGuard const&) = delete; + CWDGuard& operator=(CWDGuard const&) = delete; +}; + +TEST_SUITE(filesystem_canonical_path_test_suite) + +TEST_CASE(signature_test) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(canonical(p)); + ASSERT_NOT_NOEXCEPT(canonical(p, ec)); +} + +// There are 4 cases is the proposal for absolute path. +// Each scope tests one of the cases. +TEST_CASE(test_canonical) +{ + CWDGuard guard; + // has_root_name() && has_root_directory() + const path Root = StaticEnv::Root; + const path RootName = Root.filename(); + const path DirName = StaticEnv::Dir.filename(); + const path SymlinkName = StaticEnv::SymlinkToFile.filename(); + struct TestCase { + path p; + path expect; + path base; + TestCase(path p1, path e, path b = StaticEnv::Root) + : p(p1), expect(e), base(b) {} + }; + const TestCase testCases[] = { + { ".", Root, Root}, + { DirName / ".." / "." / DirName, StaticEnv::Dir, Root}, + { StaticEnv::Dir2 / "..", StaticEnv::Dir }, + { StaticEnv::Dir3 / "../..", StaticEnv::Dir }, + { StaticEnv::Dir / ".", StaticEnv::Dir }, + { Root / "." / DirName / ".." / DirName, StaticEnv::Dir}, + { path("..") / "." / RootName / DirName / ".." / DirName, StaticEnv::Dir, Root}, + { StaticEnv::SymlinkToFile, StaticEnv::File }, + { SymlinkName, StaticEnv::File, StaticEnv::Root} + }; + for (auto& TC : testCases) { + std::error_code ec = GetTestEC(); + fs::current_path(TC.base); + const path ret = canonical(TC.p, ec); + TEST_REQUIRE(!ec); + const path ret2 = canonical(TC.p); + TEST_CHECK(PathEq(ret, TC.expect)); + TEST_CHECK(PathEq(ret, ret2)); + TEST_CHECK(ret.is_absolute()); + } +} + +TEST_CASE(test_dne_path) +{ + std::error_code ec = GetTestEC(); + { + const path ret = canonical(StaticEnv::DNE, ec); + TEST_CHECK(ec != GetTestEC()); + TEST_REQUIRE(ec); + TEST_CHECK(ret == path{}); + } + { + TEST_CHECK_THROW(filesystem_error, canonical(StaticEnv::DNE)); + } +} + +TEST_CASE(test_exception_contains_paths) +{ +#ifndef TEST_HAS_NO_EXCEPTIONS + CWDGuard guard; + const path p = "blabla/dne"; + try { + canonical(p); + TEST_REQUIRE(false); + } catch (filesystem_error const& err) { + TEST_CHECK(err.path1() == p); + // libc++ provides the current path as the second path in the exception + LIBCPP_ONLY(TEST_CHECK(err.path2() == current_path())); + } + fs::current_path(StaticEnv::Dir); + try { + canonical(p); + TEST_REQUIRE(false); + } catch (filesystem_error const& err) { + TEST_CHECK(err.path1() == p); + LIBCPP_ONLY(TEST_CHECK(err.path2() == StaticEnv::Dir)); + } +#endif +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy/copy.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy/copy.pass.cpp new file mode 100644 index 00000000000..ba579d0af64 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy/copy.pass.cpp @@ -0,0 +1,315 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// void copy(const path& from, const path& to); +// void copy(const path& from, const path& to, error_code& ec); +// void copy(const path& from, const path& to, copy_options options); +// void copy(const path& from, const path& to, copy_options options, +// error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cstddef> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +using CO = fs::copy_options; + +TEST_SUITE(filesystem_copy_test_suite) + +TEST_CASE(signature_test) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + const copy_options opts{}; ((void)opts); + ASSERT_NOT_NOEXCEPT(fs::copy(p, p)); + ASSERT_NOT_NOEXCEPT(fs::copy(p, p, ec)); + ASSERT_NOT_NOEXCEPT(copy(p, p, opts)); + ASSERT_NOT_NOEXCEPT(copy(p, p, opts, ec)); +} + +// There are 4 cases is the proposal for absolute path. +// Each scope tests one of the cases. +TEST_CASE(test_error_reporting) +{ + auto checkThrow = [](path const& f, path const& t, const std::error_code& ec) + { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + fs::copy(f, t); + return false; + } catch (filesystem_error const& err) { + return err.path1() == f + && err.path2() == t + && err.code() == ec; + } +#else + ((void)f); ((void)t); ((void)ec); + return true; +#endif + }; + + scoped_test_env env; + const path file = env.create_file("file1", 42); + const path dir = env.create_dir("dir"); + const path fifo = env.create_fifo("fifo"); + TEST_REQUIRE(is_other(fifo)); + + const auto test_ec = GetTestEC(); + + // !exists(f) + { + std::error_code ec = test_ec; + const path f = StaticEnv::DNE; + const path t = env.test_root; + fs::copy(f, t, ec); + TEST_REQUIRE(ec); + TEST_REQUIRE(ec != test_ec); + TEST_CHECK(checkThrow(f, t, ec)); + } + { // equivalent(f, t) == true + std::error_code ec = test_ec; + fs::copy(file, file, ec); + TEST_REQUIRE(ec); + TEST_REQUIRE(ec != test_ec); + TEST_CHECK(checkThrow(file, file, ec)); + } + { // is_directory(from) && is_file(to) + std::error_code ec = test_ec; + fs::copy(dir, file, ec); + TEST_REQUIRE(ec); + TEST_REQUIRE(ec != test_ec); + TEST_CHECK(checkThrow(dir, file, ec)); + } + { // is_other(from) + std::error_code ec = test_ec; + fs::copy(fifo, dir, ec); + TEST_REQUIRE(ec); + TEST_REQUIRE(ec != test_ec); + TEST_CHECK(checkThrow(fifo, dir, ec)); + } + { // is_other(to) + std::error_code ec = test_ec; + fs::copy(file, fifo, ec); + TEST_REQUIRE(ec); + TEST_REQUIRE(ec != test_ec); + TEST_CHECK(checkThrow(file, fifo, ec)); + } +} + +TEST_CASE(from_is_symlink) +{ + scoped_test_env env; + const path file = env.create_file("file", 42); + const path symlink = env.create_symlink(file, "sym"); + const path dne = env.make_env_path("dne"); + + { // skip symlinks + std::error_code ec = GetTestEC(); + fs::copy(symlink, dne, copy_options::skip_symlinks, ec); + TEST_CHECK(!ec); + TEST_CHECK(!exists(dne)); + } + { + const path dest = env.make_env_path("dest"); + std::error_code ec = GetTestEC(); + fs::copy(symlink, dest, copy_options::copy_symlinks, ec); + TEST_CHECK(!ec); + TEST_CHECK(exists(dest)); + TEST_CHECK(is_symlink(dest)); + } + { // copy symlink but target exists + std::error_code ec = GetTestEC(); + fs::copy(symlink, file, copy_options::copy_symlinks, ec); + TEST_CHECK(ec); + TEST_CHECK(ec != GetTestEC()); + } + { // create symlinks but target exists + std::error_code ec = GetTestEC(); + fs::copy(symlink, file, copy_options::create_symlinks, ec); + TEST_CHECK(ec); + TEST_CHECK(ec != GetTestEC()); + } +} + +TEST_CASE(from_is_regular_file) +{ + scoped_test_env env; + const path file = env.create_file("file", 42); + const path dir = env.create_dir("dir"); + { // skip copy because of directory + const path dest = env.make_env_path("dest1"); + std::error_code ec = GetTestEC(); + fs::copy(file, dest, CO::directories_only, ec); + TEST_CHECK(!ec); + TEST_CHECK(!exists(dest)); + } + { // create symlink to file + const path dest = env.make_env_path("sym"); + std::error_code ec = GetTestEC(); + fs::copy(file, dest, CO::create_symlinks, ec); + TEST_CHECK(!ec); + TEST_CHECK(is_symlink(dest)); + TEST_CHECK(equivalent(file, canonical(dest))); + } + { // create hard link to file + const path dest = env.make_env_path("hardlink"); + TEST_CHECK(hard_link_count(file) == 1); + std::error_code ec = GetTestEC(); + fs::copy(file, dest, CO::create_hard_links, ec); + TEST_CHECK(!ec); + TEST_CHECK(exists(dest)); + TEST_CHECK(hard_link_count(file) == 2); + } + { // is_directory(t) + const path dest_dir = env.create_dir("dest_dir"); + const path expect_dest = dest_dir / file.filename(); + std::error_code ec = GetTestEC(); + fs::copy(file, dest_dir, ec); + TEST_CHECK(!ec); + TEST_CHECK(is_regular_file(expect_dest)); + } + { // otherwise copy_file(from, to, ...) + const path dest = env.make_env_path("file_copy"); + std::error_code ec = GetTestEC(); + fs::copy(file, dest, ec); + TEST_CHECK(!ec); + TEST_CHECK(is_regular_file(dest)); + } +} + +TEST_CASE(from_is_directory) +{ + struct FileInfo { + path filename; + std::size_t size; + }; + const FileInfo files[] = { + {"file1", 0}, + {"file2", 42}, + {"file3", 300} + }; + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path nested_dir_name = "dir2"; + const path nested_dir = env.create_dir("dir/dir2"); + + for (auto& FI : files) { + env.create_file(dir / FI.filename, FI.size); + env.create_file(nested_dir / FI.filename, FI.size); + } + { // test for non-existent directory + const path dest = env.make_env_path("dest_dir1"); + std::error_code ec = GetTestEC(); + fs::copy(dir, dest, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(is_directory(dest)); + for (auto& FI : files) { + path created = dest / FI.filename; + TEST_CHECK(is_regular_file(created)); + TEST_CHECK(file_size(created) == FI.size); + } + TEST_CHECK(!is_directory(dest / nested_dir_name)); + } + { // test for existing directory + const path dest = env.create_dir("dest_dir2"); + std::error_code ec = GetTestEC(); + fs::copy(dir, dest, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(is_directory(dest)); + for (auto& FI : files) { + path created = dest / FI.filename; + TEST_CHECK(is_regular_file(created)); + TEST_CHECK(file_size(created) == FI.size); + } + TEST_CHECK(!is_directory(dest / nested_dir_name)); + } + { // test recursive copy + const path dest = env.make_env_path("dest_dir3"); + std::error_code ec = GetTestEC(); + fs::copy(dir, dest, CO::recursive, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(is_directory(dest)); + const path nested_dest = dest / nested_dir_name; + TEST_REQUIRE(is_directory(nested_dest)); + for (auto& FI : files) { + path created = dest / FI.filename; + path nested_created = nested_dest / FI.filename; + TEST_CHECK(is_regular_file(created)); + TEST_CHECK(file_size(created) == FI.size); + TEST_CHECK(is_regular_file(nested_created)); + TEST_CHECK(file_size(nested_created) == FI.size); + } + } +} + +TEST_CASE(test_copy_symlinks_to_symlink_dir) +{ + scoped_test_env env; + const path file1 = env.create_file("file1", 42); + const path file2 = env.create_file("file2", 101); + const path file2_sym = env.create_symlink(file2, "file2_sym"); + const path dir = env.create_dir("dir"); + const path dir_sym = env.create_symlink(dir, "dir_sym"); + { + std::error_code ec = GetTestEC(); + fs::copy(file1, dir_sym, copy_options::copy_symlinks, ec); + TEST_CHECK(!ec); + const path dest = env.make_env_path("dir/file1"); + TEST_CHECK(exists(dest)); + TEST_CHECK(!is_symlink(dest)); + TEST_CHECK(file_size(dest) == 42); + } +} + + +TEST_CASE(test_dir_create_symlink) +{ + scoped_test_env env; + const path dir = env.create_dir("dir1"); + const path dest = env.make_env_path("dne"); + { + std::error_code ec = GetTestEC(); + fs::copy(dir, dest, copy_options::create_symlinks, ec); + TEST_CHECK(ec == std::make_error_code(std::errc::is_a_directory)); + TEST_CHECK(!exists(dest)); + TEST_CHECK(!is_symlink(dest)); + } + { + std::error_code ec = GetTestEC(); + fs::copy(dir, dest, copy_options::create_symlinks|copy_options::recursive, ec); + TEST_CHECK(ec == std::make_error_code(std::errc::is_a_directory)); + TEST_CHECK(!exists(dest)); + TEST_CHECK(!is_symlink(dest)); + } +} + +TEST_CASE(test_otherwise_no_effects_clause) +{ + scoped_test_env env; + const path dir = env.create_dir("dir1"); + { // skip copy because of directory + const path dest = env.make_env_path("dest1"); + std::error_code ec; + fs::copy(dir, dest, CO::directories_only, ec); + TEST_CHECK(!ec); + TEST_CHECK(!exists(dest)); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp new file mode 100644 index 00000000000..a67b431fbc8 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp @@ -0,0 +1,189 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool copy_file(const path& from, const path& to); +// bool copy_file(const path& from, const path& to, error_code& ec) noexcept; +// bool copy_file(const path& from, const path& to, copy_options options); +// bool copy_file(const path& from, const path& to, copy_options options, +// error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <chrono> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +#include <iostream> + +using namespace fs; + +using CO = fs::copy_options; + +TEST_SUITE(filesystem_copy_file_test_suite) + +TEST_CASE(test_signatures) { + const path p; + ((void)p); + const copy_options opts{}; + ((void)opts); + std::error_code ec; + ((void)ec); + ASSERT_SAME_TYPE(decltype(fs::copy_file(p, p)), bool); + ASSERT_SAME_TYPE(decltype(fs::copy_file(p, p, opts)), bool); + ASSERT_SAME_TYPE(decltype(fs::copy_file(p, p, ec)), bool); + ASSERT_SAME_TYPE(decltype(fs::copy_file(p, p, opts, ec)), bool); + ASSERT_NOT_NOEXCEPT(fs::copy_file(p, p)); + ASSERT_NOT_NOEXCEPT(fs::copy_file(p, p, opts)); + ASSERT_NOT_NOEXCEPT(fs::copy_file(p, p, ec)); + ASSERT_NOT_NOEXCEPT(fs::copy_file(p, p, opts, ec)); +} + +TEST_CASE(test_error_reporting) { + + scoped_test_env env; + const path file = env.create_file("file1", 42); + const path file2 = env.create_file("file2", 55); + const path non_regular_file = env.create_fifo("non_reg"); + const path dne = env.make_env_path("dne"); + + { // exists(to) && equivalent(to, from) + std::error_code ec; + TEST_CHECK(fs::copy_file(file, file, copy_options::overwrite_existing, + ec) == false); + TEST_CHECK(ErrorIs(ec, std::errc::file_exists)); + ExceptionChecker Checker(file, file, std::errc::file_exists, "copy_file"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing)); + + } + { // exists(to) && !(skip_existing | overwrite_existing | update_existing) + std::error_code ec; + TEST_CHECK(fs::copy_file(file, file2, ec) == false); + TEST_CHECK(ErrorIs(ec, std::errc::file_exists)); + ExceptionChecker Checker(file, file, std::errc::file_exists, "copy_file"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing)); + + } +} + +TEST_CASE(non_regular_file_test) { + scoped_test_env env; + const path fifo = env.create_fifo("fifo"); + const path dest = env.make_env_path("dest"); + const path file = env.create_file("file", 42); + + { + std::error_code ec = GetTestEC(); + TEST_REQUIRE(fs::copy_file(fifo, dest, ec) == false); + TEST_CHECK(ErrorIs(ec, std::errc::not_supported)); + TEST_CHECK(!exists(dest)); + } + { + std::error_code ec = GetTestEC(); + TEST_REQUIRE(fs::copy_file(file, fifo, copy_options::overwrite_existing, + ec) == false); + TEST_CHECK(ErrorIs(ec, std::errc::not_supported)); + TEST_CHECK(is_fifo(fifo)); + } + +} + +TEST_CASE(test_attributes_get_copied) { + scoped_test_env env; + const path file = env.create_file("file1", 42); + const path dest = env.make_env_path("file2"); + auto st = status(file); + perms new_perms = perms::owner_read; + permissions(file, new_perms); + std::error_code ec = GetTestEC(); + TEST_REQUIRE(fs::copy_file(file, dest, ec) == true); + TEST_CHECK(!ec); + auto new_st = status(dest); + TEST_CHECK(new_st.permissions() == new_perms); +} + +TEST_CASE(copy_dir_test) { + scoped_test_env env; + const path file = env.create_file("file1", 42); + const path dest = env.create_dir("dir1"); + std::error_code ec = GetTestEC(); + TEST_CHECK(fs::copy_file(file, dest, ec) == false); + TEST_CHECK(ec); + TEST_CHECK(ec != GetTestEC()); + ec = GetTestEC(); + TEST_CHECK(fs::copy_file(dest, file, ec) == false); + TEST_CHECK(ec); + TEST_CHECK(ec != GetTestEC()); +} + +TEST_CASE(copy_file) { + scoped_test_env env; + const path file = env.create_file("file1", 42); + + { // !exists(to) + const path dest = env.make_env_path("dest1"); + std::error_code ec = GetTestEC(); + + TEST_REQUIRE(fs::copy_file(file, dest, ec) == true); + TEST_CHECK(!ec); + TEST_CHECK(file_size(dest) == 42); + } + { // exists(to) && overwrite_existing + const path dest = env.create_file("dest2", 55); + permissions(dest, perms::all); + permissions(file, + perms::group_write | perms::owner_write | perms::others_write, + perm_options::remove); + + std::error_code ec = GetTestEC(); + TEST_REQUIRE(fs::copy_file(file, dest, copy_options::overwrite_existing, + ec) == true); + TEST_CHECK(!ec); + TEST_CHECK(file_size(dest) == 42); + TEST_CHECK(status(dest).permissions() == status(file).permissions()); + } + { // exists(to) && update_existing + using Sec = std::chrono::seconds; + const path older = env.create_file("older_file", 1); + + SleepFor(Sec(2)); + const path from = env.create_file("update_from", 55); + + SleepFor(Sec(2)); + const path newer = env.create_file("newer_file", 2); + + std::error_code ec = GetTestEC(); + TEST_REQUIRE( + fs::copy_file(from, older, copy_options::update_existing, ec) == true); + TEST_CHECK(!ec); + TEST_CHECK(file_size(older) == 55); + + TEST_REQUIRE( + fs::copy_file(from, newer, copy_options::update_existing, ec) == false); + TEST_CHECK(!ec); + TEST_CHECK(file_size(newer) == 2); + } + { // skip_existing + const path file2 = env.create_file("file2", 55); + std::error_code ec = GetTestEC(); + TEST_REQUIRE(fs::copy_file(file, file2, copy_options::skip_existing, ec) == + false); + TEST_CHECK(!ec); + TEST_CHECK(file_size(file2) == 55); + } +} + + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_large.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_large.pass.cpp new file mode 100644 index 00000000000..61733d526e3 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_large.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 +// REQUIRES: long_tests + +// <filesystem> + +// bool copy_file(const path& from, const path& to); +// bool copy_file(const path& from, const path& to, error_code& ec) noexcept; +// bool copy_file(const path& from, const path& to, copy_options options); +// bool copy_file(const path& from, const path& to, copy_options options, +// error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <chrono> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_copy_file_test_suite) + +static std::string random_hex_chars(uintmax_t size) { + std::string data; + data.reserve(size); + for (uintmax_t I = 0; I < size; ++I) + data.push_back(random_utils::random_hex_char()); + return data; +} + +// This test is intended to test 'sendfile's 2gb limit for a single call, and +// to ensure that libc++ correctly copies files larger than that limit. +// However it requires allocating ~5GB of filesystem space. This might not +// be acceptable on all systems. +TEST_CASE(large_file) { + using namespace fs; + constexpr uintmax_t sendfile_size_limit = 2147479552ull; + constexpr uintmax_t additional_size = 1024; + constexpr uintmax_t test_file_size = sendfile_size_limit + additional_size; + static_assert(test_file_size > sendfile_size_limit, ""); + + scoped_test_env env; + + // Check that we have more than sufficient room to create the files needed + // to perform the test. + if (space(env.test_root).available < 3 * test_file_size) { + TEST_UNSUPPORTED(); + } + + // Use python to create a file right at the size limit. + const path file = env.create_file("source", sendfile_size_limit); + // Create some random data that looks different than the data before the + // size limit. + const std::string additional_data = random_hex_chars(additional_size); + // Append this known data to the end of the source file. + { + std::ofstream outf(file.native(), std::ios_base::app); + TEST_REQUIRE(outf.good()); + outf << additional_data; + TEST_REQUIRE(outf); + } + TEST_REQUIRE(file_size(file) == test_file_size); + const path dest = env.make_env_path("dest"); + + std::error_code ec = GetTestEC(); + TEST_CHECK(copy_file(file, dest, ec)); + TEST_CHECK(!ec); + + TEST_REQUIRE(is_regular_file(dest)); + TEST_CHECK(file_size(dest) == test_file_size); + + // Read the data from the end of the destination file, and ensure it matches + // the data at the end of the source file. + std::string out_data; + out_data.reserve(additional_size); + { + std::ifstream dest_file(dest.native()); + TEST_REQUIRE(dest_file); + dest_file.seekg(sendfile_size_limit); + TEST_REQUIRE(dest_file); + dest_file >> out_data; + TEST_CHECK(dest_file.eof()); + } + TEST_CHECK(out_data.size() == additional_data.size()); + TEST_CHECK(out_data == additional_data); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_symlink/copy_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_symlink/copy_symlink.pass.cpp new file mode 100644 index 00000000000..ee4a9ed4bc5 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_symlink/copy_symlink.pass.cpp @@ -0,0 +1,108 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// void copy_symlink(const path& existing_symlink, const path& new_symlink); +// void copy_symlink(const path& existing_symlink, const path& new_symlink, +// error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_copy_symlink_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(fs::copy_symlink(p, p)); + ASSERT_NOEXCEPT(fs::copy_symlink(p, p, ec)); +} + + +TEST_CASE(test_error_reporting) +{ + auto checkThrow = [](path const& f, path const& t, const std::error_code& ec) + { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + fs::copy_symlink(f, t); + return true; + } catch (filesystem_error const& err) { + return err.path1() == f + && err.code() == ec; + } +#else + ((void)f); ((void)t); ((void)ec); + return true; +#endif + }; + + scoped_test_env env; + const path file = env.create_file("file1", 42); + const path file2 = env.create_file("file2", 55); + const path sym = env.create_symlink(file, "sym"); + const path dir = env.create_dir("dir"); + const path dne = env.make_env_path("dne"); + { // from is a file, not a symlink + std::error_code ec; + fs::copy_symlink(file, dne, ec); + TEST_REQUIRE(ec); + TEST_CHECK(checkThrow(file, dne, ec)); + } + { // from is a file, not a symlink + std::error_code ec; + fs::copy_symlink(dir, dne, ec); + TEST_REQUIRE(ec); + TEST_CHECK(checkThrow(dir, dne, ec)); + } + { // destination exists + std::error_code ec; + fs::copy_symlink(sym, file2, ec); + TEST_REQUIRE(ec); + } +} + +TEST_CASE(copy_symlink_basic) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path dir_sym = env.create_symlink(dir, "dir_sym"); + const path file = env.create_file("file", 42); + const path file_sym = env.create_symlink(file, "file_sym"); + { // test for directory symlinks + const path dest = env.make_env_path("dest1"); + std::error_code ec; + fs::copy_symlink(dir_sym, dest, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(is_symlink(dest)); + TEST_CHECK(equivalent(dest, dir)); + } + { // test for file symlinks + const path dest = env.make_env_path("dest2"); + std::error_code ec; + fs::copy_symlink(file_sym, dest, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(is_symlink(dest)); + TEST_CHECK(equivalent(dest, file)); + } +} + + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directories/create_directories.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directories/create_directories.pass.cpp new file mode 100644 index 00000000000..6b651dfb01e --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directories/create_directories.pass.cpp @@ -0,0 +1,101 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool create_directories(const path& p); +// bool create_directories(const path& p, error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_create_directories_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(fs::create_directories(p)), bool); + ASSERT_SAME_TYPE(decltype(fs::create_directories(p, ec)), bool); + ASSERT_NOT_NOEXCEPT(fs::create_directories(p)); + ASSERT_NOT_NOEXCEPT(fs::create_directories(p, ec)); +} + +TEST_CASE(create_existing_directory) +{ + scoped_test_env env; + const path dir = env.create_dir("dir1"); + std::error_code ec; + TEST_CHECK(fs::create_directories(dir, ec) == false); + TEST_CHECK(!ec); + TEST_CHECK(is_directory(dir)); +} + +TEST_CASE(create_directory_one_level) +{ + scoped_test_env env; + const path dir = env.make_env_path("dir1"); + std::error_code ec; + TEST_CHECK(fs::create_directories(dir, ec) == true); + TEST_CHECK(!ec); + TEST_CHECK(is_directory(dir)); +} + +TEST_CASE(create_directories_multi_level) +{ + scoped_test_env env; + const path dir = env.make_env_path("dir1/dir2/dir3"); + std::error_code ec; + TEST_CHECK(fs::create_directories(dir, ec) == true); + TEST_CHECK(!ec); + TEST_CHECK(is_directory(dir)); +} + +TEST_CASE(create_directory_symlinks) { + scoped_test_env env; + const path root = env.create_dir("dir"); + const path sym_dest_dead = env.make_env_path("dead"); + const path dead_sym = env.create_symlink(sym_dest_dead, "dir/sym_dir"); + const path target = env.make_env_path("dir/sym_dir/foo"); + { + std::error_code ec = GetTestEC(); + TEST_CHECK(create_directories(target, ec) == false); + TEST_CHECK(ec); + TEST_CHECK(!exists(sym_dest_dead)); + TEST_CHECK(!exists(dead_sym)); + } +} + + +TEST_CASE(create_directory_through_symlinks) { + scoped_test_env env; + const path root = env.create_dir("dir"); + const path sym_dir = env.create_symlink(root, "sym_dir"); + const path target = env.make_env_path("sym_dir/foo"); + const path resolved_target = env.make_env_path("dir/foo"); + TEST_REQUIRE(is_directory(sym_dir)); + { + std::error_code ec = GetTestEC(); + TEST_CHECK(create_directories(target, ec) == true); + TEST_CHECK(!ec); + TEST_CHECK(is_directory(target)); + TEST_CHECK(is_directory(resolved_target)); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directory/create_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directory/create_directory.pass.cpp new file mode 100644 index 00000000000..e73e21ffb44 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directory/create_directory.pass.cpp @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool create_directory(const path& p); +// bool create_directory(const path& p, error_code& ec) noexcept; +// bool create_directory(const path& p, const path& attr); +// bool create_directory(const path& p, const path& attr, error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +#include <sys/types.h> +#include <sys/stat.h> + +using namespace fs; + +fs::perms read_umask() { + mode_t old_mask = umask(0); + umask(old_mask); // reset the mask to the old value. + return static_cast<fs::perms>(old_mask); +} + +TEST_SUITE(filesystem_create_directory_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(fs::create_directory(p)), bool); + ASSERT_SAME_TYPE(decltype(fs::create_directory(p, ec)), bool); + ASSERT_SAME_TYPE(decltype(fs::create_directory(p, p)), bool); + ASSERT_SAME_TYPE(decltype(fs::create_directory(p, p, ec)), bool); + ASSERT_NOT_NOEXCEPT(fs::create_directory(p)); + ASSERT_NOEXCEPT(fs::create_directory(p, ec)); + ASSERT_NOT_NOEXCEPT(fs::create_directory(p, p)); + ASSERT_NOEXCEPT(fs::create_directory(p, p, ec)); +} + + +TEST_CASE(create_existing_directory) +{ + scoped_test_env env; + const path dir = env.create_dir("dir1"); + std::error_code ec; + TEST_CHECK(fs::create_directory(dir, ec) == false); + TEST_CHECK(!ec); + TEST_CHECK(is_directory(dir)); + // Test throwing version + TEST_CHECK(fs::create_directory(dir) == false); +} + +TEST_CASE(create_directory_one_level) +{ + scoped_test_env env; + const path dir = env.make_env_path("dir1"); + std::error_code ec; + TEST_CHECK(fs::create_directory(dir, ec) == true); + TEST_CHECK(!ec); + TEST_CHECK(is_directory(dir)); + + auto st = status(dir); + const perms expect_perms = perms::all & ~(read_umask()); + TEST_CHECK((st.permissions() & perms::all) == expect_perms); +} + +TEST_CASE(create_directory_multi_level) +{ + scoped_test_env env; + const path dir = env.make_env_path("dir1/dir2"); + const path dir1 = env.make_env_path("dir1"); + std::error_code ec; + TEST_CHECK(fs::create_directory(dir, ec) == false); + TEST_CHECK(ec); + TEST_CHECK(!is_directory(dir)); + TEST_CHECK(!is_directory(dir1)); +} + +TEST_CASE(dest_is_file) +{ + scoped_test_env env; + const path file = env.create_file("file", 42); + std::error_code ec = GetTestEC(); + TEST_CHECK(fs::create_directory(file, ec) == false); + TEST_CHECK(!ec); + TEST_CHECK(is_regular_file(file)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directory/create_directory_with_attributes.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directory/create_directory_with_attributes.pass.cpp new file mode 100644 index 00000000000..f013bbb203b --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directory/create_directory_with_attributes.pass.cpp @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool create_directory(const path& p, const path& attr); +// bool create_directory(const path& p, const path& attr, error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_create_directory_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(fs::create_directory(p, p)), bool); + ASSERT_SAME_TYPE(decltype(fs::create_directory(p, p, ec)), bool); + ASSERT_NOT_NOEXCEPT(fs::create_directory(p, p)); + ASSERT_NOEXCEPT(fs::create_directory(p, p, ec)); +} + +TEST_CASE(create_existing_directory) +{ + scoped_test_env env; + const path dir = env.create_dir("dir1"); + const path dir2 = env.create_dir("dir2"); + + const perms orig_p = status(dir).permissions(); + permissions(dir2, perms::none); + + std::error_code ec; + TEST_CHECK(fs::create_directory(dir, dir2, ec) == false); + TEST_CHECK(!ec); + + // Check that the permissions were unchanged + TEST_CHECK(orig_p == status(dir).permissions()); + + // Test throwing version + TEST_CHECK(fs::create_directory(dir, dir2) == false); +} + +TEST_CASE(create_directory_one_level) +{ + scoped_test_env env; + // Remove setgid which mkdir would inherit + permissions(env.test_root, perms::set_gid, perm_options::remove); + + const path dir = env.make_env_path("dir1"); + const path attr_dir = env.create_dir("dir2"); + permissions(attr_dir, perms::none); + + std::error_code ec; + TEST_CHECK(fs::create_directory(dir, attr_dir, ec) == true); + TEST_CHECK(!ec); + TEST_CHECK(is_directory(dir)); + + // Check that the new directory has the same permissions as attr_dir + auto st = status(dir); + TEST_CHECK(st.permissions() == perms::none); +} + +TEST_CASE(create_directory_multi_level) +{ + scoped_test_env env; + const path dir = env.make_env_path("dir1/dir2"); + const path dir1 = env.make_env_path("dir1"); + const path attr_dir = env.create_dir("attr_dir"); + std::error_code ec = GetTestEC(); + TEST_CHECK(fs::create_directory(dir, attr_dir, ec) == false); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); + TEST_CHECK(!is_directory(dir)); + TEST_CHECK(!is_directory(dir1)); +} + +TEST_CASE(dest_is_file) +{ + scoped_test_env env; + const path file = env.create_file("file", 42); + const path attr_dir = env.create_dir("attr_dir"); + std::error_code ec = GetTestEC(); + TEST_CHECK(fs::create_directory(file, attr_dir, ec) == false); + TEST_CHECK(!ec); + TEST_CHECK(is_regular_file(file)); +} + +TEST_CASE(attr_dir_is_invalid) { + scoped_test_env env; + const path file = env.create_file("file", 42); + const path dest = env.make_env_path("dir"); + const path dne = env.make_env_path("dne"); + { + std::error_code ec = GetTestEC(); + TEST_CHECK(create_directory(dest, file, ec) == false); + TEST_CHECK(ErrorIs(ec, std::errc::not_a_directory)); + } + TEST_REQUIRE(!exists(dest)); + { + std::error_code ec = GetTestEC(); + TEST_CHECK(create_directory(dest, dne, ec) == false); + TEST_CHECK(ErrorIs(ec, std::errc::not_a_directory)); + } +} + +TEST_CASE(dest_is_symlink) { + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path sym = env.create_symlink("dne_sym", "dne_sym_name"); + { + std::error_code ec = GetTestEC(); + TEST_CHECK(create_directory(sym, dir, ec) == false); + TEST_CHECK(!ec); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directory_symlink/create_directory_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directory_symlink/create_directory_symlink.pass.cpp new file mode 100644 index 00000000000..f1d9ff04190 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_directory_symlink/create_directory_symlink.pass.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// void create_directory_symlink(const path& existing_symlink, const path& new_symlink); +// void create_directory_symlink(const path& existing_symlink, const path& new_symlink, +// error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_create_directory_symlink_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(fs::create_directory_symlink(p, p)); + ASSERT_NOEXCEPT(fs::create_directory_symlink(p, p, ec)); +} + +TEST_CASE(test_error_reporting) +{ + scoped_test_env env; + const path file = env.create_file("file1", 42); + const path file2 = env.create_file("file2", 55); + const path sym = env.create_symlink(file, "sym"); + { // destination exists + std::error_code ec; + fs::create_directory_symlink(sym, file2, ec); + TEST_REQUIRE(ec); + } +} + +TEST_CASE(create_directory_symlink_basic) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path dir_sym = env.create_symlink(dir, "dir_sym"); + + const path dest = env.make_env_path("dest1"); + std::error_code ec; + fs::create_directory_symlink(dir_sym, dest, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(is_symlink(dest)); + TEST_CHECK(equivalent(dest, dir)); +} + + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_hard_link/create_hard_link.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_hard_link/create_hard_link.pass.cpp new file mode 100644 index 00000000000..f1ffc74c257 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_hard_link/create_hard_link.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// void create_hard_link(const path& existing_symlink, const path& new_symlink); +// void create_hard_link(const path& existing_symlink, const path& new_symlink, +// error_code& ec) noexcept; + +#include "filesystem_include.hpp" + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_create_hard_link_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(fs::create_hard_link(p, p)); + ASSERT_NOEXCEPT(fs::create_hard_link(p, p, ec)); +} + +TEST_CASE(test_error_reporting) +{ + scoped_test_env env; + const path file = env.create_file("file1", 42); + const path file2 = env.create_file("file2", 55); + const path sym = env.create_symlink(file, "sym"); + { // destination exists + std::error_code ec; + fs::create_hard_link(sym, file2, ec); + TEST_REQUIRE(ec); + } +} + +TEST_CASE(create_file_hard_link) +{ + scoped_test_env env; + const path file = env.create_file("file"); + const path dest = env.make_env_path("dest1"); + std::error_code ec; + TEST_CHECK(hard_link_count(file) == 1); + fs::create_hard_link(file, dest, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(exists(dest)); + TEST_CHECK(equivalent(dest, file)); + TEST_CHECK(hard_link_count(file) == 2); +} + +TEST_CASE(create_directory_hard_link_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path dest = env.make_env_path("dest2"); + std::error_code ec; + + fs::create_hard_link(dir, dest, ec); + TEST_REQUIRE(ec); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_symlink/create_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_symlink/create_symlink.pass.cpp new file mode 100644 index 00000000000..e69aae186ce --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.create_symlink/create_symlink.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// void create_symlink(const path& existing_symlink, const path& new_symlink); +// void create_symlink(const path& existing_symlink, const path& new_symlink, +// error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_create_symlink_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(fs::create_symlink(p, p)); + ASSERT_NOEXCEPT(fs::create_symlink(p, p, ec)); +} + +TEST_CASE(test_error_reporting) +{ + scoped_test_env env; + const path file = env.create_file("file1", 42); + const path file2 = env.create_file("file2", 55); + const path sym = env.create_symlink(file, "sym"); + { // destination exists + std::error_code ec; + fs::create_symlink(sym, file2, ec); + TEST_REQUIRE(ec); + } +} + +TEST_CASE(create_symlink_basic) +{ + scoped_test_env env; + const path file = env.create_file("file", 42); + const path file_sym = env.create_symlink(file, "file_sym"); + const path dir = env.create_dir("dir"); + const path dir_sym = env.create_symlink(dir, "dir_sym"); + { + const path dest = env.make_env_path("dest1"); + std::error_code ec; + fs::create_symlink(file_sym, dest, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(is_symlink(dest)); + TEST_CHECK(equivalent(dest, file)); + } + { + const path dest = env.make_env_path("dest2"); + std::error_code ec; + fs::create_symlink(dir_sym, dest, ec); + TEST_REQUIRE(!ec); + TEST_CHECK(is_symlink(dest)); + TEST_CHECK(equivalent(dest, dir)); + } +} + + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.current_path/current_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.current_path/current_path.pass.cpp new file mode 100644 index 00000000000..dd3736e089d --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.current_path/current_path.pass.cpp @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// path current_path(); +// path current_path(error_code& ec); +// void current_path(path const&); +// void current_path(path const&, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_current_path_path_test_suite) + +TEST_CASE(current_path_signature_test) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(current_path()); + ASSERT_NOT_NOEXCEPT(current_path(ec)); + ASSERT_NOT_NOEXCEPT(current_path(p)); + ASSERT_NOEXCEPT(current_path(p, ec)); +} + +TEST_CASE(current_path_test) +{ + std::error_code ec; + const path p = current_path(ec); + TEST_REQUIRE(!ec); + TEST_CHECK(p.is_absolute()); + TEST_CHECK(is_directory(p)); + + const path p2 = current_path(); + TEST_CHECK(p2 == p); +} + +TEST_CASE(current_path_after_change_test) +{ + const path new_path = StaticEnv::Dir; + current_path(new_path); + TEST_CHECK(current_path() == new_path); +} + +TEST_CASE(current_path_is_file_test) +{ + const path p = StaticEnv::File; + std::error_code ec; + const path old_p = current_path(); + current_path(p, ec); + TEST_CHECK(ec); + TEST_CHECK(old_p == current_path()); +} + +TEST_CASE(set_to_non_absolute_path) +{ + const path base = StaticEnv::Dir; + current_path(base); + const path p = StaticEnv::Dir2.filename(); + std::error_code ec; + current_path(p, ec); + TEST_CHECK(!ec); + const path new_cwd = current_path(); + TEST_CHECK(new_cwd == StaticEnv::Dir2); + TEST_CHECK(new_cwd.is_absolute()); +} + +TEST_CASE(set_to_empty) +{ + const path p = ""; + std::error_code ec; + const path old_p = current_path(); + current_path(p, ec); + TEST_CHECK(ec); + TEST_CHECK(old_p == current_path()); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.equivalent/equivalent.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.equivalent/equivalent.pass.cpp new file mode 100644 index 00000000000..392f66c6def --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.equivalent/equivalent.pass.cpp @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool equivalent(path const& lhs, path const& rhs); +// bool equivalent(path const& lhs, path const& rhs, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(equivalent_test_suite) + +TEST_CASE(signature_test) { + const path p; + ((void)p); + std::error_code ec; + ((void)ec); + ASSERT_NOEXCEPT(equivalent(p, p, ec)); + ASSERT_NOT_NOEXCEPT(equivalent(p, p)); +} + +TEST_CASE(equivalent_test) { + struct TestCase { + path lhs; + path rhs; + bool expect; + }; + const TestCase testCases[] = { + {StaticEnv::Dir, StaticEnv::Dir, true}, + {StaticEnv::File, StaticEnv::Dir, false}, + {StaticEnv::Dir, StaticEnv::SymlinkToDir, true}, + {StaticEnv::Dir, StaticEnv::SymlinkToFile, false}, + {StaticEnv::File, StaticEnv::File, true}, + {StaticEnv::File, StaticEnv::SymlinkToFile, true}, + }; + for (auto& TC : testCases) { + std::error_code ec; + TEST_CHECK(equivalent(TC.lhs, TC.rhs, ec) == TC.expect); + TEST_CHECK(!ec); + } +} + +TEST_CASE(equivalent_reports_error_if_input_dne) { + const path E = StaticEnv::File; + const path DNE = StaticEnv::DNE; + { // Test that an error is reported when either of the paths don't exist + std::error_code ec = GetTestEC(); + TEST_CHECK(equivalent(E, DNE, ec) == false); + TEST_CHECK(ec); + TEST_CHECK(ec != GetTestEC()); + } + { + std::error_code ec = GetTestEC(); + TEST_CHECK(equivalent(DNE, E, ec) == false); + TEST_CHECK(ec); + TEST_CHECK(ec != GetTestEC()); + } + { + TEST_CHECK_THROW(filesystem_error, equivalent(DNE, E)); + TEST_CHECK_THROW(filesystem_error, equivalent(E, DNE)); + } + { // Test that an exception is thrown if both paths do not exist. + TEST_CHECK_THROW(filesystem_error, equivalent(DNE, DNE)); + } + { + std::error_code ec = GetTestEC(); + TEST_CHECK(equivalent(DNE, DNE, ec) == false); + TEST_CHECK(ec); + TEST_CHECK(ec != GetTestEC()); + } +} + +TEST_CASE(equivalent_hardlink_succeeds) { + scoped_test_env env; + path const file = env.create_file("file", 42); + const path hl1 = env.create_hardlink(file, "hl1"); + const path hl2 = env.create_hardlink(file, "hl2"); + TEST_CHECK(equivalent(file, hl1)); + TEST_CHECK(equivalent(file, hl2)); + TEST_CHECK(equivalent(hl1, hl2)); +} + +TEST_CASE(equivalent_is_other_succeeds) { + scoped_test_env env; + path const file = env.create_file("file", 42); + const path fifo1 = env.create_fifo("fifo1"); + const path fifo2 = env.create_fifo("fifo2"); + // Required to test behavior for inputs where is_other(p) is true. + TEST_REQUIRE(is_other(fifo1)); + TEST_CHECK(!equivalent(file, fifo1)); + TEST_CHECK(!equivalent(fifo2, file)); + TEST_CHECK(!equivalent(fifo1, fifo2)); + TEST_CHECK(equivalent(fifo1, fifo1)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp new file mode 100644 index 00000000000..916052b7483 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool exists(file_status s) noexcept +// bool exists(path const& p); +// bool exists(path const& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(exists_test_suite) + +TEST_CASE(signature_test) +{ + file_status s; ((void)s); + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOEXCEPT(exists(s)); + ASSERT_NOEXCEPT(exists(p, ec)); + ASSERT_NOT_NOEXCEPT(exists(p)); +} + +TEST_CASE(exists_status_test) +{ + struct TestCase { + file_type type; + bool expect; + }; + const TestCase testCases[] = { + {file_type::none, false}, + {file_type::not_found, false}, + {file_type::regular, true}, + {file_type::directory, true}, + {file_type::symlink, true}, + {file_type::block, true}, + {file_type::character, true}, + {file_type::fifo, true}, + {file_type::socket, true}, + {file_type::unknown, true} + }; + for (auto& TC : testCases) { + file_status s(TC.type); + TEST_CHECK(exists(s) == TC.expect); + } +} + +TEST_CASE(test_exist_not_found) +{ + const path p = StaticEnv::DNE; + TEST_CHECK(exists(p) == false); + + std::error_code ec = GetTestEC(); + TEST_CHECK(exists(p, ec) == false); + TEST_CHECK(!ec); +} + +TEST_CASE(test_exists_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + permissions(dir, perms::none); + + std::error_code ec; + TEST_CHECK(exists(file, ec) == false); + TEST_CHECK(ec); + + TEST_CHECK_THROW(filesystem_error, exists(file)); +} + +TEST_CASE(test_name_too_long) { + std::string long_name(2500, 'a'); + const path file(long_name); + + std::error_code ec; + TEST_CHECK(exists(file, ec) == false); + TEST_CHECK(ec); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.file_size/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.file_size/file_size.pass.cpp new file mode 100644 index 00000000000..60e0042cf35 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.file_size/file_size.pass.cpp @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// uintmax_t file_size(const path& p); +// uintmax_t file_size(const path& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(file_size_test_suite) + +TEST_CASE(signature_test) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(file_size(p)), uintmax_t); + ASSERT_SAME_TYPE(decltype(file_size(p, ec)), uintmax_t); + ASSERT_NOT_NOEXCEPT(file_size(p)); + ASSERT_NOEXCEPT(file_size(p, ec)); +} + +TEST_CASE(file_size_empty_test) +{ + const path p = StaticEnv::EmptyFile; + TEST_CHECK(file_size(p) == 0); + std::error_code ec; + TEST_CHECK(file_size(p, ec) == 0); +} + +TEST_CASE(file_size_non_empty) +{ + scoped_test_env env; + const path p = env.create_file("file", 42); + TEST_CHECK(file_size(p) == 42); + std::error_code ec; + TEST_CHECK(file_size(p, ec) == 42); +} + +TEST_CASE(symlink_test_case) +{ + const path p = StaticEnv::File; + const path p2 = StaticEnv::SymlinkToFile; + TEST_CHECK(file_size(p) == file_size(p2)); +} + +TEST_CASE(file_size_error_cases) +{ + struct { + path p; + std::errc expected_err; + } TestCases[] = { + {StaticEnv::Dir, std::errc::is_a_directory}, + {StaticEnv::SymlinkToDir, std::errc::is_a_directory}, + {StaticEnv::BadSymlink, std::errc::no_such_file_or_directory}, + {StaticEnv::DNE, std::errc::no_such_file_or_directory}, + {"", std::errc::no_such_file_or_directory}}; + const uintmax_t expect = static_cast<uintmax_t>(-1); + for (auto& TC : TestCases) { + std::error_code ec = GetTestEC(); + TEST_CHECK(file_size(TC.p, ec) == expect); + TEST_CHECK(ErrorIs(ec, TC.expected_err)); + + ExceptionChecker Checker(TC.p, TC.expected_err, "file_size"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, file_size(TC.p)); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.hard_lk_ct/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.hard_lk_ct/hard_link_count.pass.cpp new file mode 100644 index 00000000000..2e9704a3dbe --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.hard_lk_ct/hard_link_count.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// uintmax_t hard_link_count(const path& p); +// uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(hard_link_count_test_suite) + +TEST_CASE(signature_test) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(hard_link_count(p)), uintmax_t); + ASSERT_SAME_TYPE(decltype(hard_link_count(p, ec)), uintmax_t); + ASSERT_NOT_NOEXCEPT(hard_link_count(p)); + ASSERT_NOEXCEPT(hard_link_count(p, ec)); +} + +TEST_CASE(hard_link_count_for_file) +{ + TEST_CHECK(hard_link_count(StaticEnv::File) == 1); + std::error_code ec; + TEST_CHECK(hard_link_count(StaticEnv::File, ec) == 1); +} + +TEST_CASE(hard_link_count_for_directory) +{ + uintmax_t DirExpect = 3; // hard link from . .. and Dir2 + uintmax_t Dir3Expect = 2; // hard link from . .. + uintmax_t DirExpectAlt = DirExpect; + uintmax_t Dir3ExpectAlt = Dir3Expect; +#if defined(__APPLE__) + // Filesystems formatted with case sensitive hfs+ behave unixish as + // expected. Normal hfs+ filesystems report the number of directory + // entries instead. + DirExpectAlt = 5; // . .. Dir2 file1 file2 + Dir3Expect = 3; // . .. file5 +#endif + TEST_CHECK(hard_link_count(StaticEnv::Dir) == DirExpect || + hard_link_count(StaticEnv::Dir) == DirExpectAlt || + hard_link_count(StaticEnv::Dir) == 1); + TEST_CHECK(hard_link_count(StaticEnv::Dir3) == Dir3Expect || + hard_link_count(StaticEnv::Dir3) == Dir3ExpectAlt || + hard_link_count(StaticEnv::Dir3) == 1); + + std::error_code ec; + TEST_CHECK(hard_link_count(StaticEnv::Dir, ec) == DirExpect || + hard_link_count(StaticEnv::Dir, ec) == DirExpectAlt || + hard_link_count(StaticEnv::Dir) == 1); + TEST_CHECK(hard_link_count(StaticEnv::Dir3, ec) == Dir3Expect || + hard_link_count(StaticEnv::Dir3, ec) == Dir3ExpectAlt || + hard_link_count(StaticEnv::Dir3) == 1); +} +TEST_CASE(hard_link_count_increments_test) +{ + scoped_test_env env; + const path file = env.create_file("file", 42); + TEST_CHECK(hard_link_count(file) == 1); + + env.create_hardlink(file, "file_hl"); + TEST_CHECK(hard_link_count(file) == 2); +} + + +TEST_CASE(hard_link_count_error_cases) +{ + const path testCases[] = { + StaticEnv::BadSymlink, + StaticEnv::DNE + }; + const uintmax_t expect = static_cast<uintmax_t>(-1); + for (auto& TC : testCases) { + std::error_code ec; + TEST_CHECK(hard_link_count(TC, ec) == expect); + TEST_CHECK(ec); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp new file mode 100644 index 00000000000..36f1d6bbd91 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool is_block_file(file_status s) noexcept +// bool is_block_file(path const& p); +// bool is_block_file(path const& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(is_block_file_test_suite) + +TEST_CASE(signature_test) +{ + file_status s; ((void)s); + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOEXCEPT(is_block_file(s)); + ASSERT_NOEXCEPT(is_block_file(p, ec)); + ASSERT_NOT_NOEXCEPT(is_block_file(p)); +} + +TEST_CASE(is_block_file_status_test) +{ + struct TestCase { + file_type type; + bool expect; + }; + const TestCase testCases[] = { + {file_type::none, false}, + {file_type::not_found, false}, + {file_type::regular, false}, + {file_type::directory, false}, + {file_type::symlink, false}, + {file_type::block, true}, + {file_type::character, false}, + {file_type::fifo, false}, + {file_type::socket, false}, + {file_type::unknown, false} + }; + for (auto& TC : testCases) { + file_status s(TC.type); + TEST_CHECK(is_block_file(s) == TC.expect); + } +} + +TEST_CASE(test_exist_not_found) +{ + const path p = StaticEnv::DNE; + TEST_CHECK(is_block_file(p) == false); +} + +TEST_CASE(test_is_block_file_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + permissions(dir, perms::none); + + std::error_code ec; + TEST_CHECK(is_block_file(file, ec) == false); + TEST_CHECK(ec); + + TEST_CHECK_THROW(filesystem_error, is_block_file(file)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp new file mode 100644 index 00000000000..b0c3fe542f3 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool is_character_file(file_status s) noexcept +// bool is_character_file(path const& p); +// bool is_character_file(path const& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(is_character_file_test_suite) + +TEST_CASE(signature_test) +{ + file_status s; ((void)s); + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOEXCEPT(is_character_file(s)); + ASSERT_NOEXCEPT(is_character_file(p, ec)); + ASSERT_NOT_NOEXCEPT(is_character_file(p)); +} + +TEST_CASE(is_character_file_status_test) +{ + struct TestCase { + file_type type; + bool expect; + }; + const TestCase testCases[] = { + {file_type::none, false}, + {file_type::not_found, false}, + {file_type::regular, false}, + {file_type::directory, false}, + {file_type::symlink, false}, + {file_type::block, false}, + {file_type::character, true}, + {file_type::fifo, false}, + {file_type::socket, false}, + {file_type::unknown, false} + }; + for (auto& TC : testCases) { + file_status s(TC.type); + TEST_CHECK(is_character_file(s) == TC.expect); + } +} + +TEST_CASE(test_exist_not_found) +{ + const path p = StaticEnv::DNE; + TEST_CHECK(is_character_file(p) == false); +} + +TEST_CASE(test_is_character_file_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + permissions(dir, perms::none); + + std::error_code ec; + TEST_CHECK(is_character_file(file, ec) == false); + TEST_CHECK(ec); + + TEST_CHECK_THROW(filesystem_error, is_character_file(file)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp new file mode 100644 index 00000000000..bb578b9cfa0 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool is_directory(file_status s) noexcept +// bool is_directory(path const& p); +// bool is_directory(path const& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(is_directory_test_suite) + +TEST_CASE(signature_test) +{ + file_status s; ((void)s); + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOEXCEPT(is_directory(s)); + ASSERT_NOEXCEPT(is_directory(p, ec)); + ASSERT_NOT_NOEXCEPT(is_directory(p)); +} + +TEST_CASE(is_directory_status_test) +{ + struct TestCase { + file_type type; + bool expect; + }; + const TestCase testCases[] = { + {file_type::none, false}, + {file_type::not_found, false}, + {file_type::regular, false}, + {file_type::directory, true}, + {file_type::symlink, false}, + {file_type::block, false}, + {file_type::character, false}, + {file_type::fifo, false}, + {file_type::socket, false}, + {file_type::unknown, false} + }; + for (auto& TC : testCases) { + file_status s(TC.type); + TEST_CHECK(is_directory(s) == TC.expect); + } +} + +TEST_CASE(test_exist_not_found) +{ + const path p = StaticEnv::DNE; + TEST_CHECK(is_directory(p) == false); +} + +TEST_CASE(static_env_test) +{ + TEST_CHECK(is_directory(StaticEnv::Dir)); + TEST_CHECK(is_directory(StaticEnv::SymlinkToDir)); + TEST_CHECK(!is_directory(StaticEnv::File)); +} + +TEST_CASE(test_is_directory_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path dir2 = env.create_dir("dir/dir2"); + permissions(dir, perms::none); + + std::error_code ec; + TEST_CHECK(is_directory(dir2, ec) == false); + TEST_CHECK(ec); + + TEST_CHECK_THROW(filesystem_error, is_directory(dir2)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp new file mode 100644 index 00000000000..cd4dd174852 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool is_empty(path const& p); +// bool is_empty(path const& p, std::error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(is_empty_test_suite) + +TEST_CASE(signature_test) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(is_empty(p, ec)); + ASSERT_NOT_NOEXCEPT(is_empty(p)); +} + +TEST_CASE(test_exist_not_found) +{ + const path p = StaticEnv::DNE; + std::error_code ec; + TEST_CHECK(is_empty(p, ec) == false); + TEST_CHECK(ec); + TEST_CHECK_THROW(filesystem_error, is_empty(p)); +} + +TEST_CASE(test_is_empty_directory) +{ + TEST_CHECK(!is_empty(StaticEnv::Dir)); + TEST_CHECK(!is_empty(StaticEnv::SymlinkToDir)); +} + +TEST_CASE(test_is_empty_directory_dynamic) +{ + scoped_test_env env; + TEST_CHECK(is_empty(env.test_root)); + env.create_file("foo", 42); + TEST_CHECK(!is_empty(env.test_root)); +} + +TEST_CASE(test_is_empty_file) +{ + TEST_CHECK(is_empty(StaticEnv::EmptyFile)); + TEST_CHECK(!is_empty(StaticEnv::NonEmptyFile)); +} + +TEST_CASE(test_is_empty_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path dir2 = env.create_dir("dir/dir2"); + permissions(dir, perms::none); + + std::error_code ec; + TEST_CHECK(is_empty(dir2, ec) == false); + TEST_CHECK(ec); + + TEST_CHECK_THROW(filesystem_error, is_empty(dir2)); +} + +TEST_CASE(test_directory_access_denied) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file1 = env.create_file("dir/file", 42); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + TEST_CHECK(is_empty(dir, ec) == false); + TEST_CHECK(ec); + TEST_CHECK(ec != GetTestEC()); + + TEST_CHECK_THROW(filesystem_error, is_empty(dir)); +} + + +TEST_CASE(test_fifo_fails) +{ + scoped_test_env env; + const path fifo = env.create_fifo("fifo"); + + std::error_code ec = GetTestEC(); + TEST_CHECK(is_empty(fifo, ec) == false); + TEST_CHECK(ec); + TEST_CHECK(ec != GetTestEC()); + + TEST_CHECK_THROW(filesystem_error, is_empty(fifo)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp new file mode 100644 index 00000000000..3c86dc0e7c2 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool is_fifo(file_status s) noexcept +// bool is_fifo(path const& p); +// bool is_fifo(path const& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(is_fifo_test_suite) + +TEST_CASE(signature_test) +{ + file_status s; ((void)s); + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOEXCEPT(is_fifo(s)); + ASSERT_NOEXCEPT(is_fifo(p, ec)); + ASSERT_NOT_NOEXCEPT(is_fifo(p)); +} + +TEST_CASE(is_fifo_status_test) +{ + struct TestCase { + file_type type; + bool expect; + }; + const TestCase testCases[] = { + {file_type::none, false}, + {file_type::not_found, false}, + {file_type::regular, false}, + {file_type::directory, false}, + {file_type::symlink, false}, + {file_type::block, false}, + {file_type::character, false}, + {file_type::fifo, true}, + {file_type::socket, false}, + {file_type::unknown, false} + }; + for (auto& TC : testCases) { + file_status s(TC.type); + TEST_CHECK(is_fifo(s) == TC.expect); + } +} + +TEST_CASE(test_exist_not_found) +{ + const path p = StaticEnv::DNE; + TEST_CHECK(is_fifo(p) == false); +} + +TEST_CASE(test_is_fifo_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + permissions(dir, perms::none); + + std::error_code ec; + TEST_CHECK(is_fifo(file, ec) == false); + TEST_CHECK(ec); + + TEST_CHECK_THROW(filesystem_error, is_fifo(file)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp new file mode 100644 index 00000000000..a2134c7c1db --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool is_other(file_status s) noexcept +// bool is_other(path const& p); +// bool is_other(path const& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(is_other_test_suite) + +TEST_CASE(signature_test) +{ + file_status s; ((void)s); + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOEXCEPT(is_other(s)); + ASSERT_NOEXCEPT(is_other(p, ec)); + ASSERT_NOT_NOEXCEPT(is_other(p)); +} + +TEST_CASE(is_other_status_test) +{ + struct TestCase { + file_type type; + bool expect; + }; + const TestCase testCases[] = { + {file_type::none, false}, + {file_type::not_found, false}, + {file_type::regular, false}, + {file_type::directory, false}, + {file_type::symlink, false}, + {file_type::block, true}, + {file_type::character, true}, + {file_type::fifo, true}, + {file_type::socket, true}, + {file_type::unknown, true} + }; + for (auto& TC : testCases) { + file_status s(TC.type); + TEST_CHECK(is_other(s) == TC.expect); + } +} + +TEST_CASE(test_exist_not_found) +{ + const path p = StaticEnv::DNE; + TEST_CHECK(is_other(p) == false); +} + +TEST_CASE(test_is_other_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + permissions(dir, perms::none); + + std::error_code ec; + TEST_CHECK(is_other(file, ec) == false); + TEST_CHECK(ec); + + TEST_CHECK_THROW(filesystem_error, is_other(file)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp new file mode 100644 index 00000000000..0c34886355b --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool is_regular_file(file_status s) noexcept +// bool is_regular_file(path const& p); +// bool is_regular_file(path const& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(is_regular_file_test_suite) + +TEST_CASE(signature_test) +{ + file_status s; ((void)s); + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOEXCEPT(is_regular_file(s)); + ASSERT_NOEXCEPT(is_regular_file(p, ec)); + ASSERT_NOT_NOEXCEPT(is_regular_file(p)); +} + +TEST_CASE(is_regular_file_status_test) +{ + struct TestCase { + file_type type; + bool expect; + }; + const TestCase testCases[] = { + {file_type::none, false}, + {file_type::not_found, false}, + {file_type::regular, true}, + {file_type::directory, false}, + {file_type::symlink, false}, + {file_type::block, false}, + {file_type::character, false}, + {file_type::fifo, false}, + {file_type::socket, false}, + {file_type::unknown, false} + }; + for (auto& TC : testCases) { + file_status s(TC.type); + TEST_CHECK(is_regular_file(s) == TC.expect); + } +} + +TEST_CASE(test_exist_not_found) +{ + const path p = StaticEnv::DNE; + TEST_CHECK(is_regular_file(p) == false); + std::error_code ec; + TEST_CHECK(is_regular_file(p, ec) == false); + TEST_CHECK(ec); +} + +TEST_CASE(test_is_regular_file_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + permissions(dir, perms::none); + + std::error_code ec; + TEST_CHECK(is_regular_file(file, ec) == false); + TEST_CHECK(ec); + + TEST_CHECK_THROW(filesystem_error, is_regular_file(file)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp new file mode 100644 index 00000000000..7273c6c2e5c --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool is_socket(file_status s) noexcept +// bool is_socket(path const& p); +// bool is_socket(path const& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(is_socket_test_suite) + +TEST_CASE(signature_test) +{ + file_status s; ((void)s); + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOEXCEPT(is_socket(s)); + ASSERT_NOEXCEPT(is_socket(p, ec)); + ASSERT_NOT_NOEXCEPT(is_socket(p)); +} + +TEST_CASE(is_socket_status_test) +{ + struct TestCase { + file_type type; + bool expect; + }; + const TestCase testCases[] = { + {file_type::none, false}, + {file_type::not_found, false}, + {file_type::regular, false}, + {file_type::directory, false}, + {file_type::symlink, false}, + {file_type::block, false}, + {file_type::character, false}, + {file_type::fifo, false}, + {file_type::socket, true}, + {file_type::unknown, false} + }; + for (auto& TC : testCases) { + file_status s(TC.type); + TEST_CHECK(is_socket(s) == TC.expect); + } +} + +TEST_CASE(test_exist_not_found) +{ + const path p = StaticEnv::DNE; + TEST_CHECK(is_socket(p) == false); +} + +TEST_CASE(test_is_socket_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + permissions(dir, perms::none); + + std::error_code ec; + TEST_CHECK(is_socket(file, ec) == false); + TEST_CHECK(ec); + + TEST_CHECK_THROW(filesystem_error, is_socket(file)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp new file mode 100644 index 00000000000..e1ac98a80d0 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool is_symlink(file_status s) noexcept +// bool is_symlink(path const& p); +// bool is_symlink(path const& p, std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(is_symlink_test_suite) + +TEST_CASE(signature_test) +{ + file_status s; ((void)s); + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOEXCEPT(is_symlink(s)); + ASSERT_NOEXCEPT(is_symlink(p, ec)); + ASSERT_NOT_NOEXCEPT(is_symlink(p)); +} + +TEST_CASE(is_symlink_status_test) +{ + struct TestCase { + file_type type; + bool expect; + }; + const TestCase testCases[] = { + {file_type::none, false}, + {file_type::not_found, false}, + {file_type::regular, false}, + {file_type::directory, false}, + {file_type::symlink, true}, + {file_type::block, false}, + {file_type::character, false}, + {file_type::fifo, false}, + {file_type::socket, false}, + {file_type::unknown, false} + }; + for (auto& TC : testCases) { + file_status s(TC.type); + TEST_CHECK(is_symlink(s) == TC.expect); + } +} + +TEST_CASE(static_env_test) +{ + struct TestCase { + path p; + bool expect; + }; + const TestCase testCases[] = { + {StaticEnv::File, false}, + {StaticEnv::Dir, false}, + {StaticEnv::SymlinkToFile, true}, + {StaticEnv::SymlinkToDir, true}, + {StaticEnv::BadSymlink, true} + }; + for (auto& TC : testCases) { + TEST_CHECK(is_symlink(TC.p) == TC.expect); + } +} + +TEST_CASE(test_exist_not_found) +{ + const path p = StaticEnv::DNE; + TEST_CHECK(is_symlink(p) == false); + std::error_code ec; + TEST_CHECK(is_symlink(p, ec) == false); + TEST_CHECK(ec); +} + +TEST_CASE(test_is_symlink_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + permissions(dir, perms::none); + + std::error_code ec; + TEST_CHECK(is_symlink(file, ec) == false); + TEST_CHECK(ec); + + TEST_CHECK_THROW(filesystem_error, is_symlink(file)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp new file mode 100644 index 00000000000..bc77f3f6351 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp @@ -0,0 +1,594 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// file_time_type last_write_time(const path& p); +// file_time_type last_write_time(const path& p, std::error_code& ec) noexcept; +// void last_write_time(const path& p, file_time_type new_time); +// void last_write_time(const path& p, file_time_type new_type, +// std::error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <chrono> +#include <fstream> +#include <cstdlib> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +#include <sys/stat.h> +#include <iostream> + +#include <fcntl.h> +#include <sys/time.h> + +using namespace fs; + +using TimeSpec = struct ::timespec; +using StatT = struct ::stat; + +using Sec = std::chrono::duration<file_time_type::rep>; +using Hours = std::chrono::hours; +using Minutes = std::chrono::minutes; +using MicroSec = std::chrono::duration<file_time_type::rep, std::micro>; +using NanoSec = std::chrono::duration<file_time_type::rep, std::nano>; +using std::chrono::duration_cast; + +#if defined(__APPLE__) +TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; } +TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; } +#else +TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; } +TimeSpec extract_atime(StatT const& st) { return st.st_atim; } +#endif + +bool ConvertToTimeSpec(TimeSpec& ts, file_time_type ft) { + using SecFieldT = decltype(TimeSpec::tv_sec); + using NSecFieldT = decltype(TimeSpec::tv_nsec); + using SecLim = std::numeric_limits<SecFieldT>; + using NSecLim = std::numeric_limits<NSecFieldT>; + + auto secs = duration_cast<Sec>(ft.time_since_epoch()); + auto nsecs = duration_cast<NanoSec>(ft.time_since_epoch() - secs); + if (nsecs.count() < 0) { + if (Sec::min().count() > SecLim::min()) { + secs += Sec(1); + nsecs -= Sec(1); + } else { + nsecs = NanoSec(0); + } + } + if (SecLim::max() < secs.count() || SecLim::min() > secs.count()) + return false; + if (NSecLim::max() < nsecs.count() || NSecLim::min() > nsecs.count()) + return false; + ts.tv_sec = secs.count(); + ts.tv_nsec = nsecs.count(); + return true; +} + +bool ConvertFromTimeSpec(file_time_type& ft, TimeSpec ts) { + auto secs_part = duration_cast<file_time_type::duration>(Sec(ts.tv_sec)); + if (duration_cast<Sec>(secs_part).count() != ts.tv_sec) + return false; + auto subsecs = duration_cast<file_time_type::duration>(NanoSec(ts.tv_nsec)); + auto dur = secs_part + subsecs; + if (dur < secs_part && subsecs.count() >= 0) + return false; + ft = file_time_type(dur); + return true; +} + +bool CompareTimeExact(TimeSpec ts, TimeSpec ts2) { + return ts2.tv_sec == ts.tv_sec && ts2.tv_nsec == ts.tv_nsec; +} +bool CompareTimeExact(file_time_type ft, TimeSpec ts) { + TimeSpec ts2 = {}; + if (!ConvertToTimeSpec(ts2, ft)) + return false; + return CompareTimeExact(ts, ts2); +} +bool CompareTimeExact(TimeSpec ts, file_time_type ft) { + return CompareTimeExact(ft, ts); +} + +struct Times { + TimeSpec access, write; +}; + +Times GetTimes(path const& p) { + using Clock = file_time_type::clock; + StatT st; + if (::stat(p.c_str(), &st) == -1) { + std::error_code ec(errno, std::generic_category()); +#ifndef TEST_HAS_NO_EXCEPTIONS + throw ec; +#else + std::cerr << ec.message() << std::endl; + std::exit(EXIT_FAILURE); +#endif + } + return {extract_atime(st), extract_mtime(st)}; +} + +TimeSpec LastAccessTime(path const& p) { return GetTimes(p).access; } + +TimeSpec LastWriteTime(path const& p) { return GetTimes(p).write; } + +std::pair<TimeSpec, TimeSpec> GetSymlinkTimes(path const& p) { + using Clock = file_time_type::clock; + StatT st; + if (::lstat(p.c_str(), &st) == -1) { + std::error_code ec(errno, std::generic_category()); +#ifndef TEST_HAS_NO_EXCEPTIONS + throw ec; +#else + std::cerr << ec.message() << std::endl; + std::exit(EXIT_FAILURE); +#endif + } + return {extract_atime(st), extract_mtime(st)}; +} + +namespace { + +// In some configurations, the comparison is tautological and the test is valid. +// We disable the warning so that we can actually test it regardless. Also, that +// diagnostic is pretty new, so also don't fail if old clang does not support it +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wtautological-constant-compare" +#endif + +static const bool SupportsNegativeTimes = [] { + using namespace std::chrono; + std::error_code ec; + TimeSpec old_write_time, new_write_time; + { // WARNING: Do not assert in this scope. + scoped_test_env env; + const path file = env.create_file("file", 42); + old_write_time = LastWriteTime(file); + file_time_type tp(seconds(-5)); + fs::last_write_time(file, tp, ec); + new_write_time = LastWriteTime(file); + } + + return !ec && new_write_time.tv_sec < 0; +}(); + +static const bool SupportsMaxTime = [] { + using namespace std::chrono; + TimeSpec max_ts = {}; + if (!ConvertToTimeSpec(max_ts, file_time_type::max())) + return false; + + std::error_code ec; + TimeSpec old_write_time, new_write_time; + { // WARNING: Do not assert in this scope. + scoped_test_env env; + const path file = env.create_file("file", 42); + old_write_time = LastWriteTime(file); + file_time_type tp = file_time_type::max(); + fs::last_write_time(file, tp, ec); + new_write_time = LastWriteTime(file); + } + return !ec && new_write_time.tv_sec > max_ts.tv_sec - 1; +}(); + +static const bool SupportsMinTime = [] { + using namespace std::chrono; + TimeSpec min_ts = {}; + if (!ConvertToTimeSpec(min_ts, file_time_type::min())) + return false; + std::error_code ec; + TimeSpec old_write_time, new_write_time; + { // WARNING: Do not assert in this scope. + scoped_test_env env; + const path file = env.create_file("file", 42); + old_write_time = LastWriteTime(file); + file_time_type tp = file_time_type::min(); + fs::last_write_time(file, tp, ec); + new_write_time = LastWriteTime(file); + } + return !ec && new_write_time.tv_sec < min_ts.tv_sec + 1; +}(); + +static const bool SupportsNanosecondRoundTrip = [] { + NanoSec ns(3); + static_assert(std::is_same<file_time_type::period, std::nano>::value, ""); + + // Test that the system call we use to set the times also supports nanosecond + // resolution. (utimes does not) + file_time_type ft(ns); + { + scoped_test_env env; + const path p = env.create_file("file", 42); + last_write_time(p, ft); + return last_write_time(p) == ft; + } +}(); + +// The HFS+ filesystem (used by default before macOS 10.13) stores timestamps at +// a 1-second granularity, and APFS (now the default) at a 1 nanosecond granularity. +// 1-second granularity is also the norm on many of the supported filesystems +// on Linux as well. +static const bool WorkaroundStatTruncatesToSeconds = [] { + MicroSec micros(3); + static_assert(std::is_same<file_time_type::period, std::nano>::value, ""); + + file_time_type ft(micros); + { + scoped_test_env env; + const path p = env.create_file("file", 42); + if (LastWriteTime(p).tv_nsec != 0) + return false; + last_write_time(p, ft); + return last_write_time(p) != ft && LastWriteTime(p).tv_nsec == 0; + } +}(); + +static const bool SupportsMinRoundTrip = [] { + TimeSpec ts = {}; + if (!ConvertToTimeSpec(ts, file_time_type::min())) + return false; + file_time_type min_val = {}; + if (!ConvertFromTimeSpec(min_val, ts)) + return false; + return min_val == file_time_type::min(); +}(); + +} // end namespace + +static bool CompareTime(TimeSpec t1, TimeSpec t2) { + if (SupportsNanosecondRoundTrip) + return CompareTimeExact(t1, t2); + if (t1.tv_sec != t2.tv_sec) + return false; + + auto diff = std::abs(t1.tv_nsec - t2.tv_nsec); + if (WorkaroundStatTruncatesToSeconds) + return diff < duration_cast<NanoSec>(Sec(1)).count(); + return diff < duration_cast<NanoSec>(MicroSec(1)).count(); +} + +static bool CompareTime(file_time_type t1, TimeSpec t2) { + TimeSpec ts1 = {}; + if (!ConvertToTimeSpec(ts1, t1)) + return false; + return CompareTime(ts1, t2); +} + +static bool CompareTime(TimeSpec t1, file_time_type t2) { + return CompareTime(t2, t1); +} + +static bool CompareTime(file_time_type t1, file_time_type t2) { + auto min_secs = duration_cast<Sec>(file_time_type::min().time_since_epoch()); + bool IsMin = + t1.time_since_epoch() < min_secs || t2.time_since_epoch() < min_secs; + + if (SupportsNanosecondRoundTrip && (!IsMin || SupportsMinRoundTrip)) + return t1 == t2; + if (IsMin) { + return duration_cast<Sec>(t1.time_since_epoch()) == + duration_cast<Sec>(t2.time_since_epoch()); + } + file_time_type::duration dur; + if (t1 > t2) + dur = t1 - t2; + else + dur = t2 - t1; + if (WorkaroundStatTruncatesToSeconds) + return duration_cast<Sec>(dur).count() == 0; + return duration_cast<MicroSec>(dur).count() == 0; +} + +// Check if a time point is representable on a given filesystem. Check that: +// (A) 'tp' is representable as a time_t +// (B) 'tp' is non-negative or the filesystem supports negative times. +// (C) 'tp' is not 'file_time_type::max()' or the filesystem supports the max +// value. +// (D) 'tp' is not 'file_time_type::min()' or the filesystem supports the min +// value. +inline bool TimeIsRepresentableByFilesystem(file_time_type tp) { + TimeSpec ts = {}; + if (!ConvertToTimeSpec(ts, tp)) + return false; + else if (tp.time_since_epoch().count() < 0 && !SupportsNegativeTimes) + return false; + else if (tp == file_time_type::max() && !SupportsMaxTime) + return false; + else if (tp == file_time_type::min() && !SupportsMinTime) + return false; + return true; +} + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +// Create a sub-second duration using the smallest period the filesystem supports. +file_time_type::duration SubSec(long long val) { + using SubSecT = file_time_type::duration; + if (SupportsNanosecondRoundTrip) { + return duration_cast<SubSecT>(NanoSec(val)); + } else { + return duration_cast<SubSecT>(MicroSec(val)); + } +} + +TEST_SUITE(last_write_time_test_suite) + +TEST_CASE(signature_test) +{ + const file_time_type t; + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(last_write_time(p)), file_time_type); + ASSERT_SAME_TYPE(decltype(last_write_time(p, ec)), file_time_type); + ASSERT_SAME_TYPE(decltype(last_write_time(p, t)), void); + ASSERT_SAME_TYPE(decltype(last_write_time(p, t, ec)), void); + ASSERT_NOT_NOEXCEPT(last_write_time(p)); + ASSERT_NOT_NOEXCEPT(last_write_time(p, t)); + ASSERT_NOEXCEPT(last_write_time(p, ec)); + ASSERT_NOEXCEPT(last_write_time(p, t, ec)); +} + +TEST_CASE(read_last_write_time_static_env_test) +{ + using C = file_time_type::clock; + file_time_type min = file_time_type::min(); + { + file_time_type ret = last_write_time(StaticEnv::File); + TEST_CHECK(ret != min); + TEST_CHECK(ret < C::now()); + TEST_CHECK(CompareTime(ret, LastWriteTime(StaticEnv::File))); + + file_time_type ret2 = last_write_time(StaticEnv::SymlinkToFile); + TEST_CHECK(CompareTime(ret, ret2)); + TEST_CHECK(CompareTime(ret2, LastWriteTime(StaticEnv::SymlinkToFile))); + } + { + file_time_type ret = last_write_time(StaticEnv::Dir); + TEST_CHECK(ret != min); + TEST_CHECK(ret < C::now()); + TEST_CHECK(CompareTime(ret, LastWriteTime(StaticEnv::Dir))); + + file_time_type ret2 = last_write_time(StaticEnv::SymlinkToDir); + TEST_CHECK(CompareTime(ret, ret2)); + TEST_CHECK(CompareTime(ret2, LastWriteTime(StaticEnv::SymlinkToDir))); + } +} + +TEST_CASE(get_last_write_time_dynamic_env_test) +{ + using Clock = file_time_type::clock; + using Sec = std::chrono::seconds; + scoped_test_env env; + + const path file = env.create_file("file", 42); + const path dir = env.create_dir("dir"); + + const auto file_times = GetTimes(file); + const TimeSpec file_write_time = file_times.write; + const auto dir_times = GetTimes(dir); + const TimeSpec dir_write_time = dir_times.write; + + file_time_type ftime = last_write_time(file); + TEST_CHECK(Clock::to_time_t(ftime) == file_write_time.tv_sec); + TEST_CHECK(CompareTime(ftime, file_write_time)); + + file_time_type dtime = last_write_time(dir); + TEST_CHECK(Clock::to_time_t(dtime) == dir_write_time.tv_sec); + TEST_CHECK(CompareTime(dtime, dir_write_time)); + + SleepFor(Sec(2)); + + // update file and add a file to the directory. Make sure the times increase. + std::ofstream of(file, std::ofstream::app); + of << "hello"; + of.close(); + env.create_file("dir/file1", 1); + + file_time_type ftime2 = last_write_time(file); + file_time_type dtime2 = last_write_time(dir); + + TEST_CHECK(ftime2 > ftime); + TEST_CHECK(dtime2 > dtime); + TEST_CHECK(CompareTime(LastWriteTime(file), ftime2)); + TEST_CHECK(CompareTime(LastWriteTime(dir), dtime2)); +} + + +TEST_CASE(set_last_write_time_dynamic_env_test) +{ + using Clock = file_time_type::clock; + scoped_test_env env; + + const path file = env.create_file("file", 42); + const path dir = env.create_dir("dir"); + const auto now = Clock::now(); + const file_time_type epoch_time = now - now.time_since_epoch(); + + const file_time_type future_time = now + Hours(3) + Sec(42) + SubSec(17); + const file_time_type past_time = now - Minutes(3) - Sec(42) - SubSec(17); + const file_time_type before_epoch_time = + epoch_time - Minutes(3) - Sec(42) - SubSec(17); + // FreeBSD has a bug in their utimes implementation where the time is not update + // when the number of seconds is '-1'. +#if defined(__FreeBSD__) + const file_time_type just_before_epoch_time = + epoch_time - Sec(2) - SubSec(17); +#else + const file_time_type just_before_epoch_time = epoch_time - SubSec(17); +#endif + + struct TestCase { + const char * case_name; + path p; + file_time_type new_time; + } cases[] = { + {"file, epoch_time", file, epoch_time}, + {"dir, epoch_time", dir, epoch_time}, + {"file, future_time", file, future_time}, + {"dir, future_time", dir, future_time}, + {"file, past_time", file, past_time}, + {"dir, past_time", dir, past_time}, + {"file, before_epoch_time", file, before_epoch_time}, + {"dir, before_epoch_time", dir, before_epoch_time}, + {"file, just_before_epoch_time", file, just_before_epoch_time}, + {"dir, just_before_epoch_time", dir, just_before_epoch_time} + }; + for (const auto& TC : cases) { + std::cerr << "Test Case = " << TC.case_name << "\n"; + const auto old_times = GetTimes(TC.p); + file_time_type old_time; + TEST_REQUIRE(ConvertFromTimeSpec(old_time, old_times.write)); + + std::error_code ec = GetTestEC(); + last_write_time(TC.p, TC.new_time, ec); + TEST_CHECK(!ec); + + ec = GetTestEC(); + file_time_type got_time = last_write_time(TC.p, ec); + TEST_REQUIRE(!ec); + + if (TimeIsRepresentableByFilesystem(TC.new_time)) { + TEST_CHECK(got_time != old_time); + TEST_CHECK(CompareTime(got_time, TC.new_time)); + TEST_CHECK(CompareTime(LastAccessTime(TC.p), old_times.access)); + } + } +} + +TEST_CASE(last_write_time_symlink_test) +{ + using Clock = file_time_type::clock; + + scoped_test_env env; + + const path file = env.create_file("file", 42); + const path sym = env.create_symlink("file", "sym"); + + const file_time_type new_time = Clock::now() + Hours(3); + + const auto old_times = GetTimes(sym); + const auto old_sym_times = GetSymlinkTimes(sym); + + std::error_code ec = GetTestEC(); + last_write_time(sym, new_time, ec); + TEST_CHECK(!ec); + + file_time_type got_time = last_write_time(sym); + TEST_CHECK(!CompareTime(got_time, old_times.write)); + if (!WorkaroundStatTruncatesToSeconds) { + TEST_CHECK(got_time == new_time); + } else { + TEST_CHECK(CompareTime(got_time, new_time)); + } + + TEST_CHECK(CompareTime(LastWriteTime(file), new_time)); + TEST_CHECK(CompareTime(LastAccessTime(sym), old_times.access)); + std::pair<TimeSpec, TimeSpec> sym_times = GetSymlinkTimes(sym); + TEST_CHECK(CompareTime(sym_times.first, old_sym_times.first)); + TEST_CHECK(CompareTime(sym_times.second, old_sym_times.second)); +} + + +TEST_CASE(test_write_min_time) +{ + using Clock = file_time_type::clock; + scoped_test_env env; + const path p = env.create_file("file", 42); + const file_time_type old_time = last_write_time(p); + file_time_type new_time = file_time_type::min(); + + std::error_code ec = GetTestEC(); + last_write_time(p, new_time, ec); + file_time_type tt = last_write_time(p); + + if (TimeIsRepresentableByFilesystem(new_time)) { + TEST_CHECK(!ec); + TEST_CHECK(CompareTime(tt, new_time)); + + last_write_time(p, old_time); + new_time = file_time_type::min() + SubSec(1); + + ec = GetTestEC(); + last_write_time(p, new_time, ec); + tt = last_write_time(p); + + if (TimeIsRepresentableByFilesystem(new_time)) { + TEST_CHECK(!ec); + TEST_CHECK(CompareTime(tt, new_time)); + } else { + TEST_CHECK(ErrorIs(ec, std::errc::value_too_large)); + TEST_CHECK(tt == old_time); + } + } else { + TEST_CHECK(ErrorIs(ec, std::errc::value_too_large)); + TEST_CHECK(tt == old_time); + } +} + +TEST_CASE(test_write_max_time) { + using Clock = file_time_type::clock; + using Sec = std::chrono::seconds; + using Hours = std::chrono::hours; + + scoped_test_env env; + const path p = env.create_file("file", 42); + const file_time_type old_time = last_write_time(p); + file_time_type new_time = file_time_type::max(); + + std::error_code ec = GetTestEC(); + last_write_time(p, new_time, ec); + file_time_type tt = last_write_time(p); + + if (TimeIsRepresentableByFilesystem(new_time)) { + TEST_CHECK(!ec); + TEST_CHECK(CompareTime(tt, new_time)); + } else { + TEST_CHECK(ErrorIs(ec, std::errc::value_too_large)); + TEST_CHECK(tt == old_time); + } +} + +TEST_CASE(test_value_on_failure) +{ + const path p = StaticEnv::DNE; + std::error_code ec = GetTestEC(); + TEST_CHECK(last_write_time(p, ec) == file_time_type::min()); + TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory)); +} + +TEST_CASE(test_exists_fails) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + permissions(dir, perms::none); + + std::error_code ec = GetTestEC(); + TEST_CHECK(last_write_time(file, ec) == file_time_type::min()); + TEST_CHECK(ErrorIs(ec, std::errc::permission_denied)); + + ExceptionChecker Checker(file, std::errc::permission_denied, + "last_write_time"); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, last_write_time(file)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.permissions/permissions.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.permissions/permissions.pass.cpp new file mode 100644 index 00000000000..cbe2b2d09fb --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.permissions/permissions.pass.cpp @@ -0,0 +1,182 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// void permissions(const path& p, perms prms, +// perm_options opts = perm_options::replace); +// void permissions(const path& p, perms prms, std::error_code& ec) noexcept; +// void permissions(const path& p, perms prms, perm_options opts, std::error_code); + + + +#include "filesystem_include.hpp" + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +using PR = fs::perms; + +TEST_SUITE(filesystem_permissions_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + const perms pr{}; ((void)pr); + const perm_options opts{}; ((void)opts); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(fs::permissions(p, pr)); + ASSERT_NOT_NOEXCEPT(fs::permissions(p, pr, opts)); + ASSERT_NOEXCEPT(fs::permissions(p, pr, ec)); + ASSERT_NOT_NOEXCEPT(fs::permissions(p, pr, opts, ec)); +} + +TEST_CASE(test_error_reporting) +{ + auto checkThrow = [](path const& f, fs::perms opts, + const std::error_code& ec) + { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + fs::permissions(f, opts); + return false; + } catch (filesystem_error const& err) { + return err.path1() == f + && err.path2() == "" + && err.code() == ec; + } +#else + ((void)f); ((void)opts); ((void)ec); + return true; +#endif + }; + + scoped_test_env env; + const path dne = env.make_env_path("dne"); + const path dne_sym = env.create_symlink(dne, "dne_sym"); + { // !exists + std::error_code ec = GetTestEC(); + fs::permissions(dne, fs::perms{}, ec); + TEST_REQUIRE(ec); + TEST_CHECK(ec != GetTestEC()); + TEST_CHECK(checkThrow(dne, fs::perms{}, ec)); + } + { + std::error_code ec = GetTestEC(); + fs::permissions(dne_sym, fs::perms{}, ec); + TEST_REQUIRE(ec); + TEST_CHECK(ec != GetTestEC()); + TEST_CHECK(checkThrow(dne_sym, fs::perms{}, ec)); + } +} + +TEST_CASE(basic_permissions_test) +{ + scoped_test_env env; + const path file = env.create_file("file1", 42); + const path dir = env.create_dir("dir1"); + const path file_for_sym = env.create_file("file2", 42); + const path sym = env.create_symlink(file_for_sym, "sym"); + const perm_options AP = perm_options::add; + const perm_options RP = perm_options::remove; + const perm_options NF = perm_options::nofollow; + struct TestCase { + path p; + perms set_perms; + perms expected; + perm_options opts; + TestCase(path xp, perms xperms, perms xexpect, + perm_options xopts = perm_options::replace) + : p(xp), set_perms(xperms), expected(xexpect), opts(xopts) {} + } cases[] = { + // test file + {file, perms::none, perms::none}, + {file, perms::owner_all, perms::owner_all}, + {file, perms::group_all, perms::owner_all | perms::group_all, AP}, + {file, perms::group_all, perms::owner_all, RP}, + // test directory + {dir, perms::none, perms::none}, + {dir, perms::owner_all, perms::owner_all}, + {dir, perms::group_all, perms::owner_all | perms::group_all, AP}, + {dir, perms::group_all, perms::owner_all, RP}, + // test symlink without symlink_nofollow + {sym, perms::none, perms::none}, + {sym, perms::owner_all, perms::owner_all}, + {sym, perms::group_all, perms::owner_all | perms::group_all, AP}, + {sym, perms::group_all, perms::owner_all, RP}, + // test non-symlink with symlink_nofollow. The last test on file/dir + // will have set their permissions to perms::owner_all + {file, perms::group_all, perms::owner_all | perms::group_all, AP | NF}, + {dir, perms::group_all, perms::owner_all | perms::group_all, AP | NF} + }; + for (auto const& TC : cases) { + TEST_CHECK(status(TC.p).permissions() != TC.expected); + { + std::error_code ec = GetTestEC(); + permissions(TC.p, TC.set_perms, TC.opts, ec); + TEST_CHECK(!ec); + auto pp = status(TC.p).permissions(); + TEST_CHECK(pp == TC.expected); + } + if (TC.opts == perm_options::replace) { + std::error_code ec = GetTestEC(); + permissions(TC.p, TC.set_perms, ec); + TEST_CHECK(!ec); + auto pp = status(TC.p).permissions(); + TEST_CHECK(pp == TC.expected); + } + } +} + +TEST_CASE(test_no_resolve_symlink_on_symlink) +{ + scoped_test_env env; + const path file = env.create_file("file", 42); + const path sym = env.create_symlink(file, "sym"); + const auto file_perms = status(file).permissions(); + + struct TestCase { + perms set_perms; + perms expected; // only expected on platform that support symlink perms. + perm_options opts = perm_options::replace; + TestCase(perms xperms, perms xexpect, + perm_options xopts = perm_options::replace) + : set_perms(xperms), expected(xexpect), opts(xopts) {} + } cases[] = { + {perms::owner_all, perms::owner_all}, + {perms::group_all, perms::owner_all | perms::group_all, perm_options::add}, + {perms::owner_all, perms::group_all, perm_options::remove}, + }; + for (auto const& TC : cases) { +#if defined(__APPLE__) || defined(__FreeBSD__) + // On OS X symlink permissions are supported. We should get an empty + // error code and the expected permissions. + const auto expected_link_perms = TC.expected; + std::error_code expected_ec; +#else + // On linux symlink permissions are not supported. The error code should + // be 'operation_not_supported' and the symlink permissions should be + // unchanged. + const auto expected_link_perms = symlink_status(sym).permissions(); + std::error_code expected_ec = std::make_error_code(std::errc::operation_not_supported); +#endif + std::error_code ec = GetTestEC(); + permissions(sym, TC.set_perms, TC.opts | perm_options::nofollow, ec); + TEST_CHECK(ec == expected_ec); + TEST_CHECK(status(file).permissions() == file_perms); + TEST_CHECK(symlink_status(sym).permissions() == expected_link_perms); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp new file mode 100644 index 00000000000..5f7b30dd60b --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp @@ -0,0 +1,133 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// path proximate(const path& p, error_code &ec) +// path proximate(const path& p, const path& base = current_path()) +// path proximate(const path& p, const path& base, error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <iostream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + + +static int count_path_elems(const fs::path& p) { + int count = 0; + for (auto& elem : p) { + if (elem != "/" && elem != "") + ++count; + } + return count; +} + +TEST_SUITE(filesystem_proximate_path_test_suite) + + +TEST_CASE(signature_test) +{ + using fs::path; + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(proximate(p)); + ASSERT_NOT_NOEXCEPT(proximate(p, p)); + ASSERT_NOT_NOEXCEPT(proximate(p, ec)); + ASSERT_NOT_NOEXCEPT(proximate(p, p, ec)); +} + +TEST_CASE(basic_test) { + using fs::path; + const path cwd = fs::current_path(); + const path parent_cwd = cwd.parent_path(); + const path curdir = cwd.filename(); + TEST_REQUIRE(!cwd.native().empty()); + int cwd_depth = count_path_elems(cwd); + path dot_dot_to_root; + for (int i=0; i < cwd_depth; ++i) + dot_dot_to_root /= ".."; + path relative_cwd = cwd.native().substr(1); + // clang-format off + struct { + std::string input; + std::string base; + std::string expect; + } TestCases[] = { + {"", "", "."}, + {cwd, "a", ".."}, + {parent_cwd, "a", "../.."}, + {"a", cwd, "a"}, + {"a", parent_cwd, "fs.op.proximate/a"}, + {"/", "a", dot_dot_to_root / ".."}, + {"/", "a/b", dot_dot_to_root / "../.."}, + {"/", "a/b/", dot_dot_to_root / "../../.."}, + {"a", "/", relative_cwd / "a"}, + {"a/b", "/", relative_cwd / "a/b"}, + {"a", "/net", ".." / relative_cwd / "a"}, + {"//foo/", "//foo", "/foo/"}, + {"//foo", "//foo/", ".."}, + {"//foo", "//foo", "."}, + {"//foo/", "//foo/", "."}, + {"//base", "a", dot_dot_to_root / "../base"}, + {"a", "a", "."}, + {"a/b", "a/b", "."}, + {"a/b/c/", "a/b/c/", "."}, + {"//foo/a/b", "//foo/a/b", "."}, + {"/a/d", "/a/b/c", "../../d"}, + {"/a/b/c", "/a/d", "../b/c"}, + {"a/b/c", "a", "b/c"}, + {"a/b/c", "a/b/c/x/y", "../.."}, + {"a/b/c", "a/b/c", "."}, + {"a/b", "c/d", "../../a/b"} + }; + // clang-format on + int ID = 0; + for (auto& TC : TestCases) { + ++ID; + std::error_code ec = GetTestEC(); + fs::path p(TC.input); + const fs::path output = fs::proximate(p, TC.base, ec); + if (ec) { + TEST_CHECK(!ec); + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Base: '" << TC.base << "'\n"; + std::cerr << " Expected: '" << TC.expect << "'\n"; + + std::cerr << std::endl; + } else if (!PathEq(output, TC.expect)) { + TEST_CHECK(PathEq(output, TC.expect)); + + const path canon_input = fs::weakly_canonical(TC.input); + const path canon_base = fs::weakly_canonical(TC.base); + const path lexically_p = canon_input.lexically_proximate(canon_base); + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Base: '" << TC.base << "'\n"; + std::cerr << " Expected: '" << TC.expect << "'\n"; + std::cerr << " Output: '" << output.native() << "'\n"; + std::cerr << " Lex Prox: '" << lexically_p.native() << "'\n"; + std::cerr << " Canon Input: " << canon_input << "\n"; + std::cerr << " Canon Base: " << canon_base << "\n"; + + std::cerr << std::endl; + } + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.read_symlink/read_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.read_symlink/read_symlink.pass.cpp new file mode 100644 index 00000000000..43dfec8347a --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.read_symlink/read_symlink.pass.cpp @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// path read_symlink(const path& p); +// path read_symlink(const path& p, error_code& ec); + +#include "filesystem_include.hpp" + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_read_symlink_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(fs::read_symlink(p)), fs::path); + ASSERT_SAME_TYPE(decltype(fs::read_symlink(p, ec)), fs::path); + + ASSERT_NOT_NOEXCEPT(fs::read_symlink(p)); + // Not noexcept because of narrow contract + ASSERT_NOT_NOEXCEPT(fs::read_symlink(p, ec)); +} + +TEST_CASE(test_error_reporting) +{ + auto checkThrow = [](path const& f, const std::error_code& ec) + { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + fs::read_symlink(f); + return false; + } catch (filesystem_error const& err) { + return err.path1() == f + && err.path2() == "" + && err.code() == ec; + } +#else + ((void)f); ((void)ec); + return true; +#endif + }; + + scoped_test_env env; + const path cases[] = { + env.make_env_path("dne"), + env.create_file("file", 42), + env.create_dir("dir") + }; + for (path const& p : cases) { + std::error_code ec; + const path ret = fs::read_symlink(p, ec); + TEST_REQUIRE(ec); + TEST_CHECK(ret == path{}); + TEST_CHECK(checkThrow(p, ec)); + } + +} + +TEST_CASE(basic_symlink_test) +{ + scoped_test_env env; + const path dne = env.make_env_path("dne"); + const path file = env.create_file("file", 42); + const path dir = env.create_dir("dir"); + const path link = env.create_symlink(dne, "link"); + const path nested_link = env.make_env_path("nested_link"); + create_symlink(link, nested_link); + struct TestCase { + path symlink; + path expected; + } testCases[] = { + {env.create_symlink(dne, "dne_link"), dne}, + {env.create_symlink(file, "file_link"), file}, + {env.create_symlink(dir, "dir_link"), dir}, + {nested_link, link} + }; + for (auto& TC : testCases) { + std::error_code ec = std::make_error_code(std::errc::address_in_use); + const path ret = read_symlink(TC.symlink, ec); + TEST_CHECK(!ec); + TEST_CHECK(ret == TC.expected); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp new file mode 100644 index 00000000000..e240c649675 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// path proximate(const path& p, error_code &ec) +// path proximate(const path& p, const path& base = current_path()) +// path proximate(const path& p, const path& base, error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <iostream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + + +TEST_SUITE(filesystem_proximate_path_test_suite) + +TEST_CASE(test_signature) { + +} +int main() { + // clang-format off + struct { + std::string input; + std::string expect; + } TestCases[] = { + {"", fs::current_path()}, + {".", fs::current_path()}, + {StaticEnv::File, StaticEnv::File}, + {StaticEnv::Dir, StaticEnv::Dir}, + {StaticEnv::SymlinkToDir, StaticEnv::Dir}, + {StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"}, + // FIXME? If the trailing separator occurs in a part of the path that exists, + // it is ommitted. Otherwise it is added to the end of the result. + {StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"}, + {StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"}, + {StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2}, + {StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""}, + {StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"}, + {StaticEnv::Dir / "../dir1", StaticEnv::Dir}, + {StaticEnv::Dir / "./.", StaticEnv::Dir}, + {StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"} + }; + // clang-format on + int ID = 0; + bool Failed = false; + for (auto& TC : TestCases) { + ++ID; + fs::path p(TC.input); + const fs::path output = fs::weakly_canonical(p); + if (output != TC.expect) { + Failed = true; + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Expected: '" << TC.expect << "'\n"; + std::cerr << " Output: '" << output.native() << "'"; + std::cerr << std::endl; + } + } + return Failed; +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp new file mode 100644 index 00000000000..37b47f1517b --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool remove(const path& p); +// bool remove(const path& p, error_code& ec) noexcept; + +#include "filesystem_include.hpp" + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_remove_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(fs::remove(p)), bool); + ASSERT_SAME_TYPE(decltype(fs::remove(p, ec)), bool); + + ASSERT_NOT_NOEXCEPT(fs::remove(p)); + ASSERT_NOEXCEPT(fs::remove(p, ec)); +} + +TEST_CASE(test_error_reporting) +{ + auto checkThrow = [](path const& f, const std::error_code& ec) + { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + fs::remove(f); + return false; + } catch (filesystem_error const& err) { + return err.path1() == f + && err.path2() == "" + && err.code() == ec; + } +#else + ((void)f); ((void)ec); + return true; +#endif + }; + scoped_test_env env; + const path non_empty_dir = env.create_dir("dir"); + env.create_file(non_empty_dir / "file1", 42); + const path bad_perms_dir = env.create_dir("bad_dir"); + const path file_in_bad_dir = env.create_file(bad_perms_dir / "file", 42); + permissions(bad_perms_dir, perms::none); + const path testCases[] = { + non_empty_dir, + file_in_bad_dir, + }; + for (auto& p : testCases) { + std::error_code ec; + + TEST_CHECK(!fs::remove(p, ec)); + TEST_CHECK(ec); + TEST_CHECK(checkThrow(p, ec)); + } + + // PR#35780 + const path testCasesNonexistant[] = { + "", + env.make_env_path("dne") + }; + + for (auto& p : testCasesNonexistant) { + std::error_code ec; + + TEST_CHECK(!fs::remove(p, ec)); + TEST_CHECK(!ec); + } +} + +TEST_CASE(basic_remove_test) +{ + scoped_test_env env; + const path dne = env.make_env_path("dne"); + const path link = env.create_symlink(dne, "link"); + const path nested_link = env.make_env_path("nested_link"); + create_symlink(link, nested_link); + const path testCases[] = { + env.create_file("file", 42), + env.create_dir("empty_dir"), + nested_link, + link + }; + for (auto& p : testCases) { + std::error_code ec = std::make_error_code(std::errc::address_in_use); + TEST_CHECK(remove(p, ec)); + TEST_CHECK(!ec); + TEST_CHECK(!exists(symlink_status(p))); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp new file mode 100644 index 00000000000..0f015c387bf --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp @@ -0,0 +1,153 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// uintmax_t remove_all(const path& p); +// uintmax_t remove_all(const path& p, error_code& ec) noexcept; + +#include "filesystem_include.hpp" + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_remove_all_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(fs::remove_all(p)), std::uintmax_t); + ASSERT_SAME_TYPE(decltype(fs::remove_all(p, ec)), std::uintmax_t); + + ASSERT_NOT_NOEXCEPT(fs::remove_all(p)); + ASSERT_NOT_NOEXCEPT(fs::remove_all(p, ec)); +} + +TEST_CASE(test_error_reporting) +{ + auto checkThrow = [](path const& f, const std::error_code& ec) + { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + fs::remove_all(f); + return false; + } catch (filesystem_error const& err) { + return err.path1() == f + && err.path2() == "" + && err.code() == ec; + } +#else + ((void)f); ((void)ec); + return true; +#endif + }; + scoped_test_env env; + const path non_empty_dir = env.create_dir("dir"); + env.create_file(non_empty_dir / "file1", 42); + const path bad_perms_dir = env.create_dir("bad_dir"); + const path file_in_bad_dir = env.create_file(bad_perms_dir / "file", 42); + permissions(bad_perms_dir, perms::none); + const path bad_perms_file = env.create_file("file2", 42); + permissions(bad_perms_file, perms::none); + + const path testCases[] = { + file_in_bad_dir + }; + const auto BadRet = static_cast<std::uintmax_t>(-1); + for (auto& p : testCases) { + std::error_code ec; + + TEST_CHECK(fs::remove_all(p, ec) == BadRet); + TEST_CHECK(ec); + TEST_CHECK(checkThrow(p, ec)); + } + + // PR#35780 + const path testCasesNonexistant[] = { + "", + env.make_env_path("dne") + }; + for (auto &p : testCasesNonexistant) { + std::error_code ec; + + TEST_CHECK(fs::remove_all(p, ec) == 0); + TEST_CHECK(!ec); + } +} + +TEST_CASE(basic_remove_all_test) +{ + scoped_test_env env; + const path dne = env.make_env_path("dne"); + const path link = env.create_symlink(dne, "link"); + const path nested_link = env.make_env_path("nested_link"); + create_symlink(link, nested_link); + const path testCases[] = { + env.create_file("file", 42), + env.create_dir("empty_dir"), + nested_link, + link + }; + for (auto& p : testCases) { + std::error_code ec = std::make_error_code(std::errc::address_in_use); + TEST_CHECK(remove(p, ec)); + TEST_CHECK(!ec); + TEST_CHECK(!exists(symlink_status(p))); + } +} + +TEST_CASE(symlink_to_dir) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file(dir / "file", 42); + const path link = env.create_symlink(dir, "sym"); + + { + std::error_code ec = std::make_error_code(std::errc::address_in_use); + TEST_CHECK(remove_all(link, ec) == 1); + TEST_CHECK(!ec); + TEST_CHECK(!exists(symlink_status(link))); + TEST_CHECK(exists(dir)); + TEST_CHECK(exists(file)); + } +} + + +TEST_CASE(nested_dir) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path dir1 = env.create_dir(dir / "dir1"); + const path out_of_dir_file = env.create_file("file1", 42); + const path all_files[] = { + dir, dir1, + env.create_file(dir / "file1", 42), + env.create_symlink(out_of_dir_file, dir / "sym1"), + env.create_file(dir1 / "file2", 42), + env.create_symlink(dir, dir1 / "sym2") + }; + const std::size_t expected_count = sizeof(all_files) / sizeof(all_files[0]); + + std::error_code ec = std::make_error_code(std::errc::address_in_use); + TEST_CHECK(remove_all(dir, ec) == expected_count); + TEST_CHECK(!ec); + for (auto const& p : all_files) { + TEST_CHECK(!exists(symlink_status(p))); + } + TEST_CHECK(exists(out_of_dir_file)); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.rename/rename.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.rename/rename.pass.cpp new file mode 100644 index 00000000000..e2ebdc61f65 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.rename/rename.pass.cpp @@ -0,0 +1,125 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// void rename(const path& old_p, const path& new_p); +// void rename(const path& old_p, const path& new_p, error_code& ec) noexcept; + +#include "filesystem_include.hpp" + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_rename_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(fs::rename(p, p)), void); + ASSERT_SAME_TYPE(decltype(fs::rename(p, p, ec)), void); + + ASSERT_NOT_NOEXCEPT(fs::rename(p, p)); + ASSERT_NOEXCEPT(fs::rename(p, p, ec)); +} + +TEST_CASE(test_error_reporting) +{ + auto checkThrow = [](path const& f, path const& t, const std::error_code& ec) + { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + fs::rename(f, t); + return false; + } catch (filesystem_error const& err) { + return err.path1() == f + && err.path2() == t + && err.code() == ec; + } +#else + ((void)f); ((void)t); ((void)ec); + return true; +#endif + }; + scoped_test_env env; + const path dne = env.make_env_path("dne"); + const path file = env.create_file("file1", 42); + const path dir = env.create_dir("dir1"); + struct TestCase { + path from; + path to; + } cases[] = { + {dne, dne}, + {file, dir}, + {dir, file} + }; + for (auto& TC : cases) { + auto from_before = status(TC.from); + auto to_before = status(TC.to); + std::error_code ec; + rename(TC.from, TC.to, ec); + TEST_REQUIRE(ec); + TEST_CHECK(from_before.type() == status(TC.from).type()); + TEST_CHECK(to_before.type() == status(TC.to).type()); + TEST_CHECK(checkThrow(TC.from, TC.to, ec)); + } +} + +TEST_CASE(basic_rename_test) +{ + scoped_test_env env; + + const std::error_code set_ec = std::make_error_code(std::errc::address_in_use); + const path file = env.create_file("file1", 42); + { // same file + std::error_code ec = set_ec; + rename(file, file, ec); + TEST_CHECK(!ec); + TEST_CHECK(is_regular_file(file)); + TEST_CHECK(file_size(file) == 42); + } + const path sym = env.create_symlink(file, "sym"); + { // file -> symlink + std::error_code ec = set_ec; + rename(file, sym, ec); + TEST_CHECK(!ec); + TEST_CHECK(!exists(file)); + TEST_CHECK(is_regular_file(symlink_status(sym))); + TEST_CHECK(file_size(sym) == 42); + } + const path file2 = env.create_file("file2", 42); + const path file3 = env.create_file("file3", 100); + { // file -> file + std::error_code ec = set_ec; + rename(file2, file3, ec); + TEST_CHECK(!ec); + TEST_CHECK(!exists(file2)); + TEST_CHECK(is_regular_file(file3)); + TEST_CHECK(file_size(file3) == 42); + } + const path dne = env.make_env_path("dne"); + const path bad_sym = env.create_symlink(dne, "bad_sym"); + const path bad_sym_dest = env.make_env_path("bad_sym2"); + { // bad-symlink + std::error_code ec = set_ec; + rename(bad_sym, bad_sym_dest, ec); + TEST_CHECK(!ec); + TEST_CHECK(!exists(symlink_status(bad_sym))); + TEST_CHECK(is_symlink(bad_sym_dest)); + TEST_CHECK(read_symlink(bad_sym_dest) == dne); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.resize_file/resize_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.resize_file/resize_file.pass.cpp new file mode 100644 index 00000000000..1a2df47e68c --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.resize_file/resize_file.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// void resize_file(const path& p, uintmax_t new_size); +// void resize_file(const path& p, uintmax_t new_size, error_code& ec) noexcept; + +#include "filesystem_include.hpp" + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_resize_file_test_suite) + +TEST_CASE(test_signatures) +{ + const path p; ((void)p); + std::uintmax_t i; ((void)i); + std::error_code ec; ((void)ec); + + ASSERT_SAME_TYPE(decltype(fs::resize_file(p, i)), void); + ASSERT_SAME_TYPE(decltype(fs::resize_file(p, i, ec)), void); + + ASSERT_NOT_NOEXCEPT(fs::resize_file(p, i)); + ASSERT_NOEXCEPT(fs::resize_file(p, i, ec)); +} + +TEST_CASE(test_error_reporting) +{ + auto checkThrow = [](path const& f, std::uintmax_t s, const std::error_code& ec) + { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + fs::resize_file(f, s); + return false; + } catch (filesystem_error const& err) { + return err.path1() == f + && err.path2() == "" + && err.code() == ec; + } +#else + ((void)f); ((void)s); ((void)ec); + return true; +#endif + }; + scoped_test_env env; + const path dne = env.make_env_path("dne"); + const path bad_sym = env.create_symlink(dne, "sym"); + const path dir = env.create_dir("dir1"); + const path cases[] = { + dne, bad_sym, dir + }; + for (auto& p : cases) { + std::error_code ec; + resize_file(p, 42, ec); + TEST_REQUIRE(ec); + TEST_CHECK(checkThrow(p, 42, ec)); + } +} + +TEST_CASE(basic_resize_file_test) +{ + scoped_test_env env; + const path file1 = env.create_file("file1", 42); + const auto set_ec = std::make_error_code(std::errc::address_in_use); + { // grow file + const std::uintmax_t new_s = 100; + std::error_code ec = set_ec; + resize_file(file1, new_s, ec); + TEST_CHECK(!ec); + TEST_CHECK(file_size(file1) == new_s); + } + { // shrink file + const std::uintmax_t new_s = 1; + std::error_code ec = set_ec; + resize_file(file1, new_s, ec); + TEST_CHECK(!ec); + TEST_CHECK(file_size(file1) == new_s); + } + { // shrink file to zero + const std::uintmax_t new_s = 0; + std::error_code ec = set_ec; + resize_file(file1, new_s, ec); + TEST_CHECK(!ec); + TEST_CHECK(file_size(file1) == new_s); + } + const path sym = env.create_symlink(file1, "sym"); + { // grow file via symlink + const std::uintmax_t new_s = 1024; + std::error_code ec = set_ec; + resize_file(sym, new_s, ec); + TEST_CHECK(!ec); + TEST_CHECK(file_size(file1) == new_s); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.space/space.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.space/space.pass.cpp new file mode 100644 index 00000000000..2c2e75af66a --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.space/space.pass.cpp @@ -0,0 +1,128 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// space_info space(const path& p); +// space_info space(const path& p, error_code& ec) noexcept; + +#include "filesystem_include.hpp" +#include <sys/statvfs.h> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +bool EqualDelta(std::uintmax_t x, std::uintmax_t y, std::uintmax_t delta) { + if (x >= y) { + return (x - y) <= delta; + } else { + return (y - x) <= delta; + } +} + +TEST_SUITE(filesystem_space_test_suite) + +TEST_CASE(signature_test) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_SAME_TYPE(decltype(space(p)), space_info); + ASSERT_SAME_TYPE(decltype(space(p, ec)), space_info); + ASSERT_NOT_NOEXCEPT(space(p)); + ASSERT_NOEXCEPT(space(p, ec)); +} + +TEST_CASE(test_error_reporting) +{ + auto checkThrow = [](path const& f, const std::error_code& ec) + { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + space(f); + return false; + } catch (filesystem_error const& err) { + return err.path1() == f + && err.path2() == "" + && err.code() == ec; + } +#else + ((void)f); ((void)ec); + return true; +#endif + }; + const path cases[] = { + "", + StaticEnv::DNE, + StaticEnv::BadSymlink + }; + for (auto& p : cases) { + const auto expect = static_cast<std::uintmax_t>(-1); + std::error_code ec; + space_info info = space(p, ec); + TEST_CHECK(ec); + TEST_CHECK(info.capacity == expect); + TEST_CHECK(info.free == expect); + TEST_CHECK(info.available == expect); + TEST_CHECK(checkThrow(p, ec)); + } +} + +TEST_CASE(basic_space_test) +{ + // All the test cases should reside on the same filesystem and therefore + // should have the same expected result. Compute this expected result + // one and check that it looks semi-sane. + struct statvfs expect; + TEST_REQUIRE(::statvfs(StaticEnv::Dir.c_str(), &expect) != -1); + TEST_CHECK(expect.f_bavail > 0); + TEST_CHECK(expect.f_bfree > 0); + TEST_CHECK(expect.f_bsize > 0); + TEST_CHECK(expect.f_blocks > 0); + TEST_REQUIRE(expect.f_frsize > 0); + auto do_mult = [&](std::uintmax_t val) { + std::uintmax_t fsize = expect.f_frsize; + std::uintmax_t new_val = val * fsize; + TEST_CHECK(new_val / fsize == val); // Test for overflow + return new_val; + }; + const std::uintmax_t bad_value = static_cast<std::uintmax_t>(-1); + const std::uintmax_t expect_capacity = do_mult(expect.f_blocks); + const std::uintmax_t expect_free = do_mult(expect.f_bfree); + const std::uintmax_t expect_avail = do_mult(expect.f_bavail); + + // Other processes running on the operating system may have changed + // the amount of space available. Check that these are within tolerances. + // Currently 5% of capacity + const std::uintmax_t delta = expect_capacity / 20; + const path cases[] = { + StaticEnv::File, + StaticEnv::Dir, + StaticEnv::Dir2, + StaticEnv::SymlinkToFile, + StaticEnv::SymlinkToDir + }; + for (auto& p : cases) { + std::error_code ec = GetTestEC(); + space_info info = space(p, ec); + TEST_CHECK(!ec); + TEST_CHECK(info.capacity != bad_value); + TEST_CHECK(expect_capacity == info.capacity); + TEST_CHECK(info.free != bad_value); + TEST_CHECK(EqualDelta(expect_free, info.free, delta)); + TEST_CHECK(info.available != bad_value); + TEST_CHECK(EqualDelta(expect_avail, info.available, delta)); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp new file mode 100644 index 00000000000..ab72f2e5dcd --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp @@ -0,0 +1,166 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// file_status status(const path& p); +// file_status status(const path& p, error_code& ec) noexcept; + +#include "filesystem_include.hpp" + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_status_test_suite) + +TEST_CASE(signature_test) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(status(p)); + ASSERT_NOEXCEPT(status(p, ec)); +} + +TEST_CASE(test_status_not_found) +{ + const std::error_code expect_ec = + std::make_error_code(std::errc::no_such_file_or_directory); + const path cases[] { + StaticEnv::DNE, + StaticEnv::BadSymlink + }; + for (auto& p : cases) { + std::error_code ec = std::make_error_code(std::errc::address_in_use); + // test non-throwing overload. + file_status st = status(p, ec); + TEST_CHECK(ec == expect_ec); + TEST_CHECK(st.type() == file_type::not_found); + TEST_CHECK(st.permissions() == perms::unknown); + // test throwing overload. It should not throw even though it reports + // that the file was not found. + TEST_CHECK_NO_THROW(st = status(p)); + TEST_CHECK(st.type() == file_type::not_found); + TEST_CHECK(st.permissions() == perms::unknown); + } +} + +TEST_CASE(test_status_cannot_resolve) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file = env.create_file("dir/file", 42); + const path sym = env.create_symlink("dir/file", "sym"); + permissions(dir, perms::none); + + const std::error_code set_ec = + std::make_error_code(std::errc::address_in_use); + const std::error_code perm_ec = + std::make_error_code(std::errc::permission_denied); + const std::error_code name_too_long_ec = + std::make_error_code(std::errc::filename_too_long); + + struct TestCase { + path p; + std::error_code expect_ec; + } const TestCases[] = { + {file, perm_ec}, + {sym, perm_ec}, + {path(std::string(2500, 'a')), name_too_long_ec} + }; + for (auto& TC : TestCases) + { + { // test non-throwing case + std::error_code ec = set_ec; + file_status st = status(TC.p, ec); + TEST_CHECK(ec == TC.expect_ec); + TEST_CHECK(st.type() == file_type::none); + TEST_CHECK(st.permissions() == perms::unknown); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + { // test throwing case + try { + status(TC.p); + } catch (filesystem_error const& err) { + TEST_CHECK(err.path1() == TC.p); + TEST_CHECK(err.path2() == ""); + TEST_CHECK(err.code() == TC.expect_ec); + } + } +#endif + } +} + +TEST_CASE(status_file_types_test) +{ + scoped_test_env env; + struct TestCase { + path p; + file_type expect_type; + } cases[] = { + {StaticEnv::File, file_type::regular}, + {StaticEnv::SymlinkToFile, file_type::regular}, + {StaticEnv::Dir, file_type::directory}, + {StaticEnv::SymlinkToDir, file_type::directory}, + // Block files tested elsewhere + {StaticEnv::CharFile, file_type::character}, +#if !defined(__APPLE__) && !defined(__FreeBSD__) // No support for domain sockets + {env.create_socket("socket"), file_type::socket}, +#endif + {env.create_fifo("fifo"), file_type::fifo} + }; + for (const auto& TC : cases) { + // test non-throwing case + std::error_code ec = std::make_error_code(std::errc::address_in_use); + file_status st = status(TC.p, ec); + TEST_CHECK(!ec); + TEST_CHECK(st.type() == TC.expect_type); + TEST_CHECK(st.permissions() != perms::unknown); + // test throwing case + TEST_REQUIRE_NO_THROW(st = status(TC.p)); + TEST_CHECK(st.type() == TC.expect_type); + TEST_CHECK(st.permissions() != perms::unknown); + } +} + +TEST_CASE(test_block_file) +{ + const path possible_paths[] = { + "/dev/drive0", // Apple + "/dev/sda", + "/dev/loop0" + }; + path p; + for (const path& possible_p : possible_paths) { + std::error_code ec; + if (exists(possible_p, ec)) { + p = possible_p; + break; + } + } + if (p == path{}) { + TEST_UNSUPPORTED(); + } + // test non-throwing case + std::error_code ec = std::make_error_code(std::errc::address_in_use); + file_status st = status(p, ec); + TEST_CHECK(!ec); + TEST_CHECK(st.type() == file_type::block); + TEST_CHECK(st.permissions() != perms::unknown); + // test throwing case + TEST_REQUIRE_NO_THROW(st = status(p)); + TEST_CHECK(st.type() == file_type::block); + TEST_CHECK(st.permissions() != perms::unknown); +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status_known/status_known.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status_known/status_known.pass.cpp new file mode 100644 index 00000000000..d74ced643f3 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status_known/status_known.pass.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// bool status_known(file_status s) noexcept; + +#include "filesystem_include.hpp" +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(status_known_test_suite) + +TEST_CASE(signature_test) +{ + file_status s; ((void)s); + ASSERT_SAME_TYPE(decltype(status_known(s)), bool); + ASSERT_NOEXCEPT(status_known(s)); +} + +TEST_CASE(status_known_test) +{ + struct TestCase { + file_type type; + bool expect; + }; + const TestCase testCases[] = { + {file_type::none, false}, + {file_type::not_found, true}, + {file_type::regular, true}, + {file_type::directory, true}, + {file_type::symlink, true}, + {file_type::block, true}, + {file_type::character, true}, + {file_type::fifo, true}, + {file_type::socket, true}, + {file_type::unknown, true} + }; + for (auto& TC : testCases) { + file_status s(TC.type); + TEST_CHECK(status_known(s) == TC.expect); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp new file mode 100644 index 00000000000..fac630da108 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp @@ -0,0 +1,192 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// file_status symlink_status(const path& p); +// file_status symlink_status(const path& p, error_code& ec) noexcept; + +#include "filesystem_include.hpp" + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +TEST_SUITE(filesystem_symlink_status_test_suite) + +TEST_CASE(signature_test) +{ + const path p; ((void)p); + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(symlink_status(p)); + ASSERT_NOEXCEPT(symlink_status(p, ec)); +} + +TEST_CASE(test_symlink_status_not_found) +{ + const std::error_code expect_ec = + std::make_error_code(std::errc::no_such_file_or_directory); + const path cases[] { + StaticEnv::DNE + }; + for (auto& p : cases) { + std::error_code ec = std::make_error_code(std::errc::address_in_use); + // test non-throwing overload. + file_status st = symlink_status(p, ec); + TEST_CHECK(ec == expect_ec); + TEST_CHECK(st.type() == file_type::not_found); + TEST_CHECK(st.permissions() == perms::unknown); + // test throwing overload. It should not throw even though it reports + // that the file was not found. + TEST_CHECK_NO_THROW(st = status(p)); + TEST_CHECK(st.type() == file_type::not_found); + TEST_CHECK(st.permissions() == perms::unknown); + } +} + +TEST_CASE(test_symlink_status_cannot_resolve) +{ + scoped_test_env env; + const path dir = env.create_dir("dir"); + const path file_in_dir = env.create_file("dir/file", 42); + const path sym_in_dir = env.create_symlink("dir/file", "dir/bad_sym"); + const path sym_points_in_dir = env.create_symlink("dir/file", "sym"); + permissions(dir, perms::none); + + const std::error_code set_ec = + std::make_error_code(std::errc::address_in_use); + const std::error_code expect_ec = + std::make_error_code(std::errc::permission_denied); + + const path fail_cases[] = { + file_in_dir, sym_in_dir + }; + for (auto& p : fail_cases) + { + { // test non-throwing case + std::error_code ec = set_ec; + file_status st = symlink_status(p, ec); + TEST_CHECK(ec == expect_ec); + TEST_CHECK(st.type() == file_type::none); + TEST_CHECK(st.permissions() == perms::unknown); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + { // test throwing case + try { + symlink_status(p); + } catch (filesystem_error const& err) { + TEST_CHECK(err.path1() == p); + TEST_CHECK(err.path2() == ""); + TEST_CHECK(err.code() == expect_ec); + } + } +#endif + } + // Test that a symlink that points into a directory without read perms + // can be stat-ed using symlink_status + { + std::error_code ec = set_ec; + file_status st = symlink_status(sym_points_in_dir, ec); + TEST_CHECK(!ec); + TEST_CHECK(st.type() == file_type::symlink); + TEST_CHECK(st.permissions() != perms::unknown); + // test non-throwing version + TEST_REQUIRE_NO_THROW(st = symlink_status(sym_points_in_dir)); + TEST_CHECK(st.type() == file_type::symlink); + TEST_CHECK(st.permissions() != perms::unknown); + } +} + + +TEST_CASE(symlink_status_file_types_test) +{ + scoped_test_env env; + struct TestCase { + path p; + file_type expect_type; + } cases[] = { + {StaticEnv::BadSymlink, file_type::symlink}, + {StaticEnv::File, file_type::regular}, + {StaticEnv::SymlinkToFile, file_type::symlink}, + {StaticEnv::Dir, file_type::directory}, + {StaticEnv::SymlinkToDir, file_type::symlink}, + // Block files tested elsewhere + {StaticEnv::CharFile, file_type::character}, +#if !defined(__APPLE__) && !defined(__FreeBSD__) // No support for domain sockets + {env.create_socket("socket"), file_type::socket}, +#endif + {env.create_fifo("fifo"), file_type::fifo} + }; + for (const auto& TC : cases) { + // test non-throwing case + std::error_code ec = std::make_error_code(std::errc::address_in_use); + file_status st = symlink_status(TC.p, ec); + TEST_CHECK(!ec); + TEST_CHECK(st.type() == TC.expect_type); + TEST_CHECK(st.permissions() != perms::unknown); + // test throwing case + TEST_REQUIRE_NO_THROW(st = symlink_status(TC.p)); + TEST_CHECK(st.type() == TC.expect_type); + TEST_CHECK(st.permissions() != perms::unknown); + } +} + +TEST_CASE(test_block_file) +{ + const path possible_paths[] = { + "/dev/drive0", // Apple + "/dev/sda", // Linux + "/dev/loop0" // Linux + // No FreeBSD files known + }; + path p; + for (const path& possible_p : possible_paths) { + std::error_code ec; + if (exists(possible_p, ec)) { + p = possible_p; + break; + } + } + if (p == path{}) { + TEST_UNSUPPORTED(); + } + scoped_test_env env; + { // test block file + // test non-throwing case + std::error_code ec = std::make_error_code(std::errc::address_in_use); + file_status st = symlink_status(p, ec); + TEST_CHECK(!ec); + TEST_CHECK(st.type() == file_type::block); + TEST_CHECK(st.permissions() != perms::unknown); + // test throwing case + TEST_REQUIRE_NO_THROW(st = symlink_status(p)); + TEST_CHECK(st.type() == file_type::block); + TEST_CHECK(st.permissions() != perms::unknown); + } + const path sym = env.make_env_path("sym"); + create_symlink(p, sym); + { // test symlink to block file + // test non-throwing case + std::error_code ec = std::make_error_code(std::errc::address_in_use); + file_status st = symlink_status(sym, ec); + TEST_CHECK(!ec); + TEST_CHECK(st.type() == file_type::symlink); + TEST_CHECK(st.permissions() != perms::unknown); + // test throwing case + TEST_REQUIRE_NO_THROW(st = symlink_status(sym)); + TEST_CHECK(st.type() == file_type::symlink); + TEST_CHECK(st.permissions() != perms::unknown); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp new file mode 100644 index 00000000000..523e429cf79 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp @@ -0,0 +1,121 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// path temp_directory_path(); +// path temp_directory_path(error_code& ec); + +#include "filesystem_include.hpp" +#include <memory> +#include <cstdlib> +#include <cstring> +#include <cassert> + +#include "test_macros.h" +#include "rapid-cxx-test.hpp" +#include "filesystem_test_helper.hpp" + +using namespace fs; + +void PutEnv(std::string var, std::string value) { + assert(::setenv(var.c_str(), value.c_str(), /* overwrite */ 1) == 0); +} + +void UnsetEnv(std::string var) { + assert(::unsetenv(var.c_str()) == 0); +} + +TEST_SUITE(filesystem_temp_directory_path_test_suite) + +TEST_CASE(signature_test) +{ + std::error_code ec; ((void)ec); + ASSERT_NOT_NOEXCEPT(temp_directory_path()); + ASSERT_NOT_NOEXCEPT(temp_directory_path(ec)); +} + +TEST_CASE(basic_tests) +{ + scoped_test_env env; + const path dne = env.make_env_path("dne"); + const path file = env.create_file("file", 42); + const path dir_perms = env.create_dir("bad_perms_dir"); + const path nested_dir = env.create_dir("bad_perms_dir/nested"); + permissions(dir_perms, perms::none); + const std::error_code expect_ec = std::make_error_code(std::errc::not_a_directory); + struct TestCase { + std::string name; + path p; + } cases[] = { + {"TMPDIR", env.create_dir("dir1")}, + {"TMP", env.create_dir("dir2")}, + {"TEMP", env.create_dir("dir3")}, + {"TEMPDIR", env.create_dir("dir4")} + }; + for (auto& TC : cases) { + PutEnv(TC.name, TC.p); + } + for (auto& TC : cases) { + std::error_code ec = GetTestEC(); + path ret = temp_directory_path(ec); + TEST_CHECK(!ec); + TEST_CHECK(ret == TC.p); + TEST_CHECK(is_directory(ret)); + + // Set the env variable to a path that does not exist and check + // that it fails. + PutEnv(TC.name, dne); + ec = GetTestEC(); + ret = temp_directory_path(ec); + LIBCPP_ONLY(TEST_CHECK(ec == expect_ec)); + TEST_CHECK(ec != GetTestEC()); + TEST_CHECK(ec); + TEST_CHECK(ret == ""); + + // Set the env variable to point to a file and check that it fails. + PutEnv(TC.name, file); + ec = GetTestEC(); + ret = temp_directory_path(ec); + LIBCPP_ONLY(TEST_CHECK(ec == expect_ec)); + TEST_CHECK(ec != GetTestEC()); + TEST_CHECK(ec); + TEST_CHECK(ret == ""); + + // Set the env variable to point to a dir we can't access + PutEnv(TC.name, nested_dir); + ec = GetTestEC(); + ret = temp_directory_path(ec); + TEST_CHECK(ec == std::make_error_code(std::errc::permission_denied)); + TEST_CHECK(ret == ""); + + // Set the env variable to point to a non-existent dir + PutEnv(TC.name, TC.p / "does_not_exist"); + ec = GetTestEC(); + ret = temp_directory_path(ec); + TEST_CHECK(ec != GetTestEC()); + TEST_CHECK(ec); + TEST_CHECK(ret == ""); + + // Finally erase this env variable + UnsetEnv(TC.name); + } + // No env variables are defined + { + std::error_code ec = GetTestEC(); + path ret = temp_directory_path(ec); + TEST_CHECK(!ec); + TEST_CHECK(ret == "/tmp"); + TEST_CHECK(is_directory(ret)); + } +} + +TEST_SUITE_END() diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp new file mode 100644 index 00000000000..0fe058ffdff --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <filesystem> + +// path weakly_canonical(const path& p); +// path weakly_canonical(const path& p, error_code& ec); + +#include "filesystem_include.hpp" +#include <type_traits> +#include <vector> +#include <iostream> +#include <cassert> + +#include "test_macros.h" +#include "test_iterators.h" +#include "count_new.hpp" +#include "filesystem_test_helper.hpp" + + +int main() { + // clang-format off + struct { + std::string input; + std::string expect; + } TestCases[] = { + {"", fs::current_path()}, + {".", fs::current_path()}, + {"/", "/"}, + {"/foo", "/foo"}, + {"/.", "/"}, + {"/./", "/"}, + {"a/b", fs::current_path() / "a/b"}, + {"a", fs::current_path() / "a"}, + {"a/b/", fs::current_path() / "a/b/"}, + {StaticEnv::File, StaticEnv::File}, + {StaticEnv::Dir, StaticEnv::Dir}, + {StaticEnv::SymlinkToDir, StaticEnv::Dir}, + {StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"}, + // FIXME? If the trailing separator occurs in a part of the path that exists, + // it is ommitted. Otherwise it is added to the end of the result. + {StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"}, + {StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"}, + {StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2}, + {StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""}, + {StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"}, + {StaticEnv::Dir / "../dir1", StaticEnv::Dir}, + {StaticEnv::Dir / "./.", StaticEnv::Dir}, + {StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"} + }; + // clang-format on + int ID = 0; + bool Failed = false; + for (auto& TC : TestCases) { + ++ID; + fs::path p(TC.input); + const fs::path output = fs::weakly_canonical(p); + if (!PathEq(output, TC.expect)) { + Failed = true; + std::cerr << "TEST CASE #" << ID << " FAILED: \n"; + std::cerr << " Input: '" << TC.input << "'\n"; + std::cerr << " Expected: '" << TC.expect << "'\n"; + std::cerr << " Output: '" << output.native() << "'"; + std::cerr << std::endl; + } + } + return Failed; +} diff --git a/libcxx/test/std/input.output/filesystems/fs.req.macros/feature_macro.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.req.macros/feature_macro.pass.cpp new file mode 100644 index 00000000000..071fa800cf6 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.req.macros/feature_macro.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// <filesystem> + +// #define __cpp_lib_filesystem 201703L + +#include <filesystem> +#include "test_macros.h" + +#if TEST_STD_VER >= 17 +#ifndef __cpp_lib_filesystem +#error Filesystem feature test macro is not defined (__cpp_lib_filesystem) +#elif __cpp_lib_filesystem != 201703L +#error Filesystem feature test macro has an incorrect value (__cpp_lib_filesystem) +#endif +#else // TEST_STD_VER < 17 +#ifdef __cpp_lib_filesystem +#error Filesystem feature test macro should not be defined before C++17 +#endif +#endif + +int main() { } diff --git a/libcxx/test/std/input.output/filesystems/fs.req.namespace/namespace.fail.cpp b/libcxx/test/std/input.output/filesystems/fs.req.namespace/namespace.fail.cpp new file mode 100644 index 00000000000..c61bb2e12e8 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.req.namespace/namespace.fail.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// REQUIRES: c++98 || c++03 || c++11 || c++14 + +// <filesystem> + +// namespace std::filesystem + +#include <filesystem> +#include "test_macros.h" + +using namespace std::filesystem; + +#if TEST_STD_VER >= 11 +// expected-error@-3 {{no namespace named 'filesystem' in namespace 'std';}} +#else +// expected-error@-5 {{expected namespace name}} +#endif + +int main() { + +} diff --git a/libcxx/test/std/input.output/filesystems/fs.req.namespace/namespace.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.req.namespace/namespace.pass.cpp new file mode 100644 index 00000000000..557de3488a7 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/fs.req.namespace/namespace.pass.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// <filesystem> + +// namespace std::filesystem + +#include <filesystem> +#include <type_traits> + +using namespace std::filesystem; + +int main() { + static_assert(std::is_same< + path, + std::filesystem::path + >::value, ""); +} diff --git a/libcxx/test/std/input.output/filesystems/lit.local.cfg b/libcxx/test/std/input.output/filesystems/lit.local.cfg new file mode 100644 index 00000000000..3d9360431f4 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/lit.local.cfg @@ -0,0 +1,3 @@ +# Disable all of the filesystem tests if the correct feature is not available. +if 'c++filesystem' not in config.available_features: + config.unsupported = True |