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         // Try to move object into itself; should do nothing
153         file = static_cast<TemporaryFile&&>(file);
154 
155         // Verify object still owns same temporary file and file exists
156         EXPECT_EQ(file.getPath(), path);
157         EXPECT_TRUE(fs::exists(path));
158     }
159 
160     // Test where fails: Cannot delete temporary file
161     {
162         // Create first TemporaryFile object and verify temporary file exists
163         TemporaryFile file1{};
164         EXPECT_FALSE(file1.getPath().empty());
165         EXPECT_TRUE(fs::exists(file1.getPath()));
166 
167         // Save path to first temporary file
168         fs::path path1 = file1.getPath();
169 
170         // Create second TemporaryFile object and verify temporary file exists
171         TemporaryFile file2{};
172         EXPECT_FALSE(file2.getPath().empty());
173         EXPECT_TRUE(fs::exists(file2.getPath()));
174 
175         // Save path to second temporary file
176         fs::path path2 = file2.getPath();
177 
178         // Verify temporary files are different
179         EXPECT_NE(path1, path2);
180 
181         // Make second temporary file unremoveable
182         makeFileUnRemovable(path2);
183 
184         try
185         {
186             // Try to move first object into the second; should throw exception
187             file2 = std::move(file1);
188             ADD_FAILURE() << "Should not have reached this line.";
189         }
190         catch (const std::exception& e)
191         {
192             // This is expected.  Exception message will vary.
193         }
194 
195         // Verify first object has not changed and first temporary file exists
196         EXPECT_EQ(file1.getPath(), path1);
197         EXPECT_TRUE(fs::exists(path1));
198 
199         // Verify second object has not changed and second temporary file exists
200         EXPECT_EQ(file2.getPath(), path2);
201         EXPECT_TRUE(fs::exists(path2));
202 
203         // Make second temporary file removeable so destructor can delete it
204         makeFileRemovable(path2);
205     }
206 }
207 
TEST(TemporaryFileTests,Destructor)208 TEST(TemporaryFileTests, Destructor)
209 {
210     // Test where works: Temporary file is deleted
211     {
212         fs::path path{};
213         {
214             TemporaryFile file{};
215             path = file.getPath();
216             EXPECT_TRUE(fs::exists(path));
217         }
218         EXPECT_FALSE(fs::exists(path));
219     }
220 
221     // Test where works: Temporary file was already deleted
222     {
223         fs::path path{};
224         {
225             TemporaryFile file{};
226             path = file.getPath();
227             EXPECT_TRUE(fs::exists(path));
228             file.remove();
229             EXPECT_FALSE(fs::exists(path));
230         }
231         EXPECT_FALSE(fs::exists(path));
232     }
233 
234     // Test where fails: Cannot delete temporary file: No exception thrown
235     {
236         fs::path path{};
237         try
238         {
239             TemporaryFile file{};
240             path = file.getPath();
241             EXPECT_TRUE(fs::exists(path));
242             makeFileUnRemovable(path);
243         }
244         catch (...)
245         {
246             ADD_FAILURE() << "Should not have caught exception.";
247         }
248 
249         // Temporary file should still exist
250         EXPECT_TRUE(fs::exists(path));
251 
252         // Make file removable and delete it
253         makeFileRemovable(path);
254         fs::remove(path);
255     }
256 }
257 
TEST(TemporaryFileTests,Remove)258 TEST(TemporaryFileTests, Remove)
259 {
260     // Test where works
261     {
262         // Create TemporaryFile object and verify temporary file exists
263         TemporaryFile file{};
264         EXPECT_FALSE(file.getPath().empty());
265         EXPECT_TRUE(fs::exists(file.getPath()));
266 
267         // Save path to temporary file
268         fs::path path = file.getPath();
269 
270         // Delete temporary file
271         file.remove();
272 
273         // Verify path is cleared and file does not exist
274         EXPECT_TRUE(file.getPath().empty());
275         EXPECT_FALSE(fs::exists(path));
276 
277         // Delete temporary file again; should do nothing
278         file.remove();
279         EXPECT_TRUE(file.getPath().empty());
280         EXPECT_FALSE(fs::exists(path));
281     }
282 
283     // Test where fails
284     {
285         // Create TemporaryFile object and verify temporary file exists
286         TemporaryFile file{};
287         EXPECT_FALSE(file.getPath().empty());
288         EXPECT_TRUE(fs::exists(file.getPath()));
289 
290         // Make file unremovable
291         makeFileUnRemovable(file.getPath());
292 
293         try
294         {
295             // Try to delete temporary file; should fail with exception
296             file.remove();
297             ADD_FAILURE() << "Should not have reached this line.";
298         }
299         catch (const std::exception& e)
300         {
301             // This is expected.  Exception message will vary.
302         }
303 
304         // Make file removable again so it will be deleted by the destructor
305         makeFileRemovable(file.getPath());
306     }
307 }
308 
TEST(TemporaryFileTests,GetPath)309 TEST(TemporaryFileTests, GetPath)
310 {
311     TemporaryFile file{};
312     EXPECT_FALSE(file.getPath().empty());
313     EXPECT_EQ(file.getPath().parent_path(), "/tmp");
314     EXPECT_TRUE(fs::exists(file.getPath()));
315 }
316