1 /* 2 * Copyright (C) 2019 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.tradefed.invoker; 17 18 import com.android.tradefed.build.IBuildInfo; 19 import com.android.tradefed.device.ITestDevice; 20 import com.android.tradefed.invoker.ExecutionFiles.FilesKey; 21 import com.android.tradefed.invoker.logger.InvocationMetricLogger; 22 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import com.android.tradefed.util.FileUtil; 25 26 import java.io.File; 27 import java.io.FileNotFoundException; 28 import java.util.List; 29 30 /** 31 * Holder object that contains all the information and dependencies a test runner or test might need 32 * to execute properly. 33 */ 34 public class TestInformation { 35 /** The context of the invocation or module in progress */ 36 private final IInvocationContext mContext; 37 /** Properties generated during execution. */ 38 private final ExecutionProperties mProperties; 39 /** 40 * Files generated during execution that needs to be carried, they will be deleted at the end of 41 * the invocation. 42 */ 43 private final ExecutionFiles mExecutionFiles; 44 45 /** Main folder for all dependencies of tests */ 46 private final File mDependenciesFolder; 47 48 private int mPrimaryDeviceIndex = 0; 49 50 // Flag to indicate if the test was informed of timeout 51 private boolean mTestTimedOut = false; 52 TestInformation(Builder builder)53 private TestInformation(Builder builder) { 54 mContext = builder.mContext; 55 mProperties = builder.mProperties; 56 mDependenciesFolder = builder.mDependenciesFolder; 57 mExecutionFiles = builder.mExecutionFiles; 58 } 59 TestInformation( TestInformation invocationInfo, IInvocationContext moduleContext, boolean copyExecFile)60 private TestInformation( 61 TestInformation invocationInfo, 62 IInvocationContext moduleContext, 63 boolean copyExecFile) { 64 mContext = moduleContext; 65 // Copy properties so each shard has its own 66 mProperties = new ExecutionProperties(); 67 mProperties.putAll(invocationInfo.mProperties.getAll()); 68 mDependenciesFolder = invocationInfo.mDependenciesFolder; 69 if (copyExecFile) { 70 mExecutionFiles = new ExecutionFiles(); 71 mExecutionFiles.putAll(invocationInfo.executionFiles()); 72 } else { 73 mExecutionFiles = invocationInfo.mExecutionFiles; 74 } 75 } 76 77 /** Create a builder for creating {@link TestInformation} instances. */ newBuilder()78 public static Builder newBuilder() { 79 return new Builder(); 80 } 81 82 /** Create an {@link TestInformation} representing a module rather than an invocation. */ createModuleTestInfo( TestInformation invocationInfo, IInvocationContext moduleContext)83 public static TestInformation createModuleTestInfo( 84 TestInformation invocationInfo, IInvocationContext moduleContext) { 85 return new TestInformation(invocationInfo, moduleContext, false); 86 } 87 88 /** Create an {@link TestInformation} with a copied {@link ExecutionFiles}. */ createCopyTestInfo( TestInformation invocationInfo, IInvocationContext context)89 public static TestInformation createCopyTestInfo( 90 TestInformation invocationInfo, IInvocationContext context) { 91 return new TestInformation(invocationInfo, context, true); 92 } 93 94 /** Returns the current invocation context, or the module context if this is a module. */ getContext()95 public IInvocationContext getContext() { 96 return mContext; 97 } 98 99 /** Returns the primary device under tests. */ getDevice()100 public ITestDevice getDevice() { 101 return mContext.getDevices().get(mPrimaryDeviceIndex); 102 } 103 104 /** Returns the list of devices part of the invocation. */ getDevices()105 public List<ITestDevice> getDevices() { 106 return mContext.getDevices(); 107 } 108 109 /** Returns the primary device build information. */ getBuildInfo()110 public IBuildInfo getBuildInfo() { 111 return mContext.getBuildInfos().get(mPrimaryDeviceIndex); 112 } 113 114 /** 115 * Test Harness internal method to switch which device is returned by default with {@link 116 * #getDevice()}. Always reset to 0. 117 */ setActiveDeviceIndex(int index)118 public final void setActiveDeviceIndex(int index) { 119 mPrimaryDeviceIndex = index; 120 } 121 122 /** 123 * Returns the properties generated during the invocation execution. Passing values and 124 * information through the {@link ExecutionProperties} is the recommended way to exchange 125 * information between target_preparers and tests. 126 */ properties()127 public ExecutionProperties properties() { 128 return mProperties; 129 } 130 131 /** 132 * Returns the files generated during the invocation execution. Passing files through the {@link 133 * ExecutionFiles} is the recommended way to make a file available between target_preparers and 134 * tests. 135 */ executionFiles()136 public ExecutionFiles executionFiles() { 137 return mExecutionFiles; 138 } 139 140 /** Returns the folder where all the dependencies are stored for an invocation. */ dependenciesFolder()141 public File dependenciesFolder() { 142 return mDependenciesFolder; 143 } 144 145 /** Returns whether the test was informed of timeout or not. */ isTestTimedOut()146 public boolean isTestTimedOut() { 147 return mTestTimedOut; 148 } 149 150 /** Notifies that test phase timeout has been triggered for this test. */ notifyTimeout()151 public void notifyTimeout() { 152 mTestTimedOut = true; 153 } 154 155 /** Builder to create a {@link TestInformation} instance. */ 156 public static class Builder { 157 private IInvocationContext mContext; 158 private ExecutionProperties mProperties; 159 private File mDependenciesFolder; 160 private ExecutionFiles mExecutionFiles; 161 Builder()162 private Builder() { 163 mProperties = new ExecutionProperties(); 164 mExecutionFiles = new ExecutionFiles(); 165 } 166 build()167 public TestInformation build() { 168 return new TestInformation(this); 169 } 170 setInvocationContext(IInvocationContext context)171 public Builder setInvocationContext(IInvocationContext context) { 172 this.mContext = context; 173 return this; 174 } 175 setDependenciesFolder(File dependenciesFolder)176 public Builder setDependenciesFolder(File dependenciesFolder) { 177 this.mDependenciesFolder = dependenciesFolder; 178 return this; 179 } 180 } 181 182 /** 183 * Search for a dependency/artifact file based on its name, and whether or not it's a target or 184 * host file (for quicker search). 185 * 186 * @param fileName The name of the file we are looking for. 187 * @param targetFirst whether or not we are favoring target-side files vs. host-side files for 188 * the search. 189 * @return The found artifact file. 190 * @throws FileNotFoundException If the file is not found. 191 */ getDependencyFile(String fileName, boolean targetFirst)192 public File getDependencyFile(String fileName, boolean targetFirst) 193 throws FileNotFoundException { 194 File dependency = null; 195 dependency = getFromEnv(fileName, targetFirst); 196 if (dependency != null && dependency.isFile()) { 197 return dependency; 198 } 199 dependency = getFromTestsDir(fileName); 200 if (dependency != null && dependency.isFile()) { 201 return dependency; 202 } 203 dependency = getFile(fileName); 204 if (dependency != null && dependency.isFile()) { 205 return dependency; 206 } 207 dependency = getFromDependencyFolder(fileName); 208 if (dependency != null && dependency.isFile()) { 209 return dependency; 210 } 211 throw new FileNotFoundException( 212 String.format("Could not find an artifact file associated with %s", fileName)); 213 } 214 getFromEnv(String fileName, boolean targetFirst)215 private File getFromEnv(String fileName, boolean targetFirst) { 216 FilesKey hostOrTarget = FilesKey.HOST_TESTS_DIRECTORY; 217 if (targetFirst) { 218 hostOrTarget = FilesKey.TARGET_TESTS_DIRECTORY; 219 } 220 File testsDir = mExecutionFiles.get(hostOrTarget); 221 if (testsDir != null && testsDir.exists()) { 222 File file = FileUtil.findFile(testsDir, fileName); 223 if (file != null) { 224 return file; 225 } 226 } 227 return null; 228 } 229 getFromTestsDir(String fileName)230 private File getFromTestsDir(String fileName) { 231 File testsDir = mExecutionFiles.get(FilesKey.TESTS_DIRECTORY); 232 if (testsDir != null && testsDir.exists()) { 233 File file = FileUtil.findFile(testsDir, fileName); 234 if (file == null) { 235 // TODO(b/138416078): Once build dependency can be fixed and test required 236 // APKs are all under the test module directory, we can remove this fallback 237 // approach to do individual download from remote artifact. 238 // Try to stage the files from remote zip files. 239 file = getBuildInfo().stageRemoteFile(fileName, testsDir); 240 if (file != null) { 241 InvocationMetricLogger.addInvocationMetrics( 242 InvocationMetricKey.STAGE_UNDEFINED_DEPENDENCY, fileName); 243 } 244 } else if (file.isDirectory()) { 245 CLog.d("Found %s as a directory, searching further.", fileName); 246 file = FileUtil.findFile(file, fileName); 247 } 248 return file; 249 } 250 return null; 251 } 252 getFile(String fileName)253 private File getFile(String fileName) { 254 return mExecutionFiles.get(fileName); 255 } 256 getFromDependencyFolder(String fileName)257 private File getFromDependencyFolder(String fileName) { 258 File testsDir = mDependenciesFolder; 259 if (testsDir != null && testsDir.exists()) { 260 File file = FileUtil.findFile(testsDir, fileName); 261 if (file != null) { 262 return file; 263 } 264 } 265 return null; 266 } 267 } 268