cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

conftest.py (10614B)


      1# SPDX-License-Identifier: GPL-2.0
      2#
      3# Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com>
      4#
      5
      6"""
      7Kconfig unit testing framework.
      8
      9This provides fixture functions commonly used from test files.
     10"""
     11
     12import os
     13import pytest
     14import shutil
     15import subprocess
     16import tempfile
     17
     18CONF_PATH = os.path.abspath(os.path.join('scripts', 'kconfig', 'conf'))
     19
     20
     21class Conf:
     22    """Kconfig runner and result checker.
     23
     24    This class provides methods to run text-based interface of Kconfig
     25    (scripts/kconfig/conf) and retrieve the resulted configuration,
     26    stdout, and stderr.  It also provides methods to compare those
     27    results with expectations.
     28    """
     29
     30    def __init__(self, request):
     31        """Create a new Conf instance.
     32
     33        request: object to introspect the requesting test module
     34        """
     35        # the directory of the test being run
     36        self._test_dir = os.path.dirname(str(request.fspath))
     37
     38    # runners
     39    def _run_conf(self, mode, dot_config=None, out_file='.config',
     40                  interactive=False, in_keys=None, extra_env={}):
     41        """Run text-based Kconfig executable and save the result.
     42
     43        mode: input mode option (--oldaskconfig, --defconfig=<file> etc.)
     44        dot_config: .config file to use for configuration base
     45        out_file: file name to contain the output config data
     46        interactive: flag to specify the interactive mode
     47        in_keys: key inputs for interactive modes
     48        extra_env: additional environments
     49        returncode: exit status of the Kconfig executable
     50        """
     51        command = [CONF_PATH, mode, 'Kconfig']
     52
     53        # Override 'srctree' environment to make the test as the top directory
     54        extra_env['srctree'] = self._test_dir
     55
     56        # Clear KCONFIG_DEFCONFIG_LIST to keep unit tests from being affected
     57        # by the user's environment.
     58        extra_env['KCONFIG_DEFCONFIG_LIST'] = ''
     59
     60        # Run Kconfig in a temporary directory.
     61        # This directory is automatically removed when done.
     62        with tempfile.TemporaryDirectory() as temp_dir:
     63
     64            # if .config is given, copy it to the working directory
     65            if dot_config:
     66                shutil.copyfile(os.path.join(self._test_dir, dot_config),
     67                                os.path.join(temp_dir, '.config'))
     68
     69            ps = subprocess.Popen(command,
     70                                  stdin=subprocess.PIPE,
     71                                  stdout=subprocess.PIPE,
     72                                  stderr=subprocess.PIPE,
     73                                  cwd=temp_dir,
     74                                  env=dict(os.environ, **extra_env))
     75
     76            # If input key sequence is given, feed it to stdin.
     77            if in_keys:
     78                ps.stdin.write(in_keys.encode('utf-8'))
     79
     80            while ps.poll() is None:
     81                # For interactive modes such as oldaskconfig, oldconfig,
     82                # send 'Enter' key until the program finishes.
     83                if interactive:
     84                    ps.stdin.write(b'\n')
     85
     86            self.retcode = ps.returncode
     87            self.stdout = ps.stdout.read().decode()
     88            self.stderr = ps.stderr.read().decode()
     89
     90            # Retrieve the resulted config data only when .config is supposed
     91            # to exist.  If the command fails, the .config does not exist.
     92            # 'listnewconfig' does not produce .config in the first place.
     93            if self.retcode == 0 and out_file:
     94                with open(os.path.join(temp_dir, out_file)) as f:
     95                    self.config = f.read()
     96            else:
     97                self.config = None
     98
     99        # Logging:
    100        # Pytest captures the following information by default.  In failure
    101        # of tests, the captured log will be displayed.  This will be useful to
    102        # figure out what has happened.
    103
    104        print("[command]\n{}\n".format(' '.join(command)))
    105
    106        print("[retcode]\n{}\n".format(self.retcode))
    107
    108        print("[stdout]")
    109        print(self.stdout)
    110
    111        print("[stderr]")
    112        print(self.stderr)
    113
    114        if self.config is not None:
    115            print("[output for '{}']".format(out_file))
    116            print(self.config)
    117
    118        return self.retcode
    119
    120    def oldaskconfig(self, dot_config=None, in_keys=None):
    121        """Run oldaskconfig.
    122
    123        dot_config: .config file to use for configuration base (optional)
    124        in_key: key inputs (optional)
    125        returncode: exit status of the Kconfig executable
    126        """
    127        return self._run_conf('--oldaskconfig', dot_config=dot_config,
    128                              interactive=True, in_keys=in_keys)
    129
    130    def oldconfig(self, dot_config=None, in_keys=None):
    131        """Run oldconfig.
    132
    133        dot_config: .config file to use for configuration base (optional)
    134        in_key: key inputs (optional)
    135        returncode: exit status of the Kconfig executable
    136        """
    137        return self._run_conf('--oldconfig', dot_config=dot_config,
    138                              interactive=True, in_keys=in_keys)
    139
    140    def olddefconfig(self, dot_config=None):
    141        """Run olddefconfig.
    142
    143        dot_config: .config file to use for configuration base (optional)
    144        returncode: exit status of the Kconfig executable
    145        """
    146        return self._run_conf('--olddefconfig', dot_config=dot_config)
    147
    148    def defconfig(self, defconfig):
    149        """Run defconfig.
    150
    151        defconfig: defconfig file for input
    152        returncode: exit status of the Kconfig executable
    153        """
    154        defconfig_path = os.path.join(self._test_dir, defconfig)
    155        return self._run_conf('--defconfig={}'.format(defconfig_path))
    156
    157    def _allconfig(self, mode, all_config):
    158        if all_config:
    159            all_config_path = os.path.join(self._test_dir, all_config)
    160            extra_env = {'KCONFIG_ALLCONFIG': all_config_path}
    161        else:
    162            extra_env = {}
    163
    164        return self._run_conf('--{}config'.format(mode), extra_env=extra_env)
    165
    166    def allyesconfig(self, all_config=None):
    167        """Run allyesconfig.
    168
    169        all_config: fragment config file for KCONFIG_ALLCONFIG (optional)
    170        returncode: exit status of the Kconfig executable
    171        """
    172        return self._allconfig('allyes', all_config)
    173
    174    def allmodconfig(self, all_config=None):
    175        """Run allmodconfig.
    176
    177        all_config: fragment config file for KCONFIG_ALLCONFIG (optional)
    178        returncode: exit status of the Kconfig executable
    179        """
    180        return self._allconfig('allmod', all_config)
    181
    182    def allnoconfig(self, all_config=None):
    183        """Run allnoconfig.
    184
    185        all_config: fragment config file for KCONFIG_ALLCONFIG (optional)
    186        returncode: exit status of the Kconfig executable
    187        """
    188        return self._allconfig('allno', all_config)
    189
    190    def alldefconfig(self, all_config=None):
    191        """Run alldefconfig.
    192
    193        all_config: fragment config file for KCONFIG_ALLCONFIG (optional)
    194        returncode: exit status of the Kconfig executable
    195        """
    196        return self._allconfig('alldef', all_config)
    197
    198    def randconfig(self, all_config=None):
    199        """Run randconfig.
    200
    201        all_config: fragment config file for KCONFIG_ALLCONFIG (optional)
    202        returncode: exit status of the Kconfig executable
    203        """
    204        return self._allconfig('rand', all_config)
    205
    206    def savedefconfig(self, dot_config):
    207        """Run savedefconfig.
    208
    209        dot_config: .config file for input
    210        returncode: exit status of the Kconfig executable
    211        """
    212        return self._run_conf('--savedefconfig', out_file='defconfig')
    213
    214    def listnewconfig(self, dot_config=None):
    215        """Run listnewconfig.
    216
    217        dot_config: .config file to use for configuration base (optional)
    218        returncode: exit status of the Kconfig executable
    219        """
    220        return self._run_conf('--listnewconfig', dot_config=dot_config,
    221                              out_file=None)
    222
    223    # checkers
    224    def _read_and_compare(self, compare, expected):
    225        """Compare the result with expectation.
    226
    227        compare: function to compare the result with expectation
    228        expected: file that contains the expected data
    229        """
    230        with open(os.path.join(self._test_dir, expected)) as f:
    231            expected_data = f.read()
    232        return compare(self, expected_data)
    233
    234    def _contains(self, attr, expected):
    235        return self._read_and_compare(
    236                                    lambda s, e: getattr(s, attr).find(e) >= 0,
    237                                    expected)
    238
    239    def _matches(self, attr, expected):
    240        return self._read_and_compare(lambda s, e: getattr(s, attr) == e,
    241                                      expected)
    242
    243    def config_contains(self, expected):
    244        """Check if resulted configuration contains expected data.
    245
    246        expected: file that contains the expected data
    247        returncode: True if result contains the expected data, False otherwise
    248        """
    249        return self._contains('config', expected)
    250
    251    def config_matches(self, expected):
    252        """Check if resulted configuration exactly matches expected data.
    253
    254        expected: file that contains the expected data
    255        returncode: True if result matches the expected data, False otherwise
    256        """
    257        return self._matches('config', expected)
    258
    259    def stdout_contains(self, expected):
    260        """Check if resulted stdout contains expected data.
    261
    262        expected: file that contains the expected data
    263        returncode: True if result contains the expected data, False otherwise
    264        """
    265        return self._contains('stdout', expected)
    266
    267    def stdout_matches(self, expected):
    268        """Check if resulted stdout exactly matches expected data.
    269
    270        expected: file that contains the expected data
    271        returncode: True if result matches the expected data, False otherwise
    272        """
    273        return self._matches('stdout', expected)
    274
    275    def stderr_contains(self, expected):
    276        """Check if resulted stderr contains expected data.
    277
    278        expected: file that contains the expected data
    279        returncode: True if result contains the expected data, False otherwise
    280        """
    281        return self._contains('stderr', expected)
    282
    283    def stderr_matches(self, expected):
    284        """Check if resulted stderr exactly matches expected data.
    285
    286        expected: file that contains the expected data
    287        returncode: True if result matches the expected data, False otherwise
    288        """
    289        return self._matches('stderr', expected)
    290
    291
    292@pytest.fixture(scope="module")
    293def conf(request):
    294    """Create a Conf instance and provide it to test functions."""
    295    return Conf(request)