1 // Copyright Andrey Semashev 2020.
3 // Distributed under the Boost Software License, Version 1.0.
4 // See http://www.boost.org/LICENSE_1_0.txt
6 // Library home page: http://www.boost.org/libs/filesystem
8 // This test verifies copy operation behavior.
10 #include <boost/filesystem/operations.hpp>
11 #include <boost/filesystem/path.hpp>
12 #include <boost/filesystem/directory.hpp>
13 #include <boost/filesystem/exception.hpp>
14 #include <boost/system/error_code.hpp>
22 #include <boost/throw_exception.hpp>
23 #include <boost/exception/diagnostic_information.hpp>
24 #include <boost/core/lightweight_test.hpp>
26 // on Windows, except for standard libaries known to have wchar_t overloads for
27 // file stream I/O, use path::string() to get a narrow character c_str()
28 #if defined(BOOST_WINDOWS_API) \
29 && (!defined(_CPPLIB_VER) || _CPPLIB_VER < 405) // not Dinkumware || no wide overloads
30 # define BOOST_FILESYSTEM_C_STR string().c_str() // use narrow, since wide not available
31 #else // use the native c_str, which will be narrow on POSIX, wide on Windows
32 # define BOOST_FILESYSTEM_C_STR c_str()
35 namespace fs
= boost::filesystem
;
39 void create_file(fs::path
const& ph
, std::string
const& contents
= std::string())
41 std::ofstream
f(ph
.BOOST_FILESYSTEM_C_STR
, std::ios_base::out
| std::ios_base::trunc
);
43 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create file: " + ph
.string()));
44 if (!contents
.empty())
48 void verify_file(fs::path
const& ph
, std::string
const& expected
)
50 std::ifstream
f(ph
.BOOST_FILESYSTEM_C_STR
);
52 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to open file: " + ph
.string()));
55 BOOST_TEST_EQ(contents
, expected
);
56 if (contents
!= expected
)
58 BOOST_THROW_EXCEPTION(std::runtime_error("verify_file failed: contents \"" + contents
+ "\" != \"" + expected
+ "\" in " + ph
.string()));
62 fs::path
create_tree()
64 fs::path root_dir
= fs::unique_path();
66 fs::create_directory(root_dir
);
67 create_file(root_dir
/ "f1", "f1");
68 create_file(root_dir
/ "f2", "f2");
70 fs::create_directory(root_dir
/ "d1");
71 create_file(root_dir
/ "d1/f1", "d1f1");
73 fs::create_directory(root_dir
/ "d1/d1");
74 create_file(root_dir
/ "d1/d1/f1", "d1d1f1");
76 fs::create_directory(root_dir
/ "d1/d2");
78 fs::create_directory(root_dir
/ "d2");
79 create_file(root_dir
/ "d2/f1", "d2f1");
84 typedef std::set
< fs::path
> directory_tree
;
86 directory_tree
collect_directory_tree(fs::path
const& root_dir
)
88 std::cout
<< "Collecting directory tree in: " << root_dir
<< '\n';
91 fs::recursive_directory_iterator
it(root_dir
, fs::directory_options::skip_permission_denied
| fs::directory_options::follow_directory_symlink
| fs::directory_options::skip_dangling_symlinks
), end
;
94 fs::path p
= fs::relative(it
->path(), root_dir
);
95 std::cout
<< p
<< '\n';
100 std::cout
<< "done." << std::endl
;
105 void test_copy_file_default(fs::path
const& root_dir
)
107 std::cout
<< "test_copy_file_default" << std::endl
;
109 fs::path target_dir
= fs::unique_path();
110 fs::create_directory(target_dir
);
112 fs::copy(root_dir
/ "f1", target_dir
);
113 fs::copy(root_dir
/ "f2", target_dir
/ "f3");
115 directory_tree tree
= collect_directory_tree(target_dir
);
117 BOOST_TEST_EQ(tree
.size(), 2u);
118 BOOST_TEST(tree
.find("f1") != tree
.end());
119 BOOST_TEST(tree
.find("f3") != tree
.end());
121 verify_file(target_dir
/ "f1", "f1");
122 verify_file(target_dir
/ "f3", "f2");
124 fs::remove_all(target_dir
);
127 void test_copy_dir_default(fs::path
const& root_dir
, bool with_symlinks
)
129 std::cout
<< "test_copy_dir_default" << std::endl
;
131 fs::path target_dir
= fs::unique_path();
133 fs::copy(root_dir
, target_dir
);
135 directory_tree tree
= collect_directory_tree(target_dir
);
137 BOOST_TEST_EQ(tree
.size(), 4u + with_symlinks
);
138 BOOST_TEST(tree
.find("f1") != tree
.end());
139 BOOST_TEST(tree
.find("f2") != tree
.end());
140 BOOST_TEST(tree
.find("d1") != tree
.end());
141 BOOST_TEST(tree
.find("d2") != tree
.end());
144 BOOST_TEST(tree
.find("s1") != tree
.end());
147 verify_file(target_dir
/ "f1", "f1");
148 verify_file(target_dir
/ "f2", "f2");
150 fs::remove_all(target_dir
);
153 void test_copy_dir_default_ec(fs::path
const& root_dir
, bool with_symlinks
)
155 // This test is similar to test_copy_dir_default, but uses an error_code overload of the operation.
156 // Tests for https://github.com/boostorg/filesystem/issues/152 fix.
158 std::cout
<< "test_copy_dir_default_ec" << std::endl
;
160 fs::path target_dir
= fs::unique_path();
162 boost::system::error_code ec
;
163 fs::copy(root_dir
, target_dir
, ec
);
166 directory_tree tree
= collect_directory_tree(target_dir
);
168 BOOST_TEST_EQ(tree
.size(), 4u + with_symlinks
);
169 BOOST_TEST(tree
.find("f1") != tree
.end());
170 BOOST_TEST(tree
.find("f2") != tree
.end());
171 BOOST_TEST(tree
.find("d1") != tree
.end());
172 BOOST_TEST(tree
.find("d2") != tree
.end());
175 BOOST_TEST(tree
.find("s1") != tree
.end());
178 verify_file(target_dir
/ "f1", "f1");
179 verify_file(target_dir
/ "f2", "f2");
181 fs::remove_all(target_dir
);
184 void test_copy_dir_recursive(fs::path
const& root_dir
)
186 std::cout
<< "test_copy_dir_recursive" << std::endl
;
188 fs::path target_dir
= fs::unique_path();
190 fs::copy(root_dir
, target_dir
, fs::copy_options::recursive
);
192 directory_tree tree
= collect_directory_tree(target_dir
);
194 BOOST_TEST_EQ(tree
.size(), 9u);
195 BOOST_TEST(tree
.find("f1") != tree
.end());
196 BOOST_TEST(tree
.find("f2") != tree
.end());
197 BOOST_TEST(tree
.find("d1") != tree
.end());
198 BOOST_TEST(tree
.find(fs::path("d1") / "f1") != tree
.end());
199 BOOST_TEST(tree
.find(fs::path("d1") / "d1") != tree
.end());
200 BOOST_TEST(tree
.find(fs::path("d1") / "d1" / "f1") != tree
.end());
201 BOOST_TEST(tree
.find(fs::path("d1") / "d2") != tree
.end());
202 BOOST_TEST(tree
.find("d2") != tree
.end());
203 BOOST_TEST(tree
.find(fs::path("d2") / "f1") != tree
.end());
205 verify_file(target_dir
/ "f1", "f1");
206 verify_file(target_dir
/ "f2", "f2");
207 verify_file(target_dir
/ "d1/f1", "d1f1");
208 verify_file(target_dir
/ "d1/d1/f1", "d1d1f1");
209 verify_file(target_dir
/ "d2/f1", "d2f1");
211 fs::remove_all(target_dir
);
214 void test_copy_dir_recursive_tree(fs::path
const& root_dir
)
216 std::cout
<< "test_copy_dir_recursive_tree" << std::endl
;
218 fs::path target_dir
= fs::unique_path();
220 fs::copy(root_dir
, target_dir
, fs::copy_options::recursive
| fs::copy_options::directories_only
);
222 directory_tree tree
= collect_directory_tree(target_dir
);
224 BOOST_TEST_EQ(tree
.size(), 4u);
225 BOOST_TEST(tree
.find("d1") != tree
.end());
226 BOOST_TEST(tree
.find(fs::path("d1") / "d1") != tree
.end());
227 BOOST_TEST(tree
.find(fs::path("d1") / "d2") != tree
.end());
228 BOOST_TEST(tree
.find("d2") != tree
.end());
230 fs::remove_all(target_dir
);
233 void test_copy_file_symlinks(fs::path
const& root_dir
)
235 std::cout
<< "test_copy_file_symlinks" << std::endl
;
237 fs::path target_dir
= fs::unique_path();
238 fs::create_directory(target_dir
);
240 fs::copy(root_dir
/ "f1", target_dir
);
242 fs::path prev_cur_dir
= fs::current_path();
243 fs::current_path(target_dir
);
244 fs::copy(".." / root_dir
/ "f2", "f2", fs::copy_options::create_symlinks
);
245 fs::current_path(prev_cur_dir
);
247 // Copying from a relative path with copy_options::create_symlinks to a directory other than current directory is a non-standard extension
248 fs::copy(target_dir
/ "f1", target_dir
/ "f3", fs::copy_options::create_symlinks
);
250 verify_file(target_dir
/ "f1", "f1");
252 fs::path link_target
= fs::read_symlink(target_dir
/ "f2");
253 if (link_target
!= (".." / root_dir
/ "f2"))
255 BOOST_ERROR("Incorrect f2 symlink in test_copy_file_symlinks");
256 std::cout
<< (target_dir
/ "f2") << " => " << link_target
<< std::endl
;
259 link_target
= fs::read_symlink(target_dir
/ "f3");
260 if (link_target
!= "f1" && link_target
!= (fs::path(".") / "f1"))
262 BOOST_ERROR("Incorrect f3 symlink in test_copy_file_symlinks");
263 std::cout
<< (target_dir
/ "f3") << " => " << link_target
<< std::endl
;
266 fs::remove_all(target_dir
);
269 void test_copy_errors(fs::path
const& root_dir
, bool symlinks_supported
)
271 std::cout
<< "test_copy_errors" << std::endl
;
273 fs::path target_dir
= fs::unique_path();
274 fs::create_directory(target_dir
);
276 BOOST_TEST_THROWS(fs::copy(root_dir
/ "non-existing", target_dir
), fs::filesystem_error
);
278 create_file(target_dir
/ "f1");
280 BOOST_TEST_THROWS(fs::copy(root_dir
/ "f1", target_dir
), fs::filesystem_error
);
281 BOOST_TEST_THROWS(fs::copy(root_dir
/ "f1", target_dir
/ "f1"), fs::filesystem_error
);
282 BOOST_TEST_THROWS(fs::copy(root_dir
/ "d1", target_dir
/ "f1"), fs::filesystem_error
);
284 BOOST_TEST_THROWS(fs::copy(target_dir
, target_dir
), fs::filesystem_error
);
285 BOOST_TEST_THROWS(fs::copy(target_dir
/ "f1", target_dir
/ "f1"), fs::filesystem_error
);
287 if (symlinks_supported
)
289 // Should fail with is_a_directory error code
290 BOOST_TEST_THROWS(fs::copy(root_dir
, target_dir
, fs::copy_options::create_symlinks
), fs::filesystem_error
);
293 fs::remove_all(target_dir
);
302 fs::path root_dir
= create_tree();
304 test_copy_file_default(root_dir
);
305 test_copy_dir_default(root_dir
, false);
306 test_copy_dir_default_ec(root_dir
, false);
307 test_copy_dir_recursive(root_dir
);
308 test_copy_dir_recursive_tree(root_dir
);
310 bool symlinks_supported
= false;
313 fs::create_symlink("f1", root_dir
/ "s1");
314 symlinks_supported
= true;
316 " *** For information only ***\n"
317 " create_symlink() attempt succeeded" << std::endl
;
319 catch (fs::filesystem_error
& e
)
322 " *** For information only ***\n"
323 " create_symlink() attempt failed\n"
324 " filesystem_error.what() reports: " << e
.what() << "\n"
325 " create_symlink() may not be supported on this operating system or file system" << std::endl
;
328 if (symlinks_supported
)
330 test_copy_dir_default(root_dir
, true);
331 test_copy_file_symlinks(root_dir
);
334 test_copy_errors(root_dir
, symlinks_supported
);
336 fs::remove_all(root_dir
);
338 return boost::report_errors();
340 catch (std::exception
& e
)
342 std::cout
<< "FAIL, exception caught: " << boost::diagnostic_information(e
) << std::endl
;