diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index 3944005d6c..f1e0517644 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -209,13 +209,12 @@ bool CreateDir(const std::string& path) return success; } -// Creates the full path of fullPath returns true on success -bool CreateFullPath(const std::string& fullPath) +bool CreateFullPath(std::string_view fullPath) { DEBUG_LOG_FMT(COMMON, "{}: path {}", __func__, fullPath); std::error_code error; - auto native_path = StringToPath(fullPath); + auto native_path = StringToPath(fullPath).parent_path(); bool success = fs::create_directories(native_path, error); // If the path was not created, check if it was a pre-existing directory if (!success && fs::is_directory(native_path)) diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 37bced5a68..4fd2d950f5 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -143,8 +143,10 @@ u64 GetSize(FILE* f); // Returns true if successful, or path already exists. bool CreateDir(const std::string& filename); -// Creates the full path of fullPath returns true on success -bool CreateFullPath(const std::string& fullPath); +// Creates the full path to the file given in fullPath. +// That is, for path '/a/b/c.bin', creates folders '/a' and '/a/b'. +// Returns true if creation is successful or if the path already exists. +bool CreateFullPath(std::string_view fullPath); enum class IfAbsentBehavior { diff --git a/Source/UnitTests/Common/FileUtilTest.cpp b/Source/UnitTests/Common/FileUtilTest.cpp index 8570c347b4..d361f656f3 100644 --- a/Source/UnitTests/Common/FileUtilTest.cpp +++ b/Source/UnitTests/Common/FileUtilTest.cpp @@ -100,3 +100,50 @@ TEST_F(FileUtilTest, DeleteDir) DeleteDirShouldReturnTrueForInvalidPath(m_invalid_path, behavior); } } + +TEST_F(FileUtilTest, CreateFullPath) +{ + ASSERT_TRUE(!m_directory_path.ends_with('/')); + File::CreateDir(m_directory_path); + + // should try to create the directory at m_directory_path, which already exists + EXPECT_TRUE(File::IsDirectory(m_directory_path)); + EXPECT_TRUE(File::CreateFullPath(m_directory_path + "/")); + + // should create the directory (one level) + std::string p1 = m_directory_path + "/p1"; + EXPECT_TRUE(!File::Exists(p1)); + EXPECT_TRUE(File::CreateFullPath(p1 + "/")); + EXPECT_TRUE(File::IsDirectory(p1)); + + // should create the directories (multiple levels) + std::string p2 = m_directory_path + "/p2"; + std::string p2a = m_directory_path + "/p2/a"; + std::string p2b = m_directory_path + "/p2/a/b"; + std::string p2c = m_directory_path + "/p2/a/b/c"; + EXPECT_TRUE(!File::Exists(p2)); + EXPECT_TRUE(!File::Exists(p2a)); + EXPECT_TRUE(!File::Exists(p2b)); + EXPECT_TRUE(!File::Exists(p2c)); + EXPECT_TRUE(File::CreateFullPath(p2c + "/")); + EXPECT_TRUE(File::IsDirectory(p2)); + EXPECT_TRUE(File::IsDirectory(p2a)); + EXPECT_TRUE(File::IsDirectory(p2b)); + EXPECT_TRUE(File::IsDirectory(p2c)); + + // if the given path ends in a file, the file should be ignored + std::string p3 = m_directory_path + "/p3"; + std::string p3file = m_directory_path + "/p3/test.bin"; + EXPECT_TRUE(!File::Exists(p3)); + EXPECT_TRUE(!File::Exists(p3file)); + EXPECT_TRUE(File::CreateFullPath(p3file)); + EXPECT_TRUE(File::IsDirectory(p3)); + EXPECT_TRUE(!File::Exists(p3file)); + + // if we try to create a directory where a file already exists, we expect it to fail and leave the + // file alone + EXPECT_TRUE(File::CreateEmptyFile(p3file)); + EXPECT_TRUE(File::IsFile(p3file)); + EXPECT_FALSE(File::CreateFullPath(p3file + "/")); + EXPECT_TRUE(File::IsFile(p3file)); +}