From baff47d66a4006d1bd14aa0b6a6a126bc1575f1a Mon Sep 17 00:00:00 2001 From: Bob Date: Tue, 13 Oct 2026 17:29:15 +0000 Subject: [PATCH 1/2] test(v4): poll Anvil startup instead of a fixed sleep Both v4 test modules started anvil and then slept 3 seconds before connecting. On a slow fork that is not enough and the matrix fails with ConnectionRefusedError; on a fast machine it wastes time; and when anvil dies at genesis the fixture still waits out the sleep and then fails on the connection instead of the real cause. Poll the RPC port for up to 60s, fail immediately with the exit code if the process exits first, and kill it on timeout. Git-Session-Id: 9d44438c-8705-491c-9930-947968df9a94 --- tests/test_uniswap4.py | 20 ++++++++++++++++++-- tests/test_v4_examples.py | 20 ++++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/tests/test_uniswap4.py b/tests/test_uniswap4.py index 22cbaf46..622177f3 100644 --- a/tests/test_uniswap4.py +++ b/tests/test_uniswap4.py @@ -1,10 +1,11 @@ import logging import os import shutil +import socket import subprocess +import time from contextlib import contextmanager from dataclasses import astuple, dataclass -from time import sleep from typing import Generator, List, Optional import pytest @@ -118,7 +119,22 @@ def anvil() -> Generator[AnvilInstance, None, None]: # Address #1 when anvil is run with `--wallet.seed test`, it starts with 1000 ETH eth_address = "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" eth_privkey = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6" - sleep(3) + # Poll until Anvil accepts connections. Fail immediately if the process + # exits (for example a genesis failure), rather than waiting out the deadline. + deadline = time.time() + 60 + while time.time() < deadline: + if p.poll() is not None: + raise RuntimeError( + f"Anvil exited before listening on port {port} (code {p.returncode})" + ) + try: + with socket.create_connection(("127.0.0.1", port), timeout=1): + break + except OSError: + time.sleep(0.5) + else: + p.kill() + raise RuntimeError(f"Anvil did not start on port {port} within 60s") yield AnvilInstance(f"http://127.0.0.1:{port}", eth_address, eth_privkey) p.kill() p.wait() diff --git a/tests/test_v4_examples.py b/tests/test_v4_examples.py index 0d0320cf..85279781 100644 --- a/tests/test_v4_examples.py +++ b/tests/test_v4_examples.py @@ -14,9 +14,10 @@ import os import shutil +import socket import subprocess +import time from dataclasses import dataclass -from time import sleep from typing import Generator import pytest @@ -98,7 +99,22 @@ def anvil() -> Generator[AnvilInstance, None, None]: # Anvil test account #2 (1000 ETH pre-funded) eth_address = "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" eth_privkey = "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" - sleep(3) + # Poll until Anvil accepts connections. Fail immediately if the process + # exits (for example a genesis failure), rather than waiting out the deadline. + deadline = time.time() + 60 + while time.time() < deadline: + if p.poll() is not None: + raise RuntimeError( + f"Anvil exited before listening on port {port} (code {p.returncode})" + ) + try: + with socket.create_connection(("127.0.0.1", port), timeout=1): + break + except OSError: + time.sleep(0.5) + else: + p.kill() + raise RuntimeError(f"Anvil did not start on port {port} within 60s") yield AnvilInstance(f"http://127.0.0.1:{port}", eth_address, eth_privkey) p.kill() p.wait() From 44371edef54766a70b3e70a7eb6c53983d62f70d Mon Sep 17 00:00:00 2001 From: Bob Date: Tue, 13 Oct 2026 17:44:29 +0000 Subject: [PATCH 2/2] fix(tests): reap timed-out Anvil and use monotonic deadlines Git-Session-Id: 0fead8e2-e37e-58e4-b158-56598d831c9d --- tests/test_uniswap4.py | 5 +++-- tests/test_v4_examples.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_uniswap4.py b/tests/test_uniswap4.py index 622177f3..864d4d44 100644 --- a/tests/test_uniswap4.py +++ b/tests/test_uniswap4.py @@ -121,8 +121,8 @@ def anvil() -> Generator[AnvilInstance, None, None]: eth_privkey = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6" # Poll until Anvil accepts connections. Fail immediately if the process # exits (for example a genesis failure), rather than waiting out the deadline. - deadline = time.time() + 60 - while time.time() < deadline: + deadline = time.monotonic() + 60 + while time.monotonic() < deadline: if p.poll() is not None: raise RuntimeError( f"Anvil exited before listening on port {port} (code {p.returncode})" @@ -134,6 +134,7 @@ def anvil() -> Generator[AnvilInstance, None, None]: time.sleep(0.5) else: p.kill() + p.wait() raise RuntimeError(f"Anvil did not start on port {port} within 60s") yield AnvilInstance(f"http://127.0.0.1:{port}", eth_address, eth_privkey) p.kill() diff --git a/tests/test_v4_examples.py b/tests/test_v4_examples.py index 85279781..003e7fce 100644 --- a/tests/test_v4_examples.py +++ b/tests/test_v4_examples.py @@ -101,8 +101,8 @@ def anvil() -> Generator[AnvilInstance, None, None]: eth_privkey = "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" # Poll until Anvil accepts connections. Fail immediately if the process # exits (for example a genesis failure), rather than waiting out the deadline. - deadline = time.time() + 60 - while time.time() < deadline: + deadline = time.monotonic() + 60 + while time.monotonic() < deadline: if p.poll() is not None: raise RuntimeError( f"Anvil exited before listening on port {port} (code {p.returncode})" @@ -114,6 +114,7 @@ def anvil() -> Generator[AnvilInstance, None, None]: time.sleep(0.5) else: p.kill() + p.wait() raise RuntimeError(f"Anvil did not start on port {port} within 60s") yield AnvilInstance(f"http://127.0.0.1:{port}", eth_address, eth_privkey) p.kill()