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 
17 package com.android.compatibility.common.tradefed.targetprep;
18 
19 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
20 import com.android.tradefed.config.IConfiguration;
21 import com.android.tradefed.config.IConfigurationReceiver;
22 import com.android.tradefed.config.Option;
23 import com.android.tradefed.config.OptionClass;
24 import com.android.tradefed.device.DeviceNotAvailableException;
25 import com.android.tradefed.device.ITestDevice;
26 import com.android.tradefed.invoker.TestInformation;
27 import com.android.tradefed.log.LogUtil.CLog;
28 import com.android.tradefed.observatory.IDiscoverDependencies;
29 import com.android.tradefed.result.CollectingTestListener;
30 import com.android.tradefed.result.TestDescription;
31 import com.android.tradefed.result.TestResult;
32 import com.android.tradefed.result.TestRunResult;
33 import com.android.tradefed.result.TestStatus;
34 import com.android.tradefed.result.error.DeviceErrorIdentifier;
35 import com.android.tradefed.result.error.InfraErrorIdentifier;
36 import com.android.tradefed.targetprep.BuildError;
37 import com.android.tradefed.targetprep.TargetSetupError;
38 import com.android.tradefed.testtype.AndroidJUnitTest;
39 
40 import java.io.File;
41 import java.io.FileNotFoundException;
42 import java.util.HashSet;
43 import java.util.Map.Entry;
44 import java.util.Set;
45 
46 /** Target preparer that instruments an APK. */
47 @OptionClass(alias = "apk-instrumentation-preparer")
48 public class ApkInstrumentationPreparer extends PreconditionPreparer
49         implements IConfigurationReceiver, IDiscoverDependencies {
50 
51     @Option(name = "apk", description = "Name of the apk to instrument", mandatory = true)
52     protected String mApkFileName = null;
53 
54     @Option(name = "package", description = "Name of the package", mandatory = true)
55     protected String mPackageName = null;
56 
57     public enum When {
58         BEFORE, AFTER, BOTH;
59     }
60 
61     @Option(name = "when", description = "When to instrument the apk", mandatory = true)
62     protected When mWhen = null;
63 
64     @Option(name = "throw-error", description = "Whether to throw error for device test failure")
65     protected boolean mThrowError = true;
66 
67     @Option(
68             name = "apk-instrumentation-filter",
69             description = "The include filters of the test name to run in the apk",
70             requiredForRerun = true)
71     private Set<String> mIncludeFilters = new HashSet<>();
72 
73     private IConfiguration mConfiguration = null;
74 
75     /** {@inheritDoc} */
76     @Override
setConfiguration(IConfiguration configuration)77     public void setConfiguration(IConfiguration configuration) {
78         mConfiguration = configuration;
79     }
80 
81     /** {@inheritDoc} */
82     @Override
run(TestInformation testInfo)83     public void run(TestInformation testInfo)
84             throws TargetSetupError, BuildError, DeviceNotAvailableException {
85         if (mWhen == When.AFTER) {
86             return;
87         }
88         ITestDevice device = testInfo.getDevice();
89         try {
90             if (instrument(testInfo)) {
91                 CLog.d("Target preparation successful");
92             } else if (mThrowError) {
93                 throw new TargetSetupError(
94                         "Not all target preparation steps completed",
95                         device.getDeviceDescriptor(),
96                         DeviceErrorIdentifier.DEVICE_UNEXPECTED_RESPONSE);
97             }
98         } catch (FileNotFoundException e) {
99             throw new TargetSetupError(
100                     "Couldn't find apk to instrument",
101                     e,
102                     device.getDeviceDescriptor(),
103                     InfraErrorIdentifier.ARTIFACT_NOT_FOUND);
104         }
105     }
106 
107     /** {@inheritDoc} */
108     @Override
tearDown(TestInformation testInfo, Throwable e)109     public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
110         if (e instanceof DeviceNotAvailableException) {
111             return;
112         }
113         if (mWhen == When.BEFORE) {
114             return;
115         }
116         try {
117             instrument(testInfo);
118         } catch (FileNotFoundException e1) {
119             CLog.e("Couldn't find apk to instrument");
120             CLog.e(e1);
121         }
122     }
123 
124     @Override
reportDependencies()125     public Set<String> reportDependencies() {
126         Set<String> deps = new HashSet<>();
127         deps.add(mApkFileName);
128         return deps;
129     }
130 
instrument(TestInformation testInfo)131     private boolean instrument(TestInformation testInfo)
132             throws DeviceNotAvailableException, FileNotFoundException {
133         CompatibilityBuildHelper buildHelper =
134                 new CompatibilityBuildHelper(testInfo.getBuildInfo());
135 
136         File apkFile = buildHelper.getTestFile(mApkFileName);
137         if (!apkFile.exists()) {
138             throw new FileNotFoundException(String.format("%s not found", mApkFileName));
139         }
140 
141         ITestDevice device = testInfo.getDevice();
142         if (device.getAppPackageInfo(mPackageName) != null) {
143             CLog.i("Package %s already present on the device, uninstalling ...", mPackageName);
144             device.uninstallPackage(mPackageName);
145         }
146         // Ensure device online before attempting instrumentation
147         testInfo.getDevice().waitForDeviceAvailable();
148         CLog.i("Instrumenting package: %s", mPackageName);
149         CollectingTestListener listener = new CollectingTestListener();
150         AndroidJUnitTest instrTest = new AndroidJUnitTest();
151         instrTest.setConfiguration(mConfiguration);
152         instrTest.setDevice(device);
153         instrTest.setInstallFile(apkFile);
154         instrTest.setPackageName(mPackageName);
155         instrTest.addAllIncludeFilters(mIncludeFilters);
156         instrTest.setRerunMode(false);
157         instrTest.setReRunUsingTestFile(false);
158         // TODO: Make this configurable.
159         instrTest.setIsolatedStorage(false);
160         instrTest.run(testInfo, listener);
161         TestRunResult result = listener.getCurrentRunResults();
162 
163         for (Entry<TestDescription, TestResult> results : result.getTestResults().entrySet()) {
164             if (TestStatus.FAILURE.equals(results.getValue().getResultStatus())) {
165                 if (mThrowError) {
166                     CLog.e(
167                             "Target preparation step %s failed.\n%s",
168                             results.getKey(), results.getValue().getStackTrace());
169                 } else {
170                     CLog.w(
171                             "Target preparation step %s failed.\n%s",
172                             results.getKey(), results.getValue().getStackTrace());
173                 }
174             }
175         }
176         // If any failure return false
177         return !(result.isRunFailure() || result.hasFailedTests());
178     }
179 }
180