501 lines
13 KiB
C++
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();
|
||
|
|
}
|