xref: /openbmc/phosphor-power/test/temporary_file_tests.cpp (revision b93d6ebef0b2628874aa7bf3b4464edbcead683f)
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 // 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
158         // Try to move object into itself; should do nothing
159         file = static_cast<TemporaryFile&&>(file);
160 #ifdef __clang__
161 #pragma clang diagnostic pop
162 #endif
163 
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 
TEST(TemporaryFileTests,Destructor)217 TEST(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 
TEST(TemporaryFileTests,Remove)267 TEST(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 
TEST(TemporaryFileTests,GetPath)318 TEST(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 }
325