diff --git a/src/api/api.cpp b/src/api/api.cpp index 433f3e10..fd584fd5 100644 --- a/src/api/api.cpp +++ b/src/api/api.cpp @@ -31,7 +31,16 @@ #include "loot/error.h" #include "backend/app/loot_paths.h" +namespace fs = boost::filesystem; + namespace loot { +std::string ResolvePath(const std::string& path) { + if (path.empty() || !fs::is_symlink(path)) + return path; + + return fs::read_symlink(path).string(); +} + LOOT_API bool IsCompatible(const unsigned int versionMajor, const unsigned int versionMinor, const unsigned int versionPatch) { if (versionMajor > 0) return versionMajor == loot::LootVersion::major; @@ -48,12 +57,14 @@ LOOT_API std::shared_ptr CreateDatabase(const GameType game, boost::log::core::get()->set_logging_enabled(false); // Check for valid paths. - if (!gamePath.empty() && !boost::filesystem::is_directory(gamePath)) - throw loot::Error(Error::Code::invalid_args, "Given game path \"" + std::string(gamePath) + "\" is not a valid directory."); + const std::string resolvedGamePath = ResolvePath(gamePath); + if (!gamePath.empty() && !fs::is_directory(resolvedGamePath)) + throw Error(Error::Code::invalid_args, "Given game path \"" + gamePath + "\" does not resolve to a valid directory."); - if (!gameLocalPath.empty() && !boost::filesystem::is_directory(gameLocalPath)) - throw loot::Error(Error::Code::invalid_args, "Given local data path \"" + std::string(gameLocalPath) + "\" is not a valid directory."); + const std::string resolvedGameLocalPath = ResolvePath(gameLocalPath); + if (!gameLocalPath.empty() && !fs::is_directory(resolvedGameLocalPath)) + throw Error(Error::Code::invalid_args, "Given local data path \"" + gameLocalPath + "\" does not resolve to a valid directory."); - return std::make_shared(game, gamePath, gameLocalPath); + return std::make_shared(game, resolvedGamePath, resolvedGameLocalPath); } } diff --git a/src/tests/api/create_database_test.h b/src/tests/api/create_database_test.h index b1a3fdf9..f1f996ae 100644 --- a/src/tests/api/create_database_test.h +++ b/src/tests/api/create_database_test.h @@ -35,17 +35,29 @@ namespace loot { namespace test { class CreateDatabaseTest : public CommonGameTestFixture { protected: - CreateDatabaseTest() : db_(nullptr) {} + CreateDatabaseTest() : + db_(nullptr), + gamePathSymlink(dataPath.parent_path().string() + "symlink"), + localPathSymlink(localPath.string() + "symlink") {} void SetUp() { CommonGameTestFixture::SetUp(); + + boost::filesystem::create_directory_symlink(dataPath.parent_path(), gamePathSymlink); + boost::filesystem::create_directory_symlink(localPath, localPathSymlink); } void TearDown() { CommonGameTestFixture::TearDown(); + + boost::filesystem::remove(gamePathSymlink); + boost::filesystem::remove(localPathSymlink); } std::shared_ptr db_; + + const boost::filesystem::path gamePathSymlink; + const boost::filesystem::path localPathSymlink; }; // Pass an empty first argument, as it's a prefix for the test instantation, @@ -86,6 +98,11 @@ TEST_P(CreateDatabaseTest, shouldReturnOkIfPassedAnEmptyLocalPathString) { EXPECT_NE(nullptr, db_); } #endif + +TEST_P(CreateDatabaseTest, shouldReturnOkIfPassedGameAndLocalPathSymlinks) { + EXPECT_NO_THROW(db_ = CreateDatabase(GetParam(), gamePathSymlink.string(), localPathSymlink.string())); + EXPECT_NE(nullptr, db_); +} } }