Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 42 additions & 4 deletions tests/assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,18 +666,23 @@ def assert_failed_proxy_auth_request(stdout):
)


def wait_for_file(path, timeout=10.0, poll_interval=0.1):
import glob
def wait_for(condition, timeout=10.0, interval=0.1):
import time

deadline = time.time() + timeout
while time.time() < deadline:
if glob.glob(str(path)):
if condition():
return True
time.sleep(poll_interval)
time.sleep(interval)
return False


def wait_for_file(path, timeout=10.0, interval=0.1):
import glob

return wait_for(lambda: glob.glob(str(path)), timeout, interval)


def wait_for_daemon(tmp_path, started_at, timeout=None):
import time

Expand All @@ -704,3 +709,36 @@ def wait_for_daemon(tmp_path, started_at, timeout=None):
time.sleep(0.1)

return False


def wait_for_stdout(process, predicate, timeout=10):
"""Read a process's stdout in a background thread, polling until
*predicate(text)* is satisfied or *timeout* expires.

Returns the full stdout text collected up to that point.
The caller should terminate/kill the process after this returns
to close the pipe and let the background thread exit.
"""
import threading

lines = []

def reader():
try:
for line in iter(process.stdout.readline, b""):
lines.append(line)
except ValueError:
pass

thread = threading.Thread(target=reader, daemon=True)
thread.start()

def stdout_text():
return b"".join(lines).decode("utf-8", errors="replace")

if not wait_for(lambda: predicate(stdout_text()), timeout):
raise TimeoutError(
f"Predicate not satisfied within {timeout}s.\n"
f"Collected output:\n{stdout_text()}"
)
return stdout_text()
38 changes: 28 additions & 10 deletions tests/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import pytest

from tests.assertions import assert_no_proxy_request
from tests.assertions import assert_no_proxy_request, wait_for, wait_for_stdout


@contextlib.contextmanager
Expand Down Expand Up @@ -98,8 +98,14 @@ def start_mitmdump(
if proxy_auth:
proxy_command += ["-v", "--proxyauth", proxy_auth]

proxy_env = os.environ.copy()
proxy_env["PYTHONUNBUFFERED"] = "1"

proxy_process = subprocess.Popen(
proxy_command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
proxy_command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
env=proxy_env,
)

try:
Expand All @@ -126,20 +132,32 @@ def proxy_test_finally(
proxy_process,
proxy_log_assert=assert_no_proxy_request,
expected_proxy_logsize=None,
timeout=10,
):
if expected_proxy_logsize is None:
expected_proxy_logsize = expected_httpserver_logsize

if proxy_process:
# Give mitmdump some time to get a response from the mock server
time.sleep(0.5)
proxy_process.terminate()
stdout_bytes, _ = proxy_process.communicate()
stdout = stdout_bytes.decode("utf-8", errors="replace")
try:
# Give mitmdump some time to get a response from the mock server
assert wait_for(
lambda: len(httpserver.log) >= expected_httpserver_logsize, timeout
)
Comment thread
jpnurmi marked this conversation as resolved.

if expected_proxy_logsize != 0:
# request passed through successfully
wait_for_stdout(
proxy_process,
lambda text: "POST" in text and "200 OK" in text,
timeout,
)
finally:
proxy_process.terminate()
proxy_process.wait(timeout=timeout)

if expected_proxy_logsize == 0:
# don't expect any incoming requests to make it through the proxy
stdout_bytes, _ = proxy_process.communicate(timeout=timeout)
stdout = stdout_bytes.decode("utf-8", errors="replace")
proxy_log_assert(stdout)
else:
# request passed through successfully
assert "POST" in stdout and "200 OK" in stdout
assert len(httpserver.log) == expected_httpserver_logsize
10 changes: 1 addition & 9 deletions tests/test_dotnet_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pytest

from tests import adb
from tests.assertions import wait_for
from tests.conditions import is_android, is_arm32, is_tsan, is_x86, is_asan

project_fixture_path = pathlib.Path("tests/fixtures/dotnet_signal")
Expand Down Expand Up @@ -281,15 +282,6 @@ def test_aot_signals_inproc(cmake):
ANDROID_PACKAGE = "io.sentry.ndk.dotnet.signal.test"


def wait_for(condition, timeout=10, interval=0.5):
start = time.time()
while time.time() - start < timeout:
if condition():
return True
time.sleep(interval)
return condition()


def run_android(args=None, strategy=None, reinit=False, timeout=30):
if args is None:
args = []
Expand Down
Loading