Extra2D/Tests/test_asset_service.cpp

501 lines
13 KiB
C++

/**
* @file test_asset_service.cpp
* @brief AssetService 单元测试
*
* 测试资源服务的加载、缓存、异步加载等功能。
*/
#include "test_framework.h"
#include <extra2d/services/asset_service.h>
#include <extra2d/asset/asset_loader.h>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <thread>
using namespace extra2d;
using namespace extra2d::test;
// ---------------------------------------------------------------------------
// 测试辅助函数
// ---------------------------------------------------------------------------
namespace {
std::string testDir = "test_temp_service";
void setupTestDir() {
std::filesystem::create_directories(testDir);
}
void cleanupTestDir() {
std::filesystem::remove_all(testDir);
}
std::string getTestPath(const std::string& name) {
return testDir + "/" + name;
}
void writeTestFile(const std::string& path, const std::vector<u8>& data) {
std::ofstream file(path, std::ios::binary);
file.write(reinterpret_cast<const char*>(data.data()), data.size());
}
}
// ---------------------------------------------------------------------------
// 测试用资源类和加载器
// ---------------------------------------------------------------------------
/**
* @brief 测试用资源类
*/
class TestResource : public Asset {
public:
AssetType type() const override { return AssetType::Data; }
bool loaded() const override {
return state_.load(std::memory_order_acquire) == AssetState::Loaded;
}
size_t memSize() const override { return data_.size(); }
const std::vector<u8>& data() const { return data_; }
void setData(const std::vector<u8>& data) {
data_ = data;
setState(AssetState::Loaded);
}
void initId(const std::string& path) {
setId(AssetID(path));
setPath(path);
}
private:
std::vector<u8> data_;
};
/**
* @brief 测试用资源加载器
*/
class TestResourceLoader : public AssetLoader<TestResource> {
public:
Ref<TestResource> load(const std::string& path) override {
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
return nullptr;
}
size_t size = static_cast<size_t>(file.tellg());
file.seekg(0);
std::vector<u8> data(size);
file.read(reinterpret_cast<char*>(data.data()), size);
auto resource = std::make_shared<TestResource>();
resource->initId(path);
resource->setData(data);
return resource;
}
Ref<TestResource> loadFromMemory(const u8* data, size_t size) override {
auto resource = std::make_shared<TestResource>();
resource->setData(std::vector<u8>(data, data + size));
return resource;
}
bool canLoad(const std::string& path) const override {
return path.find(".test") != std::string::npos;
}
AssetType type() const override { return AssetType::Data; }
std::vector<std::string> extensions() const override {
return {".test"};
}
};
// ---------------------------------------------------------------------------
// AssetService 基本测试
// ---------------------------------------------------------------------------
TEST(AssetService, Create) {
AssetService service;
TEST_ASSERT_FALSE(service.isLoaded("nonexistent"));
TEST_ASSERT_FALSE(service.isLoading("nonexistent"));
TEST_ASSERT_EQ(0u, service.size());
}
TEST(AssetService, InitShutdown) {
AssetService service;
bool initResult = service.init();
TEST_ASSERT_TRUE(initResult);
service.shutdown();
}
TEST(AssetService, SetRoot) {
AssetService service;
service.init();
service.setRoot("assets");
TEST_ASSERT_EQ("assets", service.root());
service.shutdown();
}
TEST(AssetService, SetLimit) {
AssetService service;
service.init();
service.setLimit(1024 * 1024);
service.shutdown();
}
// ---------------------------------------------------------------------------
// 加载器注册测试
// ---------------------------------------------------------------------------
TEST(AssetService, RegisterLoader) {
AssetService service;
service.init();
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
service.shutdown();
}
// ---------------------------------------------------------------------------
// 资源加载测试
// ---------------------------------------------------------------------------
TEST(AssetService, LoadNonExistent) {
setupTestDir();
AssetService service;
service.init();
service.setRoot(testDir);
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
auto handle = service.load<TestResource>("nonexistent.test");
TEST_ASSERT_FALSE(handle.valid());
service.shutdown();
cleanupTestDir();
}
TEST(AssetService, LoadFromMemory) {
AssetService service;
service.init();
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
std::vector<u8> testData = createTestData(256);
service.shutdown();
}
TEST(AssetService, IsLoaded) {
setupTestDir();
std::string filePath = getTestPath("resource.test");
writeTestFile(filePath, createTestData(256));
AssetService service;
service.init();
service.setRoot(testDir);
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
TEST_ASSERT_FALSE(service.isLoaded("resource.test"));
auto handle = service.load<TestResource>("resource.test");
TEST_ASSERT_TRUE(service.isLoaded("resource.test"));
service.shutdown();
cleanupTestDir();
}
// ---------------------------------------------------------------------------
// 缓存测试
// ---------------------------------------------------------------------------
TEST(AssetService, CacheHit) {
setupTestDir();
std::string filePath = getTestPath("cached.test");
writeTestFile(filePath, createTestData(256));
AssetService service;
service.init();
service.setRoot(testDir);
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
auto handle1 = service.load<TestResource>("cached.test");
auto handle2 = service.get<TestResource>("cached.test");
TEST_ASSERT_TRUE(handle1.valid());
TEST_ASSERT_TRUE(handle2.valid());
TEST_ASSERT_EQ(handle1.get().get(), handle2.get().get());
auto stats = service.stats();
TEST_ASSERT_GE(stats.hits, 1u);
service.shutdown();
cleanupTestDir();
}
TEST(AssetService, CacheMiss) {
AssetService service;
service.init();
auto handle = service.get<TestResource>("not_in_cache.test");
TEST_ASSERT_FALSE(handle.valid());
auto stats = service.stats();
TEST_ASSERT_GE(stats.misses, 1u);
service.shutdown();
}
TEST(AssetService, Purge) {
setupTestDir();
std::string filePath = getTestPath("purge.test");
writeTestFile(filePath, createTestData(256));
AssetService service;
service.init();
service.setRoot(testDir);
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
{
auto handle = service.load<TestResource>("purge.test");
}
service.purge();
TEST_ASSERT_EQ(0u, service.size());
service.shutdown();
cleanupTestDir();
}
TEST(AssetService, Clear) {
setupTestDir();
std::string filePath = getTestPath("clear.test");
writeTestFile(filePath, createTestData(256));
AssetService service;
service.init();
service.setRoot(testDir);
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
auto handle = service.load<TestResource>("clear.test");
TEST_ASSERT_GT(service.size(), 0u);
service.clear();
TEST_ASSERT_EQ(0u, service.size());
service.shutdown();
cleanupTestDir();
}
// ---------------------------------------------------------------------------
// 卸载测试
// ---------------------------------------------------------------------------
TEST(AssetService, Unload) {
setupTestDir();
std::string filePath = getTestPath("unload.test");
writeTestFile(filePath, createTestData(256));
AssetService service;
service.init();
service.setRoot(testDir);
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
auto handle = service.load<TestResource>("unload.test");
TEST_ASSERT_TRUE(service.isLoaded("unload.test"));
service.unload("unload.test");
TEST_ASSERT_FALSE(service.isLoaded("unload.test"));
service.shutdown();
cleanupTestDir();
}
// ---------------------------------------------------------------------------
// 资源包挂载测试
// ---------------------------------------------------------------------------
TEST(AssetService, MountPack) {
setupTestDir();
AssetPackBuilder builder;
builder.add("packed.test", createTestData(256));
builder.build(getTestPath("service.pack"));
AssetService service;
service.init();
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
bool mounted = service.mount(getTestPath("service.pack"));
TEST_ASSERT_TRUE(mounted);
service.shutdown();
cleanupTestDir();
}
TEST(AssetService, UnmountPack) {
setupTestDir();
AssetPackBuilder builder;
builder.add("packed.test", createTestData(256));
builder.build(getTestPath("unmount.pack"));
AssetService service;
service.init();
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
service.mount(getTestPath("unmount.pack"));
service.unmount(getTestPath("unmount.pack"));
service.shutdown();
cleanupTestDir();
}
// ---------------------------------------------------------------------------
// 统计测试
// ---------------------------------------------------------------------------
TEST(AssetService, Stats) {
AssetService service;
service.init();
auto stats = service.stats();
TEST_ASSERT_EQ(0u, stats.hits);
TEST_ASSERT_EQ(0u, stats.misses);
TEST_ASSERT_EQ(0u, stats.count);
service.shutdown();
}
// ---------------------------------------------------------------------------
// 数据处理管道测试
// ---------------------------------------------------------------------------
TEST(AssetService, SetPipe) {
AssetService service;
service.init();
DataPipe pipe;
pipe.encrypt("key", Decryptor::Type::XOR);
service.setPipe(std::move(pipe));
service.shutdown();
}
// ---------------------------------------------------------------------------
// 异步加载测试
// ---------------------------------------------------------------------------
TEST(AssetService, AsyncLoad) {
setupTestDir();
std::string filePath = getTestPath("async.test");
writeTestFile(filePath, createTestData(512));
AssetService service;
service.init();
service.setRoot(testDir);
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
std::atomic<bool> callbackCalled{false};
AssetHandle<TestResource> resultHandle;
service.loadAsync<TestResource>("async.test",
[&](AssetHandle<TestResource> handle) {
resultHandle = handle;
callbackCalled = true;
});
for (int i = 0; i < 100 && !callbackCalled; ++i) {
service.process();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
service.process();
TEST_ASSERT_TRUE(callbackCalled);
TEST_ASSERT_TRUE(resultHandle.valid());
service.shutdown();
cleanupTestDir();
}
TEST(AssetService, Preload) {
setupTestDir();
std::string filePath = getTestPath("preload.test");
writeTestFile(filePath, createTestData(256));
AssetService service;
service.init();
service.setRoot(testDir);
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
service.preload<TestResource>("preload.test");
for (int i = 0; i < 50; ++i) {
service.process();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
service.shutdown();
cleanupTestDir();
}
// ---------------------------------------------------------------------------
// 多资源测试
// ---------------------------------------------------------------------------
TEST(AssetService, LoadMultiple) {
setupTestDir();
for (int i = 0; i < 5; ++i) {
std::string filePath = getTestPath("multi" + std::to_string(i) + ".test");
writeTestFile(filePath, createTestData(100 * (i + 1)));
}
AssetService service;
service.init();
service.setRoot(testDir);
service.registerLoader<TestResource>(std::make_unique<TestResourceLoader>());
for (int i = 0; i < 5; ++i) {
auto handle = service.load<TestResource>("multi" + std::to_string(i) + ".test");
TEST_ASSERT_TRUE(handle.valid());
}
TEST_ASSERT_GT(service.size(), 0u);
service.shutdown();
cleanupTestDir();
}