xref: /linux/tools/testing/kunit/kunit.py (revision 0476e69f39377192d638c459d11400c6e9a6ffb0)
16ebf5866SFelix Guo#!/usr/bin/python3
26ebf5866SFelix Guo# SPDX-License-Identifier: GPL-2.0
36ebf5866SFelix Guo#
46ebf5866SFelix Guo# A thin wrapper on top of the KUnit Kernel
56ebf5866SFelix Guo#
66ebf5866SFelix Guo# Copyright (C) 2019, Google LLC.
76ebf5866SFelix Guo# Author: Felix Guo <felixguoxiuping@gmail.com>
86ebf5866SFelix Guo# Author: Brendan Higgins <brendanhiggins@google.com>
96ebf5866SFelix Guo
106ebf5866SFelix Guoimport argparse
116ebf5866SFelix Guoimport sys
126ebf5866SFelix Guoimport os
136ebf5866SFelix Guoimport time
14ff7b437fSBrendan Higginsimport shutil
156ebf5866SFelix Guo
166ebf5866SFelix Guofrom collections import namedtuple
176ebf5866SFelix Guofrom enum import Enum, auto
186ebf5866SFelix Guo
196ebf5866SFelix Guoimport kunit_config
206ebf5866SFelix Guoimport kunit_kernel
216ebf5866SFelix Guoimport kunit_parser
226ebf5866SFelix Guo
236ebf5866SFelix GuoKunitResult = namedtuple('KunitResult', ['status','result'])
246ebf5866SFelix Guo
25021ed9f5SHeidi FahimKunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs',
26021ed9f5SHeidi Fahim					   'build_dir', 'defconfig',
27*0476e69fSGreg Thelen					   'alltests', 'make_options'])
286ebf5866SFelix Guo
29be886ba9SHeidi FahimKernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0]
30be886ba9SHeidi Fahim
316ebf5866SFelix Guoclass KunitStatus(Enum):
326ebf5866SFelix Guo	SUCCESS = auto()
336ebf5866SFelix Guo	CONFIG_FAILURE = auto()
346ebf5866SFelix Guo	BUILD_FAILURE = auto()
356ebf5866SFelix Guo	TEST_FAILURE = auto()
366ebf5866SFelix Guo
37ff7b437fSBrendan Higginsdef create_default_kunitconfig():
38e3212513SSeongJae Park	if not os.path.exists(kunit_kernel.kunitconfig_path):
39ff7b437fSBrendan Higgins		shutil.copyfile('arch/um/configs/kunit_defconfig',
40e3212513SSeongJae Park				kunit_kernel.kunitconfig_path)
41ff7b437fSBrendan Higgins
42be886ba9SHeidi Fahimdef get_kernel_root_path():
43be886ba9SHeidi Fahim	parts = sys.argv[0] if not __file__ else __file__
44be886ba9SHeidi Fahim	parts = os.path.realpath(parts).split('tools/testing/kunit')
45be886ba9SHeidi Fahim	if len(parts) != 2:
46be886ba9SHeidi Fahim		sys.exit(1)
47be886ba9SHeidi Fahim	return parts[0]
48be886ba9SHeidi Fahim
496ebf5866SFelix Guodef run_tests(linux: kunit_kernel.LinuxSourceTree,
506ebf5866SFelix Guo	      request: KunitRequest) -> KunitResult:
516ebf5866SFelix Guo	config_start = time.time()
52*0476e69fSGreg Thelen	success = linux.build_reconfig(request.build_dir, request.make_options)
536ebf5866SFelix Guo	config_end = time.time()
546ebf5866SFelix Guo	if not success:
556ebf5866SFelix Guo		return KunitResult(KunitStatus.CONFIG_FAILURE, 'could not configure kernel')
566ebf5866SFelix Guo
576ebf5866SFelix Guo	kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
586ebf5866SFelix Guo
596ebf5866SFelix Guo	build_start = time.time()
60021ed9f5SHeidi Fahim	success = linux.build_um_kernel(request.alltests,
61021ed9f5SHeidi Fahim					request.jobs,
62*0476e69fSGreg Thelen					request.build_dir,
63*0476e69fSGreg Thelen					request.make_options)
646ebf5866SFelix Guo	build_end = time.time()
656ebf5866SFelix Guo	if not success:
666ebf5866SFelix Guo		return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel')
676ebf5866SFelix Guo
686ebf5866SFelix Guo	kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
696ebf5866SFelix Guo	test_start = time.time()
70021ed9f5SHeidi Fahim	kunit_output = linux.run_kernel(
71021ed9f5SHeidi Fahim		timeout=None if request.alltests else request.timeout,
726ec1b81dSSeongJae Park		build_dir=request.build_dir)
73021ed9f5SHeidi Fahim	if request.raw_output:
74021ed9f5SHeidi Fahim		raw_output = kunit_parser.raw_output(kunit_output)
75021ed9f5SHeidi Fahim		isolated = list(kunit_parser.isolate_kunit_output(raw_output))
76021ed9f5SHeidi Fahim		test_result = kunit_parser.parse_test_result(isolated)
77021ed9f5SHeidi Fahim	else:
786ebf5866SFelix Guo		test_result = kunit_parser.parse_run_tests(kunit_output)
796ebf5866SFelix Guo	test_end = time.time()
806ebf5866SFelix Guo
816ebf5866SFelix Guo	kunit_parser.print_with_timestamp((
826ebf5866SFelix Guo		'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' +
836ebf5866SFelix Guo		'building, %.3fs running\n') % (
846ebf5866SFelix Guo				test_end - config_start,
856ebf5866SFelix Guo				config_end - config_start,
866ebf5866SFelix Guo				build_end - build_start,
876ebf5866SFelix Guo				test_end - test_start))
886ebf5866SFelix Guo
896ebf5866SFelix Guo	if test_result.status != kunit_parser.TestStatus.SUCCESS:
906ebf5866SFelix Guo		return KunitResult(KunitStatus.TEST_FAILURE, test_result)
916ebf5866SFelix Guo	else:
926ebf5866SFelix Guo		return KunitResult(KunitStatus.SUCCESS, test_result)
936ebf5866SFelix Guo
94ff7b437fSBrendan Higginsdef main(argv, linux=None):
956ebf5866SFelix Guo	parser = argparse.ArgumentParser(
966ebf5866SFelix Guo			description='Helps writing and running KUnit tests.')
976ebf5866SFelix Guo	subparser = parser.add_subparsers(dest='subcommand')
986ebf5866SFelix Guo
996ebf5866SFelix Guo	run_parser = subparser.add_parser('run', help='Runs KUnit tests.')
1006ebf5866SFelix Guo	run_parser.add_argument('--raw_output', help='don\'t format output from kernel',
1016ebf5866SFelix Guo				action='store_true')
1026ebf5866SFelix Guo
1036ebf5866SFelix Guo	run_parser.add_argument('--timeout',
1046ebf5866SFelix Guo				help='maximum number of seconds to allow for all tests '
1056ebf5866SFelix Guo				'to run. This does not include time taken to build the '
1066ebf5866SFelix Guo				'tests.',
1076ebf5866SFelix Guo				type=int,
1086ebf5866SFelix Guo				default=300,
1096ebf5866SFelix Guo				metavar='timeout')
1106ebf5866SFelix Guo
1116ebf5866SFelix Guo	run_parser.add_argument('--jobs',
1126ebf5866SFelix Guo				help='As in the make command, "Specifies  the number of '
1136ebf5866SFelix Guo				'jobs (commands) to run simultaneously."',
1146ebf5866SFelix Guo				type=int, default=8, metavar='jobs')
1156ebf5866SFelix Guo
1166ebf5866SFelix Guo	run_parser.add_argument('--build_dir',
1176ebf5866SFelix Guo				help='As in the make command, it specifies the build '
1186ebf5866SFelix Guo				'directory.',
119609952c2SSeongJae Park				type=str, default='', metavar='build_dir')
1206ebf5866SFelix Guo
121ff7b437fSBrendan Higgins	run_parser.add_argument('--defconfig',
12214ee5cfdSSeongJae Park				help='Uses a default .kunitconfig.',
123ff7b437fSBrendan Higgins				action='store_true')
124ff7b437fSBrendan Higgins
125021ed9f5SHeidi Fahim	run_parser.add_argument('--alltests',
126021ed9f5SHeidi Fahim				help='Run all KUnit tests through allyesconfig',
127021ed9f5SHeidi Fahim				action='store_true')
128021ed9f5SHeidi Fahim
129*0476e69fSGreg Thelen	run_parser.add_argument('--make_options',
130*0476e69fSGreg Thelen				help='X=Y make option, can be repeated.',
131*0476e69fSGreg Thelen				action='append')
132*0476e69fSGreg Thelen
1336ebf5866SFelix Guo	cli_args = parser.parse_args(argv)
1346ebf5866SFelix Guo
1356ebf5866SFelix Guo	if cli_args.subcommand == 'run':
136be886ba9SHeidi Fahim		if get_kernel_root_path():
137be886ba9SHeidi Fahim			os.chdir(get_kernel_root_path())
138be886ba9SHeidi Fahim
139e3212513SSeongJae Park		if cli_args.build_dir:
140e3212513SSeongJae Park			if not os.path.exists(cli_args.build_dir):
141e3212513SSeongJae Park				os.mkdir(cli_args.build_dir)
142e3212513SSeongJae Park			kunit_kernel.kunitconfig_path = os.path.join(
143e3212513SSeongJae Park				cli_args.build_dir,
144e3212513SSeongJae Park				kunit_kernel.kunitconfig_path)
145e3212513SSeongJae Park
146ff7b437fSBrendan Higgins		if cli_args.defconfig:
147ff7b437fSBrendan Higgins			create_default_kunitconfig()
148ff7b437fSBrendan Higgins
149ff7b437fSBrendan Higgins		if not linux:
150ff7b437fSBrendan Higgins			linux = kunit_kernel.LinuxSourceTree()
151ff7b437fSBrendan Higgins
1526ebf5866SFelix Guo		request = KunitRequest(cli_args.raw_output,
1536ebf5866SFelix Guo				       cli_args.timeout,
1546ebf5866SFelix Guo				       cli_args.jobs,
155ff7b437fSBrendan Higgins				       cli_args.build_dir,
156021ed9f5SHeidi Fahim				       cli_args.defconfig,
157*0476e69fSGreg Thelen				       cli_args.alltests,
158*0476e69fSGreg Thelen				       cli_args.make_options)
1596ebf5866SFelix Guo		result = run_tests(linux, request)
1606ebf5866SFelix Guo		if result.status != KunitStatus.SUCCESS:
1616ebf5866SFelix Guo			sys.exit(1)
1626ebf5866SFelix Guo	else:
1636ebf5866SFelix Guo		parser.print_help()
1646ebf5866SFelix Guo
1656ebf5866SFelix Guoif __name__ == '__main__':
166ff7b437fSBrendan Higgins	main(sys.argv[1:])
167