1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-21 07:53:53 +00:00

Merge branch 'cs_universal_id_ref_id' into 'master'

Fix verification error reporting in Editor (#7387)

Closes #7387

See merge request OpenMW/openmw!3044
This commit is contained in:
psi29a 2023-05-22 09:08:57 +00:00
commit c939781cfd
6 changed files with 360 additions and 164 deletions

View file

@ -59,10 +59,19 @@ QVariant CSMTools::ReportModel::data(const QModelIndex& index, int role) const
{ {
CSMWorld::UniversalId id = mRows.at(index.row()).mId; CSMWorld::UniversalId id = mRows.at(index.row()).mId;
if (id.getArgumentType() == CSMWorld::UniversalId::ArgumentType_Id) switch (id.getArgumentType())
return QString::fromUtf8(id.getId().c_str()); {
case CSMWorld::UniversalId::ArgumentType_None:
return QString("-");
case CSMWorld::UniversalId::ArgumentType_Index:
return QString::number(id.getIndex());
case CSMWorld::UniversalId::ArgumentType_Id:
return QString::fromStdString(id.getId());
case CSMWorld::UniversalId::ArgumentType_RefId:
return QString::fromStdString(id.getRefId().toString());
}
return QString("-"); return QString("unsupported");
} }
case Column_Hint: case Column_Hint:

View file

@ -106,7 +106,7 @@ void CSMTools::ScriptCheckStage::perform(int stage, CSMDoc::Messages& messages)
mId = mDocument.getData().getScripts().getId(stage); mId = mDocument.getData().getScripts().getId(stage);
if (mDocument.isBlacklisted(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Script, mId))) if (mDocument.isBlacklisted(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Script, mId.getRefIdString())))
return; return;
// Skip "Base" records (setting!) and "Deleted" records // Skip "Base" records (setting!) and "Deleted" records

View file

