xref: /linux/tools/testing/kunit/kunit.py (revision be886ba90cce2fb2f5a4dbcda8f3be3fd1b2f484)
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
25ff7b437fSBrendan HigginsKunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', 'build_dir', 'defconfig'])
266ebf5866SFelix Guo
27*be886ba9SHeidi FahimKernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0]
28*be886ba9SHeidi Fahim
296ebf5866SFelix Guoclass KunitStatus(Enum):
306ebf5866SFelix Guo	SUCCESS = auto()
316ebf5866SFelix Guo	CONFIG_FAILURE = auto()
326ebf5866SFelix Guo	BUILD_FAILURE = auto()
336ebf5866SFelix Guo	TEST_FAILURE = auto()
346ebf5866SFelix Guo
35ff7b437fSBrendan Higginsdef create_default_kunitconfig():
36e3212513SSeongJae Park	if not os.path.exists(kunit_kernel.kunitconfig_path):
37ff7b437fSBrendan Higgins		shutil.copyfile('arch/um/configs/kunit_defconfig',
38e3212513SSeongJae Park				kunit_kernel.kunitconfig_path)
39ff7b437fSBrendan Higgins
40*be886ba9SHeidi Fahimdef get_kernel_root_path():
41*be886ba9SHeidi Fahim	parts = sys.argv[0] if not __file__ else __file__
42*be886ba9SHeidi Fahim	parts = os.path.realpath(parts).split('tools/testing/kunit')
43*be886ba9SHeidi Fahim	if len(parts) != 2:
44*be886ba9SHeidi Fahim		sys.exit(1)
45*be886ba9SHeidi Fahim	return parts[0]
46*be886ba9SHeidi Fahim
476ebf5866SFelix Guodef run_tests(linux: kunit_kernel.LinuxSourceTree,
486ebf5866SFelix Guo	      request: KunitRequest) -> KunitResult:
496ebf5866SFelix Guo	config_start = time.time()
506ebf5866SFelix Guo	success = linux.build_reconfig(request.build_dir)
516ebf5866SFelix Guo	config_end = time.time()
526ebf5866SFelix Guo	if not success:
536ebf5866SFelix Guo		return KunitResult(KunitStatus.CONFIG_FAILURE, 'could not configure kernel')
546ebf5866SFelix Guo
556ebf5866SFelix Guo	kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
566ebf5866SFelix Guo
576ebf5866SFelix Guo	build_start = time.time()
586ebf5866SFelix Guo	success = linux.build_um_kernel(request.jobs, request.build_dir)
596ebf5866SFelix Guo	build_end = time.time()
606ebf5866SFelix Guo	if not success:
616ebf5866SFelix Guo		return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel')
626ebf5866SFelix Guo
636ebf5866SFelix Guo	kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
646ebf5866SFelix Guo	test_start = time.time()
656ebf5866SFelix Guo
666ebf5866SFelix Guo	test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS,
676ebf5866SFelix Guo					      [],
686ebf5866SFelix Guo					      'Tests not Parsed.')
696ebf5866SFelix Guo	if request.raw_output:
706ebf5866SFelix Guo		kunit_parser.raw_output(
716ec1b81dSSeongJae Park			linux.run_kernel(timeout=request.timeout,
726ec1b81dSSeongJae Park					 build_dir=request.build_dir))
736ebf5866SFelix Guo	else:
746ec1b81dSSeongJae Park		kunit_output = linux.run_kernel(timeout=request.timeout,
756ec1b81dSSeongJae Park						build_dir=request.build_dir)
766ebf5866SFelix Guo		test_result = kunit_parser.parse_run_tests(kunit_output)
776ebf5866SFelix Guo	test_end = time.time()
786ebf5866SFelix Guo
796ebf5866SFelix Guo	kunit_parser.print_with_timestamp((
806ebf5866SFelix Guo		'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' +
816ebf5866SFelix Guo		'building, %.3fs running\n') % (
826ebf5866SFelix Guo				test_end - config_start,
836ebf5866SFelix Guo				config_end - config_start,
846ebf5866SFelix Guo				build_end - build_start,
856ebf5866SFelix Guo				test_end - test_start))
866ebf5866SFelix Guo
876ebf5866SFelix Guo	if test_result.status != kunit_parser.TestStatus.SUCCESS:
886ebf5866SFelix Guo		return KunitResult(KunitStatus.TEST_FAILURE, test_result)
896ebf5866SFelix Guo	else:
906ebf5866SFelix Guo		return KunitResult(KunitStatus.SUCCESS, test_result)
916ebf5866SFelix Guo
92ff7b437fSBrendan Higginsdef main(argv, linux=None):
936ebf5866SFelix Guo	parser = argparse.ArgumentParser(
946ebf5866SFelix Guo			description='Helps writing and running KUnit tests.')
956ebf5866SFelix Guo	subparser = parser.add_subparsers(dest='subcommand')
966ebf5866SFelix Guo
976ebf5866SFelix Guo	run_parser = subparser.add_parser('run', help='Runs KUnit tests.')
986ebf5866SFelix Guo	run_parser.add_argument('--raw_output', help='don\'t format output from kernel',
996ebf5866SFelix Guo				action='store_true')
1006ebf5866SFelix Guo
1016ebf5866SFelix Guo	run_parser.add_argument('--timeout',
1026ebf5866SFelix Guo				help='maximum number of seconds to allow for all tests '
1036ebf5866SFelix Guo				'to run. This does not include time taken to build the '
1046ebf5866SFelix Guo				'tests.',
1056ebf5866SFelix Guo				type=int,
1066ebf5866SFelix Guo				default=300,
1076ebf5866SFelix Guo				metavar='timeout')
1086ebf5866SFelix Guo
1096ebf5866SFelix Guo	run_parser.add_argument('--jobs',
1106ebf5866SFelix Guo				help='As in the make command, "Specifies  the number of '
1116ebf5866SFelix Guo				'jobs (commands) to run simultaneously."',
1126ebf5866SFelix Guo				type=int, default=8, metavar='jobs')
1136ebf5866SFelix Guo
1146ebf5866SFelix Guo	run_parser.add_argument('--build_dir',
1156ebf5866SFelix Guo				help='As in the make command, it specifies the build '
1166ebf5866SFelix Guo				'directory.',
117609952c2SSeongJae Park				type=str, default='', metavar='build_dir')
1186ebf5866SFelix Guo
119ff7b437fSBrendan Higgins	run_parser.add_argument('--defconfig',
12014ee5cfdSSeongJae Park				help='Uses a default .kunitconfig.',
121ff7b437fSBrendan Higgins				action='store_true')
122ff7b437fSBrendan Higgins
1236ebf5866SFelix Guo	cli_args = parser.parse_args(argv)
1246ebf5866SFelix Guo
1256ebf5866SFelix Guo	if cli_args.subcommand == 'run':
126*be886ba9SHeidi Fahim		if get_kernel_root_path():
127*be886ba9SHeidi Fahim			os.chdir(get_kernel_root_path())
128*be886ba9SHeidi Fahim
129e3212513SSeongJae Park		if cli_args.build_dir:
130e3212513SSeongJae Park			if not os.path.exists(cli_args.build_dir):
131e3212513SSeongJae Park				os.mkdir(cli_args.build_dir)
132e3212513SSeongJae Park			kunit_kernel.kunitconfig_path = os.path.join(
133e3212513SSeongJae Park				cli_args.build_dir,
134e3212513SSeongJae Park				kunit_kernel.kunitconfig_path)
135e3212513SSeongJae Park
136ff7b437fSBrendan Higgins		if cli_args.defconfig:
137ff7b437fSBrendan Higgins			create_default_kunitconfig()
138ff7b437fSBrendan Higgins
139ff7b437fSBrendan Higgins		if not linux:
140ff7b437fSBrendan Higgins			linux = kunit_kernel.LinuxSourceTree()
141ff7b437fSBrendan Higgins
1426ebf5866SFelix Guo		request = KunitRequest(cli_args.raw_output,
1436ebf5866SFelix Guo				       cli_args.timeout,
1446ebf5866SFelix Guo				       cli_args.jobs,
145ff7b437fSBrendan Higgins				       cli_args.build_dir,
146ff7b437fSBrendan Higgins				       cli_args.defconfig)
1476ebf5866SFelix Guo		result = run_tests(linux, request)
1486ebf5866SFelix Guo		if result.status != KunitStatus.SUCCESS:
1496ebf5866SFelix Guo			sys.exit(1)
1506ebf5866SFelix Guo	else:
1516ebf5866SFelix Guo		parser.print_help()
1526ebf5866SFelix Guo
1536ebf5866SFelix Guoif __name__ == '__main__':
154ff7b437fSBrendan Higgins	main(sys.argv[1:])
155