#include "npc_ai/editing/BehaviorGraph.h" #include "zeroerr/assert.h" #include "zeroerr/unittest.h" using namespace eve::npc_ai_editing; using namespace eve::editing; namespace { GraphNodeRecord node(const char* id, const char* type, bool input, bool output) { GraphNodeRecord result{GraphNodeId(id), type, EditorValue::Object{}, {}}; if (input) result.pins.push_back({GraphPinId(std::string(id) + ".in"), result.id, "flow", GraphPinDirection::Input}); if (output) result.pins.push_back({GraphPinId(std::string(id) + ".out"), result.id, "flow", GraphPinDirection::Output}); return result; } } // namespace TEST_CASE("editor.behavior.graph_compiles_deterministic_tree") { GraphDocument graph; BehaviorGraphDomain domain; CHECK(graph.createNode(node("root", "root", false, true)).ok()); CHECK(graph.createNode(node("sequence", "sequence", true, true)).ok()); CHECK(graph.createNode(node("condition", "condition", true, false)).ok()); CHECK(graph.createNode(node("action", "action", true, false)).ok()); CHECK(graph.connect({StableId("e1"), GraphPinId("root.out"), GraphPinId("sequence.in")}, domain.canConnect(*graph.findPin(GraphPinId("root.out")), *graph.findPin(GraphPinId("sequence.in")))).ok()); CHECK(graph.connect({StableId("e2"), GraphPinId("sequence.out"), GraphPinId("condition.in")}, domain.canConnect(*graph.findPin(GraphPinId("sequence.out")), *graph.findPin(GraphPinId("condition.in")))).ok()); CHECK(graph.connect({StableId("e3"), GraphPinId("sequence.out"), GraphPinId("action.in")}, domain.canConnect(*graph.findPin(GraphPinId("sequence.out")), *graph.findPin(GraphPinId("action.in")))).ok()); auto compiled = domain.compile(graph.snapshot(domain.domain())); CHECK_EQ(static_cast(compiled.status), static_cast(EditorStatus::Applied)); CHECK_EQ(compiled.root.value(), std::string("root")); CHECK_EQ(compiled.instructions.size(), 4U); CHECK_EQ(compiled.instructions[1].children.size(), 2U); } TEST_CASE("editor.behavior.graph_rejects_cycles_or_unreachable_nodes") { BehaviorGraphDomain domain; GraphDocumentData graph; graph.domain = "behavior"; graph.revision = 7; graph.nodes = {node("root", "root", false, true), node("orphan", "action", true, false)}; auto compiled = domain.compile(graph); CHECK_EQ(static_cast(compiled.status), static_cast(EditorStatus::Failed)); CHECK(!compiled.diagnostics.empty()); auto wrongDirection = domain.canConnect({GraphPinId("a"), GraphNodeId("a"), "flow", GraphPinDirection::Input}, {GraphPinId("b"), GraphNodeId("b"), "flow", GraphPinDirection::Output}); CHECK(!wrongDirection.allowed); }