Commit 578a7155 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'linux-kselftest-kunit-fixes-5.10-rc1' of...

Merge tag 'linux-kselftest-kunit-fixes-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest

Pull Kunit updates from Shuah Khan:
 "Several kunit tool bug fixes in flag handling, run outside kernel
  tree, make errors, and generating results"

* tag 'linux-kselftest-kunit-fixes-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest:
  kunit: tool: fix display of make errors
  kunit: tool: handle when .kunit exists but .kunitconfig does not
  kunit: tool: fix --alltests flag
  kunit: tool: allow generating test results in JSON
  kunit: tool: fix running kunit_tool from outside kernel tree
parents 0674324b 1abdd39f
...@@ -39,3 +39,4 @@ ...@@ -39,3 +39,4 @@
# CONFIG_QCOM_CPR is not set # CONFIG_QCOM_CPR is not set
# CONFIG_RESET_BRCMSTB_RESCAL is not set # CONFIG_RESET_BRCMSTB_RESCAL is not set
# CONFIG_RESET_INTEL_GW is not set # CONFIG_RESET_INTEL_GW is not set
# CONFIG_ADI_AXI_ADC is not set
...@@ -17,6 +17,7 @@ from collections import namedtuple ...@@ -17,6 +17,7 @@ from collections import namedtuple
from enum import Enum, auto from enum import Enum, auto
import kunit_config import kunit_config
import kunit_json
import kunit_kernel import kunit_kernel
import kunit_parser import kunit_parser
...@@ -30,9 +31,9 @@ KunitBuildRequest = namedtuple('KunitBuildRequest', ...@@ -30,9 +31,9 @@ KunitBuildRequest = namedtuple('KunitBuildRequest',
KunitExecRequest = namedtuple('KunitExecRequest', KunitExecRequest = namedtuple('KunitExecRequest',
['timeout', 'build_dir', 'alltests']) ['timeout', 'build_dir', 'alltests'])
KunitParseRequest = namedtuple('KunitParseRequest', KunitParseRequest = namedtuple('KunitParseRequest',
['raw_output', 'input_data']) ['raw_output', 'input_data', 'build_dir', 'json'])
KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs',
'build_dir', 'alltests', 'build_dir', 'alltests', 'json',
'make_options']) 'make_options'])
KernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0] KernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0]
...@@ -113,12 +114,22 @@ def parse_tests(request: KunitParseRequest) -> KunitResult: ...@@ -113,12 +114,22 @@ def parse_tests(request: KunitParseRequest) -> KunitResult:
test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS, test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS,
[], [],
'Tests not Parsed.') 'Tests not Parsed.')
if request.raw_output: if request.raw_output:
kunit_parser.raw_output(request.input_data) kunit_parser.raw_output(request.input_data)
else: else:
test_result = kunit_parser.parse_run_tests(request.input_data) test_result = kunit_parser.parse_run_tests(request.input_data)
parse_end = time.time() parse_end = time.time()
if request.json:
json_obj = kunit_json.get_json_result(
test_result=test_result,
def_config='kunit_defconfig',
build_dir=request.build_dir,
json_path=request.json)
if request.json == 'stdout':
print(json_obj)
if test_result.status != kunit_parser.TestStatus.SUCCESS: if test_result.status != kunit_parser.TestStatus.SUCCESS:
return KunitResult(KunitStatus.TEST_FAILURE, test_result, return KunitResult(KunitStatus.TEST_FAILURE, test_result,
parse_end - parse_start) parse_end - parse_start)
...@@ -151,7 +162,9 @@ def run_tests(linux: kunit_kernel.LinuxSourceTree, ...@@ -151,7 +162,9 @@ def run_tests(linux: kunit_kernel.LinuxSourceTree,
return exec_result return exec_result
parse_request = KunitParseRequest(request.raw_output, parse_request = KunitParseRequest(request.raw_output,
exec_result.result) exec_result.result,
request.build_dir,
request.json)
parse_result = parse_tests(parse_request) parse_result = parse_tests(parse_request)
run_end = time.time() run_end = time.time()
...@@ -195,7 +208,12 @@ def add_exec_opts(parser): ...@@ -195,7 +208,12 @@ def add_exec_opts(parser):
def add_parse_opts(parser): def add_parse_opts(parser):
parser.add_argument('--raw_output', help='don\'t format output from kernel', parser.add_argument('--raw_output', help='don\'t format output from kernel',
action='store_true') action='store_true')
parser.add_argument('--json',
nargs='?',
help='Stores test results in a JSON, and either '
'prints to stdout or saves to file if a '
'filename is specified',
type=str, const='stdout', default=None)
def main(argv, linux=None): def main(argv, linux=None):
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
...@@ -237,10 +255,16 @@ def main(argv, linux=None): ...@@ -237,10 +255,16 @@ def main(argv, linux=None):
cli_args = parser.parse_args(argv) cli_args = parser.parse_args(argv)
if get_kernel_root_path():
os.chdir(get_kernel_root_path())
if cli_args.subcommand == 'run': if cli_args.subcommand == 'run':
if not os.path.exists(cli_args.build_dir): if not os.path.exists(cli_args.build_dir):
os.mkdir(cli_args.build_dir) os.mkdir(cli_args.build_dir)
if not os.path.exists(kunit_kernel.kunitconfig_path):
create_default_kunitconfig()
if not linux: if not linux:
linux = kunit_kernel.LinuxSourceTree() linux = kunit_kernel.LinuxSourceTree()
...@@ -249,15 +273,19 @@ def main(argv, linux=None): ...@@ -249,15 +273,19 @@ def main(argv, linux=None):
cli_args.jobs, cli_args.jobs,
cli_args.build_dir, cli_args.build_dir,
cli_args.alltests, cli_args.alltests,
cli_args.json,
cli_args.make_options) cli_args.make_options)
result = run_tests(linux, request) result = run_tests(linux, request)
if result.status != KunitStatus.SUCCESS: if result.status != KunitStatus.SUCCESS:
sys.exit(1) sys.exit(1)
elif cli_args.subcommand == 'config': elif cli_args.subcommand == 'config':
if cli_args.build_dir: if cli_args.build_dir and (
if not os.path.exists(cli_args.build_dir): not os.path.exists(cli_args.build_dir)):
os.mkdir(cli_args.build_dir) os.mkdir(cli_args.build_dir)
if not os.path.exists(kunit_kernel.kunitconfig_path):
create_default_kunitconfig()
if not linux: if not linux:
linux = kunit_kernel.LinuxSourceTree() linux = kunit_kernel.LinuxSourceTree()
...@@ -270,10 +298,6 @@ def main(argv, linux=None): ...@@ -270,10 +298,6 @@ def main(argv, linux=None):
if result.status != KunitStatus.SUCCESS: if result.status != KunitStatus.SUCCESS:
sys.exit(1) sys.exit(1)
elif cli_args.subcommand == 'build': elif cli_args.subcommand == 'build':
if cli_args.build_dir:
if not os.path.exists(cli_args.build_dir):
os.mkdir(cli_args.build_dir)
if not linux: if not linux:
linux = kunit_kernel.LinuxSourceTree() linux = kunit_kernel.LinuxSourceTree()
...@@ -288,10 +312,6 @@ def main(argv, linux=None): ...@@ -288,10 +312,6 @@ def main(argv, linux=None):
if result.status != KunitStatus.SUCCESS: if result.status != KunitStatus.SUCCESS:
sys.exit(1) sys.exit(1)
elif cli_args.subcommand == 'exec': elif cli_args.subcommand == 'exec':
if cli_args.build_dir:
if not os.path.exists(cli_args.build_dir):
os.mkdir(cli_args.build_dir)
if not linux: if not linux:
linux = kunit_kernel.LinuxSourceTree() linux = kunit_kernel.LinuxSourceTree()
...@@ -300,7 +320,9 @@ def main(argv, linux=None): ...@@ -300,7 +320,9 @@ def main(argv, linux=None):
cli_args.alltests) cli_args.alltests)
exec_result = exec_tests(linux, exec_request) exec_result = exec_tests(linux, exec_request)
parse_request = KunitParseRequest(cli_args.raw_output, parse_request = KunitParseRequest(cli_args.raw_output,
exec_result.result) exec_result.result,
cli_args.build_dir,
cli_args.json)
result = parse_tests(parse_request) result = parse_tests(parse_request)
kunit_parser.print_with_timestamp(( kunit_parser.print_with_timestamp((
'Elapsed time: %.3fs\n') % ( 'Elapsed time: %.3fs\n') % (
...@@ -314,7 +336,9 @@ def main(argv, linux=None): ...@@ -314,7 +336,9 @@ def main(argv, linux=None):
with open(cli_args.file, 'r') as f: with open(cli_args.file, 'r') as f:
kunit_output = f.read().splitlines() kunit_output = f.read().splitlines()
request = KunitParseRequest(cli_args.raw_output, request = KunitParseRequest(cli_args.raw_output,
kunit_output) kunit_output,
cli_args.build_dir,
cli_args.json)
result = parse_tests(request) result = parse_tests(request)
if result.status != KunitStatus.SUCCESS: if result.status != KunitStatus.SUCCESS:
sys.exit(1) sys.exit(1)
......
# SPDX-License-Identifier: GPL-2.0
#
# Generates JSON from KUnit results according to
# KernelCI spec: https://github.com/kernelci/kernelci-doc/wiki/Test-API
#
# Copyright (C) 2020, Google LLC.
# Author: Heidi Fahim <heidifahim@google.com>
import json
import os
import kunit_parser
from kunit_parser import TestStatus
def get_json_result(test_result, def_config, build_dir, json_path):
sub_groups = []
# Each test suite is mapped to a KernelCI sub_group
for test_suite in test_result.suites:
sub_group = {
"name": test_suite.name,
"arch": "UM",
"defconfig": def_config,
"build_environment": build_dir,
"test_cases": [],
"lab_name": None,
"kernel": None,
"job": None,
"git_branch": "kselftest",
}
test_cases = []
# TODO: Add attachments attribute in test_case with detailed
# failure message, see https://api.kernelci.org/schema-test-case.html#get
for case in test_suite.cases:
test_case = {"name": case.name, "status": "FAIL"}
if case.status == TestStatus.SUCCESS:
test_case["status"] = "PASS"
elif case.status == TestStatus.TEST_CRASHED:
test_case["status"] = "ERROR"
test_cases.append(test_case)
sub_group["test_cases"] = test_cases
sub_groups.append(sub_group)
test_group = {
"name": "KUnit Test Group",
"arch": "UM",
"defconfig": def_config,
"build_environment": build_dir,
"sub_groups": sub_groups,
"lab_name": None,
"kernel": None,
"job": None,
"git_branch": "kselftest",
}
json_obj = json.dumps(test_group, indent=4)
if json_path != 'stdout':
with open(json_path, 'w') as result_path:
result_path.write(json_obj)
root = __file__.split('tools/testing/kunit/')[0]
kunit_parser.print_with_timestamp(
"Test results stored in %s" %
os.path.join(root, result_path.name))
return json_obj
...@@ -36,9 +36,9 @@ class LinuxSourceTreeOperations(object): ...@@ -36,9 +36,9 @@ class LinuxSourceTreeOperations(object):
try: try:
subprocess.check_output(['make', 'mrproper'], stderr=subprocess.STDOUT) subprocess.check_output(['make', 'mrproper'], stderr=subprocess.STDOUT)
except OSError as e: except OSError as e:
raise ConfigError('Could not call make command: ' + e) raise ConfigError('Could not call make command: ' + str(e))
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise ConfigError(e.output) raise ConfigError(e.output.decode())
def make_olddefconfig(self, build_dir, make_options): def make_olddefconfig(self, build_dir, make_options):
command = ['make', 'ARCH=um', 'olddefconfig'] command = ['make', 'ARCH=um', 'olddefconfig']
...@@ -49,22 +49,27 @@ class LinuxSourceTreeOperations(object): ...@@ -49,22 +49,27 @@ class LinuxSourceTreeOperations(object):
try: try:
subprocess.check_output(command, stderr=subprocess.STDOUT) subprocess.check_output(command, stderr=subprocess.STDOUT)
except OSError as e: except OSError as e:
raise ConfigError('Could not call make command: ' + e) raise ConfigError('Could not call make command: ' + str(e))
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise ConfigError(e.output) raise ConfigError(e.output.decode())
def make_allyesconfig(self): def make_allyesconfig(self, build_dir, make_options):
kunit_parser.print_with_timestamp( kunit_parser.print_with_timestamp(
'Enabling all CONFIGs for UML...') 'Enabling all CONFIGs for UML...')
command = ['make', 'ARCH=um', 'allyesconfig']
if make_options:
command.extend(make_options)
if build_dir:
command += ['O=' + build_dir]
process = subprocess.Popen( process = subprocess.Popen(
['make', 'ARCH=um', 'allyesconfig'], command,
stdout=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
process.wait() process.wait()
kunit_parser.print_with_timestamp( kunit_parser.print_with_timestamp(
'Disabling broken configs to run KUnit tests...') 'Disabling broken configs to run KUnit tests...')
with ExitStack() as es: with ExitStack() as es:
config = open(KCONFIG_PATH, 'a') config = open(get_kconfig_path(build_dir), 'a')
disable = open(BROKEN_ALLCONFIG_PATH, 'r').read() disable = open(BROKEN_ALLCONFIG_PATH, 'r').read()
config.write(disable) config.write(disable)
kunit_parser.print_with_timestamp( kunit_parser.print_with_timestamp(
...@@ -79,9 +84,9 @@ class LinuxSourceTreeOperations(object): ...@@ -79,9 +84,9 @@ class LinuxSourceTreeOperations(object):
try: try:
subprocess.check_output(command, stderr=subprocess.STDOUT) subprocess.check_output(command, stderr=subprocess.STDOUT)
except OSError as e: except OSError as e:
raise BuildError('Could not call execute make: ' + e) raise BuildError('Could not call execute make: ' + str(e))
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise BuildError(e.output) raise BuildError(e.output.decode())
def linux_bin(self, params, timeout, build_dir, outfile): def linux_bin(self, params, timeout, build_dir, outfile):
"""Runs the Linux UML binary. Must be named 'linux'.""" """Runs the Linux UML binary. Must be named 'linux'."""
...@@ -161,9 +166,9 @@ class LinuxSourceTree(object): ...@@ -161,9 +166,9 @@ class LinuxSourceTree(object):
return self.build_config(build_dir, make_options) return self.build_config(build_dir, make_options)
def build_um_kernel(self, alltests, jobs, build_dir, make_options): def build_um_kernel(self, alltests, jobs, build_dir, make_options):
if alltests:
self._ops.make_allyesconfig()
try: try:
if alltests:
self._ops.make_allyesconfig(build_dir, make_options)
self._ops.make_olddefconfig(build_dir, make_options) self._ops.make_olddefconfig(build_dir, make_options)
self._ops.make(jobs, build_dir, make_options) self._ops.make(jobs, build_dir, make_options)
except (ConfigError, BuildError) as e: except (ConfigError, BuildError) as e:
......
...@@ -11,11 +11,13 @@ from unittest import mock ...@@ -11,11 +11,13 @@ from unittest import mock
import tempfile, shutil # Handling test_tmpdir import tempfile, shutil # Handling test_tmpdir
import json
import os import os
import kunit_config import kunit_config
import kunit_parser import kunit_parser
import kunit_kernel import kunit_kernel
import kunit_json
import kunit import kunit
test_tmpdir = '' test_tmpdir = ''
...@@ -230,6 +232,37 @@ class KUnitParserTest(unittest.TestCase): ...@@ -230,6 +232,37 @@ class KUnitParserTest(unittest.TestCase):
result = kunit_parser.parse_run_tests(file.readlines()) result = kunit_parser.parse_run_tests(file.readlines())
self.assertEqual('kunit-resource-test', result.suites[0].name) self.assertEqual('kunit-resource-test', result.suites[0].name)
class KUnitJsonTest(unittest.TestCase):
def _json_for(self, log_file):
with(open(get_absolute_path(log_file))) as file:
test_result = kunit_parser.parse_run_tests(file)
json_obj = kunit_json.get_json_result(
test_result=test_result,
def_config='kunit_defconfig',
build_dir=None,
json_path='stdout')
return json.loads(json_obj)
def test_failed_test_json(self):
result = self._json_for(
'test_data/test_is_test_passed-failure.log')
self.assertEqual(
{'name': 'example_simple_test', 'status': 'FAIL'},
result["sub_groups"][1]["test_cases"][0])
def test_crashed_test_json(self):
result = self._json_for(
'test_data/test_is_test_passed-crash.log')
self.assertEqual(
{'name': 'example_simple_test', 'status': 'ERROR'},
result["sub_groups"][1]["test_cases"][0])
def test_no_tests_json(self):
result = self._json_for(
'test_data/test_is_test_passed-no_tests_run.log')
self.assertEqual(0, len(result['sub_groups']))
class StrContains(str): class StrContains(str):
def __eq__(self, other): def __eq__(self, other):
return self in other return self in other
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment