protocols: make playbooks immutable

This commit is contained in:
Maximilian Hils 2017-07-24 14:42:25 +02:00
parent 2e3704faf1
commit 41597272f9
2 changed files with 90 additions and 12 deletions

View File

@ -0,0 +1,59 @@
import typing
from mitmproxy.proxy.protocol2 import events, commands
from mitmproxy.proxy.protocol2.layer import Layer
from mitmproxy.proxy.protocol2.test import tutils
from mitmproxy.proxy.protocol2.utils import expect
class TEvent(events.Event):
commands: typing.Iterable[typing.Any]
def __init__(self, cmds=(None,)):
self.commands = cmds
class TCommand(commands.Command):
x: typing.Any
def __init__(self, x=None):
self.x = x
class TLayer(Layer):
"""
Simple echo layer
"""
@expect(TEvent)
def _handle_event(self, event: TEvent) -> commands.TCommandGenerator:
for x in event.commands:
yield TCommand(x)
def test_playbook_simple(tctx):
playbook = tutils.playbook(TLayer(tctx), [])
assert (
playbook
>> TEvent()
<< TCommand()
>> TEvent([])
<< None
)
def test_playbook_partial_assert(tctx):
playbook = tutils.playbook(TLayer(tctx), [])
playbook = (
playbook
>> TEvent()
<< TCommand()
)
assert playbook
playbook = (
playbook
>> TEvent()
<< TCommand()
)
assert playbook
assert len(playbook.actual) == len(playbook.playbook) == 4

View File

@ -3,11 +3,14 @@ import itertools
import re import re
import typing import typing
import copy
from mitmproxy.proxy.protocol2 import commands from mitmproxy.proxy.protocol2 import commands
from mitmproxy.proxy.protocol2 import events from mitmproxy.proxy.protocol2 import events
from mitmproxy.proxy.protocol2 import layer from mitmproxy.proxy.protocol2 import layer
TPlaybook = typing.List[typing.Union[commands.Command, events.Event]] TPlaybookEntry = typing.Union[commands.Command, events.Event]
TPlaybook = typing.List[TPlaybookEntry]
def _eq( def _eq(
@ -60,9 +63,13 @@ class playbook:
assert x2 == [] assert x2 == []
""" """
layer: layer.Layer layer: layer.Layer
"""The base layer"""
playbook: TPlaybook playbook: TPlaybook
_asserted: int """expected command/event sequence"""
actual: TPlaybook actual: TPlaybook
"""actual command/event sequence"""
_final: bool
"""True if no << or >> operation has been called on this."""
def __init__( def __init__(
self, self,
@ -77,25 +84,38 @@ class playbook:
self.layer = layer self.layer = layer
self.playbook = playbook self.playbook = playbook
self.actual = [] self.actual = []
self._asserted = 0 self._final = True
def _copy_with(self, entry: TPlaybookEntry):
self._final = False
p = playbook(
self.layer,
self.playbook + [entry]
)
p.actual = self.actual.copy()
return p
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)
self.playbook.append(e) return self._copy_with(e)
return self
def __lshift__(self, c): def __lshift__(self, c):
"""Add an expected command""" """Add an expected command"""
if c is None: if c is None:
return self return self
assert isinstance(c, commands.Command) assert isinstance(c, commands.Command)
self.playbook.append(c) return self._copy_with(c)
return self
def __bool__(self): def __bool__(self):
"""Determine if playbook is correct.""" """Determine if playbook is correct."""
for i, x in list(enumerate(self.playbook))[self._asserted:]:
self.layer = copy.deepcopy(self.layer)
self.playbook = copy.deepcopy(self.playbook)
self.actual = copy.deepcopy(self.actual)
already_asserted = len(self.actual)
for i, x in enumerate(self.playbook[already_asserted:], already_asserted):
if isinstance(x, commands.Command): if isinstance(x, commands.Command):
pass pass
else: else:
@ -107,11 +127,10 @@ class playbook:
self.actual.extend( self.actual.extend(
self.layer.handle_event(x) self.layer.handle_event(x)
) )
self._asserted = len(self.playbook)
success = all( success = all(
_eq(e, a) _eq(e, a)
for e, a in itertools.zip_longest(self.playbook, self.actual) for e, a in itertools.zip_longest(self.playbook, self.actual)
) )
if not success: if not success:
def _str(x): def _str(x):
@ -129,7 +148,7 @@ class playbook:
return True return True
def __del__(self): def __del__(self):
if self._asserted < len(self.playbook): if self._final and len(self.actual) < len(self.playbook):
raise RuntimeError("Unfinished playbook!") raise RuntimeError("Unfinished playbook!")
@ -144,4 +163,4 @@ class Placeholder:
return self.obj return self.obj
def __repr__(self): def __repr__(self):
return repr(self.obj) return f"P({repr(self.obj)})"