diff options
author | Eric Fiselier <eric@efcs.ca> | 2018-07-20 01:22:32 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2018-07-20 01:22:32 +0000 |
commit | c16998649e1f55c61158004f461fd629d9b8b7f9 (patch) | |
tree | c1434a61a1da354f770f4ac06994211375a84253 /libcxx/test/std/experimental | |
parent | 40fa4a1a559765ada93268c4d45c54b62779166b (diff) | |
download | bcm5719-llvm-c16998649e1f55c61158004f461fd629d9b8b7f9.tar.gz bcm5719-llvm-c16998649e1f55c61158004f461fd629d9b8b7f9.zip |
[libc++] Implement Directory Entry Caching -- Sort of.
Summary:
This patch implements directory_entry caching *almost* as specified in P0317r1. However, I explicitly chose to deviate from the standard as I'll explain below.
The approach I decided to take is a fully caching one. When `refresh()` is called, the cache is populated by calls to `stat` and `lstat` as needed.
During directory iteration the cache is only populated with the `file_type` as reported by `readdir`.
The cache can be in the following states:
* `_Empty`: There is nothing in the cache (likely due to an error)
* `_IterSymlink`: Created by directory iteration when we walk onto a symlink only the symlink file type is known.
* `_IterNonSymlink`: Created by directory iteration when we walk onto a non-symlink. Both the regular file type and symlink file type are known.
* `_RefreshSymlink` and `_RefreshNonSymlink`: A full cache created by `refresh()`. This case includes dead symlinks.
* `_RefreshSymlinkUnresolved`: A partial cache created by refresh when we fail to resolve the file pointed to by a symlink (likely due to permissions). Symlink attributes are cached, but attributes about the linked entity are not.
As mentioned, this implementation purposefully deviates from the standard. According to some readings of the specification, and the Windows filesystem implementation, the constructors and modifiers which don't pass an `error_code` must throw when the `directory_entry` points to a entity which doesn't exist. or when attribute resolution fails for another reason.
@BillyONeal has proposed a more reasonable set of requirements, where modifiers other than refresh ignore errors. This is the behavior libc++ currently implements, with the expectation some form of the new language will be accepted into the standard.
Some additional semantics which differ from the Windows implementation:
1. `refresh` will not throw when the entry doesn't exist. In this case we can still meet the functions specification, so we don't treat it as an error.
2. We don't clear the path name when a constructor fails via refresh (this will hopefully be changed in the standard as well).
It should be noted that libstdc++'s current implementation has the same behavior as libc++, except for point (2).
If the changes to the specification don't get accepted, we'll be able to make the changes later.
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0317r1.html
Reviewers: mclow.lists, gromer, ldionne, aaron.ballman
Subscribers: BillyONeal, christof, cfe-commits
Differential Revision: https://reviews.llvm.org/D49530
llvm-svn: 337516
Diffstat (limited to 'libcxx/test/std/experimental')
18 files changed, 2160 insertions, 246 deletions
diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.cons.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.cons.pass.cpp deleted file mode 100644 index 8a9a1b5d355..00000000000 --- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.cons.pass.cpp +++ /dev/null @@ -1,96 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 - -// <experimental/filesystem> - -// class directory_entry - -// directory_entry() noexcept = default; -// directory_entry(const directory_entry&) = default; -// directory_entry(directory_entry&&) noexcept = default; -// explicit directory_entry(const path); - -#include "filesystem_include.hpp" -#include <type_traits> -#include <cassert> - - -void test_default_ctor() -{ - 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()); - } -} - - -void test_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); - } - -} - -void test_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. - } -} - -void test_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); - assert(p == e.path()); - } -} - -int main() { - test_default_ctor(); - test_copy_ctor(); - test_move_ctor(); - test_path_ctor(); -} diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.cons/copy.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.cons/copy.pass.cpp new file mode 100644 index 00000000000..f46302938c5 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/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 + +// <experimental/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/experimental/filesystem/class.directory_entry/directory_entry.cons/copy_assign.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.cons/copy_assign.pass.cpp new file mode 100644 index 00000000000..447d40985bc --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/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 + +// <experimental/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/experimental/filesystem/class.directory_entry/directory_entry.cons/default.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.cons/default.pass.cpp new file mode 100644 index 00000000000..dd4543dbb9c --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/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 + +// <experimental/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/experimental/filesystem/class.directory_entry/directory_entry.cons/move.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.cons/move.pass.cpp new file mode 100644 index 00000000000..f22bf1c331c --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/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 + +// <experimental/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/experimental/filesystem/class.directory_entry/directory_entry.cons/move_assign.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.cons/move_assign.pass.cpp new file mode 100644 index 00000000000..abd9bc86542 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/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 + +// <experimental/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/experimental/filesystem/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.cons/path.pass.cpp new file mode 100644 index 00000000000..b6ecd3f5876 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/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 + +// <experimental/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/experimental/filesystem/class.directory_entry/directory_entry.mods.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods.pass.cpp deleted file mode 100644 index 13428db191a..00000000000 --- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods.pass.cpp +++ /dev/null @@ -1,112 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 - -// <experimental/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> - - -void 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); - } -} - - -void 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 - } -} - -void 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"); - } - { - assert(e.path() == p); - e.assign(p2); - assert(e.path() == p2 && e.path() != p); - e.assign(p); - assert(e.path() == p && e.path() != p2); - } -} - -void test_replace_filename_method() -{ - using namespace fs; - const path p("/path/to/foo.exe"); - const path replace("bar.out"); - const path expect("/path/to/bar.out"); - directory_entry e(p); - { - 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"); - } - { - assert(e.path() == p); - e.replace_filename(replace); - assert(e.path() == expect); - } -} - -int main() { - test_copy_assign_operator(); - test_move_assign_operator(); - test_path_assign_method(); - test_replace_filename_method(); -} diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/assign.pass.cpp new file mode 100644 index 00000000000..70f0de3fb4a --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/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 + +// <experimental/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/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp new file mode 100644 index 00000000000..40d2cef1da1 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.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 + +// <experimental/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); + 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); + 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/experimental/filesystem/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp new file mode 100644 index 00000000000..24b5ebf0765 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/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 + +// <experimental/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/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp new file mode 100644 index 00000000000..8048ad2b830 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp @@ -0,0 +1,238 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <experimental/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); + 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); + 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); + 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); + 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); + 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); + 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/experimental/filesystem/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp new file mode 100644 index 00000000000..fbd860823c8 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/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 + +// <experimental/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/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp new file mode 100644 index 00000000000..fcb21c764e0 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp @@ -0,0 +1,237 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <experimental/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 fs::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); + 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); + 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); + 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); + 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); + 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/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp new file mode 100644 index 00000000000..c89e1670988 --- /dev/null +++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp @@ -0,0 +1,210 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <experimental/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); + 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); + 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); + 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); + 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); + 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/experimental/filesystem/class.directory_entry/directory_entry.obs/status.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/status.pass.cpp index f234f0c1a7b..1f15850ce2f 100644 --- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/status.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/status.pass.cpp @@ -21,31 +21,38 @@ #include <cassert> #include "filesystem_test_helper.hpp" +#include "rapid-cxx-test.hpp" -int main() -{ +TEST_SUITE(directory_entry_status_testsuite) + +TEST_CASE(test_basic) { using namespace fs; { - const directory_entry e("foo"); + const fs::directory_entry e("foo"); std::error_code ec; - static_assert(std::is_same<decltype(e.status()), file_status>::value, ""); - static_assert(std::is_same<decltype(e.status(ec)), file_status>::value, ""); + 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, ""); } - auto TestFn = [](path const& p) { + path TestCases[] = {StaticEnv::File, StaticEnv::Dir, StaticEnv::SymlinkToFile, + StaticEnv::DNE}; + for (const auto& p : TestCases) { const directory_entry e(p); - std::error_code pec, eec; + std::error_code pec = GetTestEC(), eec = GetTestEC(1); file_status ps = fs::status(p, pec); file_status es = e.status(eec); - assert(ps.type() == es.type()); - assert(ps.permissions() == es.permissions()); - assert(pec == eec); - }; - { - TestFn(StaticEnv::File); - TestFn(StaticEnv::Dir); - TestFn(StaticEnv::SymlinkToFile); - TestFn(StaticEnv::DNE); + 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/experimental/filesystem/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp index 2ed22df3bd3..ed6aca2c7a7 100644 --- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/symlink_status.pass.cpp @@ -21,8 +21,11 @@ #include <cassert> #include "filesystem_test_helper.hpp" +#include "rapid-cxx-test.hpp" -int main() { +TEST_SUITE(directory_entry_obs_suite) + +TEST_CASE(test_signature) { using namespace fs; { const directory_entry e("foo"); @@ -32,19 +35,24 @@ int main() { static_assert(noexcept(e.symlink_status()) == false, ""); static_assert(noexcept(e.symlink_status(ec)) == true, ""); } - auto TestFn = [](path const& p) { + path TestCases[] = {StaticEnv::File, StaticEnv::Dir, StaticEnv::SymlinkToFile, + StaticEnv::DNE}; + for (const auto& p : TestCases) { const directory_entry e(p); - std::error_code pec, eec; + std::error_code pec = GetTestEC(), eec = GetTestEC(1); file_status ps = fs::symlink_status(p, pec); file_status es = e.symlink_status(eec); - assert(ps.type() == es.type()); - assert(ps.permissions() == es.permissions()); - assert(pec == eec); - }; - { - TestFn(StaticEnv::File); - TestFn(StaticEnv::Dir); - TestFn(StaticEnv::SymlinkToFile); - TestFn(StaticEnv::DNE); + 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/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp index 1f7b87ae8c5..e2b2513ecdb 100644 --- a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp @@ -62,17 +62,22 @@ TEST_CASE(symlink_test_case) TEST_CASE(file_size_error_cases) { - const path testCases[] = { - StaticEnv::Dir, - StaticEnv::SymlinkToDir, - StaticEnv::BadSymlink, - StaticEnv::DNE - }; + 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}}; const uintmax_t expect = static_cast<uintmax_t>(-1); - for (auto& TC : testCases) { - std::error_code ec; - TEST_CHECK(file_size(TC, ec) == expect); - TEST_CHECK(ec); + 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); + TEST_CHECK_THROW_RESULT(filesystem_error, Checker, file_size(TC.p)); } } |