Skip to content

Commit 0a9b5ce

Browse files
committed
feat(test_runner): Move integration test setup script to python.
Replace the shell-based test setup (run_functional.sh) with a Python module (tests/test_framework/setup.py) that implements the same approach: build or download utreexod and bitcoind, and always build florestad, before running the test suite. The test runner (tests/test_runner.py) becomes the single entry point, keeping the flags: --force-rebuild, --release, and --preserve-data-dir. Also updates doc/running-tests.md accordingly.
1 parent 2176ba2 commit 0a9b5ce

9 files changed

Lines changed: 627 additions & 143 deletions

File tree

.github/workflows/functional.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,8 @@ jobs:
3939
- name: Cache Rust
4040
uses: Swatinem/rust-cache@v2
4141

42-
- name: Prepare functional tests tasks
43-
run: |
44-
tests/prepare.sh --release
45-
4642
- name: Run functional tests tasks
47-
run: tests/run_functional.sh
43+
run: uv run tests/test_runner.py --release
4844

4945
- name: Log tests on failure
5046
if: failure()

doc/running-tests.md

Lines changed: 76 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -97,193 +97,163 @@ just test-functional "-t floresta-cli -k stop -k ping"
9797
just test-functional "-t floresta-cli -k getblock"
9898
```
9999

100-
#### From helper script
100+
#### From the test runner directly
101101

102-
We provide a single helper script [run_functional.sh](https://github.com/vinteumorg/Floresta/blob/master/tests/run_functional.sh) that checks build dependencies, builds all required binaries, and runs the tests.
102+
The test runner handles everything: checking dependencies, building binaries, and running the suite.
103103

104104
Basic usage:
105105

106106
```bash
107-
./tests/run_functional.sh
107+
uv run tests/test_runner.py
108108
```
109109

110110
##### Utreexod
111111

112-
By default, the script will build `utreexod` at the `v0.4.0` tag.
112+
By default, the runner will build `utreexod` at the `v0.4.0` tag.
113113
If you want to build a specific release, set the `UTREEXOD_REVISION` environment variable.
114114
It must be a [valid tag](https://github.com/utreexo/utreexod/tags). For example:
115115

116116
```bash
117-
UTREEXOD_REVISION=v0.3.0 ./tests/run_functional.sh
117+
UTREEXOD_REVISION=v0.3.0 uv run tests/test_runner.py
118118
```
119119

120120
##### Bitcoin-core
121121

122-
By default, the setup script will obtain a runnable `bitcoind` binary in one of three exclusive ways. The default Bitcoin Core version is `30.2`, but you can override this by setting the `BITCOIN_REVISION` environment variable. The three methods are:
123-
124-
1. **Using a user-provided binary**: If the `BITCOIND_EXE` environment variable is set and points to an executable, that exact binary is used. No download or build is attempted, and any `BITCOIN_REVISION` or build-parallelism settings are ignored.
122+
By default, the runner will download a prebuilt `bitcoind` (v30.2). If you want to use a different version, configure it with the `BITCOIN_REVISION` environment variable. Also, if you need to change the number of CPU cores for source builds, use
123+
`BUILD_BITCOIND_NPROCS`. For example:
125124

126125
```bash
127-
BITCOIND_EXE=/path/to/bitcoind ./tests/run_functional.sh
126+
BITCOIN_REVISION=28.0 BUILD_BITCOIND_NPROCS=2 uv run tests/test_runner.py
128127
```
129128

130-
2. **Downloading a prebuilt binary**: If `BITCOIND_EXE` is not set, the script will try to download a prebuilt Bitcoin Core tarball for the specified `BITCOIN_REVISION`. Prebuilt binaries are available for all platforms and operating systems supported by Bitcoin Core. The supported versions are `30.2`, `29.2`, `28.3` and `27.2`.
129+
Additionally, you can use some flags:
131130

132131
```bash
133-
BITCOIN_REVISION=28.3 ./tests/run_functional.sh
132+
uv run tests/test_runner.py --force-rebuild --preserve-data-dir
134133
```
135134

136-
3. **Building from source**: If no prebuilt binary is available for the platform, operating system, or specified version, the script will clone the Bitcoin Core repository and build `bitcoind` from the specified `BITCOIN_REVISION`. This can be a version tag (e.g., `29.1`) or a branch(e.g., `master`) from the remote repository.
137-
138-
````bash
139-
BITCOIN_REVISION=master ./tests/run_functional.sh
140-
141-
Also, if you need to change the number of CPU cores for source builds, use
142-
`BUILD_BITCOIND_NPROCS`. For example:
143-
144-
```bash
145-
BUILD_BITCOIND_NPROCS=2 ./tests/run_functional.sh
146-
````
147-
148-
Additionally, you can use some flags:
149-
150-
The `--build` argument will force the script to fetch the `bitcoind` binary again and to build the `utreexod`.
151-
152-
The `--release` argument will build the `florestad` binary in release mode, which is optimized for production use. If this flag is not provided, the binary will be built in debug mode by default.
153-
154-
The `--preserve-data-dir` argument will keep the data and logs directories after successfully running the tests
135+
The `--force-rebuild` flag will force the runner to rebuild all binaries even if they are already present.
136+
The `--release` flag will build florestad in release mode (default is debug).
137+
The `--preserve-data-dir` flag will keep the data and logs directories after running the tests
155138
(this is useful if you want to keep the data for debugging purposes).
156139

