Commit 021ed9f5 authored by Heidi Fahim's avatar Heidi Fahim Committed by Shuah Khan

kunit: Run all KUnit tests through allyesconfig

Implemented the functionality to run all KUnit tests through kunit_tool
by specifying an --alltests flag, which builds UML with allyesconfig
enabled, and consequently runs every KUnit test. A new function was
added to kunit_kernel: make_allyesconfig.
Firstly, if --alltests is specified, kunit.py triggers build_um_kernel
which call make_allyesconfig. This function calls the make command,
disables the broken configs that would otherwise prevent UML from
building, then starts the kernel with all possible configurations
enabled. All stdout and stderr is sent to test.log and read from there
then fed through kunit_parser to parse the tests to the user. Also added
a signal_handler in case kunit is interrupted while running.
Tested: Run under different conditions such as testing with
--raw_output, testing program interrupt then immediately running kunit
again without --alltests and making sure to clean the console.
Signed-off-by: default avatarHeidi Fahim <heidifahim@google.com>
Reviewed-by: default avatarBrendan Higgins <brendanhiggins@google.com>
Signed-off-by: default avatarShuah Khan <skhan@linuxfoundation.org>
parent afc63da6
# These are currently broken on UML and prevent allyesconfig from building
# CONFIG_STATIC_LINK is not set
# CONFIG_UML_NET_VECTOR is not set
# CONFIG_UML_NET_VDE is not set
# CONFIG_UML_NET_PCAP is not set
# CONFIG_NET_PTP_CLASSIFY is not set
# CONFIG_IP_VS is not set
# CONFIG_BRIDGE_EBT_BROUTE is not set
# CONFIG_BRIDGE_EBT_T_FILTER is not set
# CONFIG_BRIDGE_EBT_T_NAT is not set
# CONFIG_MTD_NAND_CADENCE is not set
# CONFIG_MTD_NAND_NANDSIM is not set
# CONFIG_BLK_DEV_NULL_BLK is not set
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_SCSI_DEBUG is not set
# CONFIG_NET_VENDOR_XILINX is not set
# CONFIG_NULL_TTY is not set
# CONFIG_PTP_1588_CLOCK is not set
# CONFIG_PINCTRL_EQUILIBRIUM is not set
# CONFIG_DMABUF_SELFTESTS is not set
# CONFIG_COMEDI is not set
# CONFIG_XIL_AXIS_FIFO is not set
# CONFIG_EXFAT_FS is not set
# CONFIG_STM_DUMMY is not set
# CONFIG_FSI_MASTER_ASPEED is not set
# CONFIG_JFS_FS is not set
# CONFIG_UBIFS_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_CRYPTO_DEV_SAFEXCEL is not set
# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set
# CONFIG_KCOV is not set
# CONFIG_LKDTM is not set
# CONFIG_REED_SOLOMON_TEST is not set
# CONFIG_TEST_RHASHTABLE is not set
# CONFIG_TEST_MEMINIT is not set
# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
# CONFIG_DEBUG_INFO_BTF is not set
# CONFIG_PTP_1588_CLOCK_INES is not set
# CONFIG_QCOM_CPR is not set
# CONFIG_RESET_BRCMSTB_RESCAL is not set
# CONFIG_RESET_INTEL_GW is not set
...@@ -22,7 +22,9 @@ import kunit_parser ...@@ -22,7 +22,9 @@ import kunit_parser
KunitResult = namedtuple('KunitResult', ['status','result']) KunitResult = namedtuple('KunitResult', ['status','result'])
KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', 'build_dir', 'defconfig']) KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs',
'build_dir', 'defconfig',
'alltests'])
KernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0] KernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0]
...@@ -55,24 +57,23 @@ def run_tests(linux: kunit_kernel.LinuxSourceTree, ...@@ -55,24 +57,23 @@ def run_tests(linux: kunit_kernel.LinuxSourceTree,
kunit_parser.print_with_timestamp('Building KUnit Kernel ...') kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
build_start = time.time() build_start = time.time()
success = linux.build_um_kernel(request.jobs, request.build_dir) success = linux.build_um_kernel(request.alltests,
request.jobs,
request.build_dir)
build_end = time.time() build_end = time.time()
if not success: if not success:
return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel') return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel')
kunit_parser.print_with_timestamp('Starting KUnit Kernel ...') kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
test_start = time.time() test_start = time.time()
kunit_output = linux.run_kernel(
test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS, timeout=None if request.alltests else request.timeout,
[], build_dir=request.build_dir)
'Tests not Parsed.')
if request.raw_output: if request.raw_output:
kunit_parser.raw_output( raw_output = kunit_parser.raw_output(kunit_output)
linux.run_kernel(timeout=request.timeout, isolated = list(kunit_parser.isolate_kunit_output(raw_output))
build_dir=request.build_dir)) test_result = kunit_parser.parse_test_result(isolated)
else: else:
kunit_output = linux.run_kernel(timeout=request.timeout,
build_dir=request.build_dir)
test_result = kunit_parser.parse_run_tests(kunit_output) test_result = kunit_parser.parse_run_tests(kunit_output)
test_end = time.time() test_end = time.time()
...@@ -120,6 +121,10 @@ def main(argv, linux=None): ...@@ -120,6 +121,10 @@ def main(argv, linux=None):
help='Uses a default .kunitconfig.', help='Uses a default .kunitconfig.',
action='store_true') action='store_true')
run_parser.add_argument('--alltests',
help='Run all KUnit tests through allyesconfig',
action='store_true')
cli_args = parser.parse_args(argv) cli_args = parser.parse_args(argv)
if cli_args.subcommand == 'run': if cli_args.subcommand == 'run':
...@@ -143,7 +148,8 @@ def main(argv, linux=None): ...@@ -143,7 +148,8 @@ def main(argv, linux=None):
cli_args.timeout, cli_args.timeout,
cli_args.jobs, cli_args.jobs,
cli_args.build_dir, cli_args.build_dir,
cli_args.defconfig) cli_args.defconfig,
cli_args.alltests)
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)
......
...@@ -10,11 +10,16 @@ ...@@ -10,11 +10,16 @@
import logging import logging
import subprocess import subprocess
import os import os
import signal
from contextlib import ExitStack
import kunit_config import kunit_config
import kunit_parser
KCONFIG_PATH = '.config' KCONFIG_PATH = '.config'
kunitconfig_path = '.kunitconfig' kunitconfig_path = '.kunitconfig'
BROKEN_ALLCONFIG_PATH = 'tools/testing/kunit/configs/broken_on_uml.config'
class ConfigError(Exception): class ConfigError(Exception):
"""Represents an error trying to configure the Linux kernel.""" """Represents an error trying to configure the Linux kernel."""
...@@ -40,12 +45,29 @@ class LinuxSourceTreeOperations(object): ...@@ -40,12 +45,29 @@ class LinuxSourceTreeOperations(object):
if build_dir: if build_dir:
command += ['O=' + build_dir] command += ['O=' + build_dir]
try: try:
subprocess.check_output(command) subprocess.check_output(command, stderr=subprocess.PIPE)
except OSError as e: except OSError as e:
raise ConfigError('Could not call make command: ' + e) raise ConfigError('Could not call make command: ' + e)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise ConfigError(e.output) raise ConfigError(e.output)
def make_allyesconfig(self):
kunit_parser.print_with_timestamp(
'Enabling all CONFIGs for UML...')
process = subprocess.Popen(
['make', 'ARCH=um', 'allyesconfig'],
stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT)
process.wait()
kunit_parser.print_with_timestamp(
'Disabling broken configs to run KUnit tests...')
with ExitStack() as es:
config = open(KCONFIG_PATH, 'a')
disable = open(BROKEN_ALLCONFIG_PATH, 'r').read()
config.write(disable)
kunit_parser.print_with_timestamp(
'Starting Kernel with all configs takes a few minutes...')
def make(self, jobs, build_dir): def make(self, jobs, build_dir):
command = ['make', 'ARCH=um', '--jobs=' + str(jobs)] command = ['make', 'ARCH=um', '--jobs=' + str(jobs)]
if build_dir: if build_dir:
...@@ -57,18 +79,16 @@ class LinuxSourceTreeOperations(object): ...@@ -57,18 +79,16 @@ class LinuxSourceTreeOperations(object):
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise BuildError(e.output) raise BuildError(e.output)
def linux_bin(self, params, timeout, build_dir): 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'."""
linux_bin = './linux' linux_bin = './linux'
if build_dir: if build_dir:
linux_bin = os.path.join(build_dir, 'linux') linux_bin = os.path.join(build_dir, 'linux')
process = subprocess.Popen( with open(outfile, 'w') as output:
[linux_bin] + params, process = subprocess.Popen([linux_bin] + params,
stdin=subprocess.PIPE, stdout=output,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stderr=subprocess.PIPE) process.wait(timeout)
process.wait(timeout=timeout)
return process
def get_kconfig_path(build_dir): def get_kconfig_path(build_dir):
...@@ -84,6 +104,7 @@ class LinuxSourceTree(object): ...@@ -84,6 +104,7 @@ class LinuxSourceTree(object):
self._kconfig = kunit_config.Kconfig() self._kconfig = kunit_config.Kconfig()
self._kconfig.read_from_file(kunitconfig_path) self._kconfig.read_from_file(kunitconfig_path)
self._ops = LinuxSourceTreeOperations() self._ops = LinuxSourceTreeOperations()
signal.signal(signal.SIGINT, self.signal_handler)
def clean(self): def clean(self):
try: try:
...@@ -135,7 +156,9 @@ class LinuxSourceTree(object): ...@@ -135,7 +156,9 @@ class LinuxSourceTree(object):
print('Generating .config ...') print('Generating .config ...')
return self.build_config(build_dir) return self.build_config(build_dir)
def build_um_kernel(self, jobs, build_dir): def build_um_kernel(self, alltests, jobs, build_dir):
if alltests:
self._ops.make_allyesconfig()
try: try:
self._ops.make_olddefconfig(build_dir) self._ops.make_olddefconfig(build_dir)
self._ops.make(jobs, build_dir) self._ops.make(jobs, build_dir)
...@@ -144,10 +167,15 @@ class LinuxSourceTree(object): ...@@ -144,10 +167,15 @@ class LinuxSourceTree(object):
return False return False
return self.validate_config(build_dir) return self.validate_config(build_dir)
def run_kernel(self, args=[], timeout=None, build_dir=''): def run_kernel(self, args=[], build_dir='', timeout=None):
args.extend(['mem=256M']) args.extend(['mem=1G'])
process = self._ops.linux_bin(args, timeout, build_dir) outfile = 'test.log'
with open(os.path.join(build_dir, 'test.log'), 'w') as f: self._ops.linux_bin(args, timeout, build_dir, outfile)
for line in process.stdout: subprocess.call(['stty', 'sane'])
f.write(line.rstrip().decode('ascii') + '\n') with open(outfile, 'r') as file:
yield line.rstrip().decode('ascii') for line in file:
yield line
def signal_handler(self, sig, frame):
logging.error('Build interruption occurred. Cleaning console.')
subprocess.call(['stty', 'sane'])
...@@ -65,6 +65,7 @@ def isolate_kunit_output(kernel_output): ...@@ -65,6 +65,7 @@ def isolate_kunit_output(kernel_output):
def raw_output(kernel_output): def raw_output(kernel_output):
for line in kernel_output: for line in kernel_output:
print(line) print(line)
yield line
DIVIDER = '=' * 60 DIVIDER = '=' * 60
......
...@@ -243,7 +243,8 @@ class KUnitMainTest(unittest.TestCase): ...@@ -243,7 +243,8 @@ class KUnitMainTest(unittest.TestCase):
kunit.main(['run'], self.linux_source_mock) kunit.main(['run'], self.linux_source_mock)
assert self.linux_source_mock.build_reconfig.call_count == 1 assert self.linux_source_mock.build_reconfig.call_count == 1
assert self.linux_source_mock.run_kernel.call_count == 1 assert self.linux_source_mock.run_kernel.call_count == 1
self.linux_source_mock.run_kernel.assert_called_once_with(build_dir='', timeout=300) self.linux_source_mock.run_kernel.assert_called_once_with(
build_dir='', timeout=300)
self.print_mock.assert_any_call(StrContains('Testing complete.')) self.print_mock.assert_any_call(StrContains('Testing complete.'))
def test_run_passes_args_fail(self): def test_run_passes_args_fail(self):
...@@ -258,25 +259,27 @@ class KUnitMainTest(unittest.TestCase): ...@@ -258,25 +259,27 @@ class KUnitMainTest(unittest.TestCase):
def test_run_raw_output(self): def test_run_raw_output(self):
self.linux_source_mock.run_kernel = mock.Mock(return_value=[]) self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
kunit.main(['run', '--raw_output'], self.linux_source_mock) with self.assertRaises(SystemExit) as e:
kunit.main(['run', '--raw_output'], self.linux_source_mock)
assert type(e.exception) == SystemExit
assert e.exception.code == 1
assert self.linux_source_mock.build_reconfig.call_count == 1 assert self.linux_source_mock.build_reconfig.call_count == 1
assert self.linux_source_mock.run_kernel.call_count == 1 assert self.linux_source_mock.run_kernel.call_count == 1
for kall in self.print_mock.call_args_list:
assert kall != mock.call(StrContains('Testing complete.'))
assert kall != mock.call(StrContains(' 0 tests run'))
def test_run_timeout(self): def test_run_timeout(self):
timeout = 3453 timeout = 3453
kunit.main(['run', '--timeout', str(timeout)], self.linux_source_mock) kunit.main(['run', '--timeout', str(timeout)], self.linux_source_mock)
assert self.linux_source_mock.build_reconfig.call_count == 1 assert self.linux_source_mock.build_reconfig.call_count == 1
self.linux_source_mock.run_kernel.assert_called_once_with(build_dir='', timeout=timeout) self.linux_source_mock.run_kernel.assert_called_once_with(
build_dir='', timeout=timeout)
self.print_mock.assert_any_call(StrContains('Testing complete.')) self.print_mock.assert_any_call(StrContains('Testing complete.'))
def test_run_builddir(self): def test_run_builddir(self):
build_dir = '.kunit' build_dir = '.kunit'
kunit.main(['run', '--build_dir', build_dir], self.linux_source_mock) kunit.main(['run', '--build_dir', build_dir], self.linux_source_mock)
assert self.linux_source_mock.build_reconfig.call_count == 1 assert self.linux_source_mock.build_reconfig.call_count == 1
self.linux_source_mock.run_kernel.assert_called_once_with(build_dir=build_dir, timeout=300) self.linux_source_mock.run_kernel.assert_called_once_with(
build_dir=build_dir, timeout=300)
self.print_mock.assert_any_call(StrContains('Testing complete.')) self.print_mock.assert_any_call(StrContains('Testing complete.'))
if __name__ == '__main__': if __name__ == '__main__':
......
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