Skip to content

Commit bb2d795

Browse files
author
Roman
committed
ruff
1 parent 6caa770 commit bb2d795

File tree

1 file changed

+114
-40
lines changed

1 file changed

+114
-40
lines changed

tests/e2e_tests/conftest.py

Lines changed: 114 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,82 @@
11
import os
22
import re
3+
import shutil
34
import shlex
45
import signal
56
import subprocess
6-
import time
7+
import sys
78
import threading
9+
import time
10+
from bittensor.utils.btlogging import logging
811

912
import pytest
1013
from async_substrate_interface import SubstrateInterface
1114

1215
from bittensor.core.async_subtensor import AsyncSubtensor
1316
from bittensor.core.subtensor import Subtensor
14-
from bittensor.utils.btlogging import logging
1517
from tests.e2e_tests.utils.e2e_test_utils import (
1618
Templates,
1719
setup_wallet,
1820
)
1921

2022

21-
# Fixture for setting up and tearing down a localnet.sh chain between tests
23+
def wait_for_node_start(process, timestamp=None):
24+
"""Waits for node to start in the docker."""
25+
while True:
26+
line = process.stdout.readline()
27+
if not line:
28+
break
29+
30+
timestamp = timestamp or int(time.time())
31+
print(line.strip())
32+
# 10 min as timeout
33+
if int(time.time()) - timestamp > 20 * 30:
34+
print("Subtensor not started in time")
35+
raise TimeoutError
36+
37+
pattern = re.compile(r"Imported #1")
38+
if pattern.search(line):
39+
print("Node started!")
40+
break
41+
42+
# Start a background reader after pattern is found
43+
# To prevent the buffer filling up
44+
def read_output():
45+
while True:
46+
if not process.stdout.readline():
47+
break
48+
49+
reader_thread = threading.Thread(target=read_output, daemon=True)
50+
reader_thread.start()
51+
52+
2253
@pytest.fixture(scope="function")
2354
def local_chain(request):
24-
param = request.param if hasattr(request, "param") else None
55+
"""Determines whether to run the localnet.sh script in a subprocess or a Docker container."""
56+
args = request.param if hasattr(request, "param") else None
57+
params = "" if args is None else f"{args}"
58+
if shutil.which("docker"):
59+
yield from docker_runner(params)
60+
return
61+
62+
if sys.platform.startswith("linux"):
63+
docker_commend = (
64+
"Install docker with command "
65+
"[blue]sudo apt-get update && sudo apt-get install docker.io -y[/blue]"
66+
)
67+
elif sys.platform == "darwin":
68+
docker_commend = "Install docker with command [blue]brew install docker[/blue]"
69+
else:
70+
docker_commend = "[blue]Unknown OS, install Docker manually: https://docs.docker.com/get-docker/[/blue]"
71+
72+
logging.warning("Docker not found in the operating system!")
73+
logging.warning(docker_commend)
74+
logging.warning("Tests are run in legacy mode.")
75+
yield from legacy_runner(request)
76+
77+
78+
def legacy_runner(params):
79+
"""Runs the localnet.sh script in a subprocess and waits for it to start."""
2580
# Get the environment variable for the script path
2681
script_path = os.getenv("LOCALNET_SH_PATH")
2782

@@ -31,54 +86,20 @@ def local_chain(request):
3186
pytest.skip("LOCALNET_SH_PATH environment variable is not set.")
3287

3388
# Check if param is None, and handle it accordingly
34-
args = "" if param is None else f"{param}"
89+
args = "" if params is None else f"{params}"
3590

3691
# Compile commands to send to process
3792
cmds = shlex.split(f"{script_path} {args}")
3893

39-
# Pattern match indicates node is compiled and ready
40-
pattern = re.compile(r"Imported #1")
41-
timestamp = int(time.time())
42-
43-
def wait_for_node_start(process, pattern):
44-
while True:
45-
line = process.stdout.readline()
46-
if not line:
47-
break
48-
49-
print(line.strip())
50-
# 10 min as timeout
51-
if int(time.time()) - timestamp > 20 * 60:
52-
print("Subtensor not started in time")
53-
raise TimeoutError
54-
if pattern.search(line):
55-
print("Node started!")
56-
break
57-
58-
# Start a background reader after pattern is found
59-
# To prevent the buffer filling up
60-
def read_output():
61-
while True:
62-
line = process.stdout.readline()
63-
if not line:
64-
break
65-
66-
reader_thread = threading.Thread(target=read_output, daemon=True)
67-
reader_thread.start()
68-
69-
env = os.environ.copy()
70-
env["BUILD_BINARY"] = "0"
71-
7294
with subprocess.Popen(
7395
cmds,
74-
env=env,
7596
start_new_session=True,
7697
stderr=subprocess.STDOUT,
7798
stdout=subprocess.PIPE,
7899
text=True,
79100
) as process:
80101
try:
81-
wait_for_node_start(process, pattern)
102+
wait_for_node_start(process)
82103
except TimeoutError:
83104
raise
84105
else:
@@ -95,6 +116,59 @@ def read_output():
95116
process.wait()
96117

97118

119+
def docker_runner(params):
120+
"""Starts a Docker container before tests and gracefully terminates it after."""
121+
122+
container_name = f"test_local_chain_{str(time.time()).replace(".", "_")}"
123+
image_name = "ghcr.io/opentensor/subtensor-localnet:latest"
124+
125+
# Command to start container
126+
cmds = [
127+
"docker",
128+
"run",
129+
"--rm",
130+
"--name",
131+
container_name,
132+
"-p",
133+
"9944:9944",
134+
"-p",
135+
"9945:9945",
136+
image_name,
137+
params,
138+
]
139+
140+
# Start container
141+
with subprocess.Popen(
142+
cmds,
143+
stdout=subprocess.PIPE,
144+
stderr=subprocess.PIPE,
145+
text=True,
146+
start_new_session=True,
147+
) as process:
148+
try:
149+
try:
150+
wait_for_node_start(process, int(time.time()))
151+
except TimeoutError:
152+
raise
153+
154+
result = subprocess.run(
155+
["docker", "ps", "-q", "-f", f"name={container_name}"],
156+
capture_output=True,
157+
text=True,
158+
)
159+
if not result.stdout.strip():
160+
raise RuntimeError("Docker container failed to start.")
161+
162+
yield SubstrateInterface(url="ws://127.0.0.1:9944")
163+
164+
finally:
165+
try:
166+
subprocess.run(["docker", "kill", container_name])
167+
process.wait()
168+
except subprocess.TimeoutExpired:
169+
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
170+
171+
98172
@pytest.fixture(scope="session")
99173
def templates():
100174
with Templates() as templates:

0 commit comments

Comments
 (0)