Skip to content

Commit 7fdfa69

Browse files
andy31415pull[bot]
authored andcommitted
Make "test cases" have tags for easier selection of what subset is being considered. (#24834)
* Add ability to tag test cases, and include/exclude tags * Restyle * Move examples to be completely ignored * Update for code review * Typo fix * Restyle * Fix logic for tag inclusion * Fix runtime selection logic * Skip slow tests during PR pull requests. Skip flaky tests by default * Use - instead of _ for arguments to fix proper naming * Code review updates * Add notes about why some yamls are removed from chiptest
1 parent 07d8961 commit 7fdfa69

File tree

4 files changed

+241
-95
lines changed

4 files changed

+241
-95
lines changed

.github/workflows/tests.yaml

+23-1
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,30 @@ jobs:
221221
--tv-app ./out/linux-x64-tv-app-${BUILD_VARIANT}/chip-tv-app \
222222
--bridge-app ./out/linux-x64-bridge-${BUILD_VARIANT}/chip-bridge-app \
223223
"
224-
- name: Run Tests using chip-repl
224+
- name: Run Tests using chip-repl (skip slow)
225225
timeout-minutes: 45
226+
if: github.event_name == 'pull_request'
227+
run: |
228+
./scripts/run_in_build_env.sh \
229+
"./scripts/tests/run_test_suite.py \
230+
--run-yamltests-with-chip-repl \
231+
--exclude-tags MANUAL \
232+
--exclude-tags FLAKY \
233+
--exclude-tags IN_DEVELOPMENT \
234+
--exclude-tags SLOW \
235+
run \
236+
--iterations 1 \
237+
--test-timeout-seconds 120 \
238+
--all-clusters-app ./out/linux-x64-all-clusters-${BUILD_VARIANT}/chip-all-clusters-app \
239+
--lock-app ./out/linux-x64-lock-${BUILD_VARIANT}/chip-lock-app \
240+
--ota-provider-app ./out/linux-x64-ota-provider-${BUILD_VARIANT}/chip-ota-provider-app \
241+
--ota-requestor-app ./out/linux-x64-ota-requestor-${BUILD_VARIANT}/chip-ota-requestor-app \
242+
--tv-app ./out/linux-x64-tv-app-${BUILD_VARIANT}/chip-tv-app \
243+
--bridge-app ./out/linux-x64-bridge-${BUILD_VARIANT}/chip-bridge-app \
244+
"
245+
- name: Run Tests using chip-repl (including slow)
246+
timeout-minutes: 45
247+
if: github.event_name == 'push'
226248
run: |
227249
./scripts/run_in_build_env.sh \
228250
"./scripts/tests/run_test_suite.py \

scripts/tests/chiptest/__init__.py

+124-60
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from typing import Iterator, Set
2323

2424
from . import linux, runner
25-
from .test_definition import ApplicationPaths, TestDefinition, TestTarget
25+
from .test_definition import ApplicationPaths, TestDefinition, TestRunTime, TestTag, TestTarget
2626

2727
_DEFAULT_CHIP_ROOT = os.path.abspath(
2828
os.path.join(os.path.dirname(__file__), "..", "..", ".."))
@@ -39,67 +39,118 @@ class ManualTest:
3939
INVALID_TESTS = {
4040
"tests.yaml", # certification/tests.yaml is not a real test
4141
"PICS.yaml", # certification/PICS.yaml is not a real test
42+
43+
# The items below are examples and will never work (likely)
44+
# completely exclude them
45+
"Config_Example.yaml",
46+
"Config_Variables_Example.yaml",
47+
"PICS_Example.yaml",
48+
"Response_Example.yaml",
49+
"Test_Example.yaml",
4250
}
4351

4452

45-
def _LoadManualTestsJson(json_file_path: str) -> Iterator[ManualTest]:
53+
def _IsValidYamlTest(name: str) -> bool:
54+
"""Check if the given file name is a valid YAML test.
55+
56+
This returns invalid for examples, simulated and other specific tests.
57+
"""
58+
59+
# Simulated tests are not runnable by repl tests, need
60+
# separate infrastructure. Exclude them completely (they are
61+
# not even manual)
62+
if name.endswith('_Simulated.yaml'):
63+
return False
64+
65+
return name not in INVALID_TESTS
66+
67+
68+
def _LoadManualTestsJson(json_file_path: str) -> Iterator[str]:
4669
with open(json_file_path, 'rt') as f:
4770
data = json.load(f)
4871
for c in data["collection"]:
4972
for name in data[c]:
50-
yield ManualTest(yaml="%s.yaml" % name, reason=json_file_path)
73+
yield f"{name}.yaml"
5174

5275

53-
def _GetManualTests() -> Set[ManualTest]:
76+
def _GetManualTests() -> Set[str]:
5477
manualtests = set()
5578

56-
# TODO:
57-
#
58-
# These are NOT manual tests, but rather "tests that fail in yaml and
59-
# for this reason are marked as manual".
60-
#
61-
# We are working to get this list down to 0.
62-
manualtests.add(ManualTest(yaml="Test_TC_ACL_2_10.yaml", reason="TODO Event Not Supported Yet"))
63-
manualtests.add(ManualTest(yaml="Test_TC_ACL_2_7.yaml", reason="TODO Event Not Supported Yet"))
64-
manualtests.add(ManualTest(yaml="Test_TC_ACL_2_8.yaml", reason="TODO Event Not Supported Yet"))
65-
manualtests.add(ManualTest(yaml="Test_TC_ACL_2_9.yaml", reason="TODO Event Not Supported Yet"))
66-
manualtests.add(ManualTest(yaml="TestEvents.yaml", reason="TODO Event Not Supported Yet"))
67-
68-
manualtests.add(ManualTest(yaml="Test_TC_ACE_1_1.yaml", reason="TODO GetCommissionerNodeId Not Supported Yet"))
69-
manualtests.add(ManualTest(yaml="Test_TC_ACE_1_5.yaml", reason="TODO GetCommissionerNodeId Not Supported Yet"))
70-
manualtests.add(ManualTest(yaml="Test_TC_SC_5_1.yaml", reason="TODO GetCommissionerNodeId Not Supported Yet"))
71-
manualtests.add(ManualTest(yaml="Test_TC_SC_5_2.yaml", reason="TODO GetCommissionerNodeId Not Supported Yet"))
72-
manualtests.add(ManualTest(yaml="TestCommissionerNodeId.yaml", reason="TODO GetCommissionerNodeId Not Supported Yet"))
73-
74-
manualtests.add(ManualTest(yaml="TestClusterMultiFabric.yaml", reason="TODO Enum Mismatch"))
75-
manualtests.add(ManualTest(yaml="TestGroupMessaging.yaml", reason="TODO Group Message Not Supported in chip-repl yet"))
76-
manualtests.add(ManualTest(yaml="TestMultiAdmin.yaml", reason="TODO chip-repl hangs on command expected to fail"))
77-
78-
# Failing, unclear why. Likely repl specific, used to pass however first
79-
# failure point seems unrelated. Historically this seems (very?) flaky
80-
# in repl.
81-
manualtests.add(ManualTest(yaml="Test_TC_OO_2_4.yaml", reason="Flaky"))
82-
83-
# Examples:
84-
#
85-
# Currently these are not in ciTests.json, however yaml logic currently
86-
# does NOT use allowlist json but rather finds all yaml files.
87-
#
88-
# This is on purpose for now to make it harder to orphan files, however
89-
# we can reconsider as things evolve.
90-
manualtests.add(ManualTest(yaml="Config_Example.yaml", reason="Example"))
91-
manualtests.add(ManualTest(yaml="Config_Variables_Example.yaml", reason="Example"))
92-
manualtests.add(ManualTest(yaml="PICS_Example.yaml", reason="Example"))
93-
manualtests.add(ManualTest(yaml="Response_Example.yaml", reason="Example"))
94-
manualtests.add(ManualTest(yaml="Test_Example.yaml", reason="Example"))
95-
9679
# Flagged as manual from: src/app/tests/suites/manualTests.json
9780
for item in _LoadManualTestsJson(os.path.join(_YAML_TEST_SUITE_PATH, "manualTests.json")):
9881
manualtests.add(item)
9982

10083
return manualtests
10184

10285

86+
def _GetFlakyTests() -> Set[str]:
87+
"""List of flaky tests, ideally this list should become empty."""
88+
return {
89+
"Test_TC_OO_2_4.yaml"
90+
}
91+
92+
93+
def _GetSlowTests() -> Set[str]:
94+
"""Generally tests using sleep() a bit too freely.
95+
96+
10s seems like a good threshold to consider something slow
97+
"""
98+
return {
99+
"DL_LockUnlock.yaml", # ~ 10 seconds
100+
"TestSubscribe_AdministratorCommissioning.yaml", # ~ 15 seconds
101+
"Test_TC_CC_5_1.yaml", # ~ 30 seconds
102+
"Test_TC_CC_5_2.yaml", # ~ 30 seconds
103+
"Test_TC_CC_5_3.yaml", # ~ 25 seconds
104+
"Test_TC_CC_6_1.yaml", # ~ 35 seconds
105+
"Test_TC_CC_6_2.yaml", # ~ 60 seconds
106+
"Test_TC_CC_6_3.yaml", # ~ 50 seconds
107+
"Test_TC_CC_7_2.yaml", # ~ 65 seconds
108+
"Test_TC_CC_7_3.yaml", # ~ 70 seconds
109+
"Test_TC_CC_7_4.yaml", # ~ 25 seconds
110+
"Test_TC_CC_8_1.yaml", # ~ 60 seconds
111+
"Test_TC_DRLK_2_4.yaml", # ~ 60 seconds
112+
"Test_TC_I_2_2.yaml", # ~ 15 seconds
113+
"Test_TC_LVL_3_1.yaml", # ~ 35 seconds
114+
"Test_TC_LVL_4_1.yaml", # ~ 55 seconds
115+
"Test_TC_LVL_5_1.yaml", # ~ 35 seconds
116+
"Test_TC_LVL_6_1.yaml", # ~ 10 seconds
117+
"Test_TC_WNCV_3_1.yaml", # ~ 20 seconds
118+
"Test_TC_WNCV_3_2.yaml", # ~ 20 seconds
119+
"Test_TC_WNCV_3_3.yaml", # ~ 15 seconds
120+
"Test_TC_WNCV_3_4.yaml", # ~ 10 seconds
121+
"Test_TC_WNCV_3_5.yaml", # ~ 10 seconds
122+
"Test_TC_WNCV_4_1.yaml", # ~ 20 seconds
123+
"Test_TC_WNCV_4_2.yaml", # ~ 20 seconds
124+
"Test_TC_WNCV_4_5.yaml", # ~ 12 seconds
125+
}
126+
127+
128+
def _GetInDevelopmentTests() -> Set[str]:
129+
"""Tests that fail in YAML for some reason.
130+
131+
Goal is for this set to become empty.
132+
"""
133+
return {
134+
# TODO: Event not yet supported:
135+
"Test_TC_ACL_2_10.yaml",
136+
"Test_TC_ACL_2_7.yaml",
137+
"Test_TC_ACL_2_8.yaml",
138+
"Test_TC_ACL_2_9.yaml",
139+
"TestEvents.yaml",
140+
141+
# TODO: CommissionerNodeId not yet supported:
142+
"Test_TC_ACE_1_1.yaml",
143+
"Test_TC_ACE_1_5.yaml",
144+
"Test_TC_SC_5_1.yaml",
145+
"Test_TC_SC_5_2.yaml",
146+
"TestCommissionerNodeId.yaml",
147+
148+
"TestClusterMultiFabric.yaml", # Enum mismatch
149+
"TestGroupMessaging.yaml", # Needs group support in repl
150+
"TestMultiAdmin.yaml", # chip-repl hang on command expeted to fail
151+
}
152+
153+
103154
def _AllYamlTests():
104155
yaml_test_suite_path = Path(_YAML_TEST_SUITE_PATH)
105156

@@ -111,12 +162,6 @@ def _AllYamlTests():
111162
if not path.is_file():
112163
continue
113164

114-
if path.name.endswith('_Simulated.yaml'):
115-
# Simulated tests are not runnable by repl tests, need
116-
# separate infrastructure. Exclude theml completely (they are
117-
# not even manual)
118-
continue
119-
120165
yield path
121166

122167

@@ -142,41 +187,60 @@ def tests_with_command(chip_tool: str, is_manual: bool):
142187

143188
result = subprocess.run([chip_tool, "tests", cmd], capture_output=True)
144189

190+
test_tags = set()
191+
if is_manual:
192+
test_tags.add(TestTag.MANUAL)
193+
145194
for name in result.stdout.decode("utf8").split("\n"):
146195
if not name:
147196
continue
148197

149198
target = target_for_name(name)
150199

151200
yield TestDefinition(
152-
run_name=name, name=name, target=target, is_manual=is_manual
201+
run_name=name, name=name, target=target, tags=test_tags
153202
)
154203

155204

156205
# TODO We will move away from hardcoded list of yamltests to run all file when yamltests
157206
# parser/runner reaches parity with the code gen version.
158207
def _hardcoded_python_yaml_tests():
159-
manual_tests = set([b.yaml for b in _GetManualTests()])
208+
manual_tests = _GetManualTests()
209+
flaky_tests = _GetFlakyTests()
210+
slow_tests = _GetSlowTests()
211+
in_development_tests = _GetInDevelopmentTests()
160212

161213
for path in _AllYamlTests():
162-
if path.name in INVALID_TESTS:
214+
if not _IsValidYamlTest(path.name):
163215
continue
164216

217+
tags = set()
218+
if path.name in manual_tests:
219+
tags.add(TestTag.MANUAL)
220+
221+
if path.name in flaky_tests:
222+
tags.add(TestTag.FLAKY)
223+
224+
if path.name in slow_tests:
225+
tags.add(TestTag.SLOW)
226+
227+
if path.name in in_development_tests:
228+
tags.add(TestTag.IN_DEVELOPMENT)
229+
165230
yield TestDefinition(
166231
run_name=str(path),
167232
name=path.stem, # `path.stem` converts "some/path/Test_ABC_1.2.yaml" to "Test_ABC.1.2"
168233
target=target_for_name(path.name),
169-
is_manual=path.name in manual_tests,
170-
use_chip_repl_yaml_tester=True
234+
tags=tags,
171235
)
172236

173237

174-
def AllTests(chip_tool: str, run_yamltests_with_chip_repl: bool):
175-
if run_yamltests_with_chip_repl:
176-
for test in _hardcoded_python_yaml_tests():
177-
yield test
178-
return
238+
def AllYamlTests():
239+
for test in _hardcoded_python_yaml_tests():
240+
yield test
241+
179242

243+
def AllChipToolTests(chip_tool: str):
180244
for test in tests_with_command(chip_tool, is_manual=False):
181245
yield test
182246

scripts/tests/chiptest/test_definition.py

+38-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import threading
2020
import time
2121
import typing
22-
from dataclasses import dataclass
22+
from dataclasses import dataclass, field
2323
from datetime import datetime
2424
from enum import Enum, auto
2525
from random import randrange
@@ -210,15 +210,48 @@ def LogContents(self):
210210
logging.error('================ CAPTURED LOG END ====================')
211211

212212

213+
class TestTag(Enum):
214+
MANUAL = auto() # requires manual input. Generally not run automatically
215+
SLOW = auto() # test uses Sleep and is generally slow (>=10s is a typical threshold)
216+
FLAKY = auto() # test is considered flaky (usually a bug/time dependent issue)
217+
IN_DEVELOPMENT = auto() # test may not pass or undergoes changes
218+
219+
def to_s(self):
220+
for (k, v) in TestTag.__members__.items():
221+
if self == v:
222+
return k
223+
raise Exception("Unknown tag: %r" % self)
224+
225+
226+
class TestRunTime(Enum):
227+
CHIP_TOOL_BUILTIN = auto() # run via chip-tool built-in test commands
228+
PYTHON_YAML = auto() # use the python yaml test runner
229+
230+
213231
@dataclass
214232
class TestDefinition:
215233
name: str
216234
run_name: str
217235
target: TestTarget
218-
is_manual: bool
219-
use_chip_repl_yaml_tester: bool = False
236+
tags: typing.Set[TestTag] = field(default_factory=set)
237+
238+
@property
239+
def is_manual(self) -> bool:
240+
return TestTag.MANUAL in self.tags
241+
242+
@property
243+
def is_slow(self) -> bool:
244+
return TestTag.SLOW in self.tags
245+
246+
@property
247+
def is_flaky(self) -> bool:
248+
return TestTag.FLAKY in self.tags
249+
250+
def tags_str(self) -> str:
251+
"""Get a human readable list of tags applied to this test"""
252+
return ", ".join([t.to_s() for t in self.tags])
220253

221-
def Run(self, runner, apps_register, paths: ApplicationPaths, pics_file: str, timeout_seconds: typing.Optional[int], dry_run=False):
254+
def Run(self, runner, apps_register, paths: ApplicationPaths, pics_file: str, timeout_seconds: typing.Optional[int], dry_run=False, test_runtime: TestRunTime = TestRunTime.CHIP_TOOL_BUILTIN):
222255
"""
223256
Executes the given test case using the provided runner for execution.
224257
"""
@@ -280,7 +313,7 @@ def Run(self, runner, apps_register, paths: ApplicationPaths, pics_file: str, ti
280313
if dry_run:
281314
logging.info(" ".join(pairing_cmd))
282315
logging.info(" ".join(test_cmd))
283-
elif self.use_chip_repl_yaml_tester:
316+
elif test_runtime == TestRunTime.PYTHON_YAML:
284317
chip_repl_yaml_tester_cmd = paths.chip_repl_yaml_tester_cmd
285318
python_cmd = chip_repl_yaml_tester_cmd + \
286319
['--setup-code', app.setupCode] + ['--yaml-path', self.run_name] + ["--pics-file", pics_file]

0 commit comments

Comments
 (0)