2 # SPDX-License-Identifier: GPL-2.0
4 # A collection of tests for tools/testing/kunit/kunit.py
6 # Copyright (C) 2019, Google LLC.
7 # Author: Brendan Higgins <brendanhiggins@google.com>
10 from unittest
import mock
12 import tempfile
, shutil
# Handling test_tmpdir
19 from typing
import Iterable
26 from kunit_printer
import stdout
29 abs_test_data_dir
= ''
32 global test_tmpdir
, abs_test_data_dir
33 test_tmpdir
= tempfile
.mkdtemp()
34 abs_test_data_dir
= os
.path
.abspath(os
.path
.join(os
.path
.dirname(__file__
), 'test_data'))
37 shutil
.rmtree(test_tmpdir
)
39 def test_data_path(path
):
40 return os
.path
.join(abs_test_data_dir
, path
)
42 class KconfigTest(unittest
.TestCase
):
44 def test_is_subset_of(self
):
45 kconfig0
= kunit_config
.Kconfig()
46 self
.assertTrue(kconfig0
.is_subset_of(kconfig0
))
48 kconfig1
= kunit_config
.Kconfig()
49 kconfig1
.add_entry('TEST', 'y')
50 self
.assertTrue(kconfig1
.is_subset_of(kconfig1
))
51 self
.assertTrue(kconfig0
.is_subset_of(kconfig1
))
52 self
.assertFalse(kconfig1
.is_subset_of(kconfig0
))
54 def test_read_from_file(self
):
55 kconfig_path
= test_data_path('test_read_from_file.kconfig')
57 kconfig
= kunit_config
.parse_file(kconfig_path
)
59 expected_kconfig
= kunit_config
.Kconfig()
60 expected_kconfig
.add_entry('UML', 'y')
61 expected_kconfig
.add_entry('MMU', 'y')
62 expected_kconfig
.add_entry('TEST', 'y')
63 expected_kconfig
.add_entry('EXAMPLE_TEST', 'y')
64 expected_kconfig
.add_entry('MK8', 'n')
66 self
.assertEqual(kconfig
, expected_kconfig
)
68 def test_write_to_file(self
):
69 kconfig_path
= os
.path
.join(test_tmpdir
, '.config')
71 expected_kconfig
= kunit_config
.Kconfig()
72 expected_kconfig
.add_entry('UML', 'y')
73 expected_kconfig
.add_entry('MMU', 'y')
74 expected_kconfig
.add_entry('TEST', 'y')
75 expected_kconfig
.add_entry('EXAMPLE_TEST', 'y')
76 expected_kconfig
.add_entry('MK8', 'n')
78 expected_kconfig
.write_to_file(kconfig_path
)
80 actual_kconfig
= kunit_config
.parse_file(kconfig_path
)
81 self
.assertEqual(actual_kconfig
, expected_kconfig
)
83 class KUnitParserTest(unittest
.TestCase
):
85 self
.print_mock
= mock
.patch('kunit_printer.Printer.print').start()
86 self
.addCleanup(mock
.patch
.stopall
)
88 def noPrintCallContains(self
, substr
: str):
89 for call
in self
.print_mock
.mock_calls
:
90 self
.assertNotIn(substr
, call
.args
[0])
92 def assertContains(self
, needle
: str, haystack
: kunit_parser
.LineStream
):
93 # Clone the iterator so we can print the contents on failure.
94 copy
, backup
= itertools
.tee(haystack
)
98 raise AssertionError(f
'"{needle}" not found in {list(backup)}!')
100 def test_output_isolated_correctly(self
):
101 log_path
= test_data_path('test_output_isolated_correctly.log')
102 with
open(log_path
) as file:
103 result
= kunit_parser
.extract_tap_lines(file.readlines())
104 self
.assertContains('TAP version 14', result
)
105 self
.assertContains('# Subtest: example', result
)
106 self
.assertContains('1..2', result
)
107 self
.assertContains('ok 1 - example_simple_test', result
)
108 self
.assertContains('ok 2 - example_mock_test', result
)
109 self
.assertContains('ok 1 - example', result
)
111 def test_output_with_prefix_isolated_correctly(self
):
112 log_path
= test_data_path('test_pound_sign.log')
113 with
open(log_path
) as file:
114 result
= kunit_parser
.extract_tap_lines(file.readlines())
115 self
.assertContains('TAP version 14', result
)
116 self
.assertContains('# Subtest: kunit-resource-test', result
)
117 self
.assertContains('1..5', result
)
118 self
.assertContains('ok 1 - kunit_resource_test_init_resources', result
)
119 self
.assertContains('ok 2 - kunit_resource_test_alloc_resource', result
)
120 self
.assertContains('ok 3 - kunit_resource_test_destroy_resource', result
)
121 self
.assertContains('foo bar #', result
)
122 self
.assertContains('ok 4 - kunit_resource_test_cleanup_resources', result
)
123 self
.assertContains('ok 5 - kunit_resource_test_proper_free_ordering', result
)
124 self
.assertContains('ok 1 - kunit-resource-test', result
)
125 self
.assertContains('foo bar # non-kunit output', result
)
126 self
.assertContains('# Subtest: kunit-try-catch-test', result
)
127 self
.assertContains('1..2', result
)
128 self
.assertContains('ok 1 - kunit_test_try_catch_successful_try_no_catch',
130 self
.assertContains('ok 2 - kunit_test_try_catch_unsuccessful_try_does_catch',
132 self
.assertContains('ok 2 - kunit-try-catch-test', result
)
133 self
.assertContains('# Subtest: string-stream-test', result
)
134 self
.assertContains('1..3', result
)
135 self
.assertContains('ok 1 - string_stream_test_empty_on_creation', result
)
136 self
.assertContains('ok 2 - string_stream_test_not_empty_after_add', result
)
137 self
.assertContains('ok 3 - string_stream_test_get_string', result
)
138 self
.assertContains('ok 3 - string-stream-test', result
)
140 def test_parse_successful_test_log(self
):
141 all_passed_log
= test_data_path('test_is_test_passed-all_passed.log')
142 with
open(all_passed_log
) as file:
143 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
144 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
145 self
.assertEqual(result
.counts
.errors
, 0)
147 def test_parse_successful_nested_tests_log(self
):
148 all_passed_log
= test_data_path('test_is_test_passed-all_passed_nested.log')
149 with
open(all_passed_log
) as file:
150 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
151 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
152 self
.assertEqual(result
.counts
.errors
, 0)
154 def test_kselftest_nested(self
):
155 kselftest_log
= test_data_path('test_is_test_passed-kselftest.log')
156 with
open(kselftest_log
) as file:
157 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
158 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
159 self
.assertEqual(result
.counts
.errors
, 0)
161 def test_parse_failed_test_log(self
):
162 failed_log
= test_data_path('test_is_test_passed-failure.log')
163 with
open(failed_log
) as file:
164 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
165 self
.assertEqual(kunit_parser
.TestStatus
.FAILURE
, result
.status
)
166 self
.assertEqual(result
.counts
.errors
, 0)
168 def test_no_header(self
):
169 empty_log
= test_data_path('test_is_test_passed-no_tests_run_no_header.log')
170 with
open(empty_log
) as file:
171 result
= kunit_parser
.parse_run_tests(
172 kunit_parser
.extract_tap_lines(file.readlines()), stdout
)
173 self
.assertEqual(0, len(result
.subtests
))
174 self
.assertEqual(kunit_parser
.TestStatus
.FAILURE_TO_PARSE_TESTS
, result
.status
)
175 self
.assertEqual(result
.counts
.errors
, 1)
177 def test_missing_test_plan(self
):
178 missing_plan_log
= test_data_path('test_is_test_passed-'
180 with
open(missing_plan_log
) as file:
181 result
= kunit_parser
.parse_run_tests(
182 kunit_parser
.extract_tap_lines(
183 file.readlines()), stdout
)
184 # A missing test plan is not an error.
185 self
.assertEqual(result
.counts
, kunit_parser
.TestCounts(passed
=10, errors
=0))
186 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
188 def test_no_tests(self
):
189 header_log
= test_data_path('test_is_test_passed-no_tests_run_with_header.log')
190 with
open(header_log
) as file:
191 result
= kunit_parser
.parse_run_tests(
192 kunit_parser
.extract_tap_lines(file.readlines()), stdout
)
193 self
.assertEqual(0, len(result
.subtests
))
194 self
.assertEqual(kunit_parser
.TestStatus
.NO_TESTS
, result
.status
)
195 self
.assertEqual(result
.counts
.errors
, 1)
197 def test_no_tests_no_plan(self
):
198 no_plan_log
= test_data_path('test_is_test_passed-no_tests_no_plan.log')
199 with
open(no_plan_log
) as file:
200 result
= kunit_parser
.parse_run_tests(
201 kunit_parser
.extract_tap_lines(file.readlines()), stdout
)
202 self
.assertEqual(0, len(result
.subtests
[0].subtests
[0].subtests
))
204 kunit_parser
.TestStatus
.NO_TESTS
,
205 result
.subtests
[0].subtests
[0].status
)
206 self
.assertEqual(result
.counts
, kunit_parser
.TestCounts(passed
=1, errors
=1))
209 def test_no_kunit_output(self
):
210 crash_log
= test_data_path('test_insufficient_memory.log')
211 print_mock
= mock
.patch('kunit_printer.Printer.print').start()
212 with
open(crash_log
) as file:
213 result
= kunit_parser
.parse_run_tests(
214 kunit_parser
.extract_tap_lines(file.readlines()), stdout
)
215 print_mock
.assert_any_call(StrContains('Could not find any KTAP output.'))
217 self
.assertEqual(0, len(result
.subtests
))
218 self
.assertEqual(result
.counts
.errors
, 1)
220 def test_skipped_test(self
):
221 skipped_log
= test_data_path('test_skip_tests.log')
222 with
open(skipped_log
) as file:
223 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
225 # A skipped test does not fail the whole suite.
226 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
227 self
.assertEqual(result
.counts
, kunit_parser
.TestCounts(passed
=4, skipped
=1))
229 def test_skipped_all_tests(self
):
230 skipped_log
= test_data_path('test_skip_all_tests.log')
231 with
open(skipped_log
) as file:
232 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
234 self
.assertEqual(kunit_parser
.TestStatus
.SKIPPED
, result
.status
)
235 self
.assertEqual(result
.counts
, kunit_parser
.TestCounts(skipped
=5))
237 def test_ignores_hyphen(self
):
238 hyphen_log
= test_data_path('test_strip_hyphen.log')
239 with
open(hyphen_log
) as file:
240 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
242 # A skipped test does not fail the whole suite.
243 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
246 result
.subtests
[0].name
)
249 result
.subtests
[1].name
)
251 def test_ignores_prefix_printk_time(self
):
252 prefix_log
= test_data_path('test_config_printk_time.log')
253 with
open(prefix_log
) as file:
254 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
255 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
256 self
.assertEqual('kunit-resource-test', result
.subtests
[0].name
)
257 self
.assertEqual(result
.counts
.errors
, 0)
259 def test_ignores_multiple_prefixes(self
):
260 prefix_log
= test_data_path('test_multiple_prefixes.log')
261 with
open(prefix_log
) as file:
262 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
263 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
264 self
.assertEqual('kunit-resource-test', result
.subtests
[0].name
)
265 self
.assertEqual(result
.counts
.errors
, 0)
267 def test_prefix_mixed_kernel_output(self
):
268 mixed_prefix_log
= test_data_path('test_interrupted_tap_output.log')
269 with
open(mixed_prefix_log
) as file:
270 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
271 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
272 self
.assertEqual('kunit-resource-test', result
.subtests
[0].name
)
273 self
.assertEqual(result
.counts
.errors
, 0)
275 def test_prefix_poundsign(self
):
276 pound_log
= test_data_path('test_pound_sign.log')
277 with
open(pound_log
) as file:
278 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
279 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
280 self
.assertEqual('kunit-resource-test', result
.subtests
[0].name
)
281 self
.assertEqual(result
.counts
.errors
, 0)
283 def test_kernel_panic_end(self
):
284 panic_log
= test_data_path('test_kernel_panic_interrupt.log')
285 with
open(panic_log
) as file:
286 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
287 self
.assertEqual(kunit_parser
.TestStatus
.TEST_CRASHED
, result
.status
)
288 self
.assertEqual('kunit-resource-test', result
.subtests
[0].name
)
289 self
.assertGreaterEqual(result
.counts
.errors
, 1)
291 def test_pound_no_prefix(self
):
292 pound_log
= test_data_path('test_pound_no_prefix.log')
293 with
open(pound_log
) as file:
294 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
295 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
296 self
.assertEqual('kunit-resource-test', result
.subtests
[0].name
)
297 self
.assertEqual(result
.counts
.errors
, 0)
299 def test_summarize_failures(self
):
303 # Subtest: all_failed_suite
307 not ok 1 - all_failed_suite
308 # Subtest: some_failed_suite
312 not ok 1 - some_failed_suite
314 result
= kunit_parser
.parse_run_tests(output
.splitlines(), stdout
)
315 self
.assertEqual(kunit_parser
.TestStatus
.FAILURE
, result
.status
)
317 self
.assertEqual(kunit_parser
._summarize
_failed
_tests
(result
),
318 'Failures: all_failed_suite, some_failed_suite.test2')
320 def test_ktap_format(self
):
321 ktap_log
= test_data_path('test_parse_ktap_output.log')
322 with
open(ktap_log
) as file:
323 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
324 self
.assertEqual(result
.counts
, kunit_parser
.TestCounts(passed
=3))
325 self
.assertEqual('suite', result
.subtests
[0].name
)
326 self
.assertEqual('case_1', result
.subtests
[0].subtests
[0].name
)
327 self
.assertEqual('case_2', result
.subtests
[0].subtests
[1].name
)
329 def test_parse_subtest_header(self
):
330 ktap_log
= test_data_path('test_parse_subtest_header.log')
331 with
open(ktap_log
) as file:
332 kunit_parser
.parse_run_tests(file.readlines(), stdout
)
333 self
.print_mock
.assert_any_call(StrContains('suite (1 subtest)'))
335 def test_parse_attributes(self
):
336 ktap_log
= test_data_path('test_parse_attributes.log')
337 with
open(ktap_log
) as file:
338 result
= kunit_parser
.parse_run_tests(file.readlines(), stdout
)
340 # Test should pass with no errors
341 self
.assertEqual(result
.counts
, kunit_parser
.TestCounts(passed
=1, errors
=0))
342 self
.assertEqual(kunit_parser
.TestStatus
.SUCCESS
, result
.status
)
344 # Ensure suite header is parsed correctly
345 self
.print_mock
.assert_any_call(StrContains('suite (1 subtest)'))
347 # Ensure attributes in correct test log
348 self
.assertContains('# module: example', result
.subtests
[0].log
)
349 self
.assertContains('# test.speed: slow', result
.subtests
[0].subtests
[0].log
)
351 def test_show_test_output_on_failure(self
):
359 result
= kunit_parser
.parse_run_tests(output
.splitlines(), stdout
)
360 self
.assertEqual(kunit_parser
.TestStatus
.FAILURE
, result
.status
)
362 self
.print_mock
.assert_any_call(StrContains('Test output.'))
363 self
.print_mock
.assert_any_call(StrContains(' Indented more.'))
364 self
.noPrintCallContains('not ok 1 test1')
366 def line_stream_from_strs(strs
: Iterable
[str]) -> kunit_parser
.LineStream
:
367 return kunit_parser
.LineStream(enumerate(strs
, start
=1))
369 class LineStreamTest(unittest
.TestCase
):
371 def test_basic(self
):
372 stream
= line_stream_from_strs(['hello', 'world'])
374 self
.assertTrue(stream
, msg
='Should be more input')
375 self
.assertEqual(stream
.line_number(), 1)
376 self
.assertEqual(stream
.peek(), 'hello')
377 self
.assertEqual(stream
.pop(), 'hello')
379 self
.assertTrue(stream
, msg
='Should be more input')
380 self
.assertEqual(stream
.line_number(), 2)
381 self
.assertEqual(stream
.peek(), 'world')
382 self
.assertEqual(stream
.pop(), 'world')
384 self
.assertFalse(stream
, msg
='Should be no more input')
385 with self
.assertRaisesRegex(ValueError, 'LineStream: going past EOF'):
388 def test_is_lazy(self
):
391 nonlocal called_times
394 yield called_times
, str(called_times
)
396 stream
= kunit_parser
.LineStream(generator())
397 self
.assertEqual(called_times
, 0)
399 self
.assertEqual(stream
.pop(), '1')
400 self
.assertEqual(called_times
, 1)
402 self
.assertEqual(stream
.pop(), '2')
403 self
.assertEqual(called_times
, 2)
405 class LinuxSourceTreeTest(unittest
.TestCase
):
408 mock
.patch
.object(signal
, 'signal').start()
409 self
.addCleanup(mock
.patch
.stopall
)
411 def test_invalid_kunitconfig(self
):
412 with self
.assertRaisesRegex(kunit_kernel
.ConfigError
, 'nonexistent.* does not exist'):
413 kunit_kernel
.LinuxSourceTree('', kunitconfig_paths
=['/nonexistent_file'])
415 def test_valid_kunitconfig(self
):
416 with tempfile
.NamedTemporaryFile('wt') as kunitconfig
:
417 kunit_kernel
.LinuxSourceTree('', kunitconfig_paths
=[kunitconfig
.name
])
419 def test_dir_kunitconfig(self
):
420 with tempfile
.TemporaryDirectory('') as dir:
421 with
open(os
.path
.join(dir, '.kunitconfig'), 'w'):
423 kunit_kernel
.LinuxSourceTree('', kunitconfig_paths
=[dir])
425 def test_multiple_kunitconfig(self
):
426 want_kconfig
= kunit_config
.Kconfig()
427 want_kconfig
.add_entry('KUNIT', 'y')
428 want_kconfig
.add_entry('KUNIT_TEST', 'm')
430 with tempfile
.TemporaryDirectory('') as dir:
431 other
= os
.path
.join(dir, 'otherkunitconfig')
432 with
open(os
.path
.join(dir, '.kunitconfig'), 'w') as f
:
433 f
.write('CONFIG_KUNIT=y')
434 with
open(other
, 'w') as f
:
435 f
.write('CONFIG_KUNIT_TEST=m')
438 tree
= kunit_kernel
.LinuxSourceTree('', kunitconfig_paths
=[dir, other
])
439 self
.assertTrue(want_kconfig
.is_subset_of(tree
._kconfig
), msg
=tree
._kconfig
)
442 def test_multiple_kunitconfig_invalid(self
):
443 with tempfile
.TemporaryDirectory('') as dir:
444 other
= os
.path
.join(dir, 'otherkunitconfig')
445 with
open(os
.path
.join(dir, '.kunitconfig'), 'w') as f
:
446 f
.write('CONFIG_KUNIT=y')
447 with
open(other
, 'w') as f
:
448 f
.write('CONFIG_KUNIT=m')
450 with self
.assertRaisesRegex(kunit_kernel
.ConfigError
, '(?s)Multiple values.*CONFIG_KUNIT'):
451 kunit_kernel
.LinuxSourceTree('', kunitconfig_paths
=[dir, other
])
454 def test_kconfig_add(self
):
455 want_kconfig
= kunit_config
.Kconfig()
456 want_kconfig
.add_entry('NOT_REAL', 'y')
458 tree
= kunit_kernel
.LinuxSourceTree('', kconfig_add
=['CONFIG_NOT_REAL=y'])
459 self
.assertTrue(want_kconfig
.is_subset_of(tree
._kconfig
), msg
=tree
._kconfig
)
461 def test_invalid_arch(self
):
462 with self
.assertRaisesRegex(kunit_kernel
.ConfigError
, 'not a valid arch, options are.*x86_64'):
463 kunit_kernel
.LinuxSourceTree('', arch
='invalid')
465 def test_run_kernel_hits_exception(self
):
466 def fake_start(unused_args
, unused_build_dir
):
467 return subprocess
.Popen(['echo "hi\nbye"'], shell
=True, text
=True, stdout
=subprocess
.PIPE
)
469 with tempfile
.TemporaryDirectory('') as build_dir
:
470 tree
= kunit_kernel
.LinuxSourceTree(build_dir
)
471 mock
.patch
.object(tree
._ops
, 'start', side_effect
=fake_start
).start()
473 with self
.assertRaises(ValueError):
474 for line
in tree
.run_kernel(build_dir
=build_dir
):
475 self
.assertEqual(line
, 'hi\n')
476 raise ValueError('uh oh, did not read all output')
478 with
open(kunit_kernel
.get_outfile_path(build_dir
), 'rt') as outfile
:
479 self
.assertEqual(outfile
.read(), 'hi\nbye\n', msg
='Missing some output')
481 def test_build_reconfig_no_config(self
):
482 with tempfile
.TemporaryDirectory('') as build_dir
:
483 with
open(kunit_kernel
.get_kunitconfig_path(build_dir
), 'w') as f
:
484 f
.write('CONFIG_KUNIT=y')
486 tree
= kunit_kernel
.LinuxSourceTree(build_dir
)
487 # Stub out the source tree operations, so we don't have
488 # the defaults for any given architecture get in the
490 tree
._ops
= kunit_kernel
.LinuxSourceTreeOperations('none', None)
491 mock_build_config
= mock
.patch
.object(tree
, 'build_config').start()
493 # Should generate the .config
494 self
.assertTrue(tree
.build_reconfig(build_dir
, make_options
=[]))
495 mock_build_config
.assert_called_once_with(build_dir
, [])
497 def test_build_reconfig_existing_config(self
):
498 with tempfile
.TemporaryDirectory('') as build_dir
:
499 # Existing .config is a superset, should not touch it
500 with
open(kunit_kernel
.get_kunitconfig_path(build_dir
), 'w') as f
:
501 f
.write('CONFIG_KUNIT=y')
502 with
open(kunit_kernel
.get_old_kunitconfig_path(build_dir
), 'w') as f
:
503 f
.write('CONFIG_KUNIT=y')
504 with
open(kunit_kernel
.get_kconfig_path(build_dir
), 'w') as f
:
505 f
.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')
507 tree
= kunit_kernel
.LinuxSourceTree(build_dir
)
508 # Stub out the source tree operations, so we don't have
509 # the defaults for any given architecture get in the
511 tree
._ops
= kunit_kernel
.LinuxSourceTreeOperations('none', None)
512 mock_build_config
= mock
.patch
.object(tree
, 'build_config').start()
514 self
.assertTrue(tree
.build_reconfig(build_dir
, make_options
=[]))
515 self
.assertEqual(mock_build_config
.call_count
, 0)
517 def test_build_reconfig_remove_option(self
):
518 with tempfile
.TemporaryDirectory('') as build_dir
:
519 # We removed CONFIG_KUNIT_TEST=y from our .kunitconfig...
520 with
open(kunit_kernel
.get_kunitconfig_path(build_dir
), 'w') as f
:
521 f
.write('CONFIG_KUNIT=y')
522 with
open(kunit_kernel
.get_old_kunitconfig_path(build_dir
), 'w') as f
:
523 f
.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')
524 with
open(kunit_kernel
.get_kconfig_path(build_dir
), 'w') as f
:
525 f
.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')
527 tree
= kunit_kernel
.LinuxSourceTree(build_dir
)
528 # Stub out the source tree operations, so we don't have
529 # the defaults for any given architecture get in the
531 tree
._ops
= kunit_kernel
.LinuxSourceTreeOperations('none', None)
532 mock_build_config
= mock
.patch
.object(tree
, 'build_config').start()
534 # ... so we should trigger a call to build_config()
535 self
.assertTrue(tree
.build_reconfig(build_dir
, make_options
=[]))
536 mock_build_config
.assert_called_once_with(build_dir
, [])
538 # TODO: add more test cases.
541 class KUnitJsonTest(unittest
.TestCase
):
543 self
.print_mock
= mock
.patch('kunit_printer.Printer.print').start()
544 self
.addCleanup(mock
.patch
.stopall
)
546 def _json_for(self
, log_file
):
547 with
open(test_data_path(log_file
)) as file:
548 test_result
= kunit_parser
.parse_run_tests(file, stdout
)
549 json_obj
= kunit_json
.get_json_result(
551 metadata
=kunit_json
.Metadata())
552 return json
.loads(json_obj
)
554 def test_failed_test_json(self
):
555 result
= self
._json
_for
('test_is_test_passed-failure.log')
557 {'name': 'example_simple_test', 'status': 'FAIL'},
558 result
["sub_groups"][1]["test_cases"][0])
560 def test_crashed_test_json(self
):
561 result
= self
._json
_for
('test_kernel_panic_interrupt.log')
563 {'name': '', 'status': 'ERROR'},
564 result
["sub_groups"][2]["test_cases"][1])
566 def test_skipped_test_json(self
):
567 result
= self
._json
_for
('test_skip_tests.log')
569 {'name': 'example_skip_test', 'status': 'SKIP'},
570 result
["sub_groups"][1]["test_cases"][1])
572 def test_no_tests_json(self
):
573 result
= self
._json
_for
('test_is_test_passed-no_tests_run_with_header.log')
574 self
.assertEqual(0, len(result
['sub_groups']))
576 def test_nested_json(self
):
577 result
= self
._json
_for
('test_is_test_passed-all_passed_nested.log')
579 {'name': 'example_simple_test', 'status': 'PASS'},
580 result
["sub_groups"][0]["sub_groups"][0]["test_cases"][0])
582 class StrContains(str):
583 def __eq__(self
, other
):
586 class KUnitMainTest(unittest
.TestCase
):
588 path
= test_data_path('test_is_test_passed-all_passed.log')
589 with
open(path
) as file:
590 all_passed_log
= file.readlines()
592 self
.print_mock
= mock
.patch('kunit_printer.Printer.print').start()
593 self
.addCleanup(mock
.patch
.stopall
)
595 self
.mock_linux_init
= mock
.patch
.object(kunit_kernel
, 'LinuxSourceTree').start()
596 self
.linux_source_mock
= self
.mock_linux_init
.return_value
597 self
.linux_source_mock
.build_reconfig
.return_value
= True
598 self
.linux_source_mock
.build_kernel
.return_value
= True
599 self
.linux_source_mock
.run_kernel
.return_value
= all_passed_log
601 def test_config_passes_args_pass(self
):
602 kunit
.main(['config', '--build_dir=.kunit'])
603 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 1)
604 self
.assertEqual(self
.linux_source_mock
.run_kernel
.call_count
, 0)
606 def test_build_passes_args_pass(self
):
607 kunit
.main(['build'])
608 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 1)
609 self
.linux_source_mock
.build_kernel
.assert_called_once_with(kunit
.get_default_jobs(), '.kunit', None)
610 self
.assertEqual(self
.linux_source_mock
.run_kernel
.call_count
, 0)
612 def test_exec_passes_args_pass(self
):
614 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 0)
615 self
.assertEqual(self
.linux_source_mock
.run_kernel
.call_count
, 1)
616 self
.linux_source_mock
.run_kernel
.assert_called_once_with(
617 args
=None, build_dir
='.kunit', filter_glob
='', filter='', filter_action
=None, timeout
=300)
618 self
.print_mock
.assert_any_call(StrContains('Testing complete.'))
620 def test_run_passes_args_pass(self
):
622 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 1)
623 self
.assertEqual(self
.linux_source_mock
.run_kernel
.call_count
, 1)
624 self
.linux_source_mock
.run_kernel
.assert_called_once_with(
625 args
=None, build_dir
='.kunit', filter_glob
='', filter='', filter_action
=None, timeout
=300)
626 self
.print_mock
.assert_any_call(StrContains('Testing complete.'))
628 def test_exec_passes_args_fail(self
):
629 self
.linux_source_mock
.run_kernel
= mock
.Mock(return_value
=[])
630 with self
.assertRaises(SystemExit) as e
:
632 self
.assertEqual(e
.exception
.code
, 1)
634 def test_run_passes_args_fail(self
):
635 self
.linux_source_mock
.run_kernel
= mock
.Mock(return_value
=[])
636 with self
.assertRaises(SystemExit) as e
:
638 self
.assertEqual(e
.exception
.code
, 1)
639 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 1)
640 self
.assertEqual(self
.linux_source_mock
.run_kernel
.call_count
, 1)
641 self
.print_mock
.assert_any_call(StrContains('Could not find any KTAP output.'))
643 def test_exec_no_tests(self
):
644 self
.linux_source_mock
.run_kernel
= mock
.Mock(return_value
=['TAP version 14', '1..0'])
645 with self
.assertRaises(SystemExit) as e
:
647 self
.assertEqual(e
.exception
.code
, 1)
648 self
.linux_source_mock
.run_kernel
.assert_called_once_with(
649 args
=None, build_dir
='.kunit', filter_glob
='', filter='', filter_action
=None, timeout
=300)
650 self
.print_mock
.assert_any_call(StrContains(' 0 tests run!'))
652 def test_exec_raw_output(self
):
653 self
.linux_source_mock
.run_kernel
= mock
.Mock(return_value
=[])
654 kunit
.main(['exec', '--raw_output'])
655 self
.assertEqual(self
.linux_source_mock
.run_kernel
.call_count
, 1)
656 for call
in self
.print_mock
.call_args_list
:
657 self
.assertNotEqual(call
, mock
.call(StrContains('Testing complete.')))
658 self
.assertNotEqual(call
, mock
.call(StrContains(' 0 tests run!')))
660 def test_run_raw_output(self
):
661 self
.linux_source_mock
.run_kernel
= mock
.Mock(return_value
=[])
662 kunit
.main(['run', '--raw_output'])
663 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 1)
664 self
.assertEqual(self
.linux_source_mock
.run_kernel
.call_count
, 1)
665 for call
in self
.print_mock
.call_args_list
:
666 self
.assertNotEqual(call
, mock
.call(StrContains('Testing complete.')))
667 self
.assertNotEqual(call
, mock
.call(StrContains(' 0 tests run!')))
669 def test_run_raw_output_kunit(self
):
670 self
.linux_source_mock
.run_kernel
= mock
.Mock(return_value
=[])
671 kunit
.main(['run', '--raw_output=kunit'])
672 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 1)
673 self
.assertEqual(self
.linux_source_mock
.run_kernel
.call_count
, 1)
674 for call
in self
.print_mock
.call_args_list
:
675 self
.assertNotEqual(call
, mock
.call(StrContains('Testing complete.')))
676 self
.assertNotEqual(call
, mock
.call(StrContains(' 0 tests run')))
678 def test_run_raw_output_invalid(self
):
679 self
.linux_source_mock
.run_kernel
= mock
.Mock(return_value
=[])
680 with self
.assertRaises(SystemExit) as e
:
681 kunit
.main(['run', '--raw_output=invalid'])
682 self
.assertNotEqual(e
.exception
.code
, 0)
684 def test_run_raw_output_does_not_take_positional_args(self
):
685 # --raw_output is a string flag, but we don't want it to consume
686 # any positional arguments, only ones after an '='
687 self
.linux_source_mock
.run_kernel
= mock
.Mock(return_value
=[])
688 kunit
.main(['run', '--raw_output', 'filter_glob'])
689 self
.linux_source_mock
.run_kernel
.assert_called_once_with(
690 args
=None, build_dir
='.kunit', filter_glob
='filter_glob', filter='', filter_action
=None, timeout
=300)
692 def test_exec_timeout(self
):
694 kunit
.main(['exec', '--timeout', str(timeout
)])
695 self
.linux_source_mock
.run_kernel
.assert_called_once_with(
696 args
=None, build_dir
='.kunit', filter_glob
='', filter='', filter_action
=None, timeout
=timeout
)
697 self
.print_mock
.assert_any_call(StrContains('Testing complete.'))
699 def test_run_timeout(self
):
701 kunit
.main(['run', '--timeout', str(timeout
)])
702 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 1)
703 self
.linux_source_mock
.run_kernel
.assert_called_once_with(
704 args
=None, build_dir
='.kunit', filter_glob
='', filter='', filter_action
=None, timeout
=timeout
)
705 self
.print_mock
.assert_any_call(StrContains('Testing complete.'))
707 def test_run_builddir(self
):
709 kunit
.main(['run', '--build_dir=.kunit'])
710 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 1)
711 self
.linux_source_mock
.run_kernel
.assert_called_once_with(
712 args
=None, build_dir
=build_dir
, filter_glob
='', filter='', filter_action
=None, timeout
=300)
713 self
.print_mock
.assert_any_call(StrContains('Testing complete.'))
715 def test_config_builddir(self
):
717 kunit
.main(['config', '--build_dir', build_dir
])
718 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 1)
720 def test_build_builddir(self
):
722 jobs
= kunit
.get_default_jobs()
723 kunit
.main(['build', '--build_dir', build_dir
])
724 self
.linux_source_mock
.build_kernel
.assert_called_once_with(jobs
, build_dir
, None)
726 def test_exec_builddir(self
):
728 kunit
.main(['exec', '--build_dir', build_dir
])
729 self
.linux_source_mock
.run_kernel
.assert_called_once_with(
730 args
=None, build_dir
=build_dir
, filter_glob
='', filter='', filter_action
=None, timeout
=300)
731 self
.print_mock
.assert_any_call(StrContains('Testing complete.'))
733 def test_run_kunitconfig(self
):
734 kunit
.main(['run', '--kunitconfig=mykunitconfig'])
735 # Just verify that we parsed and initialized it correctly here.
736 self
.mock_linux_init
.assert_called_once_with('.kunit',
737 kunitconfig_paths
=['mykunitconfig'],
741 qemu_config_path
=None,
744 def test_config_kunitconfig(self
):
745 kunit
.main(['config', '--kunitconfig=mykunitconfig'])
746 # Just verify that we parsed and initialized it correctly here.
747 self
.mock_linux_init
.assert_called_once_with('.kunit',
748 kunitconfig_paths
=['mykunitconfig'],
752 qemu_config_path
=None,
755 def test_config_alltests(self
):
756 kunit
.main(['config', '--kunitconfig=mykunitconfig', '--alltests'])
757 # Just verify that we parsed and initialized it correctly here.
758 self
.mock_linux_init
.assert_called_once_with('.kunit',
759 kunitconfig_paths
=[kunit_kernel
.ALL_TESTS_CONFIG_PATH
, 'mykunitconfig'],
763 qemu_config_path
=None,
767 @mock.patch
.object(kunit_kernel
, 'LinuxSourceTree')
768 def test_run_multiple_kunitconfig(self
, mock_linux_init
):
769 mock_linux_init
.return_value
= self
.linux_source_mock
770 kunit
.main(['run', '--kunitconfig=mykunitconfig', '--kunitconfig=other'])
771 # Just verify that we parsed and initialized it correctly here.
772 mock_linux_init
.assert_called_once_with('.kunit',
773 kunitconfig_paths
=['mykunitconfig', 'other'],
777 qemu_config_path
=None,
780 def test_run_kconfig_add(self
):
781 kunit
.main(['run', '--kconfig_add=CONFIG_KASAN=y', '--kconfig_add=CONFIG_KCSAN=y'])
782 # Just verify that we parsed and initialized it correctly here.
783 self
.mock_linux_init
.assert_called_once_with('.kunit',
784 kunitconfig_paths
=[],
785 kconfig_add
=['CONFIG_KASAN=y', 'CONFIG_KCSAN=y'],
788 qemu_config_path
=None,
791 def test_run_qemu_args(self
):
792 kunit
.main(['run', '--arch=x86_64', '--qemu_args', '-m 2048'])
793 # Just verify that we parsed and initialized it correctly here.
794 self
.mock_linux_init
.assert_called_once_with('.kunit',
795 kunitconfig_paths
=[],
799 qemu_config_path
=None,
800 extra_qemu_args
=['-m', '2048'])
802 def test_run_kernel_args(self
):
803 kunit
.main(['run', '--kernel_args=a=1', '--kernel_args=b=2'])
804 self
.assertEqual(self
.linux_source_mock
.build_reconfig
.call_count
, 1)
805 self
.linux_source_mock
.run_kernel
.assert_called_once_with(
806 args
=['a=1','b=2'], build_dir
='.kunit', filter_glob
='', filter='', filter_action
=None, timeout
=300)
807 self
.print_mock
.assert_any_call(StrContains('Testing complete.'))
809 def test_list_tests(self
):
810 want
= ['suite.test1', 'suite.test2', 'suite2.test1']
811 self
.linux_source_mock
.run_kernel
.return_value
= ['TAP version 14', 'init: random output'] + want
813 got
= kunit
._list
_tests
(self
.linux_source_mock
,
814 kunit
.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'suite', False, False))
815 self
.assertEqual(got
, want
)
816 # Should respect the user's filter glob when listing tests.
817 self
.linux_source_mock
.run_kernel
.assert_called_once_with(
818 args
=['kunit.action=list'], build_dir
='.kunit', filter_glob
='suite*', filter='', filter_action
=None, timeout
=300)
820 @mock.patch
.object(kunit
, '_list_tests')
821 def test_run_isolated_by_suite(self
, mock_tests
):
822 mock_tests
.return_value
= ['suite.test1', 'suite.test2', 'suite2.test1']
823 kunit
.main(['exec', '--run_isolated=suite', 'suite*.test*'])
825 # Should respect the user's filter glob when listing tests.
826 mock_tests
.assert_called_once_with(mock
.ANY
,
827 kunit
.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*.test*', '', None, None, 'suite', False, False))
828 self
.linux_source_mock
.run_kernel
.assert_has_calls([
829 mock
.call(args
=None, build_dir
='.kunit', filter_glob
='suite.test*', filter='', filter_action
=None, timeout
=300),
830 mock
.call(args
=None, build_dir
='.kunit', filter_glob
='suite2.test*', filter='', filter_action
=None, timeout
=300),
833 @mock.patch
.object(kunit
, '_list_tests')
834 def test_run_isolated_by_test(self
, mock_tests
):
835 mock_tests
.return_value
= ['suite.test1', 'suite.test2', 'suite2.test1']
836 kunit
.main(['exec', '--run_isolated=test', 'suite*'])
838 # Should respect the user's filter glob when listing tests.
839 mock_tests
.assert_called_once_with(mock
.ANY
,
840 kunit
.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'test', False, False))
841 self
.linux_source_mock
.run_kernel
.assert_has_calls([
842 mock
.call(args
=None, build_dir
='.kunit', filter_glob
='suite.test1', filter='', filter_action
=None, timeout
=300),
843 mock
.call(args
=None, build_dir
='.kunit', filter_glob
='suite.test2', filter='', filter_action
=None, timeout
=300),
844 mock
.call(args
=None, build_dir
='.kunit', filter_glob
='suite2.test1', filter='', filter_action
=None, timeout
=300),
847 if __name__
== '__main__':