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