kunit.py (de4fb176622d54a82ea3ceb7362392aaf5ff0b5a) kunit.py (00f75043e46d5bd2bba87b3fada6c1090b61bd40)
1#!/usr/bin/env 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
10import argparse
11import os
12import re
13import sys
14import time
15
16assert sys.version_info >= (3, 7), "Python version is too old"
17
18from dataclasses import dataclass
19from enum import Enum, auto
1#!/usr/bin/env 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
10import argparse
11import os
12import re
13import sys
14import time
15
16assert sys.version_info >= (3, 7), "Python version is too old"
17
18from dataclasses import dataclass
19from enum import Enum, auto
20from typing import Any, Iterable, Sequence, List, Optional
20from typing import Iterable, List, Optional, Sequence, Tuple
21
22import kunit_json
23import kunit_kernel
24import kunit_parser
25
26class KunitStatus(Enum):
27 SUCCESS = auto()
28 CONFIG_FAILURE = auto()
29 BUILD_FAILURE = auto()
30 TEST_FAILURE = auto()
31
32@dataclass
33class KunitResult:
34 status: KunitStatus
21
22import kunit_json
23import kunit_kernel
24import kunit_parser
25
26class KunitStatus(Enum):
27 SUCCESS = auto()
28 CONFIG_FAILURE = auto()
29 BUILD_FAILURE = auto()
30 TEST_FAILURE = auto()
31
32@dataclass
33class KunitResult:
34 status: KunitStatus
35 result: Any
36 elapsed_time: float
37
38@dataclass
39class KunitConfigRequest:
40 build_dir: str
41 make_options: Optional[List[str]]
42
43@dataclass

--- 33 unchanged lines hidden (view full) ---

77 request: KunitConfigRequest) -> KunitResult:
78 kunit_parser.print_with_timestamp('Configuring KUnit Kernel ...')
79
80 config_start = time.time()
81 success = linux.build_reconfig(request.build_dir, request.make_options)
82 config_end = time.time()
83 if not success:
84 return KunitResult(KunitStatus.CONFIG_FAILURE,
35 elapsed_time: float
36
37@dataclass
38class KunitConfigRequest:
39 build_dir: str
40 make_options: Optional[List[str]]
41
42@dataclass

--- 33 unchanged lines hidden (view full) ---

76 request: KunitConfigRequest) -> KunitResult:
77 kunit_parser.print_with_timestamp('Configuring KUnit Kernel ...')
78
79 config_start = time.time()
80 success = linux.build_reconfig(request.build_dir, request.make_options)
81 config_end = time.time()
82 if not success:
83 return KunitResult(KunitStatus.CONFIG_FAILURE,
85 'could not configure kernel',
86 config_end - config_start)
87 return KunitResult(KunitStatus.SUCCESS,
84 config_end - config_start)
85 return KunitResult(KunitStatus.SUCCESS,
88 'configured kernel successfully',
89 config_end - config_start)
90
91def build_tests(linux: kunit_kernel.LinuxSourceTree,
92 request: KunitBuildRequest) -> KunitResult:
93 kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
94
95 build_start = time.time()
96 success = linux.build_kernel(request.alltests,
97 request.jobs,
98 request.build_dir,
99 request.make_options)
100 build_end = time.time()
101 if not success:
102 return KunitResult(KunitStatus.BUILD_FAILURE,
86 config_end - config_start)
87
88def build_tests(linux: kunit_kernel.LinuxSourceTree,
89 request: KunitBuildRequest) -> KunitResult:
90 kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
91
92 build_start = time.time()
93 success = linux.build_kernel(request.alltests,
94 request.jobs,
95 request.build_dir,
96 request.make_options)
97 build_end = time.time()
98 if not success:
99 return KunitResult(KunitStatus.BUILD_FAILURE,
103 'could not build kernel',
104 build_end - build_start)
105 if not success:
106 return KunitResult(KunitStatus.BUILD_FAILURE,
100 build_end - build_start)
101 if not success:
102 return KunitResult(KunitStatus.BUILD_FAILURE,
107 'could not build kernel',
108 build_end - build_start)
109 return KunitResult(KunitStatus.SUCCESS,
103 build_end - build_start)
104 return KunitResult(KunitStatus.SUCCESS,
110 'built kernel successfully',
111 build_end - build_start)
112
113def config_and_build_tests(linux: kunit_kernel.LinuxSourceTree,
114 request: KunitBuildRequest) -> KunitResult:
115 config_result = config_tests(linux, request)
116 if config_result.status != KunitStatus.SUCCESS:
117 return config_result
118

