Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
26 kB · 637 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638#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"))))); } }}