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 
17 package com.android.compatibility.common.tradefed.targetprep;
18 
19 import static org.junit.Assert.fail;
20 import static org.mockito.Mockito.when;
21 
22 import com.android.tradefed.build.BuildInfo;
23 import com.android.tradefed.config.OptionSetter;
24 import com.android.tradefed.device.DeviceNotAvailableException;
25 import com.android.tradefed.device.ITestDevice;
26 import com.android.tradefed.invoker.IInvocationContext;
27 import com.android.tradefed.invoker.InvocationContext;
28 import com.android.tradefed.invoker.TestInformation;
29 import com.android.tradefed.targetprep.BuildError;
30 import com.android.tradefed.targetprep.TargetSetupError;
31 import com.android.tradefed.util.AaptParser;
32 import com.android.tradefed.util.FileUtil;
33 
34 import org.easymock.EasyMock;
35 import org.junit.Before;
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 import org.junit.runners.JUnit4;
39 import org.mockito.Mockito;
40 
41 import java.io.File;
42 import java.io.FileNotFoundException;
43 import java.io.IOException;
44 import java.util.HashMap;
45 import java.util.Map;
46 
47 /** Unit tests for {@link DeviceInteractionHelperInstaller} */
48 @RunWith(JUnit4.class)
49 public class DeviceInteractionHelperInstallerTest {
50     private DeviceInteractionHelperInstaller mPreparer = null;
51     private ITestDevice mMockDevice = null;
52     private OptionSetter mOptionSetter = null;
53 
54     private Map<File, AaptParser> mAaptParsers = new HashMap<File, AaptParser>();
55 
56     @Before
setUp()57     public void setUp() throws Exception {
58         mMockDevice = EasyMock.createStrictMock(ITestDevice.class);
59         EasyMock.expect(mMockDevice.getDeviceDescriptor()).andStubReturn(null);
60         EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn("SERIAL");
61         mPreparer =
62                 new DeviceInteractionHelperInstaller() {
63                     @Override
64                     protected AaptParser parseApk(File apkFile) {
65                         return mAaptParsers.getOrDefault(apkFile, AaptParser.parse(apkFile));
66                     }
67                 };
68         mOptionSetter = new OptionSetter(mPreparer);
69     }
70 
expectHelperInstall(File file)71     private void expectHelperInstall(File file) throws DeviceNotAvailableException {
72         EasyMock.expect(mMockDevice.isAppEnumerationSupported()).andReturn(true);
73         EasyMock.expect(
74                         mMockDevice.installPackage(
75                                 EasyMock.eq(file), EasyMock.anyBoolean(), EasyMock.anyObject()))
76                 .andReturn(null);
77     }
78 
expectDeviceProperty(String value)79     private void expectDeviceProperty(String value) throws DeviceNotAvailableException {
80         EasyMock.expect(mMockDevice.getProperty("ro.vendor.cts_interaction_helper_packages"))
81                 .andReturn(value);
82     }
83 
84     /** Creates a local file in dir and makes testInfo.getDependencyFile() return it. */
createFindableFile(File dir, String name, TestInformation testInfo)85     private File createFindableFile(File dir, String name, TestInformation testInfo)
86             throws FileNotFoundException, IOException {
87         File f = new File(dir, name);
88         f.createNewFile();
89         if (testInfo != null) {
90             testInfo.executionFiles().put(name, f);
91         }
92         return f;
93     }
94 
95     /** Creates a mock AaptParser that will return a desired package name for a file. */
mockApkPackage(File apk, String packageName)96     private void mockApkPackage(File apk, String packageName) {
97         AaptParser mockParser = Mockito.mock(AaptParser.class);
98         when(mockParser.getPackageName()).thenReturn(packageName);
99         mAaptParsers.put(apk, mockParser);
100     }
101 
makeTestInfo(ITestDevice device)102     private TestInformation makeTestInfo(ITestDevice device) {
103         IInvocationContext context = new InvocationContext();
104         context.addAllocatedDevice("device", device);
105         context.addDeviceBuildInfo("device", new BuildInfo());
106         return TestInformation.newBuilder().setInvocationContext(context).build();
107     }
108 
109     // With no options or device property, only the default helpers should be installed.
110     @Test
testNoOptionsNoDeviceProperty()111     public void testNoOptionsNoDeviceProperty() throws Exception {
112         File testsDir = FileUtil.createTempDir("tests_dir");
113         try {
114             TestInformation testInfo = makeTestInfo(mMockDevice);
115             File baseApk =
116                     createFindableFile(testsDir, "com.android.cts.helpers.aosp.apk", testInfo);
117 
118             expectHelperInstall(baseApk);
119             expectDeviceProperty(null);
120 
121             EasyMock.replay(mMockDevice);
122             mPreparer.setUp(testInfo);
123             EasyMock.verify(mMockDevice);
124         } finally {
125             FileUtil.recursiveDelete(testsDir);
126         }
127     }
128 
129     // With no options and an empty device property, only the default helpers should be installed.
130     @Test
testNoOptionsEmptyDeviceProperty()131     public void testNoOptionsEmptyDeviceProperty() throws Exception {
132         File testsDir = FileUtil.createTempDir("tests_dir");
133         try {
134             TestInformation testInfo = makeTestInfo(mMockDevice);
135             File baseApk =
136                     createFindableFile(testsDir, "com.android.cts.helpers.aosp.apk", testInfo);
137 
138             expectHelperInstall(baseApk);
139             expectDeviceProperty("");
140 
141             EasyMock.replay(mMockDevice);
142             mPreparer.setUp(testInfo);
143             EasyMock.verify(mMockDevice);
144         } finally {
145             FileUtil.recursiveDelete(testsDir);
146         }
147     }
148 
149     // With no options and a valid device property, the default helpers and the helpers listed in
150     // the property should all be installed.
151     @Test
testNoOptionsValidDeviceProperty()152     public void testNoOptionsValidDeviceProperty() throws Exception {
153         File testsDir = FileUtil.createTempDir("tests_dir");
154         try {
155             TestInformation testInfo = makeTestInfo(mMockDevice);
156             File baseApk =
157                     createFindableFile(testsDir, "com.android.cts.helpers.aosp.apk", testInfo);
158 
159             // Extra apks should be found based on device property.
160             File extraApk = createFindableFile(testsDir, "com.helper1.apk", testInfo);
161             mockApkPackage(extraApk, "com.helper1");
162             File extraApk2 = createFindableFile(testsDir, "com.helper2.apk", testInfo);
163             mockApkPackage(extraApk2, "com.helper2");
164 
165             expectHelperInstall(baseApk);
166             expectDeviceProperty("com.helper1:com.helper2");
167             EasyMock.checkOrder(mMockDevice, false); // Package install order is nondeterministic.
168             expectHelperInstall(extraApk);
169             expectHelperInstall(extraApk2);
170 
171             EasyMock.replay(mMockDevice);
172             mPreparer.setUp(testInfo);
173             EasyMock.verify(mMockDevice);
174         } finally {
175             FileUtil.recursiveDelete(testsDir);
176         }
177     }
178 
179     // If an explicit default package name is passed, it should be used to install the default
180     // helpers instead of the default name.
181     @Test
testFallbackPackageOptionOverridesDefault()182     public void testFallbackPackageOptionOverridesDefault() throws Exception {
183         mOptionSetter.setOptionValue("default-package", "com.fallback");
184         File testsDir = FileUtil.createTempDir("tests_dir");
185         try {
186             TestInformation testInfo = makeTestInfo(mMockDevice);
187             File baseApk =
188                     createFindableFile(testsDir, "com.android.cts.helpers.aosp.apk", testInfo);
189             File otherApk = createFindableFile(testsDir, "com.fallback.apk", testInfo);
190             mockApkPackage(otherApk, "com.fallback");
191 
192             expectHelperInstall(otherApk);
193             expectDeviceProperty(null);
194 
195             EasyMock.replay(mMockDevice);
196             mPreparer.setUp(testInfo);
197             EasyMock.verify(mMockDevice);
198         } finally {
199             FileUtil.recursiveDelete(testsDir);
200         }
201     }
202 
203     // If an explicit device property name is passed, that property should be read instead of the
204     // default.  The default helpers will still be installed.
205     @Test
testDevicePropertyOptionOverridesDefault()206     public void testDevicePropertyOptionOverridesDefault() throws Exception {
207         mOptionSetter.setOptionValue("property-name", "ro.other_property");
208         File testsDir = FileUtil.createTempDir("tests_dir");
209         try {
210             TestInformation testInfo = makeTestInfo(mMockDevice);
211             File baseApk =
212                     createFindableFile(testsDir, "com.android.cts.helpers.aosp.apk", testInfo);
213 
214             // Extra apks should be found based on device property.
215             File extraApk = createFindableFile(testsDir, "com.helper1.apk", testInfo);
216             mockApkPackage(extraApk, "com.helper1");
217             File extraApk2 = createFindableFile(testsDir, "com.helper2.apk", testInfo);
218             mockApkPackage(extraApk2, "com.helper2");
219 
220             expectHelperInstall(baseApk);
221             EasyMock.expect(mMockDevice.getProperty("ro.other_property"))
222                     .andReturn("com.helper1:com.helper2");
223             EasyMock.checkOrder(mMockDevice, false); // Package install order is nondeterministic.
224             expectHelperInstall(extraApk);
225             expectHelperInstall(extraApk2);
226 
227             EasyMock.replay(mMockDevice);
228             mPreparer.setUp(testInfo);
229             EasyMock.verify(mMockDevice);
230         } finally {
231             FileUtil.recursiveDelete(testsDir);
232         }
233     }
234 
235     // Attempting to install an apk that can't be parsed will throw BuildError.
236     @Test
testInvalidApkWontInstall()237     public void testInvalidApkWontInstall() throws Exception {
238         File testsDir = FileUtil.createTempDir("tests_dir");
239 
240         try {
241             TestInformation testInfo = makeTestInfo(mMockDevice);
242             File baseApk =
243                     createFindableFile(testsDir, "com.android.cts.helpers.aosp.apk", testInfo);
244             File extraApk = createFindableFile(testsDir, "MyCtsHelpers.apk", testInfo);
245 
246             expectHelperInstall(baseApk);
247             expectDeviceProperty("MyCtsHelpers");
248 
249             EasyMock.replay(mMockDevice);
250             try {
251                 mPreparer.setUp(testInfo);
252                 fail("BuildError not thrown");
253             } catch (BuildError e) {
254                 // expected
255             }
256 
257             EasyMock.verify(mMockDevice);
258         } finally {
259             FileUtil.recursiveDelete(testsDir);
260         }
261     }
262 
263     // Attempting to install an apk that doesn't contain a package name will throw BuildError.
264     @Test
testApkWithoutPackageWontInstall()265     public void testApkWithoutPackageWontInstall() throws Exception {
266         File testsDir = FileUtil.createTempDir("tests_dir");
267 
268         try {
269             TestInformation testInfo = makeTestInfo(mMockDevice);
270             File baseApk =
271                     createFindableFile(testsDir, "com.android.cts.helpers.aosp.apk", testInfo);
272             File extraApk = createFindableFile(testsDir, "MyCtsHelpers.apk", testInfo);
273             mockApkPackage(extraApk, "");
274 
275             expectHelperInstall(baseApk);
276             expectDeviceProperty("MyCtsHelpers");
277 
278             EasyMock.replay(mMockDevice);
279             try {
280                 mPreparer.setUp(testInfo);
281                 fail("BuildError not thrown");
282             } catch (BuildError e) {
283                 // expected
284             }
285 
286             EasyMock.verify(mMockDevice);
287         } finally {
288             FileUtil.recursiveDelete(testsDir);
289         }
290     }
291 
292     // Attempting to install an apk with a package name that doesn't match the file name will
293     // throw BuildError because the file name came from the device search path.
294     @Test
testApkWithBadPackageWontInstall()295     public void testApkWithBadPackageWontInstall() throws Exception {
296         File testsDir = FileUtil.createTempDir("tests_dir");
297 
298         try {
299             TestInformation testInfo = makeTestInfo(mMockDevice);
300             File baseApk =
301                     createFindableFile(testsDir, "com.android.cts.helpers.aosp.apk", testInfo);
302             File extraApk = createFindableFile(testsDir, "MyCtsHelpers.apk", testInfo);
303             mockApkPackage(extraApk, "NotMyCtsHelpers");
304 
305             expectHelperInstall(baseApk);
306             expectDeviceProperty("MyCtsHelpers");
307 
308             EasyMock.replay(mMockDevice);
309             try {
310                 mPreparer.setUp(testInfo);
311                 fail("BuildError not thrown");
312             } catch (BuildError e) {
313                 // expected
314             }
315             EasyMock.verify(mMockDevice);
316         } finally {
317             FileUtil.recursiveDelete(testsDir);
318         }
319     }
320 
321     // If a package listed in the device's search path is not found, the default helpers and other
322     // requested packages will still be installed.  No exception is thrown.
323     @Test
testMissingPackageFromDevicePropertyDoesntAbort()324     public void testMissingPackageFromDevicePropertyDoesntAbort() throws Exception {
325         File testsDir = FileUtil.createTempDir("tests_dir");
326         try {
327             TestInformation testInfo = makeTestInfo(mMockDevice);
328             File baseApk =
329                     createFindableFile(testsDir, "com.android.cts.helpers.aosp.apk", testInfo);
330             File extraApk = createFindableFile(testsDir, "com.helper2.apk", testInfo);
331             mockApkPackage(extraApk, "com.helper2");
332 
333             expectHelperInstall(baseApk);
334             expectDeviceProperty("MyMissingHelpers:com.helper2");
335             // Skips installing MyMissingHelpers and installs remaining packages in the list.
336             expectHelperInstall(extraApk);
337 
338             EasyMock.replay(mMockDevice);
339             mPreparer.setUp(testInfo);
340             EasyMock.verify(mMockDevice);
341         } finally {
342             FileUtil.recursiveDelete(testsDir);
343         }
344     }
345 
346     // If the fallback helpers cannot be found, BuildError is thrown and no other packages
347     // will be installed.
348     @Test
testDefaultHelpersNotFound()349     public void testDefaultHelpersNotFound() throws Exception {
350         File testsDir = FileUtil.createTempDir("tests_dir");
351         try {
352             TestInformation testInfo = makeTestInfo(mMockDevice);
353 
354             // Should throw TargetSetupError after failing to find the default helpers.
355             // No search for other files.
356 
357             EasyMock.replay(mMockDevice);
358             try {
359                 mPreparer.setUp(testInfo);
360                 fail("BuildError not thrown");
361             } catch (BuildError e) {
362                 // expected
363             }
364             EasyMock.verify(mMockDevice);
365         } finally {
366             FileUtil.recursiveDelete(testsDir);
367         }
368     }
369 
370     // If a package can't be installed, TargetSetupError is thrown.
371     @Test
testFailedInstallAborts()372     public void testFailedInstallAborts() throws Exception {
373         File testsDir = FileUtil.createTempDir("tests_dir");
374         try {
375             TestInformation testInfo = makeTestInfo(mMockDevice);
376             File baseApk =
377                     createFindableFile(testsDir, "com.android.cts.helpers.aosp.apk", testInfo);
378 
379             EasyMock.expect(mMockDevice.isAppEnumerationSupported()).andReturn(true);
380             EasyMock.expect(
381                             mMockDevice.installPackage(
382                                     EasyMock.eq(baseApk),
383                                     EasyMock.anyBoolean(),
384                                     EasyMock.anyObject()))
385                     .andReturn("Install failed");
386             // Should throw TargetSetupError after failing to install and not do any further steps.
387 
388             EasyMock.replay(mMockDevice);
389             try {
390                 mPreparer.setUp(testInfo);
391                 fail("TargetSetupError not thrown");
392             } catch (TargetSetupError e) {
393                 // expected
394             }
395             EasyMock.verify(mMockDevice);
396         } finally {
397             FileUtil.recursiveDelete(testsDir);
398         }
399     }
400 }
401