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 */ 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 */ 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 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 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 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 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 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 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