blob: 5f2bb293baff310b32b0c442d50abda163757b40 [file] [log] [blame]
Shawn McCarneyb6f07c92020-09-03 21:49:21 -05001/**
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>
Shawn McCarney5f514442024-01-04 14:03:24 -060019#include <fstream>
Shawn McCarneyb6f07c92020-09-03 21:49:21 -050020#include <string>
21#include <utility>
22
23#include <gtest/gtest.h>
24
Shawn McCarney5f514442024-01-04 14:03:24 -060025using namespace phosphor::power::util;
Shawn McCarneyb6f07c92020-09-03 21:49:21 -050026namespace fs = std::filesystem;
27
Shawn McCarney5f514442024-01-04 14:03:24 -060028/**
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 */
35inline 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 */
56inline 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
Shawn McCarneyb6f07c92020-09-03 21:49:21 -050067TEST(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();
Shawn McCarney5f514442024-01-04 14:03:24 -060080 std::string namePrefix = "phosphor-power-";
Shawn McCarneyb6f07c92020-09-03 21:49:21 -050081 EXPECT_EQ(fileName.compare(0, namePrefix.size(), namePrefix), 0);
82}
83
84TEST(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
105TEST(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
Jayanth Othayothb93d6eb2025-06-08 07:18:10 -0500152// This is undefined behavior in C++, butsuppress the warning
153// to observe how the class handles it
154#ifdef __clang__
155#pragma clang diagnostic push
156#pragma clang diagnostic ignored "-Wself-move"
157#endif
Shawn McCarneyb6f07c92020-09-03 21:49:21 -0500158 // Try to move object into itself; should do nothing
Patrick Williams81e554c2023-05-31 21:56:07 -0500159 file = static_cast<TemporaryFile&&>(file);
Jayanth Othayothb93d6eb2025-06-08 07:18:10 -0500160#ifdef __clang__
161#pragma clang diagnostic pop
162#endif
Shawn McCarneyb6f07c92020-09-03 21:49:21 -0500163
164 // Verify object still owns same temporary file and file exists
165 EXPECT_EQ(file.getPath(), path);
166 EXPECT_TRUE(fs::exists(path));
167 }
168
169 // Test where fails: Cannot delete temporary file
170 {
171 // Create first TemporaryFile object and verify temporary file exists
172 TemporaryFile file1{};
173 EXPECT_FALSE(file1.getPath().empty());
174 EXPECT_TRUE(fs::exists(file1.getPath()));
175
176 // Save path to first temporary file
177 fs::path path1 = file1.getPath();
178
179 // Create second TemporaryFile object and verify temporary file exists
180 TemporaryFile file2{};
181 EXPECT_FALSE(file2.getPath().empty());
182 EXPECT_TRUE(fs::exists(file2.getPath()));
183
184 // Save path to second temporary file
185 fs::path path2 = file2.getPath();
186
187 // Verify temporary files are different
188 EXPECT_NE(path1, path2);
189
190 // Make second temporary file unremoveable
191 makeFileUnRemovable(path2);
192
193 try
194 {
195 // Try to move first object into the second; should throw exception
196 file2 = std::move(file1);
197 ADD_FAILURE() << "Should not have reached this line.";
198 }
199 catch (const std::exception& e)
200 {
201 // This is expected. Exception message will vary.
202 }
203
204 // Verify first object has not changed and first temporary file exists
205 EXPECT_EQ(file1.getPath(), path1);
206 EXPECT_TRUE(fs::exists(path1));
207
208 // Verify second object has not changed and second temporary file exists
209 EXPECT_EQ(file2.getPath(), path2);
210 EXPECT_TRUE(fs::exists(path2));
211
212 // Make second temporary file removeable so destructor can delete it
213 makeFileRemovable(path2);
214 }
215}
216
217TEST(TemporaryFileTests, Destructor)
218{
219 // Test where works: Temporary file is deleted
220 {
221 fs::path path{};
222 {
223 TemporaryFile file{};
224 path = file.getPath();
225 EXPECT_TRUE(fs::exists(path));
226 }
227 EXPECT_FALSE(fs::exists(path));
228 }
229
230 // Test where works: Temporary file was already deleted
231 {
232 fs::path path{};
233 {
234 TemporaryFile file{};
235 path = file.getPath();
236 EXPECT_TRUE(fs::exists(path));
237 file.remove();
238 EXPECT_FALSE(fs::exists(path));
239 }
240 EXPECT_FALSE(fs::exists(path));
241 }
242
243 // Test where fails: Cannot delete temporary file: No exception thrown
244 {
245 fs::path path{};
246 try
247 {
248 TemporaryFile file{};
249 path = file.getPath();
250 EXPECT_TRUE(fs::exists(path));
251 makeFileUnRemovable(path);
252 }
253 catch (...)
254 {
255 ADD_FAILURE() << "Should not have caught exception.";
256 }
257
258 // Temporary file should still exist
259 EXPECT_TRUE(fs::exists(path));
260
261 // Make file removable and delete it
262 makeFileRemovable(path);
263 fs::remove(path);
264 }
265}
266
267TEST(TemporaryFileTests, Remove)
268{
269 // Test where works
270 {
271 // Create TemporaryFile object and verify temporary file exists
272 TemporaryFile file{};
273 EXPECT_FALSE(file.getPath().empty());
274 EXPECT_TRUE(fs::exists(file.getPath()));
275
276 // Save path to temporary file
277 fs::path path = file.getPath();
278
279 // Delete temporary file
280 file.remove();
281
282 // Verify path is cleared and file does not exist
283 EXPECT_TRUE(file.getPath().empty());
284 EXPECT_FALSE(fs::exists(path));
285
286 // Delete temporary file again; should do nothing
287 file.remove();
288 EXPECT_TRUE(file.getPath().empty());
289 EXPECT_FALSE(fs::exists(path));
290 }
291
292 // Test where fails
293 {
294 // Create TemporaryFile object and verify temporary file exists
295 TemporaryFile file{};
296 EXPECT_FALSE(file.getPath().empty());
297 EXPECT_TRUE(fs::exists(file.getPath()));
298
299 // Make file unremovable
300 makeFileUnRemovable(file.getPath());
301
302 try
303 {
304 // Try to delete temporary file; should fail with exception
305 file.remove();
306 ADD_FAILURE() << "Should not have reached this line.";
307 }
308 catch (const std::exception& e)
309 {
310 // This is expected. Exception message will vary.
311 }
312
313 // Make file removable again so it will be deleted by the destructor
314 makeFileRemovable(file.getPath());
315 }
316}
317
318TEST(TemporaryFileTests, GetPath)
319{
320 TemporaryFile file{};
321 EXPECT_FALSE(file.getPath().empty());
322 EXPECT_EQ(file.getPath().parent_path(), "/tmp");
323 EXPECT_TRUE(fs::exists(file.getPath()));
324}