#include "graphics/editing/OffscreenPreview.h" #include "graphics/Canvas.h" #include "graphics/ICanvasFactory.h" #include "graphics/ICanvasTarget.h" #include "image/ImageData.h" #include "zeroerr/assert.h" #include "zeroerr/unittest.h" #include #include using namespace eve::graphics_editing; using namespace eve::editing; namespace { class FakeCanvas final : public eve::graphics::Canvas { public: FakeCanvas(int width, int height) : width_(width), height_(height) {} int getWidth() const override { return width_; } int getHeight() const override { return height_; } eve::graphics::Texture* getTexture() override { return nullptr; } void clear(std::optional, std::optional, std::optional) override {} eve::graphics::Color getPixel(int, int) override { return {}; } eve::image::ImageData* newImageData() override { return new eve::image::ImageData(width_, height_); } void draw(eve::graphics::Graphics*, const glm::mat4&) const override {} void draw(eve::graphics::Canvas*, const glm::mat4&) const override {} private: int width_ = 0; int height_ = 0; }; class FakeCanvasFactory final : public eve::graphics::ICanvasFactory { public: eve::graphics::Canvas* newCanvas(int width, int height) override { canvas = std::make_unique(width, height); return canvas.get(); } std::unique_ptr canvas; }; class FakeCanvasTarget final : public eve::graphics::ICanvasTarget { public: void setCanvas(eve::graphics::Canvas* value) override { current = value; ++bindings; } eve::graphics::Canvas* getCanvas() const override { return current; } eve::graphics::Canvas* current = nullptr; int bindings = 0; }; } TEST_CASE("editor.preview.offscreen_artifacts_are_bounded_revision_safe_and_releasable") { FakeCanvasFactory factory; FakeCanvasTarget targets; auto* graphicsToken = reinterpret_cast(&factory); GraphicsOffscreenPreviewService service(graphicsToken, &factory, &targets); int draws = 0; auto rendered = service.render({StableId("material"), 5, 64, 32, {0.0, 0.0, 0.0, 1.0}}, [&](eve::graphics::Graphics* graphics, eve::graphics::Canvas* canvas) { REQUIRE(graphics); REQUIRE(canvas); ++draws; return eve::editing::applied(); }); REQUIRE(rendered.ok()); CHECK_EQ(draws, 1); CHECK_EQ(rendered.value().width, 64); CHECK_EQ(targets.bindings, 2); CHECK(targets.current == nullptr); auto pixels = service.image(rendered.value().handle, 5); REQUIRE(pixels.ok()); REQUIRE(pixels.value()); CHECK_EQ(pixels.value()->getWidth(), 64); CHECK_EQ(static_cast(service.image(rendered.value().handle, 6).code()), static_cast(EditorStatus::Conflict)); CHECK(service.release(rendered.value().handle).ok()); CHECK_EQ(static_cast(service.image(rendered.value().handle, 5).code()), static_cast(EditorStatus::NotFound)); CHECK_EQ(static_cast(service.render({StableId("huge"), 1, 5000, 1}, [](auto*, auto*) { return eve::editing::applied(); }).code()), static_cast(EditorStatus::Rejected)); CHECK_EQ(static_cast(service.render({StableId("throw"), 2, 32, 32}, [](auto*, auto*) -> EditorResult { throw std::runtime_error("draw failed"); }).code()), static_cast(EditorStatus::Failed)); CHECK(targets.current == nullptr); CHECK_EQ(targets.bindings, 4); }