1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
import do_setup from require 'spec.test_setup'
import Result, Scope, SimpleRegistry from require 'alv'
import Input, Op, ValueStream, EventStream, IOStream from require 'alv.base'
setup do_setup
op_with_inputs = (inputs) ->
with Op!
\setup inputs if inputs
result_with_sideinput = (value, input) ->
with Result :value
.side_inputs = { [value]: input }
class DirtyIO extends IOStream
new: => super 'dirty-io'
dirty: => true
describe 'Result', ->
it 'wraps value, children', ->
value = ValueStream.num 3
a = Result!
b = Result!
children = { a, b }
result = Result :value, :children
assert.is.equal value, result.value
assert.is.same children, result.children
it ':type gets type and assets value', ->
result = Result value: ValueStream.num 2
assert.is.equal 'num', result\type!
result = Result!
assert.has.error -> result\type!
it ':is_const', ->
value = ValueStream.num 2
pure = Result :value
impure = result_with_sideinput value, {}
assert.is.true pure\is_const!
assert.is.false impure\is_const!
assert.is.equal value, pure\const!
assert.has.error -> impure\const!
assert.has.error (-> impure\const 'test'), 'test'
it ':make_ref', ->
value = ValueStream.num 2
input = Input.hot value
op = op_with_inputs { input }
thick = Result :value, :op, children: { Result!, Result! }
ref = thick\make_ref!
assert ref
assert.is.equal thick.value, ref.value
assert.is.same thick.side_inputs, ref.side_inputs
assert.is.same {}, ref.children
assert.is.nil ref.op
it 'lifts up inputs from op', ->
event = ValueStream 'bang', false
event_input = Input.hot event
value = ValueStream 'num', 4
value_input = Input.hot value
op = op_with_inputs { event_input, value_input }
result = Result op: op, :value
assert.is.equal op, result.op
assert.is.same { [event]: event_input, [value]: value_input },
result.side_inputs
it 'does not lift up op inputs that are also child values', ->
event = ValueStream 'bang', false
event_input = Input.hot event
value = ValueStream 'num', 4
value_input = Input.hot value
op = op_with_inputs { event_input, value_input }
result = Result op: op, :value, children: { Result :value }
assert.is.same { [event]: event_input }, result.side_inputs
it 'lifts up side_inputs from children', ->
event_value = ValueStream 'bang', false
event_input = Input.hot event_value
event = Result op: op_with_inputs { event_input }
assert.is.same { [event_value]: event_input }, event.side_inputs
value_value = ValueStream 'num', 4
value_input = Input.hot value_value
value = Result op: op_with_inputs { value_input }
assert.is.same { [value_value]: value_input }, value.side_inputs
result = Result children: { event, value }
assert.is.same { [event_value]: event_input, [value_value]: value_input },
result.side_inputs
describe ':tick', ->
local a_value, a_child, a_input
local b_value, b_child, b_input
before_each ->
a_value = EventStream 'num'
a_input = Input.hot a_value
a_child = result_with_sideinput a_value, a_input
b_value = EventStream 'num'
b_input = Input.hot b_value
b_child = result_with_sideinput b_value, b_input
it 'updates children when a side_input is dirty', ->
a_value\add 1
assert.is.true a_input\dirty!
assert.is.false b_input\dirty!
a = spy.on a_child, 'tick'
b = spy.on b_child, 'tick'
result = Result children: { a_child, b_child }
result\tick!
assert.spy(a).was_called_with match.ref a_child
assert.spy(b).was_called_with match.ref b_child
it 'early-outs when no side_inputs are dirty', ->
assert.is.false a_input\dirty!
assert.is.false b_input\dirty!
a = spy.on a_child, 'tick'
b = spy.on b_child, 'tick'
result = Result children: { a_child, b_child }
result\tick!
assert.spy(a).was_not_called!
assert.spy(b).was_not_called!
it 'updates op when any op-inputs are dirty', ->
a_value\add 1
assert.is.true a_input\dirty!
assert.is.false b_input\dirty!
op = op_with_inputs a: Input.hot a_value
s = spy.on op, 'tick'
result = Result :op, children: { a_child, b_child }
result\tick!
assert.spy(s).was_called_with match.ref op
it 'early-outs when no op-inputs are dirty', ->
a_value\add 1
assert.is.true a_input\dirty!
assert.is.false b_input\dirty!
op = op_with_inputs { Input.hot b_value }
s = spy.on op, 'tick'
result = Result :op, children: { a_child, b_child }
result\tick!
assert.spy(s).was_not_called!
describe ':poll_io', ->
it 'polls IOs referenced in side_inputs', ->
io = DirtyIO!
input = Input.hot io
op = op_with_inputs { input }
result = Result :op
s = spy.on io, 'poll'
assert.is.same { [io]: input }, result.side_inputs
result\poll_io!
assert.spy(s).was_called_with match.ref io
|