gears/tests/test_signals.py
2025-03-09 23:27:45 +13:00

199 lines
4.3 KiB
Python

from dataclasses import dataclass
from typing import NamedTuple
from unittest.mock import MagicMock
import pytest
from gears import Gear
from gears.connections import connect
from gears.effect import Effect, effect_of, effect_of_2
def test_get_set():
g = Gear(1)
assert g.get() == 1
assert g() == 1
g.set(3)
assert g.get() == 3
assert g() == 3
def test_basic_effect():
g = Gear("E")
last_arg: str | None = None
@effect_of(g)
def value_changed(v: str):
nonlocal last_arg
last_arg = v
assert last_arg == "E"
g.set("hello")
assert last_arg == "hello"
def test_effect_of_2():
g1 = Gear("E")
g2 = Gear(3)
last_arg: tuple[str, int] | None = None
@effect_of_2(g1, g2)
def value_changed(v1: str, v2: int):
nonlocal last_arg
last_arg = (v1, v2)
assert last_arg == ("E", 3)
g2.set(4)
assert last_arg == ("E", 4)
def test_connect():
my_str = Gear("123")
my_int = connect(my_str, lambda s: int(s), lambda _, i: str(i))
assert my_int() == 123
my_int.set(321)
assert my_str() == "321"
# def test_dualis():
# val_both: Gear[tuple[int, int]] = Gear((5, 6))
# val0 = connect(val_both, lambda both: both[0], lambda both, val0: (val0, both[1]))
# val1 = connect(val_both, lambda both: both[1], lambda both, val1: (both[0], val1))
def test_watts():
volts = Gear(3.0)
amps = Gear(5.0)
@effect_of_2(volts, amps)
def on_watts_changed(volts: float, amps: float):
watts = volts * amps
if watts > 100:
raise ValueError("OMG ITS BURNING")
volts.set(5.0)
with pytest.raises(ValueError):
volts.set(50.0)
def test_led():
battery_voltage = Gear(3.0)
switch: Gear[bool] = Gear(True) # true means circuit closed
# ohms_circuit = Gear(1.0)
ohms_circuit = connect(
switch, lambda x: 1.0 if x else 100000000.0, lambda _, y: y < 100
)
log: list[str] = []
@effect_of_2(battery_voltage, ohms_circuit)
def on_led_inputs_changed(volts: float, ohms: float):
amps = volts / ohms
if amps > 1:
print("LED ACTIVE!")
log.append("LED ACTIVE!")
else:
print("LED INACTIVE!")
log.append("LED INACTIVE!")
# Effect(on_led_inputs_changed, battery_voltage, led_ohms)
assert log == ["LED ACTIVE!"]
switch.set(False)
assert log == ["LED ACTIVE!", "LED INACTIVE!"]
def test_connect_multiplication():
mul1 = Gear(10.0)
mul2 = connect(mul1, lambda x: x * 2.0, lambda _, y: y / 2.0)
mul4 = connect(mul2, lambda x: x * 2.0, lambda _, y: y / 2.0)
mul3 = connect(mul1, lambda x: x * 3.0, lambda _, y: y / 3.0)
assert mul2() == 20
assert mul3() == 30
mul2.set(30)
assert mul1() == 15
assert mul3() == 45
assert mul4() == 60
# mul1
# |> mul2
# |> mul4
# |> mul3
#
class Person:
name: str
class HouseHold:
people: list[Person]
class NameEntry:
def __init__(self, name: Gear[str]):
# self.name = name
textBox = TextBox(...)
effect_of(name)(textBox.setValue)
textBox.onChange = name.set
def test_connect_property():
class Foo(NamedTuple):
bar: int
baz: int
foo = Gear(Foo(bar=1, baz=2))
bar = connect(
foo,
lambda foo: foo.bar,
lambda foo, bar: Foo(bar=bar, baz=foo.baz),
# TODO: look into dataclasses.replace
)
assert bar() == 1
bar.set(5)
assert bar() == 5
assert foo() == Foo(5, 2)
# class IntEntryOld:
# on_value_changed: Signal[int]
# _val: int
# def get_val(self, val: int): ...
# def set_val(self, val: int): ...
# class IntEntry:
# def __init__(self, value: Gear[int]): ...
# int_entry = IntEntry()
# effect_of(bar)(int_entry.set_val)
# int_entry.on_value_changed.connect(bar.set)
# @effect_of(bar)
# def update_ui(bar: int):
# int_entry.set_val(bar)
# def update_model(bar_val: int):
# bar.set(bar_val)
# int_entry.on_value_changed.connect(update_model)
def hello(my_callback):
my_callback(5)
def test_demonstrate_magicmock():
m = MagicMock()
# print(f"{m.foo(4).bar[0]=}")
# m.
hello(m)
# assert m.call_count == 1
m.assert_called_once_with(5)