aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/bench/.gitignore1
-rw-r--r--test/bench/README.md56
-rwxr-xr-xtest/bench/backend3
-rw-r--r--test/bench/profiler.py25
-rwxr-xr-xtest/bench/simple.mitmproxy5
-rwxr-xr-xtest/bench/simple.traffic3
-rwxr-xr-x[-rw-r--r--]test/filename_matching.py2
-rwxr-xr-x[-rw-r--r--]test/individual_coverage.py2
-rw-r--r--test/mitmproxy/addons/test_onboarding.py4
-rw-r--r--test/mitmproxy/data/addonscripts/shutdown.py2
-rw-r--r--test/mitmproxy/net/http/test_request.py4
-rw-r--r--test/mitmproxy/proxy/protocol/test_http2.py6
-rw-r--r--test/mitmproxy/proxy/protocol/test_websocket.py21
-rw-r--r--test/mitmproxy/proxy/test_server.py86
-rw-r--r--test/mitmproxy/test_command.py25
-rw-r--r--test/mitmproxy/test_connections.py6
-rw-r--r--test/mitmproxy/test_controller.py79
-rw-r--r--test/mitmproxy/test_flow.py9
-rw-r--r--test/mitmproxy/test_fuzzing.py12
-rw-r--r--test/mitmproxy/tools/console/test_statusbar.py28
-rw-r--r--test/mitmproxy/tools/test_main.py10
-rw-r--r--test/mitmproxy/tservers.py60
-rw-r--r--test/mitmproxy/utils/test_human.py1
-rw-r--r--test/mitmproxy/utils/test_typecheck.py19
24 files changed, 263 insertions, 206 deletions
diff --git a/test/bench/.gitignore b/test/bench/.gitignore
new file mode 100644
index 00000000..1a06816d
--- /dev/null
+++ b/test/bench/.gitignore
@@ -0,0 +1 @@
+results
diff --git a/test/bench/README.md b/test/bench/README.md
new file mode 100644
index 00000000..05741c07
--- /dev/null
+++ b/test/bench/README.md
@@ -0,0 +1,56 @@
+
+This directory contains a set of tools for benchmarking and profiling mitmproxy.
+At the moment, this is simply to give developers a quick way to see the impact
+of their work. Eventually, this might grow into a performance dashboard with
+historical data, so we can track performance over time.
+
+
+# Setup
+
+Install the following tools:
+
+ go get -u github.com/rakyll/hey
+ go get github.com/cortesi/devd/cmd/devd
+
+You may also want to install snakeviz to make viewing profiles easier:
+
+ pip install snakeviz
+
+In one window, run the devd server:
+
+ ./backend
+
+
+# Running tests
+
+Each run consists of two files - a mitproxy invocation, and a traffic generator.
+Make sure the backend is started, then run the proxy:
+
+ ./simple.mitmproxy
+
+Now run the traffic generator:
+
+ ./simple.traffic
+
+After the run is done, quit the proxy with ctrl-c.
+
+
+# Reading results
+
+Results are placed in the ./results directory. You should see two files - a
+performance log from **hey**, and a profile. You can view the profile like so:
+
+ snakeviz ./results/simple.prof
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/bench/backend b/test/bench/backend
new file mode 100755
index 00000000..12a05d70
--- /dev/null
+++ b/test/bench/backend
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+devd -p 10001 . \ No newline at end of file
diff --git a/test/bench/profiler.py b/test/bench/profiler.py
new file mode 100644
index 00000000..9072e17d
--- /dev/null
+++ b/test/bench/profiler.py
@@ -0,0 +1,25 @@
+import cProfile
+from mitmproxy import ctx
+
+
+class Profile:
+ """
+ A simple profiler addon.
+ """
+ def __init__(self):
+ self.pr = cProfile.Profile()
+
+ def load(self, loader):
+ loader.add_option(
+ "profile_path",
+ str,
+ "/tmp/profile",
+ "Destination for the run profile, saved at exit"
+ )
+ self.pr.enable()
+
+ def done(self):
+ self.pr.dump_stats(ctx.options.profile_path)
+
+
+addons = [Profile()] \ No newline at end of file
diff --git a/test/bench/simple.mitmproxy b/test/bench/simple.mitmproxy
new file mode 100755
index 00000000..9de32981
--- /dev/null
+++ b/test/bench/simple.mitmproxy
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+mkdir -p results
+mitmdump -p 10002 --mode reverse:http://devd.io:10001 \
+ -s ./profiler.py --set profile_path=./results/simple.prof
diff --git a/test/bench/simple.traffic b/test/bench/simple.traffic
new file mode 100755
index 00000000..08200e05
--- /dev/null
+++ b/test/bench/simple.traffic
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+hey -disable-keepalive http://localhost:10002/profiler.py | tee ./results/simple.perf \ No newline at end of file
diff --git a/test/filename_matching.py b/test/filename_matching.py
index e74848d4..5f49725e 100644..100755
--- a/test/filename_matching.py
+++ b/test/filename_matching.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+
import os
import re
import glob
diff --git a/test/individual_coverage.py b/test/individual_coverage.py
index c975b4c8..097b290f 100644..100755
--- a/test/individual_coverage.py
+++ b/test/individual_coverage.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+
import io
import contextlib
import os
diff --git a/test/mitmproxy/addons/test_onboarding.py b/test/mitmproxy/addons/test_onboarding.py
index 810ddef1..0d99b1ff 100644
--- a/test/mitmproxy/addons/test_onboarding.py
+++ b/test/mitmproxy/addons/test_onboarding.py
@@ -4,6 +4,10 @@ from mitmproxy.addons import onboarding
from mitmproxy.test import taddons
from .. import tservers
+import asyncio
+import tornado.platform.asyncio
+asyncio.set_event_loop_policy(tornado.platform.asyncio.AnyThreadEventLoopPolicy())
+
class TestApp(tservers.HTTPProxyTest):
def addons(self):
diff --git a/test/mitmproxy/data/addonscripts/shutdown.py b/test/mitmproxy/data/addonscripts/shutdown.py
index 51a99b5c..3da4d03e 100644
--- a/test/mitmproxy/data/addonscripts/shutdown.py
+++ b/test/mitmproxy/data/addonscripts/shutdown.py
@@ -1,5 +1,5 @@
from mitmproxy import ctx
-def running():
+def tick():
ctx.master.shutdown()
diff --git a/test/mitmproxy/net/http/test_request.py b/test/mitmproxy/net/http/test_request.py
index ce49002c..ef581a91 100644
--- a/test/mitmproxy/net/http/test_request.py
+++ b/test/mitmproxy/net/http/test_request.py
@@ -351,10 +351,10 @@ class TestRequestUtils:
request.headers["Content-Type"] = "application/x-www-form-urlencoded"
assert list(request.urlencoded_form.items()) == [("foobar", "baz")]
request.raw_content = b"\xFF"
- assert len(request.urlencoded_form) == 0
+ assert len(request.urlencoded_form) == 1
def test_set_urlencoded_form(self):
- request = treq()
+ request = treq(content=b"\xec\xed")
request.urlencoded_form = [('foo', 'bar'), ('rab', 'oof')]
assert request.headers["Content-Type"] == "application/x-www-form-urlencoded"
assert request.content
diff --git a/test/mitmproxy/proxy/protocol/test_http2.py b/test/mitmproxy/proxy/protocol/test_http2.py
index d9aa03b4..13f28728 100644
--- a/test/mitmproxy/proxy/protocol/test_http2.py
+++ b/test/mitmproxy/proxy/protocol/test_http2.py
@@ -10,7 +10,6 @@ import h2
from mitmproxy import options
import mitmproxy.net
-from mitmproxy.addons import core
from ...net import tservers as net_tservers
from mitmproxy import exceptions
from mitmproxy.net.http import http1, http2
@@ -90,9 +89,7 @@ class _Http2TestBase:
@classmethod
def setup_class(cls):
cls.options = cls.get_options()
- tmaster = tservers.TestMaster(cls.options)
- tmaster.addons.add(core.Core())
- cls.proxy = tservers.ProxyThread(tmaster)
+ cls.proxy = tservers.ProxyThread(tservers.TestMaster, cls.options)
cls.proxy.start()
@classmethod
@@ -120,6 +117,7 @@ class _Http2TestBase:
def teardown(self):
if self.client:
self.client.close()
+ self.server.server.wait_for_silence()
def setup_connection(self):
self.client = mitmproxy.net.tcp.TCPClient(("127.0.0.1", self.proxy.port))
diff --git a/test/mitmproxy/proxy/protocol/test_websocket.py b/test/mitmproxy/proxy/protocol/test_websocket.py
index 661605b7..e5ed8e9d 100644
--- a/test/mitmproxy/proxy/protocol/test_websocket.py
+++ b/test/mitmproxy/proxy/protocol/test_websocket.py
@@ -3,10 +3,10 @@ import os
import struct
import tempfile
import traceback
+import time
from mitmproxy import options
from mitmproxy import exceptions
-from mitmproxy.addons import core
from mitmproxy.http import HTTPFlow
from mitmproxy.websocket import WebSocketFlow
@@ -52,9 +52,7 @@ class _WebSocketTestBase:
@classmethod
def setup_class(cls):
cls.options = cls.get_options()
- tmaster = tservers.TestMaster(cls.options)
- tmaster.addons.add(core.Core())
- cls.proxy = tservers.ProxyThread(tmaster)
+ cls.proxy = tservers.ProxyThread(tservers.TestMaster, cls.options)
cls.proxy.start()
@classmethod
@@ -163,7 +161,7 @@ class TestSimple(_WebSocketTest):
def websocket_start(self, f):
f.stream = streaming
- self.master.addons.add(Stream())
+ self.proxy.set_addons(Stream())
self.setup_connection()
frame = websockets.Frame.from_file(self.client.rfile)
@@ -204,7 +202,7 @@ class TestSimple(_WebSocketTest):
def websocket_message(self, f):
f.messages[-1].content = "foo"
- self.master.addons.add(Addon())
+ self.proxy.set_addons(Addon())
self.setup_connection()
frame = websockets.Frame.from_file(self.client.rfile)
@@ -235,7 +233,7 @@ class TestKillFlow(_WebSocketTest):
def websocket_message(self, f):
f.kill()
- self.master.addons.add(KillFlow())
+ self.proxy.set_addons(KillFlow())
self.setup_connection()
with pytest.raises(exceptions.TcpDisconnect):
@@ -329,7 +327,12 @@ class TestPong(_WebSocketTest):
assert frame.header.opcode == websockets.OPCODE.PONG
assert frame.payload == b'foobar'
- assert self.master.has_log("Pong Received from server", "info")
+ for i in range(20):
+ if self.master.has_log("Pong Received from server", "info"):
+ break
+ time.sleep(0.01)
+ else:
+ raise AssertionError("No pong seen")
class TestClose(_WebSocketTest):
@@ -405,7 +408,7 @@ class TestStreaming(_WebSocketTest):
def websocket_start(self, f):
f.stream = streaming
- self.master.addons.add(Stream())
+ self.proxy.set_addons(Stream())
self.setup_connection()
frame = None
diff --git a/test/mitmproxy/proxy/test_server.py b/test/mitmproxy/proxy/test_server.py
index 986dfb39..f594fb40 100644
--- a/test/mitmproxy/proxy/test_server.py
+++ b/test/mitmproxy/proxy/test_server.py
@@ -21,14 +21,6 @@ from pathod import pathod
from .. import tservers
from ...conftest import skip_appveyor
-"""
- Note that the choice of response code in these tests matters more than you
- might think. libcurl treats a 304 response code differently from, say, a
- 200 response code - it will correctly terminate a 304 response with no
- content-length header, whereas it will block forever waiting for content
- for a 200 response.
-"""
-
class CommonMixin:
@@ -237,28 +229,14 @@ class TestHTTP(tservers.HTTPProxyTest, CommonMixin):
p.request("get:'%s'" % response)
def test_reconnect(self):
- req = "get:'%s/p/200:b@1'" % self.server.urlbase
+ req = "get:'%s/p/200:b@1:da'" % self.server.urlbase
p = self.pathoc()
- class MockOnce:
- call = 0
-
- def mock_once(self, http1obj, req):
- self.call += 1
- if self.call == 1:
- raise exceptions.TcpDisconnect
- else:
- headers = http1.assemble_request_head(req)
- http1obj.server_conn.wfile.write(headers)
- http1obj.server_conn.wfile.flush()
-
with p.connect():
- with mock.patch("mitmproxy.proxy.protocol.http1.Http1Layer.send_request_headers",
- side_effect=MockOnce().mock_once, autospec=True):
- # Server disconnects while sending headers but mitmproxy reconnects
- resp = p.request(req)
- assert resp
- assert resp.status_code == 200
+ assert p.request(req)
+ # Server has disconnected. Mitmproxy should detect this, and reconnect.
+ assert p.request(req)
+ assert p.request(req)
def test_get_connection_switching(self):
req = "get:'%s/p/200:b@1'"
@@ -284,10 +262,9 @@ class TestHTTP(tservers.HTTPProxyTest, CommonMixin):
s = script.Script(
tutils.test_data.path("mitmproxy/data/addonscripts/stream_modify.py")
)
- self.master.addons.add(s)
+ self.set_addons(s)
d = self.pathod('200:b"foo"')
assert d.content == b"bar"
- self.master.addons.remove(s)
def test_first_line_rewrite(self):
"""
@@ -591,12 +568,11 @@ class TestTransparent(tservers.TransparentProxyTest, CommonMixin, TcpMixin):
s = script.Script(
tutils.test_data.path("mitmproxy/data/addonscripts/tcp_stream_modify.py")
)
- self.master.addons.add(s)
+ self.set_addons(s)
self._tcpproxy_on()
d = self.pathod('200:b"foo"')
self._tcpproxy_off()
assert d.content == b"bar"
- self.master.addons.remove(s)
class TestTransparentSSL(tservers.TransparentProxyTest, CommonMixin, TcpMixin):
@@ -739,7 +715,7 @@ class TestRedirectRequest(tservers.HTTPProxyTest):
This test verifies that the original destination is restored for the third request.
"""
- self.proxy.tmaster.addons.add(ARedirectRequest(self.server2.port))
+ self.set_addons(ARedirectRequest(self.server2.port))
p = self.pathoc()
with p.connect():
@@ -778,7 +754,7 @@ class AStreamRequest:
class TestStreamRequest(tservers.HTTPProxyTest):
def test_stream_simple(self):
- self.proxy.tmaster.addons.add(AStreamRequest())
+ self.set_addons(AStreamRequest())
p = self.pathoc()
with p.connect():
# a request with 100k of data but without content-length
@@ -787,7 +763,7 @@ class TestStreamRequest(tservers.HTTPProxyTest):
assert len(r1.content) > 100000
def test_stream_multiple(self):
- self.proxy.tmaster.addons.add(AStreamRequest())
+ self.set_addons(AStreamRequest())
p = self.pathoc()
with p.connect():
# simple request with streaming turned on
@@ -799,7 +775,7 @@ class TestStreamRequest(tservers.HTTPProxyTest):
assert r1.status_code == 201
def test_stream_chunked(self):
- self.proxy.tmaster.addons.add(AStreamRequest())
+ self.set_addons(AStreamRequest())
connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connection.connect(("127.0.0.1", self.proxy.port))
fconn = connection.makefile("rb")
@@ -828,7 +804,7 @@ class AFakeResponse:
class TestFakeResponse(tservers.HTTPProxyTest):
def test_fake(self):
- self.proxy.tmaster.addons.add(AFakeResponse())
+ self.set_addons(AFakeResponse())
f = self.pathod("200")
assert "header-response" in f.headers
@@ -844,7 +820,7 @@ class TestServerConnect(tservers.HTTPProxyTest):
def test_unnecessary_serverconnect(self):
"""A replayed/fake response with no upstream_cert should not connect to an upstream server"""
- self.proxy.tmaster.addons.add(AFakeResponse())
+ self.set_addons(AFakeResponse())
assert self.pathod("200").status_code == 200
assert not self.proxy.tmaster.has_log("serverconnect")
@@ -857,7 +833,7 @@ class AKillRequest:
class TestKillRequest(tservers.HTTPProxyTest):
def test_kill(self):
- self.proxy.tmaster.addons.add(AKillRequest())
+ self.set_addons(AKillRequest())
with pytest.raises(exceptions.HttpReadDisconnect):
self.pathod("200")
# Nothing should have hit the server
@@ -871,7 +847,7 @@ class AKillResponse:
class TestKillResponse(tservers.HTTPProxyTest):
def test_kill(self):
- self.proxy.tmaster.addons.add(AKillResponse())
+ self.set_addons(AKillResponse())
with pytest.raises(exceptions.HttpReadDisconnect):
self.pathod("200")
# The server should have seen a request
@@ -894,7 +870,7 @@ class AIncomplete:
class TestIncompleteResponse(tservers.HTTPProxyTest):
def test_incomplete(self):
- self.proxy.tmaster.addons.add(AIncomplete())
+ self.set_addons(AIncomplete())
assert self.pathod("200").status_code == 502
@@ -977,7 +953,7 @@ class TestUpstreamProxySSL(
def test_change_upstream_proxy_connect(self):
# skip chain[0].
- self.proxy.tmaster.addons.add(
+ self.set_addons(
UpstreamProxyChanger(
("127.0.0.1", self.chain[1].port)
)
@@ -996,8 +972,8 @@ class TestUpstreamProxySSL(
Client <- HTTPS -> Proxy <- HTTP -> Proxy <- HTTPS -> Server
"""
- self.proxy.tmaster.addons.add(RewriteToHttp())
- self.chain[1].tmaster.addons.add(RewriteToHttps())
+ self.set_addons(RewriteToHttp())
+ self.chain[1].set_addons(RewriteToHttps())
p = self.pathoc()
with p.connect():
resp = p.request("get:'/p/418'")
@@ -1055,24 +1031,8 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxyTest):
request again.
"""
- class MockOnce:
- call = 0
-
- def mock_once(self, http1obj, req):
- self.call += 1
-
- if self.call == 2:
- headers = http1.assemble_request_head(req)
- http1obj.server_conn.wfile.write(headers)
- http1obj.server_conn.wfile.flush()
- raise exceptions.TcpDisconnect
- else:
- headers = http1.assemble_request_head(req)
- http1obj.server_conn.wfile.write(headers)
- http1obj.server_conn.wfile.flush()
-
- self.chain[0].tmaster.addons.add(RequestKiller([1, 2]))
- self.chain[1].tmaster.addons.add(RequestKiller([1]))
+ self.chain[0].set_addons(RequestKiller([1, 2]))
+ self.chain[1].set_addons(RequestKiller([1]))
p = self.pathoc()
with p.connect():
@@ -1085,9 +1045,7 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxyTest):
assert len(self.chain[0].tmaster.state.flows) == 1
assert len(self.chain[1].tmaster.state.flows) == 1
- with mock.patch("mitmproxy.proxy.protocol.http1.Http1Layer.send_request_headers",
- side_effect=MockOnce().mock_once, autospec=True):
- req = p.request("get:'/p/418:b\"content2\"'")
+ req = p.request("get:'/p/418:b\"content2\"'")
assert req.status_code == 502
diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py
index e2b80753..3d0a43f8 100644
--- a/test/mitmproxy/test_command.py
+++ b/test/mitmproxy/test_command.py
@@ -309,6 +309,31 @@ class TDec:
pass
+class TAttr:
+ def __getattr__(self, item):
+ raise IOError
+
+
+class TCmds(TAttr):
+ def __init__(self):
+ self.TAttr = TAttr()
+
+ @command.command("empty")
+ def empty(self) -> None:
+ pass
+
+
+def test_collect_commands():
+ """
+ This tests for the error thrown by hasattr()
+ """
+ with taddons.context() as tctx:
+ c = command.CommandManager(tctx.master)
+ a = TCmds()
+ c.collect_commands(a)
+ assert "empty" in c.commands
+
+
def test_decorator():
with taddons.context() as tctx:
c = command.CommandManager(tctx.master)
diff --git a/test/mitmproxy/test_connections.py b/test/mitmproxy/test_connections.py
index 00cdbc87..845a9043 100644
--- a/test/mitmproxy/test_connections.py
+++ b/test/mitmproxy/test_connections.py
@@ -38,6 +38,9 @@ class TestClientConnection:
assert 'ALPN' not in repr(c)
assert 'TLS' in repr(c)
+ c.address = None
+ assert repr(c)
+
def test_tls_established_property(self):
c = tflow.tclient_conn()
c.tls_established = True
@@ -110,6 +113,9 @@ class TestServerConnection:
c.tls_established = False
assert 'TLS' not in repr(c)
+ c.address = None
+ assert repr(c)
+
def test_tls_established_property(self):
c = tflow.tserver_conn()
c.tls_established = True
diff --git a/test/mitmproxy/test_controller.py b/test/mitmproxy/test_controller.py
index e840380a..f7c64ed9 100644
--- a/test/mitmproxy/test_controller.py
+++ b/test/mitmproxy/test_controller.py
@@ -1,82 +1,31 @@
-from threading import Thread, Event
-from unittest.mock import Mock
+import asyncio
import queue
import pytest
from mitmproxy.exceptions import Kill, ControlException
from mitmproxy import controller
-from mitmproxy import master
-from mitmproxy import proxy
from mitmproxy.test import taddons
-class TMsg:
- pass
+@pytest.mark.asyncio
+async def test_master():
+ class TMsg:
+ pass
+ class tAddon:
+ def log(self, _):
+ ctx.master.should_exit.set()
-class TestMaster:
- def test_simple(self):
- class tAddon:
- def log(self, _):
- ctx.master.should_exit.set()
+ with taddons.context(tAddon()) as ctx:
+ assert not ctx.master.should_exit.is_set()
- with taddons.context() as ctx:
- ctx.master.addons.add(tAddon())
- assert not ctx.master.should_exit.is_set()
+ async def test():
msg = TMsg()
msg.reply = controller.DummyReply()
- ctx.master.event_queue.put(("log", msg))
- ctx.master.run()
- assert ctx.master.should_exit.is_set()
-
- def test_server_simple(self):
- m = master.Master(None)
- m.server = proxy.DummyServer()
- m.start()
- m.shutdown()
- m.start()
- m.shutdown()
+ await ctx.master.channel.tell("log", msg)
-
-class TestServerThread:
- def test_simple(self):
- m = Mock()
- t = master.ServerThread(m)
- t.run()
- assert m.serve_forever.called
-
-
-class TestChannel:
- def test_tell(self):
- q = queue.Queue()
- channel = controller.Channel(q, Event())
- m = Mock(name="test_tell")
- channel.tell("test", m)
- assert q.get() == ("test", m)
- assert m.reply
-
- def test_ask_simple(self):
- q = queue.Queue()
-
- def reply():
- m, obj = q.get()
- assert m == "test"
- obj.reply.send(42)
- obj.reply.take()
- obj.reply.commit()
-
- Thread(target=reply).start()
-
- channel = controller.Channel(q, Event())
- assert channel.ask("test", Mock(name="test_ask_simple")) == 42
-
- def test_ask_shutdown(self):
- q = queue.Queue()
- done = Event()
- done.set()
- channel = controller.Channel(q, done)
- with pytest.raises(Kill):
- channel.ask("test", Mock(name="test_ask_shutdown"))
+ asyncio.ensure_future(test())
+ assert not ctx.master.should_exit.is_set()
class TestReply:
diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py
index 8cc11a16..4042de5b 100644
--- a/test/mitmproxy/test_flow.py
+++ b/test/mitmproxy/test_flow.py
@@ -7,7 +7,7 @@ import mitmproxy.io
from mitmproxy import flowfilter
from mitmproxy import options
from mitmproxy.io import tnetstring
-from mitmproxy.exceptions import FlowReadException, ReplayException, ControlException
+from mitmproxy.exceptions import FlowReadException, ReplayException
from mitmproxy import flow
from mitmproxy import http
from mitmproxy.net import http as net_http
@@ -169,9 +169,10 @@ class TestFlowMaster:
f.error = flow.Error("msg")
fm.addons.handle_lifecycle("error", f)
- fm.tell("foo", f)
- with pytest.raises(ControlException):
- fm.tick(timeout=1)
+ # FIXME: This no longer works, because we consume on the main loop.
+ # fm.tell("foo", f)
+ # with pytest.raises(ControlException):
+ # fm.addons.trigger("unknown")
fm.shutdown()
diff --git a/test/mitmproxy/test_fuzzing.py b/test/mitmproxy/test_fuzzing.py
index 905ba1cd..57d0ca55 100644
--- a/test/mitmproxy/test_fuzzing.py
+++ b/test/mitmproxy/test_fuzzing.py
@@ -25,14 +25,4 @@ class TestFuzzy(tservers.HTTPProxyTest):
p = self.pathoc()
with p.connect():
resp = p.request(req % self.server.port)
- assert resp.status_code == 400
-
- # def test_invalid_upstream(self):
- # req = r"get:'http://localhost:%s/p/200:i10,\x27+\x27'"
- # p = self.pathoc()
- # assert p.request(req % self.server.port).status_code == 502
-
- # def test_upstream_disconnect(self):
- # req = r'200:d0'
- # p = self.pathod(req)
- # assert p.status_code == 502
+ assert resp.status_code == 400 \ No newline at end of file
diff --git a/test/mitmproxy/tools/console/test_statusbar.py b/test/mitmproxy/tools/console/test_statusbar.py
index db8a63a7..108f238e 100644
--- a/test/mitmproxy/tools/console/test_statusbar.py
+++ b/test/mitmproxy/tools/console/test_statusbar.py
@@ -1,3 +1,5 @@
+import pytest
+
from mitmproxy import options
from mitmproxy.tools.console import statusbar, master
@@ -31,3 +33,29 @@ def test_statusbar(monkeypatch):
bar = statusbar.StatusBar(m) # this already causes a redraw
assert bar.ib._w
+
+
+@pytest.mark.parametrize("message,ready_message", [
+ ("", [(None, ""), ("warn", "")]),
+ (("info", "Line fits into statusbar"), [("info", "Line fits into statusbar"),
+ ("warn", "")]),
+ ("Line doesn't fit into statusbar", [(None, "Line doesn'\u2026"),
+ ("warn", "(more in eventlog)")]),
+ (("alert", "Two lines.\nFirst fits"), [("alert", "Two lines."),
+ ("warn", "(more in eventlog)")]),
+ ("Two long lines\nFirst doesn't fit", [(None, "Two long li\u2026"),
+ ("warn", "(more in eventlog)")])
+])
+def test_shorten_message(message, ready_message):
+ o = options.Options()
+ m = master.ConsoleMaster(o)
+ ab = statusbar.ActionBar(m)
+ assert ab.shorten_message(message, max_width=30) == ready_message
+
+
+def test_shorten_message_narrow():
+ o = options.Options()
+ m = master.ConsoleMaster(o)
+ ab = statusbar.ActionBar(m)
+ shorten_msg = ab.shorten_message("error", max_width=4)
+ assert shorten_msg == [(None, "\u2026"), ("warn", "(more in eventlog)")]
diff --git a/test/mitmproxy/tools/test_main.py b/test/mitmproxy/tools/test_main.py
index 88e2fe86..a514df74 100644
--- a/test/mitmproxy/tools/test_main.py
+++ b/test/mitmproxy/tools/test_main.py
@@ -1,19 +1,25 @@
+import pytest
+
from mitmproxy.test import tutils
from mitmproxy.tools import main
shutdown_script = tutils.test_data.path("mitmproxy/data/addonscripts/shutdown.py")
-def test_mitmweb():
+@pytest.mark.asyncio
+async def test_mitmweb():
main.mitmweb([
"--no-web-open-browser",
"-q",
+ "-p", "0",
"-s", shutdown_script
])
-def test_mitmdump():
+@pytest.mark.asyncio
+async def test_mitmdump():
main.mitmdump([
"-q",
+ "-p", "0",
"-s", shutdown_script
])
diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py
index 0040b023..2d102a5d 100644
--- a/test/mitmproxy/tservers.py
+++ b/test/mitmproxy/tservers.py
@@ -2,7 +2,9 @@ import os.path
import threading
import tempfile
import sys
+import time
from unittest import mock
+import asyncio
import mitmproxy.platform
from mitmproxy.addons import core
@@ -12,6 +14,7 @@ from mitmproxy import controller
from mitmproxy import options
from mitmproxy import exceptions
from mitmproxy import io
+from mitmproxy.utils import human
import pathod.test
import pathod.pathoc
@@ -62,11 +65,6 @@ class TestState:
if f not in self.flows:
self.flows.append(f)
- # TODO: add TCP support?
- # def tcp_start(self, f):
- # if f not in self.flows:
- # self.flows.append(f)
-
class TestMaster(taddons.RecordingMaster):
@@ -90,13 +88,12 @@ class TestMaster(taddons.RecordingMaster):
class ProxyThread(threading.Thread):
- def __init__(self, tmaster):
+ def __init__(self, masterclass, options):
threading.Thread.__init__(self)
- self.tmaster = tmaster
- self.name = "ProxyThread (%s:%s)" % (
- tmaster.server.address[0],
- tmaster.server.address[1],
- )
+ self.masterclass = masterclass
+ self.options = options
+ self.tmaster = None
+ self.event_loop = None
controller.should_exit = False
@property
@@ -107,11 +104,27 @@ class ProxyThread(threading.Thread):
def tlog(self):
return self.tmaster.logs
+ def shutdown(self):
+ self.tmaster.shutdown()
+
def run(self):
+ self.event_loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(self.event_loop)
+ self.tmaster = self.masterclass(self.options)
+ self.tmaster.addons.add(core.Core())
+ self.name = "ProxyThread (%s)" % human.format_address(self.tmaster.server.address)
self.tmaster.run()
- def shutdown(self):
- self.tmaster.shutdown()
+ def set_addons(self, *addons):
+ self.tmaster.reset(addons)
+ self.tmaster.addons.trigger("tick")
+
+ def start(self):
+ super().start()
+ while True:
+ if self.tmaster:
+ break
+ time.sleep(0.01)
class ProxyTestBase:
@@ -132,9 +145,7 @@ class ProxyTestBase:
ssloptions=cls.ssloptions)
cls.options = cls.get_options()
- tmaster = cls.masterclass(cls.options)
- tmaster.addons.add(core.Core())
- cls.proxy = ProxyThread(tmaster)
+ cls.proxy = ProxyThread(cls.masterclass, cls.options)
cls.proxy.start()
@classmethod
@@ -173,6 +184,9 @@ class ProxyTestBase:
ssl_insecure=True,
)
+ def set_addons(self, *addons):
+ self.proxy.set_addons(*addons)
+
def addons(self):
"""
Can be over-ridden to add a standard set of addons to tests.
@@ -327,8 +341,7 @@ class SocksModeTest(HTTPProxyTest):
return opts
-class ChainProxyTest(ProxyTestBase):
-
+class HTTPUpstreamProxyTest(HTTPProxyTest):
"""
Chain three instances of mitmproxy in a row to test upstream mode.
Proxy order is cls.proxy -> cls.chain[0] -> cls.chain[1]
@@ -344,11 +357,12 @@ class ChainProxyTest(ProxyTestBase):
cls.chain = []
for _ in range(cls.n):
opts = cls.get_options()
- tmaster = cls.masterclass(opts)
- tmaster.addons.add(core.Core())
- proxy = ProxyThread(tmaster)
+ proxy = ProxyThread(cls.masterclass, opts)
proxy.start()
cls.chain.insert(0, proxy)
+ while True:
+ if proxy.event_loop and proxy.event_loop.is_running():
+ break
super().setup_class()
@@ -372,7 +386,3 @@ class ChainProxyTest(ProxyTestBase):
mode="upstream:" + s,
)
return opts
-
-
-class HTTPUpstreamProxyTest(ChainProxyTest, HTTPProxyTest):
- pass
diff --git a/test/mitmproxy/utils/test_human.py b/test/mitmproxy/utils/test_human.py
index 947cfa4a..faf35f72 100644
--- a/test/mitmproxy/utils/test_human.py
+++ b/test/mitmproxy/utils/test_human.py
@@ -56,3 +56,4 @@ def test_format_address():
assert human.format_address(("example.com", "54010")) == "example.com:54010"
assert human.format_address(("::", "8080")) == "*:8080"
assert human.format_address(("0.0.0.0", "8080")) == "*:8080"
+ assert human.format_address(None) == "<no address>"
diff --git a/test/mitmproxy/utils/test_typecheck.py b/test/mitmproxy/utils/test_typecheck.py
index 9cb4334e..85713e14 100644
--- a/test/mitmproxy/utils/test_typecheck.py
+++ b/test/mitmproxy/utils/test_typecheck.py
@@ -1,6 +1,5 @@
import io
import typing
-from unittest import mock
import pytest
from mitmproxy.utils import typecheck
@@ -32,12 +31,6 @@ def test_check_union():
with pytest.raises(TypeError):
typecheck.check_option_type("foo", [], typing.Union[int, str])
- # Python 3.5 only defines __union_params__
- m = mock.Mock()
- m.__str__ = lambda self: "typing.Union"
- m.__union_params__ = (int,)
- typecheck.check_option_type("foo", 42, m)
-
def test_check_tuple():
typecheck.check_option_type("foo", (42, "42"), typing.Tuple[int, str])
@@ -50,12 +43,6 @@ def test_check_tuple():
with pytest.raises(TypeError):
typecheck.check_option_type("foo", ("42", 42), typing.Tuple[int, str])
- # Python 3.5 only defines __tuple_params__
- m = mock.Mock()
- m.__str__ = lambda self: "typing.Tuple"
- m.__tuple_params__ = (int, str)
- typecheck.check_option_type("foo", (42, "42"), m)
-
def test_check_sequence():
typecheck.check_option_type("foo", [10], typing.Sequence[int])
@@ -68,12 +55,6 @@ def test_check_sequence():
with pytest.raises(TypeError):
typecheck.check_option_type("foo", "foo", typing.Sequence[str])
- # Python 3.5 only defines __parameters__
- m = mock.Mock()
- m.__str__ = lambda self: "typing.Sequence"
- m.__parameters__ = (int,)
- typecheck.check_option_type("foo", [10], m)
-
def test_check_io():
typecheck.check_option_type("foo", io.StringIO(), typing.IO[str])