1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // REQUIRES: can-create-symlinks
10 // UNSUPPORTED: c++03, c++11, c++14
11 
12 // <filesystem>
13 
14 // class directory_entry
15 
16 // directory_entry& operator=(directory_entry const&) = default;
17 // directory_entry& operator=(directory_entry&&) noexcept = default;
18 // void assign(path const&);
19 // void replace_filename(path const&);
20 
21 #include <filesystem>
22 #include <type_traits>
23 #include <cassert>
24 
25 #include "test_macros.h"
26 #include "filesystem_test_helper.h"
27 namespace fs = std::filesystem;
28 
test_path_assign_method()29 static void test_path_assign_method() {
30   using namespace fs;
31   const path p("foo/bar/baz");
32   const path p2("abc");
33   directory_entry e(p);
34   {
35     static_assert(std::is_same<decltype(e.assign(p)), void>::value,
36                   "return type should be void");
37     static_assert(noexcept(e.assign(p)) == false,
38                   "operation must not be noexcept");
39   }
40   {
41     assert(e.path() == p);
42     e.assign(p2);
43     assert(e.path() == p2 && e.path() != p);
44     e.assign(p);
45     assert(e.path() == p && e.path() != p2);
46   }
47 }
48 
test_path_assign_ec_method()49 static void test_path_assign_ec_method() {
50   using namespace fs;
51   const path p("foo/bar/baz");
52   const path p2("abc");
53   {
54     std::error_code ec;
55     directory_entry e(p);
56     static_assert(std::is_same<decltype(e.assign(p, ec)), void>::value,
57                   "return type should be void");
58     static_assert(noexcept(e.assign(p, ec)) == false,
59                   "operation must not be noexcept");
60   }
61   {
62     directory_entry ent(p);
63     std::error_code ec = GetTestEC();
64     ent.assign(p2, ec);
65     assert(ErrorIs(ec, std::errc::no_such_file_or_directory));
66     assert(ent.path() == p2);
67   }
68 }
69 
test_assign_calls_refresh()70 static void test_assign_calls_refresh() {
71   using namespace fs;
72   scoped_test_env env;
73   const path dir = env.create_dir("dir");
74   const path file = env.create_file("dir/file", 42);
75   const path sym = env.create_symlink("dir/file", "sym");
76 
77   {
78     directory_entry ent;
79     ent.assign(file);
80 
81     // removing the file demonstrates that the values where cached previously.
82     LIBCPP_ONLY(remove(file));
83 
84     assert(ent.is_regular_file());
85   }
86   env.create_file("dir/file", 101);
87   {
88     directory_entry ent;
89     ent.assign(sym);
90 
91     LIBCPP_ONLY(remove(file));
92     LIBCPP_ONLY(remove(sym));
93 
94     assert(ent.is_symlink());
95     assert(ent.is_regular_file());
96   }
97 }
98 
test_assign_propagates_error()99 static void test_assign_propagates_error() {
100   using namespace fs;
101   scoped_test_env env;
102 #ifdef _WIN32
103   // Windows doesn't support setting perms::none to trigger failures
104   // reading directories; test using a special inaccessible directory
105   // instead.
106   const path dir = GetWindowsInaccessibleDir();
107   if (dir.empty())
108     return;
109   const path file = dir / "inaccessible_file";
110   // We can't create files in the inaccessible directory, so this doesn't
111   // test exactly the same as the code below.
112   const path sym_out_of_dir = env.create_symlink(file, "sym");
113   {
114     directory_entry ent;
115     std::error_code ec = GetTestEC();
116     ent.assign(file, ec);
117     assert(ErrorIs(ec, std::errc::no_such_file_or_directory));
118   }
119 #else
120   const path dir = env.create_dir("dir");
121   const path file = env.create_file("dir/file", 42);
122   const path sym_out_of_dir = env.create_symlink("dir/file", "sym");
123   const path file_out_of_dir = env.create_file("file1");
124   const path sym_in_dir = env.create_symlink("file1", "dir/sym1");
125 
126   permissions(dir, perms::none);
127 
128   {
129     directory_entry ent;
130     std::error_code ec = GetTestEC();
131     ent.assign(file, ec);
132     assert(ErrorIs(ec, std::errc::permission_denied));
133   }
134   {
135     directory_entry ent;
136     std::error_code ec = GetTestEC();
137     ent.assign(sym_in_dir, ec);
138     assert(ErrorIs(ec, std::errc::permission_denied));
139   }
140 #endif
141   {
142     directory_entry ent;
143     std::error_code ec = GetTestEC();
144     ent.assign(sym_out_of_dir, ec);
145     assert(!ec);
146   }
147 }
148 
main(int,char **)149 int main(int, char**) {
150   test_path_assign_method();
151   test_path_assign_ec_method();
152   test_assign_calls_refresh();
153   test_assign_propagates_error();
154 
155   return 0;
156 }
157