@ -3,8 +3,11 @@
#include <algorithm> #include <algorithm>
#include <compare> #include <compare>
#include <iostream> #include <iostream>
#include <span>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string_view>
#include <tuple>
#include <vector> #include <vector>
namespace namespace
@ -13,12 +16,12 @@ namespace
{ {
CSMWorld::UniversalId::Class mClass; CSMWorld::UniversalId::Class mClass;
CSMWorld::UniversalId::Type mType; CSMWorld::UniversalId::Type mType;
const char* mName; std::string_view mName;
const char* mIcon; std::string_view mIcon;
}; };
static const TypeData sNoArg[] = { constexpr TypeData sNoArg[] = {
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", 0 }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", ":placeholder" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables",
":./global-variable.png" }, ":./global-variable.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", ":./gmst.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", ":./gmst.png" },
@ -80,11 +83,9 @@ namespace
":./start-script.png" }, ":./start-script.png" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata", { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata",
":./metadata.png" }, ":./metadata.png" },
// end marker
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 },
}; };
static const TypeData sIdArg[] = { constexpr TypeData sIdArg[] = {
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable",
":./global-variable.png" }, ":./global-variable.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", ":./gmst.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", ":./gmst.png" },
@ -164,24 +165,63 @@ namespace
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script",
":./start-script.png" }, ":./start-script.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Metadata", ":./metadata.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Metadata", ":./metadata.png" },
// end marker
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 },
}; };
static const TypeData sIndexArg[] = { constexpr TypeData sIndexArg[] = {
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults,
"Verification Results", ":./menu-verify.png" }, "Verification Results", ":./menu-verify.png" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log",
":./error-log.png" }, ":./error-log.png" },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search",
":./menu-search.png" }, ":./menu-search.png" },
// end marker
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 },
}; };
struct WriteToStream
{
std::ostream& mStream;
void operator()(std::monostate /*value*/) const {}
template <class T>
void operator()(const T& value) const
{
mStream << ": " << value;
}
};
struct GetTypeData
{
std::span<const TypeData> operator()(std::monostate /*value*/) const { return sNoArg; }
std::span<const TypeData> operator()(int /*value*/) const { return sIndexArg; }
template <class T>
std::span<const TypeData> operator()(const T& /*value*/) const
{
return sIdArg;
}
};
std::string toString(CSMWorld::UniversalId::ArgumentType value)
{
switch (value)
{
case CSMWorld::UniversalId::ArgumentType_None:
return "None";
case CSMWorld::UniversalId::ArgumentType_Id:
return "Id";
case CSMWorld::UniversalId::ArgumentType_Index:
return "Index";
case CSMWorld::UniversalId::ArgumentType_RefId:
return "RefId";
}
return std::to_string(value);
}
} }
CSMWorld::UniversalId::UniversalId(const std::string& universalId) CSMWorld::UniversalId::UniversalId(const std::string& universalId)
: mIndex(0) : mValue(std::monostate{})
{ {
std::string::size_type index = universalId.find(':'); std::string::size_type index = universalId.find(':');
@ -189,39 +229,40 @@ CSMWorld::UniversalId::UniversalId(const std::string& universalId)
{ {
std::string type = universalId.substr(0, index); std::string type = universalId.substr(0, index);
for (int i = 0; sIdArg[i].mName; ++i) for (const TypeData& value : sIdArg)
if (type == sIdArg[i].mName) if (type == value.mName)
{ {
mArgumentType = ArgumentType_Id; mType = value.mType;
mType = sIdArg[i].mType; mClass = value.mClass;
mClass = sIdArg[i].mClass; mValue = universalId.substr(index + 2);
mId = universalId.substr(index + 2);
return; return;
} }
for (int i = 0; sIndexArg[i].mName; ++i) for (const TypeData& value : sIndexArg)
if (type == sIndexArg[i].mName) if (type == value.mName)
{ {
mArgumentType = ArgumentType_Index; mType = value.mType;
mType = sIndexArg[i].mType; mClass = value.mClass;
mClass = sIndexArg[i].mClass;
std::istringstream stream(universalId.substr(index + 2)); std::istringstream stream(universalId.substr(index + 2));
if (stream >> mIndex) int index = 0;
if (stream >> index)
{
mValue = index;
return; return;
}
break; break;
} }
} }
else else
{ {
for (int i = 0; sNoArg[i].mName; ++i) for (const TypeData& value : sIndexArg)
if (universalId == sNoArg[i].mName) if (universalId == value.mName)
{ {
mArgumentType = ArgumentType_None; mType = value.mType;
mType = sNoArg[i].mType; mClass = value.mClass;
mClass = sNoArg[i].mClass;
return; return;
} }
} }
@ -230,30 +271,29 @@ CSMWorld::UniversalId::UniversalId(const std::string& universalId)
} }
CSMWorld::UniversalId::UniversalId(Type type) CSMWorld::UniversalId::UniversalId(Type type)
: mArgumentType(ArgumentType_None) : mType(type)
, mType(type) , mValue(std::monostate{})
, mIndex(0)
{ {
for (int i = 0; sNoArg[i].mName; ++i) for (const TypeData& value : sNoArg)
if (type == sNoArg[i].mType) if (type == value.mType)
{ {
mClass = sNoArg[i].mClass; mClass = value.mClass;
return; return;
} }
for (int i = 0; sIdArg[i].mName; ++i) for (const TypeData& value : sIdArg)
if (type == sIdArg[i].mType) if (type == value.mType)
{ {
mArgumentType = ArgumentType_Id; mValue = std::string();
mClass = sIdArg[i].mClass; mClass = value.mClass;
return; return;
} }
for (int i = 0; sIndexArg[i].mName; ++i) for (const TypeData& value : sIndexArg)
if (type == sIndexArg[i].mType) if (type == value.mType)
{ {
mArgumentType = ArgumentType_Index; mValue = int{};
mClass = sIndexArg[i].mClass; mClass = value.mClass;
return; return;
} }
@ -261,38 +301,43 @@ CSMWorld::UniversalId::UniversalId(Type type)
} }
CSMWorld::UniversalId::UniversalId(Type type, const std::string& id) CSMWorld::UniversalId::UniversalId(Type type, const std::string& id)
: mArgumentType(ArgumentType_Id) : mType(type)
, mType(type) , mValue(id)
, mId(id)
, mIndex(0)
{ {
for (int i = 0; sIdArg[i].mName; ++i) for (const TypeData& value : sIdArg)
if (type == sIdArg[i].mType) if (type == value.mType)
{ {
mClass = sIdArg[i].mClass; mClass = value.mClass;
return; return;
} }
throw std::logic_error("invalid ID argument UniversalId type"); throw std::logic_error("invalid ID argument UniversalId type: " + std::to_string(type));
} }
CSMWorld::UniversalId::UniversalId(Type type, const ESM::RefId& id) CSMWorld::UniversalId::UniversalId(Type type, ESM::RefId id)
: mType(type)
, mValue(id)
{ {
UniversalId(type, id.getRefIdString()); for (const TypeData& value : sIdArg)
if (type == value.mType)
{
mClass = value.mClass;
return;
}
throw std::logic_error("invalid RefId argument UniversalId type: " + std::to_string(type));
} }
CSMWorld::UniversalId::UniversalId(Type type, int index) CSMWorld::UniversalId::UniversalId(Type type, int index)
: mArgumentType(ArgumentType_Index) : mType(type)
, mType(type) , mValue(index)
, mIndex(index)
{ {
for (int i = 0; sIndexArg[i].mName; ++i) for (const TypeData& value : sIndexArg)
if (type == sIndexArg[i].mType) if (type == value.mType)
{ {
mClass = sIndexArg[i].mClass; mClass = value.mClass;
return; return;
} }
throw std::logic_error("invalid index argument UniversalId type"); throw std::logic_error("invalid index argument UniversalId type: " + std::to_string(type));
} }
CSMWorld::UniversalId::Class CSMWorld::UniversalId::getClass() const CSMWorld::UniversalId::Class CSMWorld::UniversalId::getClass() const
@ -302,7 +347,7 @@ CSMWorld::UniversalId::Class CSMWorld::UniversalId::getClass() const
CSMWorld::UniversalId::ArgumentType CSMWorld::UniversalId::getArgumentType() const CSMWorld::UniversalId::ArgumentType CSMWorld::UniversalId::getArgumentType() const
{ {
return mArgumentType; return static_cast<CSMWorld::UniversalId::ArgumentType>(mValue.index());
} }
CSMWorld::UniversalId::Type CSMWorld::UniversalId::getType() const CSMWorld::UniversalId::Type CSMWorld::UniversalId::getType() const
@ -312,65 +357,35 @@ CSMWorld::UniversalId::Type CSMWorld::UniversalId::getType() const
const std::string& CSMWorld::UniversalId::getId() const const std::string& CSMWorld::UniversalId::getId() const
{ {
if (mArgumentType != ArgumentType_Id) if (const std::string* result = std::get_if<std::string>(&mValue))
throw std::logic_error("invalid access to ID of non-ID UniversalId"); return *result;
return mId; throw std::logic_error("invalid access to ID of " + ::toString(getArgumentType()) + " UniversalId");
} }
int CSMWorld::UniversalId::getIndex() const int CSMWorld::UniversalId::getIndex() const
{ {
if (mArgumentType != ArgumentType_Index) if (const int* result = std::get_if<int>(&mValue))
throw std::logic_error("invalid access to index of non-index UniversalId"); return *result;
return mIndex; throw std::logic_error("invalid access to index of " + ::toString(getArgumentType()) + " UniversalId");
} }
bool CSMWorld::UniversalId::isEqual(const UniversalId& universalId) const ESM::RefId CSMWorld::UniversalId::getRefId() const
{ {
if (mClass != universalId.mClass || mArgumentType != universalId.mArgumentType || mType != universalId.mType) if (const ESM::RefId* result = std::get_if<ESM::RefId>(&mValue))
return false; return *result;
switch (mArgumentType) throw std::logic_error("invalid access to RefId of " + ::toString(getArgumentType()) + " UniversalId");
{
case ArgumentType_Id:
return mId == universalId.mId;
case ArgumentType_Index:
return mIndex == universalId.mIndex;
default:
return true;
}
}
bool CSMWorld::UniversalId::isLess(const UniversalId& universalId) const
{
if (mType < universalId.mType)
return true;
if (mType > universalId.mType)
return false;
switch (mArgumentType)
{
case ArgumentType_Id:
return mId < universalId.mId;
case ArgumentType_Index:
return mIndex < universalId.mIndex;
default:
return false;
}
} }
std::string CSMWorld::UniversalId::getTypeName() const std::string CSMWorld::UniversalId::getTypeName() const
{ {
const TypeData* typeData const std::span<const TypeData> typeData = std::visit(GetTypeData{}, mValue);
= mArgumentType == ArgumentType_None ? sNoArg : (mArgumentType == ArgumentType_Id ? sIdArg : sIndexArg);
for (int i = 0; typeData[i].mName; ++i) for (const TypeData& value : typeData)
if (typeData[i].mType == mType) if (value.mType == mType)
return typeData[i].mName; return std::string(value.mName);
throw std::logic_error("failed to retrieve UniversalId type name"); throw std::logic_error("failed to retrieve UniversalId type name");
} }
@ -381,29 +396,18 @@ std::string CSMWorld::UniversalId::toString() const
stream << getTypeName(); stream << getTypeName();
switch (mArgumentType) std::visit(WriteToStream{ stream }, mValue);
{
case ArgumentType_None:
break;
case ArgumentType_Id:
stream << ": " << mId;
break;
case ArgumentType_Index:
stream << ": " << mIndex;
break;
}
return stream.str(); return stream.str();
} }
std::string CSMWorld::UniversalId::getIcon() const std::string CSMWorld::UniversalId::getIcon() const
{ {
const TypeData* typeData const std::span<const TypeData> typeData = std::visit(GetTypeData{}, mValue);
= mArgumentType == ArgumentType_None ? sNoArg : (mArgumentType == ArgumentType_Id ? sIdArg : sIndexArg);
for (int i = 0; typeData[i].mName; ++i) for (const TypeData& value : typeData)
if (typeData[i].mType == mType) if (value.mType == mType)
return typeData[i].mIcon ? typeData[i].mIcon : ":placeholder"; return std::string(value.mIcon);
throw std::logic_error("failed to retrieve UniversalId type icon"); throw std::logic_error("failed to retrieve UniversalId type icon");
} }
@ -412,9 +416,9 @@ std::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listReferenceabl
{ {
std::vector<CSMWorld::UniversalId::Type> list; std::vector<CSMWorld::UniversalId::Type> list;
for (int i = 0; sIdArg[i].mName; ++i) for (const TypeData& value : sIdArg)
if (sIdArg[i].mClass == Class_RefRecord) if (value.mClass == Class_RefRecord)
list.push_back(sIdArg[i].mType); list.push_back(value.mType);
return list; return list;
} }
@ -423,31 +427,30 @@ std::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listTypes(int cl
{ {
std::vector<CSMWorld::UniversalId::Type> list; std::vector<CSMWorld::UniversalId::Type> list;
for (int i = 0; sNoArg[i].mName; ++i) for (const TypeData& value : sNoArg)
if (sNoArg[i].mClass & classes) if (value.mClass & classes)
list.push_back(sNoArg[i].mType); list.push_back(value.mType);
for (int i = 0; sIdArg[i].mName; ++i) for (const TypeData& value : sIdArg)
if (sIdArg[i].mClass & classes) if (value.mClass & classes)
list.push_back(sIdArg[i].mType); list.push_back(value.mType);
for (int i = 0; sIndexArg[i].mName; ++i) for (const TypeData& value : sIndexArg)
if (sIndexArg[i].mClass & classes) if (value.mClass & classes)
list.push_back(sIndexArg[i].mType); list.push_back(value.mType);
return list; return list;
} }
CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType(Type type) CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType(Type type)
{ {
for (int i = 0; sIdArg[i].mType; ++i) for (const TypeData& value : sIdArg)
if (type == sIdArg[i].mType) if (type == value.mType)
{ {
if (sIdArg[i].mClass == Class_RefRecord) if (value.mClass == Class_RefRecord)
return Type_Referenceables; return Type_Referenceables;
if (sIdArg[i].mClass == Class_SubRecord || sIdArg[i].mClass == Class_Record if (value.mClass == Class_SubRecord || value.mClass == Class_Record || value.mClass == Class_Resource)
|| sIdArg[i].mClass == Class_Resource)
{ {
if (type == Type_Cell_Missing) if (type == Type_Cell_Missing)
return Type_Cells; return Type_Cells;
@ -463,15 +466,10 @@ CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType(Type type)
bool CSMWorld::operator==(const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right) bool CSMWorld::operator==(const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right)
{ {
return left.isEqual(right); return std::tie(left.mClass, left.mType, left.mValue) == std::tie(right.mClass, right.mType, right.mValue);
}
bool CSMWorld::operator!=(const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right)
{
return !left.isEqual(right);
} }
bool CSMWorld::operator<(const UniversalId& left, const UniversalId& right) bool CSMWorld::operator<(const UniversalId& left, const UniversalId& right)
{ {
return left.isLess(right); return std::tie(left.mClass, left.mType, left.mValue) < std::tie(right.mClass, right.mType, right.mValue);
} }

View file

@ -2,6 +2,7 @@
#define CSM_WOLRD_UNIVERSALID_H #define CSM_WOLRD_UNIVERSALID_H
#include <string> #include <string>
#include <variant>
#include <vector> #include <vector>
#include <QMetaType> #include <QMetaType>
@ -31,7 +32,8 @@ namespace CSMWorld
{ {
ArgumentType_None, ArgumentType_None,
ArgumentType_Id, ArgumentType_Id,
ArgumentType_Index ArgumentType_Index,
ArgumentType_RefId,
}; };
/// \note A record list type must always be immediately followed by the matching /// \note A record list type must always be immediately followed by the matching
@ -144,14 +146,6 @@ namespace CSMWorld
NumberOfTypes = Type_RunLog + 1 NumberOfTypes = Type_RunLog + 1
}; };
private:
Class mClass;
ArgumentType mArgumentType;
Type mType;
std::string mId;
int mIndex;
public:
UniversalId(const std::string& universalId); UniversalId(const std::string& universalId);
UniversalId(Type type = Type_None); UniversalId(Type type = Type_None);
@ -159,7 +153,7 @@ namespace CSMWorld
UniversalId(Type type, const std::string& id); UniversalId(Type type, const std::string& id);
///< Using a type for a non-ID-argument UniversalId will throw an exception. ///< Using a type for a non-ID-argument UniversalId will throw an exception.
UniversalId(Type type, const ESM::RefId& id); UniversalId(Type type, ESM::RefId id);
UniversalId(Type type, int index); UniversalId(Type type, int index);
///< Using a type for a non-index-argument UniversalId will throw an exception. ///< Using a type for a non-index-argument UniversalId will throw an exception.
@ -176,9 +170,7 @@ namespace CSMWorld
int getIndex() const; int getIndex() const;
///< Calling this function for a non-index type will throw an exception. ///< Calling this function for a non-index type will throw an exception.
bool isEqual(const UniversalId& universalId) const; ESM::RefId getRefId() const;
bool isLess(const UniversalId& universalId) const;
std::string getTypeName() const; std::string getTypeName() const;
@ -195,10 +187,18 @@ namespace CSMWorld
/// that contains records of type \a type. /// that contains records of type \a type.
/// Otherwise return Type_None. /// Otherwise return Type_None.
static Type getParentType(Type type); static Type getParentType(Type type);
private:
Class mClass;
Type mType;
std::variant<std::monostate, std::string, int, ESM::RefId> mValue;
friend bool operator==(const UniversalId& left, const UniversalId& right);
friend bool operator<(const UniversalId& left, const UniversalId& right);
}; };
bool operator==(const UniversalId& left, const UniversalId& right); bool operator==(const UniversalId& left, const UniversalId& right);
bool operator!=(const UniversalId& left, const UniversalId& right);
bool operator<(const UniversalId& left, const UniversalId& right); bool operator<(const UniversalId& left, const UniversalId& right);
} }

View file

@ -1,6 +1,7 @@
file(GLOB OPENCS_TESTS_SRC_FILES file(GLOB OPENCS_TESTS_SRC_FILES
main.cpp main.cpp
model/world/testinfocollection.cpp model/world/testinfocollection.cpp
model/world/testuniversalid.cpp
) )
source_group(apps\\openmw-cs-tests FILES ${OPENCS_TESTS_SRC_FILES}) source_group(apps\\openmw-cs-tests FILES ${OPENCS_TESTS_SRC_FILES})

View file

@ -0,0 +1,188 @@
#include "apps/opencs/model/world/universalid.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdexcept>
namespace CSMWorld
{
namespace
{
using namespace ::testing;
TEST(CSMWorldUniversalIdTest, shouldFailToConstructFromNoneWithInvalidType)
{
EXPECT_THROW(
UniversalId{ static_cast<UniversalId::Type>(std::numeric_limits<int>::max()) }, std::logic_error);
}
TEST(CSMWorldUniversalIdTest, shouldFailToConstructFromStringWithInvalidType)
{
EXPECT_THROW(UniversalId(UniversalId::Type_Search, "invalid"), std::logic_error);
}
TEST(CSMWorldUniversalIdTest, shouldFailToConstructFromIntWithInvalidType)
{
EXPECT_THROW(UniversalId(UniversalId::Type_Activator, 42), std::logic_error);
}
TEST(CSMWorldUniversalIdTest, shouldFailToConstructFromRefIdWithInvalidType)
{
EXPECT_THROW(UniversalId(UniversalId::Type_Search, ESM::RefId()), std::logic_error);
}
TEST(CSMWorldUniversalIdTest, shouldFailToConstructFromInvalidUniversalIdString)
{
EXPECT_THROW(UniversalId("invalid"), std::runtime_error);
}
TEST(CSMWorldUniversalIdTest, getIndexShouldThrowExceptionForDefaultConstructed)
{
const UniversalId id;
EXPECT_THROW(id.getIndex(), std::logic_error);
}
TEST(CSMWorldUniversalIdTest, getIndexShouldThrowExceptionForConstructedFromString)
{
const UniversalId id(UniversalId::Type_Activator, "a");
EXPECT_THROW(id.getIndex(), std::logic_error);
}
TEST(CSMWorldUniversalIdTest, getIndexShouldReturnValueForConstructedFromInt)
{
const UniversalId id(UniversalId::Type_Search, 42);
EXPECT_EQ(id.getIndex(), 42);
}
TEST(CSMWorldUniversalIdTest, getIdShouldThrowExceptionForConstructedFromInt)
{
const UniversalId id(UniversalId::Type_Search, 42);
EXPECT_THROW(id.getId(), std::logic_error);
}
TEST(CSMWorldUniversalIdTest, getIdShouldReturnValueForConstructedFromString)
{
const UniversalId id(UniversalId::Type_Activator, "a");
EXPECT_EQ(id.getId(), "a");
}
TEST(CSMWorldUniversalIdTest, getRefIdShouldThrowExceptionForDefaultConstructed)
{
const UniversalId id;
EXPECT_THROW(id.getRefId(), std::logic_error);
}
TEST(CSMWorldUniversalIdTest, getRefIdShouldReturnValueForConstructedFromRefId)
{
const UniversalId id(UniversalId::Type_Skill, ESM::IndexRefId(ESM::REC_SKIL, 42));
EXPECT_EQ(id.getRefId(), ESM::IndexRefId(ESM::REC_SKIL, 42));
}
struct Params
{
UniversalId mId;
UniversalId::Type mType;
UniversalId::Class mClass;
UniversalId::ArgumentType mArgumentType;
std::string mTypeName;
std::string mString;
std::string mIcon;
};
std::ostream& operator<<(std::ostream& stream, const Params& value)
{
return stream << ".mType = " << value.mType << " .mClass = " << value.mClass
<< " .mArgumentType = " << value.mArgumentType << " .mTypeName = " << value.mTypeName
<< " .mString = " << value.mString << " .mIcon = " << value.mIcon;
}
struct CSMWorldUniversalIdValidPerTypeTest : TestWithParam<Params>
{
};
TEST_P(CSMWorldUniversalIdValidPerTypeTest, getTypeShouldReturnExpected)
{
EXPECT_EQ(GetParam().mId.getType(), GetParam().mType);
}
TEST_P(CSMWorldUniversalIdValidPerTypeTest, getClassShouldReturnExpected)
{
EXPECT_EQ(GetParam().mId.getClass(), GetParam().mClass);
}
TEST_P(CSMWorldUniversalIdValidPerTypeTest, getArgumentTypeShouldReturnExpected)
{
EXPECT_EQ(GetParam().mId.getArgumentType(), GetParam().mArgumentType);
}
TEST_P(CSMWorldUniversalIdValidPerTypeTest, shouldBeEqualToCopy)
{
EXPECT_EQ(GetParam().mId, UniversalId(GetParam().mId));
}
TEST_P(CSMWorldUniversalIdValidPerTypeTest, shouldNotBeLessThanCopy)
{
EXPECT_FALSE(GetParam().mId < UniversalId(GetParam().mId));
}
TEST_P(CSMWorldUniversalIdValidPerTypeTest, getTypeNameShouldReturnExpected)
{
EXPECT_EQ(GetParam().mId.getTypeName(), GetParam().mTypeName);
}
TEST_P(CSMWorldUniversalIdValidPerTypeTest, toStringShouldReturnExpected)
{
EXPECT_EQ(GetParam().mId.toString(), GetParam().mString);
}
TEST_P(CSMWorldUniversalIdValidPerTypeTest, getIconShouldReturnExpected)
{
EXPECT_EQ(GetParam().mId.getIcon(), GetParam().mIcon);
}
const std::array validParams = {
Params{ UniversalId(), UniversalId::Type_None, UniversalId::Class_None, UniversalId::ArgumentType_None, "-",
"-", ":placeholder" },
Params{ UniversalId(UniversalId::Type_None), UniversalId::Type_None, UniversalId::Class_None,
UniversalId::ArgumentType_None, "-", "-", ":placeholder" },
Params{ UniversalId(UniversalId::Type_RegionMap), UniversalId::Type_RegionMap, UniversalId::Class_NonRecord,
UniversalId::ArgumentType_None, "Region Map", "Region Map", ":./region-map.png" },
Params{ UniversalId(UniversalId::Type_RunLog), UniversalId::Type_RunLog, UniversalId::Class_Transient,
UniversalId::ArgumentType_None, "Run Log", "Run Log", ":./run-log.png" },
Params{ UniversalId(UniversalId::Type_Lands), UniversalId::Type_Lands, UniversalId::Class_RecordList,
UniversalId::ArgumentType_None, "Lands", "Lands", ":./land-heightmap.png" },
Params{ UniversalId(UniversalId::Type_Icons), UniversalId::Type_Icons, UniversalId::Class_ResourceList,
UniversalId::ArgumentType_None, "Icons", "Icons", ":./resources-icon" },
Params{ UniversalId(UniversalId::Type_Activator, "a"), UniversalId::Type_Activator,
UniversalId::Class_RefRecord, UniversalId::ArgumentType_Id, "Activator", "Activator: a",
":./activator.png" },
Params{ UniversalId(UniversalId::Type_Gmst, "b"), UniversalId::Type_Gmst, UniversalId::Class_Record,
UniversalId::ArgumentType_Id, "Game Setting", "Game Setting: b", ":./gmst.png" },
Params{ UniversalId(UniversalId::Type_Mesh, "c"), UniversalId::Type_Mesh, UniversalId::Class_Resource,
UniversalId::ArgumentType_Id, "Mesh", "Mesh: c", ":./resources-mesh" },
Params{ UniversalId(UniversalId::Type_Scene, "d"), UniversalId::Type_Scene, UniversalId::Class_Collection,
UniversalId::ArgumentType_Id, "Scene", "Scene: d", ":./scene.png" },
Params{ UniversalId(UniversalId::Type_Reference, "e"), UniversalId::Type_Reference,
UniversalId::Class_SubRecord, UniversalId::ArgumentType_Id, "Instance", "Instance: e",
":./instance.png" },
Params{ UniversalId(UniversalId::Type_Search, 42), UniversalId::Type_Search, UniversalId::Class_Transient,
UniversalId::ArgumentType_Index, "Global Search", "Global Search: 42", ":./menu-search.png" },
Params{ UniversalId("Instance: f"), UniversalId::Type_Reference, UniversalId::Class_SubRecord,
UniversalId::ArgumentType_Id, "Instance", "Instance: f", ":./instance.png" },
Params{ UniversalId(UniversalId::Type_Reference, ESM::RefId::stringRefId("g")), UniversalId::Type_Reference,
UniversalId::Class_SubRecord, UniversalId::ArgumentType_RefId, "Instance", "Instance: \"g\"",
":./instance.png" },
Params{ UniversalId(UniversalId::Type_Reference, ESM::RefId::index(ESM::REC_SKIL, 42)),
UniversalId::Type_Reference, UniversalId::Class_SubRecord, UniversalId::ArgumentType_RefId, "Instance",
"Instance: Index:SKIL:0x2a", ":./instance.png" },
};
INSTANTIATE_TEST_SUITE_P(ValidParams, CSMWorldUniversalIdValidPerTypeTest, ValuesIn(validParams));
}
}