Fix conversions between strings and paths

The standard library path constructor assumes the string is in
the platform's native encoding, but for LOOT all strings are in
UTF-8.
This commit is contained in:
Oliver Hamlet
2018-10-20 12:48:20 +01:00
parent 306ea4b996
commit c4a83049f5
12 changed files with 222 additions and 119 deletions
+2 -3
View File
@@ -128,7 +128,7 @@ set(PSEUDOSEM_INCLUDE_DIRS "${SOURCE_DIR}/include")
ExternalProject_Add(testing-metadata
PREFIX "external"
GIT_REPOSITORY "https://github.com/loot/testing-metadata"
GIT_TAG "1.3.0"
GIT_TAG "1.4.0"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
@@ -253,8 +253,7 @@ set (LOOT_API_HEADERS "${CMAKE_SOURCE_DIR}/include/loot/api.h"
"${CMAKE_SOURCE_DIR}/src/api/helpers/git_helper.h"
"${CMAKE_SOURCE_DIR}/src/api/helpers/crc.h"
"${CMAKE_SOURCE_DIR}/src/api/helpers/logging.h"
"${CMAKE_SOURCE_DIR}/src/api/helpers/version.h"
"${CMAKE_SOURCE_DIR}/src/api/helpers/windows_encoding_converters.h")
"${CMAKE_SOURCE_DIR}/src/api/helpers/version.h")
set (LOOT_TESTS_SRC "${CMAKE_SOURCE_DIR}/src/tests/api/internals/main.cpp")
+1 -1
View File
@@ -251,7 +251,7 @@ void Game::CacheArchives() {
// file extension with the archive extension resolves to the same file.
// Could use boost::iends_with, but it's less obvious here that the
// test string is ASCII-only.
if (boost::iequals(it->path().extension().string(), archiveFileExtension)) {
if (boost::iequals(it->path().extension().u8string(), archiveFileExtension)) {
cache_->CacheArchivePath(it->path());
}
}
+1 -1
View File
@@ -195,7 +195,7 @@ void GitHelper::Clone(const std::filesystem::path& path,
if (logger_) {
logger_->trace("Target repo path not empty, cloning into temporary directory.");
}
auto directory = "LOOT-" + path.filename().string() + "-" +
auto directory = "LOOT-" + path.filename().u8string() + "-" +
boost::lexical_cast<std::string>((boost::uuids::random_generator())());
repoPath = fs::temp_directory_path() / directory;
+4 -6
View File
@@ -28,8 +28,6 @@
#include <pseudosem.h>
#include <boost/algorithm/string.hpp>
#include "api/helpers/windows_encoding_converters.h"
#ifdef _WIN32
#ifndef UNICODE
#define UNICODE
@@ -97,14 +95,14 @@ Version::Version(const std::string& ver) {
Version::Version(const std::filesystem::path& file) {
#ifdef _WIN32
DWORD dummy = 0;
DWORD size = GetFileVersionInfoSize(ToWinWide(file.string()).c_str(), &dummy);
DWORD size = GetFileVersionInfoSize(file.wstring().c_str(), &dummy);
if (size > 0) {
LPBYTE point = new BYTE[size];
UINT uLen;
VS_FIXEDFILEINFO* info;
GetFileVersionInfo(ToWinWide(file.string()).c_str(), 0, size, point);
GetFileVersionInfo(file.wstring().c_str(), 0, size, point);
VerQueryValue(point, L"\\", (LPVOID*)&info, &uLen);
@@ -122,11 +120,11 @@ Version::Version(const std::filesystem::path& file) {
#else
// ensure filename has no quote characters in it to avoid command injection
// attacks
if (std::string::npos != file.string().find('"')) {
if (std::string::npos != file.u8string().find('"')) {
// command mostly borrowed from the gnome-exe-thumbnailer.sh script
// wrestool is part of the icoutils package
std::string cmd =
"wrestool --extract --raw --type=version \"" + file.string() +
"wrestool --extract --raw --type=version \"" + file.u8string() +
"\" | tr '\\0, ' '\\t.\\0' | sed 's/\\t\\t/_/g' | tr -c -d '[:print:]' "
"| sed -r 's/.*Version[^0-9]*([0-9]+(\\.[0-9]+)+).*/\\1/'";
@@ -1,80 +0,0 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2012-2016 WrinklyNinja
This file is part of LOOT.
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<https://www.gnu.org/licenses/>.
*/
#ifndef LOOT_API_HELPERS_WINDOWS_ENCODING_CONVERTERS
#define LOOT_API_HELPERS_WINDOWS_ENCODING_CONVERTERS
#ifdef _WIN32
#include <string>
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include "shlobj.h"
#include "shlwapi.h"
#include "windows.h"
namespace loot {
/**
* Convert a UTF-8 std::string to a UTF-16 std::wstring.
*
* This isn't strictly part of the LOOT API, but is used within the API and the
* LOOT application, so is shared through the API.
* @param str
* A string encoded in UTF-8.
* @return A wstring encoded in UTF-16.
*/
inline std::wstring ToWinWide(const std::string& str) {
size_t len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), 0, 0);
std::wstring wstr(len, 0);
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &wstr[0], len);
return wstr;
}
/**
* Convert a UTF-16 std::wstring to a UTF-8 std::string.
*
* This isn't strictly part of the LOOT API, but is used within the API and the
* LOOT application, so is shared through the API.
* @param wstr
* A wstring encoded in UTF-16.
* @return A string encoded in UTF-8.
*/
inline std::string FromWinWide(const std::wstring& wstr) {
size_t len = WideCharToMultiByte(
CP_UTF8, 0, wstr.c_str(), wstr.length(), NULL, 0, NULL, NULL);
std::string str(len, 0);
WideCharToMultiByte(
CP_UTF8, 0, wstr.c_str(), wstr.length(), &str[0], len, NULL, NULL);
return str;
}
}
#endif
#endif
+2 -2
View File
@@ -66,7 +66,7 @@ MasterlistInfo Masterlist::GetInfo(const std::filesystem::path& path,
logger->trace("Diffing masterlist HEAD and working copy.");
}
info.is_modified =
GitHelper::IsFileDifferent(path.parent_path(), path.filename().string());
GitHelper::IsFileDifferent(path.parent_path(), path.filename().u8string());
return info;
}
@@ -102,7 +102,7 @@ bool Masterlist::Update(const std::filesystem::path& path,
GitHelper git;
auto logger = getLogger();
fs::path repoPath = path.parent_path();
string filename = path.filename().string();
string filename = path.filename().u8string();
if (path.empty() || repoUrl.empty() || repoBranch.empty())
throw std::invalid_argument("Repository path, URL and branch must not be empty.");
+12 -10
View File
@@ -29,6 +29,8 @@
#include "api/metadata/condition_grammar.h"
#include "loot/exception/condition_syntax_error.h"
using std::filesystem::u8path;
namespace loot {
ConditionEvaluator::ConditionEvaluator() :
gameType_(GameType::tes4),
@@ -163,10 +165,10 @@ bool ConditionEvaluator::fileExists(const std::string& filePath) const {
// Not a loaded plugin, check the filesystem.
if (hasPluginFileExtension(filePath, gameType_))
return std::filesystem::exists(dataPath_ / filePath) ||
std::filesystem::exists(dataPath_ / (filePath + ".ghost"));
return std::filesystem::exists(dataPath_ / u8path(filePath)) ||
std::filesystem::exists(dataPath_ / u8path(filePath + ".ghost"));
else
return std::filesystem::exists(dataPath_ / filePath);
return std::filesystem::exists(dataPath_ / u8path(filePath));
}
bool ConditionEvaluator::regexMatchExists(
@@ -295,7 +297,7 @@ std::filesystem::path ConditionEvaluator::getRegexParentPath(
if (pos == std::string::npos)
return std::filesystem::path();
return std::filesystem::path(regexString.substr(0, pos));
return u8path(regexString.substr(0, pos));
}
std::string ConditionEvaluator::getRegexFilename(
@@ -359,7 +361,7 @@ bool ConditionEvaluator::isRegexMatchInDataDirectory(
std::filesystem::directory_iterator(dataPath_ / pathRegex.first),
std::filesystem::directory_iterator(),
[&](const std::filesystem::directory_entry& entry) {
const std::string filename = entry.path().filename().string();
const std::string filename = entry.path().filename().u8string();
return std::regex_match(filename, pathRegex.second) &&
condition(filename);
});
@@ -425,7 +427,7 @@ Version ConditionEvaluator::getVersion(const std::string& filePath) const {
.GetVersion()
.value_or(""));
return Version(dataPath_ / filePath);
return Version(dataPath_ / u8path(filePath));
}
}
bool ConditionEvaluator::shouldParseOnly() const {
@@ -453,12 +455,12 @@ uint32_t ConditionEvaluator::getCrc(const std::string & file) const {
// Otherwise calculate it from the file.
if (crc == 0) {
if (std::filesystem::exists(dataPath_ / file)) {
crc = GetCrc32(dataPath_ / file);
if (std::filesystem::exists(dataPath_ / u8path(file))) {
crc = GetCrc32(dataPath_ / u8path(file));
}
else if (hasPluginFileExtension(file, gameType_) &&
std::filesystem::exists(dataPath_ / (file + ".ghost"))) {
crc = GetCrc32(dataPath_ / (file + ".ghost"));
std::filesystem::exists(dataPath_ / u8path(file + ".ghost"))) {
crc = GetCrc32(dataPath_ / u8path(file + ".ghost"));
}
}
+6 -7
View File
@@ -55,7 +55,7 @@ Plugin::Plugin(const GameType gameType,
auto logger = getLogger();
try {
std::filesystem::path filepath = dataPath / name_;
std::filesystem::path filepath = dataPath / std::filesystem::u8path(name_);
// In case the plugin is ghosted.
if (!std::filesystem::exists(filepath)) {
@@ -239,7 +239,7 @@ bool Plugin::IsValid(const std::string& filename,
return false;
bool isValid;
auto path = dataPath / filename;
auto path = dataPath / std::filesystem::u8path(filename);
int ret = esp_plugin_is_valid(
GetEspluginGameId(gameType), path.u8string().c_str(), true, &isValid);
@@ -255,7 +255,7 @@ bool Plugin::IsValid(const std::string& filename,
uintmax_t Plugin::GetFileSize(const std::string& filename,
const std::filesystem::path& dataPath) {
std::filesystem::path realPath = dataPath / filename;
std::filesystem::path realPath = dataPath / std::filesystem::u8path(filename);
if (!std::filesystem::exists(realPath))
realPath += ".ghost";
@@ -323,16 +323,15 @@ bool Plugin::LoadsArchive(const std::string& pluginName,
if (gameType == GameType::tes5) {
// Skyrim plugins only load BSAs that exactly match their basename.
return std::filesystem::exists(
dataPath /
(pluginName.substr(0, pluginName.length() - 4) + archiveExtension));
auto filename = pluginName.substr(0, pluginName.length() - 4) + archiveExtension;
return std::filesystem::exists(dataPath / std::filesystem::u8path(filename));
} else if (gameType != GameType::tes4 ||
boost::iends_with(pluginName, ".esp")) {
// Oblivion .esp files and FO3, FNV, FO4 plugins can load archives which
// begin with the plugin basename.
string basename = pluginName.substr(0, pluginName.length() - 4);
for (const auto& archivePath : gameCache->GetArchivePaths()) {
if (boost::istarts_with(archivePath.filename().string(), basename)) {
if (boost::istarts_with(archivePath.filename().u8string(), basename)) {
return true;
}
}
@@ -33,6 +33,32 @@ along with LOOT. If not, see
namespace loot {
namespace test {
#ifdef _WIN32
class VersionTest : public CommonGameTestFixture {
protected:
VersionTest() : nonAsciiDll(u8"loot_ap\u00ED.dll") {}
void SetUp() {
CommonGameTestFixture::SetUp();
ASSERT_NO_THROW(std::filesystem::copy_file("loot_api.dll",
dataPath / std::filesystem::u8path(nonAsciiDll)));
}
const std::string nonAsciiDll;
};
// Pass an empty first argument, as it's a prefix for the test instantation,
// but we only have the one so no prefix is necessary.
// Just test with one game because if it works for one it will work for them
// all.
INSTANTIATE_TEST_CASE_P(, VersionTest, ::testing::Values(GameType::tes5));
TEST_P(VersionTest, shouldExtractVersionFromNonAsciiDll) {
Version version(dataPath / std::filesystem::u8path(nonAsciiDll));
std::string expected(LootVersion::string() + ".0");
EXPECT_EQ(expected, version.AsString());
}
TEST(Version, shouldExtractVersionFromApiDll) {
// Use the API DLL built.
Version version(std::filesystem::path("loot_api.dll"));
@@ -40,6 +66,7 @@ TEST(Version, shouldExtractVersionFromApiDll) {
EXPECT_EQ(expected, version.AsString());
}
#endif
TEST(Version, defaultConstructorShouldSetEmptyVersionString) {
EXPECT_EQ("", Version().AsString());
}
+41 -6
View File
@@ -37,7 +37,8 @@ protected:
repoBranch("master"),
oldBranch("old-branch"),
repoUrl("https://github.com/loot/testing-metadata.git"),
masterlistPath(localPath / "masterlist.yaml") {}
masterlistPath(localPath / "masterlist.yaml"),
nonAsciiMasterlistPath(localPath / std::filesystem::u8path(u8"masterl\u00EDst.yaml")) {}
void SetUp() {
CommonGameTestFixture::SetUp();
@@ -48,14 +49,23 @@ protected:
ASSERT_TRUE(std::filesystem::exists(metadataFilesPath / "masterlist.yaml"));
ASSERT_FALSE(std::filesystem::exists(masterlistPath));
ASSERT_FALSE(std::filesystem::exists(nonAsciiMasterlistPath));
ASSERT_FALSE(std::filesystem::exists(localPath / ".git"));
}
void runRepoCommand(const std::string& command) {
auto testPath = std::filesystem::current_path();
std::filesystem::current_path(masterlistPath.parent_path());
system(command.c_str());
std::filesystem::current_path(testPath);
}
const std::string repoUrl;
const std::string repoBranch;
const std::string oldBranch;
const std::filesystem::path masterlistPath;
const std::filesystem::path nonAsciiMasterlistPath;
};
// Pass an empty first argument, as it's a prefix for the test instantation,
@@ -125,16 +135,23 @@ TEST_P(MasterlistTest, updateShouldReturnFalseIfAnUpToDateMasterlistExists) {
EXPECT_FALSE(masterlist.Update(masterlistPath, repoUrl, repoBranch));
}
TEST_P(MasterlistTest, updateShouldReturnFalseIfAnUpToDateMasterlistWithANonAsciiFilenameExists) {
Masterlist masterlist;
EXPECT_TRUE(masterlist.Update(nonAsciiMasterlistPath, repoUrl, repoBranch));
EXPECT_TRUE(std::filesystem::exists(nonAsciiMasterlistPath));
EXPECT_FALSE(masterlist.Update(nonAsciiMasterlistPath, repoUrl, repoBranch));
EXPECT_TRUE(std::filesystem::exists(nonAsciiMasterlistPath));
}
TEST_P(MasterlistTest,
updateShouldDiscardLocalHistoryIfRemoteHistoryIsDifferent) {
Masterlist masterlist;
ASSERT_TRUE(masterlist.Update(masterlistPath, repoUrl, repoBranch));
auto testPath = std::filesystem::current_path();
std::filesystem::current_path(masterlistPath.parent_path());
system("git config commit.gpgsign false");
system("git commit --amend -m \"changing local history\"");
std::filesystem::current_path(testPath);
runRepoCommand("git config commit.gpgsign false");
runRepoCommand("git commit --amend -m \"changing local history\"");
EXPECT_TRUE(masterlist.Update(masterlistPath, repoUrl, repoBranch));
}
@@ -192,6 +209,24 @@ TEST_P(
EXPECT_TRUE(info.is_modified);
}
TEST_P(MasterlistTest,
getInfoShouldDetectWhenAMasterlistWithANonAsciiFilenameHasBeenEdited) {
Masterlist masterlist;
ASSERT_TRUE(masterlist.Update(masterlistPath, repoUrl, repoBranch));
auto nonAsciiPath = masterlistPath.parent_path() / std::filesystem::u8path(u8"non\u00C1scii.yaml");
std::filesystem::copy_file(masterlistPath, nonAsciiPath);
runRepoCommand("git add " + nonAsciiPath.string());
std::ofstream out(nonAsciiPath);
out.close();
MasterlistInfo info = masterlist.GetInfo(nonAsciiPath, false);
EXPECT_EQ(40, info.revision_id.length());
EXPECT_EQ(10, info.revision_date.length());
EXPECT_TRUE(info.is_modified);
}
TEST_P(MasterlistTest,
isLatestShouldThrowIfTheGivenPathDoesNotBelongToAGitRepository) {
ASSERT_NO_THROW(std::filesystem::copy(metadataFilesPath / "masterlist.yaml",
@@ -42,9 +42,29 @@ protected:
evaluator_(game_.Type(),
game_.DataPath(),
game_.GetCache(),
game_.GetLoadOrderHandler()) {}
game_.GetLoadOrderHandler()),
nonAsciiEsm(u8"non\u00C1scii.esm"),
nonAsciiNestedFile(u8"non\u00C1scii/test.txt") {
// Make sure the plugin with a non-ASCII filename exists.
std::filesystem::copy_file(dataPath / blankEsm,
dataPath / std::filesystem::u8path(nonAsciiEsm));
auto nonAsciiPath = dataPath / std::filesystem::u8path(nonAsciiNestedFile);
std::filesystem::create_directory(nonAsciiPath.parent_path());
std::ofstream out(nonAsciiPath);
out.close();
}
std::string IntToHexString(const uint32_t value) {
std::stringstream stream;
stream << std::hex << value;
return stream.str();
}
const std::vector<MessageContent> info_;
const std::string nonAsciiEsm;
const std::string nonAsciiNestedFile;
Game game_;
ConditionEvaluator evaluator_;
@@ -75,6 +95,36 @@ TEST_P(ConditionEvaluatorTest,
EXPECT_TRUE(evaluator_.evaluate("file(\"" + blankEsm + "\")"));
}
TEST_P(ConditionEvaluatorTest,
evaluateFileConditionShouldReturnTrueForANonAsciiFileThatExists) {
EXPECT_TRUE(evaluator_.evaluate("file(\"" + nonAsciiEsm + "\")"));
}
TEST_P(ConditionEvaluatorTest,
evaluateChecksumConditionShouldBeAbleToGetTheCrcOfANonAsciiFile) {
std::string condition("checksum(\"" + nonAsciiEsm + "\", " +
IntToHexString(blankEsmCrc) + ")");
EXPECT_TRUE(evaluator_.evaluate(condition));
}
TEST_P(ConditionEvaluatorTest,
evaluateVersionConditionShouldBeAbleToGetTheVersionOfANonAsciiFile) {
std::string condition("version(\"" + nonAsciiEsm + "\", \"5.0\", ==)");
EXPECT_TRUE(evaluator_.evaluate(condition));
}
TEST_P(ConditionEvaluatorTest,
evaluateRegexFileConditionShouldReturnTrueForANonAsciiFileThatExists) {
std::string condition(u8"file(\"non\u00C1scii.*\\.esm\")");
EXPECT_TRUE(evaluator_.evaluate(condition));
}
TEST_P(ConditionEvaluatorTest,
evaluateRegexFileConditionShouldReturnTrueForANonAsciiNestedFileThatExists) {
std::string condition(u8"file(\"non\u00C1scii/.+\\.txt\")");
EXPECT_TRUE(evaluator_.evaluate(condition));
}
TEST_P(ConditionEvaluatorTest,
evaluateShouldReturnFalseForAConditionThatIsFalse) {
EXPECT_FALSE(evaluator_.evaluate("file(\"" + missingEsp + "\")"));
@@ -88,6 +138,14 @@ TEST_P(
EXPECT_TRUE(evaluator_.evaluate(dirtyInfo, blankEsm));
}
TEST_P(
ConditionEvaluatorTest,
evaluateShouldBeTrueIfTheCrcInTheCleaningDataMatchesTheCrcOfANonAsciiPlugin) {
PluginCleaningData dirtyInfo(blankEsmCrc, "cleaner", info_, 2, 10, 30);
EXPECT_TRUE(evaluator_.evaluate(dirtyInfo, nonAsciiEsm));
}
TEST_P(
ConditionEvaluatorTest,
evaluateShouldBeFalseIfTheCrcInThePluginCleaningDataGivenDoesNotMatchTheRealPluginCrc) {
+67 -2
View File
@@ -37,6 +37,7 @@ protected:
PluginTest() :
emptyFile("EmptyFile.esm"),
lowercaseBlankEsp("blank.esp"),
nonAsciiEsp(u8"non\u00C1scii.esp"),
game_(GetParam(), dataPath.parent_path(), localPath),
blankArchive("Blank" + GetArchiveFileExtension(game_.Type())),
blankSuffixArchive("Blank - Different - suffix" +
@@ -57,6 +58,10 @@ protected:
dataPath / lowercaseBlankEsp));
#endif
// Make sure the plugin with a non-ASCII filename exists.
ASSERT_NO_THROW(std::filesystem::copy_file(dataPath / blankEsp,
dataPath / std::filesystem::u8path(nonAsciiEsp)));
if (GetParam() != GameType::fo4 && GetParam() != GameType::tes5se) {
ASSERT_NO_THROW(
std::filesystem::copy(dataPath / blankEsp, dataPath / blankEsl));
@@ -68,14 +73,34 @@ protected:
out.open(dataPath / blankSuffixArchive);
out.close();
auto nonAsciiArchivePath = dataPath / std::filesystem::u8path(u8"non\u00C1scii" + GetArchiveFileExtension(game_.Type()));
out.open(nonAsciiArchivePath);
out.close();
game_.GetCache()->CacheArchivePath(dataPath / blankArchive);
game_.GetCache()->CacheArchivePath(dataPath / blankSuffixArchive);
game_.GetCache()->CacheArchivePath(dataPath / nonAsciiArchivePath);
}
uintmax_t getGhostedPluginFileSize() {
if (GetParam() == GameType::tes4)
return 390;
else
return 1358;
}
uintmax_t getNonAsciiEspFileSize() {
if (GetParam() == GameType::tes4)
return 55;
else
return 1019;
}
Game game_;
const std::string emptyFile;
const std::string lowercaseBlankEsp;
const std::string nonAsciiEsp;
const std::string blankArchive;
const std::string blankSuffixArchive;
@@ -117,6 +142,18 @@ INSTANTIATE_TEST_CASE_P(,
GameType::fo4,
GameType::tes5se));
TEST_P(PluginTest, loadingShouldHandleNonAsciiFilenamesCorrectly) {
Plugin plugin(game_.Type(),
game_.DataPath(),
game_.GetCache(),
game_.GetLoadOrderHandler(),
nonAsciiEsp,
true);
EXPECT_EQ(nonAsciiEsp, plugin.GetName());
EXPECT_EQ(nonAsciiEsp, plugin.GetName());
}
TEST_P(PluginTest, loadingHeaderOnlyShouldReadHeaderData) {
Plugin plugin(game_.Type(),
game_.DataPath(),
@@ -259,16 +296,28 @@ TEST_P(
TEST_P(
PluginTest,
loadsArchiveForAnArchiveThatExactlyMatchesAnEspFileBasenameShouldReturnTrue) {
loadsArchiveForAnArchiveThatExactlyMatchesANonAsciiEspFileBasenameShouldReturnTrue) {
EXPECT_TRUE(Plugin(game_.Type(),
game_.DataPath(),
game_.GetCache(),
game_.GetLoadOrderHandler(),
blankEsp,
nonAsciiEsp,
true)
.LoadsArchive());
}
TEST_P(
PluginTest,
loadsArchiveForAnArchiveThatExactlyMatchesAnEspFileBasenameShouldReturnTrue) {
EXPECT_TRUE(Plugin(game_.Type(),
game_.DataPath(),
game_.GetCache(),
game_.GetLoadOrderHandler(),
blankEsp,
true)
.LoadsArchive());
}
TEST_P(
PluginTest,
loadsArchiveForAnArchiveWithAFilenameWhichStartsWithTheEsmFileBasenameShouldReturnTrueForAllGamesExceptOblivionAndSkyrim) {
@@ -318,6 +367,10 @@ TEST_P(PluginTest, isValidShouldReturnTrueForAValidPlugin) {
EXPECT_TRUE(Plugin::IsValid(blankEsm, game_.Type(), game_.DataPath()));
}
TEST_P(PluginTest, isValidShouldReturnTrueForAValidNonAsciiPlugin) {
EXPECT_TRUE(Plugin::IsValid(nonAsciiEsp, game_.Type(), game_.DataPath()));
}
TEST_P(PluginTest, isValidShouldReturnFalseForANonPluginFile) {
EXPECT_FALSE(Plugin::IsValid(nonPluginFile, game_.Type(), game_.DataPath()));
}
@@ -326,6 +379,18 @@ TEST_P(PluginTest, isValidShouldReturnFalseForAnEmptyFile) {
EXPECT_FALSE(Plugin::IsValid(emptyFile, game_.Type(), game_.DataPath()));
}
TEST_P(PluginTest, getFileSizeShouldThrowForAMissingPlugin) {
EXPECT_THROW(Plugin::GetFileSize(missingEsp, game_.DataPath()), std::filesystem::filesystem_error);
}
TEST_P(PluginTest, getFileSizeShouldReturnCorrectValueForAPlugin) {
EXPECT_EQ(getNonAsciiEspFileSize(), Plugin::GetFileSize(nonAsciiEsp, game_.DataPath()));
}
TEST_P(PluginTest, getFileSizeShouldReturnCorrectValueForAGhostedPlugin) {
EXPECT_EQ(getGhostedPluginFileSize(), Plugin::GetFileSize(blankMasterDependentEsm, game_.DataPath()));
}
TEST_P(PluginTest, isActiveShouldReturnTrueForAPluginThatIsActive) {
EXPECT_TRUE(Plugin(game_.Type(),
game_.DataPath(),