aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-05-21 15:10:49 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-02 14:24:49 +0000
commit4b87fefc2e14acab7f0215599f0ac47d2ceb6339 (patch)
treefffdf262e6688b030334a7c24a93b028f2327cf9
parentbase.match: fix bug when repeating multi-element patterns (diff)
downloadalive-4b87fefc2e14acab7f0215599f0ac47d2ceb6339.tar.gz
alive-4b87fefc2e14acab7f0215599f0ac47d2ceb6339.zip
add PureOp spec
-rw-r--r--spec/pureop_spec.moon129
-rw-r--r--spec/rtnode_spec.moon3
-rw-r--r--spec/test_setup.moon12
3 files changed, 141 insertions, 3 deletions
diff --git a/spec/pureop_spec.moon b/spec/pureop_spec.moon
new file mode 100644
index 0000000..78dce4d
--- /dev/null
+++ b/spec/pureop_spec.moon
@@ -0,0 +1,129 @@
+import do_setup, do_teardown, invoke_op from require 'spec.test_setup'
+import PureOp, Input, T, val, evt from require 'alv.base'
+import RTNode from require 'alv'
+
+setup do_setup
+teardown do_teardown
+
+class TestPureOp extends PureOp
+ pattern: (val.num / val.str / evt.num)*3
+ type: T.num
+ tick: => @out\set 1
+
+literal = (result) ->
+ eval: ->
+ si = T.num\mk_sig!
+ RTNode :result, side_inputs: { [si]: Input.hot si }
+
+describe 'PureOp', ->
+ it 'matches the pattern', ->
+ assert.has.error -> invoke_op TestPureOp, {}
+ assert.has.error -> invoke_op TestPureOp, { literal T.bool\mk_evt! }
+ invoke_op TestPureOp, { literal T.num\mk_const 1 }
+ invoke_op TestPureOp, { literal T.str\mk_const 'hello' }
+ invoke_op TestPureOp, { literal T.num\mk_evt! }
+
+ describe 'with constant inputs', ->
+ local rtn
+ it 'ticks once', ->
+ tiq = spy.on TestPureOp.__base, 'tick'
+ rtn = invoke_op TestPureOp, { T.num\mk_const(1), T.str\mk_const('hello') }
+ assert.spy(tiq).was_called_with match.is_ref(rtn.op), true
+ assert.is.equal 1, rtn.result!
+
+ it 'is constant', ->
+ assert.is.equal '=', rtn\metatype!
+
+ describe 'with signal inputs', ->
+ a = T.num\mk_sig 1
+ b = T.num\mk_sig 2
+ c = T.num\mk_sig 3
+ tiq = spy.on TestPureOp.__base, 'tick'
+ rtn = invoke_op TestPureOp, { (literal a), (literal b), (literal c) }
+ op = rtn.op
+
+ it 'sets up hot inputs', ->
+ assert.is.equal 3, #op.inputs
+ assert.is.equal a, op.inputs[1].result
+ assert.is.equal b, op.inputs[2].result
+ assert.is.equal c, op.inputs[3].result
+ assert.is.equal 'hot', op.inputs[1].mode
+ assert.is.equal 'hot', op.inputs[2].mode
+ assert.is.equal 'hot', op.inputs[3].mode
+ assert.spy(tiq).was_called!
+
+ it 'has signal output', ->
+ assert.is.equal '~', op.out.metatype
+
+ describe 'with event inputs', ->
+ a = T.num\mk_sig 1
+ b = T.num\mk_evt 2
+ c = T.num\mk_sig 3
+ tiq = spy.on TestPureOp.__base, 'tick'
+ rtn = invoke_op TestPureOp, { (literal a), (literal b), (literal c) }
+ op = rtn.op
+
+ it 'sets up hot input only for evt', ->
+ assert.is.equal 3, #op.inputs
+ assert.is.equal a, op.inputs[1].result
+ assert.is.equal b, op.inputs[2].result
+ assert.is.equal c, op.inputs[3].result
+ assert.is.equal 'cold', op.inputs[1].mode
+ assert.is.equal 'hot', op.inputs[2].mode
+ assert.is.equal 'cold', op.inputs[3].mode
+ assert.spy(tiq).was_not_called!
+
+ it 'has event output', ->
+ assert.is.equal '!', op.out.metatype
+
+ it 'only allows one event input', ->
+ a, b = T.num\mk_evt!, T.num\mk_evt!
+ assert.has.error -> invoke_op TestPureOp, { (literal a), (literal b) }
+
+ it 'supports nested input patterns', ->
+ num = val.num / evt.num
+ class NestedInputOp extends PureOp
+ pattern: (num + val.str)\named('a', 'b')\rep 2, 2
+ type: T.num
+ tick: => @out\set 1
+
+ num = T.num\mk_sig 1
+ str = T.str\mk_sig 'hello'
+ oth = T.num\mk_evt 2
+ args = { (literal num), (literal str), (literal oth), (literal str) }
+ rtn = invoke_op NestedInputOp, args
+ op = rtn.op
+
+ assert.is.equal 2, #op.inputs
+ assert.is.equal num, op.inputs[1].a.result
+ assert.is.equal str, op.inputs[1].b.result
+ assert.is.equal oth, op.inputs[2].a.result
+ assert.is.equal str, op.inputs[2].b.result
+ assert.is.equal 'cold', op.inputs[1].a.mode
+ assert.is.equal 'cold', op.inputs[1].b.mode
+ assert.is.equal 'hot', op.inputs[2].a.mode
+ assert.is.equal 'cold', op.inputs[2].b.mode
+
+ it 'supports dynamically generating the output type', ->
+ class DynamicOp extends PureOp
+ pattern: val.num + (val! / evt!)
+ type: (inputs) => inputs[2]\type!
+ tick: => @out\set @inputs[2]!
+ typ = spy.on DynamicOp, 'type'
+
+ a = T.num\mk_sig 1
+ num = T.num\mk_sig 1
+ str = T.str\mk_sig 1
+ sym = T.sym\mk_evt 1
+
+ rtn = invoke_op DynamicOp, { (literal a), (literal num) }
+ assert.is.equal '~', rtn.result.metatype
+ assert.is.equal num, rtn.result
+
+ rtn = invoke_op DynamicOp, { (literal a), (literal str) }
+ assert.is.equal '~', rtn.result.metatype
+ assert.is.equal str, rtn.result
+
+ rtn = invoke_op DynamicOp, { (literal a), (literal sym) }
+ assert.is.equal '!', rtn.result.metatype
+ assert.is.equal T.sym, rtn.result.type
diff --git a/spec/rtnode_spec.moon b/spec/rtnode_spec.moon
index 1719620..6ac7d15 100644
--- a/spec/rtnode_spec.moon
+++ b/spec/rtnode_spec.moon
@@ -18,8 +18,7 @@ dirty_op = ->
result, input, op_with_inputs { input }
node_with_sideinput = (result, input) ->
- with RTNode :result
- .side_inputs = { [result]: input }
+ RTNode :result, side_inputs: { [result]: input }
describe 'RTNode', ->
it 'wraps result, children', ->
diff --git a/spec/test_setup.moon b/spec/test_setup.moon
index dc91bdd..a402af4 100644
--- a/spec/test_setup.moon
+++ b/spec/test_setup.moon
@@ -1,4 +1,4 @@
-require 'alv'
+import Constant, Scope, Op, Tag from require 'alv'
import Copilot from require 'alv.copilot.base'
import Module from require 'alv.module'
import Logger from require 'alv.logger'
@@ -29,4 +29,14 @@ export COPILOT
do_teardown: ->
COPILOT\end_eval!
+
+ invoke_op: (op, tail, scope=Scope!) ->
+ import op_invoke from require 'alv.invoke'
+
+ fake_cell =
+ head: -> 'test_op'
+ tail: -> tail
+ tag: Tag.blank!
+
+ op_invoke\eval_cell fake_cell, Scope!, Constant.wrap op
}