Path: blob/master/tools/testing/kunit/kunit_tool_test.py
26282 views
#!/usr/bin/env python31# SPDX-License-Identifier: GPL-2.02#3# A collection of tests for tools/testing/kunit/kunit.py4#5# Copyright (C) 2019, Google LLC.6# Author: Brendan Higgins <[email protected]>78import unittest9from unittest import mock1011import tempfile, shutil # Handling test_tmpdir1213import itertools14import json15import os16import signal17import subprocess18from typing import Iterable1920import kunit_config21import kunit_parser22import kunit_kernel23import kunit_json24import kunit25from kunit_printer import stdout2627test_tmpdir = ''28abs_test_data_dir = ''2930def setUpModule():31global test_tmpdir, abs_test_data_dir32test_tmpdir = tempfile.mkdtemp()33abs_test_data_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'test_data'))3435def tearDownModule():36shutil.rmtree(test_tmpdir)3738def test_data_path(path):39return os.path.join(abs_test_data_dir, path)4041class KconfigTest(unittest.TestCase):4243def test_is_subset_of(self):44kconfig0 = kunit_config.Kconfig()45self.assertTrue(kconfig0.is_subset_of(kconfig0))4647kconfig1 = kunit_config.Kconfig()48kconfig1.add_entry('TEST', 'y')49self.assertTrue(kconfig1.is_subset_of(kconfig1))50self.assertTrue(kconfig0.is_subset_of(kconfig1))51self.assertFalse(kconfig1.is_subset_of(kconfig0))5253def test_read_from_file(self):54kconfig_path = test_data_path('test_read_from_file.kconfig')5556kconfig = kunit_config.parse_file(kconfig_path)5758expected_kconfig = kunit_config.Kconfig()59expected_kconfig.add_entry('UML', 'y')60expected_kconfig.add_entry('MMU', 'y')61expected_kconfig.add_entry('TEST', 'y')62expected_kconfig.add_entry('EXAMPLE_TEST', 'y')63expected_kconfig.add_entry('MK8', 'n')6465self.assertEqual(kconfig, expected_kconfig)6667def test_write_to_file(self):68kconfig_path = os.path.join(test_tmpdir, '.config')6970expected_kconfig = kunit_config.Kconfig()71expected_kconfig.add_entry('UML', 'y')72expected_kconfig.add_entry('MMU', 'y')73expected_kconfig.add_entry('TEST', 'y')74expected_kconfig.add_entry('EXAMPLE_TEST', 'y')75expected_kconfig.add_entry('MK8', 'n')7677expected_kconfig.write_to_file(kconfig_path)7879actual_kconfig = kunit_config.parse_file(kconfig_path)80self.assertEqual(actual_kconfig, expected_kconfig)8182class KUnitParserTest(unittest.TestCase):83def setUp(self):84self.print_mock = mock.patch('kunit_printer.Printer.print').start()85self.addCleanup(mock.patch.stopall)8687def noPrintCallContains(self, substr: str):88for call in self.print_mock.mock_calls:89self.assertNotIn(substr, call.args[0])9091def assertContains(self, needle: str, haystack: kunit_parser.LineStream):92# Clone the iterator so we can print the contents on failure.93copy, backup = itertools.tee(haystack)94for line in copy:95if needle in line:96return97raise AssertionError(f'"{needle}" not found in {list(backup)}!')9899def test_output_isolated_correctly(self):100log_path = test_data_path('test_output_isolated_correctly.log')101with open(log_path) as file:102result = kunit_parser.extract_tap_lines(file.readlines())103self.assertContains('TAP version 14', result)104self.assertContains('# Subtest: example', result)105self.assertContains('1..2', result)106self.assertContains('ok 1 - example_simple_test', result)107self.assertContains('ok 2 - example_mock_test', result)108self.assertContains('ok 1 - example', result)109110def test_output_with_prefix_isolated_correctly(self):111log_path = test_data_path('test_pound_sign.log')112with open(log_path) as file:113result = kunit_parser.extract_tap_lines(file.readlines())114self.assertContains('TAP version 14', result)115self.assertContains('# Subtest: kunit-resource-test', result)116self.assertContains('1..5', result)117self.assertContains('ok 1 - kunit_resource_test_init_resources', result)118self.assertContains('ok 2 - kunit_resource_test_alloc_resource', result)119self.assertContains('ok 3 - kunit_resource_test_destroy_resource', result)120self.assertContains('foo bar #', result)121self.assertContains('ok 4 - kunit_resource_test_cleanup_resources', result)122self.assertContains('ok 5 - kunit_resource_test_proper_free_ordering', result)123self.assertContains('ok 1 - kunit-resource-test', result)124self.assertContains('foo bar # non-kunit output', result)125self.assertContains('# Subtest: kunit-try-catch-test', result)126self.assertContains('1..2', result)127self.assertContains('ok 1 - kunit_test_try_catch_successful_try_no_catch',128result)129self.assertContains('ok 2 - kunit_test_try_catch_unsuccessful_try_does_catch',130result)131self.assertContains('ok 2 - kunit-try-catch-test', result)132self.assertContains('# Subtest: string-stream-test', result)133self.assertContains('1..3', result)134self.assertContains('ok 1 - string_stream_test_empty_on_creation', result)135self.assertContains('ok 2 - string_stream_test_not_empty_after_add', result)136self.assertContains('ok 3 - string_stream_test_get_string', result)137self.assertContains('ok 3 - string-stream-test', result)138139def test_parse_successful_test_log(self):140all_passed_log = test_data_path('test_is_test_passed-all_passed.log')141with open(all_passed_log) as file:142result = kunit_parser.parse_run_tests(file.readlines(), stdout)143self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)144self.assertEqual(result.counts.errors, 0)145146def test_parse_successful_nested_tests_log(self):147all_passed_log = test_data_path('test_is_test_passed-all_passed_nested.log')148with open(all_passed_log) as file:149result = kunit_parser.parse_run_tests(file.readlines(), stdout)150self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)151self.assertEqual(result.counts.errors, 0)152153def test_kselftest_nested(self):154kselftest_log = test_data_path('test_is_test_passed-kselftest.log')155with open(kselftest_log) as file:156result = kunit_parser.parse_run_tests(file.readlines(), stdout)157self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)158self.assertEqual(result.counts.errors, 0)159160def test_parse_failed_test_log(self):161failed_log = test_data_path('test_is_test_passed-failure.log')162with open(failed_log) as file:163result = kunit_parser.parse_run_tests(file.readlines(), stdout)164self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status)165self.assertEqual(result.counts.errors, 0)166167def test_no_header(self):168empty_log = test_data_path('test_is_test_passed-no_tests_run_no_header.log')169with open(empty_log) as file:170result = kunit_parser.parse_run_tests(171kunit_parser.extract_tap_lines(file.readlines()), stdout)172self.assertEqual(0, len(result.subtests))173self.assertEqual(kunit_parser.TestStatus.FAILURE_TO_PARSE_TESTS, result.status)174self.assertEqual(result.counts.errors, 1)175176def test_missing_test_plan(self):177missing_plan_log = test_data_path('test_is_test_passed-'178'missing_plan.log')179with open(missing_plan_log) as file:180result = kunit_parser.parse_run_tests(181kunit_parser.extract_tap_lines(182file.readlines()), stdout)183# A missing test plan is not an error.184self.assertEqual(result.counts, kunit_parser.TestCounts(passed=10, errors=0))185self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)186187def test_no_tests(self):188header_log = test_data_path('test_is_test_passed-no_tests_run_with_header.log')189with open(header_log) as file:190result = kunit_parser.parse_run_tests(191kunit_parser.extract_tap_lines(file.readlines()), stdout)192self.assertEqual(0, len(result.subtests))193self.assertEqual(kunit_parser.TestStatus.NO_TESTS, result.status)194self.assertEqual(result.counts.errors, 1)195196def test_no_tests_no_plan(self):197no_plan_log = test_data_path('test_is_test_passed-no_tests_no_plan.log')198with open(no_plan_log) as file:199result = kunit_parser.parse_run_tests(200kunit_parser.extract_tap_lines(file.readlines()), stdout)201self.assertEqual(0, len(result.subtests[0].subtests[0].subtests))202self.assertEqual(203kunit_parser.TestStatus.NO_TESTS,204result.subtests[0].subtests[0].status)205self.assertEqual(result.counts, kunit_parser.TestCounts(passed=1, errors=1))206207208def test_no_kunit_output(self):209crash_log = test_data_path('test_insufficient_memory.log')210print_mock = mock.patch('kunit_printer.Printer.print').start()211with open(crash_log) as file:212result = kunit_parser.parse_run_tests(213kunit_parser.extract_tap_lines(file.readlines()), stdout)214print_mock.assert_any_call(StrContains('Could not find any KTAP output.'))215print_mock.stop()216self.assertEqual(0, len(result.subtests))217self.assertEqual(result.counts.errors, 1)218219def test_skipped_test(self):220skipped_log = test_data_path('test_skip_tests.log')221with open(skipped_log) as file:222result = kunit_parser.parse_run_tests(file.readlines(), stdout)223224# A skipped test does not fail the whole suite.225self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)226self.assertEqual(result.counts, kunit_parser.TestCounts(passed=4, skipped=1))227228def test_skipped_all_tests(self):229skipped_log = test_data_path('test_skip_all_tests.log')230with open(skipped_log) as file:231result = kunit_parser.parse_run_tests(file.readlines(), stdout)232233self.assertEqual(kunit_parser.TestStatus.SKIPPED, result.status)234self.assertEqual(result.counts, kunit_parser.TestCounts(skipped=5))235236def test_ignores_hyphen(self):237hyphen_log = test_data_path('test_strip_hyphen.log')238with open(hyphen_log) as file:239result = kunit_parser.parse_run_tests(file.readlines(), stdout)240241# A skipped test does not fail the whole suite.242self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)243self.assertEqual(244"sysctl_test",245result.subtests[0].name)246self.assertEqual(247"example",248result.subtests[1].name)249250def test_ignores_prefix_printk_time(self):251prefix_log = test_data_path('test_config_printk_time.log')252with open(prefix_log) as file:253result = kunit_parser.parse_run_tests(file.readlines(), stdout)254self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)255self.assertEqual('kunit-resource-test', result.subtests[0].name)256self.assertEqual(result.counts.errors, 0)257258def test_ignores_multiple_prefixes(self):259prefix_log = test_data_path('test_multiple_prefixes.log')260with open(prefix_log) as file:261result = kunit_parser.parse_run_tests(file.readlines(), stdout)262self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)263self.assertEqual('kunit-resource-test', result.subtests[0].name)264self.assertEqual(result.counts.errors, 0)265266def test_prefix_mixed_kernel_output(self):267mixed_prefix_log = test_data_path('test_interrupted_tap_output.log')268with open(mixed_prefix_log) as file:269result = kunit_parser.parse_run_tests(file.readlines(), stdout)270self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)271self.assertEqual('kunit-resource-test', result.subtests[0].name)272self.assertEqual(result.counts.errors, 0)273274def test_prefix_poundsign(self):275pound_log = test_data_path('test_pound_sign.log')276with open(pound_log) as file:277result = kunit_parser.parse_run_tests(file.readlines(), stdout)278self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)279self.assertEqual('kunit-resource-test', result.subtests[0].name)280self.assertEqual(result.counts.errors, 0)281282def test_kernel_panic_end(self):283panic_log = test_data_path('test_kernel_panic_interrupt.log')284with open(panic_log) as file:285result = kunit_parser.parse_run_tests(file.readlines(), stdout)286self.assertEqual(kunit_parser.TestStatus.TEST_CRASHED, result.status)287self.assertEqual('kunit-resource-test', result.subtests[0].name)288self.assertGreaterEqual(result.counts.errors, 1)289290def test_pound_no_prefix(self):291pound_log = test_data_path('test_pound_no_prefix.log')292with open(pound_log) as file:293result = kunit_parser.parse_run_tests(file.readlines(), stdout)294self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)295self.assertEqual('kunit-resource-test', result.subtests[0].name)296self.assertEqual(result.counts.errors, 0)297298def test_summarize_failures(self):299output = """300KTAP version 13011..2302# Subtest: all_failed_suite3031..2304not ok 1 - test1305not ok 2 - test2306not ok 1 - all_failed_suite307# Subtest: some_failed_suite3081..2309ok 1 - test1310not ok 2 - test2311not ok 1 - some_failed_suite312"""313result = kunit_parser.parse_run_tests(output.splitlines(), stdout)314self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status)315316self.assertEqual(kunit_parser._summarize_failed_tests(result),317'Failures: all_failed_suite, some_failed_suite.test2')318319def test_ktap_format(self):320ktap_log = test_data_path('test_parse_ktap_output.log')321with open(ktap_log) as file:322result = kunit_parser.parse_run_tests(file.readlines(), stdout)323self.assertEqual(result.counts, kunit_parser.TestCounts(passed=3))324self.assertEqual('suite', result.subtests[0].name)325self.assertEqual('case_1', result.subtests[0].subtests[0].name)326self.assertEqual('case_2', result.subtests[0].subtests[1].name)327328def test_parse_subtest_header(self):329ktap_log = test_data_path('test_parse_subtest_header.log')330with open(ktap_log) as file:331kunit_parser.parse_run_tests(file.readlines(), stdout)332self.print_mock.assert_any_call(StrContains('suite (1 subtest)'))333334def test_parse_attributes(self):335ktap_log = test_data_path('test_parse_attributes.log')336with open(ktap_log) as file:337result = kunit_parser.parse_run_tests(file.readlines(), stdout)338339# Test should pass with no errors340self.assertEqual(result.counts, kunit_parser.TestCounts(passed=1, errors=0))341self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)342343# Ensure suite header is parsed correctly344self.print_mock.assert_any_call(StrContains('suite (1 subtest)'))345346# Ensure attributes in correct test log347self.assertContains('# module: example', result.subtests[0].log)348self.assertContains('# test.speed: slow', result.subtests[0].subtests[0].log)349350def test_show_test_output_on_failure(self):351output = """352KTAP version 13531..1354Test output.355Indented more.356not ok 1 test1357"""358result = kunit_parser.parse_run_tests(output.splitlines(), stdout)359self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status)360361self.print_mock.assert_any_call(StrContains('Test output.'))362self.print_mock.assert_any_call(StrContains(' Indented more.'))363self.noPrintCallContains('not ok 1 test1')364365def test_parse_late_test_plan(self):366output = """367TAP version 13368ok 4 test43691..4370"""371result = kunit_parser.parse_run_tests(output.splitlines(), stdout)372# Missing test results after test plan should alert a suspected test crash.373self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)374self.assertEqual(result.counts, kunit_parser.TestCounts(passed=1, errors=2))375376def line_stream_from_strs(strs: Iterable[str]) -> kunit_parser.LineStream:377return kunit_parser.LineStream(enumerate(strs, start=1))378379class LineStreamTest(unittest.TestCase):380381def test_basic(self):382stream = line_stream_from_strs(['hello', 'world'])383384self.assertTrue(stream, msg='Should be more input')385self.assertEqual(stream.line_number(), 1)386self.assertEqual(stream.peek(), 'hello')387self.assertEqual(stream.pop(), 'hello')388389self.assertTrue(stream, msg='Should be more input')390self.assertEqual(stream.line_number(), 2)391self.assertEqual(stream.peek(), 'world')392self.assertEqual(stream.pop(), 'world')393394self.assertFalse(stream, msg='Should be no more input')395with self.assertRaisesRegex(ValueError, 'LineStream: going past EOF'):396stream.pop()397398def test_is_lazy(self):399called_times = 0400def generator():401nonlocal called_times402for _ in range(1,5):403called_times += 1404yield called_times, str(called_times)405406stream = kunit_parser.LineStream(generator())407self.assertEqual(called_times, 0)408409self.assertEqual(stream.pop(), '1')410self.assertEqual(called_times, 1)411412self.assertEqual(stream.pop(), '2')413self.assertEqual(called_times, 2)414415class LinuxSourceTreeTest(unittest.TestCase):416417def setUp(self):418mock.patch.object(signal, 'signal').start()419self.addCleanup(mock.patch.stopall)420421def test_invalid_kunitconfig(self):422with self.assertRaisesRegex(kunit_kernel.ConfigError, 'nonexistent.* does not exist'):423kunit_kernel.LinuxSourceTree('', kunitconfig_paths=['/nonexistent_file'])424425def test_valid_kunitconfig(self):426with tempfile.NamedTemporaryFile('wt') as kunitconfig:427kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[kunitconfig.name])428429def test_dir_kunitconfig(self):430with tempfile.TemporaryDirectory('') as dir:431with open(os.path.join(dir, '.kunitconfig'), 'w'):432pass433kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[dir])434435def test_multiple_kunitconfig(self):436want_kconfig = kunit_config.Kconfig()437want_kconfig.add_entry('KUNIT', 'y')438want_kconfig.add_entry('KUNIT_TEST', 'm')439440with tempfile.TemporaryDirectory('') as dir:441other = os.path.join(dir, 'otherkunitconfig')442with open(os.path.join(dir, '.kunitconfig'), 'w') as f:443f.write('CONFIG_KUNIT=y')444with open(other, 'w') as f:445f.write('CONFIG_KUNIT_TEST=m')446pass447448tree = kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[dir, other])449self.assertTrue(want_kconfig.is_subset_of(tree._kconfig), msg=tree._kconfig)450451452def test_multiple_kunitconfig_invalid(self):453with tempfile.TemporaryDirectory('') as dir:454other = os.path.join(dir, 'otherkunitconfig')455with open(os.path.join(dir, '.kunitconfig'), 'w') as f:456f.write('CONFIG_KUNIT=y')457with open(other, 'w') as f:458f.write('CONFIG_KUNIT=m')459460with self.assertRaisesRegex(kunit_kernel.ConfigError, '(?s)Multiple values.*CONFIG_KUNIT'):461kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[dir, other])462463464def test_kconfig_add(self):465want_kconfig = kunit_config.Kconfig()466want_kconfig.add_entry('NOT_REAL', 'y')467468tree = kunit_kernel.LinuxSourceTree('', kconfig_add=['CONFIG_NOT_REAL=y'])469self.assertTrue(want_kconfig.is_subset_of(tree._kconfig), msg=tree._kconfig)470471def test_invalid_arch(self):472with self.assertRaisesRegex(kunit_kernel.ConfigError, 'not a valid arch, options are.*x86_64'):473kunit_kernel.LinuxSourceTree('', arch='invalid')474475def test_run_kernel_hits_exception(self):476def fake_start(unused_args, unused_build_dir):477return subprocess.Popen(['echo "hi\nbye"'], shell=True, text=True, stdout=subprocess.PIPE)478479with tempfile.TemporaryDirectory('') as build_dir:480tree = kunit_kernel.LinuxSourceTree(build_dir)481mock.patch.object(tree._ops, 'start', side_effect=fake_start).start()482483with self.assertRaises(ValueError):484for line in tree.run_kernel(build_dir=build_dir):485self.assertEqual(line, 'hi\n')486raise ValueError('uh oh, did not read all output')487488with open(kunit_kernel.get_outfile_path(build_dir), 'rt') as outfile:489self.assertEqual(outfile.read(), 'hi\nbye\n', msg='Missing some output')490491def test_build_reconfig_no_config(self):492with tempfile.TemporaryDirectory('') as build_dir:493with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f:494f.write('CONFIG_KUNIT=y')495496tree = kunit_kernel.LinuxSourceTree(build_dir)497# Stub out the source tree operations, so we don't have498# the defaults for any given architecture get in the499# way.500tree._ops = kunit_kernel.LinuxSourceTreeOperations('none', None)501mock_build_config = mock.patch.object(tree, 'build_config').start()502503# Should generate the .config504self.assertTrue(tree.build_reconfig(build_dir, make_options=[]))505mock_build_config.assert_called_once_with(build_dir, [])506507def test_build_reconfig_existing_config(self):508with tempfile.TemporaryDirectory('') as build_dir:509# Existing .config is a superset, should not touch it510with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f:511f.write('CONFIG_KUNIT=y')512with open(kunit_kernel.get_old_kunitconfig_path(build_dir), 'w') as f:513f.write('CONFIG_KUNIT=y')514with open(kunit_kernel.get_kconfig_path(build_dir), 'w') as f:515f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')516517tree = kunit_kernel.LinuxSourceTree(build_dir)518# Stub out the source tree operations, so we don't have519# the defaults for any given architecture get in the520# way.521tree._ops = kunit_kernel.LinuxSourceTreeOperations('none', None)522mock_build_config = mock.patch.object(tree, 'build_config').start()523524self.assertTrue(tree.build_reconfig(build_dir, make_options=[]))525self.assertEqual(mock_build_config.call_count, 0)526527def test_build_reconfig_remove_option(self):528with tempfile.TemporaryDirectory('') as build_dir:529# We removed CONFIG_KUNIT_TEST=y from our .kunitconfig...530with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f:531f.write('CONFIG_KUNIT=y')532with open(kunit_kernel.get_old_kunitconfig_path(build_dir), 'w') as f:533f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')534with open(kunit_kernel.get_kconfig_path(build_dir), 'w') as f:535f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')536537tree = kunit_kernel.LinuxSourceTree(build_dir)538# Stub out the source tree operations, so we don't have539# the defaults for any given architecture get in the540# way.541tree._ops = kunit_kernel.LinuxSourceTreeOperations('none', None)542mock_build_config = mock.patch.object(tree, 'build_config').start()543544# ... so we should trigger a call to build_config()545self.assertTrue(tree.build_reconfig(build_dir, make_options=[]))546mock_build_config.assert_called_once_with(build_dir, [])547548# TODO: add more test cases.549550551class KUnitJsonTest(unittest.TestCase):552def setUp(self):553self.print_mock = mock.patch('kunit_printer.Printer.print').start()554self.addCleanup(mock.patch.stopall)555556def _json_for(self, log_file):557with open(test_data_path(log_file)) as file:558test_result = kunit_parser.parse_run_tests(file, stdout)559json_obj = kunit_json.get_json_result(560test=test_result,561metadata=kunit_json.Metadata())562return json.loads(json_obj)563564def test_failed_test_json(self):565result = self._json_for('test_is_test_passed-failure.log')566self.assertEqual(567{'name': 'example_simple_test', 'status': 'FAIL'},568result["sub_groups"][1]["test_cases"][0])569570def test_crashed_test_json(self):571result = self._json_for('test_kernel_panic_interrupt.log')572self.assertEqual(573{'name': '', 'status': 'ERROR'},574result["sub_groups"][2]["test_cases"][1])575576def test_skipped_test_json(self):577result = self._json_for('test_skip_tests.log')578self.assertEqual(579{'name': 'example_skip_test', 'status': 'SKIP'},580result["sub_groups"][1]["test_cases"][1])581582def test_no_tests_json(self):583result = self._json_for('test_is_test_passed-no_tests_run_with_header.log')584self.assertEqual(0, len(result['sub_groups']))585586def test_nested_json(self):587result = self._json_for('test_is_test_passed-all_passed_nested.log')588self.assertEqual(589{'name': 'example_simple_test', 'status': 'PASS'},590result["sub_groups"][0]["sub_groups"][0]["test_cases"][0])591592class StrContains(str):593def __eq__(self, other):594return self in other595596class KUnitMainTest(unittest.TestCase):597def setUp(self):598path = test_data_path('test_is_test_passed-all_passed.log')599with open(path) as file:600all_passed_log = file.readlines()601602self.print_mock = mock.patch('kunit_printer.Printer.print').start()603self.addCleanup(mock.patch.stopall)604605self.mock_linux_init = mock.patch.object(kunit_kernel, 'LinuxSourceTree').start()606self.linux_source_mock = self.mock_linux_init.return_value607self.linux_source_mock.build_reconfig.return_value = True608self.linux_source_mock.build_kernel.return_value = True609self.linux_source_mock.run_kernel.return_value = all_passed_log610611def test_config_passes_args_pass(self):612kunit.main(['config', '--build_dir=.kunit'])613self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)614self.assertEqual(self.linux_source_mock.run_kernel.call_count, 0)615616def test_build_passes_args_pass(self):617kunit.main(['build'])618self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)619self.linux_source_mock.build_kernel.assert_called_once_with(kunit.get_default_jobs(), '.kunit', None)620self.assertEqual(self.linux_source_mock.run_kernel.call_count, 0)621622def test_exec_passes_args_pass(self):623kunit.main(['exec'])624self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 0)625self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)626self.linux_source_mock.run_kernel.assert_called_once_with(627args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)628self.print_mock.assert_any_call(StrContains('Testing complete.'))629630def test_run_passes_args_pass(self):631kunit.main(['run'])632self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)633self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)634self.linux_source_mock.run_kernel.assert_called_once_with(635args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)636self.print_mock.assert_any_call(StrContains('Testing complete.'))637638def test_exec_passes_args_fail(self):639self.linux_source_mock.run_kernel = mock.Mock(return_value=[])640with self.assertRaises(SystemExit) as e:641kunit.main(['exec'])642self.assertEqual(e.exception.code, 1)643644def test_run_passes_args_fail(self):645self.linux_source_mock.run_kernel = mock.Mock(return_value=[])646with self.assertRaises(SystemExit) as e:647kunit.main(['run'])648self.assertEqual(e.exception.code, 1)649self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)650self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)651self.print_mock.assert_any_call(StrContains('Could not find any KTAP output.'))652653def test_exec_no_tests(self):654self.linux_source_mock.run_kernel = mock.Mock(return_value=['TAP version 14', '1..0'])655with self.assertRaises(SystemExit) as e:656kunit.main(['run'])657self.assertEqual(e.exception.code, 1)658self.linux_source_mock.run_kernel.assert_called_once_with(659args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)660self.print_mock.assert_any_call(StrContains(' 0 tests run!'))661662def test_exec_raw_output(self):663self.linux_source_mock.run_kernel = mock.Mock(return_value=[])664kunit.main(['exec', '--raw_output'])665self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)666for call in self.print_mock.call_args_list:667self.assertNotEqual(call, mock.call(StrContains('Testing complete.')))668self.assertNotEqual(call, mock.call(StrContains(' 0 tests run!')))669670def test_run_raw_output(self):671self.linux_source_mock.run_kernel = mock.Mock(return_value=[])672kunit.main(['run', '--raw_output'])673self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)674self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)675for call in self.print_mock.call_args_list:676self.assertNotEqual(call, mock.call(StrContains('Testing complete.')))677self.assertNotEqual(call, mock.call(StrContains(' 0 tests run!')))678679def test_run_raw_output_kunit(self):680self.linux_source_mock.run_kernel = mock.Mock(return_value=[])681kunit.main(['run', '--raw_output=kunit'])682self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)683self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)684for call in self.print_mock.call_args_list:685self.assertNotEqual(call, mock.call(StrContains('Testing complete.')))686self.assertNotEqual(call, mock.call(StrContains(' 0 tests run')))687688def test_run_raw_output_invalid(self):689self.linux_source_mock.run_kernel = mock.Mock(return_value=[])690with self.assertRaises(SystemExit) as e:691kunit.main(['run', '--raw_output=invalid'])692self.assertNotEqual(e.exception.code, 0)693694def test_run_raw_output_does_not_take_positional_args(self):695# --raw_output is a string flag, but we don't want it to consume696# any positional arguments, only ones after an '='697self.linux_source_mock.run_kernel = mock.Mock(return_value=[])698kunit.main(['run', '--raw_output', 'filter_glob'])699self.linux_source_mock.run_kernel.assert_called_once_with(700args=None, build_dir='.kunit', filter_glob='filter_glob', filter='', filter_action=None, timeout=300)701702def test_exec_timeout(self):703timeout = 3453704kunit.main(['exec', '--timeout', str(timeout)])705self.linux_source_mock.run_kernel.assert_called_once_with(706args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=timeout)707self.print_mock.assert_any_call(StrContains('Testing complete.'))708709def test_run_timeout(self):710timeout = 3453711kunit.main(['run', '--timeout', str(timeout)])712self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)713self.linux_source_mock.run_kernel.assert_called_once_with(714args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=timeout)715self.print_mock.assert_any_call(StrContains('Testing complete.'))716717def test_run_builddir(self):718build_dir = '.kunit'719kunit.main(['run', '--build_dir=.kunit'])720self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)721self.linux_source_mock.run_kernel.assert_called_once_with(722args=None, build_dir=build_dir, filter_glob='', filter='', filter_action=None, timeout=300)723self.print_mock.assert_any_call(StrContains('Testing complete.'))724725def test_config_builddir(self):726build_dir = '.kunit'727kunit.main(['config', '--build_dir', build_dir])728self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)729730def test_build_builddir(self):731build_dir = '.kunit'732jobs = kunit.get_default_jobs()733kunit.main(['build', '--build_dir', build_dir])734self.linux_source_mock.build_kernel.assert_called_once_with(jobs, build_dir, None)735736def test_exec_builddir(self):737build_dir = '.kunit'738kunit.main(['exec', '--build_dir', build_dir])739self.linux_source_mock.run_kernel.assert_called_once_with(740args=None, build_dir=build_dir, filter_glob='', filter='', filter_action=None, timeout=300)741self.print_mock.assert_any_call(StrContains('Testing complete.'))742743def test_run_kunitconfig(self):744kunit.main(['run', '--kunitconfig=mykunitconfig'])745# Just verify that we parsed and initialized it correctly here.746self.mock_linux_init.assert_called_once_with('.kunit',747kunitconfig_paths=['mykunitconfig'],748kconfig_add=None,749arch='um',750cross_compile=None,751qemu_config_path=None,752extra_qemu_args=[])753754def test_config_kunitconfig(self):755kunit.main(['config', '--kunitconfig=mykunitconfig'])756# Just verify that we parsed and initialized it correctly here.757self.mock_linux_init.assert_called_once_with('.kunit',758kunitconfig_paths=['mykunitconfig'],759kconfig_add=None,760arch='um',761cross_compile=None,762qemu_config_path=None,763extra_qemu_args=[])764765def test_config_alltests(self):766kunit.main(['config', '--kunitconfig=mykunitconfig', '--alltests'])767# Just verify that we parsed and initialized it correctly here.768self.mock_linux_init.assert_called_once_with('.kunit',769kunitconfig_paths=[kunit_kernel.ALL_TESTS_CONFIG_PATH, 'mykunitconfig'],770kconfig_add=None,771arch='um',772cross_compile=None,773qemu_config_path=None,774extra_qemu_args=[])775776777@mock.patch.object(kunit_kernel, 'LinuxSourceTree')778def test_run_multiple_kunitconfig(self, mock_linux_init):779mock_linux_init.return_value = self.linux_source_mock780kunit.main(['run', '--kunitconfig=mykunitconfig', '--kunitconfig=other'])781# Just verify that we parsed and initialized it correctly here.782mock_linux_init.assert_called_once_with('.kunit',783kunitconfig_paths=['mykunitconfig', 'other'],784kconfig_add=None,785arch='um',786cross_compile=None,787qemu_config_path=None,788extra_qemu_args=[])789790def test_run_kconfig_add(self):791kunit.main(['run', '--kconfig_add=CONFIG_KASAN=y', '--kconfig_add=CONFIG_KCSAN=y'])792# Just verify that we parsed and initialized it correctly here.793self.mock_linux_init.assert_called_once_with('.kunit',794kunitconfig_paths=[],795kconfig_add=['CONFIG_KASAN=y', 'CONFIG_KCSAN=y'],796arch='um',797cross_compile=None,798qemu_config_path=None,799extra_qemu_args=[])800801def test_run_qemu_args(self):802kunit.main(['run', '--arch=x86_64', '--qemu_args', '-m 2048'])803# Just verify that we parsed and initialized it correctly here.804self.mock_linux_init.assert_called_once_with('.kunit',805kunitconfig_paths=[],806kconfig_add=None,807arch='x86_64',808cross_compile=None,809qemu_config_path=None,810extra_qemu_args=['-m', '2048'])811812def test_run_kernel_args(self):813kunit.main(['run', '--kernel_args=a=1', '--kernel_args=b=2'])814self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)815self.linux_source_mock.run_kernel.assert_called_once_with(816args=['a=1','b=2'], build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)817self.print_mock.assert_any_call(StrContains('Testing complete.'))818819def test_list_tests(self):820want = ['suite.test1', 'suite.test2', 'suite2.test1']821self.linux_source_mock.run_kernel.return_value = ['TAP version 14', 'init: random output'] + want822823got = kunit._list_tests(self.linux_source_mock,824kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'suite', False, False))825self.assertEqual(got, want)826# Should respect the user's filter glob when listing tests.827self.linux_source_mock.run_kernel.assert_called_once_with(828args=['kunit.action=list'], build_dir='.kunit', filter_glob='suite*', filter='', filter_action=None, timeout=300)829830@mock.patch.object(kunit, '_list_tests')831def test_run_isolated_by_suite(self, mock_tests):832mock_tests.return_value = ['suite.test1', 'suite.test2', 'suite2.test1']833kunit.main(['exec', '--run_isolated=suite', 'suite*.test*'])834835# Should respect the user's filter glob when listing tests.836mock_tests.assert_called_once_with(mock.ANY,837kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*.test*', '', None, None, 'suite', False, False))838self.linux_source_mock.run_kernel.assert_has_calls([839mock.call(args=None, build_dir='.kunit', filter_glob='suite.test*', filter='', filter_action=None, timeout=300),840mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test*', filter='', filter_action=None, timeout=300),841])842843@mock.patch.object(kunit, '_list_tests')844def test_run_isolated_by_test(self, mock_tests):845mock_tests.return_value = ['suite.test1', 'suite.test2', 'suite2.test1']846kunit.main(['exec', '--run_isolated=test', 'suite*'])847848# Should respect the user's filter glob when listing tests.849mock_tests.assert_called_once_with(mock.ANY,850kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'test', False, False))851self.linux_source_mock.run_kernel.assert_has_calls([852mock.call(args=None, build_dir='.kunit', filter_glob='suite.test1', filter='', filter_action=None, timeout=300),853mock.call(args=None, build_dir='.kunit', filter_glob='suite.test2', filter='', filter_action=None, timeout=300),854mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test1', filter='', filter_action=None, timeout=300),855])856857if __name__ == '__main__':858unittest.main()859860861