1# Copyright 2018, The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Suite Plan Finder class.""" 16 17import logging 18import os 19import re 20 21from atest import constants 22from atest.test_finders import test_filter_utils 23from atest.test_finders import test_finder_base 24from atest.test_finders import test_finder_utils 25from atest.test_finders import test_info 26from atest.test_runners import suite_plan_test_runner 27 28_SUITE_PLAN_NAME_RE = re.compile( 29 r'^.*\/(?P<suite>.*)-tradefed\/res\/config\/' 30 r'(?P<suite_plan_name>.*).xml$' 31) 32 33 34class SuitePlanFinder(test_finder_base.TestFinderBase): 35 """Suite Plan Finder class.""" 36 37 NAME = 'SUITE_PLAN' 38 _SUITE_PLAN_TEST_RUNNER = suite_plan_test_runner.SuitePlanTestRunner.NAME 39 40 def __init__(self, module_info=None): 41 super().__init__() 42 self.root_dir = os.environ.get(constants.ANDROID_BUILD_TOP) 43 self.mod_info = module_info 44 self.suite_plan_dirs = self._get_suite_plan_dirs() 45 46 def _get_mod_paths(self, module_name): 47 """Return the paths of the given module name.""" 48 if self.mod_info: 49 return self.mod_info.get_paths(module_name) 50 return [] 51 52 def _get_suite_plan_dirs(self): 53 """Get suite plan dirs from MODULE_INFO based on targets. 54 55 Strategy: 56 Search module-info.json using SUITE_PLANS to get all the suite 57 plan dirs. 58 59 Returns: 60 A tuple of lists of strings of suite plan dir rel to repo root. 61 None if the path can not be found in module-info.json. 62 """ 63 return [ 64 d 65 for x in constants.SUITE_PLANS 66 for d in self._get_mod_paths(x + '-tradefed') 67 if d is not None 68 ] 69 70 def _get_test_info_from_path(self, path, suite_name=None): 71 """Get the test info from the result of using regular expression 72 73 matching with the give path. 74 75 Args: 76 path: A string of the test's absolute or relative path. 77 suite_name: A string of the suite name. 78 79 Returns: 80 A populated TestInfo namedtuple if regular expression 81 matches, else None. 82 """ 83 # Don't use names that simply match the path, 84 # must be the actual name used by *TS to run the test. 85 match = _SUITE_PLAN_NAME_RE.match(path) 86 if not match: 87 logging.debug('Suite plan test outside config dir: %s', path) 88 return None 89 suite = match.group('suite') 90 suite_plan_name = match.group('suite_plan_name') 91 if suite_name: 92 if suite_plan_name != suite_name: 93 logging.debug( 94 'Input (%s) not valid suite plan name, did you mean: %s?', 95 suite_name, 96 suite_plan_name, 97 ) 98 return None 99 return test_info.TestInfo( 100 test_name=suite_plan_name, 101 test_runner=self._SUITE_PLAN_TEST_RUNNER, 102 build_targets=set([suite]), 103 suite=suite, 104 ) 105 106 def find_test_by_suite_path(self, suite_path): 107 """Find the first test info matching the given path. 108 109 Strategy: 110 If suite_path is to file --> Return TestInfo if the file 111 exists in the suite plan dirs, else return None. 112 If suite_path is to dir --> Return None 113 114 Args: 115 suite_path: A string of the path to the test's file or dir. 116 117 Returns: 118 A list of populated TestInfo namedtuple if test found, else None. 119 This is a list with at most 1 element. 120 """ 121 path, _ = test_filter_utils.split_methods(suite_path) 122 # Make sure we're looking for a config. 123 if not path.endswith('.xml'): 124 return None 125 path = os.path.realpath(path) 126 suite_plan_dir = test_finder_utils.get_int_dir_from_path( 127 path, self.suite_plan_dirs 128 ) 129 if suite_plan_dir: 130 rel_config = os.path.relpath(path, self.root_dir) 131 return [self._get_test_info_from_path(rel_config)] 132 return None 133 134 def find_test_by_suite_name(self, suite_name): 135 """Find the test for the given suite name. 136 137 Strategy: 138 If suite_name is cts --> Return TestInfo to indicate suite runner 139 to make cts and run test using cts-tradefed. 140 If suite_name is cts-common --> Return TestInfo to indicate suite 141 runner to make cts and run test using cts-tradefed if file exists 142 in the suite plan dirs, else return None. 143 144 Args: 145 suite_name: A string of suite name. 146 147 Returns: 148 A list of populated TestInfo namedtuple if suite_name matches 149 a suite in constants.SUITE_PLAN, else check if the file 150 existing in the suite plan dirs, else return None. 151 """ 152 logging.debug('Finding test by suite: %s', suite_name) 153 test_infos = [] 154 if suite_name in constants.SUITE_PLANS: 155 test_infos.append( 156 test_info.TestInfo( 157 test_name=suite_name, 158 test_runner=self._SUITE_PLAN_TEST_RUNNER, 159 build_targets=set([suite_name]), 160 suite=suite_name, 161 ) 162 ) 163 else: 164 test_files = test_finder_utils.search_integration_dirs( 165 suite_name, self.suite_plan_dirs 166 ) 167 if not test_files: 168 return None 169 for test_file in test_files: 170 _test_info = self._get_test_info_from_path(test_file, suite_name) 171 if _test_info: 172 test_infos.append(_test_info) 173 return test_infos 174