mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-26 18:18:25 +00:00
[sans-io] improve testing story
This commit is contained in:
parent
e0eb77a794
commit
84287f928c
@ -24,7 +24,7 @@ def test_open_connection(tctx):
|
|||||||
def test_open_connection_err(tctx):
|
def test_open_connection_err(tctx):
|
||||||
f = Placeholder()
|
f = Placeholder()
|
||||||
assert (
|
assert (
|
||||||
playbook(TCPLayer(tctx))
|
playbook(TCPLayer(tctx), hooks=True)
|
||||||
<< Hook("tcp_start", f)
|
<< Hook("tcp_start", f)
|
||||||
>> reply()
|
>> reply()
|
||||||
<< OpenConnection(tctx.server)
|
<< OpenConnection(tctx.server)
|
||||||
@ -40,7 +40,7 @@ def test_simple(tctx):
|
|||||||
f = Placeholder()
|
f = Placeholder()
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
playbook(TCPLayer(tctx))
|
playbook(TCPLayer(tctx), hooks=True)
|
||||||
<< Hook("tcp_start", f)
|
<< Hook("tcp_start", f)
|
||||||
>> reply()
|
>> reply()
|
||||||
<< OpenConnection(tctx.server)
|
<< OpenConnection(tctx.server)
|
||||||
@ -70,41 +70,27 @@ def test_receive_data_before_server_connected(tctx):
|
|||||||
assert that data received before a server connection is established
|
assert that data received before a server connection is established
|
||||||
will still be forwarded.
|
will still be forwarded.
|
||||||
"""
|
"""
|
||||||
f = Placeholder()
|
|
||||||
assert (
|
assert (
|
||||||
playbook(TCPLayer(tctx))
|
playbook(TCPLayer(tctx))
|
||||||
<< Hook("tcp_start", f)
|
|
||||||
>> reply()
|
|
||||||
<< OpenConnection(tctx.server)
|
<< OpenConnection(tctx.server)
|
||||||
>> DataReceived(tctx.client, b"hello!")
|
>> DataReceived(tctx.client, b"hello!")
|
||||||
>> reply(None, to=-2)
|
>> reply(None, to=-2)
|
||||||
<< Hook("tcp_message", f)
|
|
||||||
>> reply()
|
|
||||||
<< SendData(tctx.server, b"hello!")
|
<< SendData(tctx.server, b"hello!")
|
||||||
)
|
)
|
||||||
assert f().messages
|
|
||||||
|
|
||||||
|
|
||||||
def test_receive_data_after_half_close(tctx):
|
def test_receive_data_after_half_close(tctx):
|
||||||
"""
|
"""
|
||||||
data received after the other connection has been half-closed should still be forwarded.
|
data received after the other connection has been half-closed should still be forwarded.
|
||||||
"""
|
"""
|
||||||
f = Placeholder()
|
|
||||||
assert (
|
assert (
|
||||||
playbook(TCPLayer(tctx))
|
playbook(TCPLayer(tctx), hooks=False)
|
||||||
<< Hook("tcp_start", f)
|
|
||||||
>> reply()
|
|
||||||
<< OpenConnection(tctx.server)
|
<< OpenConnection(tctx.server)
|
||||||
>> reply(None)
|
>> reply(None)
|
||||||
>> ConnectionClosed(tctx.server)
|
>> ConnectionClosed(tctx.server)
|
||||||
<< CloseConnection(tctx.client)
|
<< CloseConnection(tctx.client)
|
||||||
>> DataReceived(tctx.client, b"i'm late")
|
>> DataReceived(tctx.client, b"i'm late")
|
||||||
<< Hook("tcp_message", f)
|
|
||||||
>> reply()
|
|
||||||
<< SendData(tctx.server, b"i'm late")
|
<< SendData(tctx.server, b"i'm late")
|
||||||
>> ConnectionClosed(tctx.client)
|
>> ConnectionClosed(tctx.client)
|
||||||
<< CloseConnection(tctx.server)
|
<< CloseConnection(tctx.server)
|
||||||
<< Hook("tcp_end", f)
|
|
||||||
>> reply()
|
|
||||||
<< None
|
|
||||||
)
|
)
|
@ -57,7 +57,7 @@ def eq(
|
|||||||
return _eq(a, b)
|
return _eq(a, b)
|
||||||
|
|
||||||
|
|
||||||
def _str(x: typing.Union[events.Event, commands.Command]):
|
def _fmt_entry(x: TPlaybookEntry):
|
||||||
arrow = ">>" if isinstance(x, events.Event) else "<<"
|
arrow = ">>" if isinstance(x, events.Event) else "<<"
|
||||||
x = str(x) \
|
x = str(x) \
|
||||||
.replace('Placeholder:None', '<unset placeholder>') \
|
.replace('Placeholder:None', '<unset placeholder>') \
|
||||||
@ -93,14 +93,17 @@ class playbook:
|
|||||||
"""actual command/event sequence"""
|
"""actual command/event sequence"""
|
||||||
_errored: bool
|
_errored: bool
|
||||||
"""used to check if playbook as been fully asserted"""
|
"""used to check if playbook as been fully asserted"""
|
||||||
ignore_log: bool
|
logs: bool
|
||||||
"""If True, log statements are ignored."""
|
"""If False, the playbook specification doesn't contain log commands."""
|
||||||
|
hooks: bool
|
||||||
|
"""If False, the playbook specification doesn't include hooks or hook replies. They are automatically replied to."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
layer: Layer,
|
layer: Layer,
|
||||||
|
hooks: bool = False,
|
||||||
|
logs: bool = False,
|
||||||
expected: typing.Optional[TPlaybook] = None,
|
expected: typing.Optional[TPlaybook] = None,
|
||||||
ignore_log: bool = True
|
|
||||||
):
|
):
|
||||||
if expected is None:
|
if expected is None:
|
||||||
expected = [
|
expected = [
|
||||||
@ -111,11 +114,14 @@ class playbook:
|
|||||||
self.expected = expected
|
self.expected = expected
|
||||||
self.actual = []
|
self.actual = []
|
||||||
self._errored = False
|
self._errored = False
|
||||||
self.ignore_log = ignore_log
|
self.logs = logs
|
||||||
|
self.hooks = hooks
|
||||||
|
|
||||||
def __rshift__(self, e):
|
def __rshift__(self, e):
|
||||||
"""Add an event to send"""
|
"""Add an event to send"""
|
||||||
assert isinstance(e, events.Event)
|
assert isinstance(e, events.Event)
|
||||||
|
if not self.hooks and isinstance(e, events.HookReply):
|
||||||
|
raise ValueError(f"Playbook must not contain hook replies if hooks=False: {e}")
|
||||||
self.expected.append(e)
|
self.expected.append(e)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -124,39 +130,48 @@ class playbook:
|
|||||||
if c is None:
|
if c is None:
|
||||||
return self
|
return self
|
||||||
assert isinstance(c, commands.Command)
|
assert isinstance(c, commands.Command)
|
||||||
assert not (self.ignore_log and isinstance(c, commands.Log))
|
if not self.logs and isinstance(c, commands.Log):
|
||||||
|
raise ValueError(f"Playbook must not contain log commands if logs=False: {c}")
|
||||||
|
if not self.hooks and isinstance(c, commands.Hook):
|
||||||
|
raise ValueError(f"Playbook must not contain hook commands if hooks=False: {c}")
|
||||||
self.expected.append(c)
|
self.expected.append(c)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
"""Determine if playbook is correct."""
|
"""Determine if playbook is correct."""
|
||||||
already_asserted = len(self.actual)
|
already_asserted = len(self.actual)
|
||||||
for i, x in enumerate(self.expected[already_asserted:], already_asserted):
|
i = already_asserted
|
||||||
|
while i < len(self.expected):
|
||||||
|
x = self.expected[i]
|
||||||
if isinstance(x, commands.Command):
|
if isinstance(x, commands.Command):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if hasattr(x, "playbook_eval"):
|
if hasattr(x, "playbook_eval"):
|
||||||
x = self.expected[i] = x.playbook_eval(self)
|
x = self.expected[i] = x.playbook_eval(self)
|
||||||
if isinstance(x, events.OpenConnectionReply):
|
if isinstance(x, events.OpenConnectionReply) and not x.reply:
|
||||||
x.command.connection.state = ConnectionState.OPEN
|
x.command.connection.state = ConnectionState.OPEN
|
||||||
elif isinstance(x, events.ConnectionClosed):
|
elif isinstance(x, events.ConnectionClosed):
|
||||||
x.connection.state &= ~ConnectionState.CAN_READ
|
x.connection.state &= ~ConnectionState.CAN_READ
|
||||||
|
|
||||||
self.actual.append(x)
|
self.actual.append(x)
|
||||||
self.actual.extend(
|
cmds = list(self.layer.handle_event(x))
|
||||||
self.layer.handle_event(x)
|
self.actual.extend(cmds)
|
||||||
)
|
if not self.logs:
|
||||||
|
for offset, cmd in enumerate(cmds):
|
||||||
if self.ignore_log:
|
if isinstance(cmd, commands.Log):
|
||||||
self.actual = [
|
self.expected.insert(i + 1 + offset, cmd)
|
||||||
x for x in self.actual if not isinstance(x, commands.Log)
|
if not self.hooks:
|
||||||
]
|
last_cmd = self.actual[-1]
|
||||||
|
if isinstance(last_cmd, commands.Hook):
|
||||||
|
self.expected.insert(i + len(cmds), last_cmd)
|
||||||
|
self.expected.insert(i + len(cmds) + 1, events.HookReply(last_cmd))
|
||||||
|
i += 1
|
||||||
|
|
||||||
if not eq(self.expected, self.actual):
|
if not eq(self.expected, self.actual):
|
||||||
self._errored = True
|
self._errored = True
|
||||||
diff = "\n".join(difflib.ndiff(
|
diff = "\n".join(difflib.ndiff(
|
||||||
[_str(x) for x in self.expected],
|
[_fmt_entry(x) for x in self.expected],
|
||||||
[_str(x) for x in self.actual]
|
[_fmt_entry(x) for x in self.actual]
|
||||||
))
|
))
|
||||||
raise AssertionError(f"Playbook mismatch!\n{diff}")
|
raise AssertionError(f"Playbook mismatch!\n{diff}")
|
||||||
else:
|
else:
|
||||||
@ -208,7 +223,7 @@ class reply(events.Event):
|
|||||||
self.to = cmd
|
self.to = cmd
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
actual_str = "\n".join(_str(x) for x in playbook.actual)
|
actual_str = "\n".join(_fmt_entry(x) for x in playbook.actual)
|
||||||
raise AssertionError(f"Expected command ({self.to}) did not occur:\n{actual_str}")
|
raise AssertionError(f"Expected command ({self.to}) did not occur:\n{actual_str}")
|
||||||
|
|
||||||
self.side_effect(self.to)
|
self.side_effect(self.to)
|
||||||
|
Loading…
Reference in New Issue
Block a user