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