1 /**
2 * Copyright © 2020 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "temporary_file.hpp"
17
18 #include <filesystem>
19 #include <fstream>
20 #include <string>
21 #include <utility>
22
23 #include <gtest/gtest.h>
24
25 using namespace phosphor::power::util;
26 namespace fs = std::filesystem;
27
28 /**
29 * Modify the specified file so that fs::remove() fails with an exception.
30 *
31 * The file will be renamed and can be restored by calling makeFileRemovable().
32 *
33 * @param path path to the file
34 */
makeFileUnRemovable(const fs::path & path)35 inline void makeFileUnRemovable(const fs::path& path)
36 {
37 // Rename the file to save its contents
38 fs::path savePath{path.native() + ".save"};
39 fs::rename(path, savePath);
40
41 // Create a directory at the original file path
42 fs::create_directory(path);
43
44 // Create a file within the directory. fs::remove() will throw an exception
45 // if the path is a non-empty directory.
46 std::ofstream childFile{path / "childFile"};
47 }
48
49 /**
50 * Modify the specified file so that fs::remove() can successfully delete it.
51 *
52 * Undo the modifications from an earlier call to makeFileUnRemovable().
53 *
54 * @param path path to the file
55 */
makeFileRemovable(const fs::path & path)56 inline void makeFileRemovable(const fs::path& path)
57 {
58 // makeFileUnRemovable() creates a directory at the file path. Remove the
59 // directory and all of its contents.
60 fs::remove_all(path);
61
62 // Rename the file back to the original path to restore its contents
63 fs::path savePath{path.native() + ".save"};
64 fs::rename(savePath, path);
65 }
66
TEST(TemporaryFileTests,DefaultConstructor)67 TEST(TemporaryFileTests, DefaultConstructor)
68 {
69 TemporaryFile file{};
70
71 fs::path path = file.getPath();
72 EXPECT_FALSE(path.empty());
73 EXPECT_TRUE(fs::exists(path));
74 EXPECT_TRUE(fs::is_regular_file(path));
75
76 fs::path parentDir = path.parent_path();
77 EXPECT_EQ(parentDir, "/tmp");
78
79 std::string fileName = path.filename();
80 std::string namePrefix = "phosphor-power-";
81 EXPECT_EQ(fileName.compare(0, namePrefix.size(), namePrefix), 0);
82 }
83
TEST(TemporaryFileTests,MoveConstructor)84 TEST(TemporaryFileTests, MoveConstructor)
85 {
86 // Create first TemporaryFile object and verify temporary file exists
87 TemporaryFile file1{};
88 EXPECT_FALSE(file1.getPath().empty());
89 EXPECT_TRUE(fs::exists(file1.getPath()));
90
91 // Save path to temporary file
92 fs::path path = file1.getPath();
93
94 // Create second TemporaryFile object by moving first object
95 TemporaryFile file2{std::move(file1)};
96
97 // Verify first object now has an empty path
98 EXPECT_TRUE(file1.getPath().empty());
99
100 // Verify second object now owns same temporary file and file exists
101 EXPECT_EQ(file2.getPath(), path);
102 EXPECT_TRUE(fs::exists(file2.getPath()));
103 }
104
TEST(TemporaryFileTests,MoveAssignmentOperator)105 TEST(TemporaryFileTests, MoveAssignmentOperator)
106 {
107 // Test where works: TemporaryFile object is moved
108 {
109 // Create first TemporaryFile object and verify temporary file exists
110 TemporaryFile file1{};
111 EXPECT_FALSE(file1.getPath().empty());
112 EXPECT_TRUE(fs::exists(file1.getPath()));
113
114 // Save path to first temporary file
115 fs::path path1 = file1.getPath();
116
117 // Create second TemporaryFile object and verify temporary file exists
118 TemporaryFile file2{};
119 EXPECT_FALSE(file2.getPath().empty());
120 EXPECT_TRUE(fs::exists(file2.getPath()));
121
122 // Save path to second temporary file
123 fs::path path2 = file2.getPath();
124
125 // Verify temporary files are different
126 EXPECT_NE(path1, path2);
127
128 // Move first object into the second
129 file2 = std::move(file1);
130
131 // Verify first object now has an empty path
132 EXPECT_TRUE(file1.getPath().empty());
133
134 // Verify second object now owns first temporary file and file exists
135 EXPECT_EQ(file2.getPath(), path1);
136 EXPECT_TRUE(fs::exists(path1));
137
138 // Verify second temporary file was deleted
139 EXPECT_FALSE(fs::exists(path2));
140 }
141
142 // Test where does nothing: TemporaryFile object is moved into itself
143 {
144 // Create TemporaryFile object and verify temporary file exists
145 TemporaryFile file{};
146 EXPECT_FALSE(file.getPath().empty());
147 EXPECT_TRUE(fs::exists(file.getPath()));
148
149 // Save path to temporary file
150 fs::path path = file.getPath();
151
152 // Try to move object into itself; should do nothing
153 file = static_cast<TemporaryFile&&>(file);
154
155 // Verify object still owns same temporary file and file exists
156 EXPECT_EQ(file.getPath(), path);
157 EXPECT_TRUE(fs::exists(path));
158 }
159
160 // Test where fails: Cannot delete temporary file
161 {
162 // Create first TemporaryFile object and verify temporary file exists
163 TemporaryFile file1{};
164 EXPECT_FALSE(file1.getPath().empty());
165 EXPECT_TRUE(fs::exists(file1.getPath()));
166
167 // Save path to first temporary file
168 fs::path path1 = file1.getPath();
169
170 // Create second TemporaryFile object and verify temporary file exists
171 TemporaryFile file2{};
172 EXPECT_FALSE(file2.getPath().empty());
173 EXPECT_TRUE(fs::exists(file2.getPath()));
174
175 // Save path to second temporary file
176 fs::path path2 = file2.getPath();
177
178 // Verify temporary files are different
179 EXPECT_NE(path1, path2);
180
181 // Make second temporary file unremoveable
182 makeFileUnRemovable(path2);
183
184 try
185 {
186 // Try to move first object into the second; should throw exception
187 file2 = std::move(file1);
188 ADD_FAILURE() << "Should not have reached this line.";
189 }
190 catch (const std::exception& e)
191 {
192 // This is expected. Exception message will vary.
193 }
194
195 // Verify first object has not changed and first temporary file exists
196 EXPECT_EQ(file1.getPath(), path1);
197 EXPECT_TRUE(fs::exists(path1));
198
199 // Verify second object has not changed and second temporary file exists
200 EXPECT_EQ(file2.getPath(), path2);
201 EXPECT_TRUE(fs::exists(path2));
202
203 // Make second temporary file removeable so destructor can delete it
204 makeFileRemovable(path2);
205 }
206 }
207
TEST(TemporaryFileTests,Destructor)208 TEST(TemporaryFileTests, Destructor)
209 {
210 // Test where works: Temporary file is deleted
211 {
212 fs::path path{};
213 {
214 TemporaryFile file{};
215 path = file.getPath();
216 EXPECT_TRUE(fs::exists(path));
217 }
218 EXPECT_FALSE(fs::exists(path));
219 }
220
221 // Test where works: Temporary file was already deleted
222 {
223 fs::path path{};
224 {
225 TemporaryFile file{};
226 path = file.getPath();
227 EXPECT_TRUE(fs::exists(path));
228 file.remove();
229 EXPECT_FALSE(fs::exists(path));
230 }
231 EXPECT_FALSE(fs::exists(path));
232 }
233
234 // Test where fails: Cannot delete temporary file: No exception thrown
235 {
236 fs::path path{};
237 try
238 {
239 TemporaryFile file{};
240 path = file.getPath();
241 EXPECT_TRUE(fs::exists(path));
242 makeFileUnRemovable(path);
243 }
244 catch (...)
245 {
246 ADD_FAILURE() << "Should not have caught exception.";
247 }
248
249 // Temporary file should still exist
250 EXPECT_TRUE(fs::exists(path));
251
252 // Make file removable and delete it
253 makeFileRemovable(path);
254 fs::remove(path);
255 }
256 }
257
TEST(TemporaryFileTests,Remove)258 TEST(TemporaryFileTests, Remove)
259 {
260 // Test where works
261 {
262 // Create TemporaryFile object and verify temporary file exists
263 TemporaryFile file{};
264 EXPECT_FALSE(file.getPath().empty());
265 EXPECT_TRUE(fs::exists(file.getPath()));
266
267 // Save path to temporary file
268 fs::path path = file.getPath();
269
270 // Delete temporary file
271 file.remove();
272
273 // Verify path is cleared and file does not exist
274 EXPECT_TRUE(file.getPath().empty());
275 EXPECT_FALSE(fs::exists(path));
276
277 // Delete temporary file again; should do nothing
278 file.remove();
279 EXPECT_TRUE(file.getPath().empty());
280 EXPECT_FALSE(fs::exists(path));
281 }
282
283 // Test where fails
284 {
285 // Create TemporaryFile object and verify temporary file exists
286 TemporaryFile file{};
287 EXPECT_FALSE(file.getPath().empty());
288 EXPECT_TRUE(fs::exists(file.getPath()));
289
290 // Make file unremovable
291 makeFileUnRemovable(file.getPath());
292
293 try
294 {
295 // Try to delete temporary file; should fail with exception
296 file.remove();
297 ADD_FAILURE() << "Should not have reached this line.";
298 }
299 catch (const std::exception& e)
300 {
301 // This is expected. Exception message will vary.
302 }
303
304 // Make file removable again so it will be deleted by the destructor
305 makeFileRemovable(file.getPath());
306 }
307 }
308
TEST(TemporaryFileTests,GetPath)309 TEST(TemporaryFileTests, GetPath)
310 {
311 TemporaryFile file{};
312 EXPECT_FALSE(file.getPath().empty());
313 EXPECT_EQ(file.getPath().parent_path(), "/tmp");
314 EXPECT_TRUE(fs::exists(file.getPath()));
315 }
316