148 lines
4.9 KiB
C++
148 lines
4.9 KiB
C++
|
|
#include <gtest/gtest.h>
|
||
|
|
|
||
|
|
#include <XCEngine/Core/Asset/ArtifactContainer.h>
|
||
|
|
|
||
|
|
#include <filesystem>
|
||
|
|
#include <fstream>
|
||
|
|
|
||
|
|
using namespace XCEngine::Resources;
|
||
|
|
namespace Containers = XCEngine::Containers;
|
||
|
|
namespace Core = XCEngine::Core;
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
void FillPayload(Containers::Array<Core::uint8>& payload,
|
||
|
|
std::initializer_list<Core::uint8> bytes) {
|
||
|
|
payload.Clear();
|
||
|
|
payload.Reserve(bytes.size());
|
||
|
|
for (const Core::uint8 value : bytes) {
|
||
|
|
payload.PushBack(value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
TEST(ArtifactContainer, WritesAndReadsMultipleEntries) {
|
||
|
|
namespace fs = std::filesystem;
|
||
|
|
|
||
|
|
const fs::path projectRoot = fs::temp_directory_path() / "xc_artifact_container_test";
|
||
|
|
fs::remove_all(projectRoot);
|
||
|
|
fs::create_directories(projectRoot);
|
||
|
|
|
||
|
|
ArtifactContainerWriter writer;
|
||
|
|
|
||
|
|
ArtifactContainerEntry mainEntry;
|
||
|
|
mainEntry.name = "main";
|
||
|
|
mainEntry.resourceType = ResourceType::Model;
|
||
|
|
mainEntry.localID = kMainAssetLocalID;
|
||
|
|
FillPayload(mainEntry.payload, { 1, 2, 3, 4 });
|
||
|
|
writer.AddEntry(mainEntry);
|
||
|
|
|
||
|
|
ArtifactContainerEntry meshEntry;
|
||
|
|
meshEntry.name = "mesh/0";
|
||
|
|
meshEntry.resourceType = ResourceType::Mesh;
|
||
|
|
meshEntry.localID = 42;
|
||
|
|
meshEntry.flags = 7;
|
||
|
|
FillPayload(meshEntry.payload, { 9, 8, 7 });
|
||
|
|
writer.AddEntry(std::move(meshEntry));
|
||
|
|
|
||
|
|
Containers::String errorMessage;
|
||
|
|
const fs::path artifactPath = projectRoot / "Library" / "Artifacts" / "ab" / "artifact.xca";
|
||
|
|
ASSERT_TRUE(writer.WriteToFile(artifactPath.string().c_str(), &errorMessage))
|
||
|
|
<< errorMessage.CStr();
|
||
|
|
ASSERT_TRUE(fs::exists(artifactPath));
|
||
|
|
|
||
|
|
ArtifactContainerReader reader;
|
||
|
|
ASSERT_TRUE(reader.Open(artifactPath.string().c_str(), &errorMessage))
|
||
|
|
<< errorMessage.CStr();
|
||
|
|
EXPECT_EQ(reader.GetEntryCount(), 2u);
|
||
|
|
|
||
|
|
const ArtifactContainerEntryView* mainView = reader.FindEntryByName("main");
|
||
|
|
ASSERT_NE(mainView, nullptr);
|
||
|
|
EXPECT_EQ(mainView->resourceType, ResourceType::Model);
|
||
|
|
EXPECT_EQ(mainView->localID, kMainAssetLocalID);
|
||
|
|
|
||
|
|
const ArtifactContainerEntryView* meshView = reader.FindEntry(ResourceType::Mesh, 42);
|
||
|
|
ASSERT_NE(meshView, nullptr);
|
||
|
|
EXPECT_EQ(meshView->name, Containers::String("mesh/0"));
|
||
|
|
EXPECT_EQ(meshView->flags, 7u);
|
||
|
|
|
||
|
|
Containers::Array<Core::uint8> payload;
|
||
|
|
ASSERT_TRUE(reader.ReadEntryPayload(*meshView, payload, &errorMessage))
|
||
|
|
<< errorMessage.CStr();
|
||
|
|
ASSERT_EQ(payload.Size(), 3u);
|
||
|
|
EXPECT_EQ(payload[0], 9u);
|
||
|
|
EXPECT_EQ(payload[1], 8u);
|
||
|
|
EXPECT_EQ(payload[2], 7u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(ArtifactContainer, RejectsCorruptFileHeader) {
|
||
|
|
namespace fs = std::filesystem;
|
||
|
|
|
||
|
|
const fs::path projectRoot = fs::temp_directory_path() / "xc_artifact_container_corrupt_test";
|
||
|
|
fs::remove_all(projectRoot);
|
||
|
|
fs::create_directories(projectRoot);
|
||
|
|
|
||
|
|
const fs::path artifactPath = projectRoot / "broken.xca";
|
||
|
|
{
|
||
|
|
std::ofstream output(artifactPath, std::ios::binary | std::ios::trunc);
|
||
|
|
output << "broken";
|
||
|
|
}
|
||
|
|
|
||
|
|
ArtifactContainerReader reader;
|
||
|
|
Containers::String errorMessage;
|
||
|
|
EXPECT_FALSE(reader.Open(artifactPath.string().c_str(), &errorMessage));
|
||
|
|
EXPECT_FALSE(errorMessage.Empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(ArtifactContainer, ResolvesEntryVirtualPaths) {
|
||
|
|
namespace fs = std::filesystem;
|
||
|
|
|
||
|
|
const fs::path projectRoot = fs::temp_directory_path() / "xc_artifact_container_entry_path_test";
|
||
|
|
fs::remove_all(projectRoot);
|
||
|
|
fs::create_directories(projectRoot);
|
||
|
|
|
||
|
|
ArtifactContainerWriter writer;
|
||
|
|
|
||
|
|
ArtifactContainerEntry mainEntry;
|
||
|
|
mainEntry.name = "main";
|
||
|
|
mainEntry.resourceType = ResourceType::Model;
|
||
|
|
mainEntry.localID = kMainAssetLocalID;
|
||
|
|
FillPayload(mainEntry.payload, { 1, 2, 3 });
|
||
|
|
writer.AddEntry(mainEntry);
|
||
|
|
|
||
|
|
ArtifactContainerEntry meshEntry;
|
||
|
|
meshEntry.name = "mesh_0.xcmesh";
|
||
|
|
meshEntry.resourceType = ResourceType::Mesh;
|
||
|
|
meshEntry.localID = 77;
|
||
|
|
FillPayload(meshEntry.payload, { 5, 6, 7, 8 });
|
||
|
|
writer.AddEntry(meshEntry);
|
||
|
|
|
||
|
|
const Containers::String containerPath =
|
||
|
|
(projectRoot / "Library" / "Artifacts" / "ab" / "artifact.xcmodel").string().c_str();
|
||
|
|
Containers::String errorMessage;
|
||
|
|
ASSERT_TRUE(writer.WriteToFile(containerPath, &errorMessage))
|
||
|
|
<< errorMessage.CStr();
|
||
|
|
|
||
|
|
const Containers::String virtualPath =
|
||
|
|
BuildArtifactContainerEntryPath(containerPath, "mesh_0.xcmesh");
|
||
|
|
Containers::String parsedContainerPath;
|
||
|
|
Containers::String parsedEntryName;
|
||
|
|
ASSERT_TRUE(TryParseArtifactContainerEntryPath(
|
||
|
|
virtualPath,
|
||
|
|
parsedContainerPath,
|
||
|
|
parsedEntryName));
|
||
|
|
EXPECT_EQ(parsedContainerPath, containerPath);
|
||
|
|
EXPECT_EQ(parsedEntryName, "mesh_0.xcmesh");
|
||
|
|
|
||
|
|
Containers::Array<Core::uint8> payload;
|
||
|
|
ASSERT_TRUE(ReadArtifactContainerPayloadByPath(
|
||
|
|
virtualPath,
|
||
|
|
ResourceType::Mesh,
|
||
|
|
payload,
|
||
|
|
&errorMessage)) << errorMessage.CStr();
|
||
|
|
ASSERT_EQ(payload.Size(), 4u);
|
||
|
|
EXPECT_EQ(payload[0], 5u);
|
||
|
|
EXPECT_EQ(payload[3], 8u);
|
||
|
|
}
|