Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Heidi Fahim <heidifahim@google.com>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>

authored by

Heidi Fahim and committed by
Shuah Khan
021ed9f5 afc63da6

+115 -36
+41
tools/testing/kunit/configs/broken_on_uml.config
··· 1 + # These are currently broken on UML and prevent allyesconfig from building 2 + # CONFIG_STATIC_LINK is not set 3 + # CONFIG_UML_NET_VECTOR is not set 4 + # CONFIG_UML_NET_VDE is not set 5 + # CONFIG_UML_NET_PCAP is not set 6 + # CONFIG_NET_PTP_CLASSIFY is not set 7 + # CONFIG_IP_VS is not set 8 + # CONFIG_BRIDGE_EBT_BROUTE is not set 9 + # CONFIG_BRIDGE_EBT_T_FILTER is not set 10 + # CONFIG_BRIDGE_EBT_T_NAT is not set 11 + # CONFIG_MTD_NAND_CADENCE is not set 12 + # CONFIG_MTD_NAND_NANDSIM is not set 13 + # CONFIG_BLK_DEV_NULL_BLK is not set 14 + # CONFIG_BLK_DEV_RAM is not set 15 + # CONFIG_SCSI_DEBUG is not set 16 + # CONFIG_NET_VENDOR_XILINX is not set 17 + # CONFIG_NULL_TTY is not set 18 + # CONFIG_PTP_1588_CLOCK is not set 19 + # CONFIG_PINCTRL_EQUILIBRIUM is not set 20 + # CONFIG_DMABUF_SELFTESTS is not set 21 + # CONFIG_COMEDI is not set 22 + # CONFIG_XIL_AXIS_FIFO is not set 23 + # CONFIG_EXFAT_FS is not set 24 + # CONFIG_STM_DUMMY is not set 25 + # CONFIG_FSI_MASTER_ASPEED is not set 26 + # CONFIG_JFS_FS is not set 27 + # CONFIG_UBIFS_FS is not set 28 + # CONFIG_CRAMFS is not set 29 + # CONFIG_CRYPTO_DEV_SAFEXCEL is not set 30 + # CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set 31 + # CONFIG_KCOV is not set 32 + # CONFIG_LKDTM is not set 33 + # CONFIG_REED_SOLOMON_TEST is not set 34 + # CONFIG_TEST_RHASHTABLE is not set 35 + # CONFIG_TEST_MEMINIT is not set 36 + # CONFIG_NETWORK_PHY_TIMESTAMPING is not set 37 + # CONFIG_DEBUG_INFO_BTF is not set 38 + # CONFIG_PTP_1588_CLOCK_INES is not set 39 + # CONFIG_QCOM_CPR is not set 40 + # CONFIG_RESET_BRCMSTB_RESCAL is not set 41 + # CONFIG_RESET_INTEL_GW is not set
+18 -12
tools/testing/kunit/kunit.py
··· 22 22 23 23 KunitResult = namedtuple('KunitResult', ['status','result']) 24 24 25 - KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', 'build_dir', 'defconfig']) 25 + KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', 26 + 'build_dir', 'defconfig', 27 + 'alltests']) 26 28 27 29 KernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0] 28 30 ··· 57 55 kunit_parser.print_with_timestamp('Building KUnit Kernel ...') 58 56 59 57 build_start = time.time() 60 - success = linux.build_um_kernel(request.jobs, request.build_dir) 58 + success = linux.build_um_kernel(request.alltests, 59 + request.jobs, 60 + request.build_dir) 61 61 build_end = time.time() 62 62 if not success: 63 63 return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel') 64 64 65 65 kunit_parser.print_with_timestamp('Starting KUnit Kernel ...') 66 66 test_start = time.time() 67 - 68 - test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS, 69 - [], 70 - 'Tests not Parsed.') 67 + kunit_output = linux.run_kernel( 68 + timeout=None if request.alltests else request.timeout, 69 + build_dir=request.build_dir) 71 70 if request.raw_output: 72 - kunit_parser.raw_output( 73 - linux.run_kernel(timeout=request.timeout, 74 - build_dir=request.build_dir)) 71 + raw_output = kunit_parser.raw_output(kunit_output) 72 + isolated = list(kunit_parser.isolate_kunit_output(raw_output)) 73 + test_result = kunit_parser.parse_test_result(isolated) 75 74 else: 76 - kunit_output = linux.run_kernel(timeout=request.timeout, 77 - build_dir=request.build_dir) 78 75 test_result = kunit_parser.parse_run_tests(kunit_output) 79 76 test_end = time.time() 80 77 ··· 121 120 help='Uses a default .kunitconfig.', 122 121 action='store_true') 123 122 123 + run_parser.add_argument('--alltests', 124 + help='Run all KUnit tests through allyesconfig', 125 + action='store_true') 126 + 124 127 cli_args = parser.parse_args(argv) 125 128 126 129 if cli_args.subcommand == 'run': ··· 148 143 cli_args.timeout, 149 144 cli_args.jobs, 150 145 cli_args.build_dir, 151 - cli_args.defconfig) 146 + cli_args.defconfig, 147 + cli_args.alltests) 152 148 result = run_tests(linux, request) 153 149 if result.status != KunitStatus.SUCCESS: 154 150 sys.exit(1)
+45 -17
tools/testing/kunit/kunit_kernel.py
··· 10 10 import logging 11 11 import subprocess 12 12 import os 13 + import signal 14 + 15 + from contextlib import ExitStack 13 16 14 17 import kunit_config 18 + import kunit_parser 15 19 16 20 KCONFIG_PATH = '.config' 17 21 kunitconfig_path = '.kunitconfig' 22 + BROKEN_ALLCONFIG_PATH = 'tools/testing/kunit/configs/broken_on_uml.config' 18 23 19 24 class ConfigError(Exception): 20 25 """Represents an error trying to configure the Linux kernel.""" ··· 45 40 if build_dir: 46 41 command += ['O=' + build_dir] 47 42 try: 48 - subprocess.check_output(command) 43 + subprocess.check_output(command, stderr=subprocess.PIPE) 49 44 except OSError as e: 50 45 raise ConfigError('Could not call make command: ' + e) 51 46 except subprocess.CalledProcessError as e: 52 47 raise ConfigError(e.output) 48 + 49 + def make_allyesconfig(self): 50 + kunit_parser.print_with_timestamp( 51 + 'Enabling all CONFIGs for UML...') 52 + process = subprocess.Popen( 53 + ['make', 'ARCH=um', 'allyesconfig'], 54 + stdout=subprocess.DEVNULL, 55 + stderr=subprocess.STDOUT) 56 + process.wait() 57 + kunit_parser.print_with_timestamp( 58 + 'Disabling broken configs to run KUnit tests...') 59 + with ExitStack() as es: 60 + config = open(KCONFIG_PATH, 'a') 61 + disable = open(BROKEN_ALLCONFIG_PATH, 'r').read() 62 + config.write(disable) 63 + kunit_parser.print_with_timestamp( 64 + 'Starting Kernel with all configs takes a few minutes...') 53 65 54 66 def make(self, jobs, build_dir): 55 67 command = ['make', 'ARCH=um', '--jobs=' + str(jobs)] ··· 79 57 except subprocess.CalledProcessError as e: 80 58 raise BuildError(e.output) 81 59 82 - def linux_bin(self, params, timeout, build_dir): 60 + def linux_bin(self, params, timeout, build_dir, outfile): 83 61 """Runs the Linux UML binary. Must be named 'linux'.""" 84 62 linux_bin = './linux' 85 63 if build_dir: 86 64 linux_bin = os.path.join(build_dir, 'linux') 87 - process = subprocess.Popen( 88 - [linux_bin] + params, 89 - stdin=subprocess.PIPE, 90 - stdout=subprocess.PIPE, 91 - stderr=subprocess.PIPE) 92 - process.wait(timeout=timeout) 93 - return process 65 + with open(outfile, 'w') as output: 66 + process = subprocess.Popen([linux_bin] + params, 67 + stdout=output, 68 + stderr=subprocess.STDOUT) 69 + process.wait(timeout) 94 70 95 71 96 72 def get_kconfig_path(build_dir): ··· 104 84 self._kconfig = kunit_config.Kconfig() 105 85 self._kconfig.read_from_file(kunitconfig_path) 106 86 self._ops = LinuxSourceTreeOperations() 87 + signal.signal(signal.SIGINT, self.signal_handler) 107 88 108 89 def clean(self): 109 90 try: ··· 156 135 print('Generating .config ...') 157 136 return self.build_config(build_dir) 158 137 159 - def build_um_kernel(self, jobs, build_dir): 138 + def build_um_kernel(self, alltests, jobs, build_dir): 139 + if alltests: 140 + self._ops.make_allyesconfig() 160 141 try: 161 142 self._ops.make_olddefconfig(build_dir) 162 143 self._ops.make(jobs, build_dir) ··· 167 144 return False 168 145 return self.validate_config(build_dir) 169 146 170 - def run_kernel(self, args=[], timeout=None, build_dir=''): 171 - args.extend(['mem=256M']) 172 - process = self._ops.linux_bin(args, timeout, build_dir) 173 - with open(os.path.join(build_dir, 'test.log'), 'w') as f: 174 - for line in process.stdout: 175 - f.write(line.rstrip().decode('ascii') + '\n') 176 - yield line.rstrip().decode('ascii') 147 + def run_kernel(self, args=[], build_dir='', timeout=None): 148 + args.extend(['mem=1G']) 149 + outfile = 'test.log' 150 + self._ops.linux_bin(args, timeout, build_dir, outfile) 151 + subprocess.call(['stty', 'sane']) 152 + with open(outfile, 'r') as file: 153 + for line in file: 154 + yield line 155 + 156 + def signal_handler(self, sig, frame): 157 + logging.error('Build interruption occurred. Cleaning console.') 158 + subprocess.call(['stty', 'sane'])
+1
tools/testing/kunit/kunit_parser.py
··· 65 65 def raw_output(kernel_output): 66 66 for line in kernel_output: 67 67 print(line) 68 + yield line 68 69 69 70 DIVIDER = '=' * 60 70 71
+10 -7
tools/testing/kunit/kunit_tool_test.py
··· 243 243 kunit.main(['run'], self.linux_source_mock) 244 244 assert self.linux_source_mock.build_reconfig.call_count == 1 245 245 assert self.linux_source_mock.run_kernel.call_count == 1 246 - self.linux_source_mock.run_kernel.assert_called_once_with(build_dir='', timeout=300) 246 + self.linux_source_mock.run_kernel.assert_called_once_with( 247 + build_dir='', timeout=300) 247 248 self.print_mock.assert_any_call(StrContains('Testing complete.')) 248 249 249 250 def test_run_passes_args_fail(self): ··· 259 258 260 259 def test_run_raw_output(self): 261 260 self.linux_source_mock.run_kernel = mock.Mock(return_value=[]) 262 - kunit.main(['run', '--raw_output'], self.linux_source_mock) 261 + with self.assertRaises(SystemExit) as e: 262 + kunit.main(['run', '--raw_output'], self.linux_source_mock) 263 + assert type(e.exception) == SystemExit 264 + assert e.exception.code == 1 263 265 assert self.linux_source_mock.build_reconfig.call_count == 1 264 266 assert self.linux_source_mock.run_kernel.call_count == 1 265 - for kall in self.print_mock.call_args_list: 266 - assert kall != mock.call(StrContains('Testing complete.')) 267 - assert kall != mock.call(StrContains(' 0 tests run')) 268 267 269 268 def test_run_timeout(self): 270 269 timeout = 3453 271 270 kunit.main(['run', '--timeout', str(timeout)], self.linux_source_mock) 272 271 assert self.linux_source_mock.build_reconfig.call_count == 1 273 - self.linux_source_mock.run_kernel.assert_called_once_with(build_dir='', timeout=timeout) 272 + self.linux_source_mock.run_kernel.assert_called_once_with( 273 + build_dir='', timeout=timeout) 274 274 self.print_mock.assert_any_call(StrContains('Testing complete.')) 275 275 276 276 def test_run_builddir(self): 277 277 build_dir = '.kunit' 278 278 kunit.main(['run', '--build_dir', build_dir], self.linux_source_mock) 279 279 assert self.linux_source_mock.build_reconfig.call_count == 1 280 - self.linux_source_mock.run_kernel.assert_called_once_with(build_dir=build_dir, timeout=300) 280 + self.linux_source_mock.run_kernel.assert_called_once_with( 281 + build_dir=build_dir, timeout=300) 281 282 self.print_mock.assert_any_call(StrContains('Testing complete.')) 282 283 283 284 if __name__ == '__main__':