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 23*45ba7a89SDavid GowKunitResult = namedtuple('KunitResult', ['status','result','elapsed_time']) 246ebf5866SFelix Guo 25*45ba7a89SDavid GowKunitConfigRequest = namedtuple('KunitConfigRequest', 26*45ba7a89SDavid Gow ['build_dir', 'defconfig', 'make_options']) 27*45ba7a89SDavid GowKunitBuildRequest = namedtuple('KunitBuildRequest', 28*45ba7a89SDavid Gow ['jobs', 'build_dir', 'alltests', 29*45ba7a89SDavid Gow 'make_options']) 30*45ba7a89SDavid GowKunitExecRequest = namedtuple('KunitExecRequest', 31*45ba7a89SDavid Gow ['timeout', 'build_dir', 'alltests']) 32*45ba7a89SDavid GowKunitParseRequest = namedtuple('KunitParseRequest', 33*45ba7a89SDavid Gow ['raw_output', 'input_data']) 34021ed9f5SHeidi FahimKunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', 35021ed9f5SHeidi Fahim 'build_dir', 'defconfig', 360476e69fSGreg Thelen 'alltests', 'make_options']) 376ebf5866SFelix Guo 38be886ba9SHeidi FahimKernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0] 39be886ba9SHeidi Fahim 406ebf5866SFelix Guoclass KunitStatus(Enum): 416ebf5866SFelix Guo SUCCESS = auto() 426ebf5866SFelix Guo CONFIG_FAILURE = auto() 436ebf5866SFelix Guo BUILD_FAILURE = auto() 446ebf5866SFelix Guo TEST_FAILURE = auto() 456ebf5866SFelix Guo 46ff7b437fSBrendan Higginsdef create_default_kunitconfig(): 47e3212513SSeongJae Park if not os.path.exists(kunit_kernel.kunitconfig_path): 48ff7b437fSBrendan Higgins shutil.copyfile('arch/um/configs/kunit_defconfig', 49e3212513SSeongJae Park kunit_kernel.kunitconfig_path) 50ff7b437fSBrendan Higgins 51be886ba9SHeidi Fahimdef get_kernel_root_path(): 52be886ba9SHeidi Fahim parts = sys.argv[0] if not __file__ else __file__ 53be886ba9SHeidi Fahim parts = os.path.realpath(parts).split('tools/testing/kunit') 54be886ba9SHeidi Fahim if len(parts) != 2: 55be886ba9SHeidi Fahim sys.exit(1) 56be886ba9SHeidi Fahim return parts[0] 57be886ba9SHeidi Fahim 58*45ba7a89SDavid Gowdef config_tests(linux: kunit_kernel.LinuxSourceTree, 59*45ba7a89SDavid Gow request: KunitConfigRequest) -> KunitResult: 60*45ba7a89SDavid Gow kunit_parser.print_with_timestamp('Configuring KUnit Kernel ...') 61*45ba7a89SDavid Gow 626ebf5866SFelix Guo config_start = time.time() 63*45ba7a89SDavid Gow if request.defconfig: 64*45ba7a89SDavid Gow create_default_kunitconfig() 650476e69fSGreg Thelen success = linux.build_reconfig(request.build_dir, request.make_options) 666ebf5866SFelix Guo config_end = time.time() 676ebf5866SFelix Guo if not success: 68*45ba7a89SDavid Gow return KunitResult(KunitStatus.CONFIG_FAILURE, 69*45ba7a89SDavid Gow 'could not configure kernel', 70*45ba7a89SDavid Gow config_end - config_start) 71*45ba7a89SDavid Gow return KunitResult(KunitStatus.SUCCESS, 72*45ba7a89SDavid Gow 'configured kernel successfully', 73*45ba7a89SDavid Gow config_end - config_start) 746ebf5866SFelix Guo 75*45ba7a89SDavid Gowdef build_tests(linux: kunit_kernel.LinuxSourceTree, 76*45ba7a89SDavid Gow request: KunitBuildRequest) -> KunitResult: 776ebf5866SFelix Guo kunit_parser.print_with_timestamp('Building KUnit Kernel ...') 786ebf5866SFelix Guo 796ebf5866SFelix Guo build_start = time.time() 80021ed9f5SHeidi Fahim success = linux.build_um_kernel(request.alltests, 81021ed9f5SHeidi Fahim request.jobs, 820476e69fSGreg Thelen request.build_dir, 830476e69fSGreg Thelen request.make_options) 846ebf5866SFelix Guo build_end = time.time() 856ebf5866SFelix Guo if not success: 866ebf5866SFelix Guo return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel') 87*45ba7a89SDavid Gow if not success: 88*45ba7a89SDavid Gow return KunitResult(KunitStatus.BUILD_FAILURE, 89*45ba7a89SDavid Gow 'could not build kernel', 90*45ba7a89SDavid Gow build_end - build_start) 91*45ba7a89SDavid Gow return KunitResult(KunitStatus.SUCCESS, 92*45ba7a89SDavid Gow 'built kernel successfully', 93*45ba7a89SDavid Gow build_end - build_start) 946ebf5866SFelix Guo 95*45ba7a89SDavid Gowdef exec_tests(linux: kunit_kernel.LinuxSourceTree, 96*45ba7a89SDavid Gow request: KunitExecRequest) -> KunitResult: 976ebf5866SFelix Guo kunit_parser.print_with_timestamp('Starting KUnit Kernel ...') 986ebf5866SFelix Guo test_start = time.time() 99*45ba7a89SDavid Gow result = linux.run_kernel( 100021ed9f5SHeidi Fahim timeout=None if request.alltests else request.timeout, 1016ec1b81dSSeongJae Park build_dir=request.build_dir) 102*45ba7a89SDavid Gow 1036ebf5866SFelix Guo test_end = time.time() 1046ebf5866SFelix Guo 105*45ba7a89SDavid Gow return KunitResult(KunitStatus.SUCCESS, 106*45ba7a89SDavid Gow result, 107*45ba7a89SDavid Gow test_end - test_start) 108*45ba7a89SDavid Gow 109*45ba7a89SDavid Gowdef parse_tests(request: KunitParseRequest) -> KunitResult: 110*45ba7a89SDavid Gow parse_start = time.time() 111*45ba7a89SDavid Gow 112*45ba7a89SDavid Gow test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS, 113*45ba7a89SDavid Gow [], 114*45ba7a89SDavid Gow 'Tests not Parsed.') 115*45ba7a89SDavid Gow if request.raw_output: 116*45ba7a89SDavid Gow kunit_parser.raw_output(request.input_data) 117*45ba7a89SDavid Gow else: 118*45ba7a89SDavid Gow test_result = kunit_parser.parse_run_tests(request.input_data) 119*45ba7a89SDavid Gow parse_end = time.time() 120*45ba7a89SDavid Gow 121*45ba7a89SDavid Gow if test_result.status != kunit_parser.TestStatus.SUCCESS: 122*45ba7a89SDavid Gow return KunitResult(KunitStatus.TEST_FAILURE, test_result, 123*45ba7a89SDavid Gow parse_end - parse_start) 124*45ba7a89SDavid Gow 125*45ba7a89SDavid Gow return KunitResult(KunitStatus.SUCCESS, test_result, 126*45ba7a89SDavid Gow parse_end - parse_start) 127*45ba7a89SDavid Gow 128*45ba7a89SDavid Gow 129*45ba7a89SDavid Gowdef run_tests(linux: kunit_kernel.LinuxSourceTree, 130*45ba7a89SDavid Gow request: KunitRequest) -> KunitResult: 131*45ba7a89SDavid Gow run_start = time.time() 132*45ba7a89SDavid Gow 133*45ba7a89SDavid Gow config_request = KunitConfigRequest(request.build_dir, 134*45ba7a89SDavid Gow request.defconfig, 135*45ba7a89SDavid Gow request.make_options) 136*45ba7a89SDavid Gow config_result = config_tests(linux, config_request) 137*45ba7a89SDavid Gow if config_result.status != KunitStatus.SUCCESS: 138*45ba7a89SDavid Gow return config_result 139*45ba7a89SDavid Gow 140*45ba7a89SDavid Gow build_request = KunitBuildRequest(request.jobs, request.build_dir, 141*45ba7a89SDavid Gow request.alltests, 142*45ba7a89SDavid Gow request.make_options) 143*45ba7a89SDavid Gow build_result = build_tests(linux, build_request) 144*45ba7a89SDavid Gow if build_result.status != KunitStatus.SUCCESS: 145*45ba7a89SDavid Gow return build_result 146*45ba7a89SDavid Gow 147*45ba7a89SDavid Gow exec_request = KunitExecRequest(request.timeout, request.build_dir, 148*45ba7a89SDavid Gow request.alltests) 149*45ba7a89SDavid Gow exec_result = exec_tests(linux, exec_request) 150*45ba7a89SDavid Gow if exec_result.status != KunitStatus.SUCCESS: 151*45ba7a89SDavid Gow return exec_result 152*45ba7a89SDavid Gow 153*45ba7a89SDavid Gow parse_request = KunitParseRequest(request.raw_output, 154*45ba7a89SDavid Gow exec_result.result) 155*45ba7a89SDavid Gow parse_result = parse_tests(parse_request) 156*45ba7a89SDavid Gow 157*45ba7a89SDavid Gow run_end = time.time() 158*45ba7a89SDavid Gow 1596ebf5866SFelix Guo kunit_parser.print_with_timestamp(( 1606ebf5866SFelix Guo 'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' + 1616ebf5866SFelix Guo 'building, %.3fs running\n') % ( 162*45ba7a89SDavid Gow run_end - run_start, 163*45ba7a89SDavid Gow config_result.elapsed_time, 164*45ba7a89SDavid Gow build_result.elapsed_time, 165*45ba7a89SDavid Gow exec_result.elapsed_time)) 166*45ba7a89SDavid Gow return parse_result 1676ebf5866SFelix Guo 168*45ba7a89SDavid Gowdef add_common_opts(parser): 169*45ba7a89SDavid Gow parser.add_argument('--build_dir', 170*45ba7a89SDavid Gow help='As in the make command, it specifies the build ' 171*45ba7a89SDavid Gow 'directory.', 172*45ba7a89SDavid Gow type=str, default='', metavar='build_dir') 173*45ba7a89SDavid Gow parser.add_argument('--make_options', 174*45ba7a89SDavid Gow help='X=Y make option, can be repeated.', 175*45ba7a89SDavid Gow action='append') 176*45ba7a89SDavid Gow parser.add_argument('--alltests', 177*45ba7a89SDavid Gow help='Run all KUnit tests through allyesconfig', 1786ebf5866SFelix Guo action='store_true') 1796ebf5866SFelix Guo 180*45ba7a89SDavid Gowdef add_config_opts(parser): 181*45ba7a89SDavid Gow parser.add_argument('--defconfig', 182*45ba7a89SDavid Gow help='Uses a default .kunitconfig.', 183*45ba7a89SDavid Gow action='store_true') 184*45ba7a89SDavid Gow 185*45ba7a89SDavid Gowdef add_build_opts(parser): 186*45ba7a89SDavid Gow parser.add_argument('--jobs', 187*45ba7a89SDavid Gow help='As in the make command, "Specifies the number of ' 188*45ba7a89SDavid Gow 'jobs (commands) to run simultaneously."', 189*45ba7a89SDavid Gow type=int, default=8, metavar='jobs') 190*45ba7a89SDavid Gow 191*45ba7a89SDavid Gowdef add_exec_opts(parser): 192*45ba7a89SDavid Gow parser.add_argument('--timeout', 1936ebf5866SFelix Guo help='maximum number of seconds to allow for all tests ' 1946ebf5866SFelix Guo 'to run. This does not include time taken to build the ' 1956ebf5866SFelix Guo 'tests.', 1966ebf5866SFelix Guo type=int, 1976ebf5866SFelix Guo default=300, 1986ebf5866SFelix Guo metavar='timeout') 1996ebf5866SFelix Guo 200*45ba7a89SDavid Gowdef add_parse_opts(parser): 201*45ba7a89SDavid Gow parser.add_argument('--raw_output', help='don\'t format output from kernel', 202ff7b437fSBrendan Higgins action='store_true') 203ff7b437fSBrendan Higgins 204021ed9f5SHeidi Fahim 205*45ba7a89SDavid Gowdef main(argv, linux=None): 206*45ba7a89SDavid Gow parser = argparse.ArgumentParser( 207*45ba7a89SDavid Gow description='Helps writing and running KUnit tests.') 208*45ba7a89SDavid Gow subparser = parser.add_subparsers(dest='subcommand') 209*45ba7a89SDavid Gow 210*45ba7a89SDavid Gow # The 'run' command will config, build, exec, and parse in one go. 211*45ba7a89SDavid Gow run_parser = subparser.add_parser('run', help='Runs KUnit tests.') 212*45ba7a89SDavid Gow add_common_opts(run_parser) 213*45ba7a89SDavid Gow add_config_opts(run_parser) 214*45ba7a89SDavid Gow add_build_opts(run_parser) 215*45ba7a89SDavid Gow add_exec_opts(run_parser) 216*45ba7a89SDavid Gow add_parse_opts(run_parser) 217*45ba7a89SDavid Gow 218*45ba7a89SDavid Gow config_parser = subparser.add_parser('config', 219*45ba7a89SDavid Gow help='Ensures that .config contains all of ' 220*45ba7a89SDavid Gow 'the options in .kunitconfig') 221*45ba7a89SDavid Gow add_common_opts(config_parser) 222*45ba7a89SDavid Gow add_config_opts(config_parser) 223*45ba7a89SDavid Gow 224*45ba7a89SDavid Gow build_parser = subparser.add_parser('build', help='Builds a kernel with KUnit tests') 225*45ba7a89SDavid Gow add_common_opts(build_parser) 226*45ba7a89SDavid Gow add_build_opts(build_parser) 227*45ba7a89SDavid Gow 228*45ba7a89SDavid Gow exec_parser = subparser.add_parser('exec', help='Run a kernel with KUnit tests') 229*45ba7a89SDavid Gow add_common_opts(exec_parser) 230*45ba7a89SDavid Gow add_exec_opts(exec_parser) 231*45ba7a89SDavid Gow add_parse_opts(exec_parser) 232*45ba7a89SDavid Gow 233*45ba7a89SDavid Gow # The 'parse' option is special, as it doesn't need the kernel source 234*45ba7a89SDavid Gow # (therefore there is no need for a build_dir, hence no add_common_opts) 235*45ba7a89SDavid Gow # and the '--file' argument is not relevant to 'run', so isn't in 236*45ba7a89SDavid Gow # add_parse_opts() 237*45ba7a89SDavid Gow parse_parser = subparser.add_parser('parse', 238*45ba7a89SDavid Gow help='Parses KUnit results from a file, ' 239*45ba7a89SDavid Gow 'and parses formatted results.') 240*45ba7a89SDavid Gow add_parse_opts(parse_parser) 241*45ba7a89SDavid Gow parse_parser.add_argument('file', 242*45ba7a89SDavid Gow help='Specifies the file to read results from.', 243*45ba7a89SDavid Gow type=str, nargs='?', metavar='input_file') 2440476e69fSGreg Thelen 2456ebf5866SFelix Guo cli_args = parser.parse_args(argv) 2466ebf5866SFelix Guo 2476ebf5866SFelix Guo if cli_args.subcommand == 'run': 248e3212513SSeongJae Park if cli_args.build_dir: 249e3212513SSeongJae Park if not os.path.exists(cli_args.build_dir): 250e3212513SSeongJae Park os.mkdir(cli_args.build_dir) 251e3212513SSeongJae Park kunit_kernel.kunitconfig_path = os.path.join( 252e3212513SSeongJae Park cli_args.build_dir, 253e3212513SSeongJae Park kunit_kernel.kunitconfig_path) 254e3212513SSeongJae Park 255ff7b437fSBrendan Higgins if not linux: 256ff7b437fSBrendan Higgins linux = kunit_kernel.LinuxSourceTree() 257ff7b437fSBrendan Higgins 2586ebf5866SFelix Guo request = KunitRequest(cli_args.raw_output, 2596ebf5866SFelix Guo cli_args.timeout, 2606ebf5866SFelix Guo cli_args.jobs, 261ff7b437fSBrendan Higgins cli_args.build_dir, 262021ed9f5SHeidi Fahim cli_args.defconfig, 2630476e69fSGreg Thelen cli_args.alltests, 2640476e69fSGreg Thelen cli_args.make_options) 2656ebf5866SFelix Guo result = run_tests(linux, request) 2666ebf5866SFelix Guo if result.status != KunitStatus.SUCCESS: 2676ebf5866SFelix Guo sys.exit(1) 268*45ba7a89SDavid Gow elif cli_args.subcommand == 'config': 269*45ba7a89SDavid Gow if cli_args.build_dir: 270*45ba7a89SDavid Gow if not os.path.exists(cli_args.build_dir): 271*45ba7a89SDavid Gow os.mkdir(cli_args.build_dir) 272*45ba7a89SDavid Gow kunit_kernel.kunitconfig_path = os.path.join( 273*45ba7a89SDavid Gow cli_args.build_dir, 274*45ba7a89SDavid Gow kunit_kernel.kunitconfig_path) 275*45ba7a89SDavid Gow 276*45ba7a89SDavid Gow if not linux: 277*45ba7a89SDavid Gow linux = kunit_kernel.LinuxSourceTree() 278*45ba7a89SDavid Gow 279*45ba7a89SDavid Gow request = KunitConfigRequest(cli_args.build_dir, 280*45ba7a89SDavid Gow cli_args.defconfig, 281*45ba7a89SDavid Gow cli_args.make_options) 282*45ba7a89SDavid Gow result = config_tests(linux, request) 283*45ba7a89SDavid Gow kunit_parser.print_with_timestamp(( 284*45ba7a89SDavid Gow 'Elapsed time: %.3fs\n') % ( 285*45ba7a89SDavid Gow result.elapsed_time)) 286*45ba7a89SDavid Gow if result.status != KunitStatus.SUCCESS: 287*45ba7a89SDavid Gow sys.exit(1) 288*45ba7a89SDavid Gow elif cli_args.subcommand == 'build': 289*45ba7a89SDavid Gow if cli_args.build_dir: 290*45ba7a89SDavid Gow if not os.path.exists(cli_args.build_dir): 291*45ba7a89SDavid Gow os.mkdir(cli_args.build_dir) 292*45ba7a89SDavid Gow kunit_kernel.kunitconfig_path = os.path.join( 293*45ba7a89SDavid Gow cli_args.build_dir, 294*45ba7a89SDavid Gow kunit_kernel.kunitconfig_path) 295*45ba7a89SDavid Gow 296*45ba7a89SDavid Gow if not linux: 297*45ba7a89SDavid Gow linux = kunit_kernel.LinuxSourceTree() 298*45ba7a89SDavid Gow 299*45ba7a89SDavid Gow request = KunitBuildRequest(cli_args.jobs, 300*45ba7a89SDavid Gow cli_args.build_dir, 301*45ba7a89SDavid Gow cli_args.alltests, 302*45ba7a89SDavid Gow cli_args.make_options) 303*45ba7a89SDavid Gow result = build_tests(linux, request) 304*45ba7a89SDavid Gow kunit_parser.print_with_timestamp(( 305*45ba7a89SDavid Gow 'Elapsed time: %.3fs\n') % ( 306*45ba7a89SDavid Gow result.elapsed_time)) 307*45ba7a89SDavid Gow if result.status != KunitStatus.SUCCESS: 308*45ba7a89SDavid Gow sys.exit(1) 309*45ba7a89SDavid Gow elif cli_args.subcommand == 'exec': 310*45ba7a89SDavid Gow if cli_args.build_dir: 311*45ba7a89SDavid Gow if not os.path.exists(cli_args.build_dir): 312*45ba7a89SDavid Gow os.mkdir(cli_args.build_dir) 313*45ba7a89SDavid Gow kunit_kernel.kunitconfig_path = os.path.join( 314*45ba7a89SDavid Gow cli_args.build_dir, 315*45ba7a89SDavid Gow kunit_kernel.kunitconfig_path) 316*45ba7a89SDavid Gow 317*45ba7a89SDavid Gow if not linux: 318*45ba7a89SDavid Gow linux = kunit_kernel.LinuxSourceTree() 319*45ba7a89SDavid Gow 320*45ba7a89SDavid Gow exec_request = KunitExecRequest(cli_args.timeout, 321*45ba7a89SDavid Gow cli_args.build_dir, 322*45ba7a89SDavid Gow cli_args.alltests) 323*45ba7a89SDavid Gow exec_result = exec_tests(linux, exec_request) 324*45ba7a89SDavid Gow parse_request = KunitParseRequest(cli_args.raw_output, 325*45ba7a89SDavid Gow exec_result.result) 326*45ba7a89SDavid Gow result = parse_tests(parse_request) 327*45ba7a89SDavid Gow kunit_parser.print_with_timestamp(( 328*45ba7a89SDavid Gow 'Elapsed time: %.3fs\n') % ( 329*45ba7a89SDavid Gow exec_result.elapsed_time)) 330*45ba7a89SDavid Gow if result.status != KunitStatus.SUCCESS: 331*45ba7a89SDavid Gow sys.exit(1) 332*45ba7a89SDavid Gow elif cli_args.subcommand == 'parse': 333*45ba7a89SDavid Gow if cli_args.file == None: 334*45ba7a89SDavid Gow kunit_output = sys.stdin 335*45ba7a89SDavid Gow else: 336*45ba7a89SDavid Gow with open(cli_args.file, 'r') as f: 337*45ba7a89SDavid Gow kunit_output = f.read().splitlines() 338*45ba7a89SDavid Gow request = KunitParseRequest(cli_args.raw_output, 339*45ba7a89SDavid Gow kunit_output) 340*45ba7a89SDavid Gow result = parse_tests(request) 341*45ba7a89SDavid Gow if result.status != KunitStatus.SUCCESS: 342*45ba7a89SDavid Gow sys.exit(1) 3436ebf5866SFelix Guo else: 3446ebf5866SFelix Guo parser.print_help() 3456ebf5866SFelix Guo 3466ebf5866SFelix Guoif __name__ == '__main__': 347ff7b437fSBrendan Higgins main(sys.argv[1:]) 348