def test_value_list_instantaneous(): # A mix of instantaneous and continuous a = Value("a") b = Value() c = Value() lst = value_list([a, b, c]) # Initial value should have passed through assert lst.value == ["a", NoValue, NoValue] m = Mock() lst.on_value_changed(m) # Changes should propagate through a.value = "A" assert lst.value == ["A", NoValue, NoValue] m.assert_called_once_with(["A", NoValue, NoValue]) # Instantaneous values should propagate only into the callback m.reset_mock() b.set_instantaneous_value("b") assert lst.value == ["A", NoValue, NoValue] m.assert_called_once_with(["A", "b", NoValue]) m.reset_mock() c.set_instantaneous_value("c") assert lst.value == ["A", NoValue, NoValue] m.assert_called_once_with(["A", NoValue, "c"])
def test_inst_positional_kwargs(): m = Mock() @instantaneous_fn def example(*args, **kwargs): return (args, kwargs) a_value = Value() b_value = Value() # No value should be assigned result = example(a=a_value, b=b_value) result.on_value_changed(m) assert result.value is NoValue # Changes should propagate, callbacks should fire but no value should be # stored m.reset_mock() a_value.set_instantaneous_value(123) m.assert_called_once_with(((), {"a": 123, "b": NoValue})) assert result.value is NoValue m.reset_mock() b_value.set_instantaneous_value(123) m.assert_called_once_with(((), {"a": NoValue, "b": 123})) assert result.value is NoValue
async def test_send_event_register(qth_client, event_loop): a = Value() qth_yarp.send_event("foo/bar", a, register=True, description="Something", qth_client=qth_client) # Allow asyncio functions to run... await asyncio.sleep(0.1) # No initial value should have been sent to Qth assert len(qth_client.send_event.mock_calls) == 0 # No registration should be made # Registration should have been sent qth_client.register.assert_called_once_with( "foo/bar", qth.EVENT_ONE_TO_MANY, "Something", ) # Setting the qth value should update Qth a.set_instantaneous_value(321) await asyncio.sleep(0.1) qth_client.send_event.assert_called_with("foo/bar", 321) # And again... a.set_instantaneous_value(1234) await asyncio.sleep(0.1) qth_client.send_event.assert_called_with("foo/bar", 1234)
def test_value_dict_instantaneous(): # A mix of instantaneous and continuous a = Value("a") b = Value() c = Value() dct = value_dict({"a": a, "b": b, "c": c}) # Initial value should have passed through assert dct.value == {"a": "a", "b": NoValue, "c": NoValue} m = Mock() dct.on_value_changed(m) # Changes should propagate through a.value = "A" assert dct.value == {"a": "A", "b": NoValue, "c": NoValue} m.assert_called_once_with({"a": "A", "b": NoValue, "c": NoValue}) # Instantaneous values should propagate only into the callback m.reset_mock() b.set_instantaneous_value("b") assert dct.value == {"a": "A", "b": NoValue, "c": NoValue} m.assert_called_once_with({"a": "A", "b": "b", "c": NoValue}) m.reset_mock() c.set_instantaneous_value("c") assert dct.value == {"a": "A", "b": NoValue, "c": NoValue} m.assert_called_once_with({"a": "A", "b": NoValue, "c": "c"})
def test_value_tuple_instantaneous(): # A mix of instantaneous and continuous a = Value("a") b = Value() c = Value() tup = value_tuple([a, b, c]) # Initial value should have passed through assert tup.value == ("a", NoValue, NoValue) m = Mock() tup.on_value_changed(m) # Changes should propagate through a.value = "A" assert tup.value == ("A", NoValue, NoValue) m.assert_called_once_with(("A", NoValue, NoValue)) # Instantaneous values should propagate only into the callback m.reset_mock() b.set_instantaneous_value("b") assert tup.value == ("A", NoValue, NoValue) m.assert_called_once_with(("A", "b", NoValue)) m.reset_mock() c.set_instantaneous_value("c") assert tup.value == ("A", NoValue, NoValue) m.assert_called_once_with(("A", NoValue, "c"))
def test_change_callback_only(): m = Mock() v = Value() v.on_value_changed(m) v.set_instantaneous_value(123) m.assert_called_once_with(123) assert v.value is NoValue
async def test_rate_limit_instantaneous(event_loop): v = Value() # No initial value to speak of rlv = rate_limit(v, 0.1, event_loop) assert rlv.value is NoValue log = [] sem = asyncio.Semaphore(0, loop=event_loop) def on_change(new_value): log.append(new_value) sem.release() rlv.on_value_changed(on_change) # First change should make it through immediately v.set_instantaneous_value(1) assert rlv.value is NoValue assert len(log) == 1 assert log[-1] == 1 await sem.acquire() # Another change made immediately after should be delayed v.set_instantaneous_value(2) assert rlv.value is NoValue assert len(log) == 1 # Change should come through after a delay before = time.time() await sem.acquire() assert time.time() - before >= 0.1 assert rlv.value is NoValue assert len(log) == 2 assert log[-1] == 2 # After a suitable delay, the next change should come through immediately await asyncio.sleep(0.15, loop=event_loop) v.set_instantaneous_value(3) assert rlv.value is NoValue assert len(log) == 3 assert log[-1] == 3 await sem.acquire() # A rapid succession of calls should result in only the last value # comming out, and then only after a delay v.set_instantaneous_value(4) v.set_instantaneous_value(5) v.set_instantaneous_value(6) assert rlv.value is NoValue assert len(log) == 3 before = time.time() await sem.acquire() assert time.time() - before >= 0.1 assert rlv.value is NoValue assert len(log) == 4 assert log[-1] == 6
def test_make_persistent(): v = Value() # Initially no value to be found... pv = make_persistent(v) assert pv.value is NoValue m = Mock() pv.on_value_changed(m) assert pv.value is NoValue v.set_instantaneous_value(2) assert pv.value == 2 m.assert_called_once_with(2)
def test_change_instantaneous(): rule = lambda x: x < 10 m = Mock() v = Value() fl = filter(v, rule) fl.on_value_changed(m) assert fl.value is NoValue v.set_instantaneous_value(2) assert fl.value is NoValue m.assert_called_once_with(2) # Above ten, shouldn't get through v.set_instantaneous_value(100) assert fl.value is NoValue m.assert_called_once_with(2)
def test_operator_wrapper_instantaneous(): # Only test negation since others are defined in exactly the same way a = Value() not_a = instantaneous_not_(a) assert not_a.value is NoValue m = Mock() not_a.on_value_changed(m) a.set_instantaneous_value(True) assert not_a.value is NoValue m.assert_called_once_with(False) m.reset_mock() a.set_instantaneous_value(False) assert not_a.value is NoValue m.assert_called_once_with(True) m.reset_mock()
async def test_instantaneous(self, event_loop): value = Value() delayed_value = delay(value, 0.1, loop=event_loop) assert delayed_value.value is NoValue # Monitor changes evt = asyncio.Event(loop=event_loop) m = Mock(side_effect=lambda *_: evt.set()) delayed_value.on_value_changed(m) # Trigger a change for later... before = time.time() value.set_instantaneous_value(123) assert delayed_value.value is NoValue assert not m.mock_calls await evt.wait() assert time.time() - before >= 0.1 m.assert_called_once_with(123) assert delayed_value.value is NoValue
async def test_send_event(qth_client, event_loop): a = Value() qth_yarp.send_event("foo/bar", a, qth_client=qth_client) # Allow asyncio functions to run... await asyncio.sleep(0.1) # No initial value should have been sent to Qth assert len(qth_client.send_event.mock_calls) == 0 # No registration should be made assert len(qth_client.register.mock_calls) == 0 # Setting the qth value should update Qth a.set_instantaneous_value(321) await asyncio.sleep(0.1) qth_client.send_event.assert_called_with("foo/bar", 321) # And again... a.set_instantaneous_value(1234) await asyncio.sleep(0.1) qth_client.send_event.assert_called_with("foo/bar", 1234)
def test_no_repeat_instantaneous(): v = Value() nrv = no_repeat(v) assert nrv.value is NoValue m = Mock() nrv.on_value_changed(m) # New value should pass through v.set_instantaneous_value(1) m.assert_called_once_with(1) assert nrv.value is NoValue # Repeat should not m.reset_mock() v.set_instantaneous_value(1) assert not m.called assert nrv.value is NoValue # New value should pass through v.set_instantaneous_value(2) m.assert_called_once_with(2) assert nrv.value is NoValue