test Nameplate, Mailbox. refactor a little bit
This commit is contained in:
parent
3a289f8912
commit
bd974f3801
|
@ -1,7 +1,7 @@
|
||||||
digraph {
|
digraph {
|
||||||
/* new idea */
|
/* new idea */
|
||||||
|
|
||||||
title [label="Message\nMachine" style="dotted"]
|
title [label="Mailbox\nMachine" style="dotted"]
|
||||||
|
|
||||||
{rank=same; S0A S0B}
|
{rank=same; S0A S0B}
|
||||||
S0A [label="S0A:\nunknown"]
|
S0A [label="S0A:\nunknown"]
|
||||||
|
@ -21,6 +21,9 @@ digraph {
|
||||||
S0A -> S1A [label="got_mailbox"]
|
S0A -> S1A [label="got_mailbox"]
|
||||||
S1A [label="S1A:\nknown"]
|
S1A [label="S1A:\nknown"]
|
||||||
S1A -> P_open [label="connected"]
|
S1A -> P_open [label="connected"]
|
||||||
|
S1A -> P1A_queue [label="add_message" style="dotted"]
|
||||||
|
P1A_queue [shape="box" label="queue" style="dotted"]
|
||||||
|
P1A_queue -> S1A [style="dotted"]
|
||||||
S1A -> S2A [style="invis"]
|
S1A -> S2A [style="invis"]
|
||||||
P_open -> P2_connected [style="invis"]
|
P_open -> P2_connected [style="invis"]
|
||||||
|
|
||||||
|
@ -85,7 +88,7 @@ digraph {
|
||||||
S4B [label="S4B:\nclosed"]
|
S4B [label="S4B:\nclosed"]
|
||||||
S4A -> S4B [label="connected"]
|
S4A -> S4B [label="connected"]
|
||||||
S4B -> S4A [label="lost"]
|
S4B -> S4A [label="lost"]
|
||||||
S4B -> S4B [label="add_message\nrx_message\nclose"]
|
S4B -> S4B [label="add_message\nrx_message\nclose"] # is "close" needed?
|
||||||
|
|
||||||
S0A -> P3A_done [label="close" color="red"]
|
S0A -> P3A_done [label="close" color="red"]
|
||||||
S0B -> P3B_done [label="close" color="red"]
|
S0B -> P3B_done [label="close" color="red"]
|
||||||
|
|
|
@ -131,20 +131,16 @@ class Mailbox(object):
|
||||||
@m.output()
|
@m.output()
|
||||||
def N_release_and_accept(self, side, phase, body):
|
def N_release_and_accept(self, side, phase, body):
|
||||||
self._N.release()
|
self._N.release()
|
||||||
self._accept(side, phase, body)
|
if phase not in self._processed:
|
||||||
|
self._processed.add(phase)
|
||||||
|
self._O.got_message(side, phase, body)
|
||||||
@m.output()
|
@m.output()
|
||||||
def RC_tx_close(self):
|
def RC_tx_close(self):
|
||||||
assert self._mood
|
assert self._mood
|
||||||
self._RC_tx_close()
|
self._RC_tx_close()
|
||||||
def _RC_tx_close(self):
|
def _RC_tx_close(self):
|
||||||
self._RC.tx_close(self._mailbox, self._mood)
|
self._RC.tx_close(self._mailbox, self._mood)
|
||||||
@m.output()
|
|
||||||
def accept(self, side, phase, body):
|
|
||||||
self._accept(side, phase, body)
|
|
||||||
def _accept(self, side, phase, body):
|
|
||||||
if phase not in self._processed:
|
|
||||||
self._processed.add(phase)
|
|
||||||
self._O.got_message(side, phase, body)
|
|
||||||
@m.output()
|
@m.output()
|
||||||
def dequeue(self, phase, body):
|
def dequeue(self, phase, body):
|
||||||
self._pending_outbound.pop(phase, None)
|
self._pending_outbound.pop(phase, None)
|
||||||
|
@ -191,10 +187,12 @@ class Mailbox(object):
|
||||||
S3B.upon(add_message, enter=S3B, outputs=[])
|
S3B.upon(add_message, enter=S3B, outputs=[])
|
||||||
S3B.upon(rx_message_theirs, enter=S3B, outputs=[])
|
S3B.upon(rx_message_theirs, enter=S3B, outputs=[])
|
||||||
S3B.upon(rx_message_ours, enter=S3B, outputs=[])
|
S3B.upon(rx_message_ours, enter=S3B, outputs=[])
|
||||||
|
S3B.upon(close, enter=S3B, outputs=[])
|
||||||
|
|
||||||
S4A.upon(connected, enter=S4B, outputs=[])
|
S4A.upon(connected, enter=S4B, outputs=[])
|
||||||
S4B.upon(lost, enter=S4A, outputs=[])
|
S4B.upon(lost, enter=S4A, outputs=[])
|
||||||
S4.upon(add_message, enter=S4, outputs=[])
|
S4.upon(add_message, enter=S4, outputs=[])
|
||||||
S4.upon(rx_message_theirs, enter=S4, outputs=[])
|
S4.upon(rx_message_theirs, enter=S4, outputs=[])
|
||||||
S4.upon(rx_message_ours, enter=S4, outputs=[])
|
S4.upon(rx_message_ours, enter=S4, outputs=[])
|
||||||
|
S4.upon(close, enter=S4, outputs=[])
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,10 @@ import mock
|
||||||
from zope.interface import directlyProvides, implementer
|
from zope.interface import directlyProvides, implementer
|
||||||
from twisted.trial import unittest
|
from twisted.trial import unittest
|
||||||
from .. import (errors, timing, _order, _receive, _key, _code, _lister,
|
from .. import (errors, timing, _order, _receive, _key, _code, _lister,
|
||||||
_input, _allocator, _send, _terminator)
|
_input, _allocator, _send, _terminator, _nameplate, _mailbox)
|
||||||
from .._interfaces import (IKey, IReceive, IBoss, ISend, IMailbox,
|
from .._interfaces import (IKey, IReceive, IBoss, ISend, IMailbox, IOrder,
|
||||||
IRendezvousConnector, ILister, IInput, IAllocator,
|
IRendezvousConnector, ILister, IInput, IAllocator,
|
||||||
INameplate, ICode, IWordlist)
|
INameplate, ICode, IWordlist, ITerminator)
|
||||||
from .._key import derive_key, derive_phase_key, encrypt_data
|
from .._key import derive_key, derive_phase_key, encrypt_data
|
||||||
from ..util import dict_to_bytes, hexstr_to_bytes, bytes_to_hexstr, to_bytes
|
from ..util import dict_to_bytes, hexstr_to_bytes, bytes_to_hexstr, to_bytes
|
||||||
from spake2 import SPAKE2_Symmetric
|
from spake2 import SPAKE2_Symmetric
|
||||||
|
@ -506,6 +506,538 @@ class Allocator(unittest.TestCase):
|
||||||
self.assertEqual(events, [("c.allocated", "1", "1-word-word"),
|
self.assertEqual(events, [("c.allocated", "1", "1-word-word"),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
class Nameplate(unittest.TestCase):
|
||||||
|
def build(self):
|
||||||
|
events = []
|
||||||
|
n = _nameplate.Nameplate()
|
||||||
|
m = Dummy("m", events, IMailbox, "got_mailbox")
|
||||||
|
i = Dummy("i", events, IInput, "got_wordlist")
|
||||||
|
rc = Dummy("rc", events, IRendezvousConnector, "tx_claim", "tx_release")
|
||||||
|
t = Dummy("t", events, ITerminator, "nameplate_done")
|
||||||
|
n.wire(m, i, rc, t)
|
||||||
|
return n, m, i, rc, t, events
|
||||||
|
|
||||||
|
def test_set_first(self):
|
||||||
|
# connection remains up throughout
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.release()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_connect_first(self):
|
||||||
|
# connection remains up throughout
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.release()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_reconnect_while_claiming(self):
|
||||||
|
# connection bounced while waiting for rx_claimed
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.lost()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
|
||||||
|
def test_reconnect_while_claimed(self):
|
||||||
|
# connection bounced while claimed: no retransmits should be sent
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.lost()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
def test_reconnect_while_releasing(self):
|
||||||
|
# connection bounced while waiting for rx_released
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.release()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.lost()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
|
||||||
|
def test_reconnect_while_done(self):
|
||||||
|
# connection bounces after we're done
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.release()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.lost()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
def test_close_while_idle(self):
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.close()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_close_while_idle_connected(self):
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
n.close()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_close_while_unclaimed(self):
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.set_nameplate("1")
|
||||||
|
n.close() # before ever being connected
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_close_while_claiming(self):
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.close()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_close_while_claiming_but_disconnected(self):
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.lost()
|
||||||
|
n.close()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
# we're now waiting for a connection, so we can release the nameplate
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_close_while_claimed(self):
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.close()
|
||||||
|
# this path behaves just like a deliberate release()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_close_while_claimed_but_disconnected(self):
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.lost()
|
||||||
|
n.close()
|
||||||
|
# we're now waiting for a connection, so we can release the nameplate
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_close_while_releasing(self):
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.release()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.close() # ignored, we're already on our way out the door
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_close_while_releasing_but_disconnecteda(self):
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.release()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.lost()
|
||||||
|
n.close()
|
||||||
|
# we must retransmit the tx_release when we reconnect
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
|
||||||
|
def test_close_while_done(self):
|
||||||
|
# connection remains up throughout
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.release()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.close() # NOP
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
def test_close_while_done_but_disconnected(self):
|
||||||
|
# connection remains up throughout
|
||||||
|
n, m, i, rc, t, events = self.build()
|
||||||
|
n.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
n.set_nameplate("1")
|
||||||
|
self.assertEqual(events, [("rc.tx_claim", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
wl = object()
|
||||||
|
with mock.patch("wormhole._nameplate.PGPWordList", return_value=wl):
|
||||||
|
n.rx_claimed("mbox1")
|
||||||
|
self.assertEqual(events, [("i.got_wordlist", wl),
|
||||||
|
("m.got_mailbox", "mbox1"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.release()
|
||||||
|
self.assertEqual(events, [("rc.tx_release", "1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.rx_released()
|
||||||
|
self.assertEqual(events, [("t.nameplate_done",)])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
n.lost()
|
||||||
|
n.close() # NOP
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
class Mailbox(unittest.TestCase):
|
||||||
|
def build(self):
|
||||||
|
events = []
|
||||||
|
m = _mailbox.Mailbox("side1")
|
||||||
|
n = Dummy("n", events, INameplate, "release")
|
||||||
|
rc = Dummy("rc", events, IRendezvousConnector,
|
||||||
|
"tx_add", "tx_open", "tx_close")
|
||||||
|
o = Dummy("o", events, IOrder, "got_message")
|
||||||
|
t = Dummy("t", events, ITerminator, "mailbox_done")
|
||||||
|
m.wire(n, rc, o, t)
|
||||||
|
return m, n, rc, o, t, events
|
||||||
|
|
||||||
|
# TODO: test moods
|
||||||
|
|
||||||
|
def assert_events(self, events, initial_events, tx_add_events):
|
||||||
|
self.assertEqual(len(events), len(initial_events)+len(tx_add_events),
|
||||||
|
events)
|
||||||
|
self.assertEqual(events[:len(initial_events)], initial_events)
|
||||||
|
self.assertEqual(set(events[len(initial_events):]), tx_add_events)
|
||||||
|
|
||||||
|
def test_connect_first(self): # connect before got_mailbox
|
||||||
|
m, n, rc, o, t, events = self.build()
|
||||||
|
m.add_message("phase1", b"msg1")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
m.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
m.got_mailbox("mbox1")
|
||||||
|
self.assertEqual(events, [("rc.tx_open", "mbox1"),
|
||||||
|
("rc.tx_add", "phase1", b"msg1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
m.add_message("phase2", b"msg2")
|
||||||
|
self.assertEqual(events, [("rc.tx_add", "phase2", b"msg2")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
# bouncing the connection should retransmit everything, even the open()
|
||||||
|
m.lost()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
# and messages sent while here should be queued
|
||||||
|
m.add_message("phase3", b"msg3")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
m.connected()
|
||||||
|
# the other messages are allowed to be sent in any order
|
||||||
|
self.assert_events(events, [("rc.tx_open", "mbox1")],
|
||||||
|
{ ("rc.tx_add", "phase1", b"msg1"),
|
||||||
|
("rc.tx_add", "phase2", b"msg2"),
|
||||||
|
("rc.tx_add", "phase3", b"msg3"),
|
||||||
|
})
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
m.rx_message("side1", "phase1", b"msg1") # echo of our message, dequeue
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
m.lost()
|
||||||
|
m.connected()
|
||||||
|
self.assert_events(events, [("rc.tx_open", "mbox1")],
|
||||||
|
{("rc.tx_add", "phase2", b"msg2"),
|
||||||
|
("rc.tx_add", "phase3", b"msg3"),
|
||||||
|
})
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
# a new message from the peer gets delivered, and the Nameplate is
|
||||||
|
# released since the message proves that our peer opened the Mailbox
|
||||||
|
# and therefore no longer needs the Nameplate
|
||||||
|
m.rx_message("side2", "phase1", b"msg1them") # new message from peer
|
||||||
|
self.assertEqual(events, [("n.release",),
|
||||||
|
("o.got_message", "side2", "phase1", b"msg1them"),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
# we de-duplicate peer messages, but still re-release the nameplate
|
||||||
|
# since Nameplate is smart enough to ignore that
|
||||||
|
m.rx_message("side2", "phase1", b"msg1them")
|
||||||
|
self.assertEqual(events, [("n.release",),
|
||||||
|
])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
m.close("happy")
|
||||||
|
self.assertEqual(events, [("rc.tx_close", "mbox1", "happy")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
# while closing, we ignore a lot
|
||||||
|
m.add_message("phase-late", b"late")
|
||||||
|
m.rx_message("side1", "phase2", b"msg2")
|
||||||
|
m.close("happy")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
# bouncing the connection forces a retransmit of the tx_close
|
||||||
|
m.lost()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
m.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_close", "mbox1", "happy")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
m.rx_closed()
|
||||||
|
self.assertEqual(events, [("t.mailbox_done",)])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
# while closed, we ignore everything
|
||||||
|
m.add_message("phase-late", b"late")
|
||||||
|
m.rx_message("side1", "phase2", b"msg2")
|
||||||
|
m.close("happy")
|
||||||
|
m.lost()
|
||||||
|
m.connected()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
def test_mailbox_first(self): # got_mailbox before connect
|
||||||
|
m, n, rc, o, t, events = self.build()
|
||||||
|
m.add_message("phase1", b"msg1")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
m.got_mailbox("mbox1")
|
||||||
|
m.add_message("phase2", b"msg2")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
|
||||||
|
m.connected()
|
||||||
|
|
||||||
|
self.assert_events(events, [("rc.tx_open", "mbox1")],
|
||||||
|
{ ("rc.tx_add", "phase1", b"msg1"),
|
||||||
|
("rc.tx_add", "phase2", b"msg2"),
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_close_while_idle(self):
|
||||||
|
m, n, rc, o, t, events = self.build()
|
||||||
|
m.close("happy")
|
||||||
|
self.assertEqual(events, [("t.mailbox_done",)])
|
||||||
|
|
||||||
|
def test_close_while_idle_but_connected(self):
|
||||||
|
m, n, rc, o, t, events = self.build()
|
||||||
|
m.connected()
|
||||||
|
m.close("happy")
|
||||||
|
self.assertEqual(events, [("t.mailbox_done",)])
|
||||||
|
|
||||||
|
def test_close_while_mailbox_disconnected(self):
|
||||||
|
m, n, rc, o, t, events = self.build()
|
||||||
|
m.got_mailbox("mbox1")
|
||||||
|
m.close("happy")
|
||||||
|
self.assertEqual(events, [("t.mailbox_done",)])
|
||||||
|
|
||||||
|
def test_close_while_reconnecting(self):
|
||||||
|
m, n, rc, o, t, events = self.build()
|
||||||
|
m.got_mailbox("mbox1")
|
||||||
|
m.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_open", "mbox1")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
m.lost()
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
m.close("happy")
|
||||||
|
self.assertEqual(events, [])
|
||||||
|
# we now wait to connect, so we can send the tx_close
|
||||||
|
|
||||||
|
m.connected()
|
||||||
|
self.assertEqual(events, [("rc.tx_close", "mbox1", "happy")])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
|
m.rx_closed()
|
||||||
|
self.assertEqual(events, [("t.mailbox_done",)])
|
||||||
|
events[:] = []
|
||||||
|
|
||||||
class Terminator(unittest.TestCase):
|
class Terminator(unittest.TestCase):
|
||||||
def build(self):
|
def build(self):
|
||||||
|
@ -562,14 +1094,15 @@ class Terminator(unittest.TestCase):
|
||||||
self._do_test("close", "nameplate", "mailbox")
|
self._do_test("close", "nameplate", "mailbox")
|
||||||
self._do_test("close", "mailbox", "nameplate")
|
self._do_test("close", "mailbox", "nameplate")
|
||||||
|
|
||||||
|
# TODO: test moods
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# #Send
|
# #Send
|
||||||
# Mailbox
|
# #Mailbox
|
||||||
# Nameplate
|
# #Nameplate
|
||||||
# #Terminator
|
# #Terminator
|
||||||
# Boss
|
# Boss
|
||||||
# RendezvousConnector (not a state machine)
|
# RendezvousConnector (not a state machine)
|
||||||
# #Input: exercise helper methods
|
# #Input: exercise helper methods
|
||||||
# #wordlist
|
# #wordlist
|
||||||
|
# test idempotency / at-most-once where applicable
|
||||||
|
|
Loading…
Reference in New Issue
Block a user