Files
EVEngine/test/editor_extension_v2.cpp

127 lines
5.3 KiB
C++

#include "zeroerr/assert.h"
#include "zeroerr/unittest.h"
#include "editor/EditorExtension.h"
#include <stdexcept>
#include <string>
using namespace eve::editor;
namespace {
class ParkBuilderExtension final : public IGameEditorExtension {
public:
std::string ownerModule() const override { return "park"; }
void registerEditor(IEditorExtensionRegistry& registry) override {
CommandDescriptor append;
append.id = CommandId("park.track.append-segment");
append.ownerModule = "spoofed-owner";
append.displayName = "Append Track Segment";
append.category = "park.track";
append.requiredFeatures = HostFeature::RuntimeWorld;
CHECK(registry.registerCommand(
append,
[&](const CommandContext&, const EditorValue& payload) {
++calls;
return eve::editing::applied<EditorValue>(payload);
},
ExtensionAudience::Developer | ExtensionAudience::Player | ExtensionAudience::Automation)
.ok());
ExtensionToolDescriptor tool;
tool.id = ToolId("park.track.build");
tool.labelKey = "editor.park.track.build";
tool.category = "park.track";
tool.targetRequirements = {CapabilityId("park.target.track-network")};
tool.audiences = ExtensionAudience::Developer | ExtensionAudience::Player;
CHECK(registry.registerTool(std::move(tool)).ok());
ExtensionPaletteDescriptor developer;
developer.id = StableId("park.level-design-palette");
developer.titleKey = "editor.palette.level.rides";
developer.tools = {ToolId("park.track.build")};
developer.commands = {CommandId("park.track.append-segment")};
developer.audiences = ExtensionAudience::Developer;
CHECK(registry.registerPalette(std::move(developer)).ok());
ExtensionPaletteDescriptor player;
player.id = StableId("park.build-rides-palette");
player.titleKey = "game.build.rides";
player.tools = {ToolId("park.track.build")};
player.commands = {CommandId("park.track.append-segment")};
player.audiences = ExtensionAudience::Player;
CHECK(registry.registerPalette(std::move(player)).ok());
ExtensionRuleDescriptor budget;
budget.id = RuleId("park.policy.build-cost");
budget.audiences = ExtensionAudience::Player | ExtensionAudience::Automation;
CHECK(registry.registerRule(std::move(budget)).ok());
}
int calls = 0;
};
class ThrowingExtension final : public IGameEditorExtension {
public:
std::string ownerModule() const override { return "throwing"; }
void registerEditor(IEditorExtensionRegistry& registry) override {
CommandDescriptor command;
command.id = CommandId("throwing.partial");
CHECK(registry.registerCommand(
command,
[](const CommandContext&, const EditorValue&) { return eve::editing::applied<EditorValue>(EditorValue{}); },
ExtensionAudience::Developer)
.ok());
throw std::runtime_error("extension load failed");
}
};
} // namespace
TEST_CASE("editor.v2.game_extension_injects_same_tool_into_editor_and_game") {
EditorCommandService commands;
EditorExtensionRegistry extensions(&commands);
ParkBuilderExtension park;
CHECK(extensions.load(park).ok());
HostProfile developer = HostProfile::developer();
HostProfile runtime = HostProfile::runtimeBuilder();
extensions.configureProfile(runtime);
CHECK(runtime.allowsCommand(CommandId("park.track.append-segment")));
auto developerTools = extensions.tools(developer);
auto runtimeTools = extensions.tools(runtime);
CHECK_EQ(developerTools.size(), static_cast<std::size_t>(1));
CHECK_EQ(runtimeTools.size(), static_cast<std::size_t>(1));
CHECK(developerTools.front().id == runtimeTools.front().id);
CHECK_EQ(extensions.palettes(developer).front().id.value(), std::string("park.level-design-palette"));
CHECK_EQ(extensions.palettes(runtime).front().id.value(), std::string("park.build-rides-palette"));
CHECK_EQ(extensions.rules(developer).size(), static_cast<std::size_t>(0));
CHECK_EQ(extensions.rules(runtime).size(), static_cast<std::size_t>(1));
const CommandDescriptor* registered = commands.find(CommandId("park.track.append-segment"));
CHECK(registered != nullptr);
CHECK_EQ(registered->ownerModule, std::string("park"));
CommandContext context;
context.profile = &runtime;
auto result = commands.execute(CommandId("park.track.append-segment"), context, EditorValue("segment"));
CHECK(result.ok());
CHECK_EQ(park.calls, 1);
CHECK(extensions.unload("park") > 0);
CHECK(commands.find(CommandId("park.track.append-segment")) == nullptr);
CHECK(extensions.tools(developer).empty());
}
TEST_CASE("editor.v2.extension_load_exception_rolls_back_partial_registration") {
EditorCommandService commands;
EditorExtensionRegistry extensions(&commands);
ThrowingExtension throwing;
auto result = extensions.load(throwing);
CHECK_EQ(static_cast<int>(result.code()), static_cast<int>(EditorStatus::Rejected));
CHECK(commands.find(CommandId("throwing.partial")) == nullptr);
}