blob: 20cdf022536c758d5fc1811236695a7f54cd9f5d [file] [log] [blame]
Simon Glass2939e892025-01-20 14:25:31 -07001# SPDX-License-Identifier: GPL-2.0
2# Copyright 2024 Google LLC
3
4import pytest
5import re
6
7# List of test suites we expect to find with 'ut info' and 'ut all'
8EXPECTED_SUITES = [
9 'addrmap', 'bdinfo', 'bloblist', 'bootm', 'bootstd',
10 'cmd', 'common', 'dm', 'env', 'exit',
Simon Glass7d4ae012025-01-20 14:25:56 -070011 'fdt', 'font', 'hush', 'lib',
Simon Glass2939e892025-01-20 14:25:31 -070012 'loadm', 'log', 'mbr', 'measurement', 'mem',
13 'overlay', 'pci_mps', 'setexpr', 'upl',
14 ]
15
16
17# Set this to True to aid debugging of tests
18DEBUG_ME = False
19
20
21def collect_info(cons, output):
22 """Process the output from 'ut all'
23
24 Args:
25 cons: U-Boot console object
26 output: Output from running 'ut all'
27
28 Returns:
29 tuple:
30 set: suite names that were found in output
31 set: test names that were found in output
32 dict: test count for each suite:
33 key: suite name
34 value: number of tests for the suite found in output
35 set: missing suites (compared to EXPECTED_SUITES)
36 set: extra suites (compared to EXPECTED_SUITES)
37 """
38 suites = set()
39 tests = set()
40 cur_suite = None
41 test_count = None
42 exp_test_count = {}
43
44 # Collect suites{}
45 for line in output.splitlines():
46 line = line.rstrip()
47 if DEBUG_ME:
48 cons.log.info(f'line: {line}')
49 m = re.search('----Running ([^ ]*) tests----', line)
50 if m:
51 if DEBUG_ME and cur_suite and cur_suite != 'info':
52 cons.log.info(f'suite: {cur_suite} expected {exp_test_count[cur_suite]} found {test_count}')
53
54 cur_suite = m.group(1)
55 if DEBUG_ME:
56 cons.log.info(f'cur_suite: {cur_suite}')
57 suites.add(cur_suite)
58
59 test_count = 0
60 m = re.match(rf'Running (\d+) {cur_suite} tests', line)
61 if m:
62 exp_test_count[cur_suite] = int(m.group(1))
63 m = re.search(r'Test: (\w*): ([-a-z0-9_]*\.c)?( .*)?', line)
64 if m:
65 test_name = m.group(1)
66 msg = m.group(3)
67 if DEBUG_ME:
68 cons.log.info(f"test_name {test_name} msg '{msg}'")
69 if msg == ' (flat tree)' and test_name not in tests:
70 tests.add(test_name)
71 test_count += 1
72 if not msg or 'skipped as it is manual' in msg:
73 tests.add(test_name)
74 test_count += 1
75 if DEBUG_ME:
76 cons.log.info(f'test_count {test_count}')
77 if DEBUG_ME:
78 cons.log.info(f'suite: {cur_suite} expected {exp_test_count[cur_suite]} found {test_count}')
79 cons.log.info(f"Tests: {' '.join(sorted(list(tests)))}")
80
81 # Figure out what is missing, or extra
82 missing = set()
83 extra = set(suites)
84 for suite in EXPECTED_SUITES:
85 if suite in extra:
86 extra.remove(suite)
87 else:
88 missing.add(suite)
89
90 return suites, tests, exp_test_count, missing, extra
91
92
93def process_ut_info(cons, output):
94 """Process the output of the 'ut info' command
95
96 Args:
97 cons: U-Boot console object
98 output: Output from running 'ut all'
99
100 Returns:
101 tuple:
102 int: Number of suites reported
103 int: Number of tests reported
104 dict: test count for each suite:
105 key: suite name
106 value: number of tests reported for the suite
107
108 """
109 suite_count = None
110 total_test_count = None
111 test_count = {}
112 for line in output.splitlines():
113 line = line.rstrip()
114 if DEBUG_ME:
115 cons.log.info(f'line: {line}')
116 m = re.match(r'Test suites: (.*)', line)
117 if m:
118 suite_count = int(m.group(1))
119 m = re.match(r'Total tests: (.*)', line)
120 if m:
121 total_test_count = int(m.group(1))
122 m = re.match(r' *([0-9?]*) (\w*)', line)
123 if m:
124 test_count[m.group(2)] = m.group(1)
125 return suite_count, total_test_count, test_count
126
127
128@pytest.mark.buildconfigspec('sandbox')
129@pytest.mark.notbuildconfigspec('sandbox_spl')
130@pytest.mark.notbuildconfigspec('sandbox64')
131def test_suite(u_boot_console):
132 """Perform various checks on the unit tests, including:
133
134 - The number of suites matches that reported by the 'ut info'
135 - Where available, the number of tests is each suite matches that
136 reported by 'ut info -s'
137 - The total number of tests adds up to the total that are actually run
138 with 'ut all'
139 - All suites are run with 'ut all'
140 - The expected set of suites is run (the list is hard-coded in this test)
141
142 """
143 cons = u_boot_console
144 with cons.log.section('Run all unit tests'):
145 # ut hush hush_test_simple_dollar prints "Unknown command" on purpose.
146 with u_boot_console.disable_check('unknown_command'):
147 output = cons.run_command('ut all')
148
149 # Process the output from the run
150 with cons.log.section('Check output'):
151 suites, all_tests, exp_test_count, missing, extra = collect_info(cons,
152 output)
153 cons.log.info(f'missing {missing}')
154 cons.log.info(f'extra {extra}')
155
Simon Glass7d4ae012025-01-20 14:25:56 -0700156 # Make sure we got a test count for each suite
157 assert suites - exp_test_count.keys() == set()
Simon Glass2939e892025-01-20 14:25:31 -0700158
159 # Run 'ut info' and compare with the log results
160 with cons.log.section('Check suite test-counts'):
161 output = cons.run_command('ut info -s')
162
163 suite_count, total_test_count, test_count = process_ut_info(cons,
164 output)
165
166 if missing or extra:
167 cons.log.info(f"suites: {' '.join(sorted(list(suites)))}")
168 cons.log.error(f'missing: {sorted(list(missing))}')
169 cons.log.error(f'extra: {sorted(list(extra))}')
170
171 assert not missing, f'Missing suites {missing}'
172 assert not extra, f'Extra suites {extra}'
173
174 cons.log.info(str(exp_test_count))
175 for suite in EXPECTED_SUITES:
Simon Glass7d4ae012025-01-20 14:25:56 -0700176 assert test_count[suite] in ['?', str(exp_test_count[suite])], \
177 f'suite {suite} expected {exp_test_count[suite]}'
Simon Glass2939e892025-01-20 14:25:31 -0700178
179 assert suite_count == len(EXPECTED_SUITES)
180 assert total_test_count == len(all_tests)