157140
Furthermore, you can run a set of specific tests, rather than all at once.
158141

159142
```bash
160143
# runs all tests in 'floresta-cli' suite
161-
./tests/run_functional.sh --test-suite floresta-cli
144+
uv run tests/test_runner.py --test-suite floresta-cli
162145

163146
# same as above
164-
./tests/run_functional.sh -t floresta-cli
147+
uv run tests/test_runner.py -t floresta-cli
165148

166149
# run the stop and ping tests in the floresta-cli suite
167-
./tests/run_functional.sh --test-suite floresta-cli --test-name stop --test-name ping
150+
uv run tests/test_runner.py --test-suite floresta-cli --test-name stop --test-name ping
168151

169152
# same as above
170-
./tests/run_functional.sh -t floresta-cli -k stop -k ping
153+
uv run tests/test_runner.py -t floresta-cli -k stop -k ping
171154

172155
# run many tests that start with the word `getblock` (getblockhash, getblockheader, etc...)
173-
./tests/run_functional.sh -t floresta-cli -k getblock
156+
uv run tests/test_runner.py -t floresta-cli -k getblock
174157
```
175158

176-
#### From python utility directly
159+
#### How the setup works
177160

178-
Additional functional tests are available (minimum python version: 3.12).
179-
It's not recommended to run them directly, since you will need to manually
180-
build the binaries yourself and place them at `$FLORESTA_TEMP_DIR/binaries`.
181-
The advantage is that you can run a specific test suite. For this you'll need to:
161+
When you run `uv run tests/test_runner.py`, the runner automatically prepares
162+
everything before executing the test suite. The setup resolves a working directory
163+
(`FLORESTA_TEMP_DIR`), then obtains three binaries: **florestad**, **utreexod**,
164+
and **bitcoind**. Each binary is resolved using a three-tier fallback strategy:
182165

183-
- Setup `floresta`/`utreexod` environment;
184-
- Setup python utility;
185-
- Run tests from python utility directly;
186-
- Clean up the environment.
166+
1. **Environment variable** — if `BITCOIND_EXE` or `UTREEXOD_EXE` is set, the
167+
runner copies that executable directly into the binaries directory.
168+
2. **Prebuilt download** — the runner downloads a prebuilt binary from the
169+
project's official release page and verifies its SHA256 checksum.
170+
3. **Source build** — as a last resort, the runner clones the repository and
171+
builds from source.
187172

188-
##### Setup `floresta`/`utreexod` environment
173+
For **florestad**, the runner always builds from the local source tree via
174+
`cargo build`. If a binary already exists in the target directory, it is skipped
175+
unless `--force-rebuild` is passed.
189176

190-
After build the `floresta` and `utreexod` binaries, you'll need to define
191-
a `FLORESTA_TEMP_DIR` environment variable. This variable points to where
192-
our functional tests will look for the binaries.
177+
All binaries are placed under `$FLORESTA_TEMP_DIR/binaries/`.
193178

