tenzir-test 0.14.0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tenzir_test/_python_runner.py +7 -2
- tenzir_test/cli.py +8 -16
- tenzir_test/config.py +28 -15
- tenzir_test/engine/state.py +2 -3
- tenzir_test/fixtures/__init__.py +4 -4
- tenzir_test/fixtures/node.py +13 -4
- tenzir_test/run.py +51 -23
- tenzir_test/runners/custom_python_fixture_runner.py +3 -2
- {tenzir_test-0.14.0.dist-info → tenzir_test-1.0.0.dist-info}/METADATA +1 -1
- {tenzir_test-0.14.0.dist-info → tenzir_test-1.0.0.dist-info}/RECORD +13 -13
- {tenzir_test-0.14.0.dist-info → tenzir_test-1.0.0.dist-info}/WHEEL +0 -0
- {tenzir_test-0.14.0.dist-info → tenzir_test-1.0.0.dist-info}/entry_points.txt +0 -0
- {tenzir_test-0.14.0.dist-info → tenzir_test-1.0.0.dist-info}/licenses/LICENSE +0 -0
tenzir_test/_python_runner.py
CHANGED
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
5
|
import runpy
|
|
6
|
+
import shlex
|
|
6
7
|
import sys
|
|
7
8
|
from contextvars import Token
|
|
8
9
|
from pathlib import Path
|
|
@@ -49,14 +50,18 @@ def _push_context_from_env() -> Token[Any] | None:
|
|
|
49
50
|
config_args_raw = payload.get("config_args", ())
|
|
50
51
|
config_args = tuple(str(arg) for arg in config_args_raw)
|
|
51
52
|
env = dict(os.environ)
|
|
53
|
+
tenzir_binary_str = env.get("TENZIR_PYTHON_FIXTURE_BINARY") or env.get("TENZIR_BINARY")
|
|
54
|
+
tenzir_node_binary_str = env.get("TENZIR_NODE_BINARY")
|
|
52
55
|
context = _fixtures.FixtureContext(
|
|
53
56
|
test=test_path,
|
|
54
57
|
config=config,
|
|
55
58
|
coverage=coverage,
|
|
56
59
|
env=env,
|
|
57
60
|
config_args=config_args,
|
|
58
|
-
tenzir_binary=
|
|
59
|
-
tenzir_node_binary=
|
|
61
|
+
tenzir_binary=tuple(shlex.split(tenzir_binary_str)) if tenzir_binary_str else None,
|
|
62
|
+
tenzir_node_binary=tuple(shlex.split(tenzir_node_binary_str))
|
|
63
|
+
if tenzir_node_binary_str
|
|
64
|
+
else None,
|
|
60
65
|
)
|
|
61
66
|
token = _fixtures.push_context(context)
|
|
62
67
|
return cast(Token[Any], token)
|
tenzir_test/cli.py
CHANGED
|
@@ -39,18 +39,6 @@ def _normalize_exit_code(value: object) -> int:
|
|
|
39
39
|
),
|
|
40
40
|
help="Project root to scan for tests.",
|
|
41
41
|
)
|
|
42
|
-
@click.option(
|
|
43
|
-
"tenzir_binary",
|
|
44
|
-
"--tenzir-binary",
|
|
45
|
-
type=click.Path(path_type=Path, dir_okay=False, writable=False, resolve_path=False),
|
|
46
|
-
help="Path to the tenzir executable.",
|
|
47
|
-
)
|
|
48
|
-
@click.option(
|
|
49
|
-
"tenzir_node_binary",
|
|
50
|
-
"--tenzir-node-binary",
|
|
51
|
-
type=click.Path(path_type=Path, dir_okay=False, writable=False, resolve_path=False),
|
|
52
|
-
help="Path to the tenzir-node executable.",
|
|
53
|
-
)
|
|
54
42
|
@click.option(
|
|
55
43
|
"package_dirs",
|
|
56
44
|
"--package-dirs",
|
|
@@ -133,6 +121,12 @@ def _normalize_exit_code(value: object) -> int:
|
|
|
133
121
|
is_flag=True,
|
|
134
122
|
help="Stream raw test output directly to the terminal.",
|
|
135
123
|
)
|
|
124
|
+
@click.option(
|
|
125
|
+
"-v",
|
|
126
|
+
"--verbose",
|
|
127
|
+
is_flag=True,
|
|
128
|
+
help="Print individual test results as they complete. By default, only failures are shown. Automatically enabled in passthrough mode.",
|
|
129
|
+
)
|
|
136
130
|
@click.option(
|
|
137
131
|
"-a",
|
|
138
132
|
"--all-projects",
|
|
@@ -144,8 +138,6 @@ def cli(
|
|
|
144
138
|
ctx: click.Context,
|
|
145
139
|
*,
|
|
146
140
|
root: Path | None,
|
|
147
|
-
tenzir_binary: Path | None,
|
|
148
|
-
tenzir_node_binary: Path | None,
|
|
149
141
|
package_dirs: tuple[str, ...],
|
|
150
142
|
tests: tuple[Path, ...],
|
|
151
143
|
update: bool,
|
|
@@ -161,6 +153,7 @@ def cli(
|
|
|
161
153
|
keep_tmp_dirs: bool,
|
|
162
154
|
jobs: int,
|
|
163
155
|
passthrough: bool,
|
|
156
|
+
verbose: bool,
|
|
164
157
|
all_projects: bool,
|
|
165
158
|
) -> int:
|
|
166
159
|
"""Execute tenzir-test scenarios."""
|
|
@@ -179,8 +172,6 @@ def cli(
|
|
|
179
172
|
try:
|
|
180
173
|
result = runtime.run_cli(
|
|
181
174
|
root=root,
|
|
182
|
-
tenzir_binary=tenzir_binary,
|
|
183
|
-
tenzir_node_binary=tenzir_node_binary,
|
|
184
175
|
package_dirs=package_paths,
|
|
185
176
|
tests=list(tests),
|
|
186
177
|
update=update,
|
|
@@ -196,6 +187,7 @@ def cli(
|
|
|
196
187
|
keep_tmp_dirs=keep_tmp_dirs,
|
|
197
188
|
jobs=jobs,
|
|
198
189
|
passthrough=passthrough,
|
|
190
|
+
verbose=verbose,
|
|
199
191
|
jobs_overridden=jobs_overridden,
|
|
200
192
|
all_projects=all_projects,
|
|
201
193
|
)
|
tenzir_test/config.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import shlex
|
|
4
5
|
import shutil
|
|
5
6
|
from dataclasses import dataclass
|
|
6
7
|
from pathlib import Path
|
|
@@ -12,8 +13,8 @@ class Settings:
|
|
|
12
13
|
"""Configuration values steering how the harness discovers binaries and data."""
|
|
13
14
|
|
|
14
15
|
root: Path
|
|
15
|
-
tenzir_binary: str | None
|
|
16
|
-
tenzir_node_binary: str | None
|
|
16
|
+
tenzir_binary: tuple[str, ...] | None
|
|
17
|
+
tenzir_node_binary: tuple[str, ...] | None
|
|
17
18
|
|
|
18
19
|
@property
|
|
19
20
|
def inputs_dir(self) -> Path:
|
|
@@ -26,17 +27,34 @@ class Settings:
|
|
|
26
27
|
return direct
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
def _resolve_binary(
|
|
31
|
+
env_var: str | None,
|
|
32
|
+
binary_name: str,
|
|
33
|
+
) -> tuple[str, ...] | None:
|
|
34
|
+
"""Resolve a binary with fallback to uvx."""
|
|
35
|
+
if env_var:
|
|
36
|
+
try:
|
|
37
|
+
parts = tuple(shlex.split(env_var))
|
|
38
|
+
except ValueError as e:
|
|
39
|
+
raise ValueError(
|
|
40
|
+
f"Invalid shell syntax in environment variable for {binary_name}: {e}"
|
|
41
|
+
) from e
|
|
42
|
+
if not parts:
|
|
43
|
+
raise ValueError(f"Empty command in environment variable for {binary_name}")
|
|
44
|
+
return parts
|
|
45
|
+
which_result = shutil.which(binary_name)
|
|
46
|
+
if which_result:
|
|
47
|
+
return (which_result,)
|
|
48
|
+
if shutil.which("uvx"):
|
|
49
|
+
if binary_name == "tenzir-node":
|
|
50
|
+
return ("uvx", "--from", "tenzir", "tenzir-node")
|
|
51
|
+
return ("uvx", binary_name)
|
|
52
|
+
return None
|
|
33
53
|
|
|
34
54
|
|
|
35
55
|
def discover_settings(
|
|
36
56
|
*,
|
|
37
57
|
root: Path | None = None,
|
|
38
|
-
tenzir_binary: str | os.PathLike[str] | None = None,
|
|
39
|
-
tenzir_node_binary: str | os.PathLike[str] | None = None,
|
|
40
58
|
env: Mapping[str, str] | None = None,
|
|
41
59
|
) -> Settings:
|
|
42
60
|
"""Produce harness settings by combining CLI overrides with environment defaults."""
|
|
@@ -46,12 +64,7 @@ def discover_settings(
|
|
|
46
64
|
chosen_root = root or environment.get("TENZIR_TEST_ROOT") or Path.cwd()
|
|
47
65
|
root_path = Path(chosen_root).resolve()
|
|
48
66
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
tenzir_path = binary_cli or binary_env or shutil.which("tenzir")
|
|
52
|
-
|
|
53
|
-
node_cli = _coerce_binary(tenzir_node_binary)
|
|
54
|
-
node_env = environment.get("TENZIR_NODE_BINARY")
|
|
55
|
-
node_path = node_cli or node_env or shutil.which("tenzir-node")
|
|
67
|
+
tenzir_path = _resolve_binary(environment.get("TENZIR_BINARY"), "tenzir")
|
|
68
|
+
node_path = _resolve_binary(environment.get("TENZIR_NODE_BINARY"), "tenzir-node")
|
|
56
69
|
|
|
57
70
|
return Settings(root=root_path, tenzir_binary=tenzir_path, tenzir_node_binary=node_path)
|
tenzir_test/engine/state.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
from ..config import Settings
|
|
7
6
|
from tenzir_test import run
|
|
8
7
|
|
|
9
|
-
TENZIR_BINARY:
|
|
10
|
-
TENZIR_NODE_BINARY:
|
|
8
|
+
TENZIR_BINARY: tuple[str, ...] | None = run.TENZIR_BINARY
|
|
9
|
+
TENZIR_NODE_BINARY: tuple[str, ...] | None = run.TENZIR_NODE_BINARY
|
|
11
10
|
ROOT: Path = run.ROOT
|
|
12
11
|
INPUTS_DIR: Path = run.INPUTS_DIR
|
|
13
12
|
|
tenzir_test/fixtures/__init__.py
CHANGED
|
@@ -42,8 +42,8 @@ class FixtureContext:
|
|
|
42
42
|
coverage: bool
|
|
43
43
|
env: dict[str, str]
|
|
44
44
|
config_args: Sequence[str]
|
|
45
|
-
tenzir_binary: str | None
|
|
46
|
-
tenzir_node_binary: str | None
|
|
45
|
+
tenzir_binary: tuple[str, ...] | None
|
|
46
|
+
tenzir_node_binary: tuple[str, ...] | None
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
_CONTEXT: ContextVar[FixtureContext | None] = ContextVar(
|
|
@@ -57,7 +57,7 @@ class Executor:
|
|
|
57
57
|
def __init__(self, env: Mapping[str, str] | None = None) -> None:
|
|
58
58
|
source = env or os.environ
|
|
59
59
|
try:
|
|
60
|
-
self.binary: str = source["TENZIR_NODE_CLIENT_BINARY"]
|
|
60
|
+
self.binary: tuple[str, ...] = tuple(shlex.split(source["TENZIR_NODE_CLIENT_BINARY"]))
|
|
61
61
|
except KeyError as exc: # pragma: no cover - defensive guard
|
|
62
62
|
raise RuntimeError("TENZIR_NODE_CLIENT_BINARY is not configured") from exc
|
|
63
63
|
self.endpoint: str | None = source.get("TENZIR_NODE_CLIENT_ENDPOINT")
|
|
@@ -72,7 +72,7 @@ class Executor:
|
|
|
72
72
|
self, source: str, desired_timeout: float | None = None, mirror: bool = False
|
|
73
73
|
) -> subprocess.CompletedProcess[bytes]:
|
|
74
74
|
cmd = [
|
|
75
|
-
self.binary,
|
|
75
|
+
*self.binary,
|
|
76
76
|
"--bare-mode",
|
|
77
77
|
"--console-verbosity=warning",
|
|
78
78
|
"--multi",
|
tenzir_test/fixtures/node.py
CHANGED
|
@@ -165,9 +165,13 @@ def node() -> Iterator[dict[str, str]]:
|
|
|
165
165
|
if context is None:
|
|
166
166
|
raise RuntimeError("node fixture requires an active test context")
|
|
167
167
|
|
|
168
|
-
node_binary = context.tenzir_node_binary
|
|
168
|
+
node_binary: tuple[str, ...] | None = context.tenzir_node_binary
|
|
169
169
|
if not node_binary:
|
|
170
|
-
raise RuntimeError(
|
|
170
|
+
raise RuntimeError(
|
|
171
|
+
"tenzir-node binary not available. The harness checks: "
|
|
172
|
+
"TENZIR_NODE_BINARY env var, PATH lookup, uvx fallback. "
|
|
173
|
+
"Ensure tenzir-node is installed, uv is available, or set TENZIR_NODE_BINARY."
|
|
174
|
+
)
|
|
171
175
|
|
|
172
176
|
env = context.env.copy()
|
|
173
177
|
# Extract and filter config arguments: we handle --config and --package-dirs separately.
|
|
@@ -240,7 +244,7 @@ def node() -> Iterator[dict[str, str]]:
|
|
|
240
244
|
test_root = context.test.parent
|
|
241
245
|
|
|
242
246
|
node_cmd = [
|
|
243
|
-
node_binary,
|
|
247
|
+
*node_binary,
|
|
244
248
|
"--bare-mode",
|
|
245
249
|
"--console-verbosity=warning",
|
|
246
250
|
f"--state-directory={state_dir}",
|
|
@@ -292,9 +296,14 @@ def node() -> Iterator[dict[str, str]]:
|
|
|
292
296
|
)
|
|
293
297
|
raise RuntimeError(f"failed to obtain endpoint from tenzir-node ({detail})")
|
|
294
298
|
|
|
299
|
+
client_binary: str | None = None
|
|
300
|
+
if context.tenzir_binary:
|
|
301
|
+
client_binary = shlex.join(context.tenzir_binary)
|
|
302
|
+
else:
|
|
303
|
+
client_binary = env.get("TENZIR_BINARY")
|
|
295
304
|
fixture_env = {
|
|
296
305
|
"TENZIR_NODE_CLIENT_ENDPOINT": endpoint,
|
|
297
|
-
"TENZIR_NODE_CLIENT_BINARY":
|
|
306
|
+
"TENZIR_NODE_CLIENT_BINARY": client_binary,
|
|
298
307
|
"TENZIR_NODE_CLIENT_TIMEOUT": str(context.config["timeout"]),
|
|
299
308
|
"TENZIR_NODE_STATE_DIRECTORY": str(state_dir),
|
|
300
309
|
"TENZIR_NODE_CACHE_DIRECTORY": str(cache_dir),
|
tenzir_test/run.py
CHANGED
|
@@ -176,8 +176,8 @@ def detect_execution_mode(root: Path) -> tuple[ExecutionMode, Path | None]:
|
|
|
176
176
|
|
|
177
177
|
|
|
178
178
|
_settings: Settings | None = None
|
|
179
|
-
TENZIR_BINARY: str | None = None
|
|
180
|
-
TENZIR_NODE_BINARY: str | None = None
|
|
179
|
+
TENZIR_BINARY: tuple[str, ...] | None = None
|
|
180
|
+
TENZIR_NODE_BINARY: tuple[str, ...] | None = None
|
|
181
181
|
ROOT: Path = Path.cwd()
|
|
182
182
|
INPUTS_DIR: Path = ROOT / "inputs"
|
|
183
183
|
EXECUTION_MODE: ExecutionMode = ExecutionMode.PROJECT
|
|
@@ -483,6 +483,7 @@ KEEP_TMP_DIRS = bool(os.environ.get(_TMP_KEEP_ENV_VAR))
|
|
|
483
483
|
|
|
484
484
|
SHOW_DIFF_OUTPUT = True
|
|
485
485
|
SHOW_DIFF_STAT = True
|
|
486
|
+
VERBOSE_OUTPUT = False
|
|
486
487
|
_BLOCK_INDENT = ""
|
|
487
488
|
_PLUS_SYMBOLS = {1: "□", 10: "▣", 100: "■"}
|
|
488
489
|
_MINUS_SYMBOLS = {1: "□", 10: "▣", 100: "■"}
|
|
@@ -506,6 +507,21 @@ def should_show_diff_stat() -> bool:
|
|
|
506
507
|
return SHOW_DIFF_STAT
|
|
507
508
|
|
|
508
509
|
|
|
510
|
+
def set_verbose_output(enabled: bool) -> None:
|
|
511
|
+
"""Enable or disable verbose output for individual test results.
|
|
512
|
+
|
|
513
|
+
When enabled, both success and skip messages are printed as tests complete.
|
|
514
|
+
When disabled (default), only failures are printed.
|
|
515
|
+
"""
|
|
516
|
+
global VERBOSE_OUTPUT
|
|
517
|
+
VERBOSE_OUTPUT = enabled
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def is_verbose_output() -> bool:
|
|
521
|
+
"""Return whether verbose output is enabled."""
|
|
522
|
+
return VERBOSE_OUTPUT
|
|
523
|
+
|
|
524
|
+
|
|
509
525
|
def set_harness_mode(mode: HarnessMode) -> None:
|
|
510
526
|
"""Set the global harness execution mode."""
|
|
511
527
|
|
|
@@ -1850,9 +1866,9 @@ def get_test_env_and_config_args(
|
|
|
1850
1866
|
if node_config_file.exists():
|
|
1851
1867
|
env["TENZIR_NODE_CONFIG"] = str(node_config_file)
|
|
1852
1868
|
if TENZIR_BINARY:
|
|
1853
|
-
env["TENZIR_BINARY"] = TENZIR_BINARY
|
|
1869
|
+
env["TENZIR_BINARY"] = shlex.join(TENZIR_BINARY)
|
|
1854
1870
|
if TENZIR_NODE_BINARY:
|
|
1855
|
-
env["TENZIR_NODE_BINARY"] = TENZIR_NODE_BINARY
|
|
1871
|
+
env["TENZIR_NODE_BINARY"] = shlex.join(TENZIR_NODE_BINARY)
|
|
1856
1872
|
env["TENZIR_TEST_ROOT"] = str(ROOT)
|
|
1857
1873
|
tmp_dir = _create_test_tmp_dir(test)
|
|
1858
1874
|
env[TEST_TMP_ENV_VAR] = str(tmp_dir)
|
|
@@ -2695,7 +2711,7 @@ def get_version() -> str:
|
|
|
2695
2711
|
return (
|
|
2696
2712
|
subprocess.check_output(
|
|
2697
2713
|
[
|
|
2698
|
-
TENZIR_BINARY,
|
|
2714
|
+
*TENZIR_BINARY,
|
|
2699
2715
|
"--bare-mode",
|
|
2700
2716
|
"--console-verbosity=warning",
|
|
2701
2717
|
"version | select version | write_lines",
|
|
@@ -2707,6 +2723,8 @@ def get_version() -> str:
|
|
|
2707
2723
|
|
|
2708
2724
|
|
|
2709
2725
|
def success(test: Path) -> None:
|
|
2726
|
+
if not is_verbose_output():
|
|
2727
|
+
return
|
|
2710
2728
|
with stdout_lock:
|
|
2711
2729
|
rel_test = _relativize_path(test)
|
|
2712
2730
|
suite_suffix = _format_suite_suffix()
|
|
@@ -2714,6 +2732,10 @@ def success(test: Path) -> None:
|
|
|
2714
2732
|
print(f"{CHECKMARK} {rel_test}{suite_suffix}{attempt_suffix}")
|
|
2715
2733
|
|
|
2716
2734
|
|
|
2735
|
+
# Failures always print regardless of verbose mode because failures are critical
|
|
2736
|
+
# information that users need to see immediately. In contrast, success() and
|
|
2737
|
+
# handle_skip() respect the verbose setting since passed/skipped tests are
|
|
2738
|
+
# expected outcomes that can be summarized at the end.
|
|
2717
2739
|
def fail(test: Path) -> None:
|
|
2718
2740
|
with stdout_lock:
|
|
2719
2741
|
rel_test = _relativize_path(test)
|
|
@@ -2927,7 +2949,7 @@ def run_simple_test(
|
|
|
2927
2949
|
if not TENZIR_BINARY:
|
|
2928
2950
|
raise RuntimeError("TENZIR_BINARY must be configured before running tests")
|
|
2929
2951
|
cmd: list[str] = [
|
|
2930
|
-
TENZIR_BINARY,
|
|
2952
|
+
*TENZIR_BINARY,
|
|
2931
2953
|
"--bare-mode",
|
|
2932
2954
|
"--console-verbosity=warning",
|
|
2933
2955
|
"--multi",
|
|
@@ -3028,9 +3050,10 @@ def run_simple_test(
|
|
|
3028
3050
|
|
|
3029
3051
|
|
|
3030
3052
|
def handle_skip(reason: str, test: Path, update: bool, output_ext: str) -> bool | str:
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3053
|
+
if is_verbose_output():
|
|
3054
|
+
rel_path = _relativize_path(test)
|
|
3055
|
+
suite_suffix = _format_suite_suffix()
|
|
3056
|
+
print(f"{SKIP} skipped {rel_path}{suite_suffix}: {reason}")
|
|
3034
3057
|
ref_path = test.with_suffix(f".{output_ext}")
|
|
3035
3058
|
if update:
|
|
3036
3059
|
with ref_path.open("wb") as f:
|
|
@@ -3336,8 +3359,6 @@ def collect_all_tests(directory: Path) -> Iterator[Path]:
|
|
|
3336
3359
|
def run_cli(
|
|
3337
3360
|
*,
|
|
3338
3361
|
root: Path | None,
|
|
3339
|
-
tenzir_binary: Path | None,
|
|
3340
|
-
tenzir_node_binary: Path | None,
|
|
3341
3362
|
package_dirs: Sequence[Path] | None = None,
|
|
3342
3363
|
tests: Sequence[Path],
|
|
3343
3364
|
update: bool,
|
|
@@ -3353,10 +3374,16 @@ def run_cli(
|
|
|
3353
3374
|
jobs: int,
|
|
3354
3375
|
keep_tmp_dirs: bool,
|
|
3355
3376
|
passthrough: bool,
|
|
3377
|
+
verbose: bool = False,
|
|
3356
3378
|
jobs_overridden: bool = False,
|
|
3357
3379
|
all_projects: bool = False,
|
|
3358
3380
|
) -> ExecutionResult:
|
|
3359
|
-
"""Execute the harness and return a structured result for library consumers.
|
|
3381
|
+
"""Execute the harness and return a structured result for library consumers.
|
|
3382
|
+
|
|
3383
|
+
Args:
|
|
3384
|
+
verbose: Print individual test results (pass/skip) as they complete.
|
|
3385
|
+
When False (default), only failures are printed during execution.
|
|
3386
|
+
"""
|
|
3360
3387
|
from tenzir_test.engine import state as engine_state
|
|
3361
3388
|
|
|
3362
3389
|
try:
|
|
@@ -3409,6 +3436,8 @@ def run_cli(
|
|
|
3409
3436
|
harness_mode = HarnessMode.COMPARE
|
|
3410
3437
|
set_harness_mode(harness_mode)
|
|
3411
3438
|
passthrough_mode = harness_mode is HarnessMode.PASSTHROUGH
|
|
3439
|
+
# Passthrough mode requires verbose output to show real-time test results
|
|
3440
|
+
set_verbose_output(verbose or passthrough_mode)
|
|
3412
3441
|
if passthrough_mode and jobs > 1:
|
|
3413
3442
|
if jobs_overridden:
|
|
3414
3443
|
print(f"{INFO} forcing --jobs=1 in passthrough mode to preserve output ordering")
|
|
@@ -3417,11 +3446,7 @@ def run_cli(
|
|
|
3417
3446
|
print(f"{INFO} ignoring --update in passthrough mode")
|
|
3418
3447
|
update = False
|
|
3419
3448
|
|
|
3420
|
-
settings = discover_settings(
|
|
3421
|
-
root=root,
|
|
3422
|
-
tenzir_binary=tenzir_binary,
|
|
3423
|
-
tenzir_node_binary=tenzir_node_binary,
|
|
3424
|
-
)
|
|
3449
|
+
settings = discover_settings(root=root)
|
|
3425
3450
|
apply_settings(settings)
|
|
3426
3451
|
_set_cli_packages(list(package_dirs or []))
|
|
3427
3452
|
selected_tests = list(tests)
|
|
@@ -3682,7 +3707,8 @@ def run_cli(
|
|
|
3682
3707
|
_print_compact_summary(project_summary)
|
|
3683
3708
|
summary_enabled = show_summary or runner_summary or fixture_summary
|
|
3684
3709
|
if summary_enabled:
|
|
3685
|
-
|
|
3710
|
+
if is_verbose_output():
|
|
3711
|
+
_print_detailed_summary(project_summary)
|
|
3686
3712
|
_print_ascii_summary(
|
|
3687
3713
|
project_summary,
|
|
3688
3714
|
include_runner=runner_summary,
|
|
@@ -3764,8 +3790,6 @@ def run_cli(
|
|
|
3764
3790
|
def execute(
|
|
3765
3791
|
*,
|
|
3766
3792
|
root: Path | None = None,
|
|
3767
|
-
tenzir_binary: Path | None = None,
|
|
3768
|
-
tenzir_node_binary: Path | None = None,
|
|
3769
3793
|
tests: Sequence[Path] = (),
|
|
3770
3794
|
update: bool = False,
|
|
3771
3795
|
debug: bool = False,
|
|
@@ -3780,16 +3804,19 @@ def execute(
|
|
|
3780
3804
|
jobs: int | None = None,
|
|
3781
3805
|
keep_tmp_dirs: bool = False,
|
|
3782
3806
|
passthrough: bool = False,
|
|
3807
|
+
verbose: bool = False,
|
|
3783
3808
|
jobs_overridden: bool = False,
|
|
3784
3809
|
all_projects: bool = False,
|
|
3785
3810
|
) -> ExecutionResult:
|
|
3786
|
-
"""Library-oriented wrapper around `run_cli` with defaulted parameters.
|
|
3811
|
+
"""Library-oriented wrapper around `run_cli` with defaulted parameters.
|
|
3787
3812
|
|
|
3813
|
+
Args:
|
|
3814
|
+
verbose: Print individual test results (pass/skip) as they complete.
|
|
3815
|
+
When False (default), only failures are printed during execution.
|
|
3816
|
+
"""
|
|
3788
3817
|
resolved_jobs = jobs if jobs is not None else get_default_jobs()
|
|
3789
3818
|
return run_cli(
|
|
3790
3819
|
root=root,
|
|
3791
|
-
tenzir_binary=tenzir_binary,
|
|
3792
|
-
tenzir_node_binary=tenzir_node_binary,
|
|
3793
3820
|
tests=list(tests),
|
|
3794
3821
|
update=update,
|
|
3795
3822
|
debug=debug,
|
|
@@ -3804,6 +3831,7 @@ def execute(
|
|
|
3804
3831
|
jobs=resolved_jobs,
|
|
3805
3832
|
keep_tmp_dirs=keep_tmp_dirs,
|
|
3806
3833
|
passthrough=passthrough,
|
|
3834
|
+
verbose=verbose,
|
|
3807
3835
|
jobs_overridden=jobs_overridden,
|
|
3808
3836
|
all_projects=all_projects,
|
|
3809
3837
|
)
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
+
import shlex
|
|
5
6
|
import subprocess
|
|
6
7
|
import sys
|
|
7
8
|
import typing
|
|
@@ -99,9 +100,9 @@ class CustomPythonFixture(ExtRunner):
|
|
|
99
100
|
endpoint = None
|
|
100
101
|
new_pythonpath = os.pathsep.join(pythonpath_entries)
|
|
101
102
|
env["PYTHONPATH"] = new_pythonpath
|
|
102
|
-
env["TENZIR_NODE_CLIENT_BINARY"] = binary
|
|
103
|
+
env["TENZIR_NODE_CLIENT_BINARY"] = shlex.join(binary)
|
|
103
104
|
env["TENZIR_NODE_CLIENT_TIMEOUT"] = str(timeout)
|
|
104
|
-
env.setdefault("TENZIR_PYTHON_FIXTURE_BINARY", binary)
|
|
105
|
+
env.setdefault("TENZIR_PYTHON_FIXTURE_BINARY", shlex.join(binary))
|
|
105
106
|
env["TENZIR_PYTHON_FIXTURE_TIMEOUT"] = str(timeout)
|
|
106
107
|
if node_requested and endpoint:
|
|
107
108
|
env["TENZIR_PYTHON_FIXTURE_ENDPOINT"] = endpoint
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tenzir-test
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Reusable test execution framework extracted from the Tenzir repository.
|
|
5
5
|
Project-URL: Homepage, https://github.com/tenzir/test
|
|
6
6
|
Project-URL: Repository, https://github.com/tenzir/test
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
tenzir_test/__init__.py,sha256=k7V6Pbjaa8SAy6t4KnaauHTyfnyVEwc1VGtH823MANU,1181
|
|
2
|
-
tenzir_test/_python_runner.py,sha256=
|
|
3
|
-
tenzir_test/cli.py,sha256=
|
|
4
|
-
tenzir_test/config.py,sha256=
|
|
2
|
+
tenzir_test/_python_runner.py,sha256=ZODuZ6ll21gxBRj34iq-7lOmZOiqzFyu7wnPnS0K648,3130
|
|
3
|
+
tenzir_test/cli.py,sha256=WwddXpkjg3OIT5E54LOobaibDkGH9UvTLzZ9iMKw-qQ,6241
|
|
4
|
+
tenzir_test/config.py,sha256=z4ayS62SfOLNwrEgNktVeulyQ2QW4KUlN1KX0Za1NDM,2110
|
|
5
5
|
tenzir_test/packages.py,sha256=cTCQdGjCS1XmuKyiwh0ew-z9tHn6J-xZ6nvBP-hU8bc,948
|
|
6
6
|
tenzir_test/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
tenzir_test/run.py,sha256=
|
|
7
|
+
tenzir_test/run.py,sha256=cWCvK4N4fKnFnJe8QBZ9OPDgc9mFIzaNhuduG1L1Mzs,132979
|
|
8
8
|
tenzir_test/engine/__init__.py,sha256=5APwy90YDm7rmL_qCZfToAcfbQthcZ8yV2_ExXKqaqE,110
|
|
9
9
|
tenzir_test/engine/operations.py,sha256=OCYjuMHyMAaay4s08u2Sl7oE-PmgeXumylp7R8GYIH4,950
|
|
10
10
|
tenzir_test/engine/registry.py,sha256=LXCr6TGlv1sR1m1eboTk7SrbS2IVErc3PqUuHxGA2xk,594
|
|
11
|
-
tenzir_test/engine/state.py,sha256=
|
|
11
|
+
tenzir_test/engine/state.py,sha256=zolKmUWpTEajoq4gY8KYv_puCeS_DOpOTExBwLulNjo,879
|
|
12
12
|
tenzir_test/engine/worker.py,sha256=WwIkx1m_ANNveQjNisy5-qpbUZl_DfDXxVIfdxABKjc,140
|
|
13
|
-
tenzir_test/fixtures/__init__.py,sha256=
|
|
14
|
-
tenzir_test/fixtures/node.py,sha256=
|
|
13
|
+
tenzir_test/fixtures/__init__.py,sha256=H8deAoYg7HMPsZZ2UTO2KE1N82CbCQqN0bXk8q7B9ww,18441
|
|
14
|
+
tenzir_test/fixtures/node.py,sha256=iNT3H0y_9g-CzJO7-EPrAYBOX69O-g3lnuQiAvGqThc,11524
|
|
15
15
|
tenzir_test/runners/__init__.py,sha256=M3p-TsDp231Dy58miDb467bA1kLYzgpa0pqVr_KP1ro,4616
|
|
16
16
|
tenzir_test/runners/_utils.py,sha256=BWv7UEPGa01l4tGTCg5i_22NblIyRw8vjk_5NIf1x_c,467
|
|
17
|
-
tenzir_test/runners/custom_python_fixture_runner.py,sha256=
|
|
17
|
+
tenzir_test/runners/custom_python_fixture_runner.py,sha256=kJ4NYfLnB2ntZguv1pzIge_ith7D49YHKM35CJXU8MA,7321
|
|
18
18
|
tenzir_test/runners/diff_runner.py,sha256=WIEry_-4R4VIlA9HON5kl2ipyy1TKzS4jRc2wPl6r5c,5476
|
|
19
19
|
tenzir_test/runners/ext_runner.py,sha256=sKL9Mw_ksVVBWnrdIJR2WS5ueVnLKuNYYWZ22FTZIPo,730
|
|
20
20
|
tenzir_test/runners/runner.py,sha256=LtlD8huQOSmD7RyYDnKeCuI4Y6vhxGXMKsHA2qgfWN0,989
|
|
21
21
|
tenzir_test/runners/shell_runner.py,sha256=OuofgHeZN2FaO6xRI3uyqstLBymc6rmWC4HAnSn91AE,6068
|
|
22
22
|
tenzir_test/runners/tenzir_runner.py,sha256=464FFYS_mh6l-ehccc-S8cIUO1MxdapwQL5X3PmMkMI,1006
|
|
23
23
|
tenzir_test/runners/tql_runner.py,sha256=2ZLMf3TIKwcOvaOFrVvvhzK-EcWmGOUZxKkbSoByyQA,248
|
|
24
|
-
tenzir_test-0.
|
|
25
|
-
tenzir_test-0.
|
|
26
|
-
tenzir_test-0.
|
|
27
|
-
tenzir_test-0.
|
|
28
|
-
tenzir_test-0.
|
|
24
|
+
tenzir_test-1.0.0.dist-info/METADATA,sha256=C1PcrFg8hConTcbJz9gQY-44_Y2xirlIyKT6mquT84o,3065
|
|
25
|
+
tenzir_test-1.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
26
|
+
tenzir_test-1.0.0.dist-info/entry_points.txt,sha256=l8DJgiEVrjScdTTo613cZ3PKodOmqrUVIbz-3awfV8w,53
|
|
27
|
+
tenzir_test-1.0.0.dist-info/licenses/LICENSE,sha256=ajMbpcBiSTXI8Rr4t17pvowV-On8DktghfZKxY_A22Q,10750
|
|
28
|
+
tenzir_test-1.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|