diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index cf1be3a18a..0a487e1022 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -90,6 +90,7 @@ file(GLOB UNITTEST_SRC_FILES fx/technique.cpp esm3/readerscache.cpp + esm3/testsaveload.cpp nifosg/testnifloader.cpp ) diff --git a/apps/openmw_test_suite/esm3/testsaveload.cpp b/apps/openmw_test_suite/esm3/testsaveload.cpp new file mode 100644 index 0000000000..d14c108c00 --- /dev/null +++ b/apps/openmw_test_suite/esm3/testsaveload.cpp @@ -0,0 +1,133 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace ESM +{ + namespace + { + using namespace ::testing; + + constexpr std::array formats = { + CurrentSaveGameFormatVersion, + }; + + constexpr std::uint32_t fakeRecordId = fourCC("FAKE"); + + template + void save(const T& record, ESMWriter& writer) + { + record.save(writer); + } + + void save(const CellRef& record, ESMWriter& writer) + { + record.save(writer, true); + } + + template + std::unique_ptr makeEsmStream(const T& record, FormatVersion formatVersion) + { + ESMWriter writer; + auto stream = std::make_unique(); + writer.setFormatVersion(formatVersion); + writer.save(*stream); + writer.startRecord(fakeRecordId); + save(record, writer); + writer.endRecord(fakeRecordId); + return stream; + } + + template + void load(ESMReader& reader, T& record) + { + record.load(reader); + } + + void load(ESMReader& reader, CellRef& record) + { + bool deleted = false; + record.load(reader, deleted, true); + } + + template + void saveAndLoadRecord(const T& record, FormatVersion formatVersion, T& result) + { + ESMReader reader; + reader.open(makeEsmStream(record, formatVersion), "stream"); + ASSERT_TRUE(reader.hasMoreRecs()); + ASSERT_EQ(reader.getRecName().toInt(), fakeRecordId); + reader.getRecHeader(); + load(reader, result); + } + + struct Esm3SaveLoadRecordTest : public TestWithParam + { + std::minstd_rand mRandom; + std::uniform_int_distribution mRefIdDistribution{ 'a', 'z' }; + + RefId generateRandomRefId(std::size_t size = 33) + { + std::string value; + while (value.size() < size) + value.push_back(static_cast(mRefIdDistribution(mRandom))); + return RefId::stringRefId(value); + } + }; + + TEST_P(Esm3SaveLoadRecordTest, playerShouldNotChange) + { + std::minstd_rand random; + Player record{}; + record.mObject.blank(); + record.mBirthsign = generateRandomRefId(); + record.mObject.mRef.mRefID = generateRandomRefId(); + std::generate_n(std::inserter(record.mPreviousItems, record.mPreviousItems.end()), 2, + [&] { return std::make_pair(generateRandomRefId(), generateRandomRefId()); }); + Player result; + saveAndLoadRecord(record, GetParam(), result); + EXPECT_EQ(record.mBirthsign, result.mBirthsign); + EXPECT_EQ(record.mPreviousItems, result.mPreviousItems); + } + + TEST_P(Esm3SaveLoadRecordTest, cellRefShouldNotChange) + { + CellRef record; + record.blank(); + record.mRefID = generateRandomRefId(); + record.mOwner = generateRandomRefId(); + record.mSoul = generateRandomRefId(); + record.mFaction = generateRandomRefId(); + record.mKey = generateRandomRefId(); + CellRef result; + saveAndLoadRecord(record, GetParam(), result); + EXPECT_EQ(record.mRefID, result.mRefID); + EXPECT_EQ(record.mOwner, result.mOwner); + EXPECT_EQ(record.mSoul, result.mSoul); + EXPECT_EQ(record.mFaction, result.mFaction); + EXPECT_EQ(record.mKey, result.mKey); + } + + TEST_P(Esm3SaveLoadRecordTest, creatureStatsShouldNotChange) + { + CreatureStats record; + record.blank(); + record.mLastHitAttemptObject = generateRandomRefId(); + record.mLastHitObject = generateRandomRefId(); + CreatureStats result; + saveAndLoadRecord(record, GetParam(), result); + EXPECT_EQ(record.mLastHitAttemptObject, result.mLastHitAttemptObject); + EXPECT_EQ(record.mLastHitObject, result.mLastHitObject); + } + + INSTANTIATE_TEST_SUITE_P(FormatVersions, Esm3SaveLoadRecordTest, ValuesIn(formats)); + } +}