diff --git a/cmake/tests.cmake b/cmake/tests.cmake index 5241d78b..94047ec9 100644 --- a/cmake/tests.cmake +++ b/cmake/tests.cmake @@ -20,8 +20,8 @@ FetchContent_Declare( FetchContent_Declare( testing-plugins - URL "https://github.com/Ortham/testing-plugins/archive/1.5.0.tar.gz" - URL_HASH "SHA256=98c9094fb0f0152b1af7a6206950207f7ddc6602cd44ed67ebf70603ef490791") + URL "https://github.com/Ortham/testing-plugins/archive/1.6.1.tar.gz" + URL_HASH "") FetchContent_MakeAvailable(GTest testing-plugins) diff --git a/include/loot/plugin_interface.h b/include/loot/plugin_interface.h index 3290eea5..de335a94 100644 --- a/include/loot/plugin_interface.h +++ b/include/loot/plugin_interface.h @@ -99,6 +99,12 @@ class PluginInterface { */ virtual bool IsLightPlugin() const = 0; + /** + * Check if the plugin is a medium plugin. + * @return True if plugin is a medium plugin, false otherwise. + */ + virtual bool IsMediumPlugin() const = 0; + /** * Check if the plugin is an override plugin. * @return True if plugin is an override plugin, false otherwise. @@ -112,6 +118,13 @@ class PluginInterface { */ virtual bool IsValidAsLightPlugin() const = 0; + /** + * Check if the plugin is or would be valid as a medium plugin. + * @return True if the plugin is a valid medium plugin or would be a valid + * medium plugin, false otherwise. + */ + virtual bool IsValidAsMediumPlugin() const = 0; + /** * Check if the plugin is or would be valid as an override plugin. * @return True if the plugin is a valid override plugin or would be a valid diff --git a/src/api/game/game.cpp b/src/api/game/game.cpp index c344e5f4..44477317 100644 --- a/src/api/game/game.cpp +++ b/src/api/game/game.cpp @@ -307,7 +307,7 @@ void Game::LoadPlugins(const std::vector& pluginPaths, } }); - if (GetType() == GameType::starfield) { + if (!loadHeadersOnly && GetType() == GameType::starfield) { auto plugins = cache_.GetPlugins(); const auto pluginsMetadata = Plugin::GetPluginsMetadata(plugins); for (auto& plugin : plugins) { diff --git a/src/api/plugin.cpp b/src/api/plugin.cpp index aaf0d58b..5c3bbbc0 100644 --- a/src/api/plugin.cpp +++ b/src/api/plugin.cpp @@ -307,6 +307,14 @@ bool Plugin::IsLightPlugin() const { return isLightPlugin; } +bool Plugin::IsMediumPlugin() const { + bool isMediumPlugin = false; + const auto ret = esp_plugin_is_medium_plugin(esPlugin.get(), &isMediumPlugin); + HandleEspluginError("check if \"" + name_ + "\" is a medium plugin", ret); + + return isMediumPlugin; +} + bool Plugin::IsOverridePlugin() const { bool isOverridePlugin = false; const auto ret = @@ -326,6 +334,16 @@ bool Plugin::IsValidAsLightPlugin() const { return isValid; } +bool Plugin::IsValidAsMediumPlugin() const { + bool isValid = false; + const auto ret = + esp_plugin_is_valid_as_medium_plugin(esPlugin.get(), &isValid); + HandleEspluginError("check if \"" + name_ + "\" is valid as a medium plugin", + ret); + + return isValid; +} + bool Plugin::IsValidAsOverridePlugin() const { bool isValid = false; const auto ret = diff --git a/src/api/plugin.h b/src/api/plugin.h index 1295049e..fce6b107 100644 --- a/src/api/plugin.h +++ b/src/api/plugin.h @@ -72,9 +72,11 @@ class Plugin final : public PluginSortingInterface { bool IsMaster() const override; bool IsLightPlugin() const override; + bool IsMediumPlugin() const override; bool IsOverridePlugin() const override; bool IsValidAsLightPlugin() const override; + bool IsValidAsMediumPlugin() const override; bool IsValidAsOverridePlugin() const override; bool IsEmpty() const override; bool LoadsArchive() const override; diff --git a/src/tests/api/interface/game_interface_test.h b/src/tests/api/interface/game_interface_test.h index e4a49cba..c43d569f 100644 --- a/src/tests/api/interface/game_interface_test.h +++ b/src/tests/api/interface/game_interface_test.h @@ -33,10 +33,22 @@ namespace test { class GameInterfaceTest : public ApiGameOperationsTest { protected: GameInterfaceTest() : - emptyFile("EmptyFile.esm"), - nonAsciiEsm(u8"non\u00C1scii.esm"), - pluginsToLoad({ - // These are all ASCII filenames. + emptyFile("EmptyFile.esm"), nonAsciiEsm(u8"non\u00C1scii.esm") { + // Make sure the plugin with a non-ASCII filename exists. + std::filesystem::copy_file(dataPath / blankEsm, + dataPath / std::filesystem::u8path(nonAsciiEsm)); + + if (GetParam() == GameType::starfield) { + pluginsToLoad = { + masterFile, + blankEsm, + blankFullEsm, + blankMasterDependentEsm, + blankEsp, + blankMasterDependentEsp, + }; + } else { + pluginsToLoad = { masterFile, blankEsm, blankDifferentEsm, @@ -48,15 +60,13 @@ class GameInterfaceTest : public ApiGameOperationsTest { blankDifferentMasterDependentEsp, blankPluginDependentEsp, blankDifferentPluginDependentEsp, - }) { - // Make sure the plugin with a non-ASCII filename exists. - std::filesystem::copy_file(dataPath / blankEsm, - dataPath / std::filesystem::u8path(nonAsciiEsm)); + }; + } } const std::string emptyFile; const std::string nonAsciiEsm; - const std::vector pluginsToLoad; + std::vector pluginsToLoad; }; // Pass an empty first argument, as it's a prefix for the test instantation, @@ -108,7 +118,11 @@ TEST_P( GameInterfaceTest, loadPluginsWithHeadersOnlyTrueShouldLoadTheHeadersOfAllInstalledPlugins) { handle_->LoadPlugins(pluginsToLoad, true); - EXPECT_EQ(11, handle_->GetLoadedPlugins().size()); + 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)); @@ -132,7 +146,11 @@ TEST_P(GameInterfaceTest, loadPluginsShouldTrimDotGhostFileExtensions) { TEST_P(GameInterfaceTest, loadPluginsWithHeadersOnlyFalseShouldFullyLoadAllInstalledPlugins) { handle_->LoadPlugins(pluginsToLoad, false); - EXPECT_EQ(11, handle_->GetLoadedPlugins().size()); + 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)); @@ -165,19 +183,31 @@ TEST_P(GameInterfaceTest, } TEST_P(GameInterfaceTest, sortPluginsShouldSucceedIfPassedValidArguments) { - std::vector expectedOrder = { - masterFile, - blankEsm, - blankMasterDependentEsm, - blankDifferentEsm, - blankDifferentMasterDependentEsm, - blankMasterDependentEsp, - blankDifferentMasterDependentEsp, - blankEsp, - blankPluginDependentEsp, - blankDifferentEsp, - blankDifferentPluginDependentEsp, - }; + std::vector expectedOrder; + if (GetParam() == GameType::starfield) { + expectedOrder = { + masterFile, + blankEsm, + blankFullEsm, + blankMasterDependentEsm, + blankEsp, + blankMasterDependentEsp, + }; + } else { + expectedOrder = { + masterFile, + blankEsm, + blankMasterDependentEsm, + blankDifferentEsm, + blankDifferentMasterDependentEsm, + blankMasterDependentEsp, + blankDifferentMasterDependentEsp, + blankEsp, + blankPluginDependentEsp, + blankDifferentEsp, + blankDifferentPluginDependentEsp, + }; + } if (GetParam() == GameType::fo4 || GetParam() == GameType::tes5se) { expectedOrder.insert(expectedOrder.begin() + 5, blankEsl); @@ -186,7 +216,7 @@ TEST_P(GameInterfaceTest, sortPluginsShouldSucceedIfPassedValidArguments) { ASSERT_NO_THROW(GenerateMasterlist()); ASSERT_NO_THROW(handle_->GetDatabase().LoadLists(masterlistPath, "")); - std::vector pluginsToSort({ + /*std::vector pluginsToSort({ // These are all ASCII filenames. blankEsp, blankPluginDependentEsp, @@ -199,14 +229,14 @@ TEST_P(GameInterfaceTest, sortPluginsShouldSucceedIfPassedValidArguments) { blankEsm, blankMasterDependentEsm, blankDifferentEsm, - }); + });*/ if (GetParam() == GameType::fo4 || GetParam() == GameType::tes5se) { - pluginsToSort.push_back(blankEsl); + pluginsToLoad.push_back(blankEsl); } handle_->LoadCurrentLoadOrderState(); - std::vector actualOrder = handle_->SortPlugins(pluginsToSort); + std::vector actualOrder = handle_->SortPlugins(pluginsToLoad); EXPECT_EQ(expectedOrder, actualOrder); } @@ -245,27 +275,41 @@ TEST_P(GameInterfaceTest, setLoadOrderShouldSetTheLoadOrder) { handle_->SetAdditionalDataPaths({}); handle_->LoadCurrentLoadOrderState(); - std::vector loadOrder({ - masterFile, - blankEsm, - blankMasterDependentEsm, - blankDifferentEsm, - blankDifferentMasterDependentEsm, - blankDifferentEsp, - blankDifferentPluginDependentEsp, - blankEsp, - blankMasterDependentEsp, - blankDifferentMasterDependentEsp, - blankPluginDependentEsp, - }); const auto gameSupportsEsl = GetParam() == GameType::fo4 || GetParam() == GameType::fo4vr || GetParam() == GameType::tes5se || GetParam() == GameType::tes5vr || GetParam() == GameType::starfield; - if (gameSupportsEsl) { - loadOrder.insert(loadOrder.begin() + 5, blankEsl); + std::vector loadOrder; + if (GetParam() == GameType::starfield) { + loadOrder = { + masterFile, + blankEsm, + blankMasterDependentEsm, + blankDifferentEsm, + blankDifferentEsp, + blankEsp, + blankMasterDependentEsp, + }; + } else { + loadOrder = { + masterFile, + blankEsm, + blankMasterDependentEsm, + blankDifferentEsm, + blankDifferentMasterDependentEsm, + blankDifferentEsp, + blankDifferentPluginDependentEsp, + blankEsp, + blankMasterDependentEsp, + blankDifferentMasterDependentEsp, + blankPluginDependentEsp, + }; + + if (gameSupportsEsl) { + loadOrder.insert(loadOrder.begin() + 5, blankEsl); + } } EXPECT_NO_THROW(handle_->SetLoadOrder(loadOrder)); diff --git a/src/tests/api/internals/game/game_test.h b/src/tests/api/internals/game/game_test.h index 0dd852e6..e584c2f4 100644 --- a/src/tests/api/internals/game/game_test.h +++ b/src/tests/api/internals/game/game_test.h @@ -37,21 +37,34 @@ class GameTest : public CommonGameTestFixture { } void loadInstalledPlugins(Game& game, bool headersOnly) { - const std::vector plugins({ - // These are all ASCII filenames. - masterFile, - blankEsm, - blankDifferentEsm, - blankMasterDependentEsm, - blankDifferentMasterDependentEsm, - blankEsp, - blankDifferentEsp, - blankMasterDependentEsp, - blankDifferentMasterDependentEsp, - blankPluginDependentEsp, - blankDifferentPluginDependentEsp, - }); - game.LoadPlugins(plugins, headersOnly); + if (GetParam() == GameType::starfield) { + const std::vector plugins({ + // These are all ASCII filenames. + masterFile, + blankEsm, + blankFullEsm, + blankMasterDependentEsm, + blankEsp, + blankMasterDependentEsp, + }); + game.LoadPlugins(plugins, headersOnly); + } else { + const std::vector plugins({ + // These are all ASCII filenames. + masterFile, + blankEsm, + blankDifferentEsm, + blankMasterDependentEsm, + blankDifferentMasterDependentEsm, + blankEsp, + blankDifferentEsp, + blankMasterDependentEsp, + blankDifferentMasterDependentEsp, + blankPluginDependentEsp, + blankDifferentPluginDependentEsp, + }); + game.LoadPlugins(plugins, headersOnly); + } } const std::string blankArchive; @@ -215,7 +228,11 @@ TEST_P( Game game = Game(GetParam(), dataPath.parent_path(), localPath); EXPECT_NO_THROW(loadInstalledPlugins(game, true)); - EXPECT_EQ(11, game.GetCache().GetPlugins().size()); + if (GetParam() == GameType::starfield) { + EXPECT_EQ(6, game.GetCache().GetPlugins().size()); + } else { + EXPECT_EQ(11, game.GetCache().GetPlugins().size()); + } // Check that one plugin's header has been read. ASSERT_NO_THROW(game.GetPlugin(masterFile)); @@ -259,7 +276,11 @@ TEST_P(GameTest, Game game = Game(GetParam(), dataPath.parent_path(), localPath); EXPECT_NO_THROW(loadInstalledPlugins(game, false)); - EXPECT_EQ(11, game.GetCache().GetPlugins().size()); + if (GetParam() == GameType::starfield) { + EXPECT_EQ(6, game.GetCache().GetPlugins().size()); + } else { + EXPECT_EQ(11, game.GetCache().GetPlugins().size()); + } // Check that one plugin's header has been read. ASSERT_NO_THROW(game.GetPlugin(blankEsm)); diff --git a/src/tests/api/internals/plugin_test.h b/src/tests/api/internals/plugin_test.h index bd17aaa0..7a3e4a99 100644 --- a/src/tests/api/internals/plugin_test.h +++ b/src/tests/api/internals/plugin_test.h @@ -71,14 +71,6 @@ class PluginTest : public CommonGameTestFixture { std::filesystem::copy(dataPath / blankEsp, dataPath / blankEsl)); } - if (GetParam() == GameType::starfield) { - // The ESL flag is not the same as in Skyrim SE, so modify the file - // accordingly. - auto bytes = ReadFile(dataPath / blankEsl); - bytes[9] = 0x1; - WriteFile(dataPath / blankEsl, bytes); - } - // Copy across archive files. std::filesystem::path blankMasterDependentArchive; if (GetParam() == GameType::fo4 || GetParam() == GameType::fo4vr || @@ -205,8 +197,10 @@ class OtherPluginType final : public PluginSortingInterface { bool IsMaster() const override { return false; } bool IsLightPlugin() const override { return false; } + bool IsMediumPlugin() const override { return false; } bool IsOverridePlugin() const override { return false; } bool IsValidAsLightPlugin() const override { return false; } + bool IsValidAsMediumPlugin() const override { return false; } bool IsValidAsOverridePlugin() const override { return false; } bool IsEmpty() const override { return false; } bool LoadsArchive() const override { return false; } @@ -265,6 +259,8 @@ TEST_P(PluginTest, loadingHeaderOnlyShouldReadHeaderData) { EXPECT_FLOAT_EQ(1.2f, plugin.GetHeaderVersion().value()); } else if (GetParam() == GameType::tes4) { EXPECT_FLOAT_EQ(0.8f, plugin.GetHeaderVersion().value()); + } else if (GetParam() == GameType::starfield) { + EXPECT_FLOAT_EQ(0.96f, plugin.GetHeaderVersion().value()); } else { EXPECT_FLOAT_EQ(0.94f, plugin.GetHeaderVersion().value()); } @@ -291,6 +287,8 @@ TEST_P(PluginTest, loadingWholePluginShouldReadHeaderData) { EXPECT_FLOAT_EQ(1.2f, plugin.GetHeaderVersion().value()); } else if (GetParam() == GameType::tes4) { EXPECT_FLOAT_EQ(0.8f, plugin.GetHeaderVersion().value()); + } else if (GetParam() == GameType::starfield) { + EXPECT_FLOAT_EQ(0.96f, plugin.GetHeaderVersion().value()); } else { EXPECT_FLOAT_EQ(0.94f, plugin.GetHeaderVersion().value()); } @@ -306,14 +304,13 @@ TEST_P(PluginTest, loadingWholePluginShouldReadFields) { EXPECT_EQ(0, plugin.GetOverrideRecordCount()); } else if (GetParam() == GameType::starfield) { Plugin master(game_.GetType(), - game_.GetCache(), - game_.DataPath() / (blankEsm), + game_.GetCache(), game_.DataPath() / "Blank.full.esm", true); const auto pluginsMetadata = Plugin::GetPluginsMetadata({&master}); EXPECT_NO_THROW(plugin.ResolveRecordIds(pluginsMetadata.get())); - EXPECT_EQ(4, plugin.GetOverrideRecordCount()); + EXPECT_EQ(1, plugin.GetOverrideRecordCount()); } else { EXPECT_EQ(4, plugin.GetOverrideRecordCount()); } @@ -356,17 +353,34 @@ TEST_P( plugin3.IsLightPlugin()); } +TEST_P( + PluginTest, + isMediumPluginShouldBeTrueForAMediumFlaggedPluginForStarfield) { + if (GetParam() != GameType::starfield) { + auto bytes = ReadFile(dataPath / blankEsm); + bytes[9] = 0x4; + WriteFile(dataPath / blankEsm, bytes); + } + + const auto pluginName = + GetParam() == GameType::starfield ? blankMediumEsm : blankEsm; + Plugin plugin(game_.GetType(), game_.GetCache(), game_.DataPath() / pluginName, + true); + + EXPECT_EQ(GetParam() == GameType::starfield, plugin.IsMediumPlugin()); +} + TEST_P(PluginTest, isOverridePluginShouldOnlyBeTrueForAStarfieldOverridePlugin) { - auto bytes = ReadFile(dataPath / blankDifferentPluginDependentEsp); + auto bytes = ReadFile(dataPath / blankMasterDependentEsp); bytes[9] = 0x2; - WriteFile(dataPath / blankDifferentPluginDependentEsp, bytes); + WriteFile(dataPath / blankMasterDependentEsp, bytes); Plugin plugin1( game_.GetType(), game_.GetCache(), game_.DataPath() / blankEsp, true); Plugin plugin2(game_.GetType(), game_.GetCache(), - game_.DataPath() / blankDifferentPluginDependentEsp, + game_.DataPath() / blankMasterDependentEsp, true); EXPECT_FALSE(plugin1.IsOverridePlugin()); @@ -379,7 +393,11 @@ TEST_P(PluginTest, loadingAPluginWithMastersShouldReadThemCorrectly) { game_.DataPath() / blankMasterDependentEsp, true); - EXPECT_EQ(std::vector({blankEsm}), plugin.GetMasters()); + if (GetParam() == GameType::starfield) { + EXPECT_EQ(std::vector({blankFullEsm}), plugin.GetMasters()); + } else { + EXPECT_EQ(std::vector({blankEsm}), plugin.GetMasters()); + } } TEST_P(PluginTest, loadingAPluginThatDoesNotExistShouldThrow) { @@ -494,9 +512,12 @@ TEST_P( TEST_P(PluginTest, loadsArchiveShouldReturnFalseForAPluginThatDoesNotLoadAnArchive) { + const auto pluginName = GetParam() == GameType::starfield + ? blankDifferentEsp + : blankDifferentMasterDependentEsp; EXPECT_FALSE(Plugin(game_.GetType(), game_.GetCache(), - game_.DataPath() / blankDifferentMasterDependentEsp, + game_.DataPath() / pluginName, true) .LoadsArchive()); } @@ -536,23 +557,41 @@ TEST_P( } } +TEST_P( + PluginTest, + isValidAsMediumPluginShouldReturnTrueOnlyForAStarfieldPluginWithNewFormIdsBetween0And0xFFFFInclusive) { + bool valid = + Plugin( + game_.GetType(), game_.GetCache(), dataPath / blankEsm, true) + .IsValidAsMediumPlugin(); + if (GetParam() == GameType::starfield) { + EXPECT_TRUE(valid); + } else { + EXPECT_FALSE(valid); + } +} + TEST_P( PluginTest, IsValidAsOverridePluginShouldOnlyReturnTrueForAStarfieldPluginWithNoNewRecords) { - Plugin plugin1( - game_.GetType(), game_.GetCache(), game_.DataPath() / blankEsp, false); + const auto sourcePluginName = GetParam() == GameType::starfield + ? blankFullEsm + : blankEsp; + const auto overridePluginName = GetParam() == GameType::starfield + ? blankMasterDependentEsp + : blankDifferentPluginDependentEsp; + + Plugin plugin1(game_.GetType(), + game_.GetCache(), + game_.DataPath() / sourcePluginName, + false); Plugin plugin2(game_.GetType(), game_.GetCache(), - game_.DataPath() / blankDifferentPluginDependentEsp, + game_.DataPath() / overridePluginName, false); if (GetParam() == GameType::starfield) { - Plugin master(game_.GetType(), - game_.GetCache(), - game_.DataPath() / blankDifferentEsp, - false); - - const auto pluginsMetadata = Plugin::GetPluginsMetadata({&master}); + const auto pluginsMetadata = Plugin::GetPluginsMetadata({&plugin1}); plugin2.ResolveRecordIds(pluginsMetadata.get()); plugin1.ResolveRecordIds(nullptr); @@ -605,8 +644,11 @@ TEST_P(PluginTest, TEST_P(PluginTest, doRecordsOverlapShouldReturnTrueIfOnePluginOverridesTheOthersRecords) { + const auto plugin1Name = + GetParam() == GameType::starfield ? blankFullEsm : blankEsm; + Plugin plugin1( - game_.GetType(), game_.GetCache(), game_.DataPath() / blankEsm, false); + game_.GetType(), game_.GetCache(), game_.DataPath() / plugin1Name, false); Plugin plugin2(game_.GetType(), game_.GetCache(), game_.DataPath() / (blankMasterDependentEsm + ".ghost"), @@ -615,7 +657,10 @@ TEST_P(PluginTest, if (GetParam() == GameType::starfield) { plugin1.ResolveRecordIds(nullptr); - const auto pluginsMetadata = Plugin::GetPluginsMetadata({&plugin1}); + Plugin master( + game_.GetType(), game_.GetCache(), game_.DataPath() / blankFullEsm, true); + + const auto pluginsMetadata = Plugin::GetPluginsMetadata({&master}); plugin2.ResolveRecordIds(pluginsMetadata.get()); } @@ -635,8 +680,11 @@ TEST_P(PluginTest, } TEST_P(PluginTest, getOverlapSizeShouldCountEachRecordOnce) { + const auto plugin1Name = + GetParam() == GameType::starfield ? blankFullEsm : blankEsm; + Plugin plugin1( - game_.GetType(), game_.GetCache(), game_.DataPath() / blankEsm, false); + game_.GetType(), game_.GetCache(), game_.DataPath() / plugin1Name, false); Plugin plugin2(game_.GetType(), game_.GetCache(), game_.DataPath() / (blankMasterDependentEsm + ".ghost"), @@ -646,15 +694,21 @@ TEST_P(PluginTest, getOverlapSizeShouldCountEachRecordOnce) { plugin1.ResolveRecordIds(nullptr); const auto pluginsMetadata = Plugin::GetPluginsMetadata({&plugin1}); + plugin2.ResolveRecordIds(pluginsMetadata.get()); - } - EXPECT_EQ(4, plugin1.GetOverlapSize({&plugin2, &plugin2})); + EXPECT_EQ(1, plugin1.GetOverlapSize({&plugin2, &plugin2})); + } else { + EXPECT_EQ(4, plugin1.GetOverlapSize({&plugin2, &plugin2})); + } } TEST_P(PluginTest, getOverlapSizeShouldCheckAgainstAllGivenPlugins) { + const auto plugin1Name = + GetParam() == GameType::starfield ? blankFullEsm : blankEsm; + Plugin plugin1( - game_.GetType(), game_.GetCache(), game_.DataPath() / blankEsm, false); + game_.GetType(), game_.GetCache(), game_.DataPath() / plugin1Name, false); Plugin plugin2( game_.GetType(), game_.GetCache(), game_.DataPath() / blankEsp, false); Plugin plugin3(game_.GetType(), @@ -668,9 +722,11 @@ TEST_P(PluginTest, getOverlapSizeShouldCheckAgainstAllGivenPlugins) { const auto pluginsMetadata = Plugin::GetPluginsMetadata({&plugin1}); plugin3.ResolveRecordIds(pluginsMetadata.get()); - } - EXPECT_EQ(4, plugin1.GetOverlapSize({&plugin2, &plugin3})); + EXPECT_EQ(1, plugin1.GetOverlapSize({&plugin2, &plugin3})); + } else { + EXPECT_EQ(4, plugin1.GetOverlapSize({&plugin2, &plugin3})); + } } TEST_P(PluginTest, diff --git a/src/tests/api/internals/sorting/plugin_graph_test.h b/src/tests/api/internals/sorting/plugin_graph_test.h index 16d38091..97a565ad 100644 --- a/src/tests/api/internals/sorting/plugin_graph_test.h +++ b/src/tests/api/internals/sorting/plugin_graph_test.h @@ -60,10 +60,14 @@ class TestPlugin : public PluginSortingInterface { bool IsLightPlugin() const override { return false; } + bool IsMediumPlugin() const override { return false; } + bool IsOverridePlugin() const override { return false; } bool IsValidAsLightPlugin() const override { return false; } + bool IsValidAsMediumPlugin() const override { return false; } + bool IsValidAsOverridePlugin() const override { return false; } bool IsEmpty() const override { return false; } diff --git a/src/tests/common_game_test_fixture.h b/src/tests/common_game_test_fixture.h index 38fa86ec..0dae0e17 100644 --- a/src/tests/common_game_test_fixture.h +++ b/src/tests/common_game_test_fixture.h @@ -65,6 +65,8 @@ class CommonGameTestFixture : public ::testing::TestWithParam { nonPluginFile("NotAPlugin.esm"), invalidPlugin("Invalid.esm"), blankEsm("Blank.esm"), + blankFullEsm("Blank.full.esm"), + blankMediumEsm("Blank.medium.esm"), blankDifferentEsm("Blank - Different.esm"), blankMasterDependentEsm("Blank - Master Dependent.esm"), blankDifferentMasterDependentEsm( @@ -97,19 +99,55 @@ class CommonGameTestFixture : public ::testing::TestWithParam { auto sourcePluginsPath = getSourcePluginsPath(); - copyPlugin(sourcePluginsPath, blankEsm); - copyPlugin(sourcePluginsPath, blankDifferentEsm); - copyPlugin(sourcePluginsPath, blankMasterDependentEsm); - copyPlugin(sourcePluginsPath, blankDifferentMasterDependentEsm); - copyPlugin(sourcePluginsPath, blankEsp); - copyPlugin(sourcePluginsPath, blankDifferentEsp); - copyPlugin(sourcePluginsPath, blankMasterDependentEsp); - copyPlugin(sourcePluginsPath, blankDifferentMasterDependentEsp); - copyPlugin(sourcePluginsPath, blankPluginDependentEsp); - copyPlugin(sourcePluginsPath, blankDifferentPluginDependentEsp); + if (GetParam() == GameType::starfield) { + copyPlugin(sourcePluginsPath, blankFullEsm); + copyPlugin(sourcePluginsPath, blankMediumEsm); + + std::filesystem::copy_file(sourcePluginsPath / blankFullEsm, + dataPath / blankEsm); + ASSERT_TRUE(exists(dataPath / blankEsm)); + + std::filesystem::copy_file(sourcePluginsPath / blankFullEsm, + dataPath / blankDifferentEsm); + ASSERT_TRUE(exists(dataPath / blankDifferentEsm)); + + std::filesystem::copy_file(sourcePluginsPath / "Blank - Override.full.esm", + dataPath / blankMasterDependentEsm); + ASSERT_TRUE(exists(dataPath / blankMasterDependentEsm)); + + std::filesystem::copy_file(sourcePluginsPath / "Blank.esp", + dataPath / blankEsp); + ASSERT_TRUE(exists(dataPath / blankEsp)); + + std::filesystem::copy_file(sourcePluginsPath / blankEsp, + dataPath / blankDifferentEsp); + ASSERT_TRUE(exists(dataPath / blankDifferentEsp)); + + std::filesystem::copy_file(sourcePluginsPath / "Blank - Override.esp", + dataPath / blankMasterDependentEsp); + ASSERT_TRUE(exists(dataPath / blankMasterDependentEsp)); + } else { + copyPlugin(sourcePluginsPath, blankEsm); + copyPlugin(sourcePluginsPath, blankDifferentEsm); + copyPlugin(sourcePluginsPath, blankMasterDependentEsm); + copyPlugin(sourcePluginsPath, blankDifferentMasterDependentEsm); + copyPlugin(sourcePluginsPath, blankEsp); + copyPlugin(sourcePluginsPath, blankDifferentEsp); + copyPlugin(sourcePluginsPath, blankMasterDependentEsp); + copyPlugin(sourcePluginsPath, blankDifferentMasterDependentEsp); + copyPlugin(sourcePluginsPath, blankPluginDependentEsp); + copyPlugin(sourcePluginsPath, blankDifferentPluginDependentEsp); + } + if (supportsLightPlugins(GetParam())) { - copyPlugin(sourcePluginsPath, blankEsl); + if (GetParam() == GameType::starfield) { + std::filesystem::copy_file(sourcePluginsPath / "Blank.small.esm", + dataPath / blankEsl); + ASSERT_TRUE(exists(dataPath / blankEsl)); + } else { + copyPlugin(sourcePluginsPath, blankEsl); + } } // Make sure the game master file exists. @@ -211,22 +249,40 @@ class CommonGameTestFixture : public ::testing::TestWithParam { } std::vector> getInitialLoadOrder() const { - auto loadOrder = std::vector>({ - {masterFile, true}, - {blankEsm, true}, - {blankDifferentEsm, false}, - {blankMasterDependentEsm, false}, - {blankDifferentMasterDependentEsm, false}, - {blankEsp, false}, - {blankDifferentEsp, false}, - {blankMasterDependentEsp, false}, - {blankDifferentMasterDependentEsp, true}, - {blankPluginDependentEsp, false}, - {blankDifferentPluginDependentEsp, false}, - }); - - if (supportsLightPlugins(GetParam())) { - loadOrder.insert(loadOrder.begin() + 5, std::make_pair(blankEsl, false)); + std::vector> loadOrder; + + if (GetParam() == GameType::starfield) { + loadOrder = { + {masterFile, true}, + {blankEsm, true}, + {blankDifferentEsm, false}, + {blankFullEsm, false}, + {blankMasterDependentEsm, false}, + {blankMediumEsm, false}, + {blankEsl, false}, + {blankEsp, false}, + {blankDifferentEsp, false}, + {blankMasterDependentEsp, false}, + }; + } else { + loadOrder = { + {masterFile, true}, + {blankEsm, true}, + {blankDifferentEsm, false}, + {blankMasterDependentEsm, false}, + {blankDifferentMasterDependentEsm, false}, + {blankEsp, false}, + {blankDifferentEsp, false}, + {blankMasterDependentEsp, false}, + {blankDifferentMasterDependentEsp, true}, + {blankPluginDependentEsp, false}, + {blankDifferentPluginDependentEsp, false}, + }; + + if (supportsLightPlugins(GetParam())) { + loadOrder.insert(loadOrder.begin() + 5, + std::make_pair(blankEsl, false)); + } } return loadOrder; @@ -238,6 +294,8 @@ class CommonGameTestFixture : public ::testing::TestWithParam { return absolute("./Morrowind/Data Files"); else if (GetParam() == GameType::tes4) return absolute("./Oblivion/Data"); + else if (GetParam() == GameType::starfield) + return absolute("./Starfield/Data"); else if (supportsLightPlugins(GetParam())) return absolute("./SkyrimSE/Data"); else @@ -267,6 +325,8 @@ class CommonGameTestFixture : public ::testing::TestWithParam { const std::string nonPluginFile; const std::string invalidPlugin; const std::string blankEsm; + const std::string blankFullEsm; + const std::string blankMediumEsm; const std::string blankDifferentEsm; const std::string blankMasterDependentEsm; const std::string blankDifferentMasterDependentEsm; @@ -315,6 +375,8 @@ class CommonGameTestFixture : public ::testing::TestWithParam { return 0x790DC6FB; case GameType::tes4: return 0x374E2A6F; + case GameType::starfield: + return 0xDE586309; default: return 0x6A1273DC; }