194-
##### Setup python utility
179+
#### Manual setup
195180

196-
- Recommended: install [uv: a rust-based python package and project manager](https://docs.astral.sh/uv/).
181+
If you prefer to manage binaries yourself (e.g. from a Nix shell or CI cache),
182+
you can skip the automatic setup entirely:
197183

198-
- Configure an isolated environment:
184+
1. Pick a working directory and export it:
199185

200186
```bash
201-
# create a virtual environment
202-
# (it's good to not mess up with your os)
203-
uv venv
204-
205-
# Alternatively, you can specify a python version (e.g, 3.12),
206-
uv venv --python 3.12
207-
208-
# activate the python virtual environment
209-
source .venv/bin/activate
210-
211-
# check if the python path was modified
212-
which python
187+
export FLORESTA_TEMP_DIR=/tmp/floresta-func-tests
188+
mkdir -p "$FLORESTA_TEMP_DIR/binaries"
213189
```
214190

215-
- Install module dependencies:
191+
2. Place the three required binaries there:
216192

217193
```bash
218-
# installs dependencies listed in pyproject.toml.
219-
# in local development environment
220-
# it do not remove existing packages.
221-
uv pip install -r pyproject.toml
222-
223-
# if you're a old-school pythonist,
224-
# install from requirements.txt
225-
# without remove existing packages.
226-
uv pip install -r tests/requirements.txt
227-
228-
# Alternatively, you can synchronize it
229-
# uses the uv.lock file to enforce
230-
# reproducible installations.
231-
uv sync
232-
```
194+
# florestad — build from the local tree
195+
cargo build --bin florestad
196+
ln -sf "$(pwd)/target/debug/florestad" "$FLORESTA_TEMP_DIR/binaries/florestad"
233197

234-
- Format code
198+
# utreexod — use your own build or a prebuilt binary
199+
cp /path/to/utreexod "$FLORESTA_TEMP_DIR/binaries/utreexod"
235200

236-
```bash
237-
uv run black ./tests
238-
239-
# if you want to just check
240-
uv run black --check --verbose ./tests
201+
# bitcoind — use your own build or a prebuilt binary
202+
cp /path/to/bitcoind "$FLORESTA_TEMP_DIR/binaries/bitcoind"
241203
```
242204

243-
- Lint code
205+
3. Run the tests. The runner will detect existing binaries and skip building:
244206

245207
```bash
246-
uv run pylint ./tests
208+
uv run tests/test_runner.py
247209
```
248210

249-
##### Run tests from python utility directly
250-
251-
Our tests are separated by "test suites". Suites are folders located in `./tests/<suite>` and the tests are the `./tests/<suite>/*-test.py` files. To run all suites, type:
211+
You can also point directly to external executables without copying:
252212

253213
```bash
254-
FLORESTA_TEMP_DIR=<your_bin_dir> uv run tests/test_runner.py
214+
BITCOIND_EXE=/usr/local/bin/bitcoind UTREEXOD_EXE=/usr/local/bin/utreexod \
215+
uv run tests/test_runner.py
255216
```
256217

257-
You can list all suites with:
218+
#### Python development tools
258219

259-
```bash
260-
FLORESTA_TEMP_DIR=<your_bin_dir> uv run tests/test_runner.py --list-suites
261-
```
220+
- Recommended: install [uv: a rust-based python package and project manager](https://docs.astral.sh/uv/).
262221

263-
To run a specific suite:
222+
- Format code:
264223

265224
```bash
266-
FLORESTA_TEMP_DIR=<your_bin_dir> uv run tests/test_runner.py --test-suite <suite>
225+
uv run black ./tests
226+
227+
# check only (no changes)
228+
uv run black --check --verbose ./tests
267229
```
268230

269-
You can even add more:
231+
- Lint code:
270232

271233
```bash
272-
FLORESTA_TEMP_DIR=<your_bin_dir> uv run tests/test_runner.py --test-suite <suite_A> --test-suite <suite_B>
234+
uv run pylint ./tests
273235
```
274236

275-
##### Clean up the environment
237+
#### Running individual test scripts
276238

277-
If you tests fails it will be necessary to cleanup the `data`
278-
folder created by the tests (some tests use it to retain
279-
information about tested nodes, like the `addnode` command).
280-
281-
You can do this by running:
239+
You can run a single test script directly. The framework will auto-resolve
240+
`FLORESTA_TEMP_DIR` if it is not set, but the binaries must already exist
241+
under `$FLORESTA_TEMP_DIR/binaries/`:
282242

283243
```bash
284-
rm -rf FLORESTA_TEMP_DIR/data
244+
uv run tests/floresta-cli/ping.py
285245
```
286246

247+
#### Clean up
248+
249+
Before each run the runner removes stale `data/` and `logs/` directories left
250+
over from previous executions (including failed ones), so you always start from
251+
a clean state.
252+
253+
On success the runner removes them again unless `--preserve-data-dir` is passed.
254+
If you need to inspect artefacts from a failed run, re-run with
255+
`--preserve-data-dir` to keep them around.
256+
287257
### Running/Developing Functional Tests with Nix
288258

289259
If you have nix, you can run the tests following the instructions [here](nix.md).

justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ test-functional-uv-fmt:
5757

5858
# Run the functional tests
5959
test-functional arg="":
60-
bash tests/run_functional.sh {{ arg }}
60+
uv run tests/test_runner.py {{ arg }}
6161

6262
# Run the benchmarks
6363
bench:

tests/test_framework/__init__.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,27 @@
1212
`add_node_settings`.
1313
"""
1414

15-
import os
16-
import re
17-
import sys
15+
import contextlib
1816
import copy
17+
import os
1918
import random
20-
import socket
21-
import shutil
19+
import re
2220
import signal
23-
import contextlib
24-
import subprocess
21+
import socket
22+
import sys
2523
import time
2624
from datetime import datetime, timezone
2725
from enum import Enum
28-
from typing import Any, Dict, List, Pattern, Tuple, Optional
26+
from typing import Any, Dict, List, Optional, Pattern, Tuple
2927

3028
from test_framework.crypto.pkcs8 import (
3129
create_pkcs8_private_key,
3230
create_pkcs8_self_signed_certificate,
3331
)
3432
from test_framework.daemon import ConfigP2P
35-
from test_framework.rpc import ConfigRPC
3633
from test_framework.electrum import ConfigElectrum, ConfigTls
3734
from test_framework.node import Node, NodeType
35+
from test_framework.rpc import ConfigRPC
3836
from test_framework.util import Utility
3937

4038

@@ -164,9 +162,6 @@ def main(self):
164162
except Exception as err:
165163
processes = []
166164
for node in self._nodes:
167-
if node.daemon.is_running:
168-
continue
169-
170165
# If the node has an RPC server, stop it gracefully
171166
# otherwise (maybe the error occurred before the RPC server
172167
# is started), try to kill the process with SIGTERM. If that

tests/test_framework/rpc/base.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@
88
"""
99

1010
import json
11+
import os
12+
import re
1113
import socket
1214
import time
13-
import re
15+
from abc import ABC, abstractmethod
1416
from datetime import datetime, timezone
1517
from typing import Any, Dict, List, Optional
1618
from urllib.parse import quote
17-
from abc import ABC, abstractmethod
1819

1920
from requests import post
2021
from requests.exceptions import HTTPError
2122
from requests.models import HTTPBasicAuth
22-
from test_framework.rpc.exceptions import JSONRPCError
2323
from test_framework.rpc import ConfigRPC
24+
from test_framework.rpc.exceptions import JSONRPCError
2425

2526

2627
# pylint: disable=too-many-public-methods
@@ -40,7 +41,7 @@ class BaseRPC(ABC):
4041
Subclasses should use `perform_request` to implement RPC calls.
4142
"""
4243

43-
TIMEOUT: int = 15 # seconds
44+
TIMEOUT: int = int(os.environ.get("FLORESTA_TEST_TIMEOUT", 15))
4445

4546
def __init__(self, config: ConfigRPC):
4647
self._config = config

0 commit comments

Comments
 (0)