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