You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/apps/opencs_tests/model/world/testinfocollection.cpp

638 lines
26 KiB
C++

#include "apps/opencs/model/world/infocollection.hpp"
#include "components/esm3/esmreader.hpp"
#include "components/esm3/esmwriter.hpp"
#include "components/esm3/formatversion.hpp"
#include "components/esm3/loaddial.hpp"
#include "components/esm3/loadinfo.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include <sstream>
#include <vector>
namespace CSMWorld
{
inline std::ostream& operator<<(std::ostream& stream, const Record<Info>* value)
{
return stream << "&Record{.mState=" << value->mState << ", .mId=" << value->get().mId << "}";
}
namespace
{
using namespace ::testing;
struct DialInfoData
{
ESM::DialInfo mValue;
bool mDeleted = false;
void save(ESM::ESMWriter& writer) const { mValue.save(writer, mDeleted); }
};
template <class T>
struct DialogueData
{
ESM::Dialogue mDialogue;
std::vector<T> mInfos;
};
DialogueData<ESM::DialInfo> generateDialogueWithInfos(
std::size_t infoCount, std::string_view dialogueId = "dialogue")
{
DialogueData<ESM::DialInfo> result;
result.mDialogue.blank();
result.mDialogue.mId = ESM::RefId::stringRefId(dialogueId);
result.mDialogue.mStringId = dialogueId;
for (std::size_t i = 0; i < infoCount; ++i)
{
ESM::DialInfo& info = result.mInfos.emplace_back();
info.blank();
info.mId = ESM::RefId::stringRefId("info" + std::to_string(i));
}
if (infoCount >= 2)
{
result.mInfos[0].mNext = result.mInfos[1].mId;
result.mInfos[infoCount - 1].mPrev = result.mInfos[infoCount - 2].mId;
}
for (std::size_t i = 1; i < infoCount - 1; ++i)
{
result.mInfos[i].mPrev = result.mInfos[i - 1].mId;
result.mInfos[i].mNext = result.mInfos[i + 1].mId;
}
return result;
}
template <class Infos>
std::unique_ptr<std::stringstream> saveDialogueWithInfos(const ESM::Dialogue& dialogue, Infos&& infos)
{
auto stream = std::make_unique<std::stringstream>();
ESM::ESMWriter writer;
writer.setFormatVersion(ESM::CurrentSaveGameFormatVersion);
writer.save(*stream);
writer.startRecord(ESM::REC_DIAL);
dialogue.save(writer);
writer.endRecord(ESM::REC_DIAL);
for (const auto& info : infos)
{
writer.startRecord(ESM::REC_INFO);
info.save(writer);
writer.endRecord(ESM::REC_INFO);
}
return stream;
}
void loadDialogueWithInfos(bool base, std::unique_ptr<std::stringstream> stream, InfoCollection& infoCollection,
InfoOrderByTopic& infoOrder)
{
ESM::ESMReader reader;
reader.open(std::move(stream), "test");
ASSERT_TRUE(reader.hasMoreRecs());
ASSERT_EQ(reader.getRecName().toInt(), ESM::REC_DIAL);
reader.getRecHeader();
bool isDeleted;
ESM::Dialogue dialogue;
dialogue.load(reader, isDeleted);
while (reader.hasMoreRecs())
{
ASSERT_EQ(reader.getRecName().toInt(), ESM::REC_INFO);
reader.getRecHeader();
infoCollection.load(reader, base, dialogue, infoOrder);
}
}
template <class Infos>
void saveAndLoadDialogueWithInfos(const ESM::Dialogue& dialogue, Infos&& infos, bool base,
InfoCollection& infoCollection, InfoOrderByTopic& infoOrder)
{
loadDialogueWithInfos(base, saveDialogueWithInfos(dialogue, infos), infoCollection, infoOrder);
}
template <class T>
void saveAndLoadDialogueWithInfos(
const DialogueData<T>& data, bool base, InfoCollection& infoCollection, InfoOrderByTopic& infoOrder)
{
saveAndLoadDialogueWithInfos(data.mDialogue, data.mInfos, base, infoCollection, infoOrder);
}
MATCHER_P(InfoId, v, "")
{
return arg.mId == v;
}
TEST(CSMWorldInfoCollectionTest, loadShouldAddRecord)
{
ESM::Dialogue dialogue;
dialogue.blank();
dialogue.mId = ESM::RefId::stringRefId("dialogue");
dialogue.mStringId = "Dialogue";
ESM::DialInfo info;
info.blank();
info.mId = ESM::RefId::stringRefId("info0");
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, base, collection, infoOrder);
EXPECT_EQ(collection.getSize(), 1);
ASSERT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 0);
const Record<Info>& record = collection.getRecord(0);
ASSERT_EQ(record.mState, RecordBase::State_BaseOnly);
EXPECT_EQ(record.mBase.mTopicId, dialogue.mId);
EXPECT_EQ(record.mBase.mOriginalId, info.mId);
EXPECT_EQ(record.mBase.mId, ESM::RefId::stringRefId("dialogue#info0"));
ASSERT_THAT(infoOrder, ElementsAre(Key(dialogue.mId)));
EXPECT_THAT(infoOrder.find(dialogue.mId)->second.getOrderedInfo(), ElementsAre(InfoId(info.mId)));
}
TEST(CSMWorldInfoCollectionTest, loadShouldAddRecordAndMarkModifiedOnlyWhenNotBase)
{
ESM::Dialogue dialogue;
dialogue.blank();
dialogue.mId = ESM::RefId::stringRefId("dialogue");
dialogue.mStringId = "Dialogue";
ESM::DialInfo info;
info.blank();
info.mId = ESM::RefId::stringRefId("info0");
const bool base = false;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, base, collection, infoOrder);
EXPECT_EQ(collection.getSize(), 1);
ASSERT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 0);
const Record<Info>& record = collection.getRecord(0);
ASSERT_EQ(record.mState, RecordBase::State_ModifiedOnly);
EXPECT_EQ(record.mModified.mTopicId, dialogue.mId);
EXPECT_EQ(record.mModified.mOriginalId, info.mId);
EXPECT_EQ(record.mModified.mId, ESM::RefId::stringRefId("dialogue#info0"));
ASSERT_THAT(infoOrder, ElementsAre(Key(dialogue.mId)));
EXPECT_THAT(infoOrder.find(dialogue.mId)->second.getOrderedInfo(), ElementsAre(InfoId(info.mId)));
}
TEST(CSMWorldInfoCollectionTest, loadShouldUpdateRecord)
{
ESM::Dialogue dialogue;
dialogue.blank();
dialogue.mId = ESM::RefId::stringRefId("dialogue");
dialogue.mStringId = "Dialogue";
ESM::DialInfo info;
info.blank();
info.mId = ESM::RefId::stringRefId("info0");
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, base, collection, infoOrder);
ESM::DialInfo updatedInfo = info;
updatedInfo.mActor = ESM::RefId::stringRefId("newActor");
saveAndLoadDialogueWithInfos(dialogue, std::array{ updatedInfo }, base, collection, infoOrder);
ASSERT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 0);
const Record<Info>& record = collection.getRecord(0);
ASSERT_EQ(record.mState, RecordBase::State_BaseOnly);
EXPECT_EQ(record.mBase.mActor, ESM::RefId::stringRefId("newActor"));
ASSERT_THAT(infoOrder, ElementsAre(Key(dialogue.mId)));
EXPECT_THAT(infoOrder.find(dialogue.mId)->second.getOrderedInfo(), ElementsAre(InfoId(info.mId)));
}
TEST(CSMWorldInfoCollectionTest, loadShouldUpdateRecordAndMarkModifiedWhenNotBase)
{
ESM::Dialogue dialogue;
dialogue.blank();
dialogue.mId = ESM::RefId::stringRefId("dialogue");
dialogue.mStringId = "Dialogue";
ESM::DialInfo info;
info.blank();
info.mId = ESM::RefId::stringRefId("info0");
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, base, collection, infoOrder);
ESM::DialInfo updatedInfo = info;
updatedInfo.mActor = ESM::RefId::stringRefId("newActor");
saveAndLoadDialogueWithInfos(dialogue, std::array{ updatedInfo }, false, collection, infoOrder);
ASSERT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 0);
const Record<Info>& record = collection.getRecord(0);
ASSERT_EQ(record.mState, RecordBase::State_Modified);
EXPECT_EQ(record.mModified.mActor, ESM::RefId::stringRefId("newActor"));
ASSERT_THAT(infoOrder, ElementsAre(Key(dialogue.mId)));
EXPECT_THAT(infoOrder.find(dialogue.mId)->second.getOrderedInfo(), ElementsAre(InfoId(info.mId)));
}
TEST(CSMWorldInfoCollectionTest, loadShouldSkipAbsentDeletedRecord)
{
ESM::Dialogue dialogue;
dialogue.blank();
dialogue.mId = ESM::RefId::stringRefId("dialogue");
dialogue.mStringId = "Dialogue";
DialInfoData info;
info.mValue.blank();
info.mValue.mId = ESM::RefId::stringRefId("info0");
info.mDeleted = true;
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, base, collection, infoOrder);
EXPECT_EQ(collection.getSize(), 0);
ASSERT_THAT(infoOrder, ElementsAre());
}
TEST(CSMWorldInfoCollectionTest, loadShouldRemovePresentDeletedBaseRecord)
{
ESM::Dialogue dialogue;
dialogue.blank();
dialogue.mId = ESM::RefId::stringRefId("dialogue");
dialogue.mStringId = "Dialogue";
DialInfoData info;
info.mValue.blank();
info.mValue.mId = ESM::RefId::stringRefId("info0");
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, base, collection, infoOrder);
info.mDeleted = true;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, base, collection, infoOrder);
EXPECT_EQ(collection.getSize(), 0);
ASSERT_THAT(infoOrder, ElementsAre(Key(dialogue.mId)));
EXPECT_THAT(infoOrder.find(dialogue.mId)->second.getOrderedInfo(), ElementsAre());
}
TEST(CSMWorldInfoCollectionTest, loadShouldMarkAsDeletedNotBaseRecord)
{
ESM::Dialogue dialogue;
dialogue.blank();
dialogue.mId = ESM::RefId::stringRefId("dialogue");
dialogue.mStringId = "Dialogue";
DialInfoData info;
info.mValue.blank();
info.mValue.mId = ESM::RefId::stringRefId("info0");
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, base, collection, infoOrder);
info.mDeleted = true;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, false, collection, infoOrder);
EXPECT_EQ(collection.getSize(), 1);
EXPECT_EQ(
collection.getRecord(ESM::RefId::stringRefId("dialogue#info0")).mState, RecordBase::State_Deleted);
ASSERT_THAT(infoOrder, ElementsAre(Key(dialogue.mId)));
EXPECT_THAT(infoOrder.find(dialogue.mId)->second.getOrderedInfo(), ElementsAre(InfoId(info.mValue.mId)));
}
TEST(CSMWorldInfoCollectionTest, sortShouldOrderRecordsBasedOnPrev)
{
const DialogueData<ESM::DialInfo> data = generateDialogueWithInfos(3);
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(data, base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.getSize(), 3);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info1")), 1);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info2")), 2);
}
TEST(CSMWorldInfoCollectionTest, sortShouldOrderRecordsBasedOnPrevWhenReversed)
{
DialogueData<ESM::DialInfo> data = generateDialogueWithInfos(3);
std::reverse(data.mInfos.begin(), data.mInfos.end());
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(data, base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.getSize(), 3);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info2")), 1);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info1")), 2);
}
TEST(CSMWorldInfoCollectionTest, sortShouldInsertNewRecordBasedOnPrev)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
const DialogueData<ESM::DialInfo> data = generateDialogueWithInfos(3);
saveAndLoadDialogueWithInfos(data, base, collection, infoOrder);
ESM::DialInfo newInfo;
newInfo.blank();
newInfo.mId = ESM::RefId::stringRefId("newInfo");
newInfo.mPrev = data.mInfos[1].mId;
newInfo.mNext = ESM::RefId::stringRefId("invalid");
saveAndLoadDialogueWithInfos(data.mDialogue, std::array{ newInfo }, base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.getSize(), 4);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info1")), 1);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#newInfo")), 2);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info2")), 3);
}
TEST(CSMWorldInfoCollectionTest, sortShouldInsertNewRecordToFrontWhenPrevIsEmpty)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
const DialogueData<ESM::DialInfo> data = generateDialogueWithInfos(3);
saveAndLoadDialogueWithInfos(data, base, collection, infoOrder);
ESM::DialInfo newInfo;
newInfo.blank();
newInfo.mId = ESM::RefId::stringRefId("newInfo");
newInfo.mNext = ESM::RefId::stringRefId("invalid");
saveAndLoadDialogueWithInfos(data.mDialogue, std::array{ newInfo }, base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.getSize(), 4);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#newInfo")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 1);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info1")), 2);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info2")), 3);
}
TEST(CSMWorldInfoCollectionTest, sortShouldInsertNewRecordToBackWhenPrevIsNotFound)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
const DialogueData<ESM::DialInfo> data = generateDialogueWithInfos(3);
saveAndLoadDialogueWithInfos(data, base, collection, infoOrder);
ESM::DialInfo newInfo;
newInfo.blank();
newInfo.mId = ESM::RefId::stringRefId("newInfo");
newInfo.mPrev = ESM::RefId::stringRefId("invalid");
saveAndLoadDialogueWithInfos(data.mDialogue, std::array{ newInfo }, base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.getSize(), 4);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info1")), 1);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info2")), 2);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#newInfo")), 3);
}
TEST(CSMWorldInfoCollectionTest, sortShouldMoveBackwardUpdatedRecordBasedOnPrev)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
const DialogueData<ESM::DialInfo> data = generateDialogueWithInfos(3);
saveAndLoadDialogueWithInfos(data, base, collection, infoOrder);
ESM::DialInfo updatedInfo = data.mInfos[2];
updatedInfo.mPrev = data.mInfos[0].mId;
updatedInfo.mNext = ESM::RefId::stringRefId("invalid");
saveAndLoadDialogueWithInfos(data.mDialogue, std::array{ updatedInfo }, base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info1")), 2);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info2")), 1);
}
TEST(CSMWorldInfoCollectionTest, sortShouldMoveForwardUpdatedRecordBasedOnPrev)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
const DialogueData<ESM::DialInfo> data = generateDialogueWithInfos(3);
saveAndLoadDialogueWithInfos(data, base, collection, infoOrder);
ESM::DialInfo updatedInfo = data.mInfos[0];
updatedInfo.mPrev = data.mInfos[1].mId;
updatedInfo.mNext = ESM::RefId::stringRefId("invalid");
saveAndLoadDialogueWithInfos(data.mDialogue, std::array{ updatedInfo }, base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 1);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info1")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info2")), 2);
}
TEST(CSMWorldInfoCollectionTest, sortShouldMoveToFrontUpdatedRecordWhenPrevIsEmpty)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
const DialogueData<ESM::DialInfo> data = generateDialogueWithInfos(3);
saveAndLoadDialogueWithInfos(data, base, collection, infoOrder);
ESM::DialInfo updatedInfo = data.mInfos[2];
updatedInfo.mPrev = ESM::RefId();
updatedInfo.mNext = ESM::RefId::stringRefId("invalid");
saveAndLoadDialogueWithInfos(data.mDialogue, std::array{ updatedInfo }, base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 1);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info1")), 2);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info2")), 0);
}
TEST(CSMWorldInfoCollectionTest, sortShouldMoveToBackUpdatedRecordWhenPrevIsNotFound)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
const DialogueData<ESM::DialInfo> data = generateDialogueWithInfos(3);
saveAndLoadDialogueWithInfos(data, base, collection, infoOrder);
ESM::DialInfo updatedInfo = data.mInfos[0];
updatedInfo.mPrev = ESM::RefId::stringRefId("invalid");
updatedInfo.mNext = ESM::RefId();
saveAndLoadDialogueWithInfos(data.mDialogue, std::array{ updatedInfo }, base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info0")), 2);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info1")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue#info2")), 1);
}
TEST(CSMWorldInfoCollectionTest, sortShouldProvideStableOrderByTopic)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "dialogue2"), base, collection, infoOrder);
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "dialogue0"), base, collection, infoOrder);
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "dialogue1"), base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue0#info0")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue0#info1")), 1);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue1#info0")), 2);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue1#info1")), 3);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue2#info0")), 4);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue2#info1")), 5);
}
TEST(CSMWorldInfoCollectionTest, getAppendIndexShouldReturnFirstIndexAfterInfoTopic)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "dialogue0"), base, collection, infoOrder);
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "dialogue1"), base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_EQ(collection.getAppendIndex(ESM::RefId::stringRefId("dialogue0#info2")), 2);
}
TEST(CSMWorldInfoCollectionTest, reorderRowsShouldFailWhenOutOfBounds)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "dialogue0"), base, collection, infoOrder);
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "dialogue1"), base, collection, infoOrder);
EXPECT_FALSE(collection.reorderRows(5, {}));
}
TEST(CSMWorldInfoCollectionTest, reorderRowsShouldFailWhenAppliedToDifferentTopics)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "dialogue0"), base, collection, infoOrder);
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "dialogue1"), base, collection, infoOrder);
EXPECT_FALSE(collection.reorderRows(0, { 0, 1, 2 }));
}
TEST(CSMWorldInfoCollectionTest, reorderRowsShouldSucceedWhenAppliedToOneTopic)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(3, "dialogue0"), base, collection, infoOrder);
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(3, "dialogue1"), base, collection, infoOrder);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue0#info0")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue0#info1")), 1);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue0#info2")), 2);
EXPECT_TRUE(collection.reorderRows(1, { 1, 0 }));
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue0#info0")), 0);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue0#info1")), 2);
EXPECT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue0#info2")), 1);
}
MATCHER_P(RecordPtrIdIs, v, "")
{
return v == arg->get().mId;
}
TEST(CSMWorldInfoCollectionTest, getInfosByTopicShouldReturnRecordsGroupedByTopic)
{
const bool base = true;
InfoOrderByTopic infoOrder;
InfoCollection collection;
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "d0"), base, collection, infoOrder);
saveAndLoadDialogueWithInfos(generateDialogueWithInfos(2, "d1"), base, collection, infoOrder);
collection.sort(infoOrder);
EXPECT_THAT(collection.getInfosByTopic(),
UnorderedElementsAre(Pair("d0", ElementsAre(RecordPtrIdIs("d0#info0"), RecordPtrIdIs("d0#info1"))),
Pair("d1", ElementsAre(RecordPtrIdIs("d1#info0"), RecordPtrIdIs("d1#info1")))));
}
}
}