| # SPDX-License-Identifier: GPL-2.0 |
| |
| import logging |
| import subprocess |
| import os |
| |
| import kunit_config |
| |
| KCONFIG_PATH = '.config' |
| |
| class ConfigError(Exception): |
| """Represents an error trying to configure the Linux kernel.""" |
| |
| |
| class BuildError(Exception): |
| """Represents an error trying to build the Linux kernel.""" |
| |
| |
| class LinuxSourceTreeOperations(object): |
| """An abstraction over command line operations performed on a source tree.""" |
| |
| def make_mrproper(self): |
| try: |
| subprocess.check_output(['make', 'mrproper']) |
| except OSError as e: |
| raise ConfigError('Could not call make command: ' + e) |
| except subprocess.CalledProcessError as e: |
| raise ConfigError(e.output) |
| |
| def make_olddefconfig(self): |
| try: |
| subprocess.check_output(['make', 'ARCH=um', 'olddefconfig']) |
| except OSError as e: |
| raise ConfigError('Could not call make command: ' + e) |
| except subprocess.CalledProcessError as e: |
| raise ConfigError(e.output) |
| |
| def make(self): |
| try: |
| subprocess.check_output(['make', 'ARCH=um']) |
| except OSError as e: |
| raise BuildError('Could not call execute make: ' + e) |
| except subprocess.CalledProcessError as e: |
| raise BuildError(e.output) |
| |
| def linux_bin(self, params, timeout): |
| """Runs the Linux UML binary. Must be named 'linux'.""" |
| process = subprocess.Popen( |
| ['./linux'] + params, |
| stdin=subprocess.PIPE, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE) |
| timed_out = False |
| try: |
| process.wait(timeout=timeout) |
| except subprocess.TimeoutExpired: |
| process.terminate() |
| timed_out = True |
| return process, timed_out |
| |
| |
| class LinuxSourceTree(object): |
| """Represents a Linux kernel source tree with KUnit tests.""" |
| |
| def __init__(self): |
| self._kconfig = kunit_config.Kconfig() |
| self._kconfig.read_from_file('kunitconfig') |
| self._ops = LinuxSourceTreeOperations() |
| |
| def clean(self): |
| try: |
| self._ops.make_mrproper() |
| except ConfigError as e: |
| logging.error(e) |
| return False |
| return True |
| |
| def build_config(self): |
| self._kconfig.write_to_file(KCONFIG_PATH) |
| try: |
| self._ops.make_olddefconfig() |
| except ConfigError as e: |
| logging.error(e) |
| return False |
| validated_kconfig = kunit_config.Kconfig() |
| validated_kconfig.read_from_file(KCONFIG_PATH) |
| if not self._kconfig.is_subset_of(validated_kconfig): |
| logging.error('Provided Kconfig is not contained in validated .config!') |
| return False |
| return True |
| |
| def build_reconfig(self): |
| """Creates a new .config if it is not a subset of the kunitconfig.""" |
| if os.path.exists(KCONFIG_PATH): |
| existing_kconfig = kunit_config.Kconfig() |
| existing_kconfig.read_from_file(KCONFIG_PATH) |
| if not self._kconfig.is_subset_of(existing_kconfig): |
| print('Regenerating .config ...') |
| os.remove(KCONFIG_PATH) |
| return self.build_config() |
| else: |
| return True |
| else: |
| print('Generating .config ...') |
| return self.build_config() |
| |
| def build_um_kernel(self): |
| try: |
| self._ops.make_olddefconfig() |
| self._ops.make() |
| except (ConfigError, BuildError) as e: |
| logging.error(e) |
| return False |
| used_kconfig = kunit_config.Kconfig() |
| used_kconfig.read_from_file(KCONFIG_PATH) |
| if not self._kconfig.is_subset_of(used_kconfig): |
| logging.error('Provided Kconfig is not contained in final config!') |
| return False |
| return True |
| |
| def run_kernel(self, args=[], timeout=None): |
| args.extend(['mem=256M']) |
| process, timed_out = self._ops.linux_bin(args, timeout) |
| with open('test.log', 'w') as f: |
| for line in process.stdout: |
| f.write(line.rstrip().decode('ascii') + '\n') |
| yield line.rstrip().decode('ascii') |
| if timed_out: |
| f.write('Timeout Reached - Process Terminated\n') |
| yield 'Timeout Reached - Process Terminated\n' |