Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 57s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 45s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 30s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 47s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m10s
Bigfoot / Sonarqube (push) Skipped
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 54s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m19s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m1s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 33s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 27s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 52s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 44s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 52s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 43s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 57s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 48s
Bigfoot / Clang Format Checks (push) Successful in 11s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 41s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m16s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 44s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 27s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m8s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 54s
405 lines
14 KiB
C++
405 lines
14 KiB
C++
/*********************************************************************
|
|
* \file SerializationManager.cpp
|
|
*
|
|
* \author Romain BOULLARD
|
|
* \date August 2026
|
|
*********************************************************************/
|
|
#include <Engine/SerializationManager/SerializationManager.hpp>
|
|
|
|
#include <Engine/Asset/AssetLibrary.hpp>
|
|
#include <Engine/DataAccessor/DataAccessor.hpp>
|
|
|
|
#if defined BIGFOOT_TOOLMODE
|
|
#include <Engine/BigFile/BigFile.hpp>
|
|
|
|
#include <EngineTests/BigFileInfo_generated.hpp>
|
|
#elif defined BIGFOOT_GAME
|
|
#include <Engine/Sasquatch/Sasquatch.hpp>
|
|
|
|
#include <EngineTests/SasquatchInfo_generated.hpp>
|
|
#endif
|
|
|
|
#include <System/File/File.hpp>
|
|
|
|
#include <AssetsForTesting/Asset/AssetA.hpp>
|
|
#include <AssetsForTesting/Asset/AssetB.hpp>
|
|
#include <AssetsForTesting/Asset/AssetC.hpp>
|
|
|
|
#include <EASTL/array.h>
|
|
#include <EASTL/optional.h>
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
namespace Bigfoot
|
|
{
|
|
class SerializationManagerFixture: public ::testing::Test
|
|
{
|
|
protected:
|
|
void SetUp() override
|
|
{
|
|
m_previousDataAccessor = g_dataAccessor;
|
|
|
|
SetActiveAssetLibrary(&m_library);
|
|
std::ignore = m_library.Insert(&m_containerA);
|
|
std::ignore = m_library.Insert(&m_containerB);
|
|
std::ignore = m_library.Insert(&m_containerC);
|
|
|
|
// leaf
|
|
AssetA leaf;
|
|
leaf.SetName("leaf");
|
|
m_leafUUID = leaf.GetHeader().uuid;
|
|
|
|
// middle --> leaf
|
|
AssetB middle;
|
|
middle.SetName("middle");
|
|
middle.GetAsset().ref_a = HardReference<AssetA> {m_leafUUID};
|
|
m_middleUUID = middle.GetHeader().uuid;
|
|
|
|
// root
|
|
// / \
|
|
// v v
|
|
// middle --> leaf
|
|
AssetC root;
|
|
root.GetAsset().asset_b_ref = HardReference<AssetB> {m_middleUUID};
|
|
root.GetAsset().inner_table->asset_a_ref = HardReference<AssetA> {m_leafUUID};
|
|
m_rootUUID = root.GetHeader().uuid;
|
|
|
|
// cycleX <--> cycleY
|
|
AssetB cycleX;
|
|
AssetB cycleY;
|
|
cycleX.GetAsset().ref_a = HardReference<AssetA> {cycleY.GetHeader().uuid};
|
|
cycleY.GetAsset().ref_a = HardReference<AssetA> {cycleX.GetHeader().uuid};
|
|
m_cycleXUUID = cycleX.GetHeader().uuid;
|
|
m_cycleYUUID = cycleY.GetHeader().uuid;
|
|
|
|
// sharedChild
|
|
AssetA sharedChild;
|
|
sharedChild.SetName("shared");
|
|
m_sharedChildUUID = sharedChild.GetHeader().uuid;
|
|
|
|
// multiRootA multiRootB
|
|
// \ /
|
|
// v v
|
|
// sharedChild
|
|
AssetB multiRootA;
|
|
multiRootA.SetName("rootA");
|
|
multiRootA.GetAsset().ref_a = HardReference<AssetA> {m_sharedChildUUID};
|
|
m_multiRootAUUID = multiRootA.GetHeader().uuid;
|
|
|
|
AssetB multiRootB;
|
|
multiRootB.SetName("rootB");
|
|
multiRootB.GetAsset().ref_a = HardReference<AssetA> {m_sharedChildUUID};
|
|
m_multiRootBUUID = multiRootB.GetHeader().uuid;
|
|
|
|
Populate({&leaf, &middle, &root, &cycleX, &cycleY, &sharedChild, &multiRootA, &multiRootB});
|
|
|
|
m_leafFlatbuffer = leaf.Pack();
|
|
m_middleFlatbuffer = middle.Pack();
|
|
m_rootFlatbuffer = root.Pack();
|
|
m_sharedChildFlatbuffer = sharedChild.Pack();
|
|
m_multiRootAFlatbuffer = multiRootA.Pack();
|
|
m_multiRootBFlatbuffer = multiRootB.Pack();
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
SetDataAccessor(m_previousDataAccessor);
|
|
SetActiveAssetLibrary(&g_assetLibrary);
|
|
}
|
|
|
|
/*
|
|
* Packs and writes p_assets to the current data accessors (bigfile for tool/sasquatch for game)
|
|
*
|
|
* \param p_assets The assets to write, in insertion order
|
|
*/
|
|
void Populate(const eastl::vector<AssetBase*>& p_assets)
|
|
{
|
|
#if defined BIGFOOT_TOOLMODE
|
|
m_bigFile.emplace(File {BIGFILE_ENGINETESTS_LOCATION});
|
|
m_bigFile->Clear();
|
|
|
|
for (AssetBase* const asset: p_assets)
|
|
{
|
|
const eastl::vector<std::byte> flatbuffer = asset->Pack();
|
|
m_bigFile->SaveAssetFlatbuffer(asset->GetHeader().uuid, flatbuffer, asset->GetHardReferences(flatbuffer));
|
|
}
|
|
|
|
SetDataAccessor(&*m_bigFile);
|
|
#elif defined BIGFOOT_GAME
|
|
struct SasquatchData
|
|
{
|
|
UUID m_uuid;
|
|
eastl::vector<std::byte> m_flatbuffer;
|
|
eastl::vector<UUID> m_hardReferences;
|
|
};
|
|
|
|
eastl::vector<SasquatchData> datas;
|
|
datas.reserve(p_assets.size());
|
|
|
|
std::size_t totalFlatbufferBytes = 0;
|
|
std::size_t totalHardReferencesBytes = 0;
|
|
for (AssetBase* const asset: p_assets)
|
|
{
|
|
eastl::vector<std::byte> flatbuffer = asset->Pack();
|
|
eastl::vector<UUID> hardReferences = asset->GetHardReferences(flatbuffer);
|
|
|
|
totalFlatbufferBytes += flatbuffer.size();
|
|
totalHardReferencesBytes += hardReferences.size() * UUID::UUID_BYTE_SIZE;
|
|
|
|
datas.push_back(SasquatchData {.m_uuid = asset->GetHeader().uuid,
|
|
.m_flatbuffer = std::move(flatbuffer),
|
|
.m_hardReferences = std::move(hardReferences)});
|
|
}
|
|
|
|
File {SASQUATCH_ENGINETESTS_LOCATION}.Delete();
|
|
{
|
|
SasquatchWriter writer {File {SASQUATCH_ENGINETESTS_LOCATION},
|
|
datas.size(),
|
|
totalFlatbufferBytes,
|
|
totalHardReferencesBytes};
|
|
for (const SasquatchData& data: datas)
|
|
{
|
|
writer.WriteAsset(data.m_uuid, data.m_flatbuffer, data.m_hardReferences);
|
|
}
|
|
}
|
|
|
|
m_sasquatch.emplace(File {SASQUATCH_ENGINETESTS_LOCATION});
|
|
SetDataAccessor(&*m_sasquatch);
|
|
#endif
|
|
}
|
|
|
|
DataAccessor* m_previousDataAccessor = nullptr;
|
|
|
|
AssetLibrary m_library;
|
|
AssetContainer<AssetA> m_containerA;
|
|
AssetContainer<AssetB> m_containerB;
|
|
AssetContainer<AssetC> m_containerC;
|
|
|
|
// An asset with no HardReferences of its own
|
|
UUID m_leafUUID;
|
|
eastl::vector<std::byte> m_leafFlatbuffer;
|
|
|
|
// HardReferences the leaf asset
|
|
UUID m_middleUUID;
|
|
eastl::vector<std::byte> m_middleFlatbuffer;
|
|
|
|
// HardReferences both the middle and leaf assets directly
|
|
UUID m_rootUUID;
|
|
eastl::vector<std::byte> m_rootFlatbuffer;
|
|
|
|
// A pair of assets HardReferencing each other
|
|
UUID m_cycleXUUID;
|
|
UUID m_cycleYUUID;
|
|
|
|
// HardReferenced by both m_multiRootAUUID and m_multiRootBUUID
|
|
UUID m_sharedChildUUID;
|
|
eastl::vector<std::byte> m_sharedChildFlatbuffer;
|
|
|
|
UUID m_multiRootAUUID;
|
|
eastl::vector<std::byte> m_multiRootAFlatbuffer;
|
|
UUID m_multiRootBUUID;
|
|
eastl::vector<std::byte> m_multiRootBFlatbuffer;
|
|
|
|
#if defined BIGFOOT_TOOLMODE
|
|
eastl::optional<BigFile> m_bigFile;
|
|
#elif defined BIGFOOT_GAME
|
|
eastl::optional<Sasquatch> m_sasquatch;
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************************/
|
|
|
|
TEST_F(SerializationManagerFixture, LoadAsset_ShouldLoadAnAssetWithNoHardReferences)
|
|
{
|
|
const HardReference<AssetA> leafRef {m_leafUUID};
|
|
ASSERT_EQ(leafRef.Get(), nullptr);
|
|
|
|
SerializationManager::LoadAsset(m_leafUUID);
|
|
|
|
ASSERT_NE(leafRef.Get(), nullptr);
|
|
EXPECT_EQ(leafRef.Get()->Pack(), m_leafFlatbuffer);
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
TEST_F(SerializationManagerFixture, LoadAsset_ShouldTransitivelyLoadEveryHardReferencedAsset)
|
|
{
|
|
const HardReference<AssetA> leafRef {m_leafUUID};
|
|
const HardReference<AssetB> middleRef {m_middleUUID};
|
|
const HardReference<AssetC> rootRef {m_rootUUID};
|
|
|
|
ASSERT_EQ(leafRef.Get(), nullptr);
|
|
ASSERT_EQ(middleRef.Get(), nullptr);
|
|
ASSERT_EQ(rootRef.Get(), nullptr);
|
|
|
|
SerializationManager::LoadAsset(m_rootUUID);
|
|
|
|
ASSERT_NE(leafRef.Get(), nullptr);
|
|
ASSERT_NE(middleRef.Get(), nullptr);
|
|
ASSERT_NE(rootRef.Get(), nullptr);
|
|
|
|
EXPECT_EQ(leafRef.Get()->Pack(), m_leafFlatbuffer);
|
|
EXPECT_EQ(middleRef.Get()->Pack(), m_middleFlatbuffer);
|
|
EXPECT_EQ(rootRef.Get()->Pack(), m_rootFlatbuffer);
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
TEST_F(SerializationManagerFixture, LoadAsset_ShouldSkipAssetsThatAreCaughtInACycle)
|
|
{
|
|
const HardReference<AssetB> xRef {m_cycleXUUID};
|
|
const HardReference<AssetB> yRef {m_cycleYUUID};
|
|
|
|
ASSERT_EQ(xRef.Get(), nullptr);
|
|
ASSERT_EQ(yRef.Get(), nullptr);
|
|
|
|
SerializationManager::LoadAsset(m_cycleXUUID);
|
|
|
|
EXPECT_EQ(xRef.Get(), nullptr);
|
|
EXPECT_EQ(yRef.Get(), nullptr);
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
TEST_F(SerializationManagerFixture, LoadAsset_ShouldNotReloadAnAssetThatIsAlreadyLoaded)
|
|
{
|
|
AssetA* alreadyLoaded = CreateAsset<AssetA>();
|
|
alreadyLoaded->SetName("in-memory");
|
|
const UUID uuid = alreadyLoaded->GetHeader().uuid;
|
|
|
|
const HardReference<AssetA> ref {uuid};
|
|
ASSERT_NE(ref.Get(), nullptr);
|
|
|
|
SerializationManager::LoadAsset(uuid);
|
|
|
|
ASSERT_NE(ref.Get(), nullptr);
|
|
EXPECT_STREQ(ref.Get()->GetHeader().name.c_str(), "in-memory");
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
TEST_F(SerializationManagerFixture, LoadAssets_ShouldLoadEveryRootAndMergeSharedHardReferences)
|
|
{
|
|
const HardReference<AssetA> sharedChildRef {m_sharedChildUUID};
|
|
const HardReference<AssetB> rootARef {m_multiRootAUUID};
|
|
const HardReference<AssetB> rootBRef {m_multiRootBUUID};
|
|
|
|
ASSERT_EQ(sharedChildRef.Get(), nullptr);
|
|
ASSERT_EQ(rootARef.Get(), nullptr);
|
|
ASSERT_EQ(rootBRef.Get(), nullptr);
|
|
|
|
const eastl::array<UUID, 2> roots {m_multiRootAUUID, m_multiRootBUUID};
|
|
SerializationManager::LoadAssets(roots);
|
|
|
|
ASSERT_NE(sharedChildRef.Get(), nullptr);
|
|
ASSERT_NE(rootARef.Get(), nullptr);
|
|
ASSERT_NE(rootBRef.Get(), nullptr);
|
|
|
|
EXPECT_EQ(sharedChildRef.Get()->Pack(), m_sharedChildFlatbuffer);
|
|
EXPECT_EQ(rootARef.Get()->Pack(), m_multiRootAFlatbuffer);
|
|
EXPECT_EQ(rootBRef.Get()->Pack(), m_multiRootBFlatbuffer);
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
#ifdef BIGFOOT_TOOLMODE
|
|
TEST_F(SerializationManagerFixture, SaveAsset_ShouldSaveTheAsset)
|
|
{
|
|
AssetB* asset = CreateAsset<AssetB>();
|
|
const UUID referencedUUID;
|
|
asset->GetAsset().ref_a = HardReference<AssetA> {referencedUUID};
|
|
|
|
const UUID uuid = asset->GetHeader().uuid;
|
|
|
|
ASSERT_FALSE(g_dataAccessor->Has(uuid));
|
|
|
|
EXPECT_TRUE(SerializationManager::SaveAsset(uuid));
|
|
|
|
ASSERT_TRUE(g_dataAccessor->Has(uuid));
|
|
EXPECT_THAT(g_dataAccessor->GetAssetHardReferences(uuid), ::testing::ElementsAre(referencedUUID));
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
TEST_F(SerializationManagerFixture, SaveAsset_ShouldFailAndNotSaveIfAnAssetHardReferencesItself)
|
|
{
|
|
AssetB* asset = CreateAsset<AssetB>();
|
|
const UUID uuid = asset->GetHeader().uuid;
|
|
asset->GetAsset().ref_a = HardReference<AssetA> {uuid};
|
|
|
|
ASSERT_FALSE(g_dataAccessor->Has(uuid));
|
|
|
|
EXPECT_FALSE(SerializationManager::SaveAsset(uuid));
|
|
|
|
EXPECT_FALSE(g_dataAccessor->Has(uuid));
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
TEST_F(SerializationManagerFixture, SaveAssets_ShouldFailAndNotSaveIfTwoAssetsInTheSameSaveHardReferenceEachOther)
|
|
{
|
|
AssetB* assetX = CreateAsset<AssetB>();
|
|
AssetB* assetY = CreateAsset<AssetB>();
|
|
|
|
const UUID uuidX = assetX->GetHeader().uuid;
|
|
const UUID uuidY = assetY->GetHeader().uuid;
|
|
|
|
assetX->GetAsset().ref_a = HardReference<AssetA> {uuidY};
|
|
assetY->GetAsset().ref_a = HardReference<AssetA> {uuidX};
|
|
|
|
ASSERT_FALSE(g_dataAccessor->Has(uuidX));
|
|
ASSERT_FALSE(g_dataAccessor->Has(uuidY));
|
|
|
|
const eastl::array<UUID, 2> uuids {uuidX, uuidY};
|
|
EXPECT_FALSE(SerializationManager::SaveAssets(uuids));
|
|
|
|
EXPECT_FALSE(g_dataAccessor->Has(uuidX));
|
|
EXPECT_FALSE(g_dataAccessor->Has(uuidY));
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
TEST_F(SerializationManagerFixture, SaveAssets_ShouldFailAndNotSaveIfAnAlreadySavedHardReferencePointsBackToIt)
|
|
{
|
|
AssetB* asset = CreateAsset<AssetB>();
|
|
const UUID uuid = asset->GetHeader().uuid;
|
|
const UUID referencedUUID;
|
|
asset->GetAsset().ref_a = HardReference<AssetA> {referencedUUID};
|
|
|
|
// referencedUUID is not part of this save; its own already-saved HardReferences (read back from the BigFile)
|
|
// point back to uuid, closing a cycle through previously persisted data. Its flatbuffer content is irrelevant.
|
|
const AssetA placeholder;
|
|
const eastl::array<UUID, 1> referencedHardReferences {uuid};
|
|
g_dataAccessor->SaveAssetFlatbuffer(referencedUUID, placeholder.Pack(), referencedHardReferences);
|
|
|
|
ASSERT_FALSE(g_dataAccessor->Has(uuid));
|
|
|
|
EXPECT_FALSE(SerializationManager::SaveAsset(uuid));
|
|
|
|
EXPECT_FALSE(g_dataAccessor->Has(uuid));
|
|
}
|
|
|
|
/****************************************************************************************/
|
|
|
|
TEST_F(SerializationManagerFixture, SaveAssets_ShouldSaveEveryAsset)
|
|
{
|
|
const AssetA* assetX = CreateAsset<AssetA>();
|
|
const AssetA* assetY = CreateAsset<AssetA>();
|
|
|
|
const UUID uuidX = assetX->GetHeader().uuid;
|
|
const UUID uuidY = assetY->GetHeader().uuid;
|
|
|
|
ASSERT_FALSE(g_dataAccessor->Has(uuidX));
|
|
ASSERT_FALSE(g_dataAccessor->Has(uuidY));
|
|
|
|
const eastl::array<UUID, 2> uuids {uuidX, uuidY};
|
|
EXPECT_TRUE(SerializationManager::SaveAssets(uuids));
|
|
|
|
ASSERT_TRUE(g_dataAccessor->Has(uuidX));
|
|
ASSERT_TRUE(g_dataAccessor->Has(uuidY));
|
|
EXPECT_EQ(g_dataAccessor->GetAssetFlatbuffer(uuidX), assetX->Pack());
|
|
EXPECT_EQ(g_dataAccessor->GetAssetFlatbuffer(uuidY), assetY->Pack());
|
|
}
|
|
#endif
|
|
} // namespace Bigfoot
|