--- 49 unchanged lines hidden (view full) ---

168
169 test_start = time.time()
170 run_result = linux.run_kernel(
171 args=request.kernel_args,
172 timeout=None if request.alltests else request.timeout,
173 filter_glob=filter_glob,
174 build_dir=request.build_dir)
175
105 build_end - build_start)
106
107def config_and_build_tests(linux: kunit_kernel.LinuxSourceTree,
108 request: KunitBuildRequest) -> KunitResult:
109 config_result = config_tests(linux, request)
110 if config_result.status != KunitStatus.SUCCESS:
111 return config_result
112

--- 49 unchanged lines hidden (view full) ---

162
163 test_start = time.time()
164 run_result = linux.run_kernel(
165 args=request.kernel_args,
166 timeout=None if request.alltests else request.timeout,
167 filter_glob=filter_glob,
168 build_dir=request.build_dir)
169
176 result = parse_tests(request, run_result)
170 _, test_result = parse_tests(request, run_result)
177 # run_kernel() doesn't block on the kernel exiting.
178 # That only happens after we get the last line of output from `run_result`.
179 # So exec_time here actually contains parsing + execution time, which is fine.
180 test_end = time.time()
181 exec_time += test_end - test_start
182
171 # run_kernel() doesn't block on the kernel exiting.
172 # That only happens after we get the last line of output from `run_result`.
173 # So exec_time here actually contains parsing + execution time, which is fine.
174 test_end = time.time()
175 exec_time += test_end - test_start
176
183 test_counts.add_subtest_counts(result.result.counts)
177 test_counts.add_subtest_counts(test_result.counts)
184
185 if len(filter_globs) == 1 and test_counts.crashed > 0:
186 bd = request.build_dir
187 print('The kernel seems to have crashed; you can decode the stack traces with:')
188 print('$ scripts/decode_stacktrace.sh {}/vmlinux {} < {} | tee {}/decoded.log | {} parse'.format(
189 bd, bd, kunit_kernel.get_outfile_path(bd), bd, sys.argv[0]))
190
191 kunit_status = _map_to_overall_status(test_counts.get_status())
178
179 if len(filter_globs) == 1 and test_counts.crashed > 0:
180 bd = request.build_dir
181 print('The kernel seems to have crashed; you can decode the stack traces with:')
182 print('$ scripts/decode_stacktrace.sh {}/vmlinux {} < {} | tee {}/decoded.log | {} parse'.format(
183 bd, bd, kunit_kernel.get_outfile_path(bd), bd, sys.argv[0]))
184
185 kunit_status = _map_to_overall_status(test_counts.get_status())
192 return KunitResult(status=kunit_status, result=result, elapsed_time=exec_time)
186 return KunitResult(status=kunit_status, elapsed_time=exec_time)
193
194def _map_to_overall_status(test_status: kunit_parser.TestStatus) -> KunitStatus:
195 if test_status in (kunit_parser.TestStatus.SUCCESS, kunit_parser.TestStatus.SKIPPED):
196 return KunitStatus.SUCCESS
197 else:
198 return KunitStatus.TEST_FAILURE
199
187
188def _map_to_overall_status(test_status: kunit_parser.TestStatus) -> KunitStatus:
189 if test_status in (kunit_parser.TestStatus.SUCCESS, kunit_parser.TestStatus.SKIPPED):
190 return KunitStatus.SUCCESS
191 else:
192 return KunitStatus.TEST_FAILURE
193
200def parse_tests(request: KunitParseRequest, input_data: Iterable[str]) -> KunitResult:
194def parse_tests(request: KunitParseRequest, input_data: Iterable[str]) -> Tuple[KunitResult, kunit_parser.Test]:
201 parse_start = time.time()
202
203 test_result = kunit_parser.Test()
204
205 if request.raw_output:
206 # Treat unparsed results as one passing test.
207 test_result.status = kunit_parser.TestStatus.SUCCESS
208 test_result.counts.passed = 1

