Skip to content

Commit 27f6a5e

Browse files
Yuval Peressnashif
authored andcommitted
ztest: add a weak implementation of test_main()
Introduce a weak implementation of test_main() which calls: * ztest_run_registered_test_suites(NULL); * ztest_verify_all_registered_test_suites_ran(); This will attempt to run all registered test suites and verify that they each ran. Signed-off-by: Yuval Peress <[email protected]>
1 parent dee79d2 commit 27f6a5e

File tree

8 files changed

+106
-9
lines changed

8 files changed

+106
-9
lines changed

scripts/pylib/twister/twisterlib.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,16 +1589,20 @@ class ScanPathResult:
15891589
has_run_registered_test_suites Whether or not the path contained at
15901590
least one call to
15911591
ztest_run_registered_test_suites.
1592+
has_test_main Whether or not the path contains a
1593+
definition of test_main(void)
15921594
"""
15931595
def __init__(self,
15941596
matches: List[str] = None,
15951597
warnings: str = None,
15961598
has_registered_test_suites: bool = False,
1597-
has_run_registered_test_suites: bool = False):
1599+
has_run_registered_test_suites: bool = False,
1600+
has_test_main: bool = False):
15981601
self.matches = matches
15991602
self.warnings = warnings
16001603
self.has_registered_test_suites = has_registered_test_suites
16011604
self.has_run_registered_test_suites = has_run_registered_test_suites
1605+
self.has_test_main = has_test_main
16021606

16031607
def __eq__(self, other):
16041608
if not isinstance(other, ScanPathResult):
@@ -1608,7 +1612,8 @@ def __eq__(self, other):
16081612
(self.has_registered_test_suites ==
16091613
other.has_registered_test_suites) and
16101614
(self.has_run_registered_test_suites ==
1611-
other.has_run_registered_test_suites))
1615+
other.has_run_registered_test_suites) and
1616+
self.has_test_main == other.has_test_main)
16121617

16131618

16141619
class TestCase(DisablePyTestCollectionMixin):
@@ -1701,6 +1706,15 @@ def scan_file(inf_name):
17011706
br"^\s*ztest_register_test_suite"
17021707
br"\(\s*(?P<suite_name>[a-zA-Z0-9_]+)\s*,",
17031708
re.MULTILINE)
1709+
# Checks if the file contains a definition of "void test_main(void)"
1710+
# Since ztest provides a plain test_main implementation it is OK to:
1711+
# 1. register test suites and not call the run function iff the test
1712+
# doesn't have a custom test_main.
1713+
# 2. register test suites and a custom test_main definition iff the test
1714+
# also calls ztest_run_registered_test_suites.
1715+
test_main_regex = re.compile(
1716+
br"^\s*void\s+test_main\(void\)",
1717+
re.MULTILINE)
17041718
stc_regex = re.compile(
17051719
br"""^\s* # empy space at the beginning is ok
17061720
# catch the case where it is declared in the same sentence, e.g:
@@ -1733,6 +1747,7 @@ def scan_file(inf_name):
17331747
warnings = None
17341748
has_registered_test_suites = False
17351749
has_run_registered_test_suites = False
1750+
has_test_main = False
17361751

17371752
with open(inf_name) as inf:
17381753
if os.name == 'nt':
@@ -1750,6 +1765,8 @@ def scan_file(inf_name):
17501765
has_registered_test_suites = True
17511766
if registered_suite_run_regex.search(main_c):
17521767
has_run_registered_test_suites = True
1768+
if test_main_regex.search(main_c):
1769+
has_test_main = True
17531770

17541771
if not suite_regex_match and not has_registered_test_suites:
17551772
# can't find ztest_test_suite, maybe a client, because
@@ -1758,7 +1775,8 @@ def scan_file(inf_name):
17581775
matches=None,
17591776
warnings=None,
17601777
has_registered_test_suites=has_registered_test_suites,
1761-
has_run_registered_test_suites=has_run_registered_test_suites)
1778+
has_run_registered_test_suites=has_run_registered_test_suites,
1779+
has_test_main=has_test_main)
17621780

17631781
suite_run_match = suite_run_regex.search(main_c)
17641782
if suite_regex_match and not suite_run_match:
@@ -1792,12 +1810,14 @@ def scan_file(inf_name):
17921810
matches=matches,
17931811
warnings=warnings,
17941812
has_registered_test_suites=has_registered_test_suites,
1795-
has_run_registered_test_suites=has_run_registered_test_suites)
1813+
has_run_registered_test_suites=has_run_registered_test_suites,
1814+
has_test_main=has_test_main)
17961815

17971816
def scan_path(self, path):
17981817
subcases = []
17991818
has_registered_test_suites = False
18001819
has_run_registered_test_suites = False
1820+
has_test_main = False
18011821
for filename in glob.glob(os.path.join(path, "src", "*.c*")):
18021822
try:
18031823
result: ScanPathResult = self.scan_file(filename)
@@ -1811,6 +1831,8 @@ def scan_path(self, path):
18111831
has_registered_test_suites = True
18121832
if result.has_run_registered_test_suites:
18131833
has_run_registered_test_suites = True
1834+
if result.has_test_main:
1835+
has_test_main = True
18141836
except ValueError as e:
18151837
logger.error("%s: can't find: %s" % (filename, e))
18161838

@@ -1824,7 +1846,8 @@ def scan_path(self, path):
18241846
except ValueError as e:
18251847
logger.error("%s: can't find: %s" % (filename, e))
18261848

1827-
if has_registered_test_suites and not has_run_registered_test_suites:
1849+
if (has_registered_test_suites and has_test_main and
1850+
not has_run_registered_test_suites):
18281851
warning = \
18291852
"Found call to 'ztest_register_test_suite()' but no "\
18301853
"call to 'ztest_run_registered_test_suites()'"

scripts/tests/twister/test_testinstance.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,24 +123,28 @@ def test_get_unique_exception(testcase_root, workdir, name, exception):
123123
'test_test_aa',
124124
'user', 'last'],
125125
has_registered_test_suites=False,
126-
has_run_registered_test_suites=False)),
126+
has_run_registered_test_suites=False,
127+
has_test_main=False)),
127128
("testcases/tests/test_a/test_ztest_error.c",
128129
ScanPathResult(
129130
warnings="Found a test that does not start with test_",
130131
matches=['1a', '1c', '2a', '2b'],
131132
has_registered_test_suites=False,
132-
has_run_registered_test_suites=False)),
133+
has_run_registered_test_suites=False,
134+
has_test_main=True)),
133135
("testcases/tests/test_a/test_ztest_error_1.c",
134136
ScanPathResult(
135137
warnings="found invalid #ifdef, #endif in ztest_test_suite()",
136138
matches=['unit_1a', 'unit_1b', 'Unit_1c'],
137139
has_registered_test_suites=False,
138-
has_run_registered_test_suites=False)),
140+
has_run_registered_test_suites=False,
141+
has_test_main=False)),
139142
("testcases/tests/test_d/test_ztest_error_register_test_suite.c",
140143
ScanPathResult(
141144
warnings=None, matches=['unit_1a', 'unit_1b'],
142145
has_registered_test_suites=True,
143-
has_run_registered_test_suites=False)),
146+
has_run_registered_test_suites=False,
147+
has_test_main=False)),
144148
]
145149

146150
@pytest.mark.parametrize("test_file, expected", TESTDATA_5)

subsys/testsuite/ztest/include/ztest_test.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,17 @@ extern struct ztest_suite_node _ztest_suite_node_list_end[];
9292
*/
9393
int ztest_run_registered_test_suites(const void *state);
9494

95+
/**
96+
* @brief Fails the test if any of the registered tests did not run.
97+
*
98+
* When registering test suites, a pragma function can be provided to determine WHEN the test should
99+
* run. It is possible that a test suite could be registered but the pragma always prevents it from
100+
* running. In cases where a test should make sure that ALL suites ran at least once, this function
101+
* may be called at the end of test_main(). It will cause the test to fail if any suite was
102+
* registered but never ran.
103+
*/
104+
void ztest_verify_all_registered_test_suites_ran(void);
105+
95106
/**
96107
* @brief Run a test suite.
97108
*

subsys/testsuite/ztest/src/ztest.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,29 @@ int ztest_run_registered_test_suites(const void *state)
465465
return count;
466466
}
467467

468+
void ztest_verify_all_registered_test_suites_ran(void)
469+
{
470+
bool all_tests_run = true;
471+
struct ztest_suite_node *ptr;
472+
473+
for (ptr = _ztest_suite_node_list_start; ptr < _ztest_suite_node_list_end; ++ptr) {
474+
if (ptr->stats.run_count < 1) {
475+
PRINT("ERROR: Test '%s' did not run.\n", ptr->name);
476+
all_tests_run = false;
477+
}
478+
}
479+
480+
if (!all_tests_run) {
481+
test_status = 1;
482+
}
483+
}
484+
485+
void __weak test_main(void)
486+
{
487+
ztest_run_registered_test_suites(NULL);
488+
ztest_verify_all_registered_test_suites_ran();
489+
}
490+
468491
#ifndef KERNEL
469492
int main(void)
470493
{

tests/ztest/automain/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright 2021 Google LLC
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
cmake_minimum_required(VERSION 3.20.0)
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(automain)
7+
8+
FILE(GLOB app_sources src/*.c)
9+
target_sources(app PRIVATE ${app_sources})

tests/ztest/automain/prj.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CONFIG_ZTEST=y
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <ztest.h>
8+
9+
/**
10+
* @brief A stub unit test.
11+
*
12+
* This is a mock unit test that is expected to run. Note that it is not added directly to a test
13+
* suite and run via ztest_run_test_suite(). It is instead registered below using
14+
* ztest_register_test_suite and will be run by the automatically generated test_main() function.
15+
*/
16+
static void test_stub(void)
17+
{
18+
}
19+
20+
ztest_register_test_suite(test_suite, NULL,
21+
ztest_unit_test(test_stub));

tests/ztest/automain/testcase.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
tests:
2+
testing.ztest.automain:
3+
tags: test_framework
4+
integration_platforms:
5+
- native_posix

0 commit comments

Comments
 (0)