]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // Copyright Andrey Semashev 2020. |
2 | ||
3 | // Distributed under the Boost Software License, Version 1.0. | |
4 | // See http://www.boost.org/LICENSE_1_0.txt | |
5 | ||
6 | // Library home page: http://www.boost.org/libs/filesystem | |
7 | ||
8 | // This test verifies copy operation behavior. | |
9 | ||
10 | #include <boost/filesystem/operations.hpp> | |
11 | #include <boost/filesystem/path.hpp> | |
12 | #include <boost/filesystem/directory.hpp> | |
13 | #include <boost/filesystem/exception.hpp> | |
1e59de90 | 14 | #include <boost/filesystem/fstream.hpp> // for BOOST_FILESYSTEM_C_STR |
20effc67 TL |
15 | #include <boost/system/error_code.hpp> |
16 | ||
17 | #include <set> | |
18 | #include <string> | |
19 | #include <fstream> | |
20 | #include <iostream> | |
21 | #include <stdexcept> | |
22 | ||
23 | #include <boost/throw_exception.hpp> | |
24 | #include <boost/exception/diagnostic_information.hpp> | |
25 | #include <boost/core/lightweight_test.hpp> | |
26 | ||
20effc67 TL |
27 | namespace fs = boost::filesystem; |
28 | ||
29 | namespace { | |
30 | ||
31 | void create_file(fs::path const& ph, std::string const& contents = std::string()) | |
32 | { | |
1e59de90 | 33 | std::ofstream f(BOOST_FILESYSTEM_C_STR(ph), std::ios_base::out | std::ios_base::trunc); |
20effc67 TL |
34 | if (!f) |
35 | BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create file: " + ph.string())); | |
36 | if (!contents.empty()) | |
37 | f << contents; | |
38 | } | |
39 | ||
40 | void verify_file(fs::path const& ph, std::string const& expected) | |
41 | { | |
1e59de90 | 42 | std::ifstream f(BOOST_FILESYSTEM_C_STR(ph)); |
20effc67 TL |
43 | if (!f) |
44 | BOOST_THROW_EXCEPTION(std::runtime_error("Failed to open file: " + ph.string())); | |
45 | std::string contents; | |
46 | f >> contents; | |
47 | BOOST_TEST_EQ(contents, expected); | |
48 | if (contents != expected) | |
49 | { | |
1e59de90 | 50 | BOOST_THROW_EXCEPTION(std::runtime_error("verify_file failed: contents \"" + contents + "\" != \"" + expected + "\" in " + ph.string())); |
20effc67 TL |
51 | } |
52 | } | |
53 | ||
54 | fs::path create_tree() | |
55 | { | |
56 | fs::path root_dir = fs::unique_path(); | |
57 | ||
58 | fs::create_directory(root_dir); | |
59 | create_file(root_dir / "f1", "f1"); | |
60 | create_file(root_dir / "f2", "f2"); | |
61 | ||
62 | fs::create_directory(root_dir / "d1"); | |
63 | create_file(root_dir / "d1/f1", "d1f1"); | |
64 | ||
65 | fs::create_directory(root_dir / "d1/d1"); | |
66 | create_file(root_dir / "d1/d1/f1", "d1d1f1"); | |
67 | ||
68 | fs::create_directory(root_dir / "d1/d2"); | |
69 | ||
70 | fs::create_directory(root_dir / "d2"); | |
71 | create_file(root_dir / "d2/f1", "d2f1"); | |
72 | ||
73 | return root_dir; | |
74 | } | |
75 | ||
76 | typedef std::set< fs::path > directory_tree; | |
77 | ||
78 | directory_tree collect_directory_tree(fs::path const& root_dir) | |
79 | { | |
80 | std::cout << "Collecting directory tree in: " << root_dir << '\n'; | |
81 | ||
82 | directory_tree tree; | |
1e59de90 TL |
83 | fs::recursive_directory_iterator it(root_dir, fs::directory_options::skip_permission_denied | |
84 | fs::directory_options::follow_directory_symlink | fs::directory_options::skip_dangling_symlinks); | |
85 | fs::recursive_directory_iterator end; | |
20effc67 TL |
86 | while (it != end) |
87 | { | |
88 | fs::path p = fs::relative(it->path(), root_dir); | |
89 | std::cout << p << '\n'; | |
90 | tree.insert(p); | |
91 | ++it; | |
92 | } | |
93 | ||
94 | std::cout << "done." << std::endl; | |
95 | ||
96 | return tree; | |
97 | } | |
98 | ||
99 | void test_copy_file_default(fs::path const& root_dir) | |
100 | { | |
101 | std::cout << "test_copy_file_default" << std::endl; | |
102 | ||
103 | fs::path target_dir = fs::unique_path(); | |
104 | fs::create_directory(target_dir); | |
105 | ||
106 | fs::copy(root_dir / "f1", target_dir); | |
107 | fs::copy(root_dir / "f2", target_dir / "f3"); | |
108 | ||
109 | directory_tree tree = collect_directory_tree(target_dir); | |
110 | ||
111 | BOOST_TEST_EQ(tree.size(), 2u); | |
112 | BOOST_TEST(tree.find("f1") != tree.end()); | |
113 | BOOST_TEST(tree.find("f3") != tree.end()); | |
114 | ||
115 | verify_file(target_dir / "f1", "f1"); | |
116 | verify_file(target_dir / "f3", "f2"); | |
117 | ||
118 | fs::remove_all(target_dir); | |
119 | } | |
120 | ||
121 | void test_copy_dir_default(fs::path const& root_dir, bool with_symlinks) | |
122 | { | |
123 | std::cout << "test_copy_dir_default" << std::endl; | |
124 | ||
125 | fs::path target_dir = fs::unique_path(); | |
126 | ||
127 | fs::copy(root_dir, target_dir); | |
128 | ||
129 | directory_tree tree = collect_directory_tree(target_dir); | |
130 | ||
131 | BOOST_TEST_EQ(tree.size(), 4u + with_symlinks); | |
132 | BOOST_TEST(tree.find("f1") != tree.end()); | |
133 | BOOST_TEST(tree.find("f2") != tree.end()); | |
134 | BOOST_TEST(tree.find("d1") != tree.end()); | |
135 | BOOST_TEST(tree.find("d2") != tree.end()); | |
136 | if (with_symlinks) | |
137 | { | |
138 | BOOST_TEST(tree.find("s1") != tree.end()); | |
139 | } | |
140 | ||
141 | verify_file(target_dir / "f1", "f1"); | |
142 | verify_file(target_dir / "f2", "f2"); | |
143 | ||
144 | fs::remove_all(target_dir); | |
145 | } | |
146 | ||
147 | void test_copy_dir_default_ec(fs::path const& root_dir, bool with_symlinks) | |
148 | { | |
149 | // This test is similar to test_copy_dir_default, but uses an error_code overload of the operation. | |
150 | // Tests for https://github.com/boostorg/filesystem/issues/152 fix. | |
151 | ||
152 | std::cout << "test_copy_dir_default_ec" << std::endl; | |
153 | ||
154 | fs::path target_dir = fs::unique_path(); | |
155 | ||
156 | boost::system::error_code ec; | |
157 | fs::copy(root_dir, target_dir, ec); | |
158 | BOOST_TEST(!ec); | |
159 | ||
160 | directory_tree tree = collect_directory_tree(target_dir); | |
161 | ||
162 | BOOST_TEST_EQ(tree.size(), 4u + with_symlinks); | |
163 | BOOST_TEST(tree.find("f1") != tree.end()); | |
164 | BOOST_TEST(tree.find("f2") != tree.end()); | |
165 | BOOST_TEST(tree.find("d1") != tree.end()); | |
166 | BOOST_TEST(tree.find("d2") != tree.end()); | |
167 | if (with_symlinks) | |
168 | { | |
169 | BOOST_TEST(tree.find("s1") != tree.end()); | |
170 | } | |
171 | ||
172 | verify_file(target_dir / "f1", "f1"); | |
173 | verify_file(target_dir / "f2", "f2"); | |
174 | ||
175 | fs::remove_all(target_dir); | |
176 | } | |
177 | ||
178 | void test_copy_dir_recursive(fs::path const& root_dir) | |
179 | { | |
180 | std::cout << "test_copy_dir_recursive" << std::endl; | |
181 | ||
182 | fs::path target_dir = fs::unique_path(); | |
183 | ||
184 | fs::copy(root_dir, target_dir, fs::copy_options::recursive); | |
185 | ||
186 | directory_tree tree = collect_directory_tree(target_dir); | |
187 | ||
188 | BOOST_TEST_EQ(tree.size(), 9u); | |
189 | BOOST_TEST(tree.find("f1") != tree.end()); | |
190 | BOOST_TEST(tree.find("f2") != tree.end()); | |
191 | BOOST_TEST(tree.find("d1") != tree.end()); | |
192 | BOOST_TEST(tree.find(fs::path("d1") / "f1") != tree.end()); | |
193 | BOOST_TEST(tree.find(fs::path("d1") / "d1") != tree.end()); | |
194 | BOOST_TEST(tree.find(fs::path("d1") / "d1" / "f1") != tree.end()); | |
195 | BOOST_TEST(tree.find(fs::path("d1") / "d2") != tree.end()); | |
196 | BOOST_TEST(tree.find("d2") != tree.end()); | |
197 | BOOST_TEST(tree.find(fs::path("d2") / "f1") != tree.end()); | |
198 | ||
199 | verify_file(target_dir / "f1", "f1"); | |
200 | verify_file(target_dir / "f2", "f2"); | |
201 | verify_file(target_dir / "d1/f1", "d1f1"); | |
202 | verify_file(target_dir / "d1/d1/f1", "d1d1f1"); | |
203 | verify_file(target_dir / "d2/f1", "d2f1"); | |
204 | ||
205 | fs::remove_all(target_dir); | |
206 | } | |
207 | ||
208 | void test_copy_dir_recursive_tree(fs::path const& root_dir) | |
209 | { | |
210 | std::cout << "test_copy_dir_recursive_tree" << std::endl; | |
211 | ||
212 | fs::path target_dir = fs::unique_path(); | |
213 | ||
214 | fs::copy(root_dir, target_dir, fs::copy_options::recursive | fs::copy_options::directories_only); | |
215 | ||
216 | directory_tree tree = collect_directory_tree(target_dir); | |
217 | ||
218 | BOOST_TEST_EQ(tree.size(), 4u); | |
219 | BOOST_TEST(tree.find("d1") != tree.end()); | |
220 | BOOST_TEST(tree.find(fs::path("d1") / "d1") != tree.end()); | |
221 | BOOST_TEST(tree.find(fs::path("d1") / "d2") != tree.end()); | |
222 | BOOST_TEST(tree.find("d2") != tree.end()); | |
223 | ||
224 | fs::remove_all(target_dir); | |
225 | } | |
226 | ||
227 | void test_copy_file_symlinks(fs::path const& root_dir) | |
228 | { | |
229 | std::cout << "test_copy_file_symlinks" << std::endl; | |
230 | ||
231 | fs::path target_dir = fs::unique_path(); | |
232 | fs::create_directory(target_dir); | |
233 | ||
234 | fs::copy(root_dir / "f1", target_dir); | |
235 | ||
236 | fs::path prev_cur_dir = fs::current_path(); | |
237 | fs::current_path(target_dir); | |
238 | fs::copy(".." / root_dir / "f2", "f2", fs::copy_options::create_symlinks); | |
239 | fs::current_path(prev_cur_dir); | |
240 | ||
241 | // Copying from a relative path with copy_options::create_symlinks to a directory other than current directory is a non-standard extension | |
242 | fs::copy(target_dir / "f1", target_dir / "f3", fs::copy_options::create_symlinks); | |
243 | ||
244 | verify_file(target_dir / "f1", "f1"); | |
245 | ||
246 | fs::path link_target = fs::read_symlink(target_dir / "f2"); | |
247 | if (link_target != (".." / root_dir / "f2")) | |
248 | { | |
249 | BOOST_ERROR("Incorrect f2 symlink in test_copy_file_symlinks"); | |
250 | std::cout << (target_dir / "f2") << " => " << link_target << std::endl; | |
251 | } | |
252 | ||
253 | link_target = fs::read_symlink(target_dir / "f3"); | |
254 | if (link_target != "f1" && link_target != (fs::path(".") / "f1")) | |
255 | { | |
256 | BOOST_ERROR("Incorrect f3 symlink in test_copy_file_symlinks"); | |
257 | std::cout << (target_dir / "f3") << " => " << link_target << std::endl; | |
258 | } | |
259 | ||
260 | fs::remove_all(target_dir); | |
261 | } | |
262 | ||
263 | void test_copy_errors(fs::path const& root_dir, bool symlinks_supported) | |
264 | { | |
265 | std::cout << "test_copy_errors" << std::endl; | |
266 | ||
267 | fs::path target_dir = fs::unique_path(); | |
268 | fs::create_directory(target_dir); | |
269 | ||
270 | BOOST_TEST_THROWS(fs::copy(root_dir / "non-existing", target_dir), fs::filesystem_error); | |
271 | ||
272 | create_file(target_dir / "f1"); | |
273 | ||
274 | BOOST_TEST_THROWS(fs::copy(root_dir / "f1", target_dir), fs::filesystem_error); | |
275 | BOOST_TEST_THROWS(fs::copy(root_dir / "f1", target_dir / "f1"), fs::filesystem_error); | |
276 | BOOST_TEST_THROWS(fs::copy(root_dir / "d1", target_dir / "f1"), fs::filesystem_error); | |
277 | ||
278 | BOOST_TEST_THROWS(fs::copy(target_dir, target_dir), fs::filesystem_error); | |
279 | BOOST_TEST_THROWS(fs::copy(target_dir / "f1", target_dir / "f1"), fs::filesystem_error); | |
280 | ||
281 | if (symlinks_supported) | |
282 | { | |
283 | // Should fail with is_a_directory error code | |
284 | BOOST_TEST_THROWS(fs::copy(root_dir, target_dir, fs::copy_options::create_symlinks), fs::filesystem_error); | |
285 | } | |
286 | ||
287 | fs::remove_all(target_dir); | |
288 | } | |
289 | ||
290 | } // namespace | |
291 | ||
292 | int main() | |
293 | { | |
294 | try | |
295 | { | |
296 | fs::path root_dir = create_tree(); | |
297 | ||
298 | test_copy_file_default(root_dir); | |
299 | test_copy_dir_default(root_dir, false); | |
300 | test_copy_dir_default_ec(root_dir, false); | |
301 | test_copy_dir_recursive(root_dir); | |
302 | test_copy_dir_recursive_tree(root_dir); | |
303 | ||
304 | bool symlinks_supported = false; | |
305 | try | |
306 | { | |
307 | fs::create_symlink("f1", root_dir / "s1"); | |
308 | symlinks_supported = true; | |
1e59de90 TL |
309 | std::cout << " *** For information only ***\n" |
310 | " create_symlink() attempt succeeded" | |
311 | << std::endl; | |
20effc67 TL |
312 | } |
313 | catch (fs::filesystem_error& e) | |
314 | { | |
1e59de90 TL |
315 | std::cout << " *** For information only ***\n" |
316 | " create_symlink() attempt failed\n" | |
317 | " filesystem_error.what() reports: " | |
318 | << e.what() << "\n" | |
319 | " create_symlink() may not be supported on this operating system or file system" | |
320 | << std::endl; | |
20effc67 TL |
321 | } |
322 | ||
323 | if (symlinks_supported) | |
324 | { | |
325 | test_copy_dir_default(root_dir, true); | |
326 | test_copy_file_symlinks(root_dir); | |
327 | } | |
328 | ||
329 | test_copy_errors(root_dir, symlinks_supported); | |
330 | ||
331 | fs::remove_all(root_dir); | |
332 | ||
333 | return boost::report_errors(); | |
334 | } | |
335 | catch (std::exception& e) | |
336 | { | |
337 | std::cout << "FAIL, exception caught: " << boost::diagnostic_information(e) << std::endl; | |
338 | return 1; | |
339 | } | |
340 | } |