mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-23 08:11:00 +00:00
Add a light-weight custom event system, use it for keepserving
This patch implements the lightweight event system I propose in #2144, adds a custom event "processing_complete" that is triggered after file read, client replay and server replay, and introduces a KeepServing addon to handle this for mitmdump.
This commit is contained in:
parent
169068c7ec
commit
228a22b3c0
@ -69,8 +69,8 @@ class AddonManager:
|
|||||||
raise exceptions.AddonError(
|
raise exceptions.AddonError(
|
||||||
"invoke_addon called without a handler context."
|
"invoke_addon called without a handler context."
|
||||||
)
|
)
|
||||||
if name not in eventsequence.Events: # prama: no cover
|
if name not in eventsequence.Events:
|
||||||
raise NotImplementedError("Unknown event")
|
name = "event_" + name
|
||||||
func = getattr(addon, name, None)
|
func = getattr(addon, name, None)
|
||||||
if func:
|
if func:
|
||||||
if not callable(func):
|
if not callable(func):
|
||||||
@ -89,4 +89,3 @@ class AddonManager:
|
|||||||
self.invoke_addon(i, name, *args, **kwargs)
|
self.invoke_addon(i, name, *args, **kwargs)
|
||||||
except exceptions.AddonHalt:
|
except exceptions.AddonHalt:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ class ClientPlayback:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.flows = None
|
self.flows = None
|
||||||
self.current_thread = None
|
self.current_thread = None
|
||||||
self.keepserving = False
|
|
||||||
self.has_replayed = False
|
self.has_replayed = False
|
||||||
|
|
||||||
def count(self) -> int:
|
def count(self) -> int:
|
||||||
@ -32,7 +31,6 @@ class ClientPlayback:
|
|||||||
self.load(flows)
|
self.load(flows)
|
||||||
else:
|
else:
|
||||||
self.flows = None
|
self.flows = None
|
||||||
self.keepserving = options.keepserving
|
|
||||||
|
|
||||||
def tick(self):
|
def tick(self):
|
||||||
if self.current_thread and not self.current_thread.is_alive():
|
if self.current_thread and not self.current_thread.is_alive():
|
||||||
@ -41,5 +39,5 @@ class ClientPlayback:
|
|||||||
self.current_thread = ctx.master.replay_request(self.flows.pop(0))
|
self.current_thread = ctx.master.replay_request(self.flows.pop(0))
|
||||||
self.has_replayed = True
|
self.has_replayed = True
|
||||||
if self.has_replayed:
|
if self.has_replayed:
|
||||||
if not self.flows and not self.current_thread and not self.keepserving:
|
if not self.flows and not self.current_thread:
|
||||||
ctx.master.shutdown()
|
ctx.master.addons.trigger("processing_complete")
|
||||||
|
7
mitmproxy/addons/keepserving.py
Normal file
7
mitmproxy/addons/keepserving.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
|
class KeepServing:
|
||||||
|
def event_processing_complete(self):
|
||||||
|
if not ctx.master.options.keepserving:
|
||||||
|
ctx.master.shutdown()
|
@ -11,7 +11,6 @@ class ReadFile:
|
|||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.path = None
|
self.path = None
|
||||||
self.keepserving = False
|
|
||||||
|
|
||||||
def load_flows_file(self, path: str) -> int:
|
def load_flows_file(self, path: str) -> int:
|
||||||
path = os.path.expanduser(path)
|
path = os.path.expanduser(path)
|
||||||
@ -33,8 +32,6 @@ class ReadFile:
|
|||||||
raise exceptions.FlowReadException(v)
|
raise exceptions.FlowReadException(v)
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, options, updated):
|
||||||
if "keepserving" in updated:
|
|
||||||
self.keepserving = options.keepserving
|
|
||||||
if "rfile" in updated and options.rfile:
|
if "rfile" in updated and options.rfile:
|
||||||
self.path = options.rfile
|
self.path = options.rfile
|
||||||
|
|
||||||
@ -46,5 +43,4 @@ class ReadFile:
|
|||||||
raise exceptions.OptionsError(v)
|
raise exceptions.OptionsError(v)
|
||||||
finally:
|
finally:
|
||||||
self.path = None
|
self.path = None
|
||||||
if not self.keepserving:
|
ctx.master.addons.trigger("processing_complete")
|
||||||
ctx.master.shutdown()
|
|
||||||
|
@ -9,13 +9,6 @@ class ReadStdin:
|
|||||||
An addon that reads from stdin if we're not attached to (someting like)
|
An addon that reads from stdin if we're not attached to (someting like)
|
||||||
a tty.
|
a tty.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
|
||||||
self.keepserving = False
|
|
||||||
|
|
||||||
def configure(self, options, updated):
|
|
||||||
if "keepserving" in updated:
|
|
||||||
self.keepserving = options.keepserving
|
|
||||||
|
|
||||||
def running(self, stdin = sys.stdin):
|
def running(self, stdin = sys.stdin):
|
||||||
if not stdin.isatty():
|
if not stdin.isatty():
|
||||||
ctx.log.info("Reading from stdin")
|
ctx.log.info("Reading from stdin")
|
||||||
@ -30,5 +23,4 @@ class ReadStdin:
|
|||||||
ctx.master.load_flow(i)
|
ctx.master.load_flow(i)
|
||||||
except exceptions.FlowReadException as e:
|
except exceptions.FlowReadException as e:
|
||||||
ctx.log.error("Error reading from stdin: %s" % e)
|
ctx.log.error("Error reading from stdin: %s" % e)
|
||||||
if not self.keepserving:
|
ctx.master.addons.trigger("processing_complete")
|
||||||
ctx.master.shutdown()
|
|
||||||
|
@ -104,7 +104,7 @@ class ServerPlayback:
|
|||||||
|
|
||||||
def tick(self):
|
def tick(self):
|
||||||
if self.stop and not self.final_flow.live:
|
if self.stop and not self.final_flow.live:
|
||||||
ctx.master.shutdown()
|
ctx.master.addons.trigger("processing_complete")
|
||||||
|
|
||||||
def request(self, f):
|
def request(self, f):
|
||||||
if self.flowmap:
|
if self.flowmap:
|
||||||
@ -115,7 +115,7 @@ class ServerPlayback:
|
|||||||
if self.options.refresh_server_playback:
|
if self.options.refresh_server_playback:
|
||||||
response.refresh()
|
response.refresh()
|
||||||
f.response = response
|
f.response = response
|
||||||
if not self.flowmap and not self.options.keepserving:
|
if not self.flowmap:
|
||||||
self.final_flow = f
|
self.final_flow = f
|
||||||
self.stop = True
|
self.stop = True
|
||||||
elif self.options.replay_kill_extra:
|
elif self.options.replay_kill_extra:
|
||||||
|
@ -79,7 +79,10 @@ class Options(optmanager.OptManager):
|
|||||||
)
|
)
|
||||||
self.add_option(
|
self.add_option(
|
||||||
"keepserving", bool, False,
|
"keepserving", bool, False,
|
||||||
"Continue serving after client playback or file read."
|
"""
|
||||||
|
Instructs mitmdump to continue serving after client playback,
|
||||||
|
server playback or file read. This option is ignored by interactive tools, which always keep serving.
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
self.add_option(
|
self.add_option(
|
||||||
"server", bool, True,
|
"server", bool, True,
|
||||||
|
@ -6,16 +6,36 @@ from mitmproxy import proxy
|
|||||||
from mitmproxy import eventsequence
|
from mitmproxy import eventsequence
|
||||||
|
|
||||||
|
|
||||||
|
class _AddonWrapper:
|
||||||
|
def __init__(self, master, addons):
|
||||||
|
self.master = master
|
||||||
|
self.addons = addons
|
||||||
|
|
||||||
|
def trigger(self, event, *args, **kwargs):
|
||||||
|
self.master.events.append((event, args, kwargs))
|
||||||
|
return self.addons.trigger(event, *args, **kwargs)
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
return getattr(self.addons, attr)
|
||||||
|
|
||||||
|
|
||||||
class RecordingMaster(mitmproxy.master.Master):
|
class RecordingMaster(mitmproxy.master.Master):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.event_log = []
|
self.addons = _AddonWrapper(self, self.addons)
|
||||||
|
self.events = []
|
||||||
|
self.logs = []
|
||||||
|
|
||||||
|
def has_event(self, name):
|
||||||
|
for i in self.events:
|
||||||
|
if i[0] == name:
|
||||||
|
return True
|
||||||
|
|
||||||
def add_log(self, e, level):
|
def add_log(self, e, level):
|
||||||
self.event_log.append((level, e))
|
self.logs.append((level, e))
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.event_log = []
|
self.logs = []
|
||||||
|
|
||||||
|
|
||||||
class context:
|
class context:
|
||||||
|
@ -2,7 +2,7 @@ from mitmproxy import controller
|
|||||||
from mitmproxy import addons
|
from mitmproxy import addons
|
||||||
from mitmproxy import options
|
from mitmproxy import options
|
||||||
from mitmproxy import master
|
from mitmproxy import master
|
||||||
from mitmproxy.addons import dumper, termlog, termstatus, readstdin
|
from mitmproxy.addons import dumper, termlog, termstatus, readstdin, keepserving
|
||||||
|
|
||||||
|
|
||||||
class DumpMaster(master.Master):
|
class DumpMaster(master.Master):
|
||||||
@ -21,7 +21,7 @@ class DumpMaster(master.Master):
|
|||||||
self.addons.add(*addons.default_addons())
|
self.addons.add(*addons.default_addons())
|
||||||
if with_dumper:
|
if with_dumper:
|
||||||
self.addons.add(dumper.Dumper())
|
self.addons.add(dumper.Dumper())
|
||||||
self.addons.add(readstdin.ReadStdin())
|
self.addons.add(readstdin.ReadStdin(), keepserving.KeepServing())
|
||||||
|
|
||||||
@controller.handler
|
@controller.handler
|
||||||
def log(self, e):
|
def log(self, e):
|
||||||
|
@ -12,7 +12,7 @@ class TestCheckALPN:
|
|||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
a = check_alpn.CheckALPN()
|
a = check_alpn.CheckALPN()
|
||||||
tctx.configure(a)
|
tctx.configure(a)
|
||||||
assert not any(msg in m for l, m in tctx.master.event_log)
|
assert not any(msg in m for l, m in tctx.master.logs)
|
||||||
|
|
||||||
def test_check_no_alpn(self, disable_alpn):
|
def test_check_no_alpn(self, disable_alpn):
|
||||||
msg = 'ALPN support missing'
|
msg = 'ALPN support missing'
|
||||||
@ -20,4 +20,4 @@ class TestCheckALPN:
|
|||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
a = check_alpn.CheckALPN()
|
a = check_alpn.CheckALPN()
|
||||||
tctx.configure(a)
|
tctx.configure(a)
|
||||||
assert any(msg in m for l, m in tctx.master.event_log)
|
assert any(msg in m for l, m in tctx.master.logs)
|
||||||
|
@ -16,4 +16,4 @@ class TestCheckCA:
|
|||||||
tctx.master.server.config.certstore.default_ca.has_expired = mock.MagicMock(return_value=expired)
|
tctx.master.server.config.certstore.default_ca.has_expired = mock.MagicMock(return_value=expired)
|
||||||
a = check_ca.CheckCA()
|
a = check_ca.CheckCA()
|
||||||
tctx.configure(a)
|
tctx.configure(a)
|
||||||
assert any(msg in m for l, m in tctx.master.event_log) is expired
|
assert any(msg in m for l, m in tctx.master.logs) is expired
|
||||||
|
@ -23,7 +23,7 @@ class MockThread():
|
|||||||
class TestClientPlayback:
|
class TestClientPlayback:
|
||||||
def test_playback(self):
|
def test_playback(self):
|
||||||
cp = clientplayback.ClientPlayback()
|
cp = clientplayback.ClientPlayback()
|
||||||
with taddons.context():
|
with taddons.context() as tctx:
|
||||||
assert cp.count() == 0
|
assert cp.count() == 0
|
||||||
f = tflow.tflow(resp=True)
|
f = tflow.tflow(resp=True)
|
||||||
cp.load([f])
|
cp.load([f])
|
||||||
@ -35,15 +35,12 @@ class TestClientPlayback:
|
|||||||
assert rp.called
|
assert rp.called
|
||||||
assert cp.current_thread
|
assert cp.current_thread
|
||||||
|
|
||||||
cp.keepserving = False
|
|
||||||
cp.flows = None
|
cp.flows = None
|
||||||
cp.current_thread = None
|
cp.current_thread = None
|
||||||
with mock.patch("mitmproxy.master.Master.shutdown") as sd:
|
|
||||||
cp.tick()
|
cp.tick()
|
||||||
assert sd.called
|
assert tctx.master.has_event("processing_complete")
|
||||||
|
|
||||||
cp.current_thread = MockThread()
|
cp.current_thread = MockThread()
|
||||||
with mock.patch("mitmproxy.master.Master.shutdown") as sd:
|
|
||||||
cp.tick()
|
cp.tick()
|
||||||
assert cp.current_thread is None
|
assert cp.current_thread is None
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ class TestContentView:
|
|||||||
with taddons.context(options=options.Options()) as ctx:
|
with taddons.context(options=options.Options()) as ctx:
|
||||||
ctx.configure(d, flow_detail=4, verbosity=3)
|
ctx.configure(d, flow_detail=4, verbosity=3)
|
||||||
d.response(tflow.tflow())
|
d.response(tflow.tflow())
|
||||||
assert "Content viewer failed" in ctx.master.event_log[0][1]
|
assert "Content viewer failed" in ctx.master.logs[0][1]
|
||||||
|
|
||||||
|
|
||||||
def test_tcp():
|
def test_tcp():
|
||||||
|
10
test/mitmproxy/addons/test_keepserving.py
Normal file
10
test/mitmproxy/addons/test_keepserving.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from mitmproxy.addons import keepserving
|
||||||
|
from mitmproxy.test import taddons
|
||||||
|
|
||||||
|
|
||||||
|
def test_keepserving():
|
||||||
|
ks = keepserving.KeepServing()
|
||||||
|
|
||||||
|
with taddons.context() as tctx:
|
||||||
|
ks.event_processing_complete()
|
||||||
|
assert tctx.master.should_exit.is_set()
|
@ -32,13 +32,13 @@ def test_configure(mck, tmpdir):
|
|||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
tf = str(tmpdir.join("tfile"))
|
tf = str(tmpdir.join("tfile"))
|
||||||
write_data(tf)
|
write_data(tf)
|
||||||
tctx.configure(rf, rfile=str(tf), keepserving=False)
|
tctx.configure(rf, rfile=str(tf))
|
||||||
assert not mck.called
|
assert not mck.called
|
||||||
rf.running()
|
rf.running()
|
||||||
assert mck.called
|
assert mck.called
|
||||||
|
|
||||||
write_data(tf, corrupt=True)
|
write_data(tf, corrupt=True)
|
||||||
tctx.configure(rf, rfile=str(tf), keepserving=False)
|
tctx.configure(rf, rfile=str(tf))
|
||||||
with pytest.raises(exceptions.OptionsError):
|
with pytest.raises(exceptions.OptionsError):
|
||||||
rf.running()
|
rf.running()
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ def test_corruption(mck, tmpdir):
|
|||||||
with pytest.raises(exceptions.FlowReadException):
|
with pytest.raises(exceptions.FlowReadException):
|
||||||
rf.load_flows_file("nonexistent")
|
rf.load_flows_file("nonexistent")
|
||||||
assert not mck.called
|
assert not mck.called
|
||||||
assert len(tctx.master.event_log) == 1
|
assert len(tctx.master.logs) == 1
|
||||||
|
|
||||||
tfc = str(tmpdir.join("tfile"))
|
tfc = str(tmpdir.join("tfile"))
|
||||||
write_data(tfc, corrupt=True)
|
write_data(tfc, corrupt=True)
|
||||||
@ -59,4 +59,4 @@ def test_corruption(mck, tmpdir):
|
|||||||
with pytest.raises(exceptions.FlowReadException):
|
with pytest.raises(exceptions.FlowReadException):
|
||||||
rf.load_flows_file(tfc)
|
rf.load_flows_file(tfc)
|
||||||
assert mck.called
|
assert mck.called
|
||||||
assert len(tctx.master.event_log) == 2
|
assert len(tctx.master.logs) == 2
|
||||||
|
@ -26,12 +26,6 @@ def gen_data(corrupt=False):
|
|||||||
return tf
|
return tf
|
||||||
|
|
||||||
|
|
||||||
def test_configure(tmpdir):
|
|
||||||
rf = readstdin.ReadStdin()
|
|
||||||
with taddons.context() as tctx:
|
|
||||||
tctx.configure(rf, keepserving=False)
|
|
||||||
|
|
||||||
|
|
||||||
class mStdin:
|
class mStdin:
|
||||||
def __init__(self, d):
|
def __init__(self, d):
|
||||||
self.buffer = d
|
self.buffer = d
|
||||||
@ -49,11 +43,11 @@ def test_read(m, tmpdir):
|
|||||||
assert m.called
|
assert m.called
|
||||||
|
|
||||||
rf.running(stdin=mStdin(None))
|
rf.running(stdin=mStdin(None))
|
||||||
assert tctx.master.event_log
|
assert tctx.master.logs
|
||||||
tctx.master.clear()
|
tctx.master.clear()
|
||||||
|
|
||||||
m.reset_mock()
|
m.reset_mock()
|
||||||
assert not m.called
|
assert not m.called
|
||||||
rf.running(stdin=mStdin(gen_data(corrupt=True)))
|
rf.running(stdin=mStdin(gen_data(corrupt=True)))
|
||||||
assert m.called
|
assert m.called
|
||||||
assert tctx.master.event_log
|
assert tctx.master.logs
|
||||||
|
@ -97,6 +97,6 @@ class TestReplaceFile:
|
|||||||
tmpfile.remove()
|
tmpfile.remove()
|
||||||
f = tflow.tflow()
|
f = tflow.tflow()
|
||||||
f.request.content = b"foo"
|
f.request.content = b"foo"
|
||||||
assert not tctx.master.event_log
|
assert not tctx.master.logs
|
||||||
r.request(f)
|
r.request(f)
|
||||||
assert tctx.master.event_log
|
assert tctx.master.logs
|
||||||
|
@ -22,14 +22,14 @@ def test_scriptenv():
|
|||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
with script.scriptenv("path", []):
|
with script.scriptenv("path", []):
|
||||||
raise SystemExit
|
raise SystemExit
|
||||||
assert tctx.master.event_log[0][0] == "error"
|
assert tctx.master.logs[0][0] == "error"
|
||||||
assert "exited" in tctx.master.event_log[0][1]
|
assert "exited" in tctx.master.logs[0][1]
|
||||||
|
|
||||||
tctx.master.clear()
|
tctx.master.clear()
|
||||||
with script.scriptenv("path", []):
|
with script.scriptenv("path", []):
|
||||||
raise ValueError("fooo")
|
raise ValueError("fooo")
|
||||||
assert tctx.master.event_log[0][0] == "error"
|
assert tctx.master.logs[0][0] == "error"
|
||||||
assert "foo" in tctx.master.event_log[0][1]
|
assert "foo" in tctx.master.logs[0][1]
|
||||||
|
|
||||||
|
|
||||||
class Called:
|
class Called:
|
||||||
@ -135,7 +135,7 @@ class TestScript:
|
|||||||
f.write(".")
|
f.write(".")
|
||||||
sc.tick()
|
sc.tick()
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
if tctx.master.event_log:
|
if tctx.master.logs:
|
||||||
return
|
return
|
||||||
raise AssertionError("Change event not detected.")
|
raise AssertionError("Change event not detected.")
|
||||||
|
|
||||||
@ -147,11 +147,11 @@ class TestScript:
|
|||||||
sc.start(tctx.options)
|
sc.start(tctx.options)
|
||||||
f = tflow.tflow(resp=True)
|
f = tflow.tflow(resp=True)
|
||||||
sc.request(f)
|
sc.request(f)
|
||||||
assert tctx.master.event_log[0][0] == "error"
|
assert tctx.master.logs[0][0] == "error"
|
||||||
assert len(tctx.master.event_log[0][1].splitlines()) == 6
|
assert len(tctx.master.logs[0][1].splitlines()) == 6
|
||||||
assert re.search(r'addonscripts[\\/]error.py", line \d+, in request', tctx.master.event_log[0][1])
|
assert re.search(r'addonscripts[\\/]error.py", line \d+, in request', tctx.master.logs[0][1])
|
||||||
assert re.search(r'addonscripts[\\/]error.py", line \d+, in mkerr', tctx.master.event_log[0][1])
|
assert re.search(r'addonscripts[\\/]error.py", line \d+, in mkerr', tctx.master.logs[0][1])
|
||||||
assert tctx.master.event_log[0][1].endswith("ValueError: Error!\n")
|
assert tctx.master.logs[0][1].endswith("ValueError: Error!\n")
|
||||||
|
|
||||||
def test_addon(self):
|
def test_addon(self):
|
||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
@ -256,7 +256,7 @@ class TestScriptLoader:
|
|||||||
"%s %s" % (rec, "c"),
|
"%s %s" % (rec, "c"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
debug = [(i[0], i[1]) for i in tctx.master.event_log if i[0] == "debug"]
|
debug = [(i[0], i[1]) for i in tctx.master.logs if i[0] == "debug"]
|
||||||
assert debug == [
|
assert debug == [
|
||||||
('debug', 'a start'),
|
('debug', 'a start'),
|
||||||
('debug', 'a configure'),
|
('debug', 'a configure'),
|
||||||
@ -270,7 +270,7 @@ class TestScriptLoader:
|
|||||||
('debug', 'c configure'),
|
('debug', 'c configure'),
|
||||||
('debug', 'c running'),
|
('debug', 'c running'),
|
||||||
]
|
]
|
||||||
tctx.master.event_log = []
|
tctx.master.logs = []
|
||||||
tctx.configure(
|
tctx.configure(
|
||||||
sc,
|
sc,
|
||||||
scripts = [
|
scripts = [
|
||||||
@ -279,11 +279,11 @@ class TestScriptLoader:
|
|||||||
"%s %s" % (rec, "b"),
|
"%s %s" % (rec, "b"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
debug = [(i[0], i[1]) for i in tctx.master.event_log if i[0] == "debug"]
|
debug = [(i[0], i[1]) for i in tctx.master.logs if i[0] == "debug"]
|
||||||
# No events, only order has changed
|
# No events, only order has changed
|
||||||
assert debug == []
|
assert debug == []
|
||||||
|
|
||||||
tctx.master.event_log = []
|
tctx.master.logs = []
|
||||||
tctx.configure(
|
tctx.configure(
|
||||||
sc,
|
sc,
|
||||||
scripts = [
|
scripts = [
|
||||||
@ -291,7 +291,7 @@ class TestScriptLoader:
|
|||||||
"%s %s" % (rec, "a"),
|
"%s %s" % (rec, "a"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
debug = [(i[0], i[1]) for i in tctx.master.event_log if i[0] == "debug"]
|
debug = [(i[0], i[1]) for i in tctx.master.logs if i[0] == "debug"]
|
||||||
assert debug == [
|
assert debug == [
|
||||||
('debug', 'c done'),
|
('debug', 'c done'),
|
||||||
('debug', 'b done'),
|
('debug', 'b done'),
|
||||||
|
@ -34,7 +34,7 @@ def test_tick():
|
|||||||
s.final_flow = tflow.tflow()
|
s.final_flow = tflow.tflow()
|
||||||
s.final_flow.live = False
|
s.final_flow.live = False
|
||||||
s.tick()
|
s.tick()
|
||||||
assert tctx.master.should_exit.is_set()
|
assert tctx.master.has_event("processing_complete")
|
||||||
|
|
||||||
|
|
||||||
def test_server_playback():
|
def test_server_playback():
|
||||||
@ -315,7 +315,6 @@ def test_server_playback_full():
|
|||||||
tctx.configure(
|
tctx.configure(
|
||||||
s,
|
s,
|
||||||
refresh_server_playback = True,
|
refresh_server_playback = True,
|
||||||
keepserving=False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
f = tflow.tflow()
|
f = tflow.tflow()
|
||||||
|
@ -6,7 +6,7 @@ def test_configure():
|
|||||||
ts = termstatus.TermStatus()
|
ts = termstatus.TermStatus()
|
||||||
with taddons.context() as ctx:
|
with taddons.context() as ctx:
|
||||||
ts.running()
|
ts.running()
|
||||||
assert not ctx.master.event_log
|
assert not ctx.master.logs
|
||||||
ctx.configure(ts, server=True)
|
ctx.configure(ts, server=True)
|
||||||
ts.running()
|
ts.running()
|
||||||
assert ctx.master.event_log
|
assert ctx.master.logs
|
||||||
|
@ -43,7 +43,7 @@ class TestConcurrent(tservers.MasterTest):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
sc.start(tctx.options)
|
sc.start(tctx.options)
|
||||||
assert "decorator not supported" in tctx.master.event_log[0][1]
|
assert "decorator not supported" in tctx.master.logs[0][1]
|
||||||
|
|
||||||
def test_concurrent_class(self):
|
def test_concurrent_class(self):
|
||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
|
@ -11,6 +11,7 @@ class TAddon:
|
|||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.tick = True
|
self.tick = True
|
||||||
|
self.custom_called = False
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Addon(%s)" % self.name
|
return "Addon(%s)" % self.name
|
||||||
@ -18,11 +19,17 @@ class TAddon:
|
|||||||
def done(self):
|
def done(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def event_custom(self):
|
||||||
|
self.custom_called = True
|
||||||
|
|
||||||
|
|
||||||
def test_simple():
|
def test_simple():
|
||||||
o = options.Options()
|
o = options.Options()
|
||||||
m = master.Master(o, proxy.DummyServer(o))
|
m = master.Master(o, proxy.DummyServer(o))
|
||||||
a = addonmanager.AddonManager(m)
|
a = addonmanager.AddonManager(m)
|
||||||
|
with pytest.raises(exceptions.AddonError):
|
||||||
|
a.invoke_addon(TAddon("one"), "done")
|
||||||
|
|
||||||
a.add(TAddon("one"))
|
a.add(TAddon("one"))
|
||||||
assert a.get("one")
|
assert a.get("one")
|
||||||
assert not a.get("two")
|
assert not a.get("two")
|
||||||
@ -33,3 +40,8 @@ def test_simple():
|
|||||||
a.trigger("done")
|
a.trigger("done")
|
||||||
with pytest.raises(exceptions.AddonError):
|
with pytest.raises(exceptions.AddonError):
|
||||||
a.trigger("tick")
|
a.trigger("tick")
|
||||||
|
|
||||||
|
ta = TAddon("one")
|
||||||
|
a.add(ta)
|
||||||
|
a.trigger("custom")
|
||||||
|
assert ta.custom_called
|
||||||
|
Loading…
Reference in New Issue
Block a user