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