Skip to content
View source

Replay tests

An application’s tests are .replay files next to its scene, one step a line, # a comment, paths relative to the replay file. A replay is a test; the framework tests itself (docs/DECISIONS.md).

终端窗口
entelechy replay examples/life/tests/blinker.replay
entelechy replay tests/*.replay --watch
entelechy player --replay examples/voxel/tests/voxel.replay

examples/life/tests/blinker.replay (excerpt):

scene blinker.esn
# The seed holds a horizontal blinker at (10..12, 10). The first frame steps it
# once, so it stands vertical; the second frame lays it flat again.
frame 0.0
expect 11 9 = 1.0 0.85 0.45 0.06
expect 10 10 = 0.07 0.09 0.14
frame 0.1
expect 10 10 = 1.0 0.85 0.45 0.06
# Painting with the pointer makes cells live at the pointer.
pointer 20 15 down
frame 0.2
expect 20 15 = 1.0 0.85 0.45 0.06
pointer up
# Holding C clears everything.
key 67 down
frame 0.3
expect 20 15 = 0.07 0.09 0.14
key 67 up
# Reset restarts from the seed.
reset
frame 0.4
expect 11 9 = 1.0 0.85 0.45 0.06
$ entelechy replay examples/life/tests/blinker.replay
examples/life/tests/blinker.replay: ok (6 frames, 15 checks)

The first scene’s replay:

scene ../scene.esn
frame 0.0
expect count rings.result = 65536
expect 128 128 = 0.0 0.0 0.4
tests/rings.replay: ok (1 frames, 2 checks)
Form What it does Example
scene <path> the scene to test, relative to the replay file; required scene ../scene.esn
library <path> Kernels beyond the scene’s use library ../kernels/
allow file.write <dir> | allow file.delete <dir> lets outputs write (or remove and rename) files under the directory; what they write is kept for the run and never reaches the disk, so every run starts from the same files allow file.write ../laws/
alike its checks hold two forms of one computation equal word for word, true only where every Kernel rounds alike, so --round-apart leaves it out alike
frame <seconds> [to <seconds> every <seconds>] evaluates a frame at this session time; with to and every, one at each step, both ends included frame 0.0, frame 0.1 to 2.0 every 0.1
settle waits for every async run to land settle
pointer [<x> <y>] [down | up] moves the pointer, in the output’s pixels from its top left (what pointer() reads), and presses or releases it pointer 20 20 down, pointer up
button <name> down | up left, right, middle, or a pad’s south, east, west, north, l1, r1, l3, r3, start, select button right down
stick left | right | triggers <x> <y> a pad’s axis pair stick left 0.5 -1
pads <count> pads connected from here on, what pads() reads pads 1
wheel <x> <y> the scroll for the next frame only wheel 0 -1
window <w> <h> [<scale>] the window in pixels (window()) and its scale, one when not given window 240 135
type "<text>" characters typed, what typed() reads next frame (32 a frame, the rest after); \", \\, \n, \t inside the quotes type "你好, Quill"
clipboard "<text>" the clipboard holding this text from here on, what an input = clipboard reads clipboard "from elsewhere"
key <code> down | up a key by the browser’s code, as key() reads it: letters and digits uppercase ASCII, space 32, enter 13, arrows 37–40, the rest in help key key 67 down
reset [<node.port>] forgets every node’s committed state, or one port’s reset, reset step.result
param <name> <literal> sets a graph parameter param exposure 1.5
expect <x> <y> = <r> <g> <b> [<tolerance>] the picture’s color at x y from its top left, channels 0 to 1; tolerance 0.01 by default expect 11 10 = 1.0 1.0 1.0
expect <node.port[i][.field]> = <value>... [<tolerance>] an element of a node’s output or a field of it: ints and bools exactly, floats and vectors within the tolerance; i counts along the Field, x fastest, then y, then z expect step.result[0].score = 1
expect <node.port[i][.field]> < | <= | > | >= <number>... every component past a bound expect fall.result[0].speed > 2.5
expect count <node.port> = <n> how many elements a node’s output holds expect count ground.result = 6889
expect placed <node> main | ordered where a node runs: the main backend, or ordered work off it expect placed ring main
show <node.port[i][.field]> prints an element after the last frame; checks nothing show step.result[0]

The grammar is also in Class reference · Replay syntax.

  • Replays run together share their backends, documents and compiled Kernels, faster than one by one.
  • --watch plays again only the replays a change reaches, each computing only what changed.
  • --round-apart runs each replay three ways, some Kernels rounding as another device might: a failure is a value two Kernels each compute and must hold equal — compute it in one.
  • --out records a replay’s frames as render does.
  • The Player’s --replay <file> plays one in the window.

The protocol’s compose writes a .replay (whole, by text, once the scene and Kernels it names exist) — an agent changing a world can write the test proving its change, then run it with entelechy replay.