Skip to content

Commit 2c8ce34

Browse files
bzbarsky-applepull[bot]
authored andcommitted
Ensure we don't run slow YAML tests in CI. (#19975)
* Move some existing slow YAML tests to the "manual" list. * Modify the test runner script so it can run manual tests if you explicitly tell it to. * Add a way to specify a per-YAML-file timeout to the runner script. * Use a 2-minute timeout in CI so that any YAML file that takes longer than that will fail.
1 parent 84da00c commit 2c8ce34

File tree

10 files changed

+14517
-16801
lines changed

10 files changed

+14517
-16801
lines changed

.github/workflows/darwin-tests.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ jobs:
104104
--target-skip-glob '{TestGroupMessaging}' \
105105
run \
106106
--iterations 1 \
107+
--test-timeout-seconds 120 \
107108
--all-clusters-app ./out/darwin-x64-all-clusters-${BUILD_VARIANT}/chip-all-clusters-app \
108109
--lock-app ./out/darwin-x64-lock-${BUILD_VARIANT}/chip-lock-app \
109110
--ota-provider-app ./out/darwin-x64-ota-provider-${BUILD_VARIANT}/chip-ota-provider-app \

.github/workflows/tests.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ jobs:
9797
--chip-tool ./out/linux-x64-chip-tool${CHIP_TOOL_VARIANT}-${BUILD_VARIANT}/chip-tool \
9898
run \
9999
--iterations 1 \
100+
--test-timeout-seconds 120 \
100101
--all-clusters-app ./out/linux-x64-all-clusters-${BUILD_VARIANT}/chip-all-clusters-app \
101102
--lock-app ./out/linux-x64-lock-${BUILD_VARIANT}/chip-lock-app \
102103
--ota-provider-app ./out/linux-x64-ota-provider-${BUILD_VARIANT}/chip-ota-provider-app \
@@ -198,6 +199,7 @@ jobs:
198199
--target-skip-glob '{TestGroupMessaging,Test_TC_DIAG_TH_NW_1_1,Test_TC_DIAG_TH_NW_1_2,Test_TC_DIAG_TH_NW_2_2,Test_TC_DIAG_TH_NW_2_3,Test_TC_DIAG_TH_NW_2_6,Test_TC_DIAG_TH_NW_2_7,Test_TC_DIAG_TH_NW_2_8,Test_TC_DIAG_TH_NW_2_9}' \
199200
run \
200201
--iterations 1 \
202+
--test-timeout-seconds 120 \
201203
--all-clusters-app ./out/darwin-x64-all-clusters-${BUILD_VARIANT}/chip-all-clusters-app \
202204
--lock-app ./out/darwin-x64-lock-${BUILD_VARIANT}/chip-lock-app \
203205
--ota-provider-app ./out/darwin-x64-ota-provider-${BUILD_VARIANT}/chip-ota-provider-app \

examples/darwin-framework-tool/templates/tests/tests.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,16 @@ function getTests() {
5858
tests.disable('Test_TC_DIAG_TH_NW_2_3');
5959

6060
// TODO: Test_TC_CC_9_1 does not work on Darwin for now.
61-
tests.disable('Test_TC_CC_9_1');
61+
// But is disabled in CI, so we can't disable it here.
62+
//tests.disable('Test_TC_CC_9_1');
6263

6364
// TODO: Test_TC_CC_9_2 does not work on Darwin for now.
64-
tests.disable('Test_TC_CC_9_2');
65+
// But is disabled in CI, so we can't disable it here.
66+
//tests.disable('Test_TC_CC_9_2');
6567

6668
// TODO: Test_TC_CC_9_3 does not work on Darwin for now.
67-
tests.disable('Test_TC_CC_9_3');
69+
// But is disabled in CI, so we can't disable it here.
70+
//tests.disable('Test_TC_CC_9_3');
6871

6972
// TODO: Test_TC_MC_3_7 does not work on Darwin for now.
7073
tests.disable('Test_TC_MC_3_7');

scripts/tests/chiptest/__init__.py

+27-12
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,41 @@
2020
from .test_definition import ApplicationPaths, TestDefinition, TestTarget
2121

2222

23-
def AllTests(chip_tool: str):
24-
"""Executes `chip_tool` binary to see what tests are available.
23+
def target_for_name(name: str):
24+
if name.startswith('TV_') or name.startswith('Test_TC_MC_'):
25+
return TestTarget.TV
26+
if name.startswith('DL_') or name.startswith('Test_TC_DL_'):
27+
return TestTarget.LOCK
28+
if name.startswith('OTA_'):
29+
return TestTarget.OTA
30+
return TestTarget.ALL_CLUSTERS
31+
32+
33+
def tests_with_command(chip_tool: str, is_manual: bool):
34+
"""Executes `chip_tool` binary to see what tests are available, using cmd
35+
to get the list.
2536
"""
37+
cmd = 'list'
38+
if is_manual:
39+
cmd += '-manual'
2640

27-
result = subprocess.run([chip_tool, 'tests', 'list'], capture_output=True)
41+
result = subprocess.run([chip_tool, 'tests', cmd], capture_output=True)
2842

2943
for name in result.stdout.decode('utf8').split('\n'):
3044
if not name:
3145
continue
3246

33-
if name.startswith('TV_') or name.startswith('Test_TC_MC_'):
34-
target = TestTarget.TV
35-
elif name.startswith('DL_') or name.startswith('Test_TC_DL_'):
36-
target = TestTarget.LOCK
37-
elif name.startswith('OTA_'):
38-
target = TestTarget.OTA
39-
else:
40-
target = TestTarget.ALL_CLUSTERS
47+
target = target_for_name(name)
48+
49+
yield TestDefinition(run_name=name, name=name, target=target, is_manual=is_manual)
50+
51+
52+
def AllTests(chip_tool: str):
53+
for test in tests_with_command(chip_tool, is_manual=False):
54+
yield test
4155

42-
yield TestDefinition(run_name=name, name=name, target=target)
56+
for test in tests_with_command(chip_tool, is_manual=True):
57+
yield test
4358

4459

4560
__all__ = [

scripts/tests/chiptest/runner.py

+22-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import re
2020
import subprocess
2121
import threading
22+
import typing
2223

2324

2425
class LogPipe(threading.Thread):
@@ -87,12 +88,24 @@ def close(self):
8788

8889

8990
class RunnerWaitQueue:
90-
91-
def __init__(self):
91+
def __init__(self, timeout_seconds: typing.Optional[int]):
9292
self.queue = queue.Queue()
93+
self.timeout_seconds = timeout_seconds
94+
self.timed_out = False
9395

9496
def __wait(self, process, userdata):
95-
process.wait()
97+
if userdata is None:
98+
# We're the main process for this wait queue.
99+
timeout = self.timeout_seconds
100+
else:
101+
timeout = None
102+
try:
103+
process.wait(timeout)
104+
except subprocess.TimeoutExpired:
105+
self.timed_out = True
106+
process.kill()
107+
# And wait for the kill() to kill it.
108+
process.wait()
96109
self.queue.put((process, userdata))
97110

98111
def add_process(self, process, userdata=None):
@@ -109,7 +122,7 @@ class Runner:
109122
def __init__(self, capture_delegate=None):
110123
self.capture_delegate = capture_delegate
111124

112-
def RunSubprocess(self, cmd, name, wait=True, dependencies=[]):
125+
def RunSubprocess(self, cmd, name, wait=True, dependencies=[], timeout_seconds: typing.Optional[int] = None):
113126
outpipe = LogPipe(
114127
logging.DEBUG, capture_delegate=self.capture_delegate,
115128
name=name + ' OUT')
@@ -127,7 +140,7 @@ def RunSubprocess(self, cmd, name, wait=True, dependencies=[]):
127140
if not wait:
128141
return s, outpipe, errpipe
129142

130-
wait = RunnerWaitQueue()
143+
wait = RunnerWaitQueue(timeout_seconds=timeout_seconds)
131144
wait.add_process(s)
132145

133146
for dependency in dependencies:
@@ -143,6 +156,9 @@ def RunSubprocess(self, cmd, name, wait=True, dependencies=[]):
143156
(process.returncode, userdata))
144157

145158
if s.returncode != 0:
146-
raise Exception('Command %r failed: %d' % (cmd, s.returncode))
159+
if wait.timed_out:
160+
raise Exception("Command %r exceeded test timeout (%d seconds)" % (cmd, wait.timeout_seconds))
161+
else:
162+
raise Exception('Command %r failed: %d' % (cmd, s.returncode))
147163

148164
logging.debug('Command %r completed with error code 0', cmd)

scripts/tests/chiptest/test_definition.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,9 @@ class TestDefinition:
207207
name: str
208208
run_name: str
209209
target: TestTarget
210+
is_manual: bool
210211

211-
def Run(self, runner, apps_register, paths: ApplicationPaths, pics_file: str):
212+
def Run(self, runner, apps_register, paths: ApplicationPaths, pics_file: str, timeout_seconds: typing.Optional[int]):
212213
"""
213214
Executes the given test case using the provided runner for execution.
214215
"""
@@ -269,7 +270,8 @@ def Run(self, runner, apps_register, paths: ApplicationPaths, pics_file: str):
269270
test_cmd = tool_cmd + ['tests', self.run_name] + ['--PICS', pics_file]
270271
runner.RunSubprocess(
271272
test_cmd,
272-
name='TEST', dependencies=[apps_register])
273+
name='TEST', dependencies=[apps_register],
274+
timeout_seconds=timeout_seconds)
273275

274276
except Exception:
275277
logging.error("!!!!!!!!!!!!!!!!!!!! ERROR !!!!!!!!!!!!!!!!!!!!!!")

scripts/tests/run_test_suite.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ def main(context, log_level, target, target_glob, target_skip_glob,
119119

120120
# Figures out selected test that match the given name(s)
121121
all_tests = [test for test in chiptest.AllTests(chip_tool)]
122-
tests = all_tests
122+
123+
# Default to only non-manual tests unless explicit targets are specified.
124+
tests = list(filter(lambda test: not test.is_manual, all_tests))
123125
if 'all' not in target:
124126
tests = []
125127
for name in target:
@@ -131,6 +133,7 @@ def main(context, log_level, target, target_glob, target_skip_glob,
131133

132134
if target_glob:
133135
matcher = GlobMatcher(target_glob.lower())
136+
# Globs ignore manual tests, because it's too easy to mess up otherwise.
134137
tests = [test for test in tests if matcher.matches(test.name.lower())]
135138

136139
if len(tests) == 0:
@@ -185,8 +188,13 @@ def cmd_list(context):
185188
type=click.Path(exists=True),
186189
default="src/app/tests/suites/certification/ci-pics-values",
187190
help='PICS file to use for test runs.')
191+
@click.option(
192+
'--test-timeout-seconds',
193+
default=None,
194+
type=int,
195+
help='If provided, fail if a test runs for longer than this time')
188196
@click.pass_context
189-
def cmd_run(context, iterations, all_clusters_app, lock_app, ota_provider_app, ota_requestor_app, tv_app, pics_file):
197+
def cmd_run(context, iterations, all_clusters_app, lock_app, ota_provider_app, ota_requestor_app, tv_app, pics_file, test_timeout_seconds):
190198
runner = chiptest.runner.Runner()
191199

192200
if all_clusters_app is None:
@@ -237,7 +245,7 @@ def cmd_run(context, iterations, all_clusters_app, lock_app, ota_provider_app, o
237245
for test in context.obj.tests:
238246
test_start = time.monotonic()
239247
try:
240-
test.Run(runner, apps_register, paths, pics_file)
248+
test.Run(runner, apps_register, paths, pics_file, test_timeout_seconds)
241249
test_end = time.monotonic()
242250
logging.info('%-20s - Completed in %0.2f seconds' %
243251
(test.name, (test_end - test_start)))

src/app/tests/suites/tests.js

+10-8
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,12 @@ function getManualTests() {
250250
'Test_TC_MF_1_26',
251251
'Test_TC_MF_1_27',
252252
'Test_TC_MF_1_28',
253+
// Slow tests that should not run in CI because they take many minutes each
254+
'Test_TC_MF_1_5',
255+
'Test_TC_MF_1_6',
256+
'Test_TC_MF_1_9',
257+
'Test_TC_MF_1_10',
258+
'Test_TC_MF_1_15',
253259
];
254260

255261
const ModeSelect = [
@@ -365,6 +371,10 @@ function getManualTests() {
365371
'Test_TC_CC_6_4',
366372
'Test_TC_CC_7_5',
367373
'Test_TC_CC_9_4',
374+
// Slow tests that should not run in CI because they take many minutes each
375+
'Test_TC_CC_9_1',
376+
'Test_TC_CC_9_2',
377+
'Test_TC_CC_9_3',
368378
];
369379

370380
const DoorLock = [
@@ -558,9 +568,6 @@ function getTests() {
558568
'Test_TC_CC_7_3',
559569
'Test_TC_CC_7_4',
560570
'Test_TC_CC_8_1',
561-
'Test_TC_CC_9_1',
562-
'Test_TC_CC_9_2',
563-
'Test_TC_CC_9_3',
564571
];
565572

566573
const DeviceManagement = [
@@ -677,11 +684,6 @@ function getTests() {
677684
const MultipleFabrics = [
678685
'Test_TC_MF_1_3',
679686
'Test_TC_MF_1_4',
680-
'Test_TC_MF_1_5',
681-
'Test_TC_MF_1_6',
682-
'Test_TC_MF_1_9',
683-
'Test_TC_MF_1_10',
684-
'Test_TC_MF_1_15',
685687
];
686688

687689
const OTASoftwareUpdate = [

0 commit comments

Comments
 (0)