--- 8 unchanged lines hidden (view full) ---

217 for line in output:
218 print(line.rstrip())
219
220 else:
221 test_result = kunit_parser.parse_run_tests(input_data)
222 parse_end = time.time()
223
224 if request.json:
195 parse_start = time.time()
196
197 test_result = kunit_parser.Test()
198
199 if request.raw_output:
200 # Treat unparsed results as one passing test.
201 test_result.status = kunit_parser.TestStatus.SUCCESS
202 test_result.counts.passed = 1

--- 8 unchanged lines hidden (view full) ---

211 for line in output:
212 print(line.rstrip())
213
214 else:
215 test_result = kunit_parser.parse_run_tests(input_data)
216 parse_end = time.time()
217
218 if request.json:
225 json_obj = kunit_json.get_json_result(
219 json_str = kunit_json.get_json_result(
226 test=test_result,
227 def_config='kunit_defconfig',
220 test=test_result,
221 def_config='kunit_defconfig',
228 build_dir=request.build_dir,
229 json_path=request.json)
222 build_dir=request.build_dir)
230 if request.json == 'stdout':
223 if request.json == 'stdout':
231 print(json_obj)
224 print(json_str)
225 else:
226 with open(request.json, 'w') as f:
227 f.write(json_str)
228 kunit_parser.print_with_timestamp("Test results stored in %s" %
229 os.path.abspath(request.json))
232
233 if test_result.status != kunit_parser.TestStatus.SUCCESS:
230
231 if test_result.status != kunit_parser.TestStatus.SUCCESS:
234 return KunitResult(KunitStatus.TEST_FAILURE, test_result,
235 parse_end - parse_start)
232 return KunitResult(KunitStatus.TEST_FAILURE, parse_end - parse_start), test_result
236
233
237 return KunitResult(KunitStatus.SUCCESS, test_result,
238 parse_end - parse_start)
234 return KunitResult(KunitStatus.SUCCESS, parse_end - parse_start), test_result
239
240def run_tests(linux: kunit_kernel.LinuxSourceTree,
241 request: KunitRequest) -> KunitResult:
242 run_start = time.time()
243
244 config_result = config_tests(linux, request)
245 if config_result.status != KunitStatus.SUCCESS:
246 return config_result

--- 261 unchanged lines hidden (view full) ---

508 sys.stdin.reconfigure(errors='backslashreplace') # pytype: disable=attribute-error
509 kunit_output = sys.stdin
510 else:
511 with open(cli_args.file, 'r', errors='backslashreplace') as f:
512 kunit_output = f.read().splitlines()
513 request = KunitParseRequest(raw_output=cli_args.raw_output,
514 build_dir='',
515 json=cli_args.json)
235
236def run_tests(linux: kunit_kernel.LinuxSourceTree,
237 request: KunitRequest) -> KunitResult:
238 run_start = time.time()
239
240 config_result = config_tests(linux, request)
241 if config_result.status != KunitStatus.SUCCESS:
242 return config_result

--- 261 unchanged lines hidden (view full) ---

504 sys.stdin.reconfigure(errors='backslashreplace') # pytype: disable=attribute-error
505 kunit_output = sys.stdin
506 else:
507 with open(cli_args.file, 'r', errors='backslashreplace') as f:
508 kunit_output = f.read().splitlines()
509 request = KunitParseRequest(raw_output=cli_args.raw_output,
510 build_dir='',
511 json=cli_args.json)
516 result = parse_tests(request, kunit_output)
512 result, _ = parse_tests(request, kunit_output)
517 if result.status != KunitStatus.SUCCESS:
518 sys.exit(1)
519 else:
520 parser.print_help()
521
522if __name__ == '__main__':
523 main(sys.argv[1:])
513 if result.status != KunitStatus.SUCCESS:
514 sys.exit(1)
515 else:
516 parser.print_help()
517
518if __name__ == '__main__':
519 main(sys.argv[1:])