/** * Copyright © 2020 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "temporary_file.hpp" #include #include #include #include #include using namespace phosphor::power::util; namespace fs = std::filesystem; /** * Modify the specified file so that fs::remove() fails with an exception. * * The file will be renamed and can be restored by calling makeFileRemovable(). * * @param path path to the file */ inline void makeFileUnRemovable(const fs::path& path) { // Rename the file to save its contents fs::path savePath{path.native() + ".save"}; fs::rename(path, savePath); // Create a directory at the original file path fs::create_directory(path); // Create a file within the directory. fs::remove() will throw an exception // if the path is a non-empty directory. std::ofstream childFile{path / "childFile"}; } /** * Modify the specified file so that fs::remove() can successfully delete it. * * Undo the modifications from an earlier call to makeFileUnRemovable(). * * @param path path to the file */ inline void makeFileRemovable(const fs::path& path) { // makeFileUnRemovable() creates a directory at the file path. Remove the // directory and all of its contents. fs::remove_all(path); // Rename the file back to the original path to restore its contents fs::path savePath{path.native() + ".save"}; fs::rename(savePath, path); } TEST(TemporaryFileTests, DefaultConstructor) { TemporaryFile file{}; fs::path path = file.getPath(); EXPECT_FALSE(path.empty()); EXPECT_TRUE(fs::exists(path)); EXPECT_TRUE(fs::is_regular_file(path)); fs::path parentDir = path.parent_path(); EXPECT_EQ(parentDir, "/tmp"); std::string fileName = path.filename(); std::string namePrefix = "phosphor-power-"; EXPECT_EQ(fileName.compare(0, namePrefix.size(), namePrefix), 0); } TEST(TemporaryFileTests, MoveConstructor) { // Create first TemporaryFile object and verify temporary file exists TemporaryFile file1{}; EXPECT_FALSE(file1.getPath().empty()); EXPECT_TRUE(fs::exists(file1.getPath())); // Save path to temporary file fs::path path = file1.getPath(); // Create second TemporaryFile object by moving first object TemporaryFile file2{std::move(file1)}; // Verify first object now has an empty path EXPECT_TRUE(file1.getPath().empty()); // Verify second object now owns same temporary file and file exists EXPECT_EQ(file2.getPath(), path); EXPECT_TRUE(fs::exists(file2.getPath())); } TEST(TemporaryFileTests, MoveAssignmentOperator) { // Test where works: TemporaryFile object is moved { // Create first TemporaryFile object and verify temporary file exists TemporaryFile file1{}; EXPECT_FALSE(file1.getPath().empty()); EXPECT_TRUE(fs::exists(file1.getPath())); // Save path to first temporary file fs::path path1 = file1.getPath(); // Create second TemporaryFile object and verify temporary file exists TemporaryFile file2{}; EXPECT_FALSE(file2.getPath().empty()); EXPECT_TRUE(fs::exists(file2.getPath())); // Save path to second temporary file fs::path path2 = file2.getPath(); // Verify temporary files are different EXPECT_NE(path1, path2); // Move first object into the second file2 = std::move(file1); // Verify first object now has an empty path EXPECT_TRUE(file1.getPath().empty()); // Verify second object now owns first temporary file and file exists EXPECT_EQ(file2.getPath(), path1); EXPECT_TRUE(fs::exists(path1)); // Verify second temporary file was deleted EXPECT_FALSE(fs::exists(path2)); } // Test where does nothing: TemporaryFile object is moved into itself { // Create TemporaryFile object and verify temporary file exists TemporaryFile file{}; EXPECT_FALSE(file.getPath().empty()); EXPECT_TRUE(fs::exists(file.getPath())); // Save path to temporary file fs::path path = file.getPath(); // Try to move object into itself; should do nothing file = static_cast(file); // Verify object still owns same temporary file and file exists EXPECT_EQ(file.getPath(), path); EXPECT_TRUE(fs::exists(path)); } // Test where fails: Cannot delete temporary file { // Create first TemporaryFile object and verify temporary file exists TemporaryFile file1{}; EXPECT_FALSE(file1.getPath().empty()); EXPECT_TRUE(fs::exists(file1.getPath())); // Save path to first temporary file fs::path path1 = file1.getPath(); // Create second TemporaryFile object and verify temporary file exists TemporaryFile file2{}; EXPECT_FALSE(file2.getPath().empty()); EXPECT_TRUE(fs::exists(file2.getPath())); // Save path to second temporary file fs::path path2 = file2.getPath(); // Verify temporary files are different EXPECT_NE(path1, path2); // Make second temporary file unremoveable makeFileUnRemovable(path2); try { // Try to move first object into the second; should throw exception file2 = std::move(file1); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { // This is expected. Exception message will vary. } // Verify first object has not changed and first temporary file exists EXPECT_EQ(file1.getPath(), path1); EXPECT_TRUE(fs::exists(path1)); // Verify second object has not changed and second temporary file exists EXPECT_EQ(file2.getPath(), path2); EXPECT_TRUE(fs::exists(path2)); // Make second temporary file removeable so destructor can delete it makeFileRemovable(path2); } } TEST(TemporaryFileTests, Destructor) { // Test where works: Temporary file is deleted { fs::path path{}; { TemporaryFile file{}; path = file.getPath(); EXPECT_TRUE(fs::exists(path)); } EXPECT_FALSE(fs::exists(path)); } // Test where works: Temporary file was already deleted { fs::path path{}; { TemporaryFile file{}; path = file.getPath(); EXPECT_TRUE(fs::exists(path)); file.remove(); EXPECT_FALSE(fs::exists(path)); } EXPECT_FALSE(fs::exists(path)); } // Test where fails: Cannot delete temporary file: No exception thrown { fs::path path{}; try { TemporaryFile file{}; path = file.getPath(); EXPECT_TRUE(fs::exists(path)); makeFileUnRemovable(path); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } // Temporary file should still exist EXPECT_TRUE(fs::exists(path)); // Make file removable and delete it makeFileRemovable(path); fs::remove(path); } } TEST(TemporaryFileTests, Remove) { // Test where works { // Create TemporaryFile object and verify temporary file exists TemporaryFile file{}; EXPECT_FALSE(file.getPath().empty()); EXPECT_TRUE(fs::exists(file.getPath())); // Save path to temporary file fs::path path = file.getPath(); // Delete temporary file file.remove(); // Verify path is cleared and file does not exist EXPECT_TRUE(file.getPath().empty()); EXPECT_FALSE(fs::exists(path)); // Delete temporary file again; should do nothing file.remove(); EXPECT_TRUE(file.getPath().empty()); EXPECT_FALSE(fs::exists(path)); } // Test where fails { // Create TemporaryFile object and verify temporary file exists TemporaryFile file{}; EXPECT_FALSE(file.getPath().empty()); EXPECT_TRUE(fs::exists(file.getPath())); // Make file unremovable makeFileUnRemovable(file.getPath()); try { // Try to delete temporary file; should fail with exception file.remove(); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { // This is expected. Exception message will vary. } // Make file removable again so it will be deleted by the destructor makeFileRemovable(file.getPath()); } } TEST(TemporaryFileTests, GetPath) { TemporaryFile file{}; EXPECT_FALSE(file.getPath().empty()); EXPECT_EQ(file.getPath().parent_path(), "/tmp"); EXPECT_TRUE(fs::exists(file.getPath())); }