1*9e94795aSAndroid Build Coastguard Worker# Copyright 2024, The Android Open Source Project 2*9e94795aSAndroid Build Coastguard Worker# 3*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*9e94795aSAndroid Build Coastguard Worker# 7*9e94795aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*9e94795aSAndroid Build Coastguard Worker# 9*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*9e94795aSAndroid Build Coastguard Worker# limitations under the License. 14*9e94795aSAndroid Build Coastguard Worker"""Test discovery agent that uses TradeFed to discover test artifacts.""" 15*9e94795aSAndroid Build Coastguard Workerimport glob 16*9e94795aSAndroid Build Coastguard Workerimport json 17*9e94795aSAndroid Build Coastguard Workerimport logging 18*9e94795aSAndroid Build Coastguard Workerimport os 19*9e94795aSAndroid Build Coastguard Workerimport subprocess 20*9e94795aSAndroid Build Coastguard Worker 21*9e94795aSAndroid Build Coastguard Worker 22*9e94795aSAndroid Build Coastguard Workerclass TestDiscoveryAgent: 23*9e94795aSAndroid Build Coastguard Worker """Test discovery agent.""" 24*9e94795aSAndroid Build Coastguard Worker 25*9e94795aSAndroid Build Coastguard Worker _TRADEFED_PREBUILT_JAR_RELATIVE_PATH = ( 26*9e94795aSAndroid Build Coastguard Worker "vendor/google_tradefederation/prebuilts/filegroups/google-tradefed/" 27*9e94795aSAndroid Build Coastguard Worker ) 28*9e94795aSAndroid Build Coastguard Worker 29*9e94795aSAndroid Build Coastguard Worker _TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY = "NoPossibleTestDiscovery" 30*9e94795aSAndroid Build Coastguard Worker 31*9e94795aSAndroid Build Coastguard Worker _TRADEFED_TEST_ZIP_REGEXES_LIST_KEY = "TestZipRegexes" 32*9e94795aSAndroid Build Coastguard Worker 33*9e94795aSAndroid Build Coastguard Worker _TRADEFED_DISCOVERY_OUTPUT_FILE_NAME = "test_discovery_agent.txt" 34*9e94795aSAndroid Build Coastguard Worker 35*9e94795aSAndroid Build Coastguard Worker def __init__( 36*9e94795aSAndroid Build Coastguard Worker self, 37*9e94795aSAndroid Build Coastguard Worker tradefed_args: list[str], 38*9e94795aSAndroid Build Coastguard Worker test_mapping_zip_path: str = "", 39*9e94795aSAndroid Build Coastguard Worker tradefed_jar_revelant_files_path: str = _TRADEFED_PREBUILT_JAR_RELATIVE_PATH, 40*9e94795aSAndroid Build Coastguard Worker ): 41*9e94795aSAndroid Build Coastguard Worker self.tradefed_args = tradefed_args 42*9e94795aSAndroid Build Coastguard Worker self.test_mapping_zip_path = test_mapping_zip_path 43*9e94795aSAndroid Build Coastguard Worker self.tradefed_jar_relevant_files_path = tradefed_jar_revelant_files_path 44*9e94795aSAndroid Build Coastguard Worker 45*9e94795aSAndroid Build Coastguard Worker def discover_test_zip_regexes(self) -> list[str]: 46*9e94795aSAndroid Build Coastguard Worker """Discover test zip regexes from TradeFed. 47*9e94795aSAndroid Build Coastguard Worker 48*9e94795aSAndroid Build Coastguard Worker Returns: 49*9e94795aSAndroid Build Coastguard Worker A list of test zip regexes that TF is going to try to pull files from. 50*9e94795aSAndroid Build Coastguard Worker """ 51*9e94795aSAndroid Build Coastguard Worker test_discovery_output_file_name = os.path.join( 52*9e94795aSAndroid Build Coastguard Worker os.environ.get('TOP'), 'out', self._TRADEFED_DISCOVERY_OUTPUT_FILE_NAME 53*9e94795aSAndroid Build Coastguard Worker ) 54*9e94795aSAndroid Build Coastguard Worker with open( 55*9e94795aSAndroid Build Coastguard Worker test_discovery_output_file_name, mode="w+t" 56*9e94795aSAndroid Build Coastguard Worker ) as test_discovery_output_file: 57*9e94795aSAndroid Build Coastguard Worker java_args = [] 58*9e94795aSAndroid Build Coastguard Worker java_args.append("prebuilts/jdk/jdk21/linux-x86/bin/java") 59*9e94795aSAndroid Build Coastguard Worker java_args.append("-cp") 60*9e94795aSAndroid Build Coastguard Worker java_args.append( 61*9e94795aSAndroid Build Coastguard Worker self.create_classpath(self.tradefed_jar_relevant_files_path) 62*9e94795aSAndroid Build Coastguard Worker ) 63*9e94795aSAndroid Build Coastguard Worker java_args.append( 64*9e94795aSAndroid Build Coastguard Worker "com.android.tradefed.observatory.TestZipDiscoveryExecutor" 65*9e94795aSAndroid Build Coastguard Worker ) 66*9e94795aSAndroid Build Coastguard Worker java_args.extend(self.tradefed_args) 67*9e94795aSAndroid Build Coastguard Worker env = os.environ.copy() 68*9e94795aSAndroid Build Coastguard Worker env.update({"DISCOVERY_OUTPUT_FILE": test_discovery_output_file.name}) 69*9e94795aSAndroid Build Coastguard Worker logging.info(f"Calling test discovery with args: {java_args}") 70*9e94795aSAndroid Build Coastguard Worker try: 71*9e94795aSAndroid Build Coastguard Worker result = subprocess.run(args=java_args, env=env, text=True, check=True) 72*9e94795aSAndroid Build Coastguard Worker logging.info(f"Test zip discovery output: {result.stdout}") 73*9e94795aSAndroid Build Coastguard Worker except subprocess.CalledProcessError as e: 74*9e94795aSAndroid Build Coastguard Worker raise TestDiscoveryError( 75*9e94795aSAndroid Build Coastguard Worker f"Failed to run test discovery, strout: {e.stdout}, strerr:" 76*9e94795aSAndroid Build Coastguard Worker f" {e.stderr}, returncode: {e.returncode}" 77*9e94795aSAndroid Build Coastguard Worker ) 78*9e94795aSAndroid Build Coastguard Worker data = json.loads(test_discovery_output_file.read()) 79*9e94795aSAndroid Build Coastguard Worker logging.info(f"Test discovery result file content: {data}") 80*9e94795aSAndroid Build Coastguard Worker if ( 81*9e94795aSAndroid Build Coastguard Worker self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY in data 82*9e94795aSAndroid Build Coastguard Worker and data[self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY] 83*9e94795aSAndroid Build Coastguard Worker ): 84*9e94795aSAndroid Build Coastguard Worker raise TestDiscoveryError("No possible test discovery") 85*9e94795aSAndroid Build Coastguard Worker if ( 86*9e94795aSAndroid Build Coastguard Worker data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is None 87*9e94795aSAndroid Build Coastguard Worker or data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is [] 88*9e94795aSAndroid Build Coastguard Worker ): 89*9e94795aSAndroid Build Coastguard Worker raise TestDiscoveryError("No test zip regexes returned") 90*9e94795aSAndroid Build Coastguard Worker return data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] 91*9e94795aSAndroid Build Coastguard Worker 92*9e94795aSAndroid Build Coastguard Worker def discover_test_modules(self) -> list[str]: 93*9e94795aSAndroid Build Coastguard Worker """Discover test modules from TradeFed. 94*9e94795aSAndroid Build Coastguard Worker 95*9e94795aSAndroid Build Coastguard Worker Returns: 96*9e94795aSAndroid Build Coastguard Worker A list of test modules that TradeFed is going to execute based on the 97*9e94795aSAndroid Build Coastguard Worker TradeFed test args. 98*9e94795aSAndroid Build Coastguard Worker """ 99*9e94795aSAndroid Build Coastguard Worker return [] 100*9e94795aSAndroid Build Coastguard Worker 101*9e94795aSAndroid Build Coastguard Worker def create_classpath(self, directory): 102*9e94795aSAndroid Build Coastguard Worker """Creates a classpath string from all .jar files in the given directory. 103*9e94795aSAndroid Build Coastguard Worker 104*9e94795aSAndroid Build Coastguard Worker Args: 105*9e94795aSAndroid Build Coastguard Worker directory: The directory to search for .jar files. 106*9e94795aSAndroid Build Coastguard Worker 107*9e94795aSAndroid Build Coastguard Worker Returns: 108*9e94795aSAndroid Build Coastguard Worker A string representing the classpath, with jar files separated by the 109*9e94795aSAndroid Build Coastguard Worker OS-specific path separator (e.g., ':' on Linux/macOS, ';' on Windows). 110*9e94795aSAndroid Build Coastguard Worker """ 111*9e94795aSAndroid Build Coastguard Worker jar_files = glob.glob(os.path.join(directory, "*.jar")) 112*9e94795aSAndroid Build Coastguard Worker return os.pathsep.join(jar_files) 113*9e94795aSAndroid Build Coastguard Worker 114*9e94795aSAndroid Build Coastguard Worker 115*9e94795aSAndroid Build Coastguard Workerclass TestDiscoveryError(Exception): 116*9e94795aSAndroid Build Coastguard Worker """A TestDiscoveryErrorclass.""" 117*9e94795aSAndroid Build Coastguard Worker 118*9e94795aSAndroid Build Coastguard Worker def __init__(self, message): 119*9e94795aSAndroid Build Coastguard Worker super().__init__(message) 120*9e94795aSAndroid Build Coastguard Worker self.message = message 121