|
import unittest |
|
from arrowlets import * |
|
|
|
class Dummy: |
|
def __init__(self): |
|
self.event_bindings = {} |
|
|
|
def bind(self, event, f): |
|
if event in self.event_bindings: |
|
self.event_bindings[event].add(f) |
|
else: |
|
self.event_bindings[event] = {f} |
|
|
|
def unbind(self, event, f): |
|
if event not in self.event_bindings or f not in self.event_bindings[event]: |
|
return |
|
self.event_bindings[event].remove(f) |
|
|
|
def trigger(self, event): |
|
assert event in self.event_bindings, f'"{event}" event not found' |
|
# we construct a list to force a copy of this to be created |
|
# so each function gets triggered, but we may remove it in the |
|
# middle of iteration |
|
for f in list(self.event_bindings[event]): |
|
f(event) |
|
|
|
class TestDummy(unittest.TestCase): |
|
def test_dummy(self): |
|
d = Dummy() |
|
flag = [False] |
|
def f(_): |
|
flag[0] = True |
|
d.bind('event', f) |
|
d.trigger('event') |
|
self.assertTrue(flag[0]) |
|
flag[0] = False |
|
d.unbind('event', f) |
|
d.trigger('event') |
|
self.assertFalse(flag[0]) |
|
|
|
class ArrowletsTest(unittest.TestCase): |
|
def test_CpsA(self): |
|
add1 = lambda x: x + 1 |
|
add2 = CpsA(add1).next(CpsA(add1)) |
|
self.assertEqual(add2.run(1), 3) |
|
|
|
def test_SimpleEventA(self): |
|
nclicks = [0] |
|
def clickTargetA(x): |
|
target, event = x |
|
nclicks[0] += 1 |
|
return target |
|
|
|
d = Dummy() |
|
SimpleEventA('click').next(clickTargetA).run(d) |
|
d.trigger('click') |
|
self.assertEqual(nclicks[0], 1) |
|
|
|
nclicks = [0] |
|
SimpleEventA('click').next(clickTargetA)\ |
|
.next(SimpleEventA('click'))\ |
|
.next(clickTargetA).run(d) |
|
d.trigger('click') |
|
d.trigger('click') # unbinds after two clicks |
|
d.trigger('click') |
|
self.assertEqual(nclicks[0], 2) |
|
|
|
def test_EventA(self): |
|
def clickTargetA(x): |
|
target, event = x |
|
nclicks[0] += 1 |
|
return target |
|
|
|
target = Dummy() |
|
|
|
### |
|
nclicks = [0] |
|
|
|
p = EventA('click')\ |
|
.next(clickTargetA)\ |
|
.next(EventA('click'))\ |
|
.next(clickTargetA)\ |
|
.run(target) |
|
|
|
target.trigger('click') |
|
target.trigger('click') |
|
self.assertEqual(nclicks[0], 2) |
|
|
|
### |
|
nclicks = [0] |
|
|
|
p = EventA('click')\ |
|
.next(clickTargetA)\ |
|
.next(EventA('click'))\ |
|
.next(clickTargetA)\ |
|
.run(target) |
|
|
|
target.trigger('click') # nclicks++ |
|
p.cancel() # cancel the chain |
|
self.assertEqual(nclicks[0], 1) |
|
target.trigger('click') # nclicks shouldn't increment here |
|
self.assertEqual(nclicks[0], 1) |
|
|
|
### |
|
nclicks = [0] |
|
|
|
t = Dummy() |
|
t.text = 'nothing here' |
|
|
|
p = EventA('click')\ |
|
.next(clickTargetA)\ |
|
.next(EventA('click'))\ |
|
.next(clickTargetA)\ |
|
.run(t) |
|
|
|
def f(_): |
|
t.text = 'clicked!' |
|
p.next(f).run(None) |
|
|
|
self.assertEqual(t.text, 'nothing here') |
|
self.assertEqual(nclicks[0], 0) |
|
t.trigger('click') |
|
self.assertEqual(nclicks[0], 1) |
|
self.assertEqual(t.text, 'clicked!') |
|
t.trigger('click') |
|
self.assertEqual(nclicks[0], 2) |
|
self.assertEqual(t.text, 'clicked!') |
|
|
|
def test_ConstA(self): |
|
ConstA(1).next(lambda x: self.assertEqual(x, 1)).run(2) |
|
|
|
def test_repeat(self): |
|
def bubblesort(x): |
|
lst, i, j = x |
|
if j + 1 < i: |
|
if lst[j] > lst[j + 1]: |
|
lst[j], lst[j+1] = lst[j+1], lst[j] |
|
return Repeat((lst, i, j+1)) |
|
elif i > 0: |
|
return Repeat((lst, i-1, 0)) |
|
else: |
|
return Done(lst) |
|
|
|
bubblesortA = AsyncA(bubblesort).repeat() |
|
lst = [1,3,2,0,4] |
|
bubblesortA.run((lst, 4, 0)) |
|
self.assertEqual(lst, [0,1,2,3,4]) |
|
|
|
def test_product(self): |
|
target1 = Dummy() |
|
target2 = Dummy() |
|
def clickTargetsA(a): |
|
t1, t2 = a[0][0], a[1][0] |
|
t1.text = 'clicked a' |
|
t2.text = 'clicked b' |
|
|
|
EventA('click').product(EventA('click'))\ |
|
.next(clickTargetsA)\ |
|
.run((target1, target2)) |
|
|
|
target1.trigger('click') |
|
target2.trigger('click') |
|
self.assertEqual(target1.text, 'clicked a') |
|
self.assertEqual(target2.text, 'clicked b') |
|
|
|
def test_or(self): |
|
def WriteA(s): |
|
def f(x): |
|
target, event = x |
|
target.text = s |
|
return target |
|
return f |
|
|
|
heads_elem = Dummy() |
|
tails_elem = Dummy() |
|
heads_elem.text = '' |
|
tails_elem.text = '' |
|
heads = ConstA(heads_elem) |
|
tails = ConstA(tails_elem) |
|
heads.next(EventA('click')).next(WriteA('I win!'))\ |
|
.or_(tails.next(EventA('click')).next(WriteA('You lose!'))).run(None) |
|
|
|
heads_elem.trigger('click') |
|
self.assertEqual(heads_elem.text, 'I win!') |
|
self.assertEqual(tails_elem.text, '') |
|
|
|
### |
|
|
|
heads_elem.text = '' |
|
tails_elem.text = '' |
|
heads.next(EventA('click')).next(WriteA('I win!'))\ |
|
.or_(tails.next(EventA('click')).next(WriteA('You lose!'))).run(None) |
|
|
|
tails_elem.trigger('click') |
|
self.assertEqual(heads_elem.text, '') |
|
self.assertEqual(tails_elem.text, 'You lose!') |
|
|
|
def test_dragndrop(self): |
|
target = Dummy() |
|
proxy = Dummy() |
|
proxy.target = target |
|
proxy.status = 'dropped' |
|
|
|
def DragElementA(target): |
|
# hack because I don't care anymore... |
|
return ConstA(target).next(lambda _: proxy) |
|
|
|
def setupA(x): |
|
proxy, event = x |
|
proxy.status = 'setup' |
|
return proxy |
|
|
|
def dragA(x): |
|
proxy, event = x |
|
proxy.status = 'dragging' |
|
return proxy |
|
|
|
def dropA(x): |
|
proxy, event = x |
|
proxy.status = 'dropped' |
|
return proxy |
|
|
|
def cancelA(x): |
|
proxy, event = x |
|
proxy.status = 'cancelled' |
|
return proxy |
|
|
|
dragOrDropA = ( |
|
(EventA('mousemove').next(dragA)).next(Repeat)\ |
|
.or_((EventA('mouseup').next(dropA).next(Done)))).repeat() |
|
|
|
dragDropOrCancelA = ( |
|
(EventA('mousemove').next(dragA).next(dragOrDropA))\ |
|
.or_((EventA('mouseup').next(cancelA)))) |
|
|
|
dragAndDropA = ( |
|
(EventA('mousedown').next(setupA).next(dragDropOrCancelA))) |
|
|
|
DragElementA('dragtarget').next(dragAndDropA).run(None) |
|
proxy.trigger('mousedown') |
|
proxy.trigger('mousemove') |
|
self.assertEqual(proxy.status, 'dragging') |
|
|
|
if __name__ == '__main__': |
|
unittest.main() |
TODO: maybe add a demo of drag and drop using TK.