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