• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.tradefed.util;
17 
18 import com.android.tradefed.build.IBuildInfo;
19 import com.android.tradefed.build.IDeviceBuildInfo;
20 import com.android.tradefed.invoker.logger.InvocationMetricLogger;
21 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey;
22 import com.android.tradefed.targetprep.AltDirBehavior;
23 
24 import java.io.File;
25 import java.io.IOException;
26 import java.net.URL;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 
31 /**
32  * A helper class for operations related to tests zip generated by Android build system
33  */
34 public class BuildTestsZipUtils {
35 
36     /**
37      * Resolve the actual apk path based on testing artifact information inside build info.
38      *
39      * @param buildInfo build artifact information
40      * @param apkFileName filename of the apk to install
41      * @param altDirs alternative search paths, in addition to path inside {@code buildInfo}
42      * @param altDirBehavior how alternative search paths should be used against path inside
43      *        {@code buildInfo}: as fallback, or as override; if unspecified, fallback will be used
44      * @param lookupInResource if the file should be looked up in test harness resources as a final
45      *        fallback mechanism
46      * @param deviceSigningKey
47      * @return a {@link File} representing the physical apk file on host or {@code null} if the
48      *     file does not exist.
49      */
getApkFile(IBuildInfo buildInfo, String apkFileName, List<File> altDirs, AltDirBehavior altDirBehavior, boolean lookupInResource, String deviceSigningKey)50     public static File getApkFile(IBuildInfo buildInfo, String apkFileName,
51             List<File> altDirs, AltDirBehavior altDirBehavior,
52             boolean lookupInResource, String deviceSigningKey) throws IOException {
53         String apkBase = apkFileName.split("\\.")[0];
54 
55         List<File> dirs = new ArrayList<>();
56         if (altDirs != null) {
57             for (File dir : altDirs) {
58                 dirs.add(dir);
59                 // Files in tests zip file will be in DATA/app/,
60                 // DATA/app/apk_name or DATA/priv-app/apk_name
61                 dirs.add(FileUtil.getFileForPath(dir, "DATA", "app"));
62                 dirs.add(FileUtil.getFileForPath(dir, "DATA", "app", apkBase));
63                 dirs.add(FileUtil.getFileForPath(dir, "DATA", "priv-app", apkBase));
64                 // Files in out dir will be in data/app/apk_name
65                 dirs.add(FileUtil.getFileForPath(dir, "data", "app", apkBase));
66             }
67         }
68         // reverse the order so ones provided via command line last can be searched first
69         Collections.reverse(dirs);
70 
71         List<File> expandedTestDirs = new ArrayList<>();
72         File testsDir = null;
73         if (buildInfo != null && buildInfo instanceof IDeviceBuildInfo) {
74             testsDir = ((IDeviceBuildInfo) buildInfo).getTestsDir();
75             if (testsDir != null && testsDir.exists()) {
76                 expandedTestDirs.add(FileUtil.getFileForPath(testsDir, "DATA", "app"));
77                 expandedTestDirs.add(FileUtil.getFileForPath(testsDir, "DATA", "app", apkBase));
78                 expandedTestDirs.add(
79                         FileUtil.getFileForPath(testsDir, "DATA", "priv-app", apkBase));
80                 expandedTestDirs.add(FileUtil.getFileForPath(testsDir, apkBase));
81 
82                 // Files in testcases directory imported from env. variable can have a folder
83                 // hierarchy, so we search for folder.
84                 File testcasesSubDir = FileUtil.findFile(testsDir, apkBase);
85                 if (testcasesSubDir != null) {
86                     expandedTestDirs.add(testcasesSubDir);
87                 } else {
88                     // If there doesn't exist a directory named after apkBase, it's possible that
89                     // the apk is built output to a different module directory. Therefore, try to
90                     // search entire testsDir to locate the apk.
91                     // TODO(dshi): Find a better way to locate apk. Ideally we should start with the
92                     // test module's directory to avoid false-positive result.
93                     expandedTestDirs.add(testsDir);
94                 }
95             }
96         }
97         if (altDirBehavior == null) {
98             altDirBehavior = AltDirBehavior.FALLBACK;
99         }
100         if (altDirBehavior == AltDirBehavior.FALLBACK) {
101             // alt dirs are appended after build artifact dirs
102             expandedTestDirs.addAll(dirs);
103             dirs = expandedTestDirs;
104         } else if (altDirBehavior == AltDirBehavior.OVERRIDE) {
105             dirs.addAll(expandedTestDirs);
106         } else {
107             throw new IOException("Missing handler for alt-dir-behavior: " + altDirBehavior);
108         }
109         if (dirs.isEmpty() && !lookupInResource) {
110             throw new IOException(
111                     "Provided buildInfo does not contain a valid tests directory and no " +
112                     "fallback options were provided");
113         }
114 
115         for (File dir : dirs) {
116             // Recursively search each folder
117             File testAppFile = FileUtil.findFile(dir, apkFileName);
118             if (testAppFile != null && testAppFile.exists()) {
119                 return testAppFile;
120             }
121         }
122         if (lookupInResource) {
123             List<String> resourceLookup = new ArrayList<>();
124             if (deviceSigningKey != null) {
125                 resourceLookup.add(String.format("/apks/%s-%s.apk", apkBase, deviceSigningKey));
126             }
127             resourceLookup.add(String.format("/apks/%s", apkFileName));
128             File apkTempFile = FileUtil.createTempFile(apkFileName, ".apk");
129             URL apkUrl = null;
130             for (String path :  resourceLookup) {
131                 apkUrl = BuildTestsZipUtils.class.getResource(path);
132                 if (apkUrl != null) {
133                     break;
134                 }
135             }
136             if (apkUrl != null) {
137                 FileUtil.writeToFile(apkUrl.openStream(), apkTempFile);
138                 // since we don't know when the file would be no longer needed, we set the file to
139                 // be deleted on VM termination
140                 apkTempFile.deleteOnExit();
141                 return apkTempFile;
142             }
143             // If we couldn't find a resource, we delete the tmp file
144             FileUtil.deleteFile(apkTempFile);
145         }
146 
147         // Try to stage the files from remote zip files.
148         if (testsDir != null) {
149             File apkFile = buildInfo.stageRemoteFile(apkFileName, testsDir);
150             if (apkFile != null) {
151                 InvocationMetricLogger.addInvocationMetrics(
152                         InvocationMetricKey.STAGE_UNDEFINED_DEPENDENCY, apkFileName);
153                 return apkFile;
154             }
155         }
156         return null;
157     }
158 }
159