• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.assertFalse;
21 import static org.junit.Assert.assertThrows;
22 import static org.junit.Assert.assertTrue;
23 import static org.mockito.Mockito.when;
24 
25 import android.service.dropbox.DropBoxManagerServiceDumpProto;
26 
27 import com.android.csuite.core.DeviceUtils.DeviceTimestamp;
28 import com.android.csuite.core.DeviceUtils.DeviceUtilsException;
29 import com.android.csuite.core.DeviceUtils.DropboxEntry;
30 import com.android.tradefed.device.DeviceRuntimeException;
31 import com.android.tradefed.device.ITestDevice;
32 import com.android.tradefed.util.CommandResult;
33 import com.android.tradefed.util.CommandStatus;
34 import com.android.tradefed.util.IRunUtil;
35 
36 import com.google.common.jimfs.Jimfs;
37 import com.google.protobuf.ByteString;
38 
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 import org.junit.runners.JUnit4;
42 import org.mockito.ArgumentMatcher;
43 import org.mockito.Mockito;
44 
45 import java.io.IOException;
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.Iterator;
51 import java.util.List;
52 import java.util.Set;
53 import java.util.concurrent.atomic.AtomicBoolean;
54 
55 @RunWith(JUnit4.class)
56 public final class DeviceUtilsTest {
57     private ITestDevice mDevice = Mockito.mock(ITestDevice.class);
58     private IRunUtil mRunUtil = Mockito.mock(IRunUtil.class);
59     private final FileSystem mFileSystem =
60             Jimfs.newFileSystem(com.google.common.jimfs.Configuration.unix());
61 
62     @Test
isPackageInstalled_packageIsInstalled_returnsTrue()63     public void isPackageInstalled_packageIsInstalled_returnsTrue() throws Exception {
64         String packageName = "package.name";
65         when(mDevice.executeShellV2Command(Mockito.startsWith("pm list packages")))
66                 .thenReturn(
67                         createSuccessfulCommandResultWithStdout("\npackage:" + packageName + "\n"));
68         DeviceUtils sut = createSubjectUnderTest();
69 
70         boolean res = sut.isPackageInstalled(packageName);
71 
72         assertTrue(res);
73     }
74 
75     @Test
isPackageInstalled_packageIsNotInstalled_returnsFalse()76     public void isPackageInstalled_packageIsNotInstalled_returnsFalse() throws Exception {
77         String packageName = "package.name";
78         when(mDevice.executeShellV2Command(Mockito.startsWith("pm list packages")))
79                 .thenReturn(createSuccessfulCommandResultWithStdout(""));
80         DeviceUtils sut = createSubjectUnderTest();
81 
82         boolean res = sut.isPackageInstalled(packageName);
83 
84         assertFalse(res);
85     }
86 
87     @Test
isPackageInstalled_commandFailed_throws()88     public void isPackageInstalled_commandFailed_throws() throws Exception {
89         when(mDevice.executeShellV2Command(Mockito.startsWith("pm list packages")))
90                 .thenReturn(createFailedCommandResult());
91         DeviceUtils sut = createSubjectUnderTest();
92 
93         assertThrows(DeviceUtilsException.class, () -> sut.isPackageInstalled("package.name"));
94     }
95 
96     @Test
launchPackage_pmDumpFailedAndPackageDoesNotExist_throws()97     public void launchPackage_pmDumpFailedAndPackageDoesNotExist_throws() throws Exception {
98         when(mDevice.executeShellV2Command(Mockito.startsWith("monkey")))
99                 .thenReturn(createFailedCommandResult());
100         when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump")))
101                 .thenReturn(createFailedCommandResult());
102         when(mDevice.executeShellV2Command(Mockito.startsWith("pm list packages")))
103                 .thenReturn(createSuccessfulCommandResultWithStdout("no packages"));
104         DeviceUtils sut = createSubjectUnderTest();
105 
106         assertThrows(DeviceUtilsException.class, () -> sut.launchPackage("package.name"));
107     }
108 
109     @Test
launchPackage_pmDumpFailedAndPackageExists_throws()110     public void launchPackage_pmDumpFailedAndPackageExists_throws() throws Exception {
111         when(mDevice.executeShellV2Command(Mockito.startsWith("monkey")))
112                 .thenReturn(createFailedCommandResult());
113         when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump")))
114                 .thenReturn(createFailedCommandResult());
115         when(mDevice.executeShellV2Command(Mockito.startsWith("pm list packages")))
116                 .thenReturn(createSuccessfulCommandResultWithStdout("package:package.name"));
117         DeviceUtils sut = createSubjectUnderTest();
118 
119         assertThrows(DeviceUtilsException.class, () -> sut.launchPackage("package.name"));
120     }
121 
122     @Test
launchPackage_amStartCommandFailed_throws()123     public void launchPackage_amStartCommandFailed_throws() throws Exception {
124         when(mDevice.executeShellV2Command(Mockito.startsWith("monkey")))
125                 .thenReturn(createFailedCommandResult());
126         when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump")))
127                 .thenReturn(
128                         createSuccessfulCommandResultWithStdout(
129                                 "        87f1610"
130                                     + " com.google.android.gms/.app.settings.GoogleSettingsActivity"
131                                     + " filter 7357509\n"
132                                     + "          Action: \"android.intent.action.MAIN\"\n"
133                                     + "          Category: \"android.intent.category.LAUNCHER\"\n"
134                                     + "          Category: \"android.intent.category.DEFAULT\"\n"
135                                     + "          Category:"
136                                     + " \"android.intent.category.NOTIFICATION_PREFERENCES\""));
137         when(mDevice.executeShellV2Command(Mockito.startsWith("am start")))
138                 .thenReturn(createFailedCommandResult());
139         DeviceUtils sut = createSubjectUnderTest();
140 
141         assertThrows(DeviceUtilsException.class, () -> sut.launchPackage("com.google.android.gms"));
142     }
143 
144     @Test
launchPackage_amFailedToLaunchThePackage_throws()145     public void launchPackage_amFailedToLaunchThePackage_throws() throws Exception {
146         when(mDevice.executeShellV2Command(Mockito.startsWith("monkey")))
147                 .thenReturn(createFailedCommandResult());
148         when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump")))
149                 .thenReturn(
150                         createSuccessfulCommandResultWithStdout(
151                                 "        87f1610"
152                                     + " com.google.android.gms/.app.settings.GoogleSettingsActivity"
153                                     + " filter 7357509\n"
154                                     + "          Action: \"android.intent.action.MAIN\"\n"
155                                     + "          Category: \"android.intent.category.LAUNCHER\"\n"
156                                     + "          Category: \"android.intent.category.DEFAULT\"\n"
157                                     + "          Category:"
158                                     + " \"android.intent.category.NOTIFICATION_PREFERENCES\""));
159         when(mDevice.executeShellV2Command(Mockito.startsWith("am start")))
160                 .thenReturn(
161                         createSuccessfulCommandResultWithStdout(
162                                 "Error: Activity not started, unable to resolve Intent"));
163         DeviceUtils sut = createSubjectUnderTest();
164 
165         assertThrows(DeviceUtilsException.class, () -> sut.launchPackage("com.google.android.gms"));
166     }
167 
168     @Test
launchPackage_monkeyFailedButAmSucceed_doesNotThrow()169     public void launchPackage_monkeyFailedButAmSucceed_doesNotThrow() throws Exception {
170         when(mDevice.executeShellV2Command(Mockito.startsWith("monkey")))
171                 .thenReturn(createFailedCommandResult());
172         when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump")))
173                 .thenReturn(
174                         createSuccessfulCommandResultWithStdout(
175                                 "        87f1610"
176                                     + " com.google.android.gms/.app.settings.GoogleSettingsActivity"
177                                     + " filter 7357509\n"
178                                     + "          Action: \"android.intent.action.MAIN\"\n"
179                                     + "          Category: \"android.intent.category.LAUNCHER\"\n"
180                                     + "          Category: \"android.intent.category.DEFAULT\"\n"
181                                     + "          Category:"
182                                     + " \"android.intent.category.NOTIFICATION_PREFERENCES\""));
183         when(mDevice.executeShellV2Command(Mockito.startsWith("am start")))
184                 .thenReturn(createSuccessfulCommandResultWithStdout(""));
185         DeviceUtils sut = createSubjectUnderTest();
186 
187         sut.launchPackage("com.google.android.gms");
188     }
189 
190     @Test
launchPackage_monkeySucceed_doesNotThrow()191     public void launchPackage_monkeySucceed_doesNotThrow() throws Exception {
192         when(mDevice.executeShellV2Command(Mockito.startsWith("monkey")))
193                 .thenReturn(createSuccessfulCommandResultWithStdout(""));
194         when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump")))
195                 .thenReturn(createFailedCommandResult());
196         when(mDevice.executeShellV2Command(Mockito.startsWith("am start")))
197                 .thenReturn(createFailedCommandResult());
198         DeviceUtils sut = createSubjectUnderTest();
199 
200         sut.launchPackage("package.name");
201     }
202 
203     @Test
getLaunchActivity_oneActivityIsLauncherAndMainAndDefault_returnsIt()204     public void getLaunchActivity_oneActivityIsLauncherAndMainAndDefault_returnsIt()
205             throws Exception {
206         String pmDump =
207                 "        eecc562 com.google.android.gms/.bugreport.BugreportActivity filter"
208                     + " ac016f3\n"
209                     + "          Action: \"android.intent.action.MAIN\"\n"
210                     + "          Category: \"android.intent.category.LAUNCHER\"\n"
211                     + "        87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity"
212                     + " filter 7357509\n"
213                     + "          Action: \"android.intent.action.MAIN\"\n"
214                     + "          Category: \"android.intent.category.LAUNCHER\"\n"
215                     + "          Category: \"android.intent.category.DEFAULT\"\n"
216                     + "          Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n"
217                     + "        28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter"
218                     + " 83cbcc0\n"
219                     + "          Action: \"android.intent.action.MAIN\"\n"
220                     + "          Category: \"android.intent.category.HOME\"\n"
221                     + "          Category: \"android.intent.category.DEFAULT\"";
222         DeviceUtils sut = createSubjectUnderTest();
223 
224         String res = sut.getLaunchActivity(pmDump);
225 
226         assertThat(res).isEqualTo("com.google.android.gms/.app.settings.GoogleSettingsActivity");
227     }
228 
229     @Test
getLaunchActivity_oneActivityIsLauncherAndMain_returnsIt()230     public void getLaunchActivity_oneActivityIsLauncherAndMain_returnsIt() throws Exception {
231         String pmDump =
232                 "        eecc562 com.google.android.gms/.bugreport.BugreportActivity filter"
233                     + " ac016f3\n"
234                     + "          Action: \"android.intent.action.MAIN\"\n"
235                     + "        87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity"
236                     + " filter 7357509\n"
237                     + "          Action: \"android.intent.action.MAIN\"\n"
238                     + "          Category: \"android.intent.category.LAUNCHER\"\n"
239                     + "          Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n"
240                     + "        28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter"
241                     + " 83cbcc0\n"
242                     + "          Action: \"android.intent.action.MAIN\"\n"
243                     + "          Category: \"android.intent.category.HOME\"\n"
244                     + "          Category: \"android.intent.category.DEFAULT\"\n"
245                     + "          mPriority=10, mOrder=0, mHasStaticPartialTypes=false,"
246                     + " mHasDynamicPartialTypes=false";
247         DeviceUtils sut = createSubjectUnderTest();
248 
249         String res = sut.getLaunchActivity(pmDump);
250 
251         assertThat(res).isEqualTo("com.google.android.gms/.app.settings.GoogleSettingsActivity");
252     }
253 
254     @Test
255     public void
getLaunchActivity_oneActivityIsLauncherAndOneActivityIsMain_returnsTheLauncherActivity()256             getLaunchActivity_oneActivityIsLauncherAndOneActivityIsMain_returnsTheLauncherActivity()
257                     throws Exception {
258         String pmDump =
259                 "        eecc562 com.google.android.gms/.bugreport.BugreportActivity filter"
260                     + " ac016f3\n"
261                     + "          Action: \"android.intent.action.MAIN\"\n"
262                     + "        87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity"
263                     + " filter 7357509\n"
264                     + "          Category: \"android.intent.category.LAUNCHER\"\n"
265                     + "          Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n"
266                     + "        28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter"
267                     + " 83cbcc0\n"
268                     + "          Action: \"android.intent.action.MAIN\"\n"
269                     + "          Category: \"android.intent.category.HOME\"\n"
270                     + "          Category: \"android.intent.category.DEFAULT\"\n"
271                     + "          mPriority=10, mOrder=0, mHasStaticPartialTypes=false,"
272                     + " mHasDynamicPartialTypes=false";
273         DeviceUtils sut = createSubjectUnderTest();
274 
275         String res = sut.getLaunchActivity(pmDump);
276 
277         assertThat(res).isEqualTo("com.google.android.gms/.app.settings.GoogleSettingsActivity");
278     }
279 
280     @Test
getLaunchActivity_oneActivityIsMain_returnsIt()281     public void getLaunchActivity_oneActivityIsMain_returnsIt() throws Exception {
282         String pmDump =
283                 "        eecc562 com.google.android.gms/.bugreport.BugreportActivity filter"
284                     + " ac016f3\n"
285                     + "          Action: \"android.intent.action.MAIN\"\n"
286                     + "        87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity"
287                     + " filter 7357509\n"
288                     + "          Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n"
289                     + "        28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter"
290                     + " 83cbcc0\n"
291                     + "          Category: \"android.intent.category.HOME\"\n"
292                     + "          Category: \"android.intent.category.DEFAULT\"\n"
293                     + "          mPriority=10, mOrder=0, mHasStaticPartialTypes=false,"
294                     + " mHasDynamicPartialTypes=false";
295         DeviceUtils sut = createSubjectUnderTest();
296 
297         String res = sut.getLaunchActivity(pmDump);
298 
299         assertThat(res).isEqualTo("com.google.android.gms/.bugreport.BugreportActivity");
300     }
301 
302     @Test
getLaunchActivity_oneActivityIsLauncher_returnsIt()303     public void getLaunchActivity_oneActivityIsLauncher_returnsIt() throws Exception {
304         String pmDump =
305                 "        eecc562 com.google.android.gms/.bugreport.BugreportActivity filter"
306                     + " ac016f3\n"
307                     + "          Category: \"android.intent.category.LAUNCHER\"\n"
308                     + "        87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity"
309                     + " filter 7357509\n"
310                     + "          Action: \"android.intent.action.MAIN\"\n"
311                     + "          Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n"
312                     + "        28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter"
313                     + " 83cbcc0\n"
314                     + "          Category: \"android.intent.category.HOME\"\n"
315                     + "          Category: \"android.intent.category.DEFAULT\"\n"
316                     + "          mPriority=10, mOrder=0, mHasStaticPartialTypes=false,"
317                     + " mHasDynamicPartialTypes=false";
318         DeviceUtils sut = createSubjectUnderTest();
319 
320         String res = sut.getLaunchActivity(pmDump);
321 
322         assertThat(res).isEqualTo("com.google.android.gms/.bugreport.BugreportActivity");
323     }
324 
325     @Test
getLaunchActivity_noMainOrLauncherActivities_throws()326     public void getLaunchActivity_noMainOrLauncherActivities_throws() throws Exception {
327         String pmDump =
328                 "        eecc562 com.google.android.gms/.bugreport.BugreportActivity filter"
329                     + " ac016f3\n"
330                     + "          Category: \"android.intent.category.HOME\"\n"
331                     + "        87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity"
332                     + " filter 7357509\n"
333                     + "          Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n"
334                     + "        28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter"
335                     + " 83cbcc0\n"
336                     + "          Category: \"android.intent.category.HOME\"\n"
337                     + "          Category: \"android.intent.category.DEFAULT\"\n"
338                     + "          mPriority=10, mOrder=0, mHasStaticPartialTypes=false,"
339                     + " mHasDynamicPartialTypes=false";
340         DeviceUtils sut = createSubjectUnderTest();
341 
342         assertThrows(DeviceUtilsException.class, () -> sut.getLaunchActivity(pmDump));
343     }
344 
345     @Test
currentTimeMillis_deviceCommandFailed_throwsException()346     public void currentTimeMillis_deviceCommandFailed_throwsException() throws Exception {
347         DeviceUtils sut = createSubjectUnderTest();
348         when(mDevice.executeShellV2Command(Mockito.startsWith("echo")))
349                 .thenReturn(createFailedCommandResult());
350 
351         assertThrows(DeviceRuntimeException.class, () -> sut.currentTimeMillis());
352     }
353 
354     @Test
currentTimeMillis_unexpectedFormat_throwsException()355     public void currentTimeMillis_unexpectedFormat_throwsException() throws Exception {
356         DeviceUtils sut = createSubjectUnderTest();
357         when(mDevice.executeShellV2Command(Mockito.startsWith("echo")))
358                 .thenReturn(createSuccessfulCommandResultWithStdout(""));
359 
360         assertThrows(DeviceRuntimeException.class, () -> sut.currentTimeMillis());
361     }
362 
363     @Test
currentTimeMillis_successful_returnsTime()364     public void currentTimeMillis_successful_returnsTime() throws Exception {
365         DeviceUtils sut = createSubjectUnderTest();
366         when(mDevice.executeShellV2Command(Mockito.startsWith("echo")))
367                 .thenReturn(createSuccessfulCommandResultWithStdout("123"));
368 
369         DeviceTimestamp result = sut.currentTimeMillis();
370 
371         assertThat(result.get()).isEqualTo(Long.parseLong("123"));
372     }
373 
374     @Test
runWithScreenRecording_recordingDidNotStart_jobIsExecuted()375     public void runWithScreenRecording_recordingDidNotStart_jobIsExecuted() throws Exception {
376         DeviceUtils sut = createSubjectUnderTest();
377         when(mRunUtil.runCmdInBackground(Mockito.argThat(contains("shell", "screenrecord"))))
378                 .thenReturn(Mockito.mock(Process.class));
379         when(mDevice.executeShellV2Command(Mockito.startsWith("ls")))
380                 .thenReturn(createFailedCommandResult());
381         AtomicBoolean executed = new AtomicBoolean(false);
382         DeviceUtils.RunnableThrowingDeviceNotAvailable job = () -> executed.set(true);
383 
384         sut.runWithScreenRecording(job, video -> {});
385 
386         assertThat(executed.get()).isTrue();
387     }
388 
389     @Test
runWithScreenRecording_recordCommandThrowsException_jobIsExecuted()390     public void runWithScreenRecording_recordCommandThrowsException_jobIsExecuted()
391             throws Exception {
392         when(mRunUtil.runCmdInBackground(Mockito.argThat(contains("shell", "screenrecord"))))
393                 .thenThrow(new IOException());
394         DeviceUtils sut = createSubjectUnderTest();
395         AtomicBoolean executed = new AtomicBoolean(false);
396         DeviceUtils.RunnableThrowingDeviceNotAvailable job = () -> executed.set(true);
397 
398         sut.runWithScreenRecording(job, video -> {});
399 
400         assertThat(executed.get()).isTrue();
401     }
402 
403     @Test
runWithScreenRecording_jobThrowsException_videoFileIsHandled()404     public void runWithScreenRecording_jobThrowsException_videoFileIsHandled() throws Exception {
405         when(mRunUtil.runCmdInBackground(Mockito.argThat(contains("shell", "screenrecord"))))
406                 .thenReturn(Mockito.mock(Process.class));
407         when(mDevice.executeShellV2Command(Mockito.startsWith("ls")))
408                 .thenReturn(createSuccessfulCommandResultWithStdout(""));
409         DeviceUtils sut = createSubjectUnderTest();
410         DeviceUtils.RunnableThrowingDeviceNotAvailable job =
411                 () -> {
412                     throw new RuntimeException();
413                 };
414         AtomicBoolean handled = new AtomicBoolean(false);
415 
416         assertThrows(
417                 RuntimeException.class,
418                 () -> sut.runWithScreenRecording(job, video -> handled.set(true)));
419 
420         assertThat(handled.get()).isTrue();
421     }
422 
423     @Test
getPackageVersionName_deviceCommandFailed_returnsUnknown()424     public void getPackageVersionName_deviceCommandFailed_returnsUnknown() throws Exception {
425         DeviceUtils sut = createSubjectUnderTest();
426         when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionName")))
427                 .thenReturn(createFailedCommandResult());
428 
429         String result = sut.getPackageVersionName("any");
430 
431         assertThat(result).isEqualTo(DeviceUtils.UNKNOWN);
432     }
433 
434     @Test
getPackageVersionName_deviceCommandReturnsUnexpected_returnsUnknown()435     public void getPackageVersionName_deviceCommandReturnsUnexpected_returnsUnknown()
436             throws Exception {
437         DeviceUtils sut = createSubjectUnderTest();
438         when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionName")))
439                 .thenReturn(
440                         createSuccessfulCommandResultWithStdout(
441                                 "unexpected " + DeviceUtils.VERSION_NAME_PREFIX));
442 
443         String result = sut.getPackageVersionName("any");
444 
445         assertThat(result).isEqualTo(DeviceUtils.UNKNOWN);
446     }
447 
448     @Test
getPackageVersionName_deviceCommandSucceed_returnsVersionName()449     public void getPackageVersionName_deviceCommandSucceed_returnsVersionName() throws Exception {
450         DeviceUtils sut = createSubjectUnderTest();
451         when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionName")))
452                 .thenReturn(
453                         createSuccessfulCommandResultWithStdout(
454                                 " " + DeviceUtils.VERSION_NAME_PREFIX + "123"));
455 
456         String result = sut.getPackageVersionName("any");
457 
458         assertThat(result).isEqualTo("123");
459     }
460 
461     @Test
getPackageVersionCode_deviceCommandFailed_returnsUnknown()462     public void getPackageVersionCode_deviceCommandFailed_returnsUnknown() throws Exception {
463         DeviceUtils sut = createSubjectUnderTest();
464         when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionCode")))
465                 .thenReturn(createFailedCommandResult());
466 
467         String result = sut.getPackageVersionCode("any");
468 
469         assertThat(result).isEqualTo(DeviceUtils.UNKNOWN);
470     }
471 
472     @Test
getPackageVersionCode_deviceCommandReturnsUnexpected_returnsUnknown()473     public void getPackageVersionCode_deviceCommandReturnsUnexpected_returnsUnknown()
474             throws Exception {
475         DeviceUtils sut = createSubjectUnderTest();
476         when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionCode")))
477                 .thenReturn(
478                         createSuccessfulCommandResultWithStdout(
479                                 "unexpected " + DeviceUtils.VERSION_CODE_PREFIX));
480 
481         String result = sut.getPackageVersionCode("any");
482 
483         assertThat(result).isEqualTo(DeviceUtils.UNKNOWN);
484     }
485 
486     @Test
getPackageVersionCode_deviceCommandSucceed_returnVersionCode()487     public void getPackageVersionCode_deviceCommandSucceed_returnVersionCode() throws Exception {
488         DeviceUtils sut = createSubjectUnderTest();
489         when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionCode")))
490                 .thenReturn(
491                         createSuccessfulCommandResultWithStdout(
492                                 " " + DeviceUtils.VERSION_CODE_PREFIX + "123"));
493 
494         String result = sut.getPackageVersionCode("any");
495 
496         assertThat(result).isEqualTo("123");
497     }
498 
499     @Test
getDropboxEntries_noEntries_returnsEmptyList()500     public void getDropboxEntries_noEntries_returnsEmptyList() throws Exception {
501         DeviceUtils sut = createSubjectUnderTest();
502         when(mRunUtil.runTimedCmd(
503                         Mockito.anyLong(),
504                         Mockito.eq("sh"),
505                         Mockito.eq("-c"),
506                         Mockito.contains("dumpsys dropbox --proto")))
507                 .thenReturn(createSuccessfulCommandResultWithStdout(""));
508 
509         List<DropboxEntry> result = sut.getDropboxEntries(Set.of(""));
510 
511         assertThat(result).isEmpty();
512     }
513 
514     @Test
getDropboxEntries_entryExists_returnsEntry()515     public void getDropboxEntries_entryExists_returnsEntry() throws Exception {
516         Path dumpFile = Files.createTempFile(mFileSystem.getPath("/"), "dropbox", ".proto");
517         long time = 123;
518         String data = "abc";
519         String tag = "tag";
520         DropBoxManagerServiceDumpProto proto =
521                 DropBoxManagerServiceDumpProto.newBuilder()
522                         .addEntries(
523                                 DropBoxManagerServiceDumpProto.Entry.newBuilder()
524                                         .setTimeMs(time)
525                                         .setData(ByteString.copyFromUtf8(data)))
526                         .build();
527         Files.write(dumpFile, proto.toByteArray());
528         DeviceUtils sut = createSubjectUnderTestWithTempFile(dumpFile);
529         when(mRunUtil.runTimedCmd(
530                         Mockito.anyLong(),
531                         Mockito.eq("sh"),
532                         Mockito.eq("-c"),
533                         Mockito.contains("dumpsys dropbox --proto")))
534                 .thenReturn(createSuccessfulCommandResultWithStdout(""));
535 
536         List<DropboxEntry> result = sut.getDropboxEntries(Set.of(tag));
537 
538         assertThat(result.get(0).getTime()).isEqualTo(time);
539         assertThat(result.get(0).getData()).isEqualTo(data);
540         assertThat(result.get(0).getTag()).isEqualTo(tag);
541     }
542 
543     @Test
getDropboxEntriesFromStdout_entryExists_returnsEntry()544     public void getDropboxEntriesFromStdout_entryExists_returnsEntry() throws Exception {
545         when(mRunUtil.runTimedCmd(
546                         Mockito.anyLong(),
547                         Mockito.eq("sh"),
548                         Mockito.eq("-c"),
549                         Mockito.contains("dumpsys dropbox --file")))
550                 .thenReturn(createSuccessfulCommandResultWithStdout(""));
551         when(mRunUtil.runTimedCmd(
552                         Mockito.anyLong(),
553                         Mockito.eq("sh"),
554                         Mockito.eq("-c"),
555                         Mockito.contains("dumpsys dropbox --print")))
556                 .thenReturn(createSuccessfulCommandResultWithStdout(""));
557         Path fileDumpFile = Files.createTempFile(mFileSystem.getPath("/"), "file", ".dump");
558         Path printDumpFile = Files.createTempFile(mFileSystem.getPath("/"), "print", ".dump");
559         String fileResult =
560                 "Drop box contents: 351 entries\n"
561                         + "Max entries: 1000\n"
562                         + "Low priority rate limit period: 2000 ms\n"
563                         + "Low priority tags: {data_app_wtf, keymaster, system_server_wtf,"
564                         + " system_app_strictmode, system_app_wtf, system_server_strictmode,"
565                         + " data_app_strictmode, netstats}\n"
566                         + "\n"
567                         + "2022-09-05 04:17:21 system_server_wtf (text, 1730 bytes)\n"
568                         + "    /data/system/dropbox/system_server_wtf@1662351441269.txt\n"
569                         + "2022-09-05 04:31:06 event_data (text, 39 bytes)\n"
570                         + "    /data/system/dropbox/event_data@1662352266197.txt\n";
571         String printResult =
572                 "Drop box contents: 351 entries\n"
573                     + "Max entries: 1000\n"
574                     + "Low priority rate limit period: 2000 ms\n"
575                     + "Low priority tags: {data_app_wtf, keymaster, system_server_wtf,"
576                     + " system_app_strictmode, system_app_wtf, system_server_strictmode,"
577                     + " data_app_strictmode, netstats}\n"
578                     + "\n"
579                     + "========================================\n"
580                     + "2022-09-05 04:17:21 system_server_wtf (text, 1730 bytes)\n"
581                     + "Process: system_server\n"
582                     + "Subject: ActivityManager\n"
583                     + "Build:"
584                     + " generic/cf_x86_64_phone/vsoc_x86_64:UpsideDownCake/MASTER/8990215:userdebug/dev-keys\n"
585                     + "Dropped-Count: 0\n"
586                     + "\n"
587                     + "android.util.Log$TerribleFailure: Sending non-protected broadcast"
588                     + " com.android.bluetooth.btservice.BLUETOOTH_COUNTER_METRICS_ACTION from"
589                     + " system uid 1002 pkg com.android.bluetooth\n"
590                     + "    at android.util.Log.wtf(Log.java:332)\n"
591                     + "    at android.util.Log.wtf(Log.java:326)\n"
592                     + "    at"
593                     + " com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:13609)\n"
594                     + "    at"
595                     + " com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:14330)\n"
596                     + "    at"
597                     + " com.android.server.am.ActivityManagerService.broadcastIntentInPackage(ActivityManagerService.java:14530)\n"
598                     + "    at"
599                     + " com.android.server.am.ActivityManagerService$LocalService.broadcastIntentInPackage(ActivityManagerService.java:17065)\n"
600                     + "    at"
601                     + " com.android.server.am.PendingIntentRecord.sendInner(PendingIntentRecord.java:526)\n"
602                     + "    at"
603                     + " com.android.server.am.PendingIntentRecord.sendWithResult(PendingIntentRecord.java:311)\n"
604                     + "    at"
605                     + " com.android.server.am.ActivityManagerService.sendIntentSender(ActivityManagerService.java:5379)\n"
606                     + "    at"
607                     + " android.app.PendingIntent.sendAndReturnResult(PendingIntent.java:1012)\n"
608                     + "    at android.app.PendingIntent.send(PendingIntent.java:983)\n"
609                     + "    at"
610                     + " com.android.server.alarm.AlarmManagerService$DeliveryTracker.deliverLocked(AlarmManagerService.java:5500)\n"
611                     + "    at"
612                     + " com.android.server.alarm.AlarmManagerService.deliverAlarmsLocked(AlarmManagerService.java:4400)\n"
613                     + "    at"
614                     + " com.android.server.alarm.AlarmManagerService$AlarmThread.run(AlarmManagerService.java:4711)\n"
615                     + "Caused by: java.lang.Throwable\n"
616                     + "    at"
617                     + " com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:13610)\n"
618                     + "    ... 11 more\n"
619                     + "\n"
620                     + "========================================\n"
621                     + "2022-09-05 04:31:06 event_data (text, 39 bytes)\n"
622                     + "start=1662350731248\n"
623                     + "end=1662352266140\n"
624                     + "\n";
625         Files.write(fileDumpFile, fileResult.getBytes());
626         Files.write(printDumpFile, printResult.getBytes());
627         DeviceUtils sut = createSubjectUnderTestWithTempFile(fileDumpFile, printDumpFile);
628 
629         List<DropboxEntry> result = sut.getDropboxEntriesFromStdout(Set.of("system_server_wtf"));
630 
631         assertThat(result.get(0).getTime()).isEqualTo(1662351441269L);
632         assertThat(result.get(0).getData()).contains("Sending non-protected broadcast");
633         assertThat(result.get(0).getTag()).isEqualTo("system_server_wtf");
634         assertThat(result.size()).isEqualTo(1);
635     }
636 
createSubjectUnderTestWithTempFile(Path... tempFiles)637     private DeviceUtils createSubjectUnderTestWithTempFile(Path... tempFiles) {
638         when(mDevice.getSerialNumber()).thenReturn("SERIAL");
639         FakeClock fakeClock = new FakeClock();
640         Iterator<Path> iter = Arrays.asList(tempFiles).iterator();
641         return new DeviceUtils(
642                 mDevice, fakeClock.getSleeper(), fakeClock, () -> mRunUtil, () -> iter.next());
643     }
644 
createSubjectUnderTest()645     private DeviceUtils createSubjectUnderTest() {
646         when(mDevice.getSerialNumber()).thenReturn("SERIAL");
647         FakeClock fakeClock = new FakeClock();
648         return new DeviceUtils(
649                 mDevice,
650                 fakeClock.getSleeper(),
651                 fakeClock,
652                 () -> mRunUtil,
653                 () -> Files.createTempFile(mFileSystem.getPath("/"), "test", ".tmp"));
654     }
655 
656     private static class FakeClock implements DeviceUtils.Clock {
657         private long mCurrentTime = System.currentTimeMillis();
658         private DeviceUtils.Sleeper mSleeper = duration -> mCurrentTime += duration;
659 
getSleeper()660         private DeviceUtils.Sleeper getSleeper() {
661             return mSleeper;
662         }
663 
664         @Override
currentTimeMillis()665         public long currentTimeMillis() {
666             return mCurrentTime += 1;
667         }
668     }
669 
contains(String... args)670     private static ArgumentMatcher<String[]> contains(String... args) {
671         return array -> Arrays.asList(array).containsAll(Arrays.asList(args));
672     }
673 
createSuccessfulCommandResultWithStdout(String stdout)674     private static CommandResult createSuccessfulCommandResultWithStdout(String stdout) {
675         CommandResult commandResult = new CommandResult(CommandStatus.SUCCESS);
676         commandResult.setExitCode(0);
677         commandResult.setStdout(stdout);
678         commandResult.setStderr("");
679         return commandResult;
680     }
681 
createFailedCommandResult()682     private static CommandResult createFailedCommandResult() {
683         CommandResult commandResult = new CommandResult(CommandStatus.FAILED);
684         commandResult.setExitCode(1);
685         commandResult.setStdout("");
686         commandResult.setStderr("error");
687         return commandResult;
688     }
689 }
690