react_test.py

  1# These tests are auto-generated with test data from:
  2# https://github.com/exercism/problem-specifications/tree/main/exercises/react/canonical-data.json
  3# File last updated on 2023-07-19
  4
  5from functools import partial
  6import unittest
  7
  8from react import (
  9    InputCell,
 10    ComputeCell,
 11)
 12
 13
 14class ReactTest(unittest.TestCase):
 15    def test_input_cells_have_a_value(self):
 16        input = InputCell(10)
 17        self.assertEqual(input.value, 10)
 18
 19    def test_an_input_cell_s_value_can_be_set(self):
 20        input = InputCell(4)
 21        input.value = 20
 22        self.assertEqual(input.value, 20)
 23
 24    def test_compute_cells_calculate_initial_value(self):
 25        input = InputCell(1)
 26        output = ComputeCell(
 27            [
 28                input,
 29            ],
 30            lambda inputs: inputs[0] + 1,
 31        )
 32        self.assertEqual(output.value, 2)
 33
 34    def test_compute_cells_take_inputs_in_the_right_order(self):
 35        one = InputCell(1)
 36        two = InputCell(2)
 37        output = ComputeCell(
 38            [
 39                one,
 40                two,
 41            ],
 42            lambda inputs: inputs[0] + inputs[1] * 10,
 43        )
 44        self.assertEqual(output.value, 21)
 45
 46    def test_compute_cells_update_value_when_dependencies_are_changed(self):
 47        input = InputCell(1)
 48        output = ComputeCell(
 49            [
 50                input,
 51            ],
 52            lambda inputs: inputs[0] + 1,
 53        )
 54        input.value = 3
 55        self.assertEqual(output.value, 4)
 56
 57    def test_compute_cells_can_depend_on_other_compute_cells(self):
 58        input = InputCell(1)
 59        times_two = ComputeCell(
 60            [
 61                input,
 62            ],
 63            lambda inputs: inputs[0] * 2,
 64        )
 65        times_thirty = ComputeCell(
 66            [
 67                input,
 68            ],
 69            lambda inputs: inputs[0] * 30,
 70        )
 71        output = ComputeCell(
 72            [
 73                times_two,
 74                times_thirty,
 75            ],
 76            lambda inputs: inputs[0] + inputs[1],
 77        )
 78        self.assertEqual(output.value, 32)
 79        input.value = 3
 80        self.assertEqual(output.value, 96)
 81
 82    def test_compute_cells_fire_callbacks(self):
 83        input = InputCell(1)
 84        output = ComputeCell(
 85            [
 86                input,
 87            ],
 88            lambda inputs: inputs[0] + 1,
 89        )
 90        cb1_observer = []
 91        callback1 = self.callback_factory(cb1_observer)
 92        output.add_callback(callback1)
 93        input.value = 3
 94        self.assertEqual(cb1_observer[-1], 4)
 95
 96    def test_callback_cells_only_fire_on_change(self):
 97        input = InputCell(1)
 98        output = ComputeCell([input], lambda inputs: 111 if inputs[0] < 3 else 222)
 99        cb1_observer = []
100        callback1 = self.callback_factory(cb1_observer)
101        output.add_callback(callback1)
102        input.value = 2
103        self.assertEqual(cb1_observer, [])
104        input.value = 4
105        self.assertEqual(cb1_observer[-1], 222)
106
107    def test_callbacks_do_not_report_already_reported_values(self):
108        input = InputCell(1)
109        output = ComputeCell(
110            [
111                input,
112            ],
113            lambda inputs: inputs[0] + 1,
114        )
115        cb1_observer = []
116        callback1 = self.callback_factory(cb1_observer)
117        output.add_callback(callback1)
118        input.value = 2
119        self.assertEqual(cb1_observer[-1], 3)
120        input.value = 3
121        self.assertEqual(cb1_observer[-1], 4)
122
123    def test_callbacks_can_fire_from_multiple_cells(self):
124        input = InputCell(1)
125        plus_one = ComputeCell(
126            [
127                input,
128            ],
129            lambda inputs: inputs[0] + 1,
130        )
131        minus_one = ComputeCell(
132            [
133                input,
134            ],
135            lambda inputs: inputs[0] - 1,
136        )
137        cb1_observer = []
138        cb2_observer = []
139        callback1 = self.callback_factory(cb1_observer)
140        callback2 = self.callback_factory(cb2_observer)
141        plus_one.add_callback(callback1)
142        minus_one.add_callback(callback2)
143        input.value = 10
144        self.assertEqual(cb1_observer[-1], 11)
145        self.assertEqual(cb2_observer[-1], 9)
146
147    def test_callbacks_can_be_added_and_removed(self):
148        input = InputCell(11)
149        output = ComputeCell(
150            [
151                input,
152            ],
153            lambda inputs: inputs[0] + 1,
154        )
155        cb1_observer = []
156        cb2_observer = []
157        cb3_observer = []
158        callback1 = self.callback_factory(cb1_observer)
159        callback2 = self.callback_factory(cb2_observer)
160        callback3 = self.callback_factory(cb3_observer)
161        output.add_callback(callback1)
162        output.add_callback(callback2)
163        input.value = 31
164        self.assertEqual(cb1_observer[-1], 32)
165        self.assertEqual(cb2_observer[-1], 32)
166        output.remove_callback(callback1)
167        output.add_callback(callback3)
168        input.value = 41
169        self.assertEqual(len(cb1_observer), 1)
170        self.assertEqual(cb2_observer[-1], 42)
171        self.assertEqual(cb3_observer[-1], 42)
172
173    def test_removing_a_callback_multiple_times_doesn_t_interfere_with_other_callbacks(
174        self,
175    ):
176        input = InputCell(1)
177        output = ComputeCell(
178            [
179                input,
180            ],
181            lambda inputs: inputs[0] + 1,
182        )
183        cb1_observer = []
184        cb2_observer = []
185        callback1 = self.callback_factory(cb1_observer)
186        callback2 = self.callback_factory(cb2_observer)
187        output.add_callback(callback1)
188        output.add_callback(callback2)
189        output.remove_callback(callback1)
190        output.remove_callback(callback1)
191        output.remove_callback(callback1)
192        input.value = 2
193        self.assertEqual(cb1_observer, [])
194        self.assertEqual(cb2_observer[-1], 3)
195
196    def test_callbacks_should_only_be_called_once_even_if_multiple_dependencies_change(
197        self,
198    ):
199        input = InputCell(1)
200        plus_one = ComputeCell(
201            [
202                input,
203            ],
204            lambda inputs: inputs[0] + 1,
205        )
206        minus_one1 = ComputeCell(
207            [
208                input,
209            ],
210            lambda inputs: inputs[0] - 1,
211        )
212        minus_one2 = ComputeCell(
213            [
214                minus_one1,
215            ],
216            lambda inputs: inputs[0] - 1,
217        )
218        output = ComputeCell(
219            [
220                plus_one,
221                minus_one2,
222            ],
223            lambda inputs: inputs[0] * inputs[1],
224        )
225        cb1_observer = []
226        callback1 = self.callback_factory(cb1_observer)
227        output.add_callback(callback1)
228        input.value = 4
229        self.assertEqual(cb1_observer[-1], 10)
230
231    def test_callbacks_should_not_be_called_if_dependencies_change_but_output_value_doesn_t_change(
232        self,
233    ):
234        input = InputCell(1)
235        plus_one = ComputeCell(
236            [
237                input,
238            ],
239            lambda inputs: inputs[0] + 1,
240        )
241        minus_one = ComputeCell(
242            [
243                input,
244            ],
245            lambda inputs: inputs[0] - 1,
246        )
247        always_two = ComputeCell(
248            [
249                plus_one,
250                minus_one,
251            ],
252            lambda inputs: inputs[0] - inputs[1],
253        )
254        cb1_observer = []
255        callback1 = self.callback_factory(cb1_observer)
256        always_two.add_callback(callback1)
257        input.value = 2
258        self.assertEqual(cb1_observer, [])
259        input.value = 3
260        self.assertEqual(cb1_observer, [])
261        input.value = 4
262        self.assertEqual(cb1_observer, [])
263        input.value = 5
264        self.assertEqual(cb1_observer, [])
265
266    # Utility functions.
267    def callback_factory(self, observer):
268        def callback(observer, value):
269            observer.append(value)
270
271        return partial(callback, observer)