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 47 def ValidateDefinition(self, line): 48 """Validate a tab delimited test case definition. 49 50 Will check whether the given line of definition has three parts 51 separated by tabs. 52 It will also trim leading and ending white spaces for each part 53 in returned tuple (if valid). 54 55 Returns: 56 A tuple in format (test suite, test name, test command) if 57 definition is valid. None otherwise. 58 """ 59 items = [ 60 item.strip() 61 for item in line.split(ltp_enums.Delimiters.TESTCASE_DEFINITION) 62 ] 63 if not len(items) == 3 or not items: 64 return None 65 else: 66 return items 67 68 def Load(self, 69 ltp_dir, 70 n_bit, 71 test_filter, 72 run_staging=False, 73 is_low_mem=False): 74 """Read the definition file and yields a TestCase generator. 75 76 Args: 77 ltp_dir: string, directory that contains ltp binaries and scripts 78 n_bit: int, bitness 79 test_filter: Filter object, test name filter from base_test 80 run_staging: bool, whether to use staging configuration 81 is_low_mem: bool, whether to use low memory device configuration 82 """ 83 scenario_groups = (ltp_configs.TEST_SUITES_LOW_MEM 84 if is_low_mem else ltp_configs.TEST_SUITES) 85 logging.info('LTP scenario groups: %s', scenario_groups) 86 87 run_scritp = self.GenerateLtpRunScript(scenario_groups) 88 89 for line in run_scritp: 90 items = self.ValidateDefinition(line) 91 if not items: 92 continue 93 94 testsuite, testname, command = items 95 if is_low_mem and testsuite.endswith( 96 ltp_configs.LOW_MEMORY_SCENARIO_GROUP_SUFFIX): 97 testsuite = testsuite[:-len( 98 ltp_configs.LOW_MEMORY_SCENARIO_GROUP_SUFFIX)] 99 100 # Tests failed to build will have prefix "DISABLED_" 101 if testname.startswith("DISABLED_"): 102 logging.info("[Parser] Skipping test case {}-{}. Reason: " 103 "not built".format(testsuite, testname)) 104 continue 105 106 # Some test cases contain semicolons in their commands, 107 # and we replace them with && 108 command = command.replace(';', '&&') 109 110 testcase = test_case.TestCase( 111 testsuite=testsuite, testname=testname, command=command) 112 113 test_display_name = "{}_{}bit".format(str(testcase), n_bit) 114 115 # Check runner's base_test filtering method 116 try: 117 self._filter_func(test_display_name) 118 except: 119 logging.info("[Parser] Skipping test case %s. Reason: " 120 "filtered" % testcase.fullname) 121 testcase.is_filtered = True 122 testcase.note = "filtered" 123 124 # For skipping tests that are not designed or ready for Android 125 if (self._ltp_tests_filter.IsInExcludeFilter(test_display_name) and 126 not test_filter.IsInIncludeFilter(test_display_name)): 127 logging.info("[Parser] Skipping test case %s. Reason: " 128 "disabled" % testcase.fullname) 129 continue 130 131 # For separating staging tests from stable tests 132 if not self._ltp_tests_filter.IsInIncludeFilter(test_display_name): 133 if not run_staging and not test_filter.IsInIncludeFilter( 134 test_display_name): 135 # Skip staging tests in stable run 136 continue 137 else: 138 testcase.is_staging = True 139 testcase.note = "staging" 140 else: 141 if run_staging: 142 # Skip stable tests in staging run 143 continue 144 145 logging.info("[Parser] Adding test case %s." % testcase.fullname) 146 yield testcase 147 148 def ReadCommentedTxt(self, filepath): 149 '''Read a lines of a file that are not commented by #. 150 151 Args: 152 filepath: string, path of file to read 153 154 Returns: 155 A set of string representing non-commented lines in given file 156 ''' 157 if not filepath: 158 logging.error('Invalid file path') 159 return None 160 161 with open(filepath, 'r') as f: 162 lines_gen = (line.strip() for line in f) 163 return set( 164 line for line in lines_gen 165 if line and not line.startswith('#')) 166 167 def GenerateLtpTestCases(self, testsuite, disabled_tests_list): 168 '''Generate test cases for each ltp test suite. 169 170 Args: 171 testsuite: string, test suite name 172 173 Returns: 174 A list of string 175 ''' 176 testsuite_script = os.path.join(self._data_path, 177 ltp_configs.LTP_RUNTEST_DIR, testsuite) 178 179 result = [] 180 for line in open(testsuite_script, 'r'): 181 line = line.strip() 182 if not line or line.startswith('#'): 183 continue 184 185 testname = line.split()[0] 186 testname_prefix = ('DISABLED_' 187 if testname in disabled_tests_list else '') 188 testname_modified = testname_prefix + testname 189 190 result.append("\t".join( 191 [testsuite, testname_modified, line[len(testname):].strip()])) 192 return result 193 194 def GenerateLtpRunScript(self, scenario_groups): 195 '''Given a scenario group generate test case script. 196 197 Args: 198 scenario_groups: list of string, name of test scenario groups to use 199 200 Returns: 201 A list of string 202 ''' 203 disabled_tests_path = os.path.join( 204 self._data_path, ltp_configs.LTP_DISABLED_BUILD_TESTS_CONFIG_PATH) 205 disabled_tests_list = self.ReadCommentedTxt(disabled_tests_path) 206 207 result = [] 208 for testsuite in scenario_groups: 209 result.extend( 210 self.GenerateLtpTestCases(testsuite, disabled_tests_list)) 211 212 #TODO(yuexima): remove duplicate 213 214 return result 215