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