diff --git a/src/gui/query/metadata_query.h b/src/gui/query/metadata_query.h index e362f8f3..a167d47d 100644 --- a/src/gui/query/metadata_query.h +++ b/src/gui/query/metadata_query.h @@ -56,7 +56,7 @@ protected: auto messages = metadata.Messages(); auto statusMessages = file->GetStatusMessages(); - auto validityMessages = CheckInstallValidity(file, metadata); + auto validityMessages = state_.getCurrentGame().CheckInstallValidity(file, metadata); messages.insert(end(messages), begin(statusMessages), end(statusMessages)); messages.insert(end(messages), begin(validityMessages), end(validityMessages)); metadata.Messages(messages); @@ -155,49 +155,6 @@ private: } } - std::vector CheckInstallValidity(std::shared_ptr plugin, const PluginMetadata& metadata) { - BOOST_LOG_TRIVIAL(trace) << "Checking that the current install is valid according to " << plugin->GetName() << "'s data."; - std::vector messages; - if (state_.getCurrentGame().IsPluginActive(plugin->GetName())) { - auto pluginExists = [&](const std::string& file) { - return boost::filesystem::exists(state_.getCurrentGame().DataPath() / file) - || ((boost::iends_with(file, ".esp") || boost::iends_with(file, ".esm")) && boost::filesystem::exists(state_.getCurrentGame().DataPath() / (file + ".ghost"))); - }; - auto tags = metadata.Tags(); - if (tags.find(Tag("Filter")) == std::end(tags)) { - for (const auto &master : plugin->GetMasters()) { - if (!pluginExists(master)) { - BOOST_LOG_TRIVIAL(error) << "\"" << plugin->GetName() << "\" requires \"" << master << "\", but it is missing."; - messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("This plugin requires \"%1%\" to be installed, but it is missing.")) % master).str())); - } else if (!state_.getCurrentGame().IsPluginActive(master)) { - BOOST_LOG_TRIVIAL(error) << "\"" << plugin->GetName() << "\" requires \"" << master << "\", but it is inactive."; - messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("This plugin requires \"%1%\" to be active, but it is inactive.")) % master).str())); - } - } - } - - for (const auto &req : metadata.Reqs()) { - if (!pluginExists(req.Name())) { - BOOST_LOG_TRIVIAL(error) << "\"" << plugin->GetName() << "\" requires \"" << req.Name() << "\", but it is missing."; - messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("This plugin requires \"%1%\" to be installed, but it is missing.")) % req.Name()).str())); - } - } - for (const auto &inc : metadata.Incs()) { - if (pluginExists(inc.Name()) && state_.getCurrentGame().IsPluginActive(inc.Name())) { - BOOST_LOG_TRIVIAL(error) << "\"" << plugin->GetName() << "\" is incompatible with \"" << inc.Name() << "\", but both are present."; - messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("This plugin is incompatible with \"%1%\", but both are present.")) % inc.Name()).str())); - } - } - } - - // Also generate dirty messages. - for (const auto &element : metadata.DirtyInfo()) { - messages.push_back(element.AsMessage()); - } - - return messages; - } - LootState& state_; }; } diff --git a/src/gui/state/game.cpp b/src/gui/state/game.cpp index ea00c30b..b8924227 100644 --- a/src/gui/state/game.cpp +++ b/src/gui/state/game.cpp @@ -128,6 +128,49 @@ std::set> Game::GetPlugins() const { return gameHandle_->GetLoadedPlugins(); } +std::vector Game::CheckInstallValidity(std::shared_ptr plugin, const PluginMetadata & metadata) { + BOOST_LOG_TRIVIAL(trace) << "Checking that the current install is valid according to " << plugin->GetName() << "'s data."; + std::vector messages; + if (IsPluginActive(plugin->GetName())) { + auto pluginExists = [&](const std::string& file) { + return boost::filesystem::exists(DataPath() / file) + || ((boost::iends_with(file, ".esp") || boost::iends_with(file, ".esm")) && boost::filesystem::exists(DataPath() / (file + ".ghost"))); + }; + auto tags = metadata.Tags(); + if (tags.find(Tag("Filter")) == std::end(tags)) { + for (const auto &master : plugin->GetMasters()) { + if (!pluginExists(master)) { + BOOST_LOG_TRIVIAL(error) << "\"" << plugin->GetName() << "\" requires \"" << master << "\", but it is missing."; + messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("This plugin requires \"%1%\" to be installed, but it is missing.")) % master).str())); + } else if (!IsPluginActive(master)) { + BOOST_LOG_TRIVIAL(error) << "\"" << plugin->GetName() << "\" requires \"" << master << "\", but it is inactive."; + messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("This plugin requires \"%1%\" to be active, but it is inactive.")) % master).str())); + } + } + } + + for (const auto &req : metadata.Reqs()) { + if (!pluginExists(req.Name())) { + BOOST_LOG_TRIVIAL(error) << "\"" << plugin->GetName() << "\" requires \"" << req.Name() << "\", but it is missing."; + messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("This plugin requires \"%1%\" to be installed, but it is missing.")) % req.Name()).str())); + } + } + for (const auto &inc : metadata.Incs()) { + if (pluginExists(inc.Name()) && IsPluginActive(inc.Name())) { + BOOST_LOG_TRIVIAL(error) << "\"" << plugin->GetName() << "\" is incompatible with \"" << inc.Name() << "\", but both are present."; + messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("This plugin is incompatible with \"%1%\", but both are present.")) % inc.Name()).str())); + } + } + } + + // Also generate dirty messages. + for (const auto &element : metadata.DirtyInfo()) { + messages.push_back(element.AsMessage()); + } + + return messages; +} + void Game::RedatePlugins() { if (Type() != GameType::tes5 && Type() != GameType::tes5se) { BOOST_LOG_TRIVIAL(warning) << "Cannot redate plugins for game " << Name(); diff --git a/src/gui/state/game.h b/src/gui/state/game.h index a58e5638..1138d51c 100644 --- a/src/gui/state/game.h +++ b/src/gui/state/game.h @@ -51,6 +51,7 @@ public: std::shared_ptr GetPlugin(const std::string& name) const; std::set> GetPlugins() const; + std::vector CheckInstallValidity(std::shared_ptr plugin, const PluginMetadata& metadata); void RedatePlugins(); //Change timestamps to match load order (Skyrim only). diff --git a/src/tests/gui/state/game_test.h b/src/tests/gui/state/game_test.h index 1cc3ecfd..b59f0e45 100644 --- a/src/tests/gui/state/game_test.h +++ b/src/tests/gui/state/game_test.h @@ -171,6 +171,82 @@ TEST_P(GameTest, initShouldNotThrowIfGameAndLocalPathsAreNotEmpty) { EXPECT_NO_THROW(game.Init()); } +TEST_P(GameTest, checkInstallValidityShouldCheckThatRequirementsArePresent) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + game.LoadAllInstalledPlugins(true); + + PluginMetadata metadata(blankEsm); + metadata.Reqs({ + File(missingEsp), + File(blankEsp), + }); + + auto messages = game.CheckInstallValidity(game.GetPlugin(blankEsm), metadata); + EXPECT_EQ(std::vector({ + Message(MessageType::error, "This plugin requires \"" + missingEsp + "\" to be installed, but it is missing."), + }), messages); +} + +TEST_P(GameTest, checkInstallValidityShouldCheckThatIncompatibilitiesAreAbsent) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + game.LoadAllInstalledPlugins(true); + + PluginMetadata metadata(blankEsm); + metadata.Incs({ + File(missingEsp), + File(masterFile), + }); + + auto messages = game.CheckInstallValidity(game.GetPlugin(blankEsm), metadata); + EXPECT_EQ(std::vector({ + Message(MessageType::error, "This plugin is incompatible with \"" + masterFile + "\", but both are present."), + }), messages); +} + +TEST_P(GameTest, checkInstallValidityShouldGenerateMessagesFromDirtyInfo) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + game.LoadAllInstalledPlugins(true); + + PluginMetadata metadata(blankEsm); + const std::vector info = std::vector({ + MessageContent("info", LanguageCode::english), + }); + + metadata.DirtyInfo({ + PluginCleaningData(blankEsmCrc, "utility1", info, 0, 1, 2), + PluginCleaningData(0xDEADBEEF, "utility2", info, 0, 5, 10), + }); + + auto messages = game.CheckInstallValidity(game.GetPlugin(blankEsm), metadata); + EXPECT_EQ(std::vector({ + PluginCleaningData(blankEsmCrc, "utility1", info, 0, 1, 2).AsMessage(), + PluginCleaningData(0xDEADBEEF, "utility2", info, 0, 5, 10).AsMessage(), + }), messages); +} + +TEST_P(GameTest, checkInstallValidityShouldCheckIfAPluginsMastersAreAllPresentAndActiveIfNoFilterTagIsPresent) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + game.LoadAllInstalledPlugins(true); + + PluginMetadata metadata(blankDifferentMasterDependentEsp); + + auto messages = game.CheckInstallValidity(game.GetPlugin(blankDifferentMasterDependentEsp), metadata); + EXPECT_EQ(std::vector({ + Message(MessageType::error, "This plugin requires \"" + blankDifferentEsm + "\" to be active, but it is inactive."), + }), messages); +} + +TEST_P(GameTest, checkInstallValidityShouldNotCheckIfAPluginsMastersAreAllActiveIfAFilterTagIsPresent) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + game.LoadAllInstalledPlugins(true); + + PluginMetadata metadata(blankDifferentMasterDependentEsp); + metadata.Tags({Tag("Filter")}); + + auto messages = game.CheckInstallValidity(game.GetPlugin(blankDifferentMasterDependentEsp), metadata); + EXPECT_TRUE(messages.empty()); +} + TEST_P(GameTest, redatePluginsShouldRedatePluginsForSkyrimAndSkyrimSEAndDoNothingForOtherGames) { Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); game.Init();