diff --git a/cpp/src/tests/api/interface/api_game_operations_test.h b/cpp/src/tests/api/interface/api_game_operations_test.h index 5efb5804..17adacb8 100644 --- a/cpp/src/tests/api/interface/api_game_operations_test.h +++ b/cpp/src/tests/api/interface/api_game_operations_test.h @@ -51,8 +51,6 @@ protected: void SetUp() override { CommonGameTestFixture::SetUp(); - ASSERT_FALSE(std::filesystem::exists(masterlistPath)); - handle_ = CreateGameHandle(GetParam(), gamePath, localPath); } @@ -103,6 +101,7 @@ protected: << " - name: " << blankEsp << endl << " after:" << endl << " - " << blankDifferentMasterDependentEsp << endl + << " - " << BLANK_OVERRIDE_ESP << endl << " - name: " << blankDifferentMasterDependentEsp << endl << " after:" << endl << " - " << blankMasterDependentEsp << endl diff --git a/cpp/src/tests/api/interface/create_game_handle_test.h b/cpp/src/tests/api/interface/create_game_handle_test.h index e6c283e0..d390ec9b 100644 --- a/cpp/src/tests/api/interface/create_game_handle_test.h +++ b/cpp/src/tests/api/interface/create_game_handle_test.h @@ -134,6 +134,8 @@ TEST_P(CreateGameHandleTest, shouldSucceedIfPassedALocalPathThatDoesNotExist) { } TEST_P(CreateGameHandleTest, shouldThrowIfPassedALocalPathThatIsNotADirectory) { + touch(dataPath / blankEsm); + EXPECT_THROW(CreateGameHandle(GetParam(), gamePath, dataPath / blankEsm), std::invalid_argument); } diff --git a/cpp/src/tests/api/interface/database_interface_test.h b/cpp/src/tests/api/interface/database_interface_test.h index a6095a51..9b6aef44 100644 --- a/cpp/src/tests/api/interface/database_interface_test.h +++ b/cpp/src/tests/api/interface/database_interface_test.h @@ -37,12 +37,6 @@ protected: minimalOutputPath_(localPath / "minimal.yml"), generalUserlistMessage("A general userlist message.") {} - void SetUp() override { - ApiGameOperationsTest::SetUp(); - - ASSERT_FALSE(std::filesystem::exists(minimalOutputPath_)); - } - std::string GetExpectedMinimalContent() const { using std::endl; @@ -285,6 +279,7 @@ TEST_P(DatabaseInterfaceTest, writeUserMetadataShouldShouldWriteUserMetadata) { } TEST_P(DatabaseInterfaceTest, evaluateShouldReturnTrueIfTheConditionIsTrue) { + touch(dataPath / BLANK_ESP); EXPECT_TRUE(handle_->GetDatabase().Evaluate("file(\"Blank.esp\")")); } @@ -294,11 +289,13 @@ TEST_P(DatabaseInterfaceTest, evaluateShouldReturnFalseIfTheConditionIsFalse) { TEST_P(DatabaseInterfaceTest, clearConditionCacheShouldCauseConditionsToBeEvaluatedFromScratch) { + touch(dataPath / BLANK_ESP); + const auto condition = "file(\"Blank.esp\")"; EXPECT_TRUE(handle_->GetDatabase().Evaluate(condition)); - std::filesystem::remove(dataPath / "Blank.esp"); + std::filesystem::remove(dataPath / BLANK_ESP); EXPECT_TRUE(handle_->GetDatabase().Evaluate(condition)); diff --git a/cpp/src/tests/api/interface/game_interface_test.h b/cpp/src/tests/api/interface/game_interface_test.h index 2e2cfa07..83dbcf6d 100644 --- a/cpp/src/tests/api/interface/game_interface_test.h +++ b/cpp/src/tests/api/interface/game_interface_test.h @@ -30,46 +30,19 @@ 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() : - 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)); + GameInterfaceTest() {} - if (GetParam() == GameType::starfield) { - pluginsToLoad = { - masterFile, - blankEsm, - blankFullEsm, - blankMasterDependentEsm, - blankEsp, - blankMasterDependentEsp, - }; - } else { - pluginsToLoad = { - // These are all ASCII filenames. - masterFile, - blankEsm, - blankDifferentEsm, - blankMasterDependentEsm, - blankDifferentMasterDependentEsm, - blankEsp, - blankDifferentEsp, - blankMasterDependentEsp, - blankDifferentMasterDependentEsp, - blankPluginDependentEsp, - blankDifferentPluginDependentEsp, - }; - } + void createNonPluginFile(std::string_view filename) { + const auto path = dataPath / std::filesystem::u8path(filename); + + std::ofstream out(path); + out << "This isn't a valid plugin file."; + out.close(); + + ASSERT_TRUE(exists(path)); } - - const std::string emptyFile; - const std::string nonAsciiEsm; - std::vector pluginsToLoad; }; // Pass an empty first argument, as it's a prefix for the test instantation, @@ -110,6 +83,7 @@ TEST_P(GameInterfaceTest, // Set no additional data paths to avoid picking up non-test plugins on PCs // which have Starfield or Fallout 4 installed. handle_->SetAdditionalDataPaths({}); + setLoadOrder({}); handle_->LoadCurrentLoadOrderState(); auto loadOrder = handle_->GetLoadOrder(); @@ -138,37 +112,48 @@ TEST_P(GameInterfaceTest, } TEST_P(GameInterfaceTest, isValidPluginShouldReturnTrueForAValidPlugin) { - EXPECT_TRUE(handle_->IsValidPlugin(blankEsm)); + copyPlugin(BLANK_ESP); + + EXPECT_TRUE(handle_->IsValidPlugin(BLANK_ESP)); } TEST_P(GameInterfaceTest, isValidPluginShouldReturnTrueForAValidNonAsciiPlugin) { - EXPECT_TRUE(handle_->IsValidPlugin(std::filesystem::u8path(nonAsciiEsm))); + copyPlugin(BLANK_ESP, NON_ASCII_ESP); + + EXPECT_TRUE(handle_->IsValidPlugin(std::filesystem::u8path(NON_ASCII_ESP))); } TEST_P(GameInterfaceTest, isValidPluginShouldReturnFalseForANonPluginFile) { - EXPECT_FALSE(handle_->IsValidPlugin(nonPluginFile)); + createNonPluginFile(NON_PLUGIN_FILE); + + EXPECT_FALSE(handle_->IsValidPlugin(NON_PLUGIN_FILE)); } TEST_P(GameInterfaceTest, isValidPluginShouldReturnFalseForAnEmptyFile) { // Write out an empty file. - touch(dataPath / emptyFile); - ASSERT_TRUE(std::filesystem::exists(dataPath / emptyFile)); + const auto emptyFilePath = dataPath / "EmptyFile.esm"; + touch(emptyFilePath); + ASSERT_TRUE(std::filesystem::exists(emptyFilePath)); - EXPECT_FALSE(handle_->IsValidPlugin(emptyFile)); + EXPECT_FALSE(handle_->IsValidPlugin(emptyFilePath)); } TEST_P(GameInterfaceTest, isValidPluginShouldResolveRelativePathsRelativeToDataPath) { - const auto path = ".." / dataPath.filename() / blankEsm; + copyPlugin(BLANK_ESP); + + const auto path = ".." / dataPath.filename() / BLANK_ESP; EXPECT_TRUE(handle_->IsValidPlugin(path)); } TEST_P(GameInterfaceTest, isValidPluginShouldUseAbsolutePathsAsGiven) { + copyPlugin(BLANK_ESP); + ASSERT_TRUE(dataPath.is_absolute()); - const auto path = dataPath / std::filesystem::u8path(blankEsm); + const auto path = dataPath / std::filesystem::u8path(BLANK_ESP); EXPECT_TRUE(handle_->IsValidPlugin(path)); } @@ -176,67 +161,72 @@ TEST_P(GameInterfaceTest, isValidPluginShouldUseAbsolutePathsAsGiven) { TEST_P( GameInterfaceTest, isValidPluginShouldTryGhostedPathIfGivenPluginDoesNotExistExceptForOpenMW) { + const auto ghostedName = std::string(BLANK_ESP) + ".ghost"; + copyPlugin(BLANK_ESP, ghostedName); + 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)); + EXPECT_FALSE(handle_->IsValidPlugin(ghostedName)); + EXPECT_FALSE(handle_->IsValidPlugin(BLANK_ESP)); } else { - EXPECT_TRUE(handle_->IsValidPlugin(blankMasterDependentEsm)); + EXPECT_TRUE(handle_->IsValidPlugin(ghostedName)); + EXPECT_TRUE(handle_->IsValidPlugin(BLANK_ESP)); } } -TEST_P(GameInterfaceTest, - loadPluginsWithHeadersOnlyTrueShouldLoadTheHeadersOfAllGivenPlugins) { - 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, loadPluginsShouldTrimDotGhostFileExtensions) { + const auto ghostedName = std::string(BLANK_ESP) + ".ghost"; + copyPlugin(BLANK_ESP, ghostedName); + if (GetParam() == GameType::openmw) { // Ghosting is not supported for OpenMW. - EXPECT_THROW( - handle_->LoadPlugins({blankMasterDependentEsm + ".ghost"}, true), - std::invalid_argument); + EXPECT_THROW(handle_->LoadPlugins({ghostedName}, true), + std::invalid_argument); return; } else { - handle_->LoadPlugins({blankMasterDependentEsm + ".ghost"}, true); + handle_->LoadPlugins({ghostedName}, true); } EXPECT_EQ(1, handle_->GetLoadedPlugins().size()); - ASSERT_NO_THROW(handle_->GetPlugin(blankMasterDependentEsm)); - const auto plugin = handle_->GetPlugin(blankMasterDependentEsm); + const auto plugin = handle_->GetPlugin(BLANK_ESP); ASSERT_NE(nullptr, plugin); - EXPECT_EQ(blankMasterDependentEsm, plugin->GetName()); + EXPECT_EQ(BLANK_ESP, plugin->GetName()); +} + +TEST_P(GameInterfaceTest, loadPluginsShouldResolveGhostedPlugins) { + const auto ghostedName = std::string(BLANK_ESP) + ".ghost"; + copyPlugin(BLANK_ESP, ghostedName); + + if (GetParam() == GameType::openmw) { + // Ghosting is not supported for OpenMW. + EXPECT_THROW(handle_->LoadPlugins({BLANK_ESP}, true), + std::invalid_argument); + return; + } else { + handle_->LoadPlugins({BLANK_ESP}, true); + } + + EXPECT_EQ(1, handle_->GetLoadedPlugins().size()); + + const auto plugin = handle_->GetPlugin(BLANK_ESP); + ASSERT_NE(nullptr, plugin); + EXPECT_EQ(BLANK_ESP, plugin->GetName()); } TEST_P(GameInterfaceTest, - loadPluginsWithHeadersOnlyTrueShouldLoadTheHeadersOfGivenPlugins) { - handle_->LoadPlugins(pluginsToLoad, true); + loadPluginsWithHeadersOnlyTrueShouldLoadTheHeadersOfAllGivenPlugins) { + const auto pluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + copyPlugin(pluginName); + copyPlugin(BLANK_ESP); - if (GetParam() == GameType::starfield) { - EXPECT_EQ(6, handle_->GetLoadedPlugins().size()); - } else { - EXPECT_EQ(11, handle_->GetLoadedPlugins().size()); - } + handle_->LoadPlugins({pluginName, BLANK_ESP}, true); + + EXPECT_EQ(2, handle_->GetLoadedPlugins().size()); // Check that one plugin's header has been read. - ASSERT_NO_THROW(handle_->GetPlugin(masterFile)); - auto plugin = handle_->GetPlugin(masterFile); + auto plugin = handle_->GetPlugin(pluginName); + ASSERT_NE(nullptr, plugin); EXPECT_EQ("5.0", plugin->GetVersion().value()); // Check that only the header has been read. @@ -244,18 +234,19 @@ TEST_P(GameInterfaceTest, } TEST_P(GameInterfaceTest, - loadPluginsWithHeadersOnlyFalseShouldFullyLoadAllInstalledPlugins) { - handle_->LoadPlugins(pluginsToLoad, false); + loadPluginsWithHeadersOnlyFalseShouldFullyLoadAllGivenPlugins) { + const auto pluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + copyPlugin(pluginName); + copyPlugin(BLANK_ESP); - if (GetParam() == GameType::starfield) { - EXPECT_EQ(6, handle_->GetLoadedPlugins().size()); - } else { - EXPECT_EQ(11, handle_->GetLoadedPlugins().size()); - } + handle_->LoadPlugins({pluginName, BLANK_ESP}, false); + + EXPECT_EQ(2, handle_->GetLoadedPlugins().size()); // Check that one plugin's header has been read. - ASSERT_NO_THROW(handle_->GetPlugin(masterFile)); - auto plugin = handle_->GetPlugin(masterFile); + auto plugin = handle_->GetPlugin(pluginName); + ASSERT_NE(nullptr, plugin); EXPECT_EQ("5.0", plugin->GetVersion().value()); // Check that not only the header has been read. @@ -263,11 +254,18 @@ TEST_P(GameInterfaceTest, } TEST_P(GameInterfaceTest, loadPluginsWithANonAsciiPluginShouldLoadIt) { - handle_->LoadPlugins({std::filesystem::u8path(nonAsciiEsm)}, false); + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_FULL_ESM, NON_ASCII_ESP); + } else { + copyPlugin(BLANK_ESM, NON_ASCII_ESP); + } + + handle_->LoadPlugins({std::filesystem::u8path(NON_ASCII_ESP)}, false); EXPECT_EQ(1, handle_->GetLoadedPlugins().size()); // Check that one plugin's header has been read. - auto plugin = handle_->GetPlugin(nonAsciiEsm); + auto plugin = handle_->GetPlugin(NON_ASCII_ESP); + ASSERT_NE(nullptr, plugin); EXPECT_EQ("5.0", plugin->GetVersion().value()); // Check that not only the header has been read. @@ -277,17 +275,20 @@ TEST_P(GameInterfaceTest, loadPluginsWithANonAsciiPluginShouldLoadIt) { 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); + const auto pluginName = + u8"\u2551\u00BB\u00C1\u2510\u2557\u00FE\u00C3\u00CE.esp"; + copyPlugin(BLANK_ESP, pluginName); - EXPECT_NO_THROW(handle_->LoadPlugins({pluginName}, false)); + EXPECT_NO_THROW( + handle_->LoadPlugins({std::filesystem::u8path(pluginName)}, false)); } TEST_P(GameInterfaceTest, loadPluginsWithANonPluginShouldNotAddItToTheLoadedPlugins) { + createNonPluginFile(NON_PLUGIN_FILE); + try { - handle_->LoadPlugins({nonPluginFile}, false); + handle_->LoadPlugins({NON_PLUGIN_FILE}, false); FAIL(); } catch (std::invalid_argument& e) { EXPECT_TRUE(startsWith( @@ -301,49 +302,56 @@ TEST_P(GameInterfaceTest, 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); + copyPlugin(BLANK_ESP, INVALID_PLUGIN); + + std::ofstream out(dataPath / INVALID_PLUGIN, std::fstream::app); out << "GRUP0"; out.close(); - ASSERT_NO_THROW(handle_->LoadPlugins({invalidPlugin}, false)); + ASSERT_NO_THROW(handle_->LoadPlugins({INVALID_PLUGIN}, false)); ASSERT_TRUE(handle_->GetLoadedPlugins().empty()); } TEST_P(GameInterfaceTest, loadPluginsShouldNotClearThePluginsCache) { - handle_->LoadPlugins({std::filesystem::u8path(blankEsm)}, true); - ASSERT_EQ(1, handle_->GetLoadedPlugins().size()); - ASSERT_NE(nullptr, handle_->GetPlugin(blankEsm)); + const auto pluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + copyPlugin(pluginName); + copyPlugin(BLANK_ESP); - handle_->LoadPlugins({std::filesystem::u8path(blankEsp)}, true); + handle_->LoadPlugins({std::filesystem::u8path(pluginName)}, true); + ASSERT_EQ(1, handle_->GetLoadedPlugins().size()); + ASSERT_NE(nullptr, handle_->GetPlugin(pluginName)); + + handle_->LoadPlugins({std::filesystem::u8path(BLANK_ESP)}, true); EXPECT_EQ(2, handle_->GetLoadedPlugins().size()); - ASSERT_NE(nullptr, handle_->GetPlugin(blankEsm)); - ASSERT_NE(nullptr, handle_->GetPlugin(blankEsp)); + ASSERT_NE(nullptr, handle_->GetPlugin(pluginName)); + ASSERT_NE(nullptr, handle_->GetPlugin(BLANK_ESP)); } TEST_P(GameInterfaceTest, loadPluginsShouldReplaceCacheEntriesForTheGivenPlugins) { - handle_->LoadPlugins({std::filesystem::u8path(blankEsm)}, true); - const auto pointer = handle_->GetPlugin(blankEsm); + copyPlugin(BLANK_ESP); + + handle_->LoadPlugins({std::filesystem::u8path(BLANK_ESP)}, true); + const auto pointer = handle_->GetPlugin(BLANK_ESP); ASSERT_NE(nullptr, pointer); - handle_->LoadPlugins({std::filesystem::u8path(blankEsm)}, false); + handle_->LoadPlugins({std::filesystem::u8path(BLANK_ESP)}, false); - const auto newPointer = handle_->GetPlugin(blankEsm); + const auto newPointer = handle_->GetPlugin(BLANK_ESP); ASSERT_NE(nullptr, newPointer); EXPECT_NE(pointer, newPointer); } TEST_P(GameInterfaceTest, loadPluginsShouldThrowIfGivenVectorElementsWithTheSameFilename) { - const auto dataPluginPath = dataPath / std::filesystem::u8path(blankEsm); + copyPlugin(BLANK_ESP); + + const auto dataPluginPath = dataPath / std::filesystem::u8path(BLANK_ESP); const auto sourcePluginPath = - getSourcePluginsPath() / std::filesystem::u8path(blankEsm); + getSourcePluginsPath() / std::filesystem::u8path(BLANK_ESP); EXPECT_THROW(handle_->LoadPlugins(std::vector( {dataPluginPath, sourcePluginPath}), @@ -353,67 +361,85 @@ TEST_P(GameInterfaceTest, TEST_P(GameInterfaceTest, loadPluginsShouldResolveRelativePathsRelativeToDataPath) { - const auto relativePath = ".." / dataPath.filename() / blankEsm; + copyPlugin(BLANK_ESP); + + const auto relativePath = ".." / dataPath.filename() / BLANK_ESP; handle_->LoadPlugins(std::vector({relativePath}), true); - EXPECT_NE(nullptr, handle_->GetPlugin(blankEsm)); + EXPECT_NE(nullptr, handle_->GetPlugin(BLANK_ESP)); } TEST_P(GameInterfaceTest, loadPluginsShouldUseAbsolutePathsAsGiven) { - const auto absolutePath = dataPath / std::filesystem::u8path(blankEsm); + copyPlugin(BLANK_ESP); + + const auto absolutePath = dataPath / std::filesystem::u8path(BLANK_ESP); handle_->LoadPlugins(std::vector({absolutePath}), true); - EXPECT_NE(nullptr, handle_->GetPlugin(blankEsm)); + EXPECT_NE(nullptr, handle_->GetPlugin(BLANK_ESP)); } TEST_P( GameInterfaceTest, loadPluginsShouldThrowIfFullyLoadingAPluginWithAMissingMasterIfGameIsMorrowindOrStarfield) { - const auto pluginName = - GetParam() == GameType::starfield ? blankFullEsm : blankEsm; - - std::filesystem::remove(dataPath / pluginName); + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_OVERRIDE_FULL_ESM, BLANK_MASTER_DEPENDENT_ESM); + } else { + copyPlugin(BLANK_MASTER_DEPENDENT_ESM); + } if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw || GetParam() == GameType::starfield) { - EXPECT_THROW(handle_->LoadPlugins({blankMasterDependentEsm}, false), + EXPECT_THROW(handle_->LoadPlugins({BLANK_MASTER_DEPENDENT_ESM}, false), PluginNotLoadedError); } else { - handle_->LoadPlugins({blankMasterDependentEsm}, false); + handle_->LoadPlugins({BLANK_MASTER_DEPENDENT_ESM}, false); - EXPECT_NE(nullptr, handle_->GetPlugin(blankMasterDependentEsm)); + EXPECT_NE(nullptr, handle_->GetPlugin(BLANK_MASTER_DEPENDENT_ESM)); } } TEST_P( GameInterfaceTest, loadPluginsShouldThrowIfAPluginHasAMasterThatIsNotInTheInputAndIsNotAlreadyLoadedAndGameIsMorrowindOrStarfield) { + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_OVERRIDE_FULL_ESM, BLANK_MASTER_DEPENDENT_ESM); + } else { + copyPlugin(BLANK_MASTER_DEPENDENT_ESM); + } + if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw || GetParam() == GameType::starfield) { - EXPECT_THROW(handle_->LoadPlugins({blankMasterDependentEsm}, false), + EXPECT_THROW(handle_->LoadPlugins({BLANK_MASTER_DEPENDENT_ESM}, false), PluginNotLoadedError); } else { - handle_->LoadPlugins({blankMasterDependentEsm}, false); + handle_->LoadPlugins({BLANK_MASTER_DEPENDENT_ESM}, false); - EXPECT_NE(nullptr, handle_->GetPlugin(blankMasterDependentEsm)); + EXPECT_NE(nullptr, handle_->GetPlugin(BLANK_MASTER_DEPENDENT_ESM)); } } TEST_P( GameInterfaceTest, loadPluginsShouldNotThrowIfAPluginHasAMasterThatIsNotInTheInputButIsAlreadyLoaded) { - const auto pluginName = - GetParam() == GameType::starfield ? blankFullEsm : blankEsm; + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_FULL_ESM); + copyPlugin(BLANK_OVERRIDE_FULL_ESM, BLANK_MASTER_DEPENDENT_ESM); - handle_->LoadPlugins({pluginName}, true); + handle_->LoadPlugins({BLANK_FULL_ESM}, true); + } else { + copyPlugin(BLANK_ESM); + copyPlugin(BLANK_MASTER_DEPENDENT_ESM); - handle_->LoadPlugins({blankMasterDependentEsm}, false); + handle_->LoadPlugins({BLANK_ESM}, true); + } - EXPECT_NE(nullptr, handle_->GetPlugin(blankMasterDependentEsm)); + handle_->LoadPlugins({BLANK_MASTER_DEPENDENT_ESM}, false); + + EXPECT_NE(nullptr, handle_->GetPlugin(BLANK_MASTER_DEPENDENT_ESM)); } TEST_P(GameInterfaceTest, @@ -424,7 +450,17 @@ TEST_P(GameInterfaceTest, } TEST_P(GameInterfaceTest, sortPluginsShouldOnlySortTheGivenPlugins) { - handle_->LoadPlugins(GetInstalledPlugins(), false); + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_FULL_ESM, BLANK_ESM); + copyPlugin(BLANK_ESP); + copyPlugin(BLANK_ESP, BLANK_DIFFERENT_ESP); + } else { + copyPlugin(BLANK_ESM); + copyPlugin(BLANK_ESP); + copyPlugin(BLANK_DIFFERENT_ESP); + } + + handle_->LoadPlugins({BLANK_ESM, BLANK_ESP, BLANK_DIFFERENT_ESP}, false); std::vector plugins{blankEsp, blankDifferentEsp}; const auto sorted = handle_->SortPlugins(plugins); @@ -434,42 +470,41 @@ TEST_P(GameInterfaceTest, sortPluginsShouldOnlySortTheGivenPlugins) { 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 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, + std::vector initialOrder; + if (GetParam() == GameType::starfield) { + initialOrder = { + blankFullEsm, + std::string(BLANK_OVERRIDE_FULL_ESM), blankEsp, - blankPluginDependentEsp, - masterFile, - blankDifferentMasterDependentEsp, + std::string(BLANK_OVERRIDE_ESP), }; } else { - expectedSortedOrder = getLoadOrder(); + initialOrder = { + blankEsm, + blankDifferentEsm, + blankMasterDependentEsm, + blankDifferentMasterDependentEsm, + blankEsp, + blankDifferentEsp, + blankMasterDependentEsp, + blankDifferentMasterDependentEsp, + blankPluginDependentEsp, + blankDifferentPluginDependentEsp, + }; } + std::vector pluginsToLoad; + for (const auto& plugin : initialOrder) { + copyPlugin(plugin); + pluginsToLoad.push_back(std::filesystem::u8path(plugin)); + } + + handle_->LoadPlugins(pluginsToLoad, false); + // 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; + const auto sorted = handle_->SortPlugins(initialOrder); + ASSERT_EQ(initialOrder, sorted) << " for sort " << i; } } @@ -485,6 +520,13 @@ TEST_P(GameInterfaceTest, sortPluginsShouldThrowIfAGivenPluginIsNotLoaded) { } TEST_P(GameInterfaceTest, sortPluginsShouldThrowIfACyclicInteractionOccurs) { + copyPlugin(BLANK_ESP); + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_ESP, BLANK_DIFFERENT_ESP); + } else { + copyPlugin(BLANK_DIFFERENT_ESP); + } + std::vector plugins{blankEsp, blankDifferentEsp}; handle_->LoadPlugins({blankEsp, blankDifferentEsp}, false); @@ -511,13 +553,15 @@ TEST_P(GameInterfaceTest, sortPluginsShouldThrowIfACyclicInteractionOccurs) { } TEST_P(GameInterfaceTest, clearLoadedPluginsShouldClearThePluginsCache) { - handle_->LoadPlugins({std::filesystem::u8path(blankEsm)}, true); - const auto pointer = handle_->GetPlugin(blankEsm); + copyPlugin(BLANK_ESP); + + handle_->LoadPlugins({std::filesystem::u8path(BLANK_ESP)}, true); + const auto pointer = handle_->GetPlugin(BLANK_ESP); ASSERT_NE(nullptr, pointer); handle_->ClearLoadedPlugins(); - EXPECT_EQ(nullptr, handle_->GetPlugin(blankEsm)); + EXPECT_EQ(nullptr, handle_->GetPlugin(BLANK_ESP)); } TEST_P(GameInterfaceTest, getPluginThatIsNotCachedShouldReturnANullPointer) { @@ -526,10 +570,12 @@ TEST_P(GameInterfaceTest, getPluginThatIsNotCachedShouldReturnANullPointer) { TEST_P(GameInterfaceTest, getPluginReturnsDifferentPointersForConsecutiveCallsGivenTheSamePlugin) { - handle_->LoadPlugins({std::filesystem::u8path(blankEsm)}, true); + copyPlugin(BLANK_ESP); - const auto pointer1 = handle_->GetPlugin(blankEsm); - const auto pointer2 = handle_->GetPlugin(blankEsm); + handle_->LoadPlugins({std::filesystem::u8path(BLANK_ESP)}, true); + + const auto pointer1 = handle_->GetPlugin(BLANK_ESP); + const auto pointer2 = handle_->GetPlugin(BLANK_ESP); EXPECT_NE(pointer1, pointer2); } @@ -541,7 +587,9 @@ TEST_P(GameInterfaceTest, TEST_P(GameInterfaceTest, getLoadedPluginsReturnsDifferentPointersForConsecutiveCalls) { - handle_->LoadPlugins({std::filesystem::u8path(blankEsm)}, true); + copyPlugin(BLANK_ESP); + + handle_->LoadPlugins({std::filesystem::u8path(BLANK_ESP)}, true); const auto pointers1 = handle_->GetLoadedPlugins(); const auto pointers2 = handle_->GetLoadedPlugins(); @@ -550,19 +598,35 @@ TEST_P(GameInterfaceTest, } TEST_P(GameInterfaceTest, sortPluginsShouldSucceedIfPassedValidArguments) { + std::vector initialOrder; std::vector expectedOrder; if (GetParam() == GameType::starfield) { - expectedOrder = { - masterFile, - blankEsm, + initialOrder = { blankFullEsm, - blankMasterDependentEsm, + std::string(BLANK_OVERRIDE_FULL_ESM), + blankEsp, + std::string(BLANK_OVERRIDE_ESP), + }; + expectedOrder = { + blankFullEsm, + std::string(BLANK_OVERRIDE_FULL_ESM), + std::string(BLANK_OVERRIDE_ESP), blankEsp, - blankMasterDependentEsp, }; } else { + initialOrder = { + blankEsm, + blankDifferentEsm, + blankMasterDependentEsm, + blankDifferentMasterDependentEsm, + blankEsp, + blankDifferentEsp, + blankMasterDependentEsp, + blankDifferentMasterDependentEsp, + blankPluginDependentEsp, + blankDifferentPluginDependentEsp, + }; expectedOrder = { - masterFile, blankEsm, blankMasterDependentEsm, blankDifferentEsm, @@ -577,71 +641,104 @@ TEST_P(GameInterfaceTest, sortPluginsShouldSucceedIfPassedValidArguments) { } if (GetParam() == GameType::fo4 || GetParam() == GameType::tes5se) { - expectedOrder.insert(expectedOrder.begin() + 5, blankEsl); + initialOrder.push_back(blankEsl); + expectedOrder.insert(expectedOrder.begin() + 4, blankEsl); } + std::vector pluginsToLoad; + for (const auto& plugin : initialOrder) { + copyPlugin(plugin); + pluginsToLoad.push_back(std::filesystem::u8path(plugin)); + } + + handle_->LoadPlugins(pluginsToLoad, false); + ASSERT_NO_THROW(GenerateMasterlist()); ASSERT_NO_THROW(handle_->GetDatabase().LoadMasterlist(masterlistPath)); - if (GetParam() == GameType::fo4 || GetParam() == GameType::tes5se) { - pluginsToLoad.push_back(blankEsl); - } - - handle_->LoadCurrentLoadOrderState(); - handle_->LoadPlugins(pluginsToLoad, false); - - std::vector pluginsToSort; - for (const auto& plugin : pluginsToLoad) { - pluginsToSort.push_back(plugin.filename().u8string()); - } - - std::vector actualOrder = handle_->SortPlugins(pluginsToSort); + const auto actualOrder = handle_->SortPlugins(initialOrder); EXPECT_EQ(expectedOrder, actualOrder); } +TEST_P(GameInterfaceTest, sortPluginsShouldSupportGhostedPlugins) { + if (GetParam() == GameType::openmw) { + return; + } + + copyPlugin(BLANK_ESP, "Blank.esp.ghost"); + + handle_->LoadPlugins({BLANK_ESP}, false); + + const auto sortedOrder = handle_->SortPlugins( + {std::string(BLANK_ESP)}); + + std::vector expectedOrder{std::string(BLANK_ESP)}; + + EXPECT_EQ(expectedOrder, sortedOrder); +} + TEST_P(GameInterfaceTest, isPluginActiveShouldReturnTrueIfTheGivenPluginIsActive) { + copyPlugin(BLANK_ESP); + setLoadOrder({{std::string(BLANK_ESP), true}}); + handle_->LoadCurrentLoadOrderState(); - EXPECT_TRUE(handle_->IsPluginActive(blankEsm)); + EXPECT_TRUE(handle_->IsPluginActive(std::string(BLANK_ESP))); } TEST_P(GameInterfaceTest, isPluginActiveShouldReturnFalseIfTheGivenPluginIsNotActive) { + copyPlugin(BLANK_ESP); + setLoadOrder({}); + handle_->LoadCurrentLoadOrderState(); + EXPECT_FALSE(handle_->IsPluginActive(blankEsp)); } TEST_P(GameInterfaceTest, isPluginActiveShouldActivePluginAsActiveWithHeaderLoaded) { + copyPlugin(BLANK_ESP); + setLoadOrder({{std::string(BLANK_ESP), true}}); + handle_->LoadCurrentLoadOrderState(); - ASSERT_NO_THROW(handle_->LoadPlugins({blankEsm}, true)); + ASSERT_NO_THROW(handle_->LoadPlugins({BLANK_ESP}, true)); - EXPECT_TRUE(handle_->IsPluginActive(blankEsm)); + EXPECT_TRUE(handle_->IsPluginActive(std::string(BLANK_ESP))); } TEST_P(GameInterfaceTest, isPluginActiveShouldInactivePluginAsInactiveWithHeaderLoaded) { + copyPlugin(BLANK_ESP); + setLoadOrder({}); + handle_->LoadCurrentLoadOrderState(); - ASSERT_NO_THROW(handle_->LoadPlugins({blankEsp}, true)); + ASSERT_NO_THROW(handle_->LoadPlugins({BLANK_ESP}, true)); - EXPECT_FALSE(handle_->IsPluginActive(blankEsp)); + EXPECT_FALSE(handle_->IsPluginActive(std::string(BLANK_ESP))); } TEST_P(GameInterfaceTest, isPluginActiveShouldActivePluginAsActiveWhenFullyLoaded) { + copyPlugin(BLANK_ESP); + setLoadOrder({{std::string(BLANK_ESP), true}}); + handle_->LoadCurrentLoadOrderState(); - ASSERT_NO_THROW(handle_->LoadPlugins({blankEsm}, false)); + ASSERT_NO_THROW(handle_->LoadPlugins({BLANK_ESP}, false)); - EXPECT_TRUE(handle_->IsPluginActive(blankEsm)); + EXPECT_TRUE(handle_->IsPluginActive(std::string(BLANK_ESP))); } TEST_P(GameInterfaceTest, isPluginActiveShouldInactivePluginAsInactiveWhenFullyLoaded) { + copyPlugin(BLANK_ESP); + setLoadOrder({}); + handle_->LoadCurrentLoadOrderState(); ASSERT_NO_THROW(handle_->LoadPlugins({blankEsp}, false)); @@ -650,115 +747,136 @@ TEST_P(GameInterfaceTest, } TEST_P(GameInterfaceTest, getLoadOrderShouldReturnTheCurrentLoadOrder) { - // Remove the non-ASCII duplicate plugin. - std::filesystem::remove(dataPath / std::filesystem::u8path(nonAsciiEsm)); - // Set no additional data paths to avoid picking up non-test plugins on PCs - // which have Starfield or Fallout 4 installed. Don't clear the additional - // data paths for OpenMW because they come from test config. - if (GetParam() != GameType::openmw) { + // which have Starfield or Fallout 4 installed. + if (GetParam() == GameType::starfield || GetParam() == GameType::fo4) { handle_->SetAdditionalDataPaths({}); } - handle_->LoadCurrentLoadOrderState(); - - if (GetParam() == GameType::openmw) { - ASSERT_EQ(std::vector({ - blankDifferentEsm, - blankDifferentMasterDependentEsm, - blankDifferentEsp, - blankDifferentPluginDependentEsp, - blankMasterDependentEsm, - blankMasterDependentEsp, - blankEsp, - blankPluginDependentEsp, - masterFile, - blankEsm, - blankDifferentMasterDependentEsp, - }), - handle_->GetLoadOrder()); - } else { - ASSERT_EQ(getLoadOrder(), handle_->GetLoadOrder()); - } -} - -TEST_P(GameInterfaceTest, setLoadOrderShouldSetTheLoadOrder) { - // Remove the non-ASCII duplicate plugin. - std::filesystem::remove(dataPath / std::filesystem::u8path(nonAsciiEsm)); - - // Set no additional data paths to avoid picking up non-test plugins on PCs - // which have Starfield or Fallout 4 installed. Don't clear the additional - // data paths for OpenMW because they come from test config. - if (GetParam() != GameType::openmw) { - handle_->SetAdditionalDataPaths({}); - } - - handle_->LoadCurrentLoadOrderState(); - - const auto gameSupportsEsl = - GetParam() == GameType::fo4 || GetParam() == GameType::fo4vr || - GetParam() == GameType::tes5se || GetParam() == GameType::tes5vr || - GetParam() == GameType::starfield; - - std::vector loadOrder; + std::vector> loadOrder; if (GetParam() == GameType::starfield) { - loadOrder = { - masterFile, - blankEsm, - blankMasterDependentEsm, - blankDifferentEsm, - blankDifferentEsp, - blankEsp, - blankMasterDependentEsp, - }; - } else if (GetParam() == GameType::openmw) { - loadOrder = { - blankDifferentMasterDependentEsm, - blankDifferentPluginDependentEsp, - blankDifferentEsm, - blankDifferentEsp, - blankMasterDependentEsm, - blankMasterDependentEsp, - blankPluginDependentEsp, - blankEsp, - masterFile, - blankDifferentMasterDependentEsp, - blankEsm, - }; + loadOrder = {{std::string(BLANK_FULL_ESM), true}, + {std::string(BLANK_ESP), false}, + {std::string(BLANK_OVERRIDE_ESP), false}}; } else { - loadOrder = { - masterFile, - blankEsm, - blankMasterDependentEsm, - blankDifferentEsm, - blankDifferentMasterDependentEsm, - blankDifferentEsp, - blankDifferentPluginDependentEsp, - blankEsp, - blankMasterDependentEsp, - blankDifferentMasterDependentEsp, - blankPluginDependentEsp, - }; + loadOrder = {{std::string(BLANK_ESM), true}, + {std::string(BLANK_DIFFERENT_ESM), true}, + {std::string(BLANK_ESP), false}, + {std::string(BLANK_MASTER_DEPENDENT_ESP), false}}; + } - if (gameSupportsEsl) { - loadOrder.insert(loadOrder.begin() + 5, blankEsl); + for (const auto& [plugin, isActive] : loadOrder) { + copyPlugin(plugin); + } + setLoadOrder(loadOrder); + + handle_->LoadCurrentLoadOrderState(); + + std::vector expectedLoadOrder; + if (GetParam() == GameType::openmw) { + // OpenMW doesn't allow the load order of inactive plugins to be persisted. + expectedLoadOrder = {std::string(BLANK_ESM), + std::string(BLANK_DIFFERENT_ESM), + std::string(BLANK_MASTER_DEPENDENT_ESP), + std::string(BLANK_ESP)}; + } else { + for (const auto& [plugin, isActive] : loadOrder) { + expectedLoadOrder.push_back(plugin); } } - EXPECT_NO_THROW(handle_->SetLoadOrder(loadOrder)); + ASSERT_EQ(expectedLoadOrder, handle_->GetLoadOrder()); +} - EXPECT_EQ(loadOrder, handle_->GetLoadOrder()); +TEST_P(GameInterfaceTest, getLoadOrderShouldStripGhostExtensionsFromPlugins) { + if (GetParam() == GameType::openmw) { + return; + } + + const auto ghostedName = "Blank.esp.ghost"; + copyPlugin(BLANK_ESP, ghostedName); + + setLoadOrder({{std::string(ghostedName), true}}); + + // Set no additional data paths to avoid picking up non-test plugins on PCs + // which have Starfield or Fallout 4 installed. + if (GetParam() == GameType::starfield || GetParam() == GameType::fo4) { + handle_->SetAdditionalDataPaths({}); + } + + handle_->LoadCurrentLoadOrderState(); + + ASSERT_EQ(std::vector({std::string(BLANK_ESP)}), + handle_->GetLoadOrder()); +} + +TEST_P(GameInterfaceTest, setLoadOrderShouldSetTheLoadOrder) { + // Set no additional data paths to avoid picking up non-test plugins on PCs + // which have Starfield or Fallout 4 installed. + if (GetParam() == GameType::starfield || GetParam() == GameType::fo4) { + handle_->SetAdditionalDataPaths({}); + } + + std::vector> initialLoadOrder; + std::vector newLoadOrder; + if (GetParam() == GameType::starfield) { + initialLoadOrder = {{std::string(BLANK_FULL_ESM), true}, + {std::string(BLANK_ESP), false}, + {std::string(BLANK_OVERRIDE_ESP), false}}; + newLoadOrder = {std::string(BLANK_FULL_ESM), + std::string(BLANK_OVERRIDE_ESP), + std::string(BLANK_ESP)}; + } else { + initialLoadOrder = {{std::string(BLANK_ESM), true}, + {std::string(BLANK_ESP), false}, + {std::string(BLANK_DIFFERENT_ESP), false}}; + newLoadOrder = {std::string(BLANK_ESM), + std::string(BLANK_DIFFERENT_ESP), + std::string(BLANK_ESP)}; + } + + for (const auto& [plugin, isActive] : initialLoadOrder) { + copyPlugin(plugin); + } + + setLoadOrder(initialLoadOrder); + handle_->LoadCurrentLoadOrderState(); + + EXPECT_NO_THROW(handle_->SetLoadOrder(newLoadOrder)); + + EXPECT_EQ(newLoadOrder, handle_->GetLoadOrder()); // It's not possible to persist the load order of inactive plugins for // OpenMW. if (GetParam() != GameType::openmw) { - if (gameSupportsEsl) { - loadOrder.erase(std::begin(loadOrder)); - } - - EXPECT_EQ(loadOrder, getLoadOrder()); + EXPECT_EQ(newLoadOrder, getLoadOrder()); } } + +TEST_P(GameInterfaceTest, setLoadOrderShouldStripGhostExtensionsFromPlugins) { + if (GetParam() == GameType::openmw) { + return; + } + + const auto pluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + + copyPlugin(pluginName); + copyPlugin(BLANK_ESP, "Blank.esp.ghost"); + + // Set no additional data paths to avoid picking up non-test plugins on PCs + // which have Starfield or Fallout 4 installed. + if (GetParam() == GameType::starfield || GetParam() == GameType::fo4) { + handle_->SetAdditionalDataPaths({}); + } + + EXPECT_NO_THROW(handle_->SetLoadOrder( + {std::string(pluginName) + ".ghost", std::string(BLANK_ESP)})); + + ASSERT_EQ(std::vector( + {std::string(pluginName), std::string(BLANK_ESP)}), + handle_->GetLoadOrder()); +} } } diff --git a/cpp/src/tests/api/interface/plugin_interface_test.h b/cpp/src/tests/api/interface/plugin_interface_test.h index 2579b63c..c5300212 100644 --- a/cpp/src/tests/api/interface/plugin_interface_test.h +++ b/cpp/src/tests/api/interface/plugin_interface_test.h @@ -31,73 +31,58 @@ along with LOOT. If not, see namespace loot::test { class PluginInterfaceTest : public ApiGameOperationsTest { protected: - PluginInterfaceTest() : - ApiGameOperationsTest(), - nonAsciiEsp(u8"non\u00C1scii.esp"), - otherNonAsciiEsp(u8"other non\u00C1scii.esp"), - blankArchive("Blank" + GetArchiveFileExtension(GetParam())), - blankSuffixArchive("Blank - Different - suffix" + - GetArchiveFileExtension(GetParam())) {} + static constexpr std::string_view OTHER_NON_ASCII_ESP = + u8"other non\u00C1scii.esp"; - void SetUp() override { - ApiGameOperationsTest::SetUp(); - - handle_->LoadPlugins(GetInstalledPlugins(), false); - - if (!supportsLightPlugins(GetParam())) { - ASSERT_NO_THROW( - std::filesystem::copy(dataPath / blankEsp, dataPath / blankEsl)); - } - ASSERT_TRUE(std::filesystem::exists(dataPath / blankEsl)); - - // Make sure the plugins with non-ASCII filenames exists. - ASSERT_NO_THROW(std::filesystem::copy_file( - dataPath / blankEsp, dataPath / std::filesystem::u8path(nonAsciiEsp))); - ASSERT_NO_THROW(std::filesystem::copy_file( - dataPath / blankEsp, - dataPath / std::filesystem::u8path(otherNonAsciiEsp))); + PluginInterfaceTest() {} + void SetUpTestArchives() { // Copy across archive files. - std::filesystem::path blankMasterDependentArchive; - if (GetParam() == GameType::fo4 || GetParam() == GameType::fo4vr || - GetParam() == GameType::starfield) { - copyPlugin(getSourceArchivesPath(GetParam()), "Blank - Main.ba2"); - copyPlugin(getSourceArchivesPath(GetParam()), "Blank - Textures.ba2"); + const auto sourceArchivesPath = getSourceArchivesPath(GetParam()); + const auto blankArchive = "Blank" + GetArchiveFileExtension(GetParam()); - blankMasterDependentArchive = "Blank - Master Dependent - Main.ba2"; - std::filesystem::copy_file( - getSourceArchivesPath(GetParam()) / "Blank - Main.ba2", - dataPath / blankMasterDependentArchive); - ASSERT_TRUE( - std::filesystem::exists(dataPath / blankMasterDependentArchive)); + if (GetParam() == GameType::fo4 || GetParam() == GameType::fo4vr) { + copyDataFile(sourceArchivesPath, "Blank - Main.ba2"); + copyDataFile(sourceArchivesPath, "Blank - Textures.ba2"); + copyDataFile(sourceArchivesPath, + "Blank - Main.ba2", + "Blank - Master Dependent - Main.ba2"); + } else if (GetParam() == GameType::starfield) { + copyDataFile(sourceArchivesPath, "Blank - Main.ba2"); + copyDataFile(sourceArchivesPath, "Blank - Textures.ba2"); + copyDataFile(sourceArchivesPath, + "Blank - Main.ba2", + "Blank - Master Dependent - Main.ba2"); + + copyDataFile( + sourceArchivesPath, "Blank - Main.ba2", "Blank.full - Main.ba2"); + copyDataFile(sourceArchivesPath, + "Blank - Textures.ba2", + "Blank.full - Textures.ba2"); } else if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw) { touch(dataPath / blankArchive); - - blankMasterDependentArchive = "Blank - Master Dependent.bsa"; - touch(dataPath / blankMasterDependentArchive); + touch(dataPath / "Blank - Master Dependent.bsa"); } else { - copyPlugin(getSourcePluginsPath(), blankArchive); + copyDataFile(sourceArchivesPath, blankArchive); // Also create a copy for Blank - Master Dependent.esp to test overlap. - blankMasterDependentArchive = "Blank - Master Dependent.bsa"; - std::filesystem::copy_file(getSourcePluginsPath() / blankArchive, - dataPath / blankMasterDependentArchive); - ASSERT_TRUE( - std::filesystem::exists(dataPath / blankMasterDependentArchive)); + copyDataFile( + sourceArchivesPath, blankArchive, "Blank - Master Dependent.bsa"); } // Create dummy archive files. + const auto blankSuffixArchive = + "Blank - Different - suffix" + GetArchiveFileExtension(GetParam()); touch(dataPath / blankSuffixArchive); - auto nonAsciiArchivePath = - dataPath / std::filesystem::u8path(u8"non\u00E1scii" + - GetArchiveFileExtension(GetParam())); - touch(dataPath / nonAsciiArchivePath); + auto nonAsciiArchive = std::filesystem::u8path( + u8"non\u00E1scii" + GetArchiveFileExtension(GetParam())); + touch(dataPath / nonAsciiArchive); - auto nonAsciiPrefixArchivePath = - dataPath / std::filesystem::u8path(u8"other non\u00E1scii2 - suffix" + - GetArchiveFileExtension(GetParam())); - touch(dataPath / nonAsciiPrefixArchivePath); + auto nonAsciiPrefixArchive = + std::filesystem::u8path(u8"other non\u00E1scii2 - suffix" + + GetArchiveFileExtension(GetParam())); + touch(dataPath / nonAsciiPrefixArchive); } std::shared_ptr LoadPluginHeader( @@ -114,11 +99,6 @@ protected: return handle_->GetPlugin(pluginName); } - std::string nonAsciiEsp; - std::string otherNonAsciiEsp; - std::string blankArchive; - std::string blankSuffixArchive; - std::shared_ptr game_; private: @@ -182,9 +162,13 @@ INSTANTIATE_TEST_SUITE_P(, TEST_P(PluginInterfaceTest, shouldBeAbleToGetHeaderDataFromPluginLoadedHeaderOnly) { - const auto plugin = LoadPluginHeader(blankEsm); + const auto pluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + copyPlugin(pluginName); - EXPECT_EQ(blankEsm, plugin->GetName()); + const auto plugin = LoadPluginHeader(pluginName); + + EXPECT_EQ(pluginName, plugin->GetName()); EXPECT_TRUE(plugin->GetMasters().empty()); if (GetParam() == GameType::openmw || GetParam() == GameType::oblivionRemastered) { @@ -208,9 +192,13 @@ TEST_P(PluginInterfaceTest, } TEST_P(PluginInterfaceTest, shouldBeAbleToGetAllDataFromFullyLoadedPlugin) { - const auto plugin = LoadPlugin(blankEsm); + const auto pluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + copyPlugin(pluginName); - EXPECT_EQ(blankEsm, plugin->GetName()); + const auto plugin = LoadPlugin(pluginName); + + EXPECT_EQ(pluginName, plugin->GetName()); EXPECT_TRUE(plugin->GetMasters().empty()); if (GetParam() == GameType::openmw || GetParam() == GameType::oblivionRemastered) { @@ -237,7 +225,9 @@ TEST_P(PluginInterfaceTest, shouldBeAbleToGetAllDataFromFullyLoadedPlugin) { TEST_P(PluginInterfaceTest, loadingANonMasterPluginShouldReadTheMasterFlagAsFalse) { - const auto plugin = LoadPluginHeader(blankMasterDependentEsp); + copyPlugin(BLANK_ESP); + + const auto plugin = LoadPluginHeader(BLANK_ESP); EXPECT_FALSE(plugin->IsMaster()); } @@ -245,23 +235,31 @@ TEST_P(PluginInterfaceTest, TEST_P( PluginInterfaceTest, isLightPluginShouldBeTrueForAPluginWithEslFileExtensionForFallout4AndSkyrimSe) { - const auto plugin1 = LoadPluginHeader(blankEsm); - const auto plugin2 = LoadPluginHeader(blankMasterDependentEsp); + const auto masterName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + const auto lightPluginName = + GetParam() == GameType::starfield ? BLANK_SMALL_ESM : BLANK_ESL; + copyPlugin(masterName); + copyPlugin(BLANK_ESP); - EXPECT_FALSE(plugin1->IsLightPlugin()); - EXPECT_FALSE(plugin2->IsLightPlugin()); + EXPECT_FALSE(LoadPluginHeader(masterName)->IsLightPlugin()); + EXPECT_FALSE(LoadPluginHeader(BLANK_ESP)->IsLightPlugin()); if (GetParam() == GameType::fo4 || GetParam() == GameType::fo4vr || GetParam() == GameType::tes5se || GetParam() == GameType::tes5vr || GetParam() == GameType::starfield) { - const auto plugin3 = LoadPluginHeader(blankEsl); - EXPECT_TRUE(plugin3->IsLightPlugin()); + copyPlugin(lightPluginName); + + EXPECT_TRUE(LoadPluginHeader(lightPluginName)->IsLightPlugin()); } } TEST_P(PluginInterfaceTest, isMediumPluginShouldBeTrueForAMediumFlaggedPluginForStarfield) { - if (GetParam() != GameType::starfield) { + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_MEDIUM_ESM); + } else { + copyPlugin(BLANK_ESM); auto bytes = ReadFile(dataPath / blankEsm); bytes[9] = 0x4; WriteFile(dataPath / blankEsm, bytes); @@ -276,30 +274,45 @@ TEST_P(PluginInterfaceTest, TEST_P(PluginInterfaceTest, isUpdatePluginShouldOnlyBeTrueForAStarfieldUpdatePlugin) { - auto bytes = ReadFile(dataPath / blankMasterDependentEsp); + copyPlugin(BLANK_ESP); + + EXPECT_FALSE(LoadPluginHeader(BLANK_ESP)->IsUpdatePlugin()); + + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_OVERRIDE_ESP, BLANK_MASTER_DEPENDENT_ESP); + } else { + copyPlugin(BLANK_MASTER_DEPENDENT_ESP); + } + + auto bytes = ReadFile(dataPath / BLANK_MASTER_DEPENDENT_ESP); bytes[9] = 0x2; - WriteFile(dataPath / blankMasterDependentEsp, bytes); + WriteFile(dataPath / BLANK_MASTER_DEPENDENT_ESP, bytes); - const auto plugin1 = LoadPluginHeader(blankEsp); - const auto plugin2 = LoadPluginHeader(blankMasterDependentEsp); + const auto plugin = LoadPluginHeader(BLANK_MASTER_DEPENDENT_ESP); - EXPECT_FALSE(plugin1->IsUpdatePlugin()); - EXPECT_EQ(GetParam() == GameType::starfield, plugin2->IsUpdatePlugin()); + EXPECT_EQ(GetParam() == GameType::starfield, plugin->IsUpdatePlugin()); } TEST_P(PluginInterfaceTest, isBlueprintPluginShouldOnlyBeTrueForAStarfieldBlueprintPlugin) { - SetBlueprintFlag(dataPath / blankMasterDependentEsp); + copyPlugin(BLANK_ESP); - const auto plugin1 = LoadPluginHeader(blankEsp); - const auto plugin2 = LoadPluginHeader(blankMasterDependentEsp); + EXPECT_FALSE(LoadPluginHeader(BLANK_ESP)->IsBlueprintPlugin()); - EXPECT_FALSE(plugin1->IsBlueprintPlugin()); - EXPECT_EQ(GetParam() == GameType::starfield, plugin2->IsBlueprintPlugin()); + SetBlueprintFlag(dataPath / BLANK_ESP); + + const auto plugin = LoadPluginHeader(BLANK_ESP); + + EXPECT_EQ(GetParam() == GameType::starfield, plugin->IsBlueprintPlugin()); } TEST_P(PluginInterfaceTest, loadingAPluginWithMastersShouldReadThemCorrectly) { - const auto plugin = LoadPluginHeader(blankMasterDependentEsp); + const auto pluginName = GetParam() == GameType::starfield + ? BLANK_OVERRIDE_ESP + : BLANK_MASTER_DEPENDENT_ESP; + copyPlugin(pluginName); + + const auto plugin = LoadPluginHeader(pluginName); if (GetParam() == GameType::starfield) { EXPECT_EQ(std::vector({blankFullEsm}), plugin->GetMasters()); @@ -311,7 +324,13 @@ TEST_P(PluginInterfaceTest, loadingAPluginWithMastersShouldReadThemCorrectly) { TEST_P( PluginInterfaceTest, loadsArchiveForAnArchiveThatExactlyMatchesAnEsmFileBasenameShouldReturnTrueForAllGamesExceptMorrowindAndOblivion) { - bool loadsArchive = LoadPluginHeader(blankEsm)->LoadsArchive(); + SetUpTestArchives(); + + const auto pluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + copyPlugin(pluginName); + + bool loadsArchive = LoadPluginHeader(pluginName)->LoadsArchive(); if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw || GetParam() == GameType::tes4 || @@ -325,7 +344,10 @@ TEST_P( TEST_P( PluginInterfaceTest, loadsArchiveForAnArchiveThatExactlyMatchesANonAsciiEspFileBasenameShouldReturnTrueForAllGamesExceptMorrowindAndStarfield) { - bool loadsArchive = LoadPluginHeader(nonAsciiEsp)->LoadsArchive(); + SetUpTestArchives(); + copyPlugin(BLANK_ESP, NON_ASCII_ESP); + + bool loadsArchive = LoadPluginHeader(NON_ASCII_ESP)->LoadsArchive(); if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw || GetParam() == GameType::starfield) @@ -338,6 +360,9 @@ TEST_P( TEST_P( PluginInterfaceTest, loadsArchiveForAnArchiveThatExactlyMatchesAnEspFileBasenameShouldReturnTrueForAllGamesExceptMorrowind) { + SetUpTestArchives(); + copyPlugin(BLANK_ESP); + bool loadsArchive = LoadPluginHeader(blankEsp)->LoadsArchive(); if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw) @@ -349,7 +374,14 @@ TEST_P( TEST_P( PluginInterfaceTest, loadsArchiveForAnArchiveWithAFilenameWhichStartsWithTheEsmFileBasenameShouldReturnTrueForOnlyTheFalloutGames) { - bool loadsArchive = LoadPluginHeader(blankDifferentEsm)->LoadsArchive(); + SetUpTestArchives(); + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_FULL_ESM, BLANK_DIFFERENT_ESM); + } else { + copyPlugin(BLANK_DIFFERENT_ESM); + } + + bool loadsArchive = LoadPluginHeader(BLANK_DIFFERENT_ESM)->LoadsArchive(); if (GetParam() == GameType::fo3 || GetParam() == GameType::fonv || GetParam() == GameType::fo4 || GetParam() == GameType::fo4vr) { @@ -362,7 +394,14 @@ TEST_P( TEST_P( PluginInterfaceTest, loadsArchiveForAnArchiveWithAFilenameWhichStartsWithTheEspFileBasenameShouldReturnTrueForOnlyOblivionAndTheFalloutGames) { - bool loadsArchive = LoadPluginHeader(blankDifferentEsp)->LoadsArchive(); + SetUpTestArchives(); + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_ESP, BLANK_DIFFERENT_ESP); + } else { + copyPlugin(BLANK_DIFFERENT_ESP); + } + + bool loadsArchive = LoadPluginHeader(BLANK_DIFFERENT_ESP)->LoadsArchive(); if (GetParam() == GameType::tes4 || GetParam() == GameType::fo3 || GetParam() == GameType::fonv || GetParam() == GameType::fo4 || @@ -378,7 +417,10 @@ TEST_P( TEST_P( PluginInterfaceTest, loadsArchiveForAnArchiveWithAFilenameWhichStartsWithTheNonAsciiEspFileBasenameShouldReturnTrueForOnlyOblivionAndTheFalloutGames) { - bool loadsArchive = LoadPluginHeader(otherNonAsciiEsp)->LoadsArchive(); + SetUpTestArchives(); + copyPlugin(BLANK_ESP, OTHER_NON_ASCII_ESP); + + bool loadsArchive = LoadPluginHeader(OTHER_NON_ASCII_ESP)->LoadsArchive(); if (GetParam() == GameType::tes4 || GetParam() == GameType::fo3 || GetParam() == GameType::fonv || GetParam() == GameType::fo4 || @@ -393,10 +435,15 @@ TEST_P( TEST_P(PluginInterfaceTest, loadsArchiveShouldReturnFalseForAPluginThatDoesNotLoadAnArchive) { - const auto pluginName = GetParam() == GameType::starfield - ? blankDifferentEsp - : blankDifferentMasterDependentEsp; - bool loadsArchive = LoadPluginHeader(pluginName)->LoadsArchive(); + SetUpTestArchives(); + if (GetParam() == GameType::starfield) { + copyPlugin(BLANK_ESP, BLANK_DIFFERENT_MASTER_DEPENDENT_ESP); + } else { + copyPlugin(BLANK_DIFFERENT_MASTER_DEPENDENT_ESP); + } + + bool loadsArchive = + LoadPluginHeader(BLANK_DIFFERENT_MASTER_DEPENDENT_ESP)->LoadsArchive(); EXPECT_FALSE(loadsArchive); } @@ -404,7 +451,11 @@ TEST_P(PluginInterfaceTest, TEST_P( PluginInterfaceTest, isValidAsLightPluginShouldReturnTrueOnlyForASkyrimSEOrFallout4PluginWithNewFormIdsBetween0x800And0xFFFInclusive) { - bool valid = LoadPlugin(blankEsm)->IsValidAsLightPlugin(); + const auto pluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + copyPlugin(pluginName); + + bool valid = LoadPlugin(pluginName)->IsValidAsLightPlugin(); if (GetParam() == GameType::fo4 || GetParam() == GameType::fo4vr || GetParam() == GameType::tes5se || GetParam() == GameType::tes5vr || GetParam() == GameType::starfield) { @@ -417,11 +468,14 @@ TEST_P( TEST_P( PluginInterfaceTest, isValidAsMediumPluginShouldReturnTrueOnlyForAStarfieldPluginWithNewFormIdsBetween0And0xFFFFInclusive) { - bool valid = LoadPlugin(blankEsm)->IsValidAsMediumPlugin(); if (GetParam() == GameType::starfield) { - EXPECT_TRUE(valid); + copyPlugin(BLANK_FULL_ESM); + + EXPECT_TRUE(LoadPlugin(BLANK_FULL_ESM)->IsValidAsMediumPlugin()); } else { - EXPECT_FALSE(valid); + copyPlugin(BLANK_ESM); + + EXPECT_FALSE(LoadPlugin(BLANK_ESM)->IsValidAsMediumPlugin()); } } @@ -431,10 +485,18 @@ TEST_P( const auto sourcePluginName = GetParam() == GameType::starfield ? blankFullEsm : blankEsp; const auto updatePluginName = GetParam() == GameType::starfield - ? blankMasterDependentEsp - : blankDifferentPluginDependentEsp; + ? BLANK_OVERRIDE_ESP + : BLANK_DIFFERENT_PLUGIN_DEPENDENT_ESP; + copyPlugin(sourcePluginName); + copyPlugin(updatePluginName); std::vector> plugins; + + if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw) { + copyPlugin(BLANK_DIFFERENT_ESP); + plugins.push_back(LoadPlugin(BLANK_DIFFERENT_ESP)); + } + plugins.push_back(LoadPlugin(sourcePluginName)); plugins.push_back(LoadPlugin(updatePluginName)); @@ -445,7 +507,11 @@ TEST_P( TEST_P(PluginInterfaceTest, doRecordsOverlapShouldReturnFalseIfTheArgumentIsNotAPluginObject) { - const auto plugin1 = LoadPlugin(blankEsm); + const auto pluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + copyPlugin(pluginName); + + const auto plugin1 = LoadPlugin(pluginName); TestPlugin plugin2; EXPECT_FALSE(plugin1->DoRecordsOverlap(plugin2)); @@ -453,8 +519,16 @@ TEST_P(PluginInterfaceTest, TEST_P(PluginInterfaceTest, doRecordsOverlapShouldReturnFalseForTwoPluginsWithOnlyHeadersLoaded) { - const auto plugin1 = LoadPluginHeader(blankEsm); - const auto plugin2 = LoadPluginHeader(blankMasterDependentEsm); + const auto basePluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + const auto dependentPluginName = GetParam() == GameType::starfield + ? BLANK_OVERRIDE_FULL_ESM + : BLANK_MASTER_DEPENDENT_ESM; + copyPlugin(basePluginName); + copyPlugin(dependentPluginName); + + const auto plugin1 = LoadPluginHeader(basePluginName); + const auto plugin2 = LoadPluginHeader(dependentPluginName); EXPECT_FALSE(plugin1->DoRecordsOverlap(*plugin2)); EXPECT_FALSE(plugin2->DoRecordsOverlap(*plugin1)); @@ -462,8 +536,13 @@ TEST_P(PluginInterfaceTest, TEST_P(PluginInterfaceTest, doRecordsOverlapShouldReturnFalseIfThePluginsHaveUnrelatedRecords) { - const auto plugin1 = LoadPlugin(blankEsm); - const auto plugin2 = LoadPlugin(blankEsp); + const auto pluginName1 = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + copyPlugin(pluginName1); + copyPlugin(BLANK_ESP); + + const auto plugin1 = LoadPlugin(pluginName1); + const auto plugin2 = LoadPlugin(BLANK_ESP); EXPECT_FALSE(plugin1->DoRecordsOverlap(*plugin2)); EXPECT_FALSE(plugin2->DoRecordsOverlap(*plugin1)); @@ -471,11 +550,16 @@ TEST_P(PluginInterfaceTest, TEST_P(PluginInterfaceTest, doRecordsOverlapShouldReturnTrueIfOnePluginOverridesTheOthersRecords) { - const auto plugin1Name = - GetParam() == GameType::starfield ? blankFullEsm : blankEsm; + const auto basePluginName = + GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM; + const auto dependentPluginName = GetParam() == GameType::starfield + ? BLANK_OVERRIDE_FULL_ESM + : BLANK_MASTER_DEPENDENT_ESM; + copyPlugin(basePluginName); + copyPlugin(dependentPluginName); - const auto plugin1 = LoadPlugin(plugin1Name); - const auto plugin2 = LoadPlugin(blankMasterDependentEsm); + const auto plugin1 = LoadPlugin(basePluginName); + const auto plugin2 = LoadPlugin(dependentPluginName); EXPECT_TRUE(plugin1->DoRecordsOverlap(*plugin2)); EXPECT_TRUE(plugin2->DoRecordsOverlap(*plugin1)); diff --git a/cpp/src/tests/common_game_test_fixture.h b/cpp/src/tests/common_game_test_fixture.h index 25d347cc..af8b2347 100644 --- a/cpp/src/tests/common_game_test_fixture.h +++ b/cpp/src/tests/common_game_test_fixture.h @@ -79,14 +79,17 @@ inline constexpr std::string_view BLANK_PLUGIN_DEPENDENT_ESP = "Blank - Plugin Dependent.esp"; inline constexpr std::string_view BLANK_DIFFERENT_PLUGIN_DEPENDENT_ESP = "Blank - Different Plugin Dependent.esp"; +inline constexpr std::string_view BLANK_SMALL_ESM = "Blank.small.esm"; +inline constexpr std::string_view BLANK_OVERRIDE_FULL_ESM = + "Blank - Override.full.esm"; +inline constexpr std::string_view BLANK_OVERRIDE_ESP = "Blank - Override.esp"; +static constexpr std::string_view NON_ASCII_ESP = u8"non\u00C1scii.esp"; class CommonGameTestFixture : public ::testing::Test { protected: CommonGameTestFixture(GameType gameType) : gameType_(gameType), rootTestPath(getRootTestPath()), - french(FRENCH), - german(GERMAN), missingPath(rootTestPath / "missing"), gamePath(rootTestPath / "games" / "game"), dataPath(gamePath / getPluginsFolder()), @@ -95,7 +98,6 @@ protected: masterFile(getMasterFile()), missingEsp(MISSING_ESP), nonPluginFile(NON_PLUGIN_FILE), - invalidPlugin(INVALID_PLUGIN), blankEsm(BLANK_ESM), blankFullEsm(BLANK_FULL_ESM), blankMediumEsm(BLANK_MEDIUM_ESM), @@ -110,118 +112,39 @@ protected: blankPluginDependentEsp(BLANK_PLUGIN_DEPENDENT_ESP), blankDifferentPluginDependentEsp(BLANK_DIFFERENT_PLUGIN_DEPENDENT_ESP), blankEsmCrc(getBlankEsmCrc()) { - assertInitialState(); - } - - void assertInitialState() { using std::filesystem::create_directories; - using std::filesystem::exists; create_directories(dataPath); - ASSERT_TRUE(exists(dataPath)); - create_directories(localPath); - ASSERT_TRUE(exists(localPath)); - create_directories(metadataFilesPath); - ASSERT_TRUE(exists(metadataFilesPath)); - - auto sourcePluginsPath = getSourcePluginsPath(); - - if (gameType_ == 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(gameType_)) { - if (gameType_ == 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. - ASSERT_NO_THROW( - std::filesystem::copy_file(dataPath / blankEsm, dataPath / masterFile)); - ASSERT_TRUE(exists(dataPath / masterFile)); - - // Set initial load order and active plugins. - setLoadOrder(getInitialLoadOrder()); - - // Ghost a plugin, except for OpenMW. - if (gameType_ != GameType::openmw) { - ASSERT_NO_THROW(std::filesystem::rename( - dataPath / blankMasterDependentEsm, - dataPath / (blankMasterDependentEsm + ".ghost"))); - ASSERT_FALSE(exists(dataPath / blankMasterDependentEsm)); - ASSERT_TRUE(exists(dataPath / (blankMasterDependentEsm + ".ghost"))); - } else { - touch(gamePath / "openmw.cfg"); - } - - // Write out an non-empty, non-plugin file. - std::ofstream out(dataPath / nonPluginFile); - out << "This isn't a valid plugin file."; - out.close(); - ASSERT_TRUE(exists(dataPath / nonPluginFile)); - - ASSERT_FALSE(exists(missingPath)); - ASSERT_FALSE(exists(dataPath / missingEsp)); } - void copyPlugin(const std::filesystem::path& sourceParentPath, - std::string_view filename) { - std::filesystem::copy_file(sourceParentPath / filename, - dataPath / filename); - ASSERT_TRUE(std::filesystem::exists(dataPath / filename)); + void copyPlugin(std::string_view filename) { + copyDataFile(getSourcePluginsPath(), filename); + } + + void copyPlugin(std::string_view sourceFilename, + std::string_view destinationFilename) { + copyDataFile(getSourcePluginsPath(), sourceFilename, destinationFilename); + } + + void copyDataFile(const std::filesystem::path& sourceParentPath, + std::string_view filename) { + copyDataFile(sourceParentPath, filename, filename); + } + + void copyDataFile(const std::filesystem::path& sourceParentPath, + std::string_view sourceFilename, + std::string_view destinationFilename) { + std::filesystem::copy_file( + sourceParentPath / std::filesystem::u8path(sourceFilename), + dataPath / std::filesystem::u8path(destinationFilename)); + + ASSERT_TRUE(std::filesystem::exists( + dataPath / std::filesystem::u8path(destinationFilename))); } void TearDown() override { - // Grant write permissions to everything in rootTestPath - // in case the test made anything read only. - for (const auto& path : - std::filesystem::recursive_directory_iterator(rootTestPath)) { - std::filesystem::permissions(path, std::filesystem::perms::all); - } std::filesystem::remove_all(rootTestPath); } @@ -287,46 +210,6 @@ protected: return actual; } - std::vector> getInitialLoadOrder() const { - std::vector> loadOrder; - - if (gameType_ == 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(gameType_)) { - loadOrder.insert(loadOrder.begin() + 5, - std::make_pair(blankEsl, false)); - } - } - - return loadOrder; - } - std::filesystem::path getSourcePluginsPath() const { return loot::test::getSourcePluginsPath(gameType_); } @@ -355,52 +238,6 @@ protected: out.write(content.data(), content.size()); } - std::vector GetInstalledPlugins() { - if (gameType_ == GameType::starfield) { - return { - masterFile, - blankEsm, - blankDifferentEsm, - blankFullEsm, - blankMasterDependentEsm, - blankMediumEsm, - blankEsl, - blankEsp, - blankDifferentEsp, - blankMasterDependentEsp, - }; - } else if (supportsLightPlugins(gameType_)) { - return { - masterFile, - blankEsm, - blankDifferentEsm, - blankMasterDependentEsm, - blankDifferentMasterDependentEsm, - blankEsl, - blankEsp, - blankDifferentEsp, - blankMasterDependentEsp, - blankDifferentMasterDependentEsp, - blankPluginDependentEsp, - blankDifferentPluginDependentEsp, - }; - } else { - return { - masterFile, - blankEsm, - blankDifferentEsm, - blankMasterDependentEsm, - blankDifferentMasterDependentEsm, - blankEsp, - blankDifferentEsp, - blankMasterDependentEsp, - blankDifferentMasterDependentEsp, - blankPluginDependentEsp, - blankDifferentPluginDependentEsp, - }; - } - } - void SetBlueprintFlag(const std::filesystem::path& path) { auto bytes = ReadFile(path); bytes[9] = 0x8; @@ -434,9 +271,6 @@ private: const std::filesystem::path rootTestPath; protected: - const std::string french; - const std::string german; - const std::filesystem::path missingPath; const std::filesystem::path gamePath; const std::filesystem::path dataPath; @@ -446,7 +280,6 @@ protected: const std::string masterFile; const std::string missingEsp; const std::string nonPluginFile; - const std::string invalidPlugin; const std::string blankEsm; const std::string blankFullEsm; const std::string blankMediumEsm; @@ -512,6 +345,7 @@ private: } } +protected: void setLoadOrder( const std::vector>& loadOrder) const { if (gameType_ == GameType::tes3) { @@ -572,6 +406,7 @@ private: } } +private: static bool isLoadOrderTimestampBased(GameType gameType) { return gameType == GameType::tes3 || gameType == GameType::tes4 || gameType == GameType::fo3 || gameType == GameType::fonv;