Home | History | Annotate | Download | only in ltp
      1 #
      2 # Copyright (C) 2016 The Android Open Source Project
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #      http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 #
     16 
     17 import os
     18 import logging
     19 import itertools
     20 
     21 from vts.runners.host import const
     22 from vts.testcases.kernel.ltp import ltp_configs
     23 from vts.testcases.kernel.ltp import ltp_enums
     24 from vts.testcases.kernel.ltp import test_case
     25 from vts.testcases.kernel.ltp.configs import stable_tests
     26 from vts.testcases.kernel.ltp.configs import disabled_tests
     27 from vts.utils.python.common import filter_utils
     28 
     29 
     30 class TestCasesParser(object):
     31     """Load a ltp vts testcase definition file and parse it into a generator.
     32 
     33     Attributes:
     34         _data_path: string, the vts data path on host side
     35         _filter_func: function, a filter method that will emit exception if a test is filtered
     36         _ltp_tests_filter: list of string, filter for tests that are stable and disabled
     37     """
     38 
     39     def __init__(self, data_path, filter_func):
     40         self._data_path = data_path
     41         self._filter_func = filter_func
     42         self._ltp_tests_filter = filter_utils.Filter(
     43             stable_tests.STABLE_TESTS,
     44             disabled_tests.DISABLED_TESTS,
     45             enable_regex=True)
     46         self._ltp_tests_filter.ExpandBitness()
     47 
     48     def ValidateDefinition(self, line):
     49         """Validate a tab delimited test case definition.
     50 
     51         Will check whether the given line of definition has three parts
     52         separated by tabs.
     53         It will also trim leading and ending white spaces for each part
     54         in returned tuple (if valid).
     55 
     56         Returns:
     57             A tuple in format (test suite, test name, test command) if
     58             definition is valid. None otherwise.
     59         """
     60         items = [
     61             item.strip()
     62             for item in line.split(ltp_enums.Delimiters.TESTCASE_DEFINITION)
     63         ]
     64         if not len(items) == 3 or not items:
     65             return None
     66         else:
     67             return items
     68 
     69     def Load(self,
     70              ltp_dir,
     71              n_bit,
     72              test_filter,
     73              run_staging=False,
     74              is_low_mem=False):
     75         """Read the definition file and yields a TestCase generator.
     76 
     77         Args:
     78             ltp_dir: string, directory that contains ltp binaries and scripts
     79             n_bit: int, bitness
     80             test_filter: Filter object, test name filter from base_test
     81             run_staging: bool, whether to use staging configuration
     82             is_low_mem: bool, whether to use low memory device configuration
     83         """
     84         scenario_groups = (ltp_configs.TEST_SUITES_LOW_MEM
     85                            if is_low_mem else ltp_configs.TEST_SUITES)
     86         logging.info('LTP scenario groups: %s', scenario_groups)
     87 
     88         run_scritp = self.GenerateLtpRunScript(scenario_groups)
     89 
     90         for line in run_scritp:
     91             items = self.ValidateDefinition(line)
     92             if not items:
     93                 continue
     94 
     95             testsuite, testname, command = items
     96             if is_low_mem and testsuite.endswith(
     97                     ltp_configs.LOW_MEMORY_SCENARIO_GROUP_SUFFIX):
     98                 testsuite = testsuite[:-len(
     99                     ltp_configs.LOW_MEMORY_SCENARIO_GROUP_SUFFIX)]
    100 
    101             # Tests failed to build will have prefix "DISABLED_"
    102             if testname.startswith("DISABLED_"):
    103                 logging.info("[Parser] Skipping test case {}-{}. Reason: "
    104                              "not built".format(testsuite, testname))
    105                 continue
    106 
    107             # Some test cases contain semicolons in their commands,
    108             # and we replace them with &&
    109             command = command.replace(';', '&&')
    110 
    111             # Some test cases have hardcoded "/tmp" in the command
    112             # we replace that with ltp_configs.TMPDIR
    113             command = command.replace('/tmp', ltp_configs.TMPDIR)
    114 
    115             testcase = test_case.TestCase(
    116                 testsuite=testsuite, testname=testname, command=command)
    117 
    118             test_display_name = "{}_{}bit".format(str(testcase), n_bit)
    119 
    120             # Check runner's base_test filtering method
    121             try:
    122                 self._filter_func(test_display_name)
    123             except:
    124                 logging.info("[Parser] Skipping test case %s. Reason: "
    125                              "filtered" % testcase.fullname)
    126                 testcase.is_filtered = True
    127                 testcase.note = "filtered"
    128 
    129             # For skipping tests that are not designed or ready for Android,
    130             # check for bit specific test in disabled list as well as non-bit specific
    131             if ((self._ltp_tests_filter.IsInExcludeFilter(str(testcase)) or
    132                  self._ltp_tests_filter.IsInExcludeFilter(test_display_name)) and
    133                     not test_filter.IsInIncludeFilter(test_display_name)):
    134                 logging.info("[Parser] Skipping test case %s. Reason: "
    135                              "disabled" % testcase.fullname)
    136                 continue
    137 
    138             # For separating staging tests from stable tests
    139             if not self._ltp_tests_filter.IsInIncludeFilter(test_display_name):
    140                 if not run_staging and not test_filter.IsInIncludeFilter(
    141                         test_display_name):
    142                     # Skip staging tests in stable run
    143                     continue
    144                 else:
    145                     testcase.is_staging = True
    146                     testcase.note = "staging"
    147             else:
    148                 if run_staging:
    149                     # Skip stable tests in staging run
    150                     continue
    151 
    152             logging.info("[Parser] Adding test case %s." % testcase.fullname)
    153             yield testcase
    154 
    155     def ReadCommentedTxt(self, filepath):
    156         '''Read a lines of a file that are not commented by #.
    157 
    158         Args:
    159             filepath: string, path of file to read
    160 
    161         Returns:
    162             A set of string representing non-commented lines in given file
    163         '''
    164         if not filepath:
    165             logging.error('Invalid file path')
    166             return None
    167 
    168         with open(filepath, 'r') as f:
    169             lines_gen = (line.strip() for line in f)
    170             return set(
    171                 line for line in lines_gen
    172                 if line and not line.startswith('#'))
    173 
    174     def GenerateLtpTestCases(self, testsuite, disabled_tests_list):
    175         '''Generate test cases for each ltp test suite.
    176 
    177         Args:
    178             testsuite: string, test suite name
    179 
    180         Returns:
    181             A list of string
    182         '''
    183         testsuite_script = os.path.join(self._data_path,
    184                                         ltp_configs.LTP_RUNTEST_DIR, testsuite)
    185 
    186         result = []
    187         for line in open(testsuite_script, 'r'):
    188             line = line.strip()
    189             if not line or line.startswith('#'):
    190                 continue
    191 
    192             testname = line.split()[0]
    193             testname_prefix = ('DISABLED_'
    194                                if testname in disabled_tests_list else '')
    195             testname_modified = testname_prefix + testname
    196 
    197             result.append("\t".join(
    198                 [testsuite, testname_modified, line[len(testname):].strip()]))
    199         return result
    200 
    201     def GenerateLtpRunScript(self, scenario_groups):
    202         '''Given a scenario group generate test case script.
    203 
    204         Args:
    205             scenario_groups: list of string, name of test scenario groups to use
    206 
    207         Returns:
    208             A list of string
    209         '''
    210         disabled_tests_path = os.path.join(
    211             self._data_path, ltp_configs.LTP_DISABLED_BUILD_TESTS_CONFIG_PATH)
    212         disabled_tests_list = self.ReadCommentedTxt(disabled_tests_path)
    213 
    214         result = []
    215         for testsuite in scenario_groups:
    216             result.extend(
    217                 self.GenerateLtpTestCases(testsuite, disabled_tests_list))
    218 
    219         #TODO(yuexima): remove duplicate
    220 
    221         return result
    222