Skip to content

Commit c448f53

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 c448f53

File tree

8 files changed

+124
-15
lines changed

8 files changed

+124
-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: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@
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

11+
# pylint: disable-msg=too-many-arguments
1012
def run_all_tests(
1113
runtime: RuntimeAdapter,
1214
test_suite_paths: List[str],
1315
validators: List[Validator],
1416
reporters: List[TestReporter],
1517
filters: List[TestFilter],
18+
override: ConfigOverride,
1619
) -> int:
1720
ret = 0
1821

1922
for test_suite_path in test_suite_paths:
2023
test_suite = run_tests_from_test_suite(
21-
test_suite_path, runtime, validators, reporters, filters,
24+
test_suite_path, runtime, validators, reporters, filters, override
2225
)
2326
for reporter in reporters:
2427
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+
3+
from abc import ABC, abstractmethod
4+
from typing import Any, Dict, Optional
5+
from .test_case import (
6+
Config,
7+
)
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: 15 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,14 +19,17 @@
1919
from .reporters import TestReporter
2020
from .test_suite import TestSuite
2121
from .validators import Validator
22+
from .override import ConfigOverride
2223

2324

25+
# pylint: disable-msg=too-many-arguments, too-many-locals
2426
def run_tests_from_test_suite(
2527
test_suite_path: str,
2628
runtime: RuntimeAdapter,
2729
validators: List[Validator],
2830
reporters: List[TestReporter],
2931
filters: List[TestFilter],
32+
config_override: ConfigOverride,
3033
) -> TestSuite:
3134
test_cases: List[TestCase] = []
3235
test_start = datetime.now()
@@ -45,7 +48,12 @@ def run_tests_from_test_suite(
4548
test_case = _skip_single_test(runtime, validators, test_path)
4649
break
4750
else:
48-
test_case = _execute_single_test(runtime, validators, test_path)
51+
test_config_override = config_override.get_test_override(
52+
test_suite_name, test_name
53+
)
54+
test_case = _execute_single_test(
55+
runtime, validators, test_path, test_config_override
56+
)
4957
test_cases.append(test_case)
5058
for reporter in reporters:
5159
reporter.report_test(test_case)
@@ -73,9 +81,12 @@ def _skip_single_test(
7381

7482

7583
def _execute_single_test(
76-
runtime: RuntimeAdapter, validators: List[Validator], test_path: str
84+
runtime: RuntimeAdapter,
85+
validators: List[Validator],
86+
test_path: str,
87+
config_override: Optional[Config],
7788
) -> TestCase:
78-
config = _read_test_config(test_path)
89+
config = config_override or _read_test_config(test_path)
7990
test_start = time.time()
8091
test_output = runtime.run_test(test_path, config.args, config.env, config.dirs)
8192
elapsed = time.time() - test_start

0 commit comments

Comments
 (0)