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