1c25ce589SFinn Behrens#!/usr/bin/env python3
26ebf5866SFelix Guo# SPDX-License-Identifier: GPL-2.0
36ebf5866SFelix Guo#
46ebf5866SFelix Guo# A collection of tests for tools/testing/kunit/kunit.py
56ebf5866SFelix Guo#
66ebf5866SFelix Guo# Copyright (C) 2019, Google LLC.
76ebf5866SFelix Guo# Author: Brendan Higgins <[email protected]>
86ebf5866SFelix Guo
96ebf5866SFelix Guoimport unittest
106ebf5866SFelix Guofrom unittest import mock
116ebf5866SFelix Guo
126ebf5866SFelix Guoimport tempfile, shutil # Handling test_tmpdir
136ebf5866SFelix Guo
14b29b14f1SDaniel Latypovimport itertools
1521a6d178SHeidi Fahimimport json
16142189f0SDaniel Latypovimport os
17243180f5SDaniel Latypovimport signal
187d7c48dfSDaniel Latypovimport subprocess
19142189f0SDaniel Latypovfrom typing import Iterable
206ebf5866SFelix Guo
216ebf5866SFelix Guoimport kunit_config
226ebf5866SFelix Guoimport kunit_parser
236ebf5866SFelix Guoimport kunit_kernel
2421a6d178SHeidi Fahimimport kunit_json
256ebf5866SFelix Guoimport kunit
26062a9dd9SDavid Gowfrom kunit_printer import stdout
276ebf5866SFelix Guo
286ebf5866SFelix Guotest_tmpdir = ''
29cd4a9bc8SDaniel Latypovabs_test_data_dir = ''
306ebf5866SFelix Guo
316ebf5866SFelix Guodef setUpModule():
32cd4a9bc8SDaniel Latypov	global test_tmpdir, abs_test_data_dir
336ebf5866SFelix Guo	test_tmpdir = tempfile.mkdtemp()
34cd4a9bc8SDaniel Latypov	abs_test_data_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'test_data'))
356ebf5866SFelix Guo
366ebf5866SFelix Guodef tearDownModule():
376ebf5866SFelix Guo	shutil.rmtree(test_tmpdir)
386ebf5866SFelix Guo
39cd4a9bc8SDaniel Latypovdef test_data_path(path):
40cd4a9bc8SDaniel Latypov	return os.path.join(abs_test_data_dir, path)
416ebf5866SFelix Guo
426ebf5866SFelix Guoclass KconfigTest(unittest.TestCase):
436ebf5866SFelix Guo
446ebf5866SFelix Guo	def test_is_subset_of(self):
456ebf5866SFelix Guo		kconfig0 = kunit_config.Kconfig()
466ebf5866SFelix Guo		self.assertTrue(kconfig0.is_subset_of(kconfig0))
476ebf5866SFelix Guo
486ebf5866SFelix Guo		kconfig1 = kunit_config.Kconfig()
498a7c6f85SDaniel Latypov		kconfig1.add_entry('TEST', 'y')
506ebf5866SFelix Guo		self.assertTrue(kconfig1.is_subset_of(kconfig1))
516ebf5866SFelix Guo		self.assertTrue(kconfig0.is_subset_of(kconfig1))
526ebf5866SFelix Guo		self.assertFalse(kconfig1.is_subset_of(kconfig0))
536ebf5866SFelix Guo
546ebf5866SFelix Guo	def test_read_from_file(self):
55cd4a9bc8SDaniel Latypov		kconfig_path = test_data_path('test_read_from_file.kconfig')
566ebf5866SFelix Guo
5798978490SDaniel Latypov		kconfig = kunit_config.parse_file(kconfig_path)
586ebf5866SFelix Guo
596ebf5866SFelix Guo		expected_kconfig = kunit_config.Kconfig()
608a7c6f85SDaniel Latypov		expected_kconfig.add_entry('UML', 'y')
618a7c6f85SDaniel Latypov		expected_kconfig.add_entry('MMU', 'y')
628a7c6f85SDaniel Latypov		expected_kconfig.add_entry('TEST', 'y')
638a7c6f85SDaniel Latypov		expected_kconfig.add_entry('EXAMPLE_TEST', 'y')
648a7c6f85SDaniel Latypov		expected_kconfig.add_entry('MK8', 'n')
656ebf5866SFelix Guo
668a7c6f85SDaniel Latypov		self.assertEqual(kconfig, expected_kconfig)
676ebf5866SFelix Guo
686ebf5866SFelix Guo	def test_write_to_file(self):
696ebf5866SFelix Guo		kconfig_path = os.path.join(test_tmpdir, '.config')
706ebf5866SFelix Guo
716ebf5866SFelix Guo		expected_kconfig = kunit_config.Kconfig()
728a7c6f85SDaniel Latypov		expected_kconfig.add_entry('UML', 'y')
738a7c6f85SDaniel Latypov		expected_kconfig.add_entry('MMU', 'y')
748a7c6f85SDaniel Latypov		expected_kconfig.add_entry('TEST', 'y')
758a7c6f85SDaniel Latypov		expected_kconfig.add_entry('EXAMPLE_TEST', 'y')
768a7c6f85SDaniel Latypov		expected_kconfig.add_entry('MK8', 'n')
776ebf5866SFelix Guo
786ebf5866SFelix Guo		expected_kconfig.write_to_file(kconfig_path)
796ebf5866SFelix Guo
8098978490SDaniel Latypov		actual_kconfig = kunit_config.parse_file(kconfig_path)
818a7c6f85SDaniel Latypov		self.assertEqual(actual_kconfig, expected_kconfig)
826ebf5866SFelix Guo
836ebf5866SFelix Guoclass KUnitParserTest(unittest.TestCase):
843ffdcf7eSDaniel Latypov	def setUp(self):
853ffdcf7eSDaniel Latypov		self.print_mock = mock.patch('kunit_printer.Printer.print').start()
863ffdcf7eSDaniel Latypov		self.addCleanup(mock.patch.stopall)
876ebf5866SFelix Guo
885937e0c0SDaniel Latypov	def noPrintCallContains(self, substr: str):
895937e0c0SDaniel Latypov		for call in self.print_mock.mock_calls:
905937e0c0SDaniel Latypov			self.assertNotIn(substr, call.args[0])
915937e0c0SDaniel Latypov
92b29b14f1SDaniel Latypov	def assertContains(self, needle: str, haystack: kunit_parser.LineStream):
93b29b14f1SDaniel Latypov		# Clone the iterator so we can print the contents on failure.
94b29b14f1SDaniel Latypov		copy, backup = itertools.tee(haystack)
95b29b14f1SDaniel Latypov		for line in copy:
966ebf5866SFelix Guo			if needle in line:
976ebf5866SFelix Guo				return
98b29b14f1SDaniel Latypov		raise AssertionError(f'"{needle}" not found in {list(backup)}!')
996ebf5866SFelix Guo
1006ebf5866SFelix Guo	def test_output_isolated_correctly(self):
101cd4a9bc8SDaniel Latypov		log_path = test_data_path('test_output_isolated_correctly.log')
102a3ece079SDaniel Latypov		with open(log_path) as file:
103b29b14f1SDaniel Latypov			result = kunit_parser.extract_tap_lines(file.readlines())
104060352e1SDaniel Latypov		self.assertContains('TAP version 14', result)
1056ebf5866SFelix Guo		self.assertContains('# Subtest: example', result)
1066ebf5866SFelix Guo		self.assertContains('1..2', result)
1076ebf5866SFelix Guo		self.assertContains('ok 1 - example_simple_test', result)
1086ebf5866SFelix Guo		self.assertContains('ok 2 - example_mock_test', result)
1096ebf5866SFelix Guo		self.assertContains('ok 1 - example', result)
1106ebf5866SFelix Guo
111afc63da6SHeidi Fahim	def test_output_with_prefix_isolated_correctly(self):
112cd4a9bc8SDaniel Latypov		log_path = test_data_path('test_pound_sign.log')
113afc63da6SHeidi Fahim		with open(log_path) as file:
114b29b14f1SDaniel Latypov			result = kunit_parser.extract_tap_lines(file.readlines())
115060352e1SDaniel Latypov		self.assertContains('TAP version 14', result)
116afc63da6SHeidi Fahim		self.assertContains('# Subtest: kunit-resource-test', result)
117afc63da6SHeidi Fahim		self.assertContains('1..5', result)
118afc63da6SHeidi Fahim		self.assertContains('ok 1 - kunit_resource_test_init_resources', result)
119afc63da6SHeidi Fahim		self.assertContains('ok 2 - kunit_resource_test_alloc_resource', result)
120afc63da6SHeidi Fahim		self.assertContains('ok 3 - kunit_resource_test_destroy_resource', result)
121afc63da6SHeidi Fahim		self.assertContains('foo bar 	#', result)
122afc63da6SHeidi Fahim		self.assertContains('ok 4 - kunit_resource_test_cleanup_resources', result)
123afc63da6SHeidi Fahim		self.assertContains('ok 5 - kunit_resource_test_proper_free_ordering', result)
124afc63da6SHeidi Fahim		self.assertContains('ok 1 - kunit-resource-test', result)
125afc63da6SHeidi Fahim		self.assertContains('foo bar 	# non-kunit output', result)
126afc63da6SHeidi Fahim		self.assertContains('# Subtest: kunit-try-catch-test', result)
127afc63da6SHeidi Fahim		self.assertContains('1..2', result)
128afc63da6SHeidi Fahim		self.assertContains('ok 1 - kunit_test_try_catch_successful_try_no_catch',
129afc63da6SHeidi Fahim				    result)
130afc63da6SHeidi Fahim		self.assertContains('ok 2 - kunit_test_try_catch_unsuccessful_try_does_catch',
131afc63da6SHeidi Fahim				    result)
132afc63da6SHeidi Fahim		self.assertContains('ok 2 - kunit-try-catch-test', result)
133afc63da6SHeidi Fahim		self.assertContains('# Subtest: string-stream-test', result)
134afc63da6SHeidi Fahim		self.assertContains('1..3', result)
135afc63da6SHeidi Fahim		self.assertContains('ok 1 - string_stream_test_empty_on_creation', result)
136afc63da6SHeidi Fahim		self.assertContains('ok 2 - string_stream_test_not_empty_after_add', result)
137afc63da6SHeidi Fahim		self.assertContains('ok 3 - string_stream_test_get_string', result)
138afc63da6SHeidi Fahim		self.assertContains('ok 3 - string-stream-test', result)
139afc63da6SHeidi Fahim
1406ebf5866SFelix Guo	def test_parse_successful_test_log(self):
141cd4a9bc8SDaniel Latypov		all_passed_log = test_data_path('test_is_test_passed-all_passed.log')
142a3ece079SDaniel Latypov		with open(all_passed_log) as file:
143062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
14405d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
14505d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 0)
1466ebf5866SFelix Guo
147d65d07cbSRae Moar	def test_parse_successful_nested_tests_log(self):
148d65d07cbSRae Moar		all_passed_log = test_data_path('test_is_test_passed-all_passed_nested.log')
149d65d07cbSRae Moar		with open(all_passed_log) as file:
150062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
15105d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
15205d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 0)
153d65d07cbSRae Moar
154d65d07cbSRae Moar	def test_kselftest_nested(self):
155d65d07cbSRae Moar		kselftest_log = test_data_path('test_is_test_passed-kselftest.log')
156d65d07cbSRae Moar		with open(kselftest_log) as file:
157062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
15805d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
15905d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 0)
160d65d07cbSRae Moar
1616ebf5866SFelix Guo	def test_parse_failed_test_log(self):
162cd4a9bc8SDaniel Latypov		failed_log = test_data_path('test_is_test_passed-failure.log')
163a3ece079SDaniel Latypov		with open(failed_log) as file:
164062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
16505d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status)
16605d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 0)
1676ebf5866SFelix Guo
168ed01ad3aSRae Moar	def test_no_header(self):
169ed01ad3aSRae Moar		empty_log = test_data_path('test_is_test_passed-no_tests_run_no_header.log')
170ed01ad3aSRae Moar		with open(empty_log) as file:
171ed01ad3aSRae Moar			result = kunit_parser.parse_run_tests(
172062a9dd9SDavid Gow				kunit_parser.extract_tap_lines(file.readlines()), stdout)
173e0cc8c05SDaniel Latypov		self.assertEqual(0, len(result.subtests))
17405d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.FAILURE_TO_PARSE_TESTS, result.status)
17505d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 1)
176ed01ad3aSRae Moar
177d65d07cbSRae Moar	def test_missing_test_plan(self):
178d65d07cbSRae Moar		missing_plan_log = test_data_path('test_is_test_passed-'
179d65d07cbSRae Moar			'missing_plan.log')
180d65d07cbSRae Moar		with open(missing_plan_log) as file:
181d65d07cbSRae Moar			result = kunit_parser.parse_run_tests(
182d65d07cbSRae Moar				kunit_parser.extract_tap_lines(
183062a9dd9SDavid Gow				file.readlines()), stdout)
184c68077b1SDavid Gow		# A missing test plan is not an error.
185f473dd94SDaniel Latypov		self.assertEqual(result.counts, kunit_parser.TestCounts(passed=10, errors=0))
18605d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
187d65d07cbSRae Moar
1886ebf5866SFelix Guo	def test_no_tests(self):
189d65d07cbSRae Moar		header_log = test_data_path('test_is_test_passed-no_tests_run_with_header.log')
190d65d07cbSRae Moar		with open(header_log) as file:
1916ebf5866SFelix Guo			result = kunit_parser.parse_run_tests(
192062a9dd9SDavid Gow				kunit_parser.extract_tap_lines(file.readlines()), stdout)
193e0cc8c05SDaniel Latypov		self.assertEqual(0, len(result.subtests))
19405d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.NO_TESTS, result.status)
19505d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 1)
1966ebf5866SFelix Guo
197e56e4828SDavid Gow	def test_no_tests_no_plan(self):
198e56e4828SDavid Gow		no_plan_log = test_data_path('test_is_test_passed-no_tests_no_plan.log')
199e56e4828SDavid Gow		with open(no_plan_log) as file:
200e56e4828SDavid Gow			result = kunit_parser.parse_run_tests(
201062a9dd9SDavid Gow				kunit_parser.extract_tap_lines(file.readlines()), stdout)
202e0cc8c05SDaniel Latypov		self.assertEqual(0, len(result.subtests[0].subtests[0].subtests))
203e56e4828SDavid Gow		self.assertEqual(
204e56e4828SDavid Gow			kunit_parser.TestStatus.NO_TESTS,
205e0cc8c05SDaniel Latypov			result.subtests[0].subtests[0].status)
20605d9d2c3SDaniel Latypov		self.assertEqual(result.counts, kunit_parser.TestCounts(passed=1, errors=1))
207e56e4828SDavid Gow
208e56e4828SDavid Gow
209e173b8b8SUriel Guajardo	def test_no_kunit_output(self):
210cd4a9bc8SDaniel Latypov		crash_log = test_data_path('test_insufficient_memory.log')
211e756dbebSDaniel Latypov		print_mock = mock.patch('kunit_printer.Printer.print').start()
212a3ece079SDaniel Latypov		with open(crash_log) as file:
213e173b8b8SUriel Guajardo			result = kunit_parser.parse_run_tests(
214062a9dd9SDavid Gow				kunit_parser.extract_tap_lines(file.readlines()), stdout)
2150a7d5c30SDaniel Latypov		print_mock.assert_any_call(StrContains('Could not find any KTAP output.'))
216e173b8b8SUriel Guajardo		print_mock.stop()
217e0cc8c05SDaniel Latypov		self.assertEqual(0, len(result.subtests))
21805d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 1)
219e173b8b8SUriel Guajardo
2205acaf603SDavid Gow	def test_skipped_test(self):
2215acaf603SDavid Gow		skipped_log = test_data_path('test_skip_tests.log')
222a54ea2e0SDaniel Latypov		with open(skipped_log) as file:
223062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
2245acaf603SDavid Gow
2255acaf603SDavid Gow		# A skipped test does not fail the whole suite.
22605d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
22705d9d2c3SDaniel Latypov		self.assertEqual(result.counts, kunit_parser.TestCounts(passed=4, skipped=1))
2285acaf603SDavid Gow
2295acaf603SDavid Gow	def test_skipped_all_tests(self):
2305acaf603SDavid Gow		skipped_log = test_data_path('test_skip_all_tests.log')
231a54ea2e0SDaniel Latypov		with open(skipped_log) as file:
232062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
2335acaf603SDavid Gow
23405d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SKIPPED, result.status)
23505d9d2c3SDaniel Latypov		self.assertEqual(result.counts, kunit_parser.TestCounts(skipped=5))
2365acaf603SDavid Gow
237d65d07cbSRae Moar	def test_ignores_hyphen(self):
238d65d07cbSRae Moar		hyphen_log = test_data_path('test_strip_hyphen.log')
2390453f984SDaniel Latypov		with open(hyphen_log) as file:
240062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
241d65d07cbSRae Moar
242d65d07cbSRae Moar		# A skipped test does not fail the whole suite.
24305d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
244d65d07cbSRae Moar		self.assertEqual(
245d65d07cbSRae Moar			"sysctl_test",
246e0cc8c05SDaniel Latypov			result.subtests[0].name)
247d65d07cbSRae Moar		self.assertEqual(
248d65d07cbSRae Moar			"example",
249e0cc8c05SDaniel Latypov			result.subtests[1].name)
2505acaf603SDavid Gow
251afc63da6SHeidi Fahim	def test_ignores_prefix_printk_time(self):
252cd4a9bc8SDaniel Latypov		prefix_log = test_data_path('test_config_printk_time.log')
253afc63da6SHeidi Fahim		with open(prefix_log) as file:
254062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
25505d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
256e0cc8c05SDaniel Latypov		self.assertEqual('kunit-resource-test', result.subtests[0].name)
25705d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 0)
258afc63da6SHeidi Fahim
259afc63da6SHeidi Fahim	def test_ignores_multiple_prefixes(self):
260cd4a9bc8SDaniel Latypov		prefix_log = test_data_path('test_multiple_prefixes.log')
261afc63da6SHeidi Fahim		with open(prefix_log) as file:
262062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
26305d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
264e0cc8c05SDaniel Latypov		self.assertEqual('kunit-resource-test', result.subtests[0].name)
26505d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 0)
266afc63da6SHeidi Fahim
267afc63da6SHeidi Fahim	def test_prefix_mixed_kernel_output(self):
268cd4a9bc8SDaniel Latypov		mixed_prefix_log = test_data_path('test_interrupted_tap_output.log')
269afc63da6SHeidi Fahim		with open(mixed_prefix_log) as file:
270062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
27105d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
272e0cc8c05SDaniel Latypov		self.assertEqual('kunit-resource-test', result.subtests[0].name)
27305d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 0)
274afc63da6SHeidi Fahim
275afc63da6SHeidi Fahim	def test_prefix_poundsign(self):
276cd4a9bc8SDaniel Latypov		pound_log = test_data_path('test_pound_sign.log')
277afc63da6SHeidi Fahim		with open(pound_log) as file:
278062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
27905d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
280e0cc8c05SDaniel Latypov		self.assertEqual('kunit-resource-test', result.subtests[0].name)
28105d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 0)
282afc63da6SHeidi Fahim
283afc63da6SHeidi Fahim	def test_kernel_panic_end(self):
284cd4a9bc8SDaniel Latypov		panic_log = test_data_path('test_kernel_panic_interrupt.log')
285afc63da6SHeidi Fahim		with open(panic_log) as file:
286062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
28705d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.TEST_CRASHED, result.status)
288e0cc8c05SDaniel Latypov		self.assertEqual('kunit-resource-test', result.subtests[0].name)
28905d9d2c3SDaniel Latypov		self.assertGreaterEqual(result.counts.errors, 1)
290afc63da6SHeidi Fahim
291afc63da6SHeidi Fahim	def test_pound_no_prefix(self):
292cd4a9bc8SDaniel Latypov		pound_log = test_data_path('test_pound_no_prefix.log')
293afc63da6SHeidi Fahim		with open(pound_log) as file:
294062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
29505d9d2c3SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
296e0cc8c05SDaniel Latypov		self.assertEqual('kunit-resource-test', result.subtests[0].name)
29705d9d2c3SDaniel Latypov		self.assertEqual(result.counts.errors, 0)
298afc63da6SHeidi Fahim
299f19dd011SDaniel Latypov	def test_summarize_failures(self):
300f19dd011SDaniel Latypov		output = """
301f19dd011SDaniel Latypov		KTAP version 1
302f19dd011SDaniel Latypov		1..2
303f19dd011SDaniel Latypov			# Subtest: all_failed_suite
304f19dd011SDaniel Latypov			1..2
305f19dd011SDaniel Latypov			not ok 1 - test1
306f19dd011SDaniel Latypov			not ok 2 - test2
307f19dd011SDaniel Latypov		not ok 1 - all_failed_suite
308f19dd011SDaniel Latypov			# Subtest: some_failed_suite
309f19dd011SDaniel Latypov			1..2
310f19dd011SDaniel Latypov			ok 1 - test1
311f19dd011SDaniel Latypov			not ok 2 - test2
312f19dd011SDaniel Latypov		not ok 1 - some_failed_suite
313f19dd011SDaniel Latypov		"""
314062a9dd9SDavid Gow		result = kunit_parser.parse_run_tests(output.splitlines(), stdout)
315f19dd011SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status)
316f19dd011SDaniel Latypov
317f19dd011SDaniel Latypov		self.assertEqual(kunit_parser._summarize_failed_tests(result),
318f19dd011SDaniel Latypov			'Failures: all_failed_suite, some_failed_suite.test2')
319f19dd011SDaniel Latypov
320434498a6SRae Moar	def test_ktap_format(self):
321434498a6SRae Moar		ktap_log = test_data_path('test_parse_ktap_output.log')
322434498a6SRae Moar		with open(ktap_log) as file:
323062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
324434498a6SRae Moar		self.assertEqual(result.counts, kunit_parser.TestCounts(passed=3))
325434498a6SRae Moar		self.assertEqual('suite', result.subtests[0].name)
326434498a6SRae Moar		self.assertEqual('case_1', result.subtests[0].subtests[0].name)
327434498a6SRae Moar		self.assertEqual('case_2', result.subtests[0].subtests[1].name)
328434498a6SRae Moar
329434498a6SRae Moar	def test_parse_subtest_header(self):
330434498a6SRae Moar		ktap_log = test_data_path('test_parse_subtest_header.log')
331434498a6SRae Moar		with open(ktap_log) as file:
332062a9dd9SDavid Gow			kunit_parser.parse_run_tests(file.readlines(), stdout)
333434498a6SRae Moar		self.print_mock.assert_any_call(StrContains('suite (1 subtest)'))
334f19dd011SDaniel Latypov
3356eb0ea28SRae Moar	def test_parse_attributes(self):
3366eb0ea28SRae Moar		ktap_log = test_data_path('test_parse_attributes.log')
3376eb0ea28SRae Moar		with open(ktap_log) as file:
338062a9dd9SDavid Gow			result = kunit_parser.parse_run_tests(file.readlines(), stdout)
3396eb0ea28SRae Moar
3406eb0ea28SRae Moar		# Test should pass with no errors
3416eb0ea28SRae Moar		self.assertEqual(result.counts, kunit_parser.TestCounts(passed=1, errors=0))
3426eb0ea28SRae Moar		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
3436eb0ea28SRae Moar
3446eb0ea28SRae Moar		# Ensure suite header is parsed correctly
3456eb0ea28SRae Moar		self.print_mock.assert_any_call(StrContains('suite (1 subtest)'))
3466eb0ea28SRae Moar
3476eb0ea28SRae Moar		# Ensure attributes in correct test log
3486eb0ea28SRae Moar		self.assertContains('# module: example', result.subtests[0].log)
3496eb0ea28SRae Moar		self.assertContains('# test.speed: slow', result.subtests[0].subtests[0].log)
3506eb0ea28SRae Moar
3515937e0c0SDaniel Latypov	def test_show_test_output_on_failure(self):
3525937e0c0SDaniel Latypov		output = """
3535937e0c0SDaniel Latypov		KTAP version 1
3545937e0c0SDaniel Latypov		1..1
3555937e0c0SDaniel Latypov		  Test output.
356c2bb92bcSDaniel Latypov		    Indented more.
3575937e0c0SDaniel Latypov		not ok 1 test1
3585937e0c0SDaniel Latypov		"""
359062a9dd9SDavid Gow		result = kunit_parser.parse_run_tests(output.splitlines(), stdout)
3605937e0c0SDaniel Latypov		self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status)
3615937e0c0SDaniel Latypov
3625937e0c0SDaniel Latypov		self.print_mock.assert_any_call(StrContains('Test output.'))
363c2bb92bcSDaniel Latypov		self.print_mock.assert_any_call(StrContains('  Indented more.'))
3645937e0c0SDaniel Latypov		self.noPrintCallContains('not ok 1 test1')
3655937e0c0SDaniel Latypov
3662e0cf2b3SRae Moar	def test_parse_late_test_plan(self):
3672e0cf2b3SRae Moar		output = """
3682e0cf2b3SRae Moar		TAP version 13
3692e0cf2b3SRae Moar		ok 4 test4
3702e0cf2b3SRae Moar		1..4
3712e0cf2b3SRae Moar		"""
3722e0cf2b3SRae Moar		result = kunit_parser.parse_run_tests(output.splitlines(), stdout)
3732e0cf2b3SRae Moar		# Missing test results after test plan should alert a suspected test crash.
374*14e594a1SRae Moar		self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
375*14e594a1SRae Moar		self.assertEqual(result.counts, kunit_parser.TestCounts(passed=1, errors=2))
3762e0cf2b3SRae Moar
377142189f0SDaniel Latypovdef line_stream_from_strs(strs: Iterable[str]) -> kunit_parser.LineStream:
378142189f0SDaniel Latypov	return kunit_parser.LineStream(enumerate(strs, start=1))
379142189f0SDaniel Latypov
380142189f0SDaniel Latypovclass LineStreamTest(unittest.TestCase):
381142189f0SDaniel Latypov
382142189f0SDaniel Latypov	def test_basic(self):
383142189f0SDaniel Latypov		stream = line_stream_from_strs(['hello', 'world'])
384142189f0SDaniel Latypov
385142189f0SDaniel Latypov		self.assertTrue(stream, msg='Should be more input')
386142189f0SDaniel Latypov		self.assertEqual(stream.line_number(), 1)
387142189f0SDaniel Latypov		self.assertEqual(stream.peek(), 'hello')
388142189f0SDaniel Latypov		self.assertEqual(stream.pop(), 'hello')
389142189f0SDaniel Latypov
390142189f0SDaniel Latypov		self.assertTrue(stream, msg='Should be more input')
391142189f0SDaniel Latypov		self.assertEqual(stream.line_number(), 2)
392142189f0SDaniel Latypov		self.assertEqual(stream.peek(), 'world')
393142189f0SDaniel Latypov		self.assertEqual(stream.pop(), 'world')
394142189f0SDaniel Latypov
395142189f0SDaniel Latypov		self.assertFalse(stream, msg='Should be no more input')
396142189f0SDaniel Latypov		with self.assertRaisesRegex(ValueError, 'LineStream: going past EOF'):
397142189f0SDaniel Latypov			stream.pop()
398142189f0SDaniel Latypov
399142189f0SDaniel Latypov	def test_is_lazy(self):
400142189f0SDaniel Latypov		called_times = 0
401142189f0SDaniel Latypov		def generator():
402142189f0SDaniel Latypov			nonlocal called_times
4030453f984SDaniel Latypov			for _ in range(1,5):
404142189f0SDaniel Latypov				called_times += 1
405142189f0SDaniel Latypov				yield called_times, str(called_times)
406142189f0SDaniel Latypov
407142189f0SDaniel Latypov		stream = kunit_parser.LineStream(generator())
408142189f0SDaniel Latypov		self.assertEqual(called_times, 0)
409142189f0SDaniel Latypov
410142189f0SDaniel Latypov		self.assertEqual(stream.pop(), '1')
411142189f0SDaniel Latypov		self.assertEqual(called_times, 1)
412142189f0SDaniel Latypov
413142189f0SDaniel Latypov		self.assertEqual(stream.pop(), '2')
414142189f0SDaniel Latypov		self.assertEqual(called_times, 2)
415142189f0SDaniel Latypov
416243180f5SDaniel Latypovclass LinuxSourceTreeTest(unittest.TestCase):
417243180f5SDaniel Latypov
418243180f5SDaniel Latypov	def setUp(self):
419243180f5SDaniel Latypov		mock.patch.object(signal, 'signal').start()
420243180f5SDaniel Latypov		self.addCleanup(mock.patch.stopall)
421243180f5SDaniel Latypov
422243180f5SDaniel Latypov	def test_invalid_kunitconfig(self):
423243180f5SDaniel Latypov		with self.assertRaisesRegex(kunit_kernel.ConfigError, 'nonexistent.* does not exist'):
42453b46621SDaniel Latypov			kunit_kernel.LinuxSourceTree('', kunitconfig_paths=['/nonexistent_file'])
425243180f5SDaniel Latypov
426243180f5SDaniel Latypov	def test_valid_kunitconfig(self):
427243180f5SDaniel Latypov		with tempfile.NamedTemporaryFile('wt') as kunitconfig:
42853b46621SDaniel Latypov			kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[kunitconfig.name])
429243180f5SDaniel Latypov
4309854781dSDaniel Latypov	def test_dir_kunitconfig(self):
4319854781dSDaniel Latypov		with tempfile.TemporaryDirectory('') as dir:
432a54ea2e0SDaniel Latypov			with open(os.path.join(dir, '.kunitconfig'), 'w'):
4339854781dSDaniel Latypov				pass
43453b46621SDaniel Latypov			kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[dir])
43553b46621SDaniel Latypov
43653b46621SDaniel Latypov	def test_multiple_kunitconfig(self):
43753b46621SDaniel Latypov		want_kconfig = kunit_config.Kconfig()
43853b46621SDaniel Latypov		want_kconfig.add_entry('KUNIT', 'y')
43953b46621SDaniel Latypov		want_kconfig.add_entry('KUNIT_TEST', 'm')
44053b46621SDaniel Latypov
44153b46621SDaniel Latypov		with tempfile.TemporaryDirectory('') as dir:
44253b46621SDaniel Latypov			other = os.path.join(dir, 'otherkunitconfig')
44353b46621SDaniel Latypov			with open(os.path.join(dir, '.kunitconfig'), 'w') as f:
44453b46621SDaniel Latypov				f.write('CONFIG_KUNIT=y')
44553b46621SDaniel Latypov			with open(other, 'w') as f:
44653b46621SDaniel Latypov				f.write('CONFIG_KUNIT_TEST=m')
44753b46621SDaniel Latypov				pass
44853b46621SDaniel Latypov
44953b46621SDaniel Latypov			tree = kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[dir, other])
45053b46621SDaniel Latypov			self.assertTrue(want_kconfig.is_subset_of(tree._kconfig), msg=tree._kconfig)
45153b46621SDaniel Latypov
45253b46621SDaniel Latypov
45353b46621SDaniel Latypov	def test_multiple_kunitconfig_invalid(self):
45453b46621SDaniel Latypov		with tempfile.TemporaryDirectory('') as dir:
45553b46621SDaniel Latypov			other = os.path.join(dir, 'otherkunitconfig')
45653b46621SDaniel Latypov			with open(os.path.join(dir, '.kunitconfig'), 'w') as f:
45753b46621SDaniel Latypov				f.write('CONFIG_KUNIT=y')
45853b46621SDaniel Latypov			with open(other, 'w') as f:
45953b46621SDaniel Latypov				f.write('CONFIG_KUNIT=m')
46053b46621SDaniel Latypov
46153b46621SDaniel Latypov			with self.assertRaisesRegex(kunit_kernel.ConfigError, '(?s)Multiple values.*CONFIG_KUNIT'):
46253b46621SDaniel Latypov				kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[dir, other])
46353b46621SDaniel Latypov
4649854781dSDaniel Latypov
4659f57cc76SDaniel Latypov	def test_kconfig_add(self):
4668a7c6f85SDaniel Latypov		want_kconfig = kunit_config.Kconfig()
4678a7c6f85SDaniel Latypov		want_kconfig.add_entry('NOT_REAL', 'y')
4688a7c6f85SDaniel Latypov
4699f57cc76SDaniel Latypov		tree = kunit_kernel.LinuxSourceTree('', kconfig_add=['CONFIG_NOT_REAL=y'])
4708a7c6f85SDaniel Latypov		self.assertTrue(want_kconfig.is_subset_of(tree._kconfig), msg=tree._kconfig)
4719f57cc76SDaniel Latypov
472fe678fedSDaniel Latypov	def test_invalid_arch(self):
473fe678fedSDaniel Latypov		with self.assertRaisesRegex(kunit_kernel.ConfigError, 'not a valid arch, options are.*x86_64'):
474fe678fedSDaniel Latypov			kunit_kernel.LinuxSourceTree('', arch='invalid')
475fe678fedSDaniel Latypov
4767d7c48dfSDaniel Latypov	def test_run_kernel_hits_exception(self):
4777d7c48dfSDaniel Latypov		def fake_start(unused_args, unused_build_dir):
4787d7c48dfSDaniel Latypov			return subprocess.Popen(['echo "hi\nbye"'], shell=True, text=True, stdout=subprocess.PIPE)
4797d7c48dfSDaniel Latypov
4807d7c48dfSDaniel Latypov		with tempfile.TemporaryDirectory('') as build_dir:
48176f0d6f5SDaniel Latypov			tree = kunit_kernel.LinuxSourceTree(build_dir)
4827d7c48dfSDaniel Latypov			mock.patch.object(tree._ops, 'start', side_effect=fake_start).start()
4837d7c48dfSDaniel Latypov
4847d7c48dfSDaniel Latypov			with self.assertRaises(ValueError):
4857d7c48dfSDaniel Latypov				for line in tree.run_kernel(build_dir=build_dir):
4867d7c48dfSDaniel Latypov					self.assertEqual(line, 'hi\n')
4877d7c48dfSDaniel Latypov					raise ValueError('uh oh, did not read all output')
4887d7c48dfSDaniel Latypov
4897d7c48dfSDaniel Latypov			with open(kunit_kernel.get_outfile_path(build_dir), 'rt') as outfile:
4907d7c48dfSDaniel Latypov				self.assertEqual(outfile.read(), 'hi\nbye\n', msg='Missing some output')
4917d7c48dfSDaniel Latypov
4924c2911f1SDaniel Latypov	def test_build_reconfig_no_config(self):
4934c2911f1SDaniel Latypov		with tempfile.TemporaryDirectory('') as build_dir:
4944c2911f1SDaniel Latypov			with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f:
4954c2911f1SDaniel Latypov				f.write('CONFIG_KUNIT=y')
4964c2911f1SDaniel Latypov
4974c2911f1SDaniel Latypov			tree = kunit_kernel.LinuxSourceTree(build_dir)
4986fc3a863SDavid Gow			# Stub out the source tree operations, so we don't have
4996fc3a863SDavid Gow			# the defaults for any given architecture get in the
5006fc3a863SDavid Gow			# way.
5016fc3a863SDavid Gow			tree._ops = kunit_kernel.LinuxSourceTreeOperations('none', None)
5024c2911f1SDaniel Latypov			mock_build_config = mock.patch.object(tree, 'build_config').start()
5034c2911f1SDaniel Latypov
5044c2911f1SDaniel Latypov			# Should generate the .config
5054c2911f1SDaniel Latypov			self.assertTrue(tree.build_reconfig(build_dir, make_options=[]))
5064c2911f1SDaniel Latypov			mock_build_config.assert_called_once_with(build_dir, [])
5074c2911f1SDaniel Latypov
5084c2911f1SDaniel Latypov	def test_build_reconfig_existing_config(self):
5094c2911f1SDaniel Latypov		with tempfile.TemporaryDirectory('') as build_dir:
5104c2911f1SDaniel Latypov			# Existing .config is a superset, should not touch it
5114c2911f1SDaniel Latypov			with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f:
5124c2911f1SDaniel Latypov				f.write('CONFIG_KUNIT=y')
5134c2911f1SDaniel Latypov			with open(kunit_kernel.get_old_kunitconfig_path(build_dir), 'w') as f:
5144c2911f1SDaniel Latypov				f.write('CONFIG_KUNIT=y')
5154c2911f1SDaniel Latypov			with open(kunit_kernel.get_kconfig_path(build_dir), 'w') as f:
5164c2911f1SDaniel Latypov				f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')
5174c2911f1SDaniel Latypov
5184c2911f1SDaniel Latypov			tree = kunit_kernel.LinuxSourceTree(build_dir)
5196fc3a863SDavid Gow			# Stub out the source tree operations, so we don't have
5206fc3a863SDavid Gow			# the defaults for any given architecture get in the
5216fc3a863SDavid Gow			# way.
5226fc3a863SDavid Gow			tree._ops = kunit_kernel.LinuxSourceTreeOperations('none', None)
5234c2911f1SDaniel Latypov			mock_build_config = mock.patch.object(tree, 'build_config').start()
5244c2911f1SDaniel Latypov
5254c2911f1SDaniel Latypov			self.assertTrue(tree.build_reconfig(build_dir, make_options=[]))
5264c2911f1SDaniel Latypov			self.assertEqual(mock_build_config.call_count, 0)
5274c2911f1SDaniel Latypov
5284c2911f1SDaniel Latypov	def test_build_reconfig_remove_option(self):
5294c2911f1SDaniel Latypov		with tempfile.TemporaryDirectory('') as build_dir:
5304c2911f1SDaniel Latypov			# We removed CONFIG_KUNIT_TEST=y from our .kunitconfig...
5314c2911f1SDaniel Latypov			with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f:
5324c2911f1SDaniel Latypov				f.write('CONFIG_KUNIT=y')
5334c2911f1SDaniel Latypov			with open(kunit_kernel.get_old_kunitconfig_path(build_dir), 'w') as f:
5344c2911f1SDaniel Latypov				f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')
5354c2911f1SDaniel Latypov			with open(kunit_kernel.get_kconfig_path(build_dir), 'w') as f:
5364c2911f1SDaniel Latypov				f.write('CONFIG_KUNIT=y\nCONFIG_KUNIT_TEST=y')
5374c2911f1SDaniel Latypov
5384c2911f1SDaniel Latypov			tree = kunit_kernel.LinuxSourceTree(build_dir)
5396fc3a863SDavid Gow			# Stub out the source tree operations, so we don't have
5406fc3a863SDavid Gow			# the defaults for any given architecture get in the
5416fc3a863SDavid Gow			# way.
5426fc3a863SDavid Gow			tree._ops = kunit_kernel.LinuxSourceTreeOperations('none', None)
5434c2911f1SDaniel Latypov			mock_build_config = mock.patch.object(tree, 'build_config').start()
5444c2911f1SDaniel Latypov
5454c2911f1SDaniel Latypov			# ... so we should trigger a call to build_config()
5464c2911f1SDaniel Latypov			self.assertTrue(tree.build_reconfig(build_dir, make_options=[]))
5474c2911f1SDaniel Latypov			mock_build_config.assert_called_once_with(build_dir, [])
5484c2911f1SDaniel Latypov
549243180f5SDaniel Latypov	# TODO: add more test cases.
550243180f5SDaniel Latypov
551243180f5SDaniel Latypov
55221a6d178SHeidi Fahimclass KUnitJsonTest(unittest.TestCase):
5533ffdcf7eSDaniel Latypov	def setUp(self):
5543ffdcf7eSDaniel Latypov		self.print_mock = mock.patch('kunit_printer.Printer.print').start()
5553ffdcf7eSDaniel Latypov		self.addCleanup(mock.patch.stopall)
55621a6d178SHeidi Fahim
55721a6d178SHeidi Fahim	def _json_for(self, log_file):
558cd4a9bc8SDaniel Latypov		with open(test_data_path(log_file)) as file:
559062a9dd9SDavid Gow			test_result = kunit_parser.parse_run_tests(file, stdout)
56021a6d178SHeidi Fahim			json_obj = kunit_json.get_json_result(
561e0cc8c05SDaniel Latypov				test=test_result,
562ee96d25fSDaniel Latypov				metadata=kunit_json.Metadata())
56321a6d178SHeidi Fahim		return json.loads(json_obj)
56421a6d178SHeidi Fahim
56521a6d178SHeidi Fahim	def test_failed_test_json(self):
566cd4a9bc8SDaniel Latypov		result = self._json_for('test_is_test_passed-failure.log')
56721a6d178SHeidi Fahim		self.assertEqual(
56821a6d178SHeidi Fahim			{'name': 'example_simple_test', 'status': 'FAIL'},
56921a6d178SHeidi Fahim			result["sub_groups"][1]["test_cases"][0])
57021a6d178SHeidi Fahim
57121a6d178SHeidi Fahim	def test_crashed_test_json(self):
57233d4a933SDaniel Latypov		result = self._json_for('test_kernel_panic_interrupt.log')
57321a6d178SHeidi Fahim		self.assertEqual(
57433d4a933SDaniel Latypov			{'name': '', 'status': 'ERROR'},
57533d4a933SDaniel Latypov			result["sub_groups"][2]["test_cases"][1])
57621a6d178SHeidi Fahim
5779a6bb30aSDaniel Latypov	def test_skipped_test_json(self):
5789a6bb30aSDaniel Latypov		result = self._json_for('test_skip_tests.log')
5799a6bb30aSDaniel Latypov		self.assertEqual(
5809a6bb30aSDaniel Latypov			{'name': 'example_skip_test', 'status': 'SKIP'},
5819a6bb30aSDaniel Latypov			result["sub_groups"][1]["test_cases"][1])
5829a6bb30aSDaniel Latypov
58321a6d178SHeidi Fahim	def test_no_tests_json(self):
584ed01ad3aSRae Moar		result = self._json_for('test_is_test_passed-no_tests_run_with_header.log')
58521a6d178SHeidi Fahim		self.assertEqual(0, len(result['sub_groups']))
58621a6d178SHeidi Fahim
587d65d07cbSRae Moar	def test_nested_json(self):
588d65d07cbSRae Moar		result = self._json_for('test_is_test_passed-all_passed_nested.log')
589d65d07cbSRae Moar		self.assertEqual(
590d65d07cbSRae Moar			{'name': 'example_simple_test', 'status': 'PASS'},
591d65d07cbSRae Moar			result["sub_groups"][0]["sub_groups"][0]["test_cases"][0])
592d65d07cbSRae Moar
5936ebf5866SFelix Guoclass StrContains(str):
5946ebf5866SFelix Guo	def __eq__(self, other):
5956ebf5866SFelix Guo		return self in other
5966ebf5866SFelix Guo
5976ebf5866SFelix Guoclass KUnitMainTest(unittest.TestCase):
5986ebf5866SFelix Guo	def setUp(self):
599cd4a9bc8SDaniel Latypov		path = test_data_path('test_is_test_passed-all_passed.log')
600cfd607e4SDaniel Latypov		with open(path) as file:
6016ebf5866SFelix Guo			all_passed_log = file.readlines()
602cfd607e4SDaniel Latypov
603e756dbebSDaniel Latypov		self.print_mock = mock.patch('kunit_printer.Printer.print').start()
604cfd607e4SDaniel Latypov		self.addCleanup(mock.patch.stopall)
605cfd607e4SDaniel Latypov
6068a04930fSDaniel Latypov		self.mock_linux_init = mock.patch.object(kunit_kernel, 'LinuxSourceTree').start()
6078a04930fSDaniel Latypov		self.linux_source_mock = self.mock_linux_init.return_value
6088a04930fSDaniel Latypov		self.linux_source_mock.build_reconfig.return_value = True
6098a04930fSDaniel Latypov		self.linux_source_mock.build_kernel.return_value = True
6108a04930fSDaniel Latypov		self.linux_source_mock.run_kernel.return_value = all_passed_log
6116ebf5866SFelix Guo
61245ba7a89SDavid Gow	def test_config_passes_args_pass(self):
6138a04930fSDaniel Latypov		kunit.main(['config', '--build_dir=.kunit'])
6140b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
6150b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.run_kernel.call_count, 0)
61645ba7a89SDavid Gow
61745ba7a89SDavid Gow	def test_build_passes_args_pass(self):
6188a04930fSDaniel Latypov		kunit.main(['build'])
6191ee2ba89SDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
620980ac3adSDaniel Latypov		self.linux_source_mock.build_kernel.assert_called_once_with(kunit.get_default_jobs(), '.kunit', None)
6210b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.run_kernel.call_count, 0)
62245ba7a89SDavid Gow
62345ba7a89SDavid Gow	def test_exec_passes_args_pass(self):
6248a04930fSDaniel Latypov		kunit.main(['exec'])
6250b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 0)
6260b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)
627d992880bSDaniel Latypov		self.linux_source_mock.run_kernel.assert_called_once_with(
628723c8258SRae Moar			args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)
62945ba7a89SDavid Gow		self.print_mock.assert_any_call(StrContains('Testing complete.'))
63045ba7a89SDavid Gow
6316ebf5866SFelix Guo	def test_run_passes_args_pass(self):
6328a04930fSDaniel Latypov		kunit.main(['run'])
6330b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
6340b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)
635021ed9f5SHeidi Fahim		self.linux_source_mock.run_kernel.assert_called_once_with(
636723c8258SRae Moar			args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)
6376ebf5866SFelix Guo		self.print_mock.assert_any_call(StrContains('Testing complete.'))
6386ebf5866SFelix Guo
63945ba7a89SDavid Gow	def test_exec_passes_args_fail(self):
64045ba7a89SDavid Gow		self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
64145ba7a89SDavid Gow		with self.assertRaises(SystemExit) as e:
6428a04930fSDaniel Latypov			kunit.main(['exec'])
6430b3e6807SDaniel Latypov		self.assertEqual(e.exception.code, 1)
64445ba7a89SDavid Gow
6456ebf5866SFelix Guo	def test_run_passes_args_fail(self):
6466ebf5866SFelix Guo		self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
6476ebf5866SFelix Guo		with self.assertRaises(SystemExit) as e:
6488a04930fSDaniel Latypov			kunit.main(['run'])
6490b3e6807SDaniel Latypov		self.assertEqual(e.exception.code, 1)
6500b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
6510b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)
6520a7d5c30SDaniel Latypov		self.print_mock.assert_any_call(StrContains('Could not find any KTAP output.'))
653d65d07cbSRae Moar
654d65d07cbSRae Moar	def test_exec_no_tests(self):
655d65d07cbSRae Moar		self.linux_source_mock.run_kernel = mock.Mock(return_value=['TAP version 14', '1..0'])
656d65d07cbSRae Moar		with self.assertRaises(SystemExit) as e:
6578a04930fSDaniel Latypov			kunit.main(['run'])
6580453f984SDaniel Latypov		self.assertEqual(e.exception.code, 1)
659d65d07cbSRae Moar		self.linux_source_mock.run_kernel.assert_called_once_with(
660723c8258SRae Moar			args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)
661d65d07cbSRae Moar		self.print_mock.assert_any_call(StrContains(' 0 tests run!'))
6626ebf5866SFelix Guo
66345ba7a89SDavid Gow	def test_exec_raw_output(self):
66445ba7a89SDavid Gow		self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
6658a04930fSDaniel Latypov		kunit.main(['exec', '--raw_output'])
6660b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)
6670b3e6807SDaniel Latypov		for call in self.print_mock.call_args_list:
6680b3e6807SDaniel Latypov			self.assertNotEqual(call, mock.call(StrContains('Testing complete.')))
669d65d07cbSRae Moar			self.assertNotEqual(call, mock.call(StrContains(' 0 tests run!')))
67045ba7a89SDavid Gow
6716ebf5866SFelix Guo	def test_run_raw_output(self):
6726ebf5866SFelix Guo		self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
6738a04930fSDaniel Latypov		kunit.main(['run', '--raw_output'])
6740b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
6750b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)
6760b3e6807SDaniel Latypov		for call in self.print_mock.call_args_list:
6770b3e6807SDaniel Latypov			self.assertNotEqual(call, mock.call(StrContains('Testing complete.')))
678d65d07cbSRae Moar			self.assertNotEqual(call, mock.call(StrContains(' 0 tests run!')))
67945ba7a89SDavid Gow
6806a499c9cSDaniel Latypov	def test_run_raw_output_kunit(self):
6816a499c9cSDaniel Latypov		self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
6828a04930fSDaniel Latypov		kunit.main(['run', '--raw_output=kunit'])
6836a499c9cSDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
6846a499c9cSDaniel Latypov		self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)
6856a499c9cSDaniel Latypov		for call in self.print_mock.call_args_list:
6866a499c9cSDaniel Latypov			self.assertNotEqual(call, mock.call(StrContains('Testing complete.')))
6876a499c9cSDaniel Latypov			self.assertNotEqual(call, mock.call(StrContains(' 0 tests run')))
6886a499c9cSDaniel Latypov
689baa33315SDaniel Latypov	def test_run_raw_output_invalid(self):
690baa33315SDaniel Latypov		self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
691baa33315SDaniel Latypov		with self.assertRaises(SystemExit) as e:
6928a04930fSDaniel Latypov			kunit.main(['run', '--raw_output=invalid'])
6930453f984SDaniel Latypov		self.assertNotEqual(e.exception.code, 0)
694baa33315SDaniel Latypov
695d8c23eadSDaniel Latypov	def test_run_raw_output_does_not_take_positional_args(self):
696d8c23eadSDaniel Latypov		# --raw_output is a string flag, but we don't want it to consume
697d8c23eadSDaniel Latypov		# any positional arguments, only ones after an '='
698d8c23eadSDaniel Latypov		self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
6998a04930fSDaniel Latypov		kunit.main(['run', '--raw_output', 'filter_glob'])
700d8c23eadSDaniel Latypov		self.linux_source_mock.run_kernel.assert_called_once_with(
701723c8258SRae Moar			args=None, build_dir='.kunit', filter_glob='filter_glob', filter='', filter_action=None, timeout=300)
702d8c23eadSDaniel Latypov
70345ba7a89SDavid Gow	def test_exec_timeout(self):
70445ba7a89SDavid Gow		timeout = 3453
7058a04930fSDaniel Latypov		kunit.main(['exec', '--timeout', str(timeout)])
706d992880bSDaniel Latypov		self.linux_source_mock.run_kernel.assert_called_once_with(
707723c8258SRae Moar			args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=timeout)
70845ba7a89SDavid Gow		self.print_mock.assert_any_call(StrContains('Testing complete.'))
7096ebf5866SFelix Guo
7106ebf5866SFelix Guo	def test_run_timeout(self):
7116ebf5866SFelix Guo		timeout = 3453
7128a04930fSDaniel Latypov		kunit.main(['run', '--timeout', str(timeout)])
7130b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
714021ed9f5SHeidi Fahim		self.linux_source_mock.run_kernel.assert_called_once_with(
715723c8258SRae Moar			args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=timeout)
7166ebf5866SFelix Guo		self.print_mock.assert_any_call(StrContains('Testing complete.'))
7176ebf5866SFelix Guo
718b1b35201SSeongJae Park	def test_run_builddir(self):
719b1b35201SSeongJae Park		build_dir = '.kunit'
7208a04930fSDaniel Latypov		kunit.main(['run', '--build_dir=.kunit'])
7210b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
722021ed9f5SHeidi Fahim		self.linux_source_mock.run_kernel.assert_called_once_with(
723723c8258SRae Moar			args=None, build_dir=build_dir, filter_glob='', filter='', filter_action=None, timeout=300)
724b1b35201SSeongJae Park		self.print_mock.assert_any_call(StrContains('Testing complete.'))
725b1b35201SSeongJae Park
72645ba7a89SDavid Gow	def test_config_builddir(self):
72745ba7a89SDavid Gow		build_dir = '.kunit'
7288a04930fSDaniel Latypov		kunit.main(['config', '--build_dir', build_dir])
7290b3e6807SDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
73045ba7a89SDavid Gow
73145ba7a89SDavid Gow	def test_build_builddir(self):
73245ba7a89SDavid Gow		build_dir = '.kunit'
733ad659ccbSDavid Gow		jobs = kunit.get_default_jobs()
7348a04930fSDaniel Latypov		kunit.main(['build', '--build_dir', build_dir])
735980ac3adSDaniel Latypov		self.linux_source_mock.build_kernel.assert_called_once_with(jobs, build_dir, None)
73645ba7a89SDavid Gow
73745ba7a89SDavid Gow	def test_exec_builddir(self):
73845ba7a89SDavid Gow		build_dir = '.kunit'
7398a04930fSDaniel Latypov		kunit.main(['exec', '--build_dir', build_dir])
740d992880bSDaniel Latypov		self.linux_source_mock.run_kernel.assert_called_once_with(
741723c8258SRae Moar			args=None, build_dir=build_dir, filter_glob='', filter='', filter_action=None, timeout=300)
74245ba7a89SDavid Gow		self.print_mock.assert_any_call(StrContains('Testing complete.'))
74345ba7a89SDavid Gow
7448a04930fSDaniel Latypov	def test_run_kunitconfig(self):
745243180f5SDaniel Latypov		kunit.main(['run', '--kunitconfig=mykunitconfig'])
746243180f5SDaniel Latypov		# Just verify that we parsed and initialized it correctly here.
7478a04930fSDaniel Latypov		self.mock_linux_init.assert_called_once_with('.kunit',
74853b46621SDaniel Latypov						kunitconfig_paths=['mykunitconfig'],
7499f57cc76SDaniel Latypov						kconfig_add=None,
75087c9c163SBrendan Higgins						arch='um',
75187c9c163SBrendan Higgins						cross_compile=None,
752a9333bd3SDaniel Latypov						qemu_config_path=None,
753a9333bd3SDaniel Latypov						extra_qemu_args=[])
754243180f5SDaniel Latypov
7558a04930fSDaniel Latypov	def test_config_kunitconfig(self):
756243180f5SDaniel Latypov		kunit.main(['config', '--kunitconfig=mykunitconfig'])
757243180f5SDaniel Latypov		# Just verify that we parsed and initialized it correctly here.
7588a04930fSDaniel Latypov		self.mock_linux_init.assert_called_once_with('.kunit',
75953b46621SDaniel Latypov						kunitconfig_paths=['mykunitconfig'],
76053b46621SDaniel Latypov						kconfig_add=None,
76153b46621SDaniel Latypov						arch='um',
76253b46621SDaniel Latypov						cross_compile=None,
76353b46621SDaniel Latypov						qemu_config_path=None,
76453b46621SDaniel Latypov						extra_qemu_args=[])
76553b46621SDaniel Latypov
766980ac3adSDaniel Latypov	def test_config_alltests(self):
767980ac3adSDaniel Latypov		kunit.main(['config', '--kunitconfig=mykunitconfig', '--alltests'])
768980ac3adSDaniel Latypov		# Just verify that we parsed and initialized it correctly here.
769980ac3adSDaniel Latypov		self.mock_linux_init.assert_called_once_with('.kunit',
770980ac3adSDaniel Latypov						kunitconfig_paths=[kunit_kernel.ALL_TESTS_CONFIG_PATH, 'mykunitconfig'],
771980ac3adSDaniel Latypov						kconfig_add=None,
772980ac3adSDaniel Latypov						arch='um',
773980ac3adSDaniel Latypov						cross_compile=None,
774980ac3adSDaniel Latypov						qemu_config_path=None,
775980ac3adSDaniel Latypov						extra_qemu_args=[])
776980ac3adSDaniel Latypov
777980ac3adSDaniel Latypov
77853b46621SDaniel Latypov	@mock.patch.object(kunit_kernel, 'LinuxSourceTree')
77953b46621SDaniel Latypov	def test_run_multiple_kunitconfig(self, mock_linux_init):
78053b46621SDaniel Latypov		mock_linux_init.return_value = self.linux_source_mock
78153b46621SDaniel Latypov		kunit.main(['run', '--kunitconfig=mykunitconfig', '--kunitconfig=other'])
78253b46621SDaniel Latypov		# Just verify that we parsed and initialized it correctly here.
78353b46621SDaniel Latypov		mock_linux_init.assert_called_once_with('.kunit',
78453b46621SDaniel Latypov							kunitconfig_paths=['mykunitconfig', 'other'],
7859f57cc76SDaniel Latypov							kconfig_add=None,
7869f57cc76SDaniel Latypov							arch='um',
7879f57cc76SDaniel Latypov							cross_compile=None,
788a9333bd3SDaniel Latypov							qemu_config_path=None,
789a9333bd3SDaniel Latypov							extra_qemu_args=[])
7909f57cc76SDaniel Latypov
7918a04930fSDaniel Latypov	def test_run_kconfig_add(self):
7929f57cc76SDaniel Latypov		kunit.main(['run', '--kconfig_add=CONFIG_KASAN=y', '--kconfig_add=CONFIG_KCSAN=y'])
7939f57cc76SDaniel Latypov		# Just verify that we parsed and initialized it correctly here.
7948a04930fSDaniel Latypov		self.mock_linux_init.assert_called_once_with('.kunit',
795980ac3adSDaniel Latypov						kunitconfig_paths=[],
7969f57cc76SDaniel Latypov						kconfig_add=['CONFIG_KASAN=y', 'CONFIG_KCSAN=y'],
79787c9c163SBrendan Higgins						arch='um',
79887c9c163SBrendan Higgins						cross_compile=None,
799a9333bd3SDaniel Latypov						qemu_config_path=None,
800a9333bd3SDaniel Latypov						extra_qemu_args=[])
801a9333bd3SDaniel Latypov
802a9333bd3SDaniel Latypov	def test_run_qemu_args(self):
803a9333bd3SDaniel Latypov		kunit.main(['run', '--arch=x86_64', '--qemu_args', '-m 2048'])
804a9333bd3SDaniel Latypov		# Just verify that we parsed and initialized it correctly here.
805a9333bd3SDaniel Latypov		self.mock_linux_init.assert_called_once_with('.kunit',
806980ac3adSDaniel Latypov						kunitconfig_paths=[],
807a9333bd3SDaniel Latypov						kconfig_add=None,
808a9333bd3SDaniel Latypov						arch='x86_64',
809a9333bd3SDaniel Latypov						cross_compile=None,
810a9333bd3SDaniel Latypov						qemu_config_path=None,
811a9333bd3SDaniel Latypov						extra_qemu_args=['-m', '2048'])
812243180f5SDaniel Latypov
8136cb51a18SDaniel Latypov	def test_run_kernel_args(self):
8148a04930fSDaniel Latypov		kunit.main(['run', '--kernel_args=a=1', '--kernel_args=b=2'])
8156cb51a18SDaniel Latypov		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
8166cb51a18SDaniel Latypov		self.linux_source_mock.run_kernel.assert_called_once_with(
817723c8258SRae Moar		      args=['a=1','b=2'], build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)
8186cb51a18SDaniel Latypov		self.print_mock.assert_any_call(StrContains('Testing complete.'))
8196cb51a18SDaniel Latypov
820ff9e09a3SDaniel Latypov	def test_list_tests(self):
821ff9e09a3SDaniel Latypov		want = ['suite.test1', 'suite.test2', 'suite2.test1']
822ff9e09a3SDaniel Latypov		self.linux_source_mock.run_kernel.return_value = ['TAP version 14', 'init: random output'] + want
823ff9e09a3SDaniel Latypov
824ff9e09a3SDaniel Latypov		got = kunit._list_tests(self.linux_source_mock,
8253c67a2c0SRae Moar				     kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'suite', False, False))
826ff9e09a3SDaniel Latypov		self.assertEqual(got, want)
827ff9e09a3SDaniel Latypov		# Should respect the user's filter glob when listing tests.
828ff9e09a3SDaniel Latypov		self.linux_source_mock.run_kernel.assert_called_once_with(
829723c8258SRae Moar			args=['kunit.action=list'], build_dir='.kunit', filter_glob='suite*', filter='', filter_action=None, timeout=300)
830ff9e09a3SDaniel Latypov
831ff9e09a3SDaniel Latypov	@mock.patch.object(kunit, '_list_tests')
832ff9e09a3SDaniel Latypov	def test_run_isolated_by_suite(self, mock_tests):
833ff9e09a3SDaniel Latypov		mock_tests.return_value = ['suite.test1', 'suite.test2', 'suite2.test1']
8348a04930fSDaniel Latypov		kunit.main(['exec', '--run_isolated=suite', 'suite*.test*'])
835ff9e09a3SDaniel Latypov
836ff9e09a3SDaniel Latypov		# Should respect the user's filter glob when listing tests.
837ff9e09a3SDaniel Latypov		mock_tests.assert_called_once_with(mock.ANY,
8383c67a2c0SRae Moar				     kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*.test*', '', None, None, 'suite', False, False))
839ff9e09a3SDaniel Latypov		self.linux_source_mock.run_kernel.assert_has_calls([
840723c8258SRae Moar			mock.call(args=None, build_dir='.kunit', filter_glob='suite.test*', filter='', filter_action=None, timeout=300),
841723c8258SRae Moar			mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test*', filter='', filter_action=None, timeout=300),
842ff9e09a3SDaniel Latypov		])
843ff9e09a3SDaniel Latypov
844ff9e09a3SDaniel Latypov	@mock.patch.object(kunit, '_list_tests')
845ff9e09a3SDaniel Latypov	def test_run_isolated_by_test(self, mock_tests):
846ff9e09a3SDaniel Latypov		mock_tests.return_value = ['suite.test1', 'suite.test2', 'suite2.test1']
8478a04930fSDaniel Latypov		kunit.main(['exec', '--run_isolated=test', 'suite*'])
848ff9e09a3SDaniel Latypov
849ff9e09a3SDaniel Latypov		# Should respect the user's filter glob when listing tests.
850ff9e09a3SDaniel Latypov		mock_tests.assert_called_once_with(mock.ANY,
8513c67a2c0SRae Moar				     kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'test', False, False))
852ff9e09a3SDaniel Latypov		self.linux_source_mock.run_kernel.assert_has_calls([
853723c8258SRae Moar			mock.call(args=None, build_dir='.kunit', filter_glob='suite.test1', filter='', filter_action=None, timeout=300),
854723c8258SRae Moar			mock.call(args=None, build_dir='.kunit', filter_glob='suite.test2', filter='', filter_action=None, timeout=300),
855723c8258SRae Moar			mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test1', filter='', filter_action=None, timeout=300),
856ff9e09a3SDaniel Latypov		])
857ff9e09a3SDaniel Latypov
8586ebf5866SFelix Guoif __name__ == '__main__':
8596ebf5866SFelix Guo	unittest.main()
860