b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | #!/usr/bin/python3 |
| 2 | # SPDX-License-Identifier: GPL-2.0 |
| 3 | # |
| 4 | # A thin wrapper on top of the KUnit Kernel |
| 5 | # |
| 6 | # Copyright (C) 2019, Google LLC. |
| 7 | # Author: Felix Guo <felixguoxiuping@gmail.com> |
| 8 | # Author: Brendan Higgins <brendanhiggins@google.com> |
| 9 | |
| 10 | import argparse |
| 11 | import sys |
| 12 | import os |
| 13 | import time |
| 14 | import shutil |
| 15 | |
| 16 | from collections import namedtuple |
| 17 | from enum import Enum, auto |
| 18 | |
| 19 | import kunit_config |
| 20 | import kunit_kernel |
| 21 | import kunit_parser |
| 22 | |
| 23 | KunitResult = namedtuple('KunitResult', ['status','result']) |
| 24 | |
| 25 | KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', 'build_dir', 'defconfig']) |
| 26 | |
| 27 | class KunitStatus(Enum): |
| 28 | SUCCESS = auto() |
| 29 | CONFIG_FAILURE = auto() |
| 30 | BUILD_FAILURE = auto() |
| 31 | TEST_FAILURE = auto() |
| 32 | |
| 33 | def create_default_kunitconfig(): |
| 34 | if not os.path.exists(kunit_kernel.KUNITCONFIG_PATH): |
| 35 | shutil.copyfile('arch/um/configs/kunit_defconfig', |
| 36 | kunit_kernel.KUNITCONFIG_PATH) |
| 37 | |
| 38 | def run_tests(linux: kunit_kernel.LinuxSourceTree, |
| 39 | request: KunitRequest) -> KunitResult: |
| 40 | if request.defconfig: |
| 41 | create_default_kunitconfig() |
| 42 | |
| 43 | config_start = time.time() |
| 44 | success = linux.build_reconfig(request.build_dir) |
| 45 | config_end = time.time() |
| 46 | if not success: |
| 47 | return KunitResult(KunitStatus.CONFIG_FAILURE, 'could not configure kernel') |
| 48 | |
| 49 | kunit_parser.print_with_timestamp('Building KUnit Kernel ...') |
| 50 | |
| 51 | build_start = time.time() |
| 52 | success = linux.build_um_kernel(request.jobs, request.build_dir) |
| 53 | build_end = time.time() |
| 54 | if not success: |
| 55 | return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel') |
| 56 | |
| 57 | kunit_parser.print_with_timestamp('Starting KUnit Kernel ...') |
| 58 | test_start = time.time() |
| 59 | |
| 60 | test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS, |
| 61 | [], |
| 62 | 'Tests not Parsed.') |
| 63 | if request.raw_output: |
| 64 | kunit_parser.raw_output( |
| 65 | linux.run_kernel(timeout=request.timeout, |
| 66 | build_dir=request.build_dir)) |
| 67 | else: |
| 68 | kunit_output = linux.run_kernel(timeout=request.timeout, |
| 69 | build_dir=request.build_dir) |
| 70 | test_result = kunit_parser.parse_run_tests(kunit_output) |
| 71 | test_end = time.time() |
| 72 | |
| 73 | kunit_parser.print_with_timestamp(( |
| 74 | 'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' + |
| 75 | 'building, %.3fs running\n') % ( |
| 76 | test_end - config_start, |
| 77 | config_end - config_start, |
| 78 | build_end - build_start, |
| 79 | test_end - test_start)) |
| 80 | |
| 81 | if test_result.status != kunit_parser.TestStatus.SUCCESS: |
| 82 | return KunitResult(KunitStatus.TEST_FAILURE, test_result) |
| 83 | else: |
| 84 | return KunitResult(KunitStatus.SUCCESS, test_result) |
| 85 | |
| 86 | def main(argv, linux=None): |
| 87 | parser = argparse.ArgumentParser( |
| 88 | description='Helps writing and running KUnit tests.') |
| 89 | subparser = parser.add_subparsers(dest='subcommand') |
| 90 | |
| 91 | run_parser = subparser.add_parser('run', help='Runs KUnit tests.') |
| 92 | run_parser.add_argument('--raw_output', help='don\'t format output from kernel', |
| 93 | action='store_true') |
| 94 | |
| 95 | run_parser.add_argument('--timeout', |
| 96 | help='maximum number of seconds to allow for all tests ' |
| 97 | 'to run. This does not include time taken to build the ' |
| 98 | 'tests.', |
| 99 | type=int, |
| 100 | default=300, |
| 101 | metavar='timeout') |
| 102 | |
| 103 | run_parser.add_argument('--jobs', |
| 104 | help='As in the make command, "Specifies the number of ' |
| 105 | 'jobs (commands) to run simultaneously."', |
| 106 | type=int, default=8, metavar='jobs') |
| 107 | |
| 108 | run_parser.add_argument('--build_dir', |
| 109 | help='As in the make command, it specifies the build ' |
| 110 | 'directory.', |
| 111 | type=str, default=None, metavar='build_dir') |
| 112 | |
| 113 | run_parser.add_argument('--defconfig', |
| 114 | help='Uses a default kunitconfig.', |
| 115 | action='store_true') |
| 116 | |
| 117 | cli_args = parser.parse_args(argv) |
| 118 | |
| 119 | if cli_args.subcommand == 'run': |
| 120 | if cli_args.defconfig: |
| 121 | create_default_kunitconfig() |
| 122 | |
| 123 | if not linux: |
| 124 | linux = kunit_kernel.LinuxSourceTree() |
| 125 | |
| 126 | request = KunitRequest(cli_args.raw_output, |
| 127 | cli_args.timeout, |
| 128 | cli_args.jobs, |
| 129 | cli_args.build_dir, |
| 130 | cli_args.defconfig) |
| 131 | result = run_tests(linux, request) |
| 132 | if result.status != KunitStatus.SUCCESS: |
| 133 | sys.exit(1) |
| 134 | else: |
| 135 | parser.print_help() |
| 136 | |
| 137 | if __name__ == '__main__': |
| 138 | main(sys.argv[1:]) |