Skip to content

Commit 6409602

Browse files
committed
Add support for test config overrides
It can be useful to override the config for certain tests. The main use case for this is to override environment variables which control test assertions (e.g. NO_DANGLING_FILESYSTEM, ERRNO_MODE_WINDOWS). When running the tests in CI, it would be nicer to support this without manually modifying the JSON test config files.
1 parent 047a002 commit 6409602

File tree

8 files changed

+122
-15
lines changed

8 files changed

+122
-15
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,21 @@ python3 test-runner/wasi_test_runner.py
6262
-r adapters/wasmtime.py # path to a runtime adapter
6363
```
6464

65+
You can also optionally override test configs with the `--config-override`
66+
option:
67+
68+
```bash
69+
python3 test-runner/wasi_test_runner.py \
70+
-t ./tests/assemblyscript/testsuite/ # path to folders containing .wasm test files \
71+
./tests/c/testsuite/ \
72+
./tests/rust/testsuite/ \
73+
--config-override examples/config_override.json \
74+
-r adapters/wasmtime.py # path to a runtime adapter
75+
```
76+
77+
This can be useful for passing additional environment variables to certain
78+
tests without modifying the test config files.
79+
6580
The default executable in the adapter used for test execution can be
6681
overridden using `TEST_RUNTIME_EXE` variable. This only works with adapters defined in
6782
[adapters/](adapters/), and might not work with 3rd party adapters.

examples/config_override.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"WASI C tests": {
3+
"stat-dev-ino": {
4+
"dirs": [
5+
"fs-tests.dir"
6+
],
7+
"env": {
8+
"TEST_VAR": "test"
9+
},
10+
"args": [
11+
"fs-tests.dir",
12+
"test_arg"
13+
]
14+
}
15+
}
16+
}

test-runner/tests/test_test_suite_runner.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import wasi_test_runner.test_case as tc
55
import wasi_test_runner.test_suite_runner as tsr
6+
from wasi_test_runner.override import StubConfigOverride
67

78

89
def get_mock_open() -> Mock:
@@ -67,7 +68,9 @@ def test_runner_end_to_end() -> None:
6768
filters = [filt]
6869

6970
with patch("glob.glob", return_value=test_paths):
70-
suite = tsr.run_tests_from_test_suite("my-path", runtime, validators, reporters, filters) # type: ignore
71+
suite = tsr.run_tests_from_test_suite(
72+
"my-path", runtime, validators, reporters, filters, StubConfigOverride() # type: ignore
73+
)
7174

7275
# Assert manifest was read correctly
7376
assert suite.name == "test-suite"
@@ -104,6 +107,8 @@ def test_runner_end_to_end() -> None:
104107

105108
@patch("os.path.exists", Mock(return_value=False))
106109
def test_runner_should_use_path_for_name_if_manifest_does_not_exist() -> None:
107-
suite = tsr.run_tests_from_test_suite("my-path", Mock(), [], [], [])
110+
suite = tsr.run_tests_from_test_suite(
111+
"my-path", Mock(), [], [], [], StubConfigOverride()
112+
)
108113

109114
assert suite.name == "my-path"

test-runner/wasi_test_runner/__main__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .reporters.console import ConsoleTestReporter
1212
from .reporters.json import JSONTestReporter
1313
from .validators import exit_code_validator, stdout_validator, Validator
14+
from .override import ConfigOverride, JSONConfigOverride, StubConfigOverride
1415

1516

1617
def main() -> int:
@@ -46,6 +47,11 @@ def main() -> int:
4647
default=False,
4748
help="Disables color for console output reporter.",
4849
)
50+
parser.add_argument(
51+
"--config-override",
52+
required=False,
53+
help="Location of JSON file containing overrides for the config used for each test",
54+
)
4955

5056
options = parser.parse_args()
5157

@@ -59,12 +65,17 @@ def main() -> int:
5965
for filt in options.exclude_filter:
6066
filters.append(JSONTestExcludeFilter(filt))
6167

68+
override: ConfigOverride = StubConfigOverride()
69+
if options.config_override:
70+
override = JSONConfigOverride(options.config_override)
71+
6272
return run_all_tests(
6373
RuntimeAdapter(options.runtime_adapter),
6474
options.test_suite,
6575
validators,
6676
reporters,
6777
filters,
78+
override,
6879
)
6980

7081

test-runner/wasi_test_runner/harness.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .test_suite_runner import run_tests_from_test_suite
66
from .runtime_adapter import RuntimeAdapter
77
from .validators import Validator
8+
from .override import ConfigOverride
89

910

1011
def run_all_tests(
@@ -13,12 +14,13 @@ def run_all_tests(
1314
validators: List[Validator],
1415
reporters: List[TestReporter],
1516
filters: List[TestFilter],
17+
override: ConfigOverride,
1618
) -> int:
1719
ret = 0
1820

1921
for test_suite_path in test_suite_paths:
2022
test_suite = run_tests_from_test_suite(
21-
test_suite_path, runtime, validators, reporters, filters,
23+
test_suite_path, runtime, validators, reporters, filters, override
2224
)
2325
for reporter in reporters:
2426
reporter.report_test_suite(test_suite)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import json
2+
from typing import Any, Dict, Optional
3+
from .test_case import (
4+
Config,
5+
)
6+
from abc import ABC
7+
from abc import abstractmethod
8+
9+
10+
class ConfigOverride(ABC):
11+
@abstractmethod
12+
def get_test_override(
13+
self, test_suite_name: str, test_name: str
14+
) -> Optional[Config]:
15+
pass
16+
17+
18+
class JSONConfigOverride(ConfigOverride):
19+
overrides_dict: Dict[str, Dict[str, Dict[str, Any]]]
20+
21+
def __init__(self, overrides_path: str) -> None:
22+
with open(overrides_path, encoding="utf-8") as file:
23+
self.overrides_dict = json.load(file)
24+
25+
def get_test_override(
26+
self, test_suite_name: str, test_name: str
27+
) -> Optional[Config]:
28+
test_suite_overrides = self.overrides_dict.get(test_suite_name)
29+
30+
if test_suite_overrides is None:
31+
return None
32+
33+
test_override = test_suite_overrides.get(test_name)
34+
35+
if test_override is None:
36+
return None
37+
38+
return Config.from_dict(test_override)
39+
40+
41+
class StubConfigOverride(ConfigOverride):
42+
def get_test_override(
43+
self, test_suite_name: str, test_name: str
44+
) -> Optional[Config]:
45+
return None

test-runner/wasi_test_runner/test_case.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,19 @@ class Config(NamedTuple):
3737

3838
@classmethod
3939
def from_file(cls: Type[T], config_file: str) -> T:
40-
default = cls()
41-
4240
with open(config_file, encoding="utf-8") as file:
4341
dict_config = json.load(file)
42+
return cls.from_dict(dict_config)
43+
44+
@classmethod
45+
def _validate_dict(cls: Type[T], dict_config: Dict[str, Any]) -> None:
46+
for field_name in dict_config:
47+
if field_name not in cls._fields:
48+
logging.warning("Unknown field in the config file: %s", field_name)
49+
50+
@classmethod
51+
def from_dict(cls: Type[T], dict_config: Dict[str, Any]) -> T:
52+
default = cls()
4453

4554
cls._validate_dict(dict_config)
4655

@@ -53,12 +62,6 @@ def from_file(cls: Type[T], config_file: str) -> T:
5362
stdout=dict_config.get("stdout", default.stdout),
5463
)
5564

56-
@classmethod
57-
def _validate_dict(cls: Type[T], dict_config: Dict[str, Any]) -> None:
58-
for field_name in dict_config:
59-
if field_name not in cls._fields:
60-
logging.warning("Unknown field in the config file: %s", field_name)
61-
6265

6366
class TestCase(NamedTuple):
6467
name: str

test-runner/wasi_test_runner/test_suite_runner.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import time
77

88
from datetime import datetime
9-
from typing import List, cast
9+
from typing import List, Optional, cast
1010

1111
from .filters import TestFilter
1212
from .runtime_adapter import RuntimeAdapter
@@ -19,6 +19,7 @@
1919
from .reporters import TestReporter
2020
from .test_suite import TestSuite
2121
from .validators import Validator
22+
from .override import ConfigOverride
2223

2324

2425
def run_tests_from_test_suite(
@@ -27,6 +28,7 @@ def run_tests_from_test_suite(
2728
validators: List[Validator],
2829
reporters: List[TestReporter],
2930
filters: List[TestFilter],
31+
config_override: ConfigOverride,
3032
) -> TestSuite:
3133
test_cases: List[TestCase] = []
3234
test_start = datetime.now()
@@ -45,7 +47,12 @@ def run_tests_from_test_suite(
4547
test_case = _skip_single_test(runtime, validators, test_path)
4648
break
4749
else:
48-
test_case = _execute_single_test(runtime, validators, test_path)
50+
test_config_override = config_override.get_test_override(
51+
test_suite_name, test_name
52+
)
53+
test_case = _execute_single_test(
54+
runtime, validators, test_path, test_config_override
55+
)
4956
test_cases.append(test_case)
5057
for reporter in reporters:
5158
reporter.report_test(test_case)
@@ -73,9 +80,12 @@ def _skip_single_test(
7380

7481

7582
def _execute_single_test(
76-
runtime: RuntimeAdapter, validators: List[Validator], test_path: str
83+
runtime: RuntimeAdapter,
84+
validators: List[Validator],
85+
test_path: str,
86+
config_override: Optional[Config],
7787
) -> TestCase:
78-
config = _read_test_config(test_path)
88+
config = config_override or _read_test_config(test_path)
7989
test_start = time.time()
8090
test_output = runtime.run_test(test_path, config.args, config.env, config.dirs)
8191
elapsed = time.time() - test_start

0 commit comments

Comments
 (0)