1 /* 2 * Copyright (C) 2015 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 package com.android.compatibility.common.tradefed.build; 17 18 import com.android.tradefed.build.IBuildInfo; 19 import com.android.tradefed.build.IDeviceBuildInfo; 20 import com.android.tradefed.build.IFolderBuildInfo; 21 import com.android.tradefed.build.VersionedFile; 22 import com.android.tradefed.testtype.IAbi; 23 import com.android.tradefed.util.FileUtil; 24 25 import java.io.File; 26 import java.io.FileNotFoundException; 27 import java.io.IOException; 28 import java.text.SimpleDateFormat; 29 import java.util.Date; 30 import java.util.HashMap; 31 import java.util.Map; 32 33 /** 34 * A simple helper that stores and retrieves information from a {@link IBuildInfo}. 35 */ 36 public class CompatibilityBuildHelper { 37 38 public static final String MODULE_IDS = "MODULE_IDS"; 39 public static final String ROOT_DIR = "ROOT_DIR"; 40 public static final String SUITE_BUILD = "SUITE_BUILD"; 41 public static final String SUITE_NAME = "SUITE_NAME"; 42 public static final String SUITE_FULL_NAME = "SUITE_FULL_NAME"; 43 public static final String SUITE_VERSION = "SUITE_VERSION"; 44 public static final String SUITE_PLAN = "SUITE_PLAN"; 45 public static final String START_TIME_MS = "START_TIME_MS"; 46 public static final String COMMAND_LINE_ARGS = "command_line_args"; 47 48 private static final String DYNAMIC_CONFIG_OVERRIDE_URL = "DYNAMIC_CONFIG_OVERRIDE_URL"; 49 private static final String BUSINESS_LOGIC_HOST_FILE = "BUSINESS_LOGIC_HOST_FILE"; 50 private static final String RETRY_COMMAND_LINE_ARGS = "retry_command_line_args"; 51 52 private static final String CONFIG_PATH_PREFIX = "DYNAMIC_CONFIG_FILE:"; 53 54 private final IBuildInfo mBuildInfo; 55 56 /** 57 * Creates a {@link CompatibilityBuildHelper} wrapping the given {@link IBuildInfo}. 58 */ CompatibilityBuildHelper(IBuildInfo buildInfo)59 public CompatibilityBuildHelper(IBuildInfo buildInfo) { 60 mBuildInfo = buildInfo; 61 } 62 getBuildInfo()63 public IBuildInfo getBuildInfo() { 64 return mBuildInfo; 65 } 66 setRetryCommandLineArgs(String commandLineArgs)67 public void setRetryCommandLineArgs(String commandLineArgs) { 68 mBuildInfo.addBuildAttribute(RETRY_COMMAND_LINE_ARGS, commandLineArgs); 69 } 70 getCommandLineArgs()71 public String getCommandLineArgs() { 72 if (mBuildInfo.getBuildAttributes().containsKey(RETRY_COMMAND_LINE_ARGS)) { 73 return mBuildInfo.getBuildAttributes().get(RETRY_COMMAND_LINE_ARGS); 74 } else { 75 // NOTE: this is a temporary workaround set in TestInvocation#invoke in tradefed. 76 // This will be moved to a separate method in a new invocation metadata class. 77 return mBuildInfo.getBuildAttributes().get(COMMAND_LINE_ARGS); 78 } 79 } 80 getRecentCommandLineArgs()81 public String getRecentCommandLineArgs() { 82 return mBuildInfo.getBuildAttributes().get(COMMAND_LINE_ARGS); 83 } 84 getSuiteBuild()85 public String getSuiteBuild() { 86 return mBuildInfo.getBuildAttributes().get(SUITE_BUILD); 87 } 88 getSuiteName()89 public String getSuiteName() { 90 return mBuildInfo.getBuildAttributes().get(SUITE_NAME); 91 } 92 getSuiteFullName()93 public String getSuiteFullName() { 94 return mBuildInfo.getBuildAttributes().get(SUITE_FULL_NAME); 95 } 96 getSuiteVersion()97 public String getSuiteVersion() { 98 return mBuildInfo.getBuildAttributes().get(SUITE_VERSION); 99 } 100 getSuitePlan()101 public String getSuitePlan() { 102 return mBuildInfo.getBuildAttributes().get(SUITE_PLAN); 103 } 104 getDynamicConfigUrl()105 public String getDynamicConfigUrl() { 106 return mBuildInfo.getBuildAttributes().get(DYNAMIC_CONFIG_OVERRIDE_URL); 107 } 108 getStartTime()109 public long getStartTime() { 110 return Long.parseLong(mBuildInfo.getBuildAttributes().get(START_TIME_MS)); 111 } 112 addDynamicConfigFile(String moduleName, File configFile)113 public void addDynamicConfigFile(String moduleName, File configFile) { 114 // If invocation fails and ResultReporter never moves this file into the result, 115 // using setFile() ensures BuildInfo will delete upon cleanUp(). 116 mBuildInfo.setFile(configFile.getName(), configFile, 117 CONFIG_PATH_PREFIX + moduleName /* version */); 118 } 119 120 /** 121 * Set the business logic file for this invocation. 122 * 123 * @param hostFile The business logic host file. 124 */ setBusinessLogicHostFile(File hostFile)125 public void setBusinessLogicHostFile(File hostFile) { 126 setBusinessLogicHostFile(hostFile, null); 127 } 128 129 /** 130 * Set the business logic file with specific module id for this invocation. 131 * 132 * @param hostFile The business logic host file. 133 * @param moduleId The name of the moduleId. 134 */ setBusinessLogicHostFile(File hostFile, String moduleId)135 public void setBusinessLogicHostFile(File hostFile, String moduleId) { 136 String key = (moduleId == null) ? "" : moduleId; 137 mBuildInfo.setFile(BUSINESS_LOGIC_HOST_FILE + key, hostFile, 138 hostFile.getName()/* version */); 139 } 140 setModuleIds(String[] moduleIds)141 public void setModuleIds(String[] moduleIds) { 142 mBuildInfo.addBuildAttribute(MODULE_IDS, String.join(",", moduleIds)); 143 } 144 145 /** 146 * Returns the map of the dynamic config files downloaded. 147 */ getDynamicConfigFiles()148 public Map<String, File> getDynamicConfigFiles() { 149 Map<String, File> configMap = new HashMap<>(); 150 for (VersionedFile vFile : mBuildInfo.getFiles()) { 151 if (vFile.getVersion().startsWith(CONFIG_PATH_PREFIX)) { 152 configMap.put( 153 vFile.getVersion().substring(CONFIG_PATH_PREFIX.length()), 154 vFile.getFile()); 155 } 156 } 157 return configMap; 158 } 159 160 /** 161 * @return whether the business logic file has been set for this invocation. 162 */ hasBusinessLogicHostFile()163 public boolean hasBusinessLogicHostFile() { 164 return hasBusinessLogicHostFile(null); 165 } 166 167 /** 168 * Check whether the business logic file has been set with specific module id for this 169 * invocation. 170 * 171 * @param moduleId The name of the moduleId. 172 * @return True if the business logic file has been set. False otherwise. 173 */ hasBusinessLogicHostFile(String moduleId)174 public boolean hasBusinessLogicHostFile(String moduleId) { 175 String key = (moduleId == null) ? "" : moduleId; 176 return mBuildInfo.getFile(BUSINESS_LOGIC_HOST_FILE + key) != null; 177 } 178 179 /** 180 * @return a {@link File} representing the file containing business logic data for this 181 * invocation, or null if the business logic file has not been set. 182 */ getBusinessLogicHostFile()183 public File getBusinessLogicHostFile() { 184 return getBusinessLogicHostFile(null); 185 } 186 187 /** 188 * Get the file containing business logic data with specific module id for this invocation. 189 * 190 * @param moduleId The name of the moduleId. 191 * @return a {@link File} representing the file containing business logic data with 192 * specific module id for this invocation , or null if the business logic file has not been set. 193 */ getBusinessLogicHostFile(String moduleId)194 public File getBusinessLogicHostFile(String moduleId) { 195 String key = (moduleId == null) ? "" : moduleId; 196 return mBuildInfo.getFile(BUSINESS_LOGIC_HOST_FILE + key); 197 } 198 199 /** 200 * @return a {@link File} representing the directory holding the Compatibility installation 201 * @throws FileNotFoundException if the directory does not exist 202 */ getRootDir()203 public File getRootDir() throws FileNotFoundException { 204 File dir = null; 205 if (mBuildInfo instanceof IFolderBuildInfo) { 206 dir = ((IFolderBuildInfo) mBuildInfo).getRootDir(); 207 } 208 if (dir == null || !dir.exists()) { 209 String rootDir = mBuildInfo.getBuildAttributes().get(ROOT_DIR); 210 if (rootDir != null) { 211 dir = new File(rootDir); 212 } 213 } 214 if (dir == null || !dir.exists()) { 215 throw new FileNotFoundException( 216 String.format("Compatibility root directory %s does not exist", dir)); 217 } 218 return dir; 219 } 220 221 /** 222 * @return a {@link File} representing the "android-<suite>" folder of the Compatibility 223 * installation 224 * @throws FileNotFoundException if the directory does not exist 225 */ getDir()226 public File getDir() throws FileNotFoundException { 227 File dir = 228 new File(getRootDir(), String.format("android-%s", getSuiteName().toLowerCase())); 229 if (!dir.exists()) { 230 throw new FileNotFoundException(String.format( 231 "Compatibility install folder %s does not exist", 232 dir.getAbsolutePath())); 233 } 234 return dir; 235 } 236 237 /** 238 * @return a {@link File} representing the results directory. 239 * @throws FileNotFoundException if the directory structure is not valid. 240 */ getResultsDir()241 public File getResultsDir() throws FileNotFoundException { 242 return new File(getDir(), "results"); 243 } 244 245 /** 246 * @return a {@link File} representing the result directory of the current invocation. 247 * @throws FileNotFoundException if the directory structure is not valid. 248 */ getResultDir()249 public File getResultDir() throws FileNotFoundException { 250 return new File(getResultsDir(), 251 getDirSuffix(Long.parseLong(mBuildInfo.getBuildAttributes().get(START_TIME_MS)))); 252 } 253 254 /** 255 * @return a {@link File} representing the directory to store result logs. 256 * @throws FileNotFoundException if the directory structure is not valid. 257 */ getLogsDir()258 public File getLogsDir() throws FileNotFoundException { 259 return new File(getDir(), "logs"); 260 } 261 262 /** 263 * @return a {@link File} representing the log directory of the current invocation. 264 * @throws FileNotFoundException if the directory structure is not valid. 265 */ getInvocationLogDir()266 public File getInvocationLogDir() throws FileNotFoundException { 267 return new File( 268 getLogsDir(), 269 getDirSuffix(Long.parseLong(mBuildInfo.getBuildAttributes().get(START_TIME_MS)))); 270 } 271 272 /** 273 * @return a {@link File} representing the directory to store derivedplan files. 274 * @throws FileNotFoundException if the directory structure is not valid. 275 */ getSubPlansDir()276 public File getSubPlansDir() throws FileNotFoundException { 277 File subPlansDir = new File(getDir(), "subplans"); 278 if (!subPlansDir.exists()) { 279 subPlansDir.mkdirs(); 280 } 281 return subPlansDir; 282 } 283 284 /** 285 * @return a {@link File} representing the test modules directory. 286 * @throws FileNotFoundException if the directory structure is not valid. 287 */ getTestsDir()288 public File getTestsDir() throws FileNotFoundException { 289 // We have 2 options that can be the test modules dir (and we're going 290 // look for them in the following order): 291 // 1. ../android-*ts/testcases/ 292 // 2. The build info tests dir 293 // ANDROID_HOST_OUT and ANDROID_TARGET_OUT are already linked 294 // by tradefed to the tests dir when they exists so there is 295 // no need to explicitly search them. 296 297 File testsDir = null; 298 try { 299 testsDir = new File(getDir(), "testcases"); 300 } catch (FileNotFoundException | NullPointerException e) { 301 // Ok, no root dir for us to get, moving on to the next option. 302 testsDir = null; 303 } 304 305 if (testsDir == null) { 306 if (mBuildInfo instanceof IDeviceBuildInfo) { 307 testsDir = ((IDeviceBuildInfo) mBuildInfo).getTestsDir(); 308 } 309 } 310 311 // This just means we have no signs of where to check for the test dir. 312 if (testsDir == null) { 313 throw new FileNotFoundException( 314 String.format("No Compatibility tests folder set, did you run lunch?")); 315 } 316 317 if (!testsDir.exists()) { 318 throw new FileNotFoundException(String.format( 319 "Compatibility tests folder %s does not exist", 320 testsDir.getAbsolutePath())); 321 } 322 323 return testsDir; 324 } 325 326 /** 327 * @return a {@link File} representing the test file in the test modules directory. 328 * @throws FileNotFoundException if the test file cannot be found 329 */ getTestFile(String filename)330 public File getTestFile(String filename) throws FileNotFoundException { 331 return getTestFile(filename, null); 332 } 333 334 /** 335 * @return a {@link File} representing the test file in the test modules directory. 336 * @throws FileNotFoundException if the test file cannot be found 337 */ getTestFile(String filename, IAbi abi)338 public File getTestFile(String filename, IAbi abi) throws FileNotFoundException { 339 File testsDir = getTestsDir(); 340 341 // The file may be in a subdirectory so do a more thorough search 342 // if it did not exist. 343 File testFile = null; 344 try { 345 testFile = FileUtil.findFile(filename, abi, testsDir); 346 if (testFile != null) { 347 return testFile; 348 } 349 350 // TODO(b/138416078): Once build dependency can be fixed and test required APKs are all 351 // under the test module directory, we can remove this fallback approach to do 352 // individual download from remote artifact. 353 // Try to stage the files from remote zip files. 354 testFile = mBuildInfo.stageRemoteFile(filename, testsDir); 355 if (testFile != null) { 356 // Search again to match the given abi. 357 testFile = FileUtil.findFile(filename, abi, testsDir); 358 if (testFile != null) { 359 return testFile; 360 } 361 } 362 } catch (IOException e) { 363 throw new FileNotFoundException( 364 String.format( 365 "Failure in finding compatibility test file %s due to %s", 366 filename, e)); 367 } 368 369 throw new FileNotFoundException(String.format( 370 "Compatibility test file %s does not exist", filename)); 371 } 372 373 /** 374 * @return a {@link File} in the resultDir for logging invocation failures 375 */ getInvocationFailureFile()376 public File getInvocationFailureFile() throws FileNotFoundException { 377 return new File(getResultDir(), "invocation_failure.txt"); 378 } 379 380 /** 381 * @return a {@link File} in the resultDir for counting expected test runs 382 */ getTestRunsFile()383 public File getTestRunsFile() throws FileNotFoundException { 384 return new File(getResultDir(), "test_runs.txt"); 385 } 386 387 /** 388 * @return a {@link String} to use for directory suffixes created from the given time. 389 */ getDirSuffix(long millis)390 public static String getDirSuffix(long millis) { 391 return new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss").format(new Date(millis)); 392 } 393 } 394