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 17import os 18import logging 19import itertools 20 21from vts.runners.host import const 22 23from vts.testcases.kernel.ltp import ltp_configs 24from vts.testcases.kernel.ltp import ltp_enums 25from vts.testcases.kernel.ltp import test_case 26 27 28class TestCasesParser(object): 29 """Load a ltp vts testcase definition file and parse it into a generator. 30 31 Attributes: 32 _data_path: string, the vts data path on host side 33 _filter_func: function, a filter method that will emit exception if a test is filtered 34 disabled_tests: list of string 35 staging_tests: list of string 36 """ 37 38 def __init__(self, data_path, filter_func, disabled_tests, staging_tests): 39 self._data_path = data_path 40 self._filter_func = filter_func 41 self._disabled_tests = disabled_tests 42 self._staging_tests = staging_tests 43 44 def ValidateDefinition(self, line): 45 """Validate a tab delimited test case definition. 46 47 Will check whether the given line of definition has three parts 48 separated by tabs. 49 It will also trim leading and ending white spaces for each part 50 in returned tuple (if valid). 51 52 Returns: 53 A tuple in format (test suite, test name, test command) if 54 definition is valid. None otherwise. 55 """ 56 items = [ 57 item.strip() 58 for item in line.split(ltp_enums.Delimiters.TESTCASE_DEFINITION) 59 ] 60 if not len(items) == 3 or not items: 61 return None 62 else: 63 return items 64 65 def Load(self, ltp_dir, n_bit, run_staging=False): 66 """Read the definition file and yields a TestCase generator.""" 67 run_scritp = self.GenerateLtpRunScript() 68 69 for line in run_scritp: 70 items = self.ValidateDefinition(line) 71 if not items: 72 continue 73 74 testsuite, testname, command = items 75 76 # Tests failed to build will have prefix "DISABLED_" 77 if testname.startswith("DISABLED_"): 78 logging.info("[Parser] Skipping test case {}-{}. Reason: " 79 "not built".format(testsuite, testname)) 80 continue 81 82 # Some test cases contain semicolons in their commands, 83 # and we replace them with && 84 command = command.replace(';', '&&') 85 86 testcase = test_case.TestCase( 87 testsuite=testsuite, testname=testname, command=command) 88 89 test_display_name = "{}_{}bit".format(str(testcase), n_bit) 90 91 # Check runner's base_test filtering method 92 try: 93 self._filter_func(test_display_name) 94 except: 95 logging.info("[Parser] Skipping test case %s. Reason: " 96 "filtered" % testcase.fullname) 97 testcase.is_filtered = True 98 testcase.note = "filtered" 99 100 # For skipping tests that are not designed or ready for Android 101 if test_display_name in self._disabled_tests: 102 logging.info("[Parser] Skipping test case %s. Reason: " 103 "disabled" % testcase.fullname) 104 continue 105 106 # For separating staging tests from stable tests 107 if test_display_name in self._staging_tests: 108 if not run_staging: 109 # Skip staging tests in stable run 110 continue 111 else: 112 testcase.is_staging = True 113 testcase.note = "staging" 114 else: 115 if run_staging: 116 # Skip stable tests in staging run 117 continue 118 119 logging.info("[Parser] Adding test case %s." % testcase.fullname) 120 yield testcase 121 122 def ReadCommentedTxt(self, filepath): 123 '''Read a lines of a file that are not commented by #. 124 125 Args: 126 filepath: string, path of file to read 127 128 Returns: 129 A set of string representing non-commented lines in given file 130 ''' 131 if not filepath: 132 logging.error('Invalid file path') 133 return None 134 135 with open(filepath, 'r') as f: 136 lines_gen = (line.strip() for line in f) 137 return set( 138 line for line in lines_gen 139 if line and not line.startswith('#')) 140 141 def GenerateLtpTestCases(self, testsuite, disabled_tests_list): 142 '''Generate test cases for each ltp test suite. 143 144 Args: 145 testsuite: string, test suite name 146 147 Returns: 148 A list of string 149 ''' 150 testsuite_script = os.path.join(self._data_path, 151 ltp_configs.LTP_RUNTEST_DIR, testsuite) 152 153 result = [] 154 for line in open(testsuite_script, 'r'): 155 line = line.strip() 156 if not line or line.startswith('#'): 157 continue 158 159 testname = line.split()[0] 160 testname_prefix = ('DISABLED_' 161 if testname in disabled_tests_list else '') 162 testname_modified = testname_prefix + testname 163 164 result.append("\t".join([testsuite, testname_modified, line[len( 165 testname):].strip()])) 166 return result 167 168 def GenerateLtpRunScript(self): 169 '''Given a scenario group generate test case script. 170 171 Args: 172 scenario_group: string, file path of scanerio group file 173 174 Returns: 175 A list of string 176 ''' 177 disabled_tests_path = os.path.join( 178 self._data_path, ltp_configs.LTP_DISABLED_BUILD_TESTS_CONFIG_PATH) 179 disabled_tests_list = self.ReadCommentedTxt(disabled_tests_path) 180 181 result = [] 182 for testsuite in ltp_configs.TEST_SUITES: 183 result.extend( 184 self.GenerateLtpTestCases(testsuite, disabled_tests_list)) 185 186 #TODO(yuexima): remove duplicate 187 188 return result 189