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// UNSUPPORTED: no-filesystem
12// UNSUPPORTED: availability-filesystem-missing
13
14// <filesystem>
15
16// void rename(const path& old_p, const path& new_p);
17// void rename(const path& old_p, const path& new_p, error_code& ec) noexcept;
18
19#include <filesystem>
20
21#include "test_macros.h"
22#include "filesystem_test_helper.h"
23namespace fs = std::filesystem;
24using namespace fs;
25
26static void test_signatures()
27{
28 const path p; ((void)p);
29 std::error_code ec; ((void)ec);
30 ASSERT_SAME_TYPE(decltype(fs::rename(p, p)), void);
31 ASSERT_SAME_TYPE(decltype(fs::rename(p, p, ec)), void);
32
33 ASSERT_NOT_NOEXCEPT(fs::rename(from: p, to: p));
34 ASSERT_NOEXCEPT(fs::rename(from: p, to: p, ec&: ec));
35}
36
37static void test_error_reporting()
38{
39 auto checkThrow = [](path const& f, path const& t, const std::error_code& ec)
40 {
41#ifndef TEST_HAS_NO_EXCEPTIONS
42 try {
43 fs::rename(from: f, to: t);
44 return false;
45 } catch (filesystem_error const& err) {
46 return err.path1() == f
47 && err.path2() == t
48 && err.code() == ec;
49 }
50#else
51 ((void)f); ((void)t); ((void)ec);
52 return true;
53#endif
54 };
55 scoped_test_env env;
56 const path dne = env.make_env_path("dne");
57 const path file = env.create_file("file1", 42);
58 const path dir = env.create_dir("dir1");
59 struct TestCase {
60 path from;
61 path to;
62 } cases[] = {
63 {.from: dne, .to: dne},
64 {.from: file, .to: dir},
65#ifndef _WIN32
66 // The spec doesn't say that this case must be an error; fs.op.rename
67 // note 1.2.1 says that a file may be overwritten by a rename.
68 // On Windows, with rename() implemented with MoveFileExW, overwriting
69 // a file with a directory is not an error.
70 {.from: dir, .to: file},
71#endif
72 };
73 for (auto& TC : cases) {
74 auto from_before = status(TC.from);
75 auto to_before = status(TC.to);
76 std::error_code ec;
77 rename(TC.from, TC.to, ec);
78 assert(ec);
79 assert(from_before.type() == status(TC.from).type());
80 assert(to_before.type() == status(TC.to).type());
81 assert(checkThrow(TC.from, TC.to, ec));
82 }
83}
84
85static void basic_rename_test()
86{
87 scoped_test_env env;
88
89 const std::error_code set_ec = std::make_error_code(e: std::errc::address_in_use);
90 const path file = env.create_file("file1", 42);
91 { // same file
92 std::error_code ec = set_ec;
93 rename(from: file, to: file, ec&: ec);
94 assert(!ec);
95 assert(is_regular_file(p: file));
96 assert(file_size(p: file) == 42);
97 }
98 const path sym = env.create_symlink(file, "sym");
99 { // file -> symlink
100 std::error_code ec = set_ec;
101 rename(from: file, to: sym, ec&: ec);
102 assert(!ec);
103 assert(!exists(p: file));
104 assert(is_regular_file(s: symlink_status(p: sym)));
105 assert(file_size(p: sym) == 42);
106 }
107 const path file2 = env.create_file("file2", 42);
108 const path file3 = env.create_file("file3", 100);
109 { // file -> file
110 std::error_code ec = set_ec;
111 rename(from: file2, to: file3, ec&: ec);
112 assert(!ec);
113 assert(!exists(p: file2));
114 assert(is_regular_file(p: file3));
115 assert(file_size(p: file3) == 42);
116 }
117 const path dne = env.make_env_path("dne");
118 const path bad_sym = env.create_symlink(dne, "bad_sym");
119 const path bad_sym_dest = env.make_env_path("bad_sym2");
120 { // bad-symlink
121 std::error_code ec = set_ec;
122 rename(from: bad_sym, to: bad_sym_dest, ec&: ec);
123 assert(!ec);
124 assert(!exists(s: symlink_status(p: bad_sym)));
125 assert(is_symlink(p: bad_sym_dest));
126 assert(read_symlink(p: bad_sym_dest) == dne);
127 }
128}
129
130static void basic_rename_dir_test()
131{
132 static_test_env env;
133 const std::error_code set_ec = std::make_error_code(e: std::errc::address_in_use);
134 const path new_dir = env.makePath("new_dir");
135 { // dir -> dir (with contents)
136 std::error_code ec = set_ec;
137 rename(env.Dir, new_dir, ec);
138 assert(!ec);
139 assert(!exists(env.Dir));
140 assert(is_directory(p: new_dir));
141 assert(exists(p: new_dir / "file1"));
142 }
143#ifdef _WIN32
144 // On Windows, renaming a directory over a file isn't an error (this
145 // case is skipped in test_error_reporting above).
146 { // dir -> file
147 std::error_code ec = set_ec;
148 rename(new_dir, env.NonEmptyFile, ec);
149 assert(!ec);
150 assert(!exists(new_dir));
151 assert(is_directory(env.NonEmptyFile));
152 assert(exists(env.NonEmptyFile / "file1"));
153 }
154#endif
155}
156
157int main(int, char**) {
158 test_signatures();
159 test_error_reporting();
160 basic_rename_test();
161 basic_rename_dir_test();
162
163 return 0;
164}
165

source code of libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.rename/rename.pass.cpp