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