Skip to content

Commit 5bf2592

Browse files
authored
Merge pull request #152 from wingo/real-argv
Report actual argv on errors
2 parents 124dfc1 + 1649793 commit 5bf2592

File tree

7 files changed

+208
-112
lines changed

7 files changed

+208
-112
lines changed

adapters/wasm-micro-runtime.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,35 @@
1-
import argparse
21
import subprocess
3-
import sys
42
import os
53
import shlex
4+
from pathlib import Path
5+
from typing import Dict, List, Tuple
66

77
# shlex.split() splits according to shell quoting rules
88
IWASM = shlex.split(os.getenv("IWASM", "iwasm"))
99

10-
parser = argparse.ArgumentParser()
11-
parser.add_argument("--version", action="store_true")
12-
parser.add_argument("--test-file", action="store")
13-
parser.add_argument("--arg", action="append", default=[])
14-
parser.add_argument("--env", action="append", default=[])
15-
parser.add_argument("--dir", action="append", default=[])
1610

17-
args = parser.parse_args()
11+
def get_name() -> str:
12+
return "wamr"
1813

19-
if args.version:
20-
subprocess.run(IWASM + ["--version"])
21-
sys.exit(0)
2214

23-
TEST_FILE = args.test_file
24-
PROG_ARGS = args.arg
25-
ENV_ARGS = [f"--env={i}" for i in args.env]
26-
DIR_ARGS = [f"--map-dir={i}" for i in args.dir]
15+
def get_version() -> str:
16+
# ensure no args when version is queried
17+
result = subprocess.run(IWASM + ["--version"],
18+
encoding="UTF-8", capture_output=True,
19+
check=True)
20+
output = result.stdout.splitlines()[0].split(" ")
21+
return output[1]
2722

28-
r = subprocess.run(IWASM + ENV_ARGS + DIR_ARGS + [TEST_FILE] + PROG_ARGS)
29-
sys.exit(r.returncode)
23+
24+
def compute_argv(test_path: str,
25+
args: List[str],
26+
env: Dict[str, str],
27+
dirs: List[Tuple[Path, str]]) -> List[str]:
28+
argv = [] + IWASM
29+
for k, v in env.items():
30+
argv += ["--env", f"{k}={v}"]
31+
for host, guest in dirs:
32+
argv += ["--map-dir", f"{host}::{guest}"] # noqa: E231
33+
argv += [test_path]
34+
argv += args
35+
return argv

adapters/wasmtime.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,35 @@
1-
import argparse
21
import subprocess
3-
import sys
42
import os
53
import shlex
4+
from pathlib import Path
5+
from typing import Dict, List, Tuple
66

77
# shlex.split() splits according to shell quoting rules
88
WASMTIME = shlex.split(os.getenv("WASMTIME", "wasmtime"))
99

10-
parser = argparse.ArgumentParser()
11-
parser.add_argument("--version", action="store_true")
12-
parser.add_argument("--test-file", action="store")
13-
parser.add_argument("--arg", action="append", default=[])
14-
parser.add_argument("--env", action="append", default=[])
15-
parser.add_argument("--dir", action="append", default=[])
1610

17-
args = parser.parse_args()
11+
def get_name() -> str:
12+
return "wasmtime"
1813

19-
if args.version:
14+
15+
def get_version() -> str:
2016
# ensure no args when version is queried
21-
subprocess.run(WASMTIME[0:1] + ["--version"])
22-
sys.exit(0)
17+
result = subprocess.run(WASMTIME[0:1] + ["--version"],
18+
encoding="UTF-8", capture_output=True,
19+
check=True)
20+
output = result.stdout.splitlines()[0].split(" ")
21+
return output[1]
2322

24-
TEST_FILE = args.test_file
25-
PROG_ARGS = args.arg
26-
ENV_ARGS = [j for i in args.env for j in ["--env", i]]
27-
DIR_ARGS = [j for i in args.dir for j in ["--dir", i]]
2823

29-
r = subprocess.run(WASMTIME + ENV_ARGS + DIR_ARGS + [TEST_FILE] + PROG_ARGS)
30-
sys.exit(r.returncode)
24+
def compute_argv(test_path: str,
25+
args: List[str],
26+
env: Dict[str, str],
27+
dirs: List[Tuple[Path, str]]) -> List[str]:
28+
argv = [] + WASMTIME
29+
for k, v in env.items():
30+
argv += ["--env", f"{k}={v}"]
31+
for host, guest in dirs:
32+
argv += ["--dir", f"{host}::{guest}"] # noqa: E231
33+
argv += [test_path]
34+
argv += args
35+
return argv

adapters/wizard.py

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,44 @@
1-
import argparse
21
import subprocess
3-
import sys
42
import os
53
import shlex
4+
from pathlib import Path
5+
from typing import Dict, List, Tuple
66

77
# shlex.split() splits according to shell quoting rules
88
WIZARD = shlex.split(os.getenv("WIZARD", "wizeng.x86-64-linux"))
99

10-
parser = argparse.ArgumentParser()
11-
parser.add_argument("--version", action="store_true")
12-
parser.add_argument("--test-file", action="store")
13-
parser.add_argument("--arg", action="append", default=[])
14-
parser.add_argument("--env", action="append", default=[])
15-
parser.add_argument("--dir", action="append", default=[])
1610

17-
args = parser.parse_args()
11+
def get_name() -> str:
12+
return "wizard"
1813

19-
if args.version:
14+
15+
def get_version() -> str:
2016
# ensure no args when version is queried
21-
subprocess.run(WIZARD[0:1] + ["-version"])
22-
sys.exit(0)
17+
output = ""
18+
try:
19+
result = subprocess.run(WIZARD[0:1] + ["--version"],
20+
encoding="UTF-8", capture_output=True,
21+
check=False)
22+
output = result.stdout;
23+
except subprocess.CalledProcessError as e:
24+
# https://github.com/titzer/wizard-engine/issues/483
25+
if e.returncode != 3:
26+
raise e
27+
output = result.stdout
28+
output = output.splitlines()[0].split(" ")
29+
return output[1]
2330

24-
TEST_FILE = args.test_file
25-
PROG_ARGS = args.arg
26-
ENV_ARGS = None if len(args.env) == 0 else f'--env={",".join(args.env)}'
27-
DIR_ARGS = None if len(args.dir) == 0 else f'--dir={",".join(args.dir)}'
2831

29-
r = subprocess.run([arg for arg in WIZARD + [ENV_ARGS, DIR_ARGS, TEST_FILE] + PROG_ARGS if arg])
30-
sys.exit(r.returncode)
32+
def compute_argv(test_path: str,
33+
args: List[str],
34+
env: Dict[str, str],
35+
dirs: List[Tuple[Path, str]]) -> List[str]:
36+
argv = [] + WIZARD
37+
for k, v in env.items():
38+
argv += [f"--env={k}={v}"]
39+
for host, guest in dirs:
40+
# FIXME: https://github.com/titzer/wizard-engine/issues/482
41+
argv += [f"--dir={host}"]
42+
argv += [test_path]
43+
argv += args
44+
return argv

run-tests

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ from pathlib import Path
99
sys.path.insert(0, str(Path(__file__).parent / "test-runner"))
1010

1111
from wasi_test_runner.harness import run_tests
12-
from wasi_test_runner.runtime_adapter import RuntimeAdapter
12+
from wasi_test_runner import runtime_adapter
1313

1414
parser = argparse.ArgumentParser(
1515
description="WASI test runner"
@@ -42,7 +42,9 @@ parser.add_argument(
4242
help="Print more information about test results.",
4343
)
4444

45-
def find_test_dirs(root):
45+
def find_test_dirs():
46+
root = Path(__file__).parent / "tests"
47+
root = root.relative_to(Path('.').absolute(), walk_up=True)
4648
test_dirs = []
4749
for root, dirs, files in root.walk(on_error=print):
4850
if "manifest.json" in files:
@@ -53,32 +55,32 @@ def find_runtime_adapters(root, verbose=False):
5355
print(f"Detecting WASI runtime availability:")
5456
adapters = []
5557
for candidate in root.glob("*.py"):
56-
adapter = RuntimeAdapter(candidate)
5758
try:
59+
adapter = runtime_adapter.RuntimeAdapter(candidate)
5860
print(f" {candidate.name}: {adapter.get_version()}")
5961
adapters.append(adapter)
60-
except subprocess.CalledProcessError as e:
61-
print(f" {candidate.name}: unavailable; pass `--runtime {candidate}` to debug.")
62+
except runtime_adapter.LegacyRuntimeAdapterError:
63+
print(f" {candidate} is too old; update to new module format.")
64+
except runtime_adapter.UnavailableRuntimeAdapterError:
65+
print(f" {candidate.name} unavailable; pass `--runtime {candidate}` to debug.")
6266
print("")
6367
if len(adapters) == 0:
6468
print("Error: No WASI runtimes found")
6569
sys.exit(1)
6670
return adapters
6771

6872
options = parser.parse_args()
69-
test_suite = find_test_dirs(Path(__file__).parent / "tests")
73+
test_suite = find_test_dirs()
7074
if options.runtime_adapter:
71-
runtime_adapters = [RuntimeAdapter(options.runtime_adapter)]
72-
# Ensure it works.
7375
try:
74-
runtime_adapters[0].get_version()
75-
except subprocess.CalledProcessError as e:
76-
print(f"Error: failed to load {options.runtime_adapter}:")
77-
print(f" Failed command line: {' '.join(e.cmd)}")
78-
if e.stdout.strip() != "":
79-
print(f" stdout:\n{e.stdout}")
80-
if e.stderr.strip() != "":
81-
print(f" stderr:\n{e.stderr}")
76+
runtime_adapters = [
77+
runtime_adapter.RuntimeAdapter(options.runtime_adapter)
78+
]
79+
except runtime_adapter.LegacyRuntimeAdapterError as e:
80+
print(f"Error: {e.adapter_path} is too old; update to new module format.")
81+
sys.exit(1)
82+
except runtime_adapter.UnavailableRuntimeAdapterError as e:
83+
print(f"Error: failed to load {e.adapter_path}: {e.error}")
8284
sys.exit(1)
8385
else:
8486
runtime_adapters = find_runtime_adapters(Path(__file__).parent / "adapters")

test-runner/tests/test_test_suite_runner.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import Any
2+
from pathlib import Path
23
from unittest.mock import ANY, MagicMock, Mock, patch, mock_open
34

45
import wasi_test_runner.test_case as tc
@@ -10,9 +11,9 @@ def get_mock_open() -> Mock:
1011
def open_mock(filename: str, *_args: Any, **_kwargs: Any) -> Any:
1112
file_content = {
1213
"my-path/manifest.json": '{"name": "test-suite"}',
13-
"test1.json": '{"dirs": [".", "deep/dir"]}',
14-
"test2.json": '{"exit_code": 1, "args": ["a", "b"]}',
15-
"test3.json": '{"stdout": "output", "env": {"x": "1"}}',
14+
"my-path/test1.json": '{"dirs": [".", "deep/dir"]}',
15+
"my-path/test2.json": '{"exit_code": 1, "args": ["a", "b"]}',
16+
"my-path/test3.json": '{"stdout": "output", "env": {"x": "1"}}',
1617
}
1718
if filename in file_content:
1819
return mock_open(read_data=file_content[filename]).return_value
@@ -26,7 +27,10 @@ def open_mock(filename: str, *_args: Any, **_kwargs: Any) -> Any:
2627
@patch("builtins.open", get_mock_open())
2728
@patch("os.path.exists", Mock(return_value=True))
2829
def test_runner_end_to_end() -> None:
29-
test_paths = ["test1.wasm", "test2.wasm", "test3.wasm"]
30+
test_suite_dir = "my-path"
31+
test_suite_name = "test-suite"
32+
test_files = ["test1.wasm", "test2.wasm", "test3.wasm"]
33+
test_paths = [Path(test_suite_dir) / f for f in test_files]
3034

3135
failures = [tc.Failure("a", "b"), tc.Failure("x", "y"), tc.Failure("x", "z")]
3236

@@ -75,9 +79,8 @@ def test_runner_end_to_end() -> None:
7579
filt.should_skip.return_value = (False, None)
7680
filters = [filt]
7781

78-
test_suite_dir = "my-path"
79-
test_suite_name = "test-suite"
80-
with patch("glob.glob", return_value=test_paths):
82+
with (patch("glob.glob", return_value=[str(p) for p in test_paths]),
83+
patch("wasi_test_runner.test_suite_runner._cleanup_test_output")):
8184
suite = tsr.run_tests_from_test_suite(test_suite_dir, runtime,
8285
validators, # type: ignore
8386
reporters, # type: ignore
@@ -93,8 +96,9 @@ def test_runner_end_to_end() -> None:
9396
# Assert test runner calls
9497
assert runtime.run_test.call_count == 3
9598
for test_path, config in zip(test_paths, expected_config):
99+
expected_dirs = [(Path(test_suite_dir) / d, d) for d in config.dirs]
96100
runtime.compute_argv.assert_any_call(
97-
test_path, config.args, config.env, config.dirs
101+
str(test_path), config.args, config.env, expected_dirs
98102
)
99103
runtime.run_test.assert_called_with(expected_argv)
100104

0 commit comments

Comments
 (0)