diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/conftest.py | 22 | ||||
-rw-r--r-- | test/mitmproxy/addons/test_stickycookie.py | 1 | ||||
-rw-r--r-- | test/mitmproxy/data/addonscripts/recorder.py | 4 | ||||
-rw-r--r-- | test/mitmproxy/protocol/test_websocket.py | 11 | ||||
-rw-r--r-- | test/mitmproxy/test_eventsequence.py | 138 | ||||
-rw-r--r-- | test/mitmproxy/test_flow.py | 101 | ||||
-rw-r--r-- | test/mitmproxy/test_server.py | 18 | ||||
-rw-r--r-- | test/mitmproxy/test_stateobject.py | 12 | ||||
-rw-r--r-- | test/mitmproxy/test_version.py | 10 | ||||
-rw-r--r-- | test/mitmproxy/tservers.py | 4 |
10 files changed, 208 insertions, 113 deletions
diff --git a/test/conftest.py b/test/conftest.py index 50ec3421..83823a19 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -103,15 +103,17 @@ def pytest_runtestloop(session): measured_files = [os.path.normpath(os.path.relpath(f, prefix)) for f in cov.get_data().measured_files()] measured_files = [f for f in measured_files if not any(f.startswith(excluded_f) for excluded_f in excluded_files)] - for name in pytest.config.option.full_cov: + for name in coverage_values.keys(): files = [f for f in measured_files if f.startswith(os.path.normpath(name))] try: with open(os.devnull, 'w') as null: - coverage_values[name] = cov.report(files, ignore_errors=True, file=null) + overall = cov.report(files, ignore_errors=True, file=null) + singles = [(s, cov.report(s, ignore_errors=True, file=null)) for s in files] + coverage_values[name] = (overall, singles) except: pass - if any(v < 100 for v in coverage_values.values()): + if any(v < 100 for v, _ in coverage_values.values()): # make sure we get the EXIT_TESTSFAILED exit code session.testsfailed += 1 else: @@ -132,12 +134,15 @@ def pytest_terminal_summary(terminalreporter, exitstatus): msg = "FAIL: Full test coverage not reached!\n" terminalreporter.write(msg, **markup) - for name, value in coverage_values.items(): - if value < 100: + for name in sorted(coverage_values.keys()): + msg = 'Coverage for {}: {:.2f}%\n'.format(name, coverage_values[name][0]) + if coverage_values[name][0] < 100: markup = {'red': True, 'bold': True} + for s, v in sorted(coverage_values[name][1]): + if v < 100: + msg += ' {}: {:.2f}%\n'.format(s, v) else: markup = {'green': True} - msg = 'Coverage for {}: {:.2f}%\n'.format(name, value) terminalreporter.write(msg, **markup) else: markup = {'green': True} @@ -145,6 +150,7 @@ def pytest_terminal_summary(terminalreporter, exitstatus): msg += '{}\n\n'.format('\n'.join(pytest.config.option.full_cov)) terminalreporter.write(msg, **markup) - msg = 'Excluded files:\n' - msg += '{}\n'.format('\n'.join(pytest.config.option.no_full_cov)) + msg = '\nExcluded files:\n' + for s in sorted(pytest.config.option.no_full_cov): + msg += " {}\n".format(s) terminalreporter.write(msg) diff --git a/test/mitmproxy/addons/test_stickycookie.py b/test/mitmproxy/addons/test_stickycookie.py index 157f2959..9092e09b 100644 --- a/test/mitmproxy/addons/test_stickycookie.py +++ b/test/mitmproxy/addons/test_stickycookie.py @@ -39,7 +39,6 @@ class TestStickyCookie: assert "cookie" not in f.request.headers f = f.copy() - f.reply.acked = False sc.request(f) assert f.request.headers["cookie"] == "foo=bar" diff --git a/test/mitmproxy/data/addonscripts/recorder.py b/test/mitmproxy/data/addonscripts/recorder.py index 5be88e5c..6b9b6ea8 100644 --- a/test/mitmproxy/data/addonscripts/recorder.py +++ b/test/mitmproxy/data/addonscripts/recorder.py @@ -1,5 +1,5 @@ from mitmproxy import controller -from mitmproxy import events +from mitmproxy import eventsequence from mitmproxy import ctx import sys @@ -11,7 +11,7 @@ class CallLogger: self.name = name def __getattr__(self, attr): - if attr in events.Events: + if attr in eventsequence.Events: def prox(*args, **kwargs): lg = (self.name, attr, args, kwargs) if attr != "log": diff --git a/test/mitmproxy/protocol/test_websocket.py b/test/mitmproxy/protocol/test_websocket.py index e42250e0..73ee8b35 100644 --- a/test/mitmproxy/protocol/test_websocket.py +++ b/test/mitmproxy/protocol/test_websocket.py @@ -179,16 +179,15 @@ class TestSimple(_WebSocketTest): assert isinstance(self.master.state.flows[1], WebSocketFlow) assert len(self.master.state.flows[1].messages) == 5 assert self.master.state.flows[1].messages[0].content == b'server-foobar' - assert self.master.state.flows[1].messages[0].type == 'text' + assert self.master.state.flows[1].messages[0].type == websockets.OPCODE.TEXT assert self.master.state.flows[1].messages[1].content == b'client-foobar' - assert self.master.state.flows[1].messages[1].type == 'text' + assert self.master.state.flows[1].messages[1].type == websockets.OPCODE.TEXT assert self.master.state.flows[1].messages[2].content == b'client-foobar' - assert self.master.state.flows[1].messages[2].type == 'text' + assert self.master.state.flows[1].messages[2].type == websockets.OPCODE.TEXT assert self.master.state.flows[1].messages[3].content == b'\xde\xad\xbe\xef' - assert self.master.state.flows[1].messages[3].type == 'binary' + assert self.master.state.flows[1].messages[3].type == websockets.OPCODE.BINARY assert self.master.state.flows[1].messages[4].content == b'\xde\xad\xbe\xef' - assert self.master.state.flows[1].messages[4].type == 'binary' - assert [m.info for m in self.master.state.flows[1].messages] + assert self.master.state.flows[1].messages[4].type == websockets.OPCODE.BINARY class TestSimpleTLS(_WebSocketTest): diff --git a/test/mitmproxy/test_eventsequence.py b/test/mitmproxy/test_eventsequence.py index 262df4b0..fe0f92b3 100644 --- a/test/mitmproxy/test_eventsequence.py +++ b/test/mitmproxy/test_eventsequence.py @@ -1,81 +1,57 @@ -from mitmproxy import events -import contextlib -from . import tservers - - -class Eventer: - def __init__(self, **handlers): - self.failure = None - self.called = [] - self.handlers = handlers - for i in events.Events - {"tick"}: - def mkprox(): - evt = i - - def prox(*args, **kwargs): - self.called.append(evt) - if evt in self.handlers: - try: - handlers[evt](*args, **kwargs) - except AssertionError as e: - self.failure = e - return prox - setattr(self, i, mkprox()) - - def fail(self): - pass - - -class SequenceTester: - @contextlib.contextmanager - def addon(self, addon): - self.master.addons.add(addon) - yield - self.master.addons.remove(addon) - if addon.failure: - raise addon.failure - - -class TestBasic(tservers.HTTPProxyTest, SequenceTester): - ssl = True - - def test_requestheaders(self): - - def hdrs(f): - assert f.request.headers - assert not f.request.content - - def req(f): - assert f.request.headers - assert f.request.content - - with self.addon(Eventer(requestheaders=hdrs, request=req)): - p = self.pathoc() - with p.connect(): - assert p.request("get:'/p/200':b@10").status_code == 200 - - def test_100_continue_fail(self): - e = Eventer() - with self.addon(e): - p = self.pathoc() - with p.connect(): - p.request( - """ - get:'/p/200' - h'expect'='100-continue' - h'content-length'='1000' - da - """ - ) - assert "requestheaders" in e.called - assert "responseheaders" not in e.called - - def test_connect(self): - e = Eventer() - with self.addon(e): - p = self.pathoc() - with p.connect(): - p.request("get:'/p/200:b@1'") - assert "http_connect" in e.called - assert e.called.count("requestheaders") == 1 - assert e.called.count("request") == 1 +import pytest + +from mitmproxy import eventsequence +from mitmproxy.test import tflow + + +@pytest.mark.parametrize("resp, err", [ + (False, False), + (True, False), + (False, True), + (True, True), +]) +def test_http_flow(resp, err): + f = tflow.tflow(resp=resp, err=err) + i = eventsequence.iterate(f) + assert next(i) == ("requestheaders", f) + assert next(i) == ("request", f) + if resp: + assert next(i) == ("responseheaders", f) + assert next(i) == ("response", f) + if err: + assert next(i) == ("error", f) + + +@pytest.mark.parametrize("err", [False, True]) +def test_websocket_flow(err): + f = tflow.twebsocketflow(err=err) + i = eventsequence.iterate(f) + assert next(i) == ("websocket_start", f) + assert len(f.messages) == 0 + assert next(i) == ("websocket_message", f) + assert len(f.messages) == 1 + assert next(i) == ("websocket_message", f) + assert len(f.messages) == 2 + if err: + assert next(i) == ("websocket_error", f) + assert next(i) == ("websocket_end", f) + + +@pytest.mark.parametrize("err", [False, True]) +def test_tcp_flow(err): + f = tflow.ttcpflow(err=err) + i = eventsequence.iterate(f) + assert next(i) == ("tcp_start", f) + assert len(f.messages) == 0 + assert next(i) == ("tcp_message", f) + assert len(f.messages) == 1 + assert next(i) == ("tcp_message", f) + assert len(f.messages) == 2 + if err: + assert next(i) == ("tcp_error", f) + assert next(i) == ("tcp_end", f) + + +def test_invalid(): + with pytest.raises(TypeError): + next(eventsequence.iterate(42)) diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py index 3b5d0ac1..65e6845f 100644 --- a/test/mitmproxy/test_flow.py +++ b/test/mitmproxy/test_flow.py @@ -10,6 +10,7 @@ from mitmproxy.exceptions import FlowReadException, Kill from mitmproxy import flow from mitmproxy import http from mitmproxy import connections +from mitmproxy import tcp from mitmproxy.proxy import ProxyConfig from mitmproxy.proxy.server import DummyServer from mitmproxy import master @@ -156,8 +157,99 @@ class TestHTTPFlow: assert f.response.raw_content == b"abarb" +class TestWebSocketFlow: + + def test_copy(self): + f = tflow.twebsocketflow() + f.get_state() + f2 = f.copy() + a = f.get_state() + b = f2.get_state() + del a["id"] + del b["id"] + del a["handshake_flow"]["id"] + del b["handshake_flow"]["id"] + assert a == b + assert not f == f2 + assert f is not f2 + + assert f.client_key == f2.client_key + assert f.client_protocol == f2.client_protocol + assert f.client_extensions == f2.client_extensions + assert f.server_accept == f2.server_accept + assert f.server_protocol == f2.server_protocol + assert f.server_extensions == f2.server_extensions + assert f.messages is not f2.messages + assert f.handshake_flow is not f2.handshake_flow + + for m in f.messages: + m2 = m.copy() + m2.set_state(m2.get_state()) + assert m is not m2 + assert m.get_state() == m2.get_state() + + f = tflow.twebsocketflow(err=True) + f2 = f.copy() + assert f is not f2 + assert f.handshake_flow is not f2.handshake_flow + assert f.error.get_state() == f2.error.get_state() + assert f.error is not f2.error + + def test_match(self): + f = tflow.twebsocketflow() + assert not flowfilter.match("~b nonexistent", f) + assert flowfilter.match(None, f) + assert not flowfilter.match("~b nonexistent", f) + + f = tflow.twebsocketflow(err=True) + assert flowfilter.match("~e", f) + + with pytest.raises(ValueError): + flowfilter.match("~", f) + + def test_repr(self): + f = tflow.twebsocketflow() + assert 'WebSocketFlow' in repr(f) + assert 'binary message: ' in repr(f.messages[0]) + assert 'text message: ' in repr(f.messages[1]) + + class TestTCPFlow: + def test_copy(self): + f = tflow.ttcpflow() + f.get_state() + f2 = f.copy() + a = f.get_state() + b = f2.get_state() + del a["id"] + del b["id"] + assert a == b + assert not f == f2 + assert f is not f2 + + assert f.messages is not f2.messages + + for m in f.messages: + assert m.get_state() + m2 = m.copy() + assert not m == m2 + assert m is not m2 + + a = m.get_state() + b = m2.get_state() + assert a == b + + m = tcp.TCPMessage(False, 'foo') + m.set_state(f.messages[0].get_state()) + assert m.timestamp == f.messages[0].timestamp + + f = tflow.ttcpflow(err=True) + f2 = f.copy() + assert f is not f2 + assert f.error.get_state() == f2.error.get_state() + assert f.error is not f2.error + def test_match(self): f = tflow.ttcpflow() assert not flowfilter.match("~b nonexistent", f) @@ -170,6 +262,11 @@ class TestTCPFlow: with pytest.raises(ValueError): flowfilter.match("~", f) + def test_repr(self): + f = tflow.ttcpflow() + assert 'TCPFlow' in repr(f) + assert '-> ' in repr(f.messages[0]) + class TestSerialize: @@ -308,11 +405,11 @@ class TestFlowMaster: fm.clientconnect(f.client_conn) f.request = http.HTTPRequest.wrap(mitmproxy.test.tutils.treq()) fm.request(f) - assert s.flow_count() == 1 + assert len(s.flows) == 1 f.response = http.HTTPResponse.wrap(mitmproxy.test.tutils.tresp()) fm.response(f) - assert s.flow_count() == 1 + assert len(s.flows) == 1 fm.clientdisconnect(f.client_conn) diff --git a/test/mitmproxy/test_server.py b/test/mitmproxy/test_server.py index dfd61540..9cd47cea 100644 --- a/test/mitmproxy/test_server.py +++ b/test/mitmproxy/test_server.py @@ -984,16 +984,16 @@ class TestUpstreamProxySSL( assert req.status_code == 418 # CONNECT from pathoc to chain[0], - assert self.proxy.tmaster.state.flow_count() == 1 + assert len(self.proxy.tmaster.state.flows) == 1 assert self.proxy.tmaster.state.flows[0].server_conn.via # request from pathoc to chain[0] # CONNECT from proxy to chain[1], - assert self.chain[0].tmaster.state.flow_count() == 1 + assert len(self.chain[0].tmaster.state.flows) == 1 assert self.chain[0].tmaster.state.flows[0].server_conn.via # request from proxy to chain[1] # request from chain[0] (regular proxy doesn't store CONNECTs) assert not self.chain[1].tmaster.state.flows[0].server_conn.via - assert self.chain[1].tmaster.state.flow_count() == 1 + assert len(self.chain[1].tmaster.state.flows) == 1 def test_change_upstream_proxy_connect(self): # skip chain[0]. @@ -1050,17 +1050,17 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxyTest): assert req.status_code == 418 # First request goes through all three proxies exactly once - assert self.proxy.tmaster.state.flow_count() == 1 - assert self.chain[0].tmaster.state.flow_count() == 1 - assert self.chain[1].tmaster.state.flow_count() == 1 + assert len(self.proxy.tmaster.state.flows) == 1 + assert len(self.chain[0].tmaster.state.flows) == 1 + assert len(self.chain[1].tmaster.state.flows) == 1 req = p.request("get:'/p/418:b\"content2\"'") assert req.status_code == 502 - assert self.proxy.tmaster.state.flow_count() == 2 - assert self.chain[0].tmaster.state.flow_count() == 2 + assert len(self.proxy.tmaster.state.flows) == 2 + assert len(self.chain[0].tmaster.state.flows) == 2 # Upstream sees two requests due to reconnection attempt - assert self.chain[1].tmaster.state.flow_count() == 3 + assert len(self.chain[1].tmaster.state.flows) == 3 assert not self.chain[1].tmaster.state.flows[-1].response assert not self.chain[1].tmaster.state.flows[-2].response diff --git a/test/mitmproxy/test_stateobject.py b/test/mitmproxy/test_stateobject.py index edec92c2..7b8e30d0 100644 --- a/test/mitmproxy/test_stateobject.py +++ b/test/mitmproxy/test_stateobject.py @@ -1,4 +1,5 @@ from typing import List +import pytest from mitmproxy.stateobject import StateObject @@ -67,3 +68,14 @@ def test_container_list(): assert len(copy.children) == 2 assert copy.children is not a.children assert copy.children[0] is not a.children[0] + + +def test_too_much_state(): + a = Container() + a.child = Child(42) + s = a.get_state() + s['foo'] = 'bar' + b = Container() + + with pytest.raises(RuntimeWarning): + b.set_state(s) diff --git a/test/mitmproxy/test_version.py b/test/mitmproxy/test_version.py new file mode 100644 index 00000000..f87b0851 --- /dev/null +++ b/test/mitmproxy/test_version.py @@ -0,0 +1,10 @@ +import runpy + +from mitmproxy import version + + +def test_version(capsys): + runpy.run_module('mitmproxy.version', run_name='__main__') + stdout, stderr = capsys.readouterr() + assert len(stdout) > 0 + assert stdout.strip() == version.VERSION diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py index 170a4917..c9a7e595 100644 --- a/test/mitmproxy/tservers.py +++ b/test/mitmproxy/tservers.py @@ -35,10 +35,6 @@ class TestState: # if f not in self.flows: # self.flows.append(f) - # FIXME: compat with old state - remove in favor of len(state.flows) - def flow_count(self): - return len(self.flows) - class TestMaster(master.Master): |