1 /*
2  * Copyright (C) 2021 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.csuite.core;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import static org.junit.Assert.assertThrows;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.times;
23 import static org.mockito.Mockito.when;
24 
25 import com.android.csuite.core.DeviceUtils.DeviceTimestamp;
26 import com.android.csuite.core.DeviceUtils.DropboxEntry;
27 import com.android.csuite.core.TestUtils.TestArtifactReceiver;
28 import com.android.tradefed.build.BuildInfo;
29 import com.android.tradefed.device.ITestDevice;
30 import com.android.tradefed.invoker.IInvocationContext;
31 import com.android.tradefed.invoker.InvocationContext;
32 import com.android.tradefed.invoker.TestInformation;
33 import com.android.tradefed.result.FileInputStreamSource;
34 import com.android.tradefed.result.InputStreamSource;
35 
36 import com.google.common.jimfs.Jimfs;
37 
38 import org.junit.Rule;
39 import org.junit.Test;
40 import org.junit.rules.TemporaryFolder;
41 import org.junit.runner.RunWith;
42 import org.junit.runners.JUnit4;
43 import org.mockito.Mockito;
44 
45 import java.io.File;
46 import java.nio.file.FileSystem;
47 import java.nio.file.Files;
48 import java.nio.file.Path;
49 import java.util.Arrays;
50 import java.util.List;
51 import java.util.stream.Collectors;
52 
53 @RunWith(JUnit4.class)
54 public final class TestUtilsTest {
55     private final TestArtifactReceiver mMockTestArtifactReceiver =
56             Mockito.mock(TestArtifactReceiver.class);
57     private final ITestDevice mMockDevice = mock(ITestDevice.class);
58     private final DeviceUtils mMockDeviceUtils = Mockito.mock(DeviceUtils.class);
59     private static final String TEST_PACKAGE_NAME = "package.name";
60     @Rule public final TemporaryFolder mTempFolder = new TemporaryFolder();
61     private final FileSystem mFileSystem =
62             Jimfs.newFileSystem(com.google.common.jimfs.Configuration.unix());
63 
64     @Test
listApks_withSplitApksInSubDirectory_returnsApks()65     public void listApks_withSplitApksInSubDirectory_returnsApks() throws Exception {
66         Path root = mFileSystem.getPath("apk");
67         Files.createDirectories(root);
68         Files.createDirectories(root.resolve("sub"));
69         Files.createFile(root.resolve("sub").resolve("base.apk"));
70         Files.createFile(root.resolve("sub").resolve("config.apk"));
71 
72         List<Path> res = TestUtils.listApks(root);
73 
74         List<String> fileNames =
75                 res.stream()
76                         .map(Path::getFileName)
77                         .map(Path::toString)
78                         .collect(Collectors.toList());
79         assertThat(fileNames).containsExactly("base.apk", "config.apk");
80     }
81 
82     @Test
listApks_withSingleSplitApkDirectory_returnsApks()83     public void listApks_withSingleSplitApkDirectory_returnsApks() throws Exception {
84         Path root = mFileSystem.getPath("apk");
85         Files.createDirectories(root);
86         Files.createFile(root.resolve("base.apk"));
87 
88         List<Path> res = TestUtils.listApks(root);
89 
90         List<String> fileNames =
91                 res.stream()
92                         .map(Path::getFileName)
93                         .map(Path::toString)
94                         .collect(Collectors.toList());
95         assertThat(fileNames).containsExactly("base.apk");
96     }
97 
98     @Test
listApks_withSplitApkDirectory_returnsListWithBaseApkAsTheFirstElement()99     public void listApks_withSplitApkDirectory_returnsListWithBaseApkAsTheFirstElement()
100             throws Exception {
101         Path root = mFileSystem.getPath("apk");
102         Files.createDirectories(root);
103         Files.createFile(root.resolve("base.apk"));
104         Files.createFile(root.resolve("a.apk"));
105         Files.createFile(root.resolve("b.apk"));
106         Files.createFile(root.resolve("c.apk"));
107 
108         List<Path> res = TestUtils.listApks(root);
109 
110         assertThat(res.get(0).getFileName().toString()).isEqualTo("base.apk");
111     }
112 
113     @Test
listApks_withSingleApkDirectory_returnsApks()114     public void listApks_withSingleApkDirectory_returnsApks() throws Exception {
115         Path root = mFileSystem.getPath("apk");
116         Files.createDirectories(root);
117         Files.createFile(root.resolve("single.apk"));
118 
119         List<Path> res = TestUtils.listApks(root);
120 
121         List<String> fileNames =
122                 res.stream()
123                         .map(Path::getFileName)
124                         .map(Path::toString)
125                         .collect(Collectors.toList());
126         assertThat(fileNames).containsExactly("single.apk");
127     }
128 
129     @Test
listApks_withSingleApkFile_returnsApks()130     public void listApks_withSingleApkFile_returnsApks() throws Exception {
131         Path root = mFileSystem.getPath("single.apk");
132         Files.createFile(root);
133 
134         List<Path> res = TestUtils.listApks(root);
135 
136         List<String> fileNames =
137                 res.stream()
138                         .map(Path::getFileName)
139                         .map(Path::toString)
140                         .collect(Collectors.toList());
141         assertThat(fileNames).containsExactly("single.apk");
142     }
143 
144     @Test
listApks_withApkDirectoryContainingObbFiles_returnsApksWithObb()145     public void listApks_withApkDirectoryContainingObbFiles_returnsApksWithObb() throws Exception {
146         Path root = mFileSystem.getPath("apk");
147         Files.createDirectories(root);
148         Files.createFile(root.resolve("single.apk"));
149         Files.createFile(root.resolve("single.not_apk"));
150         Files.createFile(root.resolve("main.123.package.obb"));
151 
152         List<Path> res = TestUtils.listApks(root);
153 
154         List<String> fileNames =
155                 res.stream()
156                         .map(Path::getFileName)
157                         .map(Path::toString)
158                         .collect(Collectors.toList());
159         assertThat(fileNames).containsExactly("single.apk", "main.123.package.obb");
160     }
161 
162     @Test
listApks_withApkDirectoryContainingOtherFileTypes_returnsApksOnly()163     public void listApks_withApkDirectoryContainingOtherFileTypes_returnsApksOnly()
164             throws Exception {
165         Path root = mFileSystem.getPath("apk");
166         Files.createDirectories(root);
167         Files.createFile(root.resolve("single.apk"));
168         Files.createFile(root.resolve("single.not_apk"));
169 
170         List<Path> res = TestUtils.listApks(root);
171 
172         List<String> fileNames =
173                 res.stream()
174                         .map(Path::getFileName)
175                         .map(Path::toString)
176                         .collect(Collectors.toList());
177         assertThat(fileNames).containsExactly("single.apk");
178     }
179 
180     @Test
listApks_withApkDirectoryContainingNoApks_throwException()181     public void listApks_withApkDirectoryContainingNoApks_throwException() throws Exception {
182         Path root = mFileSystem.getPath("apk");
183         Files.createDirectories(root);
184         Files.createFile(root.resolve("single.not_apk"));
185 
186         assertThrows(TestUtils.TestUtilsException.class, () -> TestUtils.listApks(root));
187     }
188 
189     @Test
listApks_withApkDirectoryContainingOnlyObbFiles_throwException()190     public void listApks_withApkDirectoryContainingOnlyObbFiles_throwException() throws Exception {
191         Path root = mFileSystem.getPath("apk");
192         Files.createDirectories(root);
193         Files.createFile(root.resolve("main.123.package.obb"));
194 
195         assertThrows(TestUtils.TestUtilsException.class, () -> TestUtils.listApks(root));
196     }
197 
198     @Test
listApks_withNonApkFile_throwException()199     public void listApks_withNonApkFile_throwException() throws Exception {
200         Path root = mFileSystem.getPath("single.not_apk");
201         Files.createFile(root);
202 
203         assertThrows(TestUtils.TestUtilsException.class, () -> TestUtils.listApks(root));
204     }
205 
206     @Test
listApks_withMultipleSingleApks_throwException()207     public void listApks_withMultipleSingleApks_throwException() throws Exception {
208         Path root = mFileSystem.getPath("apk");
209         Files.createDirectories(root);
210         Files.createFile(root.resolve("single1.apk"));
211         Files.createFile(root.resolve("single2.apk"));
212 
213         assertThrows(TestUtils.TestUtilsException.class, () -> TestUtils.listApks(root));
214     }
215 
216     @Test
listApks_withApksInMultipleDirectories_throwException()217     public void listApks_withApksInMultipleDirectories_throwException() throws Exception {
218         Path root = mFileSystem.getPath("apk");
219         Files.createDirectories(root);
220         Files.createDirectories(root.resolve("1"));
221         Files.createDirectories(root.resolve("2"));
222         Files.createFile(root.resolve("1").resolve("base.apk"));
223         Files.createFile(root.resolve("2").resolve("config.apk"));
224 
225         assertThrows(TestUtils.TestUtilsException.class, () -> TestUtils.listApks(root));
226     }
227 
228     @Test
listApks_apksInTheSameDirectoryAndObbsInADifferentDirectory_doesNotThrow()229     public void listApks_apksInTheSameDirectoryAndObbsInADifferentDirectory_doesNotThrow()
230             throws Exception {
231         Path root = mFileSystem.getPath("apk");
232         Files.createDirectories(root);
233         Files.createDirectories(root.resolve("1"));
234         Files.createDirectories(root.resolve("2"));
235         Files.createFile(root.resolve("1").resolve("base.apk"));
236         Files.createFile(root.resolve("1").resolve("config.apk"));
237         Files.createFile(root.resolve("2").resolve("main.123.com.package.obb"));
238 
239         TestUtils.listApks(root);
240     }
241 
242     @Test
collectScreenshot_savesToTestLog()243     public void collectScreenshot_savesToTestLog() throws Exception {
244         TestUtils sut = createSubjectUnderTest();
245         InputStreamSource screenshotData = new FileInputStreamSource(mTempFolder.newFile());
246         when(mMockDevice.getScreenshot()).thenReturn(screenshotData);
247         when(mMockDevice.getSerialNumber()).thenReturn("SERIAL");
248 
249         sut.collectScreenshot(TEST_PACKAGE_NAME);
250 
251         Mockito.verify(mMockTestArtifactReceiver, times(1))
252                 .addTestArtifact(
253                         Mockito.contains("screenshot"),
254                         Mockito.any(),
255                         Mockito.any(InputStreamSource.class));
256     }
257 
258     @Test
saveApks_always_savesOnTestPass()259     public void saveApks_always_savesOnTestPass() throws Exception {
260         TestUtils sut = createSubjectUnderTest();
261         boolean testPassed = true;
262 
263         sut.saveApks(
264                 TestUtils.TakeEffectWhen.ALWAYS, testPassed, "apk", Arrays.asList(new File("")));
265 
266         Mockito.verify(mMockTestArtifactReceiver, times(1))
267                 .addTestArtifact(Mockito.contains("apk"), Mockito.any(), Mockito.any(File.class));
268     }
269 
270     @Test
saveApks_always_savesOnTestFail()271     public void saveApks_always_savesOnTestFail() throws Exception {
272         TestUtils sut = createSubjectUnderTest();
273         boolean testPassed = false;
274 
275         sut.saveApks(
276                 TestUtils.TakeEffectWhen.ALWAYS, testPassed, "apk", Arrays.asList(new File("")));
277 
278         Mockito.verify(mMockTestArtifactReceiver, times(1))
279                 .addTestArtifact(Mockito.contains("apk"), Mockito.any(), Mockito.any(File.class));
280     }
281 
282     @Test
saveApks_never_doesNotSaveOnTestPass()283     public void saveApks_never_doesNotSaveOnTestPass() throws Exception {
284         TestUtils sut = createSubjectUnderTest();
285         boolean testPassed = true;
286 
287         sut.saveApks(
288                 TestUtils.TakeEffectWhen.NEVER, testPassed, "apk", Arrays.asList(new File("")));
289 
290         Mockito.verify(mMockTestArtifactReceiver, times(0))
291                 .addTestArtifact(Mockito.contains("apk"), Mockito.any(), Mockito.any(File.class));
292     }
293 
294     @Test
saveApks_never_doesNotSaveOnTestFail()295     public void saveApks_never_doesNotSaveOnTestFail() throws Exception {
296         TestUtils sut = createSubjectUnderTest();
297         boolean testPassed = false;
298 
299         sut.saveApks(
300                 TestUtils.TakeEffectWhen.NEVER, testPassed, "apk", Arrays.asList(new File("")));
301 
302         Mockito.verify(mMockTestArtifactReceiver, times(0))
303                 .addTestArtifact(Mockito.contains("apk"), Mockito.any(), Mockito.any(File.class));
304     }
305 
306     @Test
saveApks_onPass_doesNotSaveOnTestFail()307     public void saveApks_onPass_doesNotSaveOnTestFail() throws Exception {
308         TestUtils sut = createSubjectUnderTest();
309         boolean testPassed = false;
310 
311         sut.saveApks(
312                 TestUtils.TakeEffectWhen.ON_PASS, testPassed, "apk", Arrays.asList(new File("")));
313 
314         Mockito.verify(mMockTestArtifactReceiver, times(0))
315                 .addTestArtifact(Mockito.contains("apk"), Mockito.any(), Mockito.any(File.class));
316     }
317 
318     @Test
saveApks_onPass_savesOnTestPass()319     public void saveApks_onPass_savesOnTestPass() throws Exception {
320         TestUtils sut = createSubjectUnderTest();
321         boolean testPassed = true;
322 
323         sut.saveApks(
324                 TestUtils.TakeEffectWhen.ON_PASS, testPassed, "apk", Arrays.asList(new File("")));
325 
326         Mockito.verify(mMockTestArtifactReceiver, times(1))
327                 .addTestArtifact(Mockito.contains("apk"), Mockito.any(), Mockito.any(File.class));
328     }
329 
330     @Test
saveApks_onFail_doesNotSaveOnTestPass()331     public void saveApks_onFail_doesNotSaveOnTestPass() throws Exception {
332         TestUtils sut = createSubjectUnderTest();
333         boolean testPassed = true;
334 
335         sut.saveApks(
336                 TestUtils.TakeEffectWhen.ON_FAIL, testPassed, "apk", Arrays.asList(new File("")));
337 
338         Mockito.verify(mMockTestArtifactReceiver, times(0))
339                 .addTestArtifact(Mockito.contains("apk"), Mockito.any(), Mockito.any(File.class));
340     }
341 
342     @Test
saveApks_onFail_savesOnTestFail()343     public void saveApks_onFail_savesOnTestFail() throws Exception {
344         TestUtils sut = createSubjectUnderTest();
345         boolean testPassed = false;
346 
347         sut.saveApks(
348                 TestUtils.TakeEffectWhen.ON_FAIL, testPassed, "apk", Arrays.asList(new File("")));
349 
350         Mockito.verify(mMockTestArtifactReceiver, times(1))
351                 .addTestArtifact(Mockito.contains("apk"), Mockito.any(), Mockito.any(File.class));
352     }
353 
354     @Test
getDropboxPackageCrashLog_noEntries_returnsNull()355     public void getDropboxPackageCrashLog_noEntries_returnsNull() throws Exception {
356         TestUtils sut = createSubjectUnderTest();
357         when(mMockDeviceUtils.getDropboxEntries(Mockito.any())).thenReturn(List.of());
358         DeviceTimestamp startTime = new DeviceTimestamp(0);
359 
360         String result = sut.getDropboxPackageCrashLog(TEST_PACKAGE_NAME, startTime, false);
361 
362         assertThat(result).isNull();
363     }
364 
365     @Test
getDropboxPackageCrashLog_noEntries_doesNotSaveOutput()366     public void getDropboxPackageCrashLog_noEntries_doesNotSaveOutput() throws Exception {
367         TestUtils sut = createSubjectUnderTest();
368         when(mMockDeviceUtils.getDropboxEntries(Mockito.any())).thenReturn(List.of());
369         DeviceTimestamp startTime = new DeviceTimestamp(0);
370         boolean saveToFile = true;
371 
372         sut.getDropboxPackageCrashLog(TEST_PACKAGE_NAME, startTime, saveToFile);
373 
374         Mockito.verify(mMockTestArtifactReceiver, Mockito.never())
375                 .addTestArtifact(
376                         Mockito.contains("dropbox"), Mockito.any(), Mockito.any(byte[].class));
377     }
378 
379     @Test
getDropboxPackageCrashLog_appCrashed_saveOutput()380     public void getDropboxPackageCrashLog_appCrashed_saveOutput() throws Exception {
381         TestUtils sut = createSubjectUnderTest();
382         when(mMockDeviceUtils.getDropboxEntries(
383                         Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()))
384                 .thenReturn(
385                         List.of(
386                                 new DeviceUtils.DropboxEntry(
387                                         2,
388                                         DeviceUtils.DROPBOX_APP_CRASH_TAGS
389                                                 .toArray(
390                                                         new String
391                                                                 [DeviceUtils.DROPBOX_APP_CRASH_TAGS
392                                                                         .size()])[0],
393                                         "Package: " + TEST_PACKAGE_NAME)));
394         DeviceTimestamp startTime = new DeviceTimestamp(0);
395         boolean saveToFile = true;
396 
397         sut.getDropboxPackageCrashLog(TEST_PACKAGE_NAME, startTime, saveToFile);
398 
399         Mockito.verify(mMockTestArtifactReceiver, Mockito.times(1))
400                 .addTestArtifact(
401                         Mockito.contains("dropbox"), Mockito.any(), Mockito.any(byte[].class));
402     }
403 
404     @Test
compileTestFailureMessage_videoStartTimeProvided_returnWithVideoTimeForCrashes()405     public void compileTestFailureMessage_videoStartTimeProvided_returnWithVideoTimeForCrashes()
406             throws Exception {
407         TestUtils sut = createSubjectUnderTest();
408         int videoStartTime = 10000;
409         int crashTime1 = 12000;
410         int crashTime2 = 72000;
411         String expectedTime1 = "00:02";
412         String expectedTime2 = "01:02";
413         List<DropboxEntry> crashEntries =
414                 List.of(
415                         new DeviceUtils.DropboxEntry(
416                                 crashTime1,
417                                 DeviceUtils.DROPBOX_APP_CRASH_TAGS
418                                         .toArray(
419                                                 new String
420                                                         [DeviceUtils.DROPBOX_APP_CRASH_TAGS
421                                                                 .size()])[0],
422                                 TEST_PACKAGE_NAME + " entry1"),
423                         new DeviceUtils.DropboxEntry(
424                                 crashTime2,
425                                 DeviceUtils.DROPBOX_APP_CRASH_TAGS
426                                         .toArray(
427                                                 new String
428                                                         [DeviceUtils.DROPBOX_APP_CRASH_TAGS
429                                                                 .size()])[0],
430                                 TEST_PACKAGE_NAME + " entry2"));
431 
432         String crashMessage =
433                 sut.compileTestFailureMessage(
434                         TEST_PACKAGE_NAME,
435                         crashEntries,
436                         false,
437                         new DeviceTimestamp(videoStartTime));
438 
439         assertThat(crashMessage).contains(expectedTime1);
440         assertThat(crashMessage).contains(expectedTime2);
441     }
442 
createSubjectUnderTest()443     private TestUtils createSubjectUnderTest() {
444         return new TestUtils(createTestInfo(), mMockTestArtifactReceiver, mMockDeviceUtils);
445     }
446 
createTestInfo()447     private TestInformation createTestInfo() {
448         IInvocationContext context = new InvocationContext();
449         context.addAllocatedDevice("device1", mMockDevice);
450         context.addDeviceBuildInfo("device1", new BuildInfo());
451         return TestInformation.newBuilder().setInvocationContext(context).build();
452     }
453 }
454