mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Sync with libloot v0.26.1
Aside from adding GameType::oblivionRemastered and its usage, this is almost entirely about moving C++ files and tests around.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#include "api/convert.h"
|
||||
|
||||
#include "api/exception.h"
|
||||
#include "api/exception/exception.h"
|
||||
|
||||
namespace loot {
|
||||
// To public types
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "api/database.h"
|
||||
|
||||
#include "api/convert.h"
|
||||
#include "api/exception.h"
|
||||
#include "api/exception/exception.h"
|
||||
|
||||
namespace loot {
|
||||
Database::Database(::rust::Box<loot::rust::Database>&& database) :
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "api/exception.h"
|
||||
#include "api/exception/exception.h"
|
||||
|
||||
#include <charconv>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "api/game.h"
|
||||
|
||||
#include "api/convert.h"
|
||||
#include "api/exception.h"
|
||||
#include "api/exception/exception.h"
|
||||
|
||||
namespace {
|
||||
loot::GameType convert(loot::rust::GameType gameType) {
|
||||
@@ -29,6 +29,8 @@ loot::GameType convert(loot::rust::GameType gameType) {
|
||||
return loot::GameType::starfield;
|
||||
case loot::rust::GameType::OpenMW:
|
||||
return loot::GameType::openmw;
|
||||
case loot::rust::GameType::OblivionRemastered:
|
||||
return loot::GameType::oblivionRemastered;
|
||||
default:
|
||||
throw std::logic_error("Unsupported GameType value");
|
||||
}
|
||||
@@ -58,6 +60,8 @@ loot::rust::GameType convert(loot::GameType gameType) {
|
||||
return loot::rust::GameType::Starfield;
|
||||
case loot::GameType::openmw:
|
||||
return loot::rust::GameType::OpenMW;
|
||||
case loot::GameType::oblivionRemastered:
|
||||
return loot::rust::GameType::OblivionRemastered;
|
||||
default:
|
||||
throw std::logic_error("Unsupported GameType value");
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ public:
|
||||
|
||||
GameType GetType() const override;
|
||||
|
||||
std::vector<std::filesystem::path> GetAdditionalDataPaths() const;
|
||||
std::vector<std::filesystem::path> GetAdditionalDataPaths() const override;
|
||||
|
||||
void SetAdditionalDataPaths(
|
||||
const std::vector<std::filesystem::path>& additionalDataPaths) override;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <typeinfo>
|
||||
|
||||
#include "api/convert.h"
|
||||
#include "api/exception.h"
|
||||
#include "api/exception/exception.h"
|
||||
|
||||
namespace loot {
|
||||
Plugin::Plugin(::rust::Box<loot::rust::Plugin> plugin) :
|
||||
@@ -101,9 +101,7 @@ bool Plugin::DoRecordsOverlap(const PluginInterface& plugin) const {
|
||||
|
||||
return plugin_->do_records_overlap(*otherPlugin.plugin_);
|
||||
} catch (std::bad_cast&) {
|
||||
throw std::invalid_argument(
|
||||
"Tried to check if records overlapped with a different concrete type "
|
||||
"implementing PluginInterface");
|
||||
return false;
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(mapError(e));
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "LOOT"
|
||||
VALUE "FileDescription", "Library providing LOOT's core functionality"
|
||||
VALUE "FileVersion", "0.26.0"
|
||||
VALUE "FileVersion", "0.26.1"
|
||||
VALUE "InternalName", "loot"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2013-2022 Oliver Hamlet"
|
||||
VALUE "OriginalFilename", "loot.dll"
|
||||
VALUE "ProductName", "LOOT"
|
||||
VALUE "ProductVersion", "0.26.0"
|
||||
VALUE "ProductVersion", "0.26.1"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -21,6 +21,7 @@ impl TryFrom<libloot::GameType> for GameType {
|
||||
libloot::GameType::Morrowind => Ok(GameType::Morrowind),
|
||||
libloot::GameType::Starfield => Ok(GameType::Starfield),
|
||||
libloot::GameType::OpenMW => Ok(GameType::OpenMW),
|
||||
libloot::GameType::OblivionRemastered => Ok(GameType::OblivionRemastered),
|
||||
_ => Err(UnsupportedEnumValueError),
|
||||
}
|
||||
}
|
||||
@@ -42,6 +43,7 @@ impl TryFrom<GameType> for libloot::GameType {
|
||||
GameType::Morrowind => Ok(libloot::GameType::Morrowind),
|
||||
GameType::Starfield => Ok(libloot::GameType::Starfield),
|
||||
GameType::OpenMW => Ok(libloot::GameType::OpenMW),
|
||||
GameType::OblivionRemastered => Ok(libloot::GameType::OblivionRemastered),
|
||||
_ => Err(UnsupportedEnumValueError),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +232,7 @@ mod ffi {
|
||||
Morrowind,
|
||||
Starfield,
|
||||
OpenMW,
|
||||
OblivionRemastered,
|
||||
}
|
||||
|
||||
pub enum MessageType {
|
||||
|
||||
@@ -88,17 +88,7 @@ protected:
|
||||
// but we only have the one so no prefix is necessary.
|
||||
INSTANTIATE_TEST_SUITE_P(,
|
||||
CreateGameHandleTest,
|
||||
::testing::Values(GameType::tes4,
|
||||
GameType::tes5,
|
||||
GameType::fo3,
|
||||
GameType::fonv,
|
||||
GameType::fo4,
|
||||
GameType::tes5se,
|
||||
GameType::fo4vr,
|
||||
GameType::tes5vr,
|
||||
GameType::tes3,
|
||||
GameType::starfield,
|
||||
GameType::openmw));
|
||||
::testing::ValuesIn(ALL_GAME_TYPES));
|
||||
|
||||
TEST_P(CreateGameHandleTest,
|
||||
shouldSucceedIfPassedValidParametersWithRelativePaths) {
|
||||
@@ -151,6 +141,74 @@ TEST_P(CreateGameHandleTest,
|
||||
EXPECT_TRUE(handle_);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
TEST_P(
|
||||
CreateGameHandleTest,
|
||||
shouldThrowOnLinuxIfLocalPathIsNotGivenExceptForMorrowindOpenMWAndOblivionRemastered) {
|
||||
if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw ||
|
||||
GetParam() == GameType::oblivionRemastered) {
|
||||
EXPECT_NO_THROW(CreateGameHandle(GetParam(), gamePath));
|
||||
} else {
|
||||
EXPECT_THROW(CreateGameHandle(GetParam(), gamePath), std::system_error);
|
||||
}
|
||||
}
|
||||
#else
|
||||
TEST_P(CreateGameHandleTest,
|
||||
shouldNotThrowOnWindowsIfLocalPathIsNotGiven) {
|
||||
EXPECT_NO_THROW(CreateGameHandle(GetParam(), gamePath));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_P(CreateGameHandleTest,
|
||||
shouldNotThrowIfGameAndLocalPathsAreNotEmpty) {
|
||||
EXPECT_NO_THROW(CreateGameHandle(GetParam(), gamePath, localPath));
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
CreateGameHandleTest,
|
||||
shouldSetAdditionalDataPathsForFallout4FromMicrosoftStoreOrStarfield) {
|
||||
if (GetParam() == GameType::fo4) {
|
||||
// Create the file that indicates it's a Microsoft Store install.
|
||||
touch(gamePath / "appxmanifest.xml");
|
||||
} else if (GetParam() == GameType::openmw) {
|
||||
std::ofstream out(gamePath / "openmw.cfg");
|
||||
out << "data-local=\"" << (localPath / "data").u8string() << "\""
|
||||
<< std::endl
|
||||
<< "config=\"" << localPath.u8string() << "\"";
|
||||
}
|
||||
|
||||
const auto game = CreateGameHandle(GetParam(), gamePath, localPath);
|
||||
|
||||
if (GetParam() == GameType::fo4) {
|
||||
const auto basePath = gamePath / ".." / "..";
|
||||
EXPECT_EQ(std::vector<std::filesystem::path>(
|
||||
{basePath / "Fallout 4- Automatron (PC)" / "Content" / "Data",
|
||||
basePath / "Fallout 4- Nuka-World (PC)" / "Content" / "Data",
|
||||
basePath / "Fallout 4- Wasteland Workshop (PC)" / "Content" /
|
||||
"Data",
|
||||
basePath / "Fallout 4- High Resolution Texture Pack" /
|
||||
"Content" / "Data",
|
||||
basePath / "Fallout 4- Vault-Tec Workshop (PC)" / "Content" /
|
||||
"Data",
|
||||
basePath / "Fallout 4- Far Harbor (PC)" / "Content" / "Data",
|
||||
basePath / "Fallout 4- Contraptions Workshop (PC)" /
|
||||
"Content" / "Data"}),
|
||||
game->GetAdditionalDataPaths());
|
||||
} else if (GetParam() == GameType::starfield) {
|
||||
ASSERT_EQ(1, game->GetAdditionalDataPaths().size());
|
||||
|
||||
const auto expectedSuffix = std::filesystem::u8path("Documents") /
|
||||
"My Games" / "Starfield" / "Data";
|
||||
EXPECT_TRUE(endsWith(game->GetAdditionalDataPaths()[0].u8string(),
|
||||
expectedSuffix.u8string()));
|
||||
} else if (GetParam() == GameType::openmw) {
|
||||
EXPECT_EQ(std::vector<std::filesystem::path>{localPath / "data"},
|
||||
game->GetAdditionalDataPaths());
|
||||
} else {
|
||||
EXPECT_TRUE(game->GetAdditionalDataPaths().empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,12 +113,7 @@ protected:
|
||||
// but we only have the one so no prefix is necessary.
|
||||
INSTANTIATE_TEST_SUITE_P(,
|
||||
DatabaseInterfaceTest,
|
||||
::testing::Values(GameType::tes4,
|
||||
GameType::tes5,
|
||||
GameType::fo3,
|
||||
GameType::fonv,
|
||||
GameType::fo4,
|
||||
GameType::tes5se));
|
||||
::testing::ValuesIn(ALL_GAME_TYPES));
|
||||
|
||||
TEST_P(DatabaseInterfaceTest,
|
||||
loadMasterlistShouldSucceedEvenIfGameHandleIsDiscarded) {
|
||||
@@ -275,7 +270,7 @@ TEST_P(DatabaseInterfaceTest, writeUserMetadataShouldShouldWriteUserMetadata) {
|
||||
}
|
||||
|
||||
TEST_P(DatabaseInterfaceTest, evaluateShouldReturnTrueIfTheConditionIsTrue) {
|
||||
EXPECT_TRUE(handle_->GetDatabase().Evaluate("file(\"Blank.esp\")"));
|
||||
EXPECT_TRUE(handle_->GetDatabase().Evaluate("file(\"Blank.esp\")"));
|
||||
}
|
||||
|
||||
TEST_P(DatabaseInterfaceTest, evaluateShouldReturnFalseIfTheConditionIsFalse) {
|
||||
|
||||
@@ -30,6 +30,8 @@ along with LOOT. If not, see
|
||||
|
||||
namespace loot {
|
||||
namespace test {
|
||||
constexpr unsigned int ESP_ERROR_PLUGIN_METADATA_NOT_FOUND = 14;
|
||||
|
||||
class GameInterfaceTest : public ApiGameOperationsTest {
|
||||
protected:
|
||||
GameInterfaceTest() :
|
||||
@@ -74,17 +76,7 @@ protected:
|
||||
// but we only have the one so no prefix is necessary.
|
||||
INSTANTIATE_TEST_SUITE_P(,
|
||||
GameInterfaceTest,
|
||||
::testing::Values(GameType::tes4,
|
||||
GameType::tes5,
|
||||
GameType::fo3,
|
||||
GameType::fonv,
|
||||
GameType::fo4,
|
||||
GameType::tes5se,
|
||||
GameType::fo4vr,
|
||||
GameType::tes5vr,
|
||||
GameType::tes3,
|
||||
GameType::starfield,
|
||||
GameType::openmw));
|
||||
::testing::ValuesIn(ALL_GAME_TYPES));
|
||||
|
||||
TEST_P(GameInterfaceTest, setAdditionalDataPathsShouldDoThat) {
|
||||
const auto paths = std::vector<std::filesystem::path>{
|
||||
@@ -95,6 +87,56 @@ TEST_P(GameInterfaceTest, setAdditionalDataPathsShouldDoThat) {
|
||||
EXPECT_EQ(paths, handle_->GetAdditionalDataPaths());
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest, setAdditionalDataPathsShouldClearTheConditionCache) {
|
||||
PluginMetadata metadata(blankEsm);
|
||||
metadata.SetLoadAfterFiles({File("plugin.esp", "", "file(\"plugin.esp\")")});
|
||||
handle_->GetDatabase().SetPluginUserMetadata(metadata);
|
||||
|
||||
auto evaluatedMetadata =
|
||||
handle_->GetDatabase().GetPluginUserMetadata(blankEsm, true);
|
||||
EXPECT_FALSE(evaluatedMetadata.has_value());
|
||||
|
||||
const auto dataFilePath = gamePath.parent_path() / "Data" / "plugin.esp";
|
||||
touch(dataFilePath);
|
||||
handle_->SetAdditionalDataPaths({dataFilePath.parent_path()});
|
||||
|
||||
evaluatedMetadata =
|
||||
handle_->GetDatabase().GetPluginUserMetadata(blankEsm, true);
|
||||
EXPECT_FALSE(evaluatedMetadata.value().GetLoadAfterFiles().empty());
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
setAdditionalDataPathsShouldUpdateWhereLoadOrderPluginsAreFound) {
|
||||
// Set no additional data paths to avoid picking up non-test plugins on PCs
|
||||
// which have Starfield or Fallout 4 installed.
|
||||
handle_->SetAdditionalDataPaths({});
|
||||
handle_->LoadCurrentLoadOrderState();
|
||||
auto loadOrder = handle_->GetLoadOrder();
|
||||
|
||||
const auto filename = "plugin.esp";
|
||||
const auto dataFilePath = gamePath.parent_path() / "Data" / filename;
|
||||
std::filesystem::create_directories(dataFilePath.parent_path());
|
||||
std::filesystem::copy_file(getSourcePluginsPath() / blankEsp, dataFilePath);
|
||||
ASSERT_TRUE(std::filesystem::exists(dataFilePath));
|
||||
|
||||
if (GetParam() == GameType::starfield) {
|
||||
std::filesystem::copy_file(getSourcePluginsPath() / blankEsp,
|
||||
dataPath / filename);
|
||||
ASSERT_TRUE(std::filesystem::exists(dataPath / filename));
|
||||
}
|
||||
|
||||
std::filesystem::last_write_time(
|
||||
dataFilePath,
|
||||
std::filesystem::file_time_type::clock().now() + std::chrono::hours(1));
|
||||
|
||||
handle_->SetAdditionalDataPaths({dataFilePath.parent_path()});
|
||||
handle_->LoadCurrentLoadOrderState();
|
||||
|
||||
loadOrder.push_back(filename);
|
||||
|
||||
EXPECT_EQ(loadOrder, handle_->GetLoadOrder());
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest, isValidPluginShouldReturnTrueForAValidPlugin) {
|
||||
EXPECT_TRUE(handle_->IsValidPlugin(blankEsm));
|
||||
}
|
||||
@@ -116,9 +158,37 @@ TEST_P(GameInterfaceTest, isValidPluginShouldReturnFalseForAnEmptyFile) {
|
||||
EXPECT_FALSE(handle_->IsValidPlugin(emptyFile));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
isValidPluginShouldResolveRelativePathsRelativeToDataPath) {
|
||||
const auto path = ".." / dataPath.filename() / blankEsm;
|
||||
|
||||
EXPECT_TRUE(handle_->IsValidPlugin(path));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest, isValidPluginShouldUseAbsolutePathsAsGiven) {
|
||||
ASSERT_TRUE(dataPath.is_absolute());
|
||||
|
||||
const auto path = dataPath / std::filesystem::u8path(blankEsm);
|
||||
|
||||
EXPECT_TRUE(handle_->IsValidPlugin(path));
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
GameInterfaceTest,
|
||||
loadPluginsWithHeadersOnlyTrueShouldLoadTheHeadersOfAllGivenPlugins) {
|
||||
isValidPluginShouldTryGhostedPathIfGivenPluginDoesNotExistExceptForOpenMW) {
|
||||
if (GetParam() == GameType::openmw) {
|
||||
// This wasn't done for OpenMW during common setup.
|
||||
const auto pluginPath = dataPath / (blankMasterDependentEsm + ".ghost");
|
||||
std::filesystem::rename(dataPath / blankMasterDependentEsm, pluginPath);
|
||||
|
||||
EXPECT_FALSE(handle_->IsValidPlugin(blankMasterDependentEsm));
|
||||
} else {
|
||||
EXPECT_TRUE(handle_->IsValidPlugin(blankMasterDependentEsm));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
loadPluginsWithHeadersOnlyTrueShouldLoadTheHeadersOfAllGivenPlugins) {
|
||||
handle_->LoadPlugins(pluginsToLoad, true);
|
||||
if (GetParam() == GameType::starfield) {
|
||||
EXPECT_EQ(6, handle_->GetLoadedPlugins().size());
|
||||
@@ -154,9 +224,29 @@ TEST_P(GameInterfaceTest, loadPluginsShouldTrimDotGhostFileExtensions) {
|
||||
EXPECT_EQ(blankMasterDependentEsm, plugin->GetName());
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
loadPluginsWithHeadersOnlyTrueShouldLoadTheHeadersOfGivenPlugins) {
|
||||
handle_->LoadPlugins(pluginsToLoad, true);
|
||||
|
||||
if (GetParam() == GameType::starfield) {
|
||||
EXPECT_EQ(6, handle_->GetLoadedPlugins().size());
|
||||
} else {
|
||||
EXPECT_EQ(11, handle_->GetLoadedPlugins().size());
|
||||
}
|
||||
|
||||
// Check that one plugin's header has been read.
|
||||
ASSERT_NO_THROW(handle_->GetPlugin(masterFile));
|
||||
auto plugin = handle_->GetPlugin(masterFile);
|
||||
EXPECT_EQ("5.0", plugin->GetVersion().value());
|
||||
|
||||
// Check that only the header has been read.
|
||||
EXPECT_FALSE(plugin->GetCRC());
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
loadPluginsWithHeadersOnlyFalseShouldFullyLoadAllInstalledPlugins) {
|
||||
handle_->LoadPlugins(pluginsToLoad, false);
|
||||
|
||||
if (GetParam() == GameType::starfield) {
|
||||
EXPECT_EQ(6, handle_->GetLoadedPlugins().size());
|
||||
} else {
|
||||
@@ -184,6 +274,211 @@ TEST_P(GameInterfaceTest, loadPluginsWithANonAsciiPluginShouldLoadIt) {
|
||||
EXPECT_EQ(blankEsmCrc, plugin->GetCRC().value());
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
GameInterfaceTest,
|
||||
loadPluginsShouldNotThrowIfAFilenameHasNonWindows1252EncodableCharacters) {
|
||||
const auto pluginName = std::filesystem::u8path(
|
||||
u8"\u2551\u00BB\u00C1\u2510\u2557\u00FE\u00C3\u00CE.esp");
|
||||
std::filesystem::copy(dataPath / blankEsp, dataPath / pluginName);
|
||||
|
||||
EXPECT_NO_THROW(handle_->LoadPlugins({pluginName}, false));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
loadPluginsWithANonPluginShouldNotAddItToTheLoadedPlugins) {
|
||||
ASSERT_THROW(handle_->LoadPlugins({nonPluginFile}, false),
|
||||
std::invalid_argument);
|
||||
|
||||
ASSERT_TRUE(handle_->GetLoadedPlugins().empty());
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
loadPluginsWithAnInvalidPluginShouldNotAddItToTheLoadedPlugins) {
|
||||
ASSERT_FALSE(std::filesystem::exists(dataPath / invalidPlugin));
|
||||
ASSERT_NO_THROW(std::filesystem::copy_file(dataPath / blankEsm,
|
||||
dataPath / invalidPlugin));
|
||||
ASSERT_TRUE(std::filesystem::exists(dataPath / invalidPlugin));
|
||||
std::ofstream out(dataPath / invalidPlugin, std::fstream::app);
|
||||
out << "GRUP0";
|
||||
out.close();
|
||||
|
||||
ASSERT_NO_THROW(handle_->LoadPlugins({invalidPlugin}, false));
|
||||
|
||||
ASSERT_TRUE(handle_->GetLoadedPlugins().empty());
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest, loadPluginsShouldNotClearThePluginsCache) {
|
||||
handle_->LoadPlugins({std::filesystem::u8path(blankEsm)}, true);
|
||||
ASSERT_EQ(1, handle_->GetLoadedPlugins().size());
|
||||
|
||||
handle_->LoadPlugins({std::filesystem::u8path(blankEsp)}, true);
|
||||
|
||||
EXPECT_EQ(2, handle_->GetLoadedPlugins().size());
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
loadPluginsShouldReplaceCacheEntriesForTheGivenPlugins) {
|
||||
handle_->LoadPlugins({std::filesystem::u8path(blankEsm)}, true);
|
||||
const auto pointer = handle_->GetPlugin(blankEsm);
|
||||
ASSERT_NE(nullptr, pointer);
|
||||
|
||||
handle_->LoadPlugins({std::filesystem::u8path(blankEsm)}, false);
|
||||
|
||||
const auto newPointer = handle_->GetPlugin(blankEsm);
|
||||
ASSERT_NE(nullptr, newPointer);
|
||||
EXPECT_NE(pointer, newPointer);
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
loadPluginsShouldThrowIfGivenVectorElementsWithTheSameFilename) {
|
||||
const auto dataPluginPath = dataPath / std::filesystem::u8path(blankEsm);
|
||||
const auto sourcePluginPath =
|
||||
getSourcePluginsPath() / std::filesystem::u8path(blankEsm);
|
||||
|
||||
EXPECT_THROW(handle_->LoadPlugins(std::vector<std::filesystem::path>(
|
||||
{dataPluginPath, sourcePluginPath}),
|
||||
true),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
loadPluginsShouldResolveRelativePathsRelativeToDataPath) {
|
||||
const auto relativePath = ".." / dataPath.filename() / blankEsm;
|
||||
|
||||
handle_->LoadPlugins(std::vector<std::filesystem::path>({relativePath}),
|
||||
true);
|
||||
|
||||
EXPECT_NE(nullptr, handle_->GetPlugin(blankEsm));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest, loadPluginsShouldUseAbsolutePathsAsGiven) {
|
||||
const auto absolutePath = dataPath / std::filesystem::u8path(blankEsm);
|
||||
|
||||
handle_->LoadPlugins(std::vector<std::filesystem::path>({absolutePath}),
|
||||
true);
|
||||
|
||||
EXPECT_NE(nullptr, handle_->GetPlugin(blankEsm));
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
GameInterfaceTest,
|
||||
loadPluginsShouldThrowIfFullyLoadingAPluginWithAMissingMasterIfGameIsMorrowindOrStarfield) {
|
||||
const auto pluginName =
|
||||
GetParam() == GameType::starfield ? blankFullEsm : blankEsm;
|
||||
|
||||
std::filesystem::remove(dataPath / pluginName);
|
||||
|
||||
if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw ||
|
||||
GetParam() == GameType::starfield) {
|
||||
try {
|
||||
handle_->LoadPlugins({blankMasterDependentEsm}, false);
|
||||
FAIL();
|
||||
} catch (const std::system_error& e) {
|
||||
EXPECT_EQ(ESP_ERROR_PLUGIN_METADATA_NOT_FOUND, e.code().value());
|
||||
EXPECT_EQ(esplugin_category(), e.code().category());
|
||||
}
|
||||
} else {
|
||||
handle_->LoadPlugins({blankMasterDependentEsm}, false);
|
||||
|
||||
EXPECT_NE(nullptr, handle_->GetPlugin(blankMasterDependentEsm));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
GameInterfaceTest,
|
||||
loadPluginsShouldThrowIfAPluginHasAMasterThatIsNotInTheInputAndIsNotAlreadyLoadedAndGameIsMorrowindOrStarfield) {
|
||||
if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw ||
|
||||
GetParam() == GameType::starfield) {
|
||||
try {
|
||||
handle_->LoadPlugins({blankMasterDependentEsm}, false);
|
||||
FAIL();
|
||||
} catch (const std::system_error& e) {
|
||||
EXPECT_EQ(ESP_ERROR_PLUGIN_METADATA_NOT_FOUND, e.code().value());
|
||||
EXPECT_EQ(esplugin_category(), e.code().category());
|
||||
}
|
||||
} else {
|
||||
handle_->LoadPlugins({blankMasterDependentEsm}, false);
|
||||
|
||||
EXPECT_NE(nullptr, handle_->GetPlugin(blankMasterDependentEsm));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
GameInterfaceTest,
|
||||
loadPluginsShouldNotThrowIfAPluginHasAMasterThatIsNotInTheInputButIsAlreadyLoaded) {
|
||||
const auto pluginName =
|
||||
GetParam() == GameType::starfield ? blankFullEsm : blankEsm;
|
||||
|
||||
handle_->LoadPlugins({pluginName}, true);
|
||||
|
||||
handle_->LoadPlugins({blankMasterDependentEsm}, false);
|
||||
|
||||
EXPECT_NE(nullptr, handle_->GetPlugin(blankMasterDependentEsm));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
sortPluginsWithNoLoadedPluginsShouldReturnAnEmptyList) {
|
||||
const auto sorted = handle_->SortPlugins(handle_->GetLoadOrder());
|
||||
|
||||
EXPECT_TRUE(sorted.empty());
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest, sortPluginsShouldOnlySortTheGivenPlugins) {
|
||||
handle_->LoadPlugins(GetInstalledPlugins(), false);
|
||||
|
||||
std::vector<std::string> plugins{blankEsp, blankDifferentEsp};
|
||||
const auto sorted = handle_->SortPlugins(plugins);
|
||||
|
||||
EXPECT_EQ(plugins, sorted);
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
sortingShouldNotMakeUnnecessaryChangesToAnExistingLoadOrder) {
|
||||
std::filesystem::remove(dataPath / std::filesystem::u8path(nonAsciiEsm));
|
||||
|
||||
handle_->LoadCurrentLoadOrderState();
|
||||
|
||||
auto plugins = GetInstalledPlugins();
|
||||
handle_->LoadPlugins({plugins.front()}, true);
|
||||
plugins.erase(plugins.begin());
|
||||
handle_->LoadPlugins(plugins, false);
|
||||
|
||||
std::vector<std::string> expectedSortedOrder;
|
||||
if (GetParam() == GameType::openmw) {
|
||||
// The existing load order for OpenMW doesn't have plugins loading after
|
||||
// their masters, because the game doesn't enforce that, and the test
|
||||
// setup cannot enforce the positions of inactive plugins.
|
||||
expectedSortedOrder = {
|
||||
blankDifferentEsm,
|
||||
blankDifferentMasterDependentEsm,
|
||||
blankDifferentEsp,
|
||||
blankDifferentPluginDependentEsp,
|
||||
blankEsm,
|
||||
blankMasterDependentEsm,
|
||||
blankMasterDependentEsp,
|
||||
blankEsp,
|
||||
blankPluginDependentEsp,
|
||||
masterFile,
|
||||
blankDifferentMasterDependentEsp,
|
||||
};
|
||||
} else {
|
||||
expectedSortedOrder = getLoadOrder();
|
||||
}
|
||||
|
||||
// Check stability by running the sort 100 times.
|
||||
for (int i = 0; i < 100; i++) {
|
||||
auto input = handle_->GetLoadOrder();
|
||||
auto sorted = handle_->SortPlugins(handle_->GetLoadOrder());
|
||||
ASSERT_EQ(expectedSortedOrder, sorted) << " for sort " << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest, sortPluginsShouldThrowIfAGivenPluginIsNotLoaded) {
|
||||
std::vector<std::string> plugins{blankEsp, blankDifferentEsp};
|
||||
|
||||
EXPECT_THROW(handle_->SortPlugins(plugins), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest, clearLoadedPluginsShouldClearThePluginsCache) {
|
||||
handle_->LoadPlugins({std::filesystem::u8path(blankEsm)}, true);
|
||||
const auto pointer = handle_->GetPlugin(blankEsm);
|
||||
@@ -255,18 +550,54 @@ TEST_P(GameInterfaceTest, sortPluginsShouldSucceedIfPassedValidArguments) {
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
isPluginActiveShouldReturnFalseIfTheGivenPluginIsNotActive) {
|
||||
isPluginActiveShouldReturnTrueIfTheGivenPluginIsActive) {
|
||||
handle_->LoadCurrentLoadOrderState();
|
||||
|
||||
EXPECT_TRUE(handle_->IsPluginActive(blankEsm));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
isPluginActiveShouldReturnTrueIfTheGivenPluginIsActive) {
|
||||
isPluginActiveShouldReturnFalseIfTheGivenPluginIsNotActive) {
|
||||
handle_->LoadCurrentLoadOrderState();
|
||||
EXPECT_FALSE(handle_->IsPluginActive(blankEsp));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
isPluginActiveShouldActivePluginAsActiveWithHeaderLoaded) {
|
||||
handle_->LoadCurrentLoadOrderState();
|
||||
|
||||
ASSERT_NO_THROW(handle_->LoadPlugins({blankEsm}, true));
|
||||
|
||||
EXPECT_TRUE(handle_->IsPluginActive(blankEsm));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
isPluginActiveShouldInactivePluginAsInactiveWithHeaderLoaded) {
|
||||
handle_->LoadCurrentLoadOrderState();
|
||||
|
||||
ASSERT_NO_THROW(handle_->LoadPlugins({blankEsp}, true));
|
||||
|
||||
EXPECT_FALSE(handle_->IsPluginActive(blankEsp));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
isPluginActiveShouldActivePluginAsActiveWhenFullyLoaded) {
|
||||
handle_->LoadCurrentLoadOrderState();
|
||||
|
||||
ASSERT_NO_THROW(handle_->LoadPlugins({blankEsm}, false));
|
||||
|
||||
EXPECT_TRUE(handle_->IsPluginActive(blankEsm));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest,
|
||||
isPluginActiveShouldInactivePluginAsInactiveWhenFullyLoaded) {
|
||||
handle_->LoadCurrentLoadOrderState();
|
||||
|
||||
ASSERT_NO_THROW(handle_->LoadPlugins({blankEsp}, false));
|
||||
|
||||
EXPECT_FALSE(handle_->IsPluginActive(blankEsp));
|
||||
}
|
||||
|
||||
TEST_P(GameInterfaceTest, getLoadOrderShouldReturnTheCurrentLoadOrder) {
|
||||
// Remove the non-ASCII duplicate plugin.
|
||||
std::filesystem::remove(dataPath / std::filesystem::u8path(nonAsciiEsm));
|
||||
|
||||
@@ -25,10 +25,20 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "loot/api.h"
|
||||
#include "tests/api/interface/metadata/conditional_metadata_test.h"
|
||||
#include "tests/api/interface/metadata/file_test.h"
|
||||
#include "tests/api/interface/metadata/group_test.h"
|
||||
#include "tests/api/interface/metadata/location_test.h"
|
||||
#include "tests/api/interface/metadata/message_content_test.h"
|
||||
#include "tests/api/interface/metadata/message_test.h"
|
||||
#include "tests/api/interface/metadata/plugin_cleaning_data_test.h"
|
||||
#include "tests/api/interface/metadata/plugin_metadata_test.h"
|
||||
#include "tests/api/interface/metadata/tag_test.h"
|
||||
#include "tests/api/interface/create_game_handle_test.h"
|
||||
#include "tests/api/interface/database_interface_test.h"
|
||||
#include "tests/api/interface/game_interface_test.h"
|
||||
#include "tests/api/interface/is_compatible_test.h"
|
||||
#include "tests/api/interface/plugin_interface_test.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
+3
-8
@@ -22,8 +22,8 @@ along with LOOT. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LOOT_TESTS_API_INTERNALS_METADATA_CONDITIONAL_METADATA_TEST
|
||||
#define LOOT_TESTS_API_INTERNALS_METADATA_CONDITIONAL_METADATA_TEST
|
||||
#ifndef LOOT_TESTS_API_INTERFACE_METADATA_CONDITIONAL_METADATA_TEST
|
||||
#define LOOT_TESTS_API_INTERFACE_METADATA_CONDITIONAL_METADATA_TEST
|
||||
|
||||
#include "loot/metadata/conditional_metadata.h"
|
||||
#include "tests/common_game_test_fixture.h"
|
||||
@@ -41,12 +41,7 @@ protected:
|
||||
// but we only have the one so no prefix is necessary.
|
||||
INSTANTIATE_TEST_SUITE_P(,
|
||||
ConditionalMetadataTest,
|
||||
::testing::Values(GameType::tes4,
|
||||
GameType::tes5,
|
||||
GameType::fo3,
|
||||
GameType::fonv,
|
||||
GameType::fo4,
|
||||
GameType::tes5se));
|
||||
::testing::ValuesIn(ALL_GAME_TYPES));
|
||||
|
||||
TEST_P(ConditionalMetadataTest,
|
||||
defaultConstructorShouldSetEmptyConditionString) {
|
||||
+3
-5
@@ -22,15 +22,14 @@ along with LOOT. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LOOT_TESTS_API_INTERNALS_METADATA_FILE_TEST
|
||||
#define LOOT_TESTS_API_INTERNALS_METADATA_FILE_TEST
|
||||
#ifndef LOOT_TESTS_API_INTERFACE_METADATA_FILE_TEST
|
||||
#define LOOT_TESTS_API_INTERFACE_METADATA_FILE_TEST
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "loot/metadata/file.h"
|
||||
|
||||
namespace loot {
|
||||
namespace test {
|
||||
namespace loot::test {
|
||||
TEST(File, defaultConstructorShouldInitialiseEmptyStrings) {
|
||||
File file;
|
||||
|
||||
@@ -381,6 +380,5 @@ TEST(File, getDisplayNameShouldReturnDisplayString) {
|
||||
EXPECT_EQ("display", file.GetDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+3
-5
@@ -22,15 +22,14 @@ along with LOOT. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LOOT_TESTS_API_INTERNALS_METADATA_GROUP_TEST
|
||||
#define LOOT_TESTS_API_INTERNALS_METADATA_GROUP_TEST
|
||||
#ifndef LOOT_TESTS_API_INTERFACE_METADATA_GROUP_TEST
|
||||
#define LOOT_TESTS_API_INTERFACE_METADATA_GROUP_TEST
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "loot/metadata/group.h"
|
||||
|
||||
namespace loot {
|
||||
namespace test {
|
||||
namespace loot::test {
|
||||
TEST(Group, defaultConstructorShouldCreateDefaultGroup) {
|
||||
Group group;
|
||||
|
||||
@@ -350,6 +349,5 @@ TEST(
|
||||
EXPECT_TRUE(group2 >= group1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user