Files
EVEngine/test/ModelPrefetch.cpp
T

89 lines
3.4 KiB
C++

#include <future>
#include <thread>
#include <vector>
#include "common/AsyncWork.h"
#include "common/Capability.h"
#include "common/Resource.h"
#include "filesystem/Filesystem.h"
#include "model3d/Model3D.h"
#include "zeroerr/assert.h"
#include "zeroerr/unittest.h"
namespace {
struct ExecutorScope {
eve::caps::IAsyncWorkExecutor* previous = eve::cap::query<eve::caps::IAsyncWorkExecutor>();
explicit ExecutorScope(eve::caps::IAsyncWorkExecutor* current) {
if (previous) eve::cap::revoke(previous);
if (current) eve::cap::provide(current);
}
~ExecutorScope() { eve::cap::provide(previous); }
};
class Worker final : public eve::caps::IAsyncWorkExecutor {
public:
std::vector<std::future<void>> jobs;
std::thread::id workerId;
eve::Result<void> submit(std::function<void()> work) override {
jobs.push_back(std::async(std::launch::async, [this, work = std::move(work)] {
workerId = std::this_thread::get_id();
work();
}));
return eve::Result<void>::success();
}
void join() {
for (auto& job : jobs)
if (job.valid()) job.get();
}
~Worker() { join(); }
};
} // namespace
TEST_CASE("model3d.prefetch rejects missing executor and empty paths") {
ExecutorScope scope(nullptr);
auto* models = eve::model3d::Model3D::create();
auto empty = models->requestModelData("");
REQUIRE(!empty.ok());
auto unsupported = models->requestModelData("prefetch.obj");
REQUIRE(!unsupported.ok());
}
TEST_CASE("model3d.prefetch decodes on worker and joins the canonical cache") {
auto* fs = eve::filesystem::Filesystem::create();
REQUIRE(fs->setIdentity("eve_model_prefetch_contract", true));
REQUIRE(fs->setupWriteDirectory());
const std::string obj = "v 0 0 0\nv 1 0 0\nv 0 1 0\nf 1 2 3\n";
fs->write("prefetch.obj", obj.data(), obj.size());
Worker worker;
ExecutorScope scope(&worker);
auto* models = eve::model3d::Model3D::create();
REQUIRE(models->requestModelData("prefetch.obj").ok());
REQUIRE(models->requestModelData("prefetch.obj").ok());
auto* first = models->newModelDataFromFile("prefetch.obj");
REQUIRE(first != nullptr);
REQUIRE(first->getMeshCount() == 1);
REQUIRE(models->newModelDataFromFile("prefetch.obj") == first);
worker.join();
REQUIRE(worker.jobs.size() == 1);
REQUIRE(worker.workerId != std::this_thread::get_id());
eve::model3d::ModelLoadOptions options;
options.joinIdenticalVertices = false;
options.improveCacheLocality = false;
REQUIRE(models->requestModelData("prefetch.obj", options).ok());
REQUIRE(models->requestModelData("prefetch.obj", options).ok());
auto* explicitOptions = models->newModelDataFromFile("prefetch.obj", options);
REQUIRE(explicitOptions != first);
REQUIRE(explicitOptions->getMeshCount() == 1);
REQUIRE(models->newModelDataFromFile("prefetch.obj", options) == explicitOptions);
worker.join();
REQUIRE(worker.jobs.size() == 2);
REQUIRE(models->requestModelData("missing-prefetch-model.glb").ok());
bool reportedFailure = false;
try {
models->newModelDataFromFile("missing-prefetch-model.glb");
} catch (const std::exception&) {
reportedFailure = true;
}
worker.join();
REQUIRE(reportedFailure);
REQUIRE(eve::ResourceManager::getInstance().pendingCount() == 0);
}