xref: /linux/tools/testing/kunit/kunit.py (revision 021ed9f551da33449a5238e45e849913422671d7)
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
25*021ed9f5SHeidi FahimKunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs',
26*021ed9f5SHeidi Fahim					   'build_dir', 'defconfig',
27*021ed9f5SHeidi Fahim					   'alltests'])
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()
526ebf5866SFelix Guo	success = linux.build_reconfig(request.build_dir)
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()
60*021ed9f5SHeidi Fahim	success = linux.build_um_kernel(request.alltests,
61*021ed9f5SHeidi Fahim					request.jobs,
62*021ed9f5SHeidi Fahim					request.build_dir)
636ebf5866SFelix Guo	build_end = time.time()
646ebf5866SFelix Guo	if not success:
656ebf5866SFelix Guo		return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel')
666ebf5866SFelix Guo
676ebf5866SFelix Guo	kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
686ebf5866SFelix Guo	test_start = time.time()
69*021ed9f5SHeidi Fahim	kunit_output = linux.run_kernel(
70*021ed9f5SHeidi Fahim		timeout=None if request.alltests else request.timeout,
716ec1b81dSSeongJae Park		build_dir=request.build_dir)
72*021ed9f5SHeidi Fahim	if request.raw_output:
73*021ed9f5SHeidi Fahim		raw_output = kunit_parser.raw_output(kunit_output)
74*021ed9f5SHeidi Fahim		isolated = list(kunit_parser.isolate_kunit_output(raw_output))
75*021ed9f5SHeidi Fahim		test_result = kunit_parser.parse_test_result(isolated)
76*021ed9f5SHeidi Fahim	else:
776ebf5866SFelix Guo		test_result = kunit_parser.parse_run_tests(kunit_output)
786ebf5866SFelix Guo	test_end = time.time()
796ebf5866SFelix Guo
806ebf5866SFelix Guo	kunit_parser.print_with_timestamp((
816ebf5866SFelix Guo		'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' +
826ebf5866SFelix Guo		'building, %.3fs running\n') % (
836ebf5866SFelix Guo				test_end - config_start,
846ebf5866SFelix Guo				config_end - config_start,
856ebf5866SFelix Guo				build_end - build_start,
866ebf5866SFelix Guo				test_end - test_start))
876ebf5866SFelix Guo
886ebf5866SFelix Guo	if test_result.status != kunit_parser.TestStatus.SUCCESS:
896ebf5866SFelix Guo		return KunitResult(KunitStatus.TEST_FAILURE, test_result)
906ebf5866SFelix Guo	else:
916ebf5866SFelix Guo		return KunitResult(KunitStatus.SUCCESS, test_result)
926ebf5866SFelix Guo
93ff7b437fSBrendan Higginsdef main(argv, linux=None):
946ebf5866SFelix Guo	parser = argparse.ArgumentParser(
956ebf5866SFelix Guo			description='Helps writing and running KUnit tests.')
966ebf5866SFelix Guo	subparser = parser.add_subparsers(dest='subcommand')
976ebf5866SFelix Guo
986ebf5866SFelix Guo	run_parser = subparser.add_parser('run', help='Runs KUnit tests.')
996ebf5866SFelix Guo	run_parser.add_argument('--raw_output', help='don\'t format output from kernel',
1006ebf5866SFelix Guo				action='store_true')
1016ebf5866SFelix Guo
1026ebf5866SFelix Guo	run_parser.add_argument('--timeout',
1036ebf5866SFelix Guo				help='maximum number of seconds to allow for all tests '
1046ebf5866SFelix Guo				'to run. This does not include time taken to build the '
1056ebf5866SFelix Guo				'tests.',
1066ebf5866SFelix Guo				type=int,
1076ebf5866SFelix Guo				default=300,
1086ebf5866SFelix Guo				metavar='timeout')
1096ebf5866SFelix Guo
1106ebf5866SFelix Guo	run_parser.add_argument('--jobs',
1116ebf5866SFelix Guo				help='As in the make command, "Specifies  the number of '
1126ebf5866SFelix Guo				'jobs (commands) to run simultaneously."',
1136ebf5866SFelix Guo				type=int, default=8, metavar='jobs')
1146ebf5866SFelix Guo
1156ebf5866SFelix Guo	run_parser.add_argument('--build_dir',
1166ebf5866SFelix Guo				help='As in the make command, it specifies the build '
1176ebf5866SFelix Guo				'directory.',
118609952c2SSeongJae Park				type=str, default='', metavar='build_dir')
1196ebf5866SFelix Guo
120ff7b437fSBrendan Higgins	run_parser.add_argument('--defconfig',
12114ee5cfdSSeongJae Park				help='Uses a default .kunitconfig.',
122ff7b437fSBrendan Higgins				action='store_true')
123ff7b437fSBrendan Higgins
124*021ed9f5SHeidi Fahim	run_parser.add_argument('--alltests',
125*021ed9f5SHeidi Fahim				help='Run all KUnit tests through allyesconfig',
126*021ed9f5SHeidi Fahim				action='store_true')
127*021ed9f5SHeidi Fahim
1286ebf5866SFelix Guo	cli_args = parser.parse_args(argv)
1296ebf5866SFelix Guo
1306ebf5866SFelix Guo	if cli_args.subcommand == 'run':
131be886ba9SHeidi Fahim		if get_kernel_root_path():
132be886ba9SHeidi Fahim			os.chdir(get_kernel_root_path())
133be886ba9SHeidi Fahim
134e3212513SSeongJae Park		if cli_args.build_dir:
135e3212513SSeongJae Park			if not os.path.exists(cli_args.build_dir):
136e3212513SSeongJae Park				os.mkdir(cli_args.build_dir)
137e3212513SSeongJae Park			kunit_kernel.kunitconfig_path = os.path.join(
138e3212513SSeongJae Park				cli_args.build_dir,
139e3212513SSeongJae Park				kunit_kernel.kunitconfig_path)
140e3212513SSeongJae Park
141ff7b437fSBrendan Higgins		if cli_args.defconfig:
142ff7b437fSBrendan Higgins			create_default_kunitconfig()
143ff7b437fSBrendan Higgins
144ff7b437fSBrendan Higgins		if not linux:
145ff7b437fSBrendan Higgins			linux = kunit_kernel.LinuxSourceTree()
146ff7b437fSBrendan Higgins
1476ebf5866SFelix Guo		request = KunitRequest(cli_args.raw_output,
1486ebf5866SFelix Guo				       cli_args.timeout,
1496ebf5866SFelix Guo				       cli_args.jobs,
150ff7b437fSBrendan Higgins				       cli_args.build_dir,
151*021ed9f5SHeidi Fahim				       cli_args.defconfig,
152*021ed9f5SHeidi Fahim				       cli_args.alltests)
1536ebf5866SFelix Guo		result = run_tests(linux, request)
1546ebf5866SFelix Guo		if result.status != KunitStatus.SUCCESS:
1556ebf5866SFelix Guo			sys.exit(1)
1566ebf5866SFelix Guo	else:
1576ebf5866SFelix Guo		parser.print_help()
1586ebf5866SFelix Guo
1596ebf5866SFelix Guoif __name__ == '__main__':
160ff7b437fSBrendan Higgins	main(sys.argv[1:])
161