aboutsummaryrefslogtreecommitdiffstats
path: root/spec/result_spec.moon
blob: f7006f1a3c0613473d8b59100365bba6d31b3cbd (plain)
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