1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3# 4# A collection of tests for tools/testing/kunit/kunit.py 5# 6# Copyright (C) 2019, Google LLC. 7# Author: Brendan Higgins <[email protected]> 8 9import unittest 10from unittest import mock 11 12import tempfile, shutil # Handling test_tmpdir 13 14import itertools 15import json 16import os 17import signal 18import subprocess 19from typing import Iterable 20 21import kunit_config 22import kunit_parser 23import kunit_kernel 24import kunit_json 25import kunit 26 27test_tmpdir = '' 28abs_test_data_dir = '' 29 30def setUpModule(): 31 global test_tmpdir, abs_test_data_dir 32 test_tmpdir = tempfile.mkdtemp() 33 abs_test_data_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'test_data')) 34 35def tearDownModule(): 36 shutil.rmtree(test_tmpdir) 37 38def test_data_path(path): 39 return os.path.join(abs_test_data_dir, path) 40 41class KconfigTest(unittest.TestCase): 42 43 def test_is_subset_of(self): 44 kconfig0 = kunit_config.Kconfig() 45 self.assertTrue(kconfig0.is_subset_of(kconfig0)) 46 47 kconfig1 = kunit_config.Kconfig() 48 kconfig1.add_entry('TEST', 'y') 49 self.assertTrue(kconfig1.is_subset_of(kconfig1)) 50 self.assertTrue(kconfig0.is_subset_of(kconfig1)) 51 self.assertFalse(kconfig1.is_subset_of(kconfig0)) 52 53 def test_read_from_file(self): 54 kconfig_path = test_data_path('test_read_from_file.kconfig') 55 56 kconfig = kunit_config.parse_file(kconfig_path) 57 58 expected_kconfig = kunit_config.Kconfig() 59 expected_kconfig.add_entry('UML', 'y') 60 expected_kconfig.add_entry('MMU', 'y') 61 expected_kconfig.add_entry('TEST', 'y') 62 expected_kconfig.add_entry('EXAMPLE_TEST', 'y') 63 expected_kconfig.add_entry('MK8', 'n') 64 65 self.assertEqual(kconfig, expected_kconfig) 66 67 def test_write_to_file(self): 68 kconfig_path = os.path.join(test_tmpdir, '.config') 69 70 expected_kconfig = kunit_config.Kconfig() 71 expected_kconfig.add_entry('UML', 'y') 72 expected_kconfig.add_entry('MMU', 'y') 73 expected_kconfig.add_entry('TEST', 'y') 74 expected_kconfig.add_entry('EXAMPLE_TEST', 'y') 75 expected_kconfig.add_entry('MK8', 'n') 76 77 expected_kconfig.write_to_file(kconfig_path) 78 79 actual_kconfig = kunit_config.parse_file(kconfig_path) 80 self.assertEqual(actual_kconfig, expected_kconfig) 81 82class KUnitParserTest(unittest.TestCase): 83 def setUp(self): 84 self.print_mock = mock.patch('kunit_printer.Printer.print').start() 85 self.addCleanup(mock.patch.stopall) 86 87 def assertContains(self, needle: str, haystack: kunit_parser.LineStream): 88 # Clone the iterator so we can print the contents on failure. 89 copy, backup = itertools.tee(haystack) 90 for line in copy: 91 if needle in line: 92 return 93 raise AssertionError(f'"{needle}" not found in {list(backup)}!') 94 95 def test_output_isolated_correctly(self): 96 log_path = test_data_path('test_output_isolated_correctly.log') 97 with open(log_path) as file: 98 result = kunit_parser.extract_tap_lines(file.readlines()) 99 self.assertContains('TAP version 14', result) 100 self.assertContains('# Subtest: example', result) 101 self.assertContains('1..2', result) 102 self.assertContains('ok 1 - example_simple_test', result) 103 self.assertContains('ok 2 - example_mock_test', result) 104 self.assertContains('ok 1 - example', result) 105 106 def test_output_with_prefix_isolated_correctly(self): 107 log_path = test_data_path('test_pound_sign.log') 108 with open(log_path) as file: 109 result = kunit_parser.extract_tap_lines(file.readlines()) 110 self.assertContains('TAP version 14', result) 111 self.assertContains('# Subtest: kunit-resource-test', result) 112 self.assertContains('1..5', result) 113 self.assertContains('ok 1 - kunit_resource_test_init_resources', result) 114 self.assertContains('ok 2 - kunit_resource_test_alloc_resource', result) 115 self.assertContains('ok 3 - kunit_resource_test_destroy_resource', result) 116 self.assertContains('foo bar #', result) 117 self.assertContains('ok 4 - kunit_resource_test_cleanup_resources', result) 118 self.assertContains('ok 5 - kunit_resource_test_proper_free_ordering', result) 119 self.assertContains('ok 1 - kunit-resource-test', result) 120 self.assertContains('foo bar # non-kunit output', result) 121 self.assertContains('# Subtest: kunit-try-catch-test', result) 122 self.assertContains('1..2', result) 123 self.assertContains('ok 1 - kunit_test_try_catch_successful_try_no_catch', 124 result) 125 self.assertContains('ok 2 - kunit_test_try_catch_unsuccessful_try_does_catch', 126 result) 127 self.assertContains('ok 2 - kunit-try-catch-test', result) 128 self.assertContains('# Subtest: string-stream-test', result) 129 self.assertContains('1..3', result) 130 self.assertContains('ok 1 - string_stream_test_empty_on_creation', result) 131 self.assertContains('ok 2 - string_stream_test_not_empty_after_add', result) 132 self.assertContains('ok 3 - string_stream_test_get_string', result) 133 self.assertContains('ok 3 - string-stream-test', result) 134 135 def test_parse_successful_test_log(self): 136 all_passed_log = test_data_path('test_is_test_passed-all_passed.log') 137 with open(all_passed_log) as file: 138 result = kunit_parser.parse_run_tests(file.readlines()) 139 self.assertEqual( 140 kunit_parser.TestStatus.SUCCESS, 141 result.status) 142 143 def test_parse_successful_nested_tests_log(self): 144 all_passed_log = test_data_path('test_is_test_passed-all_passed_nested.log') 145 with open(all_passed_log) as file: 146 result = kunit_parser.parse_run_tests(file.readlines()) 147 self.assertEqual( 148 kunit_parser.TestStatus.SUCCESS, 149 result.status) 150 151 def test_kselftest_nested(self): 152 kselftest_log = test_data_path('test_is_test_passed-kselftest.log') 153 with open(kselftest_log) as file: 154 result = kunit_parser.parse_run_tests(file.readlines()) 155 self.assertEqual( 156 kunit_parser.TestStatus.SUCCESS, 157 result.status) 158 159 def test_parse_failed_test_log(self): 160 failed_log = test_data_path('test_is_test_passed-failure.log') 161 with open(failed_log) as file: 162 result = kunit_parser.parse_run_tests(file.readlines()) 163 self.assertEqual( 164 kunit_parser.TestStatus.FAILURE, 165 result.status) 166 167 def test_no_header(self): 168 empty_log = test_data_path('test_is_test_passed-no_tests_run_no_header.log') 169 with open(empty_log) as file: 170 result = kunit_parser.parse_run_tests( 171 kunit_parser.extract_tap_lines(file.readlines())) 172 self.assertEqual(0, len(result.subtests)) 173 self.assertEqual( 174 kunit_parser.TestStatus.FAILURE_TO_PARSE_TESTS, 175 result.status) 176 177 def test_missing_test_plan(self): 178 missing_plan_log = test_data_path('test_is_test_passed-' 179 'missing_plan.log') 180 with open(missing_plan_log) as file: 181 result = kunit_parser.parse_run_tests( 182 kunit_parser.extract_tap_lines( 183 file.readlines())) 184 # A missing test plan is not an error. 185 self.assertEqual(0, result.counts.errors) 186 # All tests should be accounted for. 187 self.assertEqual(10, result.counts.total()) 188 self.assertEqual( 189 kunit_parser.TestStatus.SUCCESS, 190 result.status) 191 192 def test_no_tests(self): 193 header_log = test_data_path('test_is_test_passed-no_tests_run_with_header.log') 194 with open(header_log) as file: 195 result = kunit_parser.parse_run_tests( 196 kunit_parser.extract_tap_lines(file.readlines())) 197 self.assertEqual(0, len(result.subtests)) 198 self.assertEqual( 199 kunit_parser.TestStatus.NO_TESTS, 200 result.status) 201 202 def test_no_tests_no_plan(self): 203 no_plan_log = test_data_path('test_is_test_passed-no_tests_no_plan.log') 204 with open(no_plan_log) as file: 205 result = kunit_parser.parse_run_tests( 206 kunit_parser.extract_tap_lines(file.readlines())) 207 self.assertEqual(0, len(result.subtests[0].subtests[0].subtests)) 208 self.assertEqual( 209 kunit_parser.TestStatus.NO_TESTS, 210 result.subtests[0].subtests[0].status) 211 self.assertEqual(1, result.counts.errors) 212 213 214 def test_no_kunit_output(self): 215 crash_log = test_data_path('test_insufficient_memory.log') 216 print_mock = mock.patch('kunit_printer.Printer.print').start() 217 with open(crash_log) as file: 218 result = kunit_parser.parse_run_tests( 219 kunit_parser.extract_tap_lines(file.readlines())) 220 print_mock.assert_any_call(StrContains('could not find any KTAP output!')) 221 print_mock.stop() 222 self.assertEqual(0, len(result.subtests)) 223 224 def test_skipped_test(self): 225 skipped_log = test_data_path('test_skip_tests.log') 226 with open(skipped_log) as file: 227 result = kunit_parser.parse_run_tests(file.readlines()) 228 229 # A skipped test does not fail the whole suite. 230 self.assertEqual( 231 kunit_parser.TestStatus.SUCCESS, 232 result.status) 233 234 def test_skipped_all_tests(self): 235 skipped_log = test_data_path('test_skip_all_tests.log') 236 with open(skipped_log) as file: 237 result = kunit_parser.parse_run_tests(file.readlines()) 238 239 self.assertEqual( 240 kunit_parser.TestStatus.SKIPPED, 241 result.status) 242 243 def test_ignores_hyphen(self): 244 hyphen_log = test_data_path('test_strip_hyphen.log') 245 with open(hyphen_log) as file: 246 result = kunit_parser.parse_run_tests(file.readlines()) 247 248 # A skipped test does not fail the whole suite. 249 self.assertEqual( 250 kunit_parser.TestStatus.SUCCESS, 251 result.status) 252 self.assertEqual( 253 "sysctl_test", 254 result.subtests[0].name) 255 self.assertEqual( 256 "example", 257 result.subtests[1].name) 258 file.close() 259 260 261 def test_ignores_prefix_printk_time(self): 262 prefix_log = test_data_path('test_config_printk_time.log') 263 with open(prefix_log) as file: 264 result = kunit_parser.parse_run_tests(file.readlines()) 265 self.assertEqual( 266 kunit_parser.TestStatus.SUCCESS, 267 result.status) 268 self.assertEqual('kunit-resource-test', result.subtests[0].name) 269 270 def test_ignores_multiple_prefixes(self): 271 prefix_log = test_data_path('test_multiple_prefixes.log') 272 with open(prefix_log) as file: 273 result = kunit_parser.parse_run_tests(file.readlines()) 274 self.assertEqual( 275 kunit_parser.TestStatus.SUCCESS, 276 result.status) 277 self.assertEqual('kunit-resource-test', result.subtests[0].name) 278 279 def test_prefix_mixed_kernel_output(self): 280 mixed_prefix_log = test_data_path('test_interrupted_tap_output.log') 281 with open(mixed_prefix_log) as file: 282 result = kunit_parser.parse_run_tests(file.readlines()) 283 self.assertEqual( 284 kunit_parser.TestStatus.SUCCESS, 285 result.status) 286 self.assertEqual('kunit-resource-test', result.subtests[0].name) 287 288 def test_prefix_poundsign(self): 289 pound_log = test_data_path('test_pound_sign.log') 290 with open(pound_log) as file: 291 result = kunit_parser.parse_run_tests(file.readlines()) 292 self.assertEqual( 293 kunit_parser.TestStatus.SUCCESS, 294 result.status) 295 self.assertEqual('kunit-resource-test', result.subtests[0].name) 296 297 def test_kernel_panic_end(self): 298 panic_log = test_data_path('test_kernel_panic_interrupt.log') 299 with open(panic_log) as file: 300 result = kunit_parser.parse_run_tests(file.readlines()) 301 self.assertEqual( 302 kunit_parser.TestStatus.TEST_CRASHED, 303 result.status) 304 self.assertEqual('kunit-resource-test', result.subtests[0].name) 305 306 def test_pound_no_prefix(self): 307 pound_log = test_data_path('test_pound_no_prefix.log') 308 with open(pound_log) as file: 309 result = kunit_parser.parse_run_tests(file.readlines()) 310 self.assertEqual( 311 kunit_parser.TestStatus.SUCCESS, 312 result.status) 313 self.assertEqual('kunit-resource-test', result.subtests[0].name) 314 315def line_stream_from_strs(strs: Iterable[str]) -> kunit_parser.LineStream: 316 return kunit_parser.LineStream(enumerate(strs, start=1)) 317 318class LineStreamTest(unittest.TestCase): 319 320 def test_basic(self): 321 stream = line_stream_from_strs(['hello', 'world']) 322 323 self.assertTrue(stream, msg='Should be more input') 324 self.assertEqual(stream.line_number(), 1) 325 self.assertEqual(stream.peek(), 'hello') 326 self.assertEqual(stream.pop(), 'hello') 327 328 self.assertTrue(stream, msg='Should be more input') 329 self.assertEqual(stream.line_number(), 2) 330 self.assertEqual(stream.peek(), 'world') 331 self.assertEqual(stream.pop(), 'world') 332 333 self.assertFalse(stream, msg='Should be no more input') 334 with self.assertRaisesRegex(ValueError, 'LineStream: going past EOF'): 335 stream.pop() 336 337 def test_is_lazy(self): 338 called_times = 0 339 def generator(): 340 nonlocal called_times 341 for _ in range(1,5): 342 called_times += 1 343 yield called_times, str(called_times) 344 345 stream = kunit_parser.LineStream(generator()) 346 self.assertEqual(called_times, 0) 347 348 self.assertEqual(stream.pop(), '1') 349 self.assertEqual(called_times, 1) 350 351 self.assertEqual(stream.pop(), '2') 352 self.assertEqual(called_times, 2) 353 354class LinuxSourceTreeTest(unittest.TestCase): 355 356 def setUp(self): 357 mock.patch.object(signal, 'signal').start() 358 self.addCleanup(mock.patch.stopall) 359 360 def test_invalid_kunitconfig(self): 361 with self.assertRaisesRegex(kunit_kernel.ConfigError, 'nonexistent.* does not exist'): 362 kunit_kernel.LinuxSourceTree('', kunitconfig_paths=['/nonexistent_file']) 363 364 def test_valid_kunitconfig(self): 365 with tempfile.NamedTemporaryFile('wt') as kunitconfig: 366 kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[kunitconfig.name]) 367 368 def test_dir_kunitconfig(self): 369 with tempfile.TemporaryDirectory('') as dir: 370 with open(os.path.join(dir, '.kunitconfig'), 'w'): 371 pass 372 kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[dir]) 373 374 def test_multiple_kunitconfig(self): 375 want_kconfig = kunit_config.Kconfig() 376 want_kconfig.add_entry('KUNIT', 'y') 377 want_kconfig.add_entry('KUNIT_TEST', 'm') 378 379 with tempfile.TemporaryDirectory('') as dir: 380 other = os.path.join(dir, 'otherkunitconfig') 381 with open(os.path.join(dir, '.kunitconfig'), 'w') as f: 382 f.write('CONFIG_KUNIT=y') 383 with open(other, 'w') as f: 384 f.write('CONFIG_KUNIT_TEST=m') 385 pass 386 387 tree = kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[dir, other]) 388 self.assertTrue(want_kconfig.is_subset_of(tree._kconfig), msg=tree._kconfig) 389 390 391 def test_multiple_kunitconfig_invalid(self): 392 with tempfile.TemporaryDirectory('') as dir: 393 other = os.path.join(dir, 'otherkunitconfig') 394 with open(os.path.join(dir, '.kunitconfig'), 'w') as f: 395 f.write('CONFIG_KUNIT=y') 396 with open(other, 'w') as f: 397 f.write('CONFIG_KUNIT=m') 398 399 with self.assertRaisesRegex(kunit_kernel.ConfigError, '(?s)Multiple values.*CONFIG_KUNIT'): 400 kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[dir, other]) 401 402 403 def test_kconfig_add(self): 404 want_kconfig = kunit_config.Kconfig() 405 want_kconfig.add_entry('NOT_REAL', 'y') 406 407 tree = kunit_kernel.LinuxSourceTree('', kconfig_add=['CONFIG_NOT_REAL=y']) 408 self.assertTrue(want_kconfig.is_subset_of(tree._kconfig), msg=tree._kconfig) 409 410 def test_invalid_arch(self): 411 with self.assertRaisesRegex(kunit_kernel.ConfigError, 'not a valid arch, options are.*x86_64'): 412 kunit_kernel.LinuxSourceTree('', arch='invalid') 413 414 def test_run_kernel_hits_exception(self): 415 def fake_start(unused_args, unused_build_dir): 416 return subprocess.Popen(['echo "hi\nbye"'], shell=True, text=True, stdout=subprocess.PIPE) 417 418 with tempfile.TemporaryDirectory('') as build_dir: 419 tree = kunit_kernel.LinuxSourceTree(build_dir) 420 mock.patch.object(tree._ops, 'start', side_effect=fake_start).start() 421 422 with self.assertRaises(ValueError): 423 for line in tree.run_kernel(build_dir=build_dir): 424 self.assertEqual(line, 'hi\n') 425 raise ValueError('uh oh, did not read all output') 426 427 with open(kunit_kernel.get_outfile_path(build_dir), 'rt') as outfile: 428 self.assertEqual(outfile.read(), 'hi\nbye\n', msg='Missing some output') 429 430 def test_build_reconfig_no_config(self): 431 with tempfile.TemporaryDirectory('') as build_dir: 432 with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f: 433 f.write('CONFIG_KUNIT=y') 434 435 tree = kunit_kernel.LinuxSourceTree(build_dir) 436 # Stub out the source tree operations, so we don't have 437 # the defaults for any given architecture get in the 438 # way. 439 tree._ops = kunit_kernel.LinuxSourceTreeOperations('none', None) 440 mock_build_config = mock.patch.object(tree, 'build_config').start() 441 442 # Should generate the .config 443 self.assertTrue(tree.build_reconfig(build_dir, make_options=[])) 444 mock_build_config.assert_called_once_with(build_dir, []) 445 446 def test_build_reconfig_existing_config(self): 447 with tempfile.TemporaryDirectory('') as build_dir: 448 # Existing .config is a superset, should not touch it 449 with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f: 450 f.write('CONFIG_KUNIT=y') 451 with open(kunit_kernel.get_old_kunitconfig_path(build_dir), 'w') as f: 452 f.write('CONFIG_KUNIT=y') 453 with open(kunit_kernel.get_kconfig_path(build_dir), 'w') as f: 454 f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y') 455 456 tree = kunit_kernel.LinuxSourceTree(build_dir) 457 # Stub out the source tree operations, so we don't have 458 # the defaults for any given architecture get in the 459 # way. 460 tree._ops = kunit_kernel.LinuxSourceTreeOperations('none', None) 461 mock_build_config = mock.patch.object(tree, 'build_config').start() 462 463 self.assertTrue(tree.build_reconfig(build_dir, make_options=[])) 464 self.assertEqual(mock_build_config.call_count, 0) 465 466 def test_build_reconfig_remove_option(self): 467 with tempfile.TemporaryDirectory('') as build_dir: 468 # We removed CONFIG_KUNIT_TEST=y from our .kunitconfig... 469 with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f: 470 f.write('CONFIG_KUNIT=y') 471 with open(kunit_kernel.get_old_kunitconfig_path(build_dir), 'w') as f: 472 f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y') 473 with open(kunit_kernel.get_kconfig_path(build_dir), 'w') as f: 474 f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y') 475 476 tree = kunit_kernel.LinuxSourceTree(build_dir) 477 # Stub out the source tree operations, so we don't have 478 # the defaults for any given architecture get in the 479 # way. 480 tree._ops = kunit_kernel.LinuxSourceTreeOperations('none', None) 481 mock_build_config = mock.patch.object(tree, 'build_config').start() 482 483 # ... so we should trigger a call to build_config() 484 self.assertTrue(tree.build_reconfig(build_dir, make_options=[])) 485 mock_build_config.assert_called_once_with(build_dir, []) 486 487 # TODO: add more test cases. 488 489 490class KUnitJsonTest(unittest.TestCase): 491 def setUp(self): 492 self.print_mock = mock.patch('kunit_printer.Printer.print').start() 493 self.addCleanup(mock.patch.stopall) 494 495 def _json_for(self, log_file): 496 with open(test_data_path(log_file)) as file: 497 test_result = kunit_parser.parse_run_tests(file) 498 json_obj = kunit_json.get_json_result( 499 test=test_result, 500 metadata=kunit_json.Metadata()) 501 return json.loads(json_obj) 502 503 def test_failed_test_json(self): 504 result = self._json_for('test_is_test_passed-failure.log') 505 self.assertEqual( 506 {'name': 'example_simple_test', 'status': 'FAIL'}, 507 result["sub_groups"][1]["test_cases"][0]) 508 509 def test_crashed_test_json(self): 510 result = self._json_for('test_kernel_panic_interrupt.log') 511 self.assertEqual( 512 {'name': '', 'status': 'ERROR'}, 513 result["sub_groups"][2]["test_cases"][1]) 514 515 def test_skipped_test_json(self): 516 result = self._json_for('test_skip_tests.log') 517 self.assertEqual( 518 {'name': 'example_skip_test', 'status': 'SKIP'}, 519 result["sub_groups"][1]["test_cases"][1]) 520 521 def test_no_tests_json(self): 522 result = self._json_for('test_is_test_passed-no_tests_run_with_header.log') 523 self.assertEqual(0, len(result['sub_groups'])) 524 525 def test_nested_json(self): 526 result = self._json_for('test_is_test_passed-all_passed_nested.log') 527 self.assertEqual( 528 {'name': 'example_simple_test', 'status': 'PASS'}, 529 result["sub_groups"][0]["sub_groups"][0]["test_cases"][0]) 530 531class StrContains(str): 532 def __eq__(self, other): 533 return self in other 534 535class KUnitMainTest(unittest.TestCase): 536 def setUp(self): 537 path = test_data_path('test_is_test_passed-all_passed.log') 538 with open(path) as file: 539 all_passed_log = file.readlines() 540 541 self.print_mock = mock.patch('kunit_printer.Printer.print').start() 542 self.addCleanup(mock.patch.stopall) 543 544 self.mock_linux_init = mock.patch.object(kunit_kernel, 'LinuxSourceTree').start() 545 self.linux_source_mock = self.mock_linux_init.return_value 546 self.linux_source_mock.build_reconfig.return_value = True 547 self.linux_source_mock.build_kernel.return_value = True 548 self.linux_source_mock.run_kernel.return_value = all_passed_log 549 550 def test_config_passes_args_pass(self): 551 kunit.main(['config', '--build_dir=.kunit']) 552 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) 553 self.assertEqual(self.linux_source_mock.run_kernel.call_count, 0) 554 555 def test_build_passes_args_pass(self): 556 kunit.main(['build']) 557 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) 558 self.linux_source_mock.build_kernel.assert_called_once_with(kunit.get_default_jobs(), '.kunit', None) 559 self.assertEqual(self.linux_source_mock.run_kernel.call_count, 0) 560 561 def test_exec_passes_args_pass(self): 562 kunit.main(['exec']) 563 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 0) 564 self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1) 565 self.linux_source_mock.run_kernel.assert_called_once_with( 566 args=None, build_dir='.kunit', filter_glob='', timeout=300) 567 self.print_mock.assert_any_call(StrContains('Testing complete.')) 568 569 def test_run_passes_args_pass(self): 570 kunit.main(['run']) 571 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) 572 self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1) 573 self.linux_source_mock.run_kernel.assert_called_once_with( 574 args=None, build_dir='.kunit', filter_glob='', timeout=300) 575 self.print_mock.assert_any_call(StrContains('Testing complete.')) 576 577 def test_exec_passes_args_fail(self): 578 self.linux_source_mock.run_kernel = mock.Mock(return_value=[]) 579 with self.assertRaises(SystemExit) as e: 580 kunit.main(['exec']) 581 self.assertEqual(e.exception.code, 1) 582 583 def test_run_passes_args_fail(self): 584 self.linux_source_mock.run_kernel = mock.Mock(return_value=[]) 585 with self.assertRaises(SystemExit) as e: 586 kunit.main(['run']) 587 self.assertEqual(e.exception.code, 1) 588 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) 589 self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1) 590 self.print_mock.assert_any_call(StrContains('could not find any KTAP output!')) 591 592 def test_exec_no_tests(self): 593 self.linux_source_mock.run_kernel = mock.Mock(return_value=['TAP version 14', '1..0']) 594 with self.assertRaises(SystemExit) as e: 595 kunit.main(['run']) 596 self.assertEqual(e.exception.code, 1) 597 self.linux_source_mock.run_kernel.assert_called_once_with( 598 args=None, build_dir='.kunit', filter_glob='', timeout=300) 599 self.print_mock.assert_any_call(StrContains(' 0 tests run!')) 600 601 def test_exec_raw_output(self): 602 self.linux_source_mock.run_kernel = mock.Mock(return_value=[]) 603 kunit.main(['exec', '--raw_output']) 604 self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1) 605 for call in self.print_mock.call_args_list: 606 self.assertNotEqual(call, mock.call(StrContains('Testing complete.'))) 607 self.assertNotEqual(call, mock.call(StrContains(' 0 tests run!'))) 608 609 def test_run_raw_output(self): 610 self.linux_source_mock.run_kernel = mock.Mock(return_value=[]) 611 kunit.main(['run', '--raw_output']) 612 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) 613 self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1) 614 for call in self.print_mock.call_args_list: 615 self.assertNotEqual(call, mock.call(StrContains('Testing complete.'))) 616 self.assertNotEqual(call, mock.call(StrContains(' 0 tests run!'))) 617 618 def test_run_raw_output_kunit(self): 619 self.linux_source_mock.run_kernel = mock.Mock(return_value=[]) 620 kunit.main(['run', '--raw_output=kunit']) 621 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) 622 self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1) 623 for call in self.print_mock.call_args_list: 624 self.assertNotEqual(call, mock.call(StrContains('Testing complete.'))) 625 self.assertNotEqual(call, mock.call(StrContains(' 0 tests run'))) 626 627 def test_run_raw_output_invalid(self): 628 self.linux_source_mock.run_kernel = mock.Mock(return_value=[]) 629 with self.assertRaises(SystemExit) as e: 630 kunit.main(['run', '--raw_output=invalid']) 631 self.assertNotEqual(e.exception.code, 0) 632 633 def test_run_raw_output_does_not_take_positional_args(self): 634 # --raw_output is a string flag, but we don't want it to consume 635 # any positional arguments, only ones after an '=' 636 self.linux_source_mock.run_kernel = mock.Mock(return_value=[]) 637 kunit.main(['run', '--raw_output', 'filter_glob']) 638 self.linux_source_mock.run_kernel.assert_called_once_with( 639 args=None, build_dir='.kunit', filter_glob='filter_glob', timeout=300) 640 641 def test_exec_timeout(self): 642 timeout = 3453 643 kunit.main(['exec', '--timeout', str(timeout)]) 644 self.linux_source_mock.run_kernel.assert_called_once_with( 645 args=None, build_dir='.kunit', filter_glob='', timeout=timeout) 646 self.print_mock.assert_any_call(StrContains('Testing complete.')) 647 648 def test_run_timeout(self): 649 timeout = 3453 650 kunit.main(['run', '--timeout', str(timeout)]) 651 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) 652 self.linux_source_mock.run_kernel.assert_called_once_with( 653 args=None, build_dir='.kunit', filter_glob='', timeout=timeout) 654 self.print_mock.assert_any_call(StrContains('Testing complete.')) 655 656 def test_run_builddir(self): 657 build_dir = '.kunit' 658 kunit.main(['run', '--build_dir=.kunit']) 659 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) 660 self.linux_source_mock.run_kernel.assert_called_once_with( 661 args=None, build_dir=build_dir, filter_glob='', timeout=300) 662 self.print_mock.assert_any_call(StrContains('Testing complete.')) 663 664 def test_config_builddir(self): 665 build_dir = '.kunit' 666 kunit.main(['config', '--build_dir', build_dir]) 667 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) 668 669 def test_build_builddir(self): 670 build_dir = '.kunit' 671 jobs = kunit.get_default_jobs() 672 kunit.main(['build', '--build_dir', build_dir]) 673 self.linux_source_mock.build_kernel.assert_called_once_with(jobs, build_dir, None) 674 675 def test_exec_builddir(self): 676 build_dir = '.kunit' 677 kunit.main(['exec', '--build_dir', build_dir]) 678 self.linux_source_mock.run_kernel.assert_called_once_with( 679 args=None, build_dir=build_dir, filter_glob='', timeout=300) 680 self.print_mock.assert_any_call(StrContains('Testing complete.')) 681 682 def test_run_kunitconfig(self): 683 kunit.main(['run', '--kunitconfig=mykunitconfig']) 684 # Just verify that we parsed and initialized it correctly here. 685 self.mock_linux_init.assert_called_once_with('.kunit', 686 kunitconfig_paths=['mykunitconfig'], 687 kconfig_add=None, 688 arch='um', 689 cross_compile=None, 690 qemu_config_path=None, 691 extra_qemu_args=[]) 692 693 def test_config_kunitconfig(self): 694 kunit.main(['config', '--kunitconfig=mykunitconfig']) 695 # Just verify that we parsed and initialized it correctly here. 696 self.mock_linux_init.assert_called_once_with('.kunit', 697 kunitconfig_paths=['mykunitconfig'], 698 kconfig_add=None, 699 arch='um', 700 cross_compile=None, 701 qemu_config_path=None, 702 extra_qemu_args=[]) 703 704 def test_config_alltests(self): 705 kunit.main(['config', '--kunitconfig=mykunitconfig', '--alltests']) 706 # Just verify that we parsed and initialized it correctly here. 707 self.mock_linux_init.assert_called_once_with('.kunit', 708 kunitconfig_paths=[kunit_kernel.ALL_TESTS_CONFIG_PATH, 'mykunitconfig'], 709 kconfig_add=None, 710 arch='um', 711 cross_compile=None, 712 qemu_config_path=None, 713 extra_qemu_args=[]) 714 715 716 @mock.patch.object(kunit_kernel, 'LinuxSourceTree') 717 def test_run_multiple_kunitconfig(self, mock_linux_init): 718 mock_linux_init.return_value = self.linux_source_mock 719 kunit.main(['run', '--kunitconfig=mykunitconfig', '--kunitconfig=other']) 720 # Just verify that we parsed and initialized it correctly here. 721 mock_linux_init.assert_called_once_with('.kunit', 722 kunitconfig_paths=['mykunitconfig', 'other'], 723 kconfig_add=None, 724 arch='um', 725 cross_compile=None, 726 qemu_config_path=None, 727 extra_qemu_args=[]) 728 729 def test_run_kconfig_add(self): 730 kunit.main(['run', '--kconfig_add=CONFIG_KASAN=y', '--kconfig_add=CONFIG_KCSAN=y']) 731 # Just verify that we parsed and initialized it correctly here. 732 self.mock_linux_init.assert_called_once_with('.kunit', 733 kunitconfig_paths=[], 734 kconfig_add=['CONFIG_KASAN=y', 'CONFIG_KCSAN=y'], 735 arch='um', 736 cross_compile=None, 737 qemu_config_path=None, 738 extra_qemu_args=[]) 739 740 def test_run_qemu_args(self): 741 kunit.main(['run', '--arch=x86_64', '--qemu_args', '-m 2048']) 742 # Just verify that we parsed and initialized it correctly here. 743 self.mock_linux_init.assert_called_once_with('.kunit', 744 kunitconfig_paths=[], 745 kconfig_add=None, 746 arch='x86_64', 747 cross_compile=None, 748 qemu_config_path=None, 749 extra_qemu_args=['-m', '2048']) 750 751 def test_run_kernel_args(self): 752 kunit.main(['run', '--kernel_args=a=1', '--kernel_args=b=2']) 753 self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) 754 self.linux_source_mock.run_kernel.assert_called_once_with( 755 args=['a=1','b=2'], build_dir='.kunit', filter_glob='', timeout=300) 756 self.print_mock.assert_any_call(StrContains('Testing complete.')) 757 758 def test_list_tests(self): 759 want = ['suite.test1', 'suite.test2', 'suite2.test1'] 760 self.linux_source_mock.run_kernel.return_value = ['TAP version 14', 'init: random output'] + want 761 762 got = kunit._list_tests(self.linux_source_mock, 763 kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*', None, 'suite')) 764 765 self.assertEqual(got, want) 766 # Should respect the user's filter glob when listing tests. 767 self.linux_source_mock.run_kernel.assert_called_once_with( 768 args=['kunit.action=list'], build_dir='.kunit', filter_glob='suite*', timeout=300) 769 770 771 @mock.patch.object(kunit, '_list_tests') 772 def test_run_isolated_by_suite(self, mock_tests): 773 mock_tests.return_value = ['suite.test1', 'suite.test2', 'suite2.test1'] 774 kunit.main(['exec', '--run_isolated=suite', 'suite*.test*']) 775 776 # Should respect the user's filter glob when listing tests. 777 mock_tests.assert_called_once_with(mock.ANY, 778 kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*.test*', None, 'suite')) 779 self.linux_source_mock.run_kernel.assert_has_calls([ 780 mock.call(args=None, build_dir='.kunit', filter_glob='suite.test*', timeout=300), 781 mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test*', timeout=300), 782 ]) 783 784 @mock.patch.object(kunit, '_list_tests') 785 def test_run_isolated_by_test(self, mock_tests): 786 mock_tests.return_value = ['suite.test1', 'suite.test2', 'suite2.test1'] 787 kunit.main(['exec', '--run_isolated=test', 'suite*']) 788 789 # Should respect the user's filter glob when listing tests. 790 mock_tests.assert_called_once_with(mock.ANY, 791 kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*', None, 'test')) 792 self.linux_source_mock.run_kernel.assert_has_calls([ 793 mock.call(args=None, build_dir='.kunit', filter_glob='suite.test1', timeout=300), 794 mock.call(args=None, build_dir='.kunit', filter_glob='suite.test2', timeout=300), 795 mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test1', timeout=300), 796 ]) 797 798 799if __name__ == '__main__': 800 unittest.main() 801