import { describe, expect, it } from 'vitest'; import { AddPortCommand } from '../../src/core/commands/portCommands'; import { SetOverrideCommand } from '../../src/core/commands/sceneObjectCommands'; import { BEHAVIOR_END, BEHAVIOR_PARAM, BEHAVIOR_START, type Graph, GraphInterpreter, RUN_BEHAVIOR, type TraceListener, type Value, } from '../../src/core/graph'; import { graphLookup } from '../../src/core/model/graphLookup'; import { BehaviorSystem } from '../../src/runtime/systems/BehaviorSystem'; import { Input } from '../../src/runtime/systems/Input'; import { World } from '../../src/runtime/World'; import { fakeContext, graphEditor, makeBehavior, makeProject, makeSpriteScene, place } from '../helpers'; function recorder() { const seen = new Map>>(); const listener: TraceListener = { onEdge: () => {}, onNode: () => {}, onValues: (graph, id, values) => { const byNode = seen.get(graph) ?? new Map>(); seen.set(graph, byNode.set(id, values)); }, }; return { seen, listener }; } describe('live values', () => { it('record what each node last produced, a read node after its own write', () => { const fixture = makeProject(); const behavior = makeBehavior(fixture, 'B', ['count']); const edit = graphEditor(fixture, behavior); const start = edit.addNode('OnStart'); const count = edit.variables(['count'], { write: true }); const add = edit.addNode('Add', { b: 1 }); edit.wire([ [start, 'exec', count, 'exec'], [count, 'count', add, 'a'], [add, 'result', count, 'count'], ]); const { seen, listener } = recorder(); const interpreter = new GraphInterpreter(behavior.graph, graphLookup(fixture.project)); interpreter.trace = listener; const ctx = fakeContext({ variables: { count: 5, other: 9 } }); interpreter.fire('OnStart', ctx); ctx.variables.count = 99; const values = seen.get(behavior.graph)!; expect(values.get(add.id)).toEqual({ result: 6 }); expect(values.get(count.id)).toEqual({ count: 6 }); }); it('keep a nested behavior apart, under its own graph', () => { const fixture = makeProject(); const callee = makeBehavior(fixture, 'Double'); const inner = graphEditor(fixture, callee); const start = inner.addNode(BEHAVIOR_START); const end = inner.addNode(BEHAVIOR_END); fixture.stack.execute(new AddPortCommand(callee.id, start.id, { key: 'in1', kind: 'number' })); fixture.stack.execute(new AddPortCommand(callee.id, end.id, { key: 'out1', kind: 'number' })); const times = inner.addNode('Multiply', { b: 2 }); inner.wire([ [start, 'exec', end, 'exec'], [start, 'in1', times, 'a'], [times, 'result', end, 'out1'], ]); const caller = makeBehavior(fixture, 'Caller'); const outer = graphEditor(fixture, caller); const onStart = outer.addNode('OnStart'); const call = outer.addNode(RUN_BEHAVIOR, { [BEHAVIOR_PARAM]: callee.id, in1: 21 }); outer.connect(onStart, 'exec', call, 'exec'); const { seen, listener } = recorder(); const interpreter = new GraphInterpreter(caller.graph, graphLookup(fixture.project)); interpreter.trace = listener; interpreter.fire('OnStart', fakeContext()); expect(seen.get(callee.graph)?.get(start.id)).toEqual({ in1: 21 }); expect(seen.get(callee.graph)?.get(times.id)).toEqual({ result: 42 }); expect(seen.get(caller.graph)?.get(call.id)).toEqual({ out1: 42 }); expect(seen.get(caller.graph)?.has(times.id)).toBe(false); }); it('show the instance that produced last, not the last one in the list', () => { const f = makeSpriteScene(); const behavior = makeBehavior(f, 'Counter', ['count']); const edit = graphEditor(f, behavior); const update = edit.addNode('OnUpdate'); const count = edit.variables(['count'], { write: true }); const add = edit.addNode('Add', { b: 1 }); edit.wire([ [update, 'exec', count, 'exec'], [count, 'count', add, 'a'], [add, 'result', count, 'count'], ]); place(f, behavior.id, { x: 0, y: 0 }); const second = place(f, behavior.id, { x: 0, y: 0 }); f.stack.execute(new SetOverrideCommand(f.sceneId, second, 'count', 10)); const world = World.instantiate(f.project, f.sceneId); const system = new BehaviorSystem(f.project, new Input()); world.addSystem(system); world.tick(1 / 60); expect(system.traceFor(behavior.id).values.get(count.id)).toEqual({ count: 11 }); world.entities.find((e) => e.objectId === second)!.props.enabled = false; world.tick(1 / 60); expect(system.traceFor(behavior.id).values.get(count.id)).toEqual({ count: 2 }); }); });