Files
Bigfoot/Bigfoot/Tests/Engine/SerializationManager/SerializationManager.cpp
T
rboullard 8cb9c634ca
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
CreateAsset
2026-08-31 23:29:16 +02:00

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