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