Skip to content

Commit f86fee1

Browse files
committed
Merge tag 'linux-kselftest-kunit-fixes-5.10-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
Pull Kunit fixes from Shuah Khan: "Several fixes to Kunit documentation and tools, and to not pollute the source directory. Also remove the incorrect kunit .gitattributes file" * tag 'linux-kselftest-kunit-fixes-5.10-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: kunit: fix display of failed expectations for strings kunit: tool: fix extra trailing \n in raw + parsed test output kunit: tool: print out stderr from make (like build warnings) KUnit: Docs: usage: wording fixes KUnit: Docs: style: fix some Kconfig example issues KUnit: Docs: fix a wording typo kunit: Do not pollute source directory with generated files (test.log) kunit: Do not pollute source directory with generated files (.kunitconfig) kunit: tool: fix pre-existing python type annotation errors kunit: Fix kunit.py parse subcommand (use null build_dir) kunit: tool: unmark test_data as binary blobs
2 parents 0fa8ee0 + 3084db0 commit f86fee1

9 files changed

Lines changed: 80 additions & 54 deletions

File tree

Documentation/dev-tools/kunit/faq.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ things to try.
9090
re-run kunit_tool.
9191
5. Try to run ``make ARCH=um defconfig`` before running ``kunit.py run``. This
9292
may help clean up any residual config items which could be causing problems.
93-
6. Finally, try running KUnit outside UML. KUnit and KUnit tests can run be
93+
6. Finally, try running KUnit outside UML. KUnit and KUnit tests can be
9494
built into any kernel, or can be built as a module and loaded at runtime.
9595
Doing so should allow you to determine if UML is causing the issue you're
9696
seeing. When tests are built-in, they will execute when the kernel boots, and

Documentation/dev-tools/kunit/style.rst

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,17 +175,17 @@ An example Kconfig entry:
175175

176176
.. code-block:: none
177177
178-
config FOO_KUNIT_TEST
179-
tristate "KUnit test for foo" if !KUNIT_ALL_TESTS
180-
depends on KUNIT
181-
default KUNIT_ALL_TESTS
182-
help
183-
This builds unit tests for foo.
178+
config FOO_KUNIT_TEST
179+
tristate "KUnit test for foo" if !KUNIT_ALL_TESTS
180+
depends on KUNIT
181+
default KUNIT_ALL_TESTS
182+
help
183+
This builds unit tests for foo.
184184
185-
For more information on KUnit and unit tests in general, please refer
186-
to the KUnit documentation in Documentation/dev-tools/kunit
185+
For more information on KUnit and unit tests in general, please refer
186+
to the KUnit documentation in Documentation/dev-tools/kunit/.
187187
188-
If unsure, say N
188+
If unsure, say N.
189189
190190
191191
Test File and Module Names

Documentation/dev-tools/kunit/usage.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ behavior of a function called ``add``; the first parameter is always of type
9292
the second parameter, in this case, is what the value is expected to be; the
9393
last value is what the value actually is. If ``add`` passes all of these
9494
expectations, the test case, ``add_test_basic`` will pass; if any one of these
95-
expectations fail, the test case will fail.
95+
expectations fails, the test case will fail.
9696

9797
It is important to understand that a test case *fails* when any expectation is
9898
violated; however, the test will continue running, potentially trying other
@@ -202,7 +202,7 @@ Example:
202202
kunit_test_suite(example_test_suite);
203203
204204
In the above example the test suite, ``example_test_suite``, would run the test
205-
cases ``example_test_foo``, ``example_test_bar``, and ``example_test_baz``,
205+
cases ``example_test_foo``, ``example_test_bar``, and ``example_test_baz``;
206206
each would have ``example_test_init`` called immediately before it and would
207207
have ``example_test_exit`` called immediately after it.
208208
``kunit_test_suite(example_test_suite)`` registers the test suite with the
@@ -229,7 +229,7 @@ through some sort of indirection where a function is exposed as part of an API
229229
such that the definition of that function can be changed without affecting the
230230
rest of the code base. In the kernel this primarily comes from two constructs,
231231
classes, structs that contain function pointers that are provided by the
232-
implementer, and architecture specific functions which have definitions selected
232+
implementer, and architecture-specific functions which have definitions selected
233233
at compile time.
234234

235235
Classes
@@ -459,7 +459,7 @@ KUnit on non-UML architectures
459459
By default KUnit uses UML as a way to provide dependencies for code under test.
460460
Under most circumstances KUnit's usage of UML should be treated as an
461461
implementation detail of how KUnit works under the hood. Nevertheless, there
462-
are instances where being able to run architecture specific code or test
462+
are instances where being able to run architecture-specific code or test
463463
against real hardware is desirable. For these reasons KUnit supports running on
464464
other architectures.
465465

@@ -599,7 +599,7 @@ writing normal KUnit tests. One special caveat is that you have to reset
599599
hardware state in between test cases; if this is not possible, you may only be
600600
able to run one test case per invocation.
601601

602-
.. TODO(brendanhiggins@google.com): Add an actual example of an architecture
602+
.. TODO(brendanhiggins@google.com): Add an actual example of an architecture-
603603
dependent KUnit test.
604604
605605
KUnit debugfs representation

include/kunit/test.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,7 @@ do { \
11051105
KUNIT_ASSERTION(test, \
11061106
strcmp(__left, __right) op 0, \
11071107
kunit_binary_str_assert, \
1108-
KUNIT_INIT_BINARY_ASSERT_STRUCT(test, \
1108+
KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(test, \
11091109
assert_type, \
11101110
#op, \
11111111
#left, \

tools/testing/kunit/.gitattributes

Lines changed: 0 additions & 1 deletion
This file was deleted.

tools/testing/kunit/kunit.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import sys
1212
import os
1313
import time
14-
import shutil
1514

1615
from collections import namedtuple
1716
from enum import Enum, auto
@@ -44,11 +43,6 @@ class KunitStatus(Enum):
4443
BUILD_FAILURE = auto()
4544
TEST_FAILURE = auto()
4645

47-
def create_default_kunitconfig():
48-
if not os.path.exists(kunit_kernel.kunitconfig_path):
49-
shutil.copyfile('arch/um/configs/kunit_defconfig',
50-
kunit_kernel.kunitconfig_path)
51-
5246
def get_kernel_root_path():
5347
parts = sys.argv[0] if not __file__ else __file__
5448
parts = os.path.realpath(parts).split('tools/testing/kunit')
@@ -61,7 +55,6 @@ def config_tests(linux: kunit_kernel.LinuxSourceTree,
6155
kunit_parser.print_with_timestamp('Configuring KUnit Kernel ...')
6256

6357
config_start = time.time()
64-
create_default_kunitconfig()
6558
success = linux.build_reconfig(request.build_dir, request.make_options)
6659
config_end = time.time()
6760
if not success:
@@ -262,12 +255,12 @@ def main(argv, linux=None):
262255
if not os.path.exists(cli_args.build_dir):
263256
os.mkdir(cli_args.build_dir)
264257

265-
if not os.path.exists(kunit_kernel.kunitconfig_path):
266-
create_default_kunitconfig()
267-
268258
if not linux:
269259
linux = kunit_kernel.LinuxSourceTree()
270260

261+
linux.create_kunitconfig(cli_args.build_dir)
262+
linux.read_kunitconfig(cli_args.build_dir)
263+
271264
request = KunitRequest(cli_args.raw_output,
272265
cli_args.timeout,
273266
cli_args.jobs,
@@ -283,12 +276,12 @@ def main(argv, linux=None):
283276
not os.path.exists(cli_args.build_dir)):
284277
os.mkdir(cli_args.build_dir)
285278

286-
if not os.path.exists(kunit_kernel.kunitconfig_path):
287-
create_default_kunitconfig()
288-
289279
if not linux:
290280
linux = kunit_kernel.LinuxSourceTree()
291281

282+
linux.create_kunitconfig(cli_args.build_dir)
283+
linux.read_kunitconfig(cli_args.build_dir)
284+
292285
request = KunitConfigRequest(cli_args.build_dir,
293286
cli_args.make_options)
294287
result = config_tests(linux, request)
@@ -301,6 +294,9 @@ def main(argv, linux=None):
301294
if not linux:
302295
linux = kunit_kernel.LinuxSourceTree()
303296

297+
linux.create_kunitconfig(cli_args.build_dir)
298+
linux.read_kunitconfig(cli_args.build_dir)
299+
304300
request = KunitBuildRequest(cli_args.jobs,
305301
cli_args.build_dir,
306302
cli_args.alltests,
@@ -315,6 +311,9 @@ def main(argv, linux=None):
315311
if not linux:
316312
linux = kunit_kernel.LinuxSourceTree()
317313

314+
linux.create_kunitconfig(cli_args.build_dir)
315+
linux.read_kunitconfig(cli_args.build_dir)
316+
318317
exec_request = KunitExecRequest(cli_args.timeout,
319318
cli_args.build_dir,
320319
cli_args.alltests)
@@ -337,7 +336,7 @@ def main(argv, linux=None):
337336
kunit_output = f.read().splitlines()
338337
request = KunitParseRequest(cli_args.raw_output,
339338
kunit_output,
340-
cli_args.build_dir,
339+
None,
341340
cli_args.json)
342341
result = parse_tests(request)
343342
if result.status != KunitStatus.SUCCESS:

tools/testing/kunit/kunit_kernel.py

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
# Author: Felix Guo <felixguoxiuping@gmail.com>
77
# Author: Brendan Higgins <brendanhiggins@google.com>
88

9-
109
import logging
1110
import subprocess
1211
import os
12+
import shutil
1313
import signal
1414

1515
from contextlib import ExitStack
@@ -18,8 +18,10 @@
1818
import kunit_parser
1919

2020
KCONFIG_PATH = '.config'
21-
kunitconfig_path = '.kunitconfig'
21+
KUNITCONFIG_PATH = '.kunitconfig'
22+
DEFAULT_KUNITCONFIG_PATH = 'arch/um/configs/kunit_defconfig'
2223
BROKEN_ALLCONFIG_PATH = 'tools/testing/kunit/configs/broken_on_uml.config'
24+
OUTFILE_PATH = 'test.log'
2325

2426
class ConfigError(Exception):
2527
"""Represents an error trying to configure the Linux kernel."""
@@ -82,36 +84,51 @@ def make(self, jobs, build_dir, make_options):
8284
if build_dir:
8385
command += ['O=' + build_dir]
8486
try:
85-
subprocess.check_output(command, stderr=subprocess.STDOUT)
87+
proc = subprocess.Popen(command,
88+
stderr=subprocess.PIPE,
89+
stdout=subprocess.DEVNULL)
8690
except OSError as e:
87-
raise BuildError('Could not call execute make: ' + str(e))
88-
except subprocess.CalledProcessError as e:
89-
raise BuildError(e.output.decode())
90-
91-
def linux_bin(self, params, timeout, build_dir, outfile):
91+
raise BuildError('Could not call make command: ' + str(e))
92+
_, stderr = proc.communicate()
93+
if proc.returncode != 0:
94+
raise BuildError(stderr.decode())
95+
if stderr: # likely only due to build warnings
96+
print(stderr.decode())
97+
98+
def linux_bin(self, params, timeout, build_dir):
9299
"""Runs the Linux UML binary. Must be named 'linux'."""
93100
linux_bin = './linux'
94101
if build_dir:
95102
linux_bin = os.path.join(build_dir, 'linux')
103+
outfile = get_outfile_path(build_dir)
96104
with open(outfile, 'w') as output:
97105
process = subprocess.Popen([linux_bin] + params,
98106
stdout=output,
99107
stderr=subprocess.STDOUT)
100108
process.wait(timeout)
101109

102-
103110
def get_kconfig_path(build_dir):
104111
kconfig_path = KCONFIG_PATH
105112
if build_dir:
106113
kconfig_path = os.path.join(build_dir, KCONFIG_PATH)
107114
return kconfig_path
108115

116+
def get_kunitconfig_path(build_dir):
117+
kunitconfig_path = KUNITCONFIG_PATH
118+
if build_dir:
119+
kunitconfig_path = os.path.join(build_dir, KUNITCONFIG_PATH)
120+
return kunitconfig_path
121+
122+
def get_outfile_path(build_dir):
123+
outfile_path = OUTFILE_PATH
124+
if build_dir:
125+
outfile_path = os.path.join(build_dir, OUTFILE_PATH)
126+
return outfile_path
127+
109128
class LinuxSourceTree(object):
110129
"""Represents a Linux kernel source tree with KUnit tests."""
111130

112131
def __init__(self):
113-
self._kconfig = kunit_config.Kconfig()
114-
self._kconfig.read_from_file(kunitconfig_path)
115132
self._ops = LinuxSourceTreeOperations()
116133
signal.signal(signal.SIGINT, self.signal_handler)
117134

@@ -123,6 +140,16 @@ def clean(self):
123140
return False
124141
return True
125142

143+
def create_kunitconfig(self, build_dir, defconfig=DEFAULT_KUNITCONFIG_PATH):
144+
kunitconfig_path = get_kunitconfig_path(build_dir)
145+
if not os.path.exists(kunitconfig_path):
146+
shutil.copyfile(defconfig, kunitconfig_path)
147+
148+
def read_kunitconfig(self, build_dir):
149+
kunitconfig_path = get_kunitconfig_path(build_dir)
150+
self._kconfig = kunit_config.Kconfig()
151+
self._kconfig.read_from_file(kunitconfig_path)
152+
126153
def validate_config(self, build_dir):
127154
kconfig_path = get_kconfig_path(build_dir)
128155
validated_kconfig = kunit_config.Kconfig()
@@ -178,8 +205,8 @@ def build_um_kernel(self, alltests, jobs, build_dir, make_options):
178205

179206
def run_kernel(self, args=[], build_dir='', timeout=None):
180207
args.extend(['mem=1G'])
181-
outfile = 'test.log'
182-
self._ops.linux_bin(args, timeout, build_dir, outfile)
208+
self._ops.linux_bin(args, timeout, build_dir)
209+
outfile = get_outfile_path(build_dir)
183210
subprocess.call(['stty', 'sane'])
184211
with open(outfile, 'r') as file:
185212
for line in file:

tools/testing/kunit/kunit_parser.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from datetime import datetime
1313
from enum import Enum, auto
1414
from functools import reduce
15-
from typing import List
15+
from typing import List, Optional, Tuple
1616

1717
TestResult = namedtuple('TestResult', ['status','suites','log'])
1818

@@ -54,6 +54,7 @@ class TestStatus(Enum):
5454
def isolate_kunit_output(kernel_output):
5555
started = False
5656
for line in kernel_output:
57+
line = line.rstrip() # line always has a trailing \n
5758
if kunit_start_re.search(line):
5859
prefix_len = len(line.split('TAP version')[0])
5960
started = True
@@ -65,7 +66,7 @@ def isolate_kunit_output(kernel_output):
6566

6667
def raw_output(kernel_output):
6768
for line in kernel_output:
68-
print(line)
69+
print(line.rstrip())
6970

7071
DIVIDER = '=' * 60
7172

@@ -151,7 +152,7 @@ def parse_diagnostic(lines: List[str], test_case: TestCase) -> bool:
151152
else:
152153
return False
153154

154-
def parse_test_case(lines: List[str]) -> TestCase:
155+
def parse_test_case(lines: List[str]) -> Optional[TestCase]:
155156
test_case = TestCase()
156157
save_non_diagnositic(lines, test_case)
157158
while parse_diagnostic(lines, test_case):
@@ -163,7 +164,7 @@ def parse_test_case(lines: List[str]) -> TestCase:
163164

164165
SUBTEST_HEADER = re.compile(r'^[\s]+# Subtest: (.*)$')
165166

166-
def parse_subtest_header(lines: List[str]) -> str:
167+
def parse_subtest_header(lines: List[str]) -> Optional[str]:
167168
consume_non_diagnositic(lines)
168169
if not lines:
169170
return None
@@ -176,7 +177,7 @@ def parse_subtest_header(lines: List[str]) -> str:
176177

177178
SUBTEST_PLAN = re.compile(r'[\s]+[0-9]+\.\.([0-9]+)')
178179

179-
def parse_subtest_plan(lines: List[str]) -> int:
180+
def parse_subtest_plan(lines: List[str]) -> Optional[int]:
180181
consume_non_diagnositic(lines)
181182
match = SUBTEST_PLAN.match(lines[0])
182183
if match:
@@ -230,7 +231,7 @@ def bubble_up_test_case_errors(test_suite: TestSuite) -> TestStatus:
230231
max_test_case_status = bubble_up_errors(lambda x: x.status, test_suite.cases)
231232
return max_status(max_test_case_status, test_suite.status)
232233

233-
def parse_test_suite(lines: List[str], expected_suite_index: int) -> TestSuite:
234+
def parse_test_suite(lines: List[str], expected_suite_index: int) -> Optional[TestSuite]:
234235
if not lines:
235236
return None
236237
consume_non_diagnositic(lines)
@@ -271,7 +272,7 @@ def parse_tap_header(lines: List[str]) -> bool:
271272

272273
TEST_PLAN = re.compile(r'[0-9]+\.\.([0-9]+)')
273274

274-
def parse_test_plan(lines: List[str]) -> int:
275+
def parse_test_plan(lines: List[str]) -> Optional[int]:
275276
consume_non_diagnositic(lines)
276277
match = TEST_PLAN.match(lines[0])
277278
if match:
@@ -310,7 +311,7 @@ def parse_test_result(lines: List[str]) -> TestResult:
310311
else:
311312
return TestResult(TestStatus.NO_TESTS, [], lines)
312313

313-
def print_and_count_results(test_result: TestResult) -> None:
314+
def print_and_count_results(test_result: TestResult) -> Tuple[int, int, int]:
314315
total_tests = 0
315316
failed_tests = 0
316317
crashed_tests = 0

tools/testing/kunit/kunit_tool_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def test_output_isolated_correctly(self):
102102
'test_data/test_output_isolated_correctly.log')
103103
file = open(log_path)
104104
result = kunit_parser.isolate_kunit_output(file.readlines())
105-
self.assertContains('TAP version 14\n', result)
105+
self.assertContains('TAP version 14', result)
106106
self.assertContains(' # Subtest: example', result)
107107
self.assertContains(' 1..2', result)
108108
self.assertContains(' ok 1 - example_simple_test', result)
@@ -115,7 +115,7 @@ def test_output_with_prefix_isolated_correctly(self):
115115
'test_data/test_pound_sign.log')
116116
with open(log_path) as file:
117117
result = kunit_parser.isolate_kunit_output(file.readlines())
118-
self.assertContains('TAP version 14\n', result)
118+
self.assertContains('TAP version 14', result)
119119
self.assertContains(' # Subtest: kunit-resource-test', result)
120120
self.assertContains(' 1..5', result)
121121
self.assertContains(' ok 1 - kunit_resource_test_init_resources', result)

0 commit comments

Comments
 (0)