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.DeviceNotAvailableException; 31 import com.android.tradefed.device.DeviceRuntimeException; 32 import com.android.tradefed.device.ITestDevice; 33 import com.android.tradefed.util.CommandResult; 34 import com.android.tradefed.util.CommandStatus; 35 import com.android.tradefed.util.IRunUtil; 36 37 import com.google.common.jimfs.Jimfs; 38 import com.google.protobuf.ByteString; 39 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.JUnit4; 43 import org.mockito.ArgumentCaptor; 44 import org.mockito.ArgumentMatcher; 45 import org.mockito.Mockito; 46 47 import java.io.IOException; 48 import java.nio.file.FileSystem; 49 import java.nio.file.Files; 50 import java.nio.file.Path; 51 import java.util.Arrays; 52 import java.util.Iterator; 53 import java.util.List; 54 import java.util.Set; 55 import java.util.concurrent.atomic.AtomicBoolean; 56 import java.util.stream.Collectors; 57 58 @RunWith(JUnit4.class) 59 public final class DeviceUtilsTest { 60 private ITestDevice mDevice = Mockito.mock(ITestDevice.class); 61 private IRunUtil mRunUtil = Mockito.mock(IRunUtil.class); 62 private final FileSystem mFileSystem = 63 Jimfs.newFileSystem(com.google.common.jimfs.Configuration.unix()); 64 private static final String TEST_PACKAGE_NAME = "package.name"; 65 66 @Test grantExternalStoragePermissions_commandFailed_doesNotThrow()67 public void grantExternalStoragePermissions_commandFailed_doesNotThrow() throws Exception { 68 ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class); 69 when(mDevice.executeShellV2Command(captor.capture())) 70 .thenReturn(createFailedCommandResult()); 71 DeviceUtils sut = createSubjectUnderTest(); 72 73 sut.grantExternalStoragePermissions(TEST_PACKAGE_NAME); 74 75 assertThat(captor.getValue()).contains("MANAGE_EXTERNAL_STORAGE allow"); 76 } 77 78 @Test isPackageInstalled_packageIsInstalled_returnsTrue()79 public void isPackageInstalled_packageIsInstalled_returnsTrue() throws Exception { 80 String packageName = "package.name"; 81 when(mDevice.executeShellV2Command(Mockito.startsWith("pm list packages"))) 82 .thenReturn( 83 createSuccessfulCommandResultWithStdout("\npackage:" + packageName + "\n")); 84 DeviceUtils sut = createSubjectUnderTest(); 85 86 boolean res = sut.isPackageInstalled(packageName); 87 88 assertTrue(res); 89 } 90 91 @Test isPackageInstalled_packageIsNotInstalled_returnsFalse()92 public void isPackageInstalled_packageIsNotInstalled_returnsFalse() throws Exception { 93 String packageName = "package.name"; 94 when(mDevice.executeShellV2Command(Mockito.startsWith("pm list packages"))) 95 .thenReturn(createSuccessfulCommandResultWithStdout("")); 96 DeviceUtils sut = createSubjectUnderTest(); 97 98 boolean res = sut.isPackageInstalled(packageName); 99 100 assertFalse(res); 101 } 102 103 @Test isPackageInstalled_commandFailed_throws()104 public void isPackageInstalled_commandFailed_throws() throws Exception { 105 when(mDevice.executeShellV2Command(Mockito.startsWith("pm list packages"))) 106 .thenReturn(createFailedCommandResult()); 107 DeviceUtils sut = createSubjectUnderTest(); 108 109 assertThrows(DeviceUtilsException.class, () -> sut.isPackageInstalled("package.name")); 110 } 111 112 @Test launchPackage_pmDumpFailedAndPackageDoesNotExist_throws()113 public void launchPackage_pmDumpFailedAndPackageDoesNotExist_throws() throws Exception { 114 when(mDevice.executeShellV2Command(Mockito.startsWith("monkey"))) 115 .thenReturn(createFailedCommandResult()); 116 when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump"))) 117 .thenReturn(createFailedCommandResult()); 118 when(mDevice.executeShellV2Command(Mockito.startsWith("pm list packages"))) 119 .thenReturn(createSuccessfulCommandResultWithStdout("no packages")); 120 DeviceUtils sut = createSubjectUnderTest(); 121 122 assertThrows(DeviceUtilsException.class, () -> sut.launchPackage("package.name")); 123 } 124 125 @Test launchPackage_pmDumpFailedAndPackageExists_throws()126 public void launchPackage_pmDumpFailedAndPackageExists_throws() throws Exception { 127 when(mDevice.executeShellV2Command(Mockito.startsWith("monkey"))) 128 .thenReturn(createFailedCommandResult()); 129 when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump"))) 130 .thenReturn(createFailedCommandResult()); 131 when(mDevice.executeShellV2Command(Mockito.startsWith("pm list packages"))) 132 .thenReturn(createSuccessfulCommandResultWithStdout("package:package.name")); 133 DeviceUtils sut = createSubjectUnderTest(); 134 135 assertThrows(DeviceUtilsException.class, () -> sut.launchPackage("package.name")); 136 } 137 138 @Test launchPackage_amStartCommandFailed_throws()139 public void launchPackage_amStartCommandFailed_throws() throws Exception { 140 when(mDevice.executeShellV2Command(Mockito.startsWith("monkey"))) 141 .thenReturn(createFailedCommandResult()); 142 when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump"))) 143 .thenReturn( 144 createSuccessfulCommandResultWithStdout( 145 " 87f1610" 146 + " com.google.android.gms/.app.settings.GoogleSettingsActivity" 147 + " filter 7357509\n" 148 + " Action: \"android.intent.action.MAIN\"\n" 149 + " Category: \"android.intent.category.LAUNCHER\"\n" 150 + " Category: \"android.intent.category.DEFAULT\"\n" 151 + " Category:" 152 + " \"android.intent.category.NOTIFICATION_PREFERENCES\"")); 153 when(mDevice.executeShellV2Command(Mockito.startsWith("am start"))) 154 .thenReturn(createFailedCommandResult()); 155 DeviceUtils sut = createSubjectUnderTest(); 156 157 assertThrows(DeviceUtilsException.class, () -> sut.launchPackage("com.google.android.gms")); 158 } 159 160 @Test launchPackage_amFailedToLaunchThePackage_throws()161 public void launchPackage_amFailedToLaunchThePackage_throws() throws Exception { 162 when(mDevice.executeShellV2Command(Mockito.startsWith("monkey"))) 163 .thenReturn(createFailedCommandResult()); 164 when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump"))) 165 .thenReturn( 166 createSuccessfulCommandResultWithStdout( 167 " 87f1610" 168 + " com.google.android.gms/.app.settings.GoogleSettingsActivity" 169 + " filter 7357509\n" 170 + " Action: \"android.intent.action.MAIN\"\n" 171 + " Category: \"android.intent.category.LAUNCHER\"\n" 172 + " Category: \"android.intent.category.DEFAULT\"\n" 173 + " Category:" 174 + " \"android.intent.category.NOTIFICATION_PREFERENCES\"")); 175 when(mDevice.executeShellV2Command(Mockito.startsWith("am start"))) 176 .thenReturn( 177 createSuccessfulCommandResultWithStdout( 178 "Error: Activity not started, unable to resolve Intent")); 179 DeviceUtils sut = createSubjectUnderTest(); 180 181 assertThrows(DeviceUtilsException.class, () -> sut.launchPackage("com.google.android.gms")); 182 } 183 184 @Test launchPackage_monkeyFailedButAmSucceed_doesNotThrow()185 public void launchPackage_monkeyFailedButAmSucceed_doesNotThrow() throws Exception { 186 when(mDevice.executeShellV2Command(Mockito.startsWith("monkey"))) 187 .thenReturn(createFailedCommandResult()); 188 when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump"))) 189 .thenReturn( 190 createSuccessfulCommandResultWithStdout( 191 " 87f1610" 192 + " com.google.android.gms/.app.settings.GoogleSettingsActivity" 193 + " filter 7357509\n" 194 + " Action: \"android.intent.action.MAIN\"\n" 195 + " Category: \"android.intent.category.LAUNCHER\"\n" 196 + " Category: \"android.intent.category.DEFAULT\"\n" 197 + " Category:" 198 + " \"android.intent.category.NOTIFICATION_PREFERENCES\"")); 199 when(mDevice.executeShellV2Command(Mockito.startsWith("am start"))) 200 .thenReturn(createSuccessfulCommandResultWithStdout("")); 201 DeviceUtils sut = createSubjectUnderTest(); 202 203 sut.launchPackage("com.google.android.gms"); 204 } 205 206 @Test launchPackage_monkeySucceed_doesNotThrow()207 public void launchPackage_monkeySucceed_doesNotThrow() throws Exception { 208 when(mDevice.executeShellV2Command(Mockito.startsWith("monkey"))) 209 .thenReturn(createSuccessfulCommandResultWithStdout("")); 210 when(mDevice.executeShellV2Command(Mockito.startsWith("pm dump"))) 211 .thenReturn(createFailedCommandResult()); 212 when(mDevice.executeShellV2Command(Mockito.startsWith("am start"))) 213 .thenReturn(createFailedCommandResult()); 214 DeviceUtils sut = createSubjectUnderTest(); 215 216 sut.launchPackage("package.name"); 217 } 218 219 @Test getLaunchActivity_oneActivityIsLauncherAndMainAndDefault_returnsIt()220 public void getLaunchActivity_oneActivityIsLauncherAndMainAndDefault_returnsIt() 221 throws Exception { 222 String pmDump = 223 " eecc562 com.google.android.gms/.bugreport.BugreportActivity filter" 224 + " ac016f3\n" 225 + " Action: \"android.intent.action.MAIN\"\n" 226 + " Category: \"android.intent.category.LAUNCHER\"\n" 227 + " 87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity" 228 + " filter 7357509\n" 229 + " Action: \"android.intent.action.MAIN\"\n" 230 + " Category: \"android.intent.category.LAUNCHER\"\n" 231 + " Category: \"android.intent.category.DEFAULT\"\n" 232 + " Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n" 233 + " 28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter" 234 + " 83cbcc0\n" 235 + " Action: \"android.intent.action.MAIN\"\n" 236 + " Category: \"android.intent.category.HOME\"\n" 237 + " Category: \"android.intent.category.DEFAULT\""; 238 DeviceUtils sut = createSubjectUnderTest(); 239 240 String res = sut.getLaunchActivity(pmDump); 241 242 assertThat(res).isEqualTo("com.google.android.gms/.app.settings.GoogleSettingsActivity"); 243 } 244 245 @Test getLaunchActivity_oneActivityIsLauncherAndMain_returnsIt()246 public void getLaunchActivity_oneActivityIsLauncherAndMain_returnsIt() throws Exception { 247 String pmDump = 248 " eecc562 com.google.android.gms/.bugreport.BugreportActivity filter" 249 + " ac016f3\n" 250 + " Action: \"android.intent.action.MAIN\"\n" 251 + " 87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity" 252 + " filter 7357509\n" 253 + " Action: \"android.intent.action.MAIN\"\n" 254 + " Category: \"android.intent.category.LAUNCHER\"\n" 255 + " Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n" 256 + " 28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter" 257 + " 83cbcc0\n" 258 + " Action: \"android.intent.action.MAIN\"\n" 259 + " Category: \"android.intent.category.HOME\"\n" 260 + " Category: \"android.intent.category.DEFAULT\"\n" 261 + " mPriority=10, mOrder=0, mHasStaticPartialTypes=false," 262 + " mHasDynamicPartialTypes=false"; 263 DeviceUtils sut = createSubjectUnderTest(); 264 265 String res = sut.getLaunchActivity(pmDump); 266 267 assertThat(res).isEqualTo("com.google.android.gms/.app.settings.GoogleSettingsActivity"); 268 } 269 270 @Test 271 public void getLaunchActivity_oneActivityIsLauncherAndOneActivityIsMain_returnsTheLauncherActivity()272 getLaunchActivity_oneActivityIsLauncherAndOneActivityIsMain_returnsTheLauncherActivity() 273 throws Exception { 274 String pmDump = 275 " eecc562 com.google.android.gms/.bugreport.BugreportActivity filter" 276 + " ac016f3\n" 277 + " Action: \"android.intent.action.MAIN\"\n" 278 + " 87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity" 279 + " filter 7357509\n" 280 + " Category: \"android.intent.category.LAUNCHER\"\n" 281 + " Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n" 282 + " 28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter" 283 + " 83cbcc0\n" 284 + " Action: \"android.intent.action.MAIN\"\n" 285 + " Category: \"android.intent.category.HOME\"\n" 286 + " Category: \"android.intent.category.DEFAULT\"\n" 287 + " mPriority=10, mOrder=0, mHasStaticPartialTypes=false," 288 + " mHasDynamicPartialTypes=false"; 289 DeviceUtils sut = createSubjectUnderTest(); 290 291 String res = sut.getLaunchActivity(pmDump); 292 293 assertThat(res).isEqualTo("com.google.android.gms/.app.settings.GoogleSettingsActivity"); 294 } 295 296 @Test getLaunchActivity_oneActivityIsMain_returnsIt()297 public void getLaunchActivity_oneActivityIsMain_returnsIt() throws Exception { 298 String pmDump = 299 " eecc562 com.google.android.gms/.bugreport.BugreportActivity filter" 300 + " ac016f3\n" 301 + " Action: \"android.intent.action.MAIN\"\n" 302 + " 87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity" 303 + " filter 7357509\n" 304 + " Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n" 305 + " 28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter" 306 + " 83cbcc0\n" 307 + " Category: \"android.intent.category.HOME\"\n" 308 + " Category: \"android.intent.category.DEFAULT\"\n" 309 + " mPriority=10, mOrder=0, mHasStaticPartialTypes=false," 310 + " mHasDynamicPartialTypes=false"; 311 DeviceUtils sut = createSubjectUnderTest(); 312 313 String res = sut.getLaunchActivity(pmDump); 314 315 assertThat(res).isEqualTo("com.google.android.gms/.bugreport.BugreportActivity"); 316 } 317 318 @Test getLaunchActivity_oneActivityIsLauncher_returnsIt()319 public void getLaunchActivity_oneActivityIsLauncher_returnsIt() throws Exception { 320 String pmDump = 321 " eecc562 com.google.android.gms/.bugreport.BugreportActivity filter" 322 + " ac016f3\n" 323 + " Category: \"android.intent.category.LAUNCHER\"\n" 324 + " 87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity" 325 + " filter 7357509\n" 326 + " Action: \"android.intent.action.MAIN\"\n" 327 + " Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n" 328 + " 28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter" 329 + " 83cbcc0\n" 330 + " Category: \"android.intent.category.HOME\"\n" 331 + " Category: \"android.intent.category.DEFAULT\"\n" 332 + " mPriority=10, mOrder=0, mHasStaticPartialTypes=false," 333 + " mHasDynamicPartialTypes=false"; 334 DeviceUtils sut = createSubjectUnderTest(); 335 336 String res = sut.getLaunchActivity(pmDump); 337 338 assertThat(res).isEqualTo("com.google.android.gms/.bugreport.BugreportActivity"); 339 } 340 341 @Test getLaunchActivity_noMainOrLauncherActivities_throws()342 public void getLaunchActivity_noMainOrLauncherActivities_throws() throws Exception { 343 String pmDump = 344 " eecc562 com.google.android.gms/.bugreport.BugreportActivity filter" 345 + " ac016f3\n" 346 + " Category: \"android.intent.category.HOME\"\n" 347 + " 87f1610 com.google.android.gms/.app.settings.GoogleSettingsActivity" 348 + " filter 7357509\n" 349 + " Category: \"android.intent.category.NOTIFICATION_PREFERENCES\"\n" 350 + " 28957f2 com.google.android.gms/.kids.SyncTailTrapperActivity filter" 351 + " 83cbcc0\n" 352 + " Category: \"android.intent.category.HOME\"\n" 353 + " Category: \"android.intent.category.DEFAULT\"\n" 354 + " mPriority=10, mOrder=0, mHasStaticPartialTypes=false," 355 + " mHasDynamicPartialTypes=false"; 356 DeviceUtils sut = createSubjectUnderTest(); 357 358 assertThrows(DeviceUtilsException.class, () -> sut.getLaunchActivity(pmDump)); 359 } 360 361 @Test currentTimeMillis_deviceCommandFailed_throwsException()362 public void currentTimeMillis_deviceCommandFailed_throwsException() throws Exception { 363 DeviceUtils sut = createSubjectUnderTest(); 364 when(mDevice.executeShellV2Command(Mockito.startsWith("echo"))) 365 .thenReturn(createFailedCommandResult()); 366 367 assertThrows(DeviceRuntimeException.class, () -> sut.currentTimeMillis()); 368 } 369 370 @Test currentTimeMillis_unexpectedFormat_throwsException()371 public void currentTimeMillis_unexpectedFormat_throwsException() throws Exception { 372 DeviceUtils sut = createSubjectUnderTest(); 373 when(mDevice.executeShellV2Command(Mockito.startsWith("echo"))) 374 .thenReturn(createSuccessfulCommandResultWithStdout("")); 375 376 assertThrows(DeviceRuntimeException.class, () -> sut.currentTimeMillis()); 377 } 378 379 @Test currentTimeMillis_successful_returnsTime()380 public void currentTimeMillis_successful_returnsTime() throws Exception { 381 DeviceUtils sut = createSubjectUnderTest(); 382 when(mDevice.executeShellV2Command(Mockito.startsWith("echo"))) 383 .thenReturn(createSuccessfulCommandResultWithStdout("123")); 384 385 DeviceTimestamp result = sut.currentTimeMillis(); 386 387 assertThat(result.get()).isEqualTo(Long.parseLong("123")); 388 } 389 390 @Test runWithScreenRecording_recordingDidNotStart_jobIsExecuted()391 public void runWithScreenRecording_recordingDidNotStart_jobIsExecuted() throws Exception { 392 DeviceUtils sut = createSubjectUnderTest(); 393 when(mRunUtil.runCmdInBackground(Mockito.argThat(contains("shell", "screenrecord")))) 394 .thenReturn(Mockito.mock(Process.class)); 395 when(mDevice.executeShellV2Command(Mockito.startsWith("ls"))) 396 .thenReturn(createFailedCommandResult()); 397 AtomicBoolean executed = new AtomicBoolean(false); 398 DeviceUtils.RunnableThrowingDeviceNotAvailable job = () -> executed.set(true); 399 400 sut.runWithScreenRecording(job, (video, time) -> {}); 401 402 assertThat(executed.get()).isTrue(); 403 } 404 405 @Test runWithScreenRecording_recordCommandThrowsException_jobIsExecuted()406 public void runWithScreenRecording_recordCommandThrowsException_jobIsExecuted() 407 throws Exception { 408 when(mRunUtil.runCmdInBackground(Mockito.argThat(contains("shell", "screenrecord")))) 409 .thenThrow(new IOException()); 410 DeviceUtils sut = createSubjectUnderTest(); 411 AtomicBoolean executed = new AtomicBoolean(false); 412 DeviceUtils.RunnableThrowingDeviceNotAvailable job = () -> executed.set(true); 413 414 sut.runWithScreenRecording(job, (video, time) -> {}); 415 416 assertThat(executed.get()).isTrue(); 417 } 418 419 @Test runWithScreenRecording_jobThrowsException_videoFileIsHandled()420 public void runWithScreenRecording_jobThrowsException_videoFileIsHandled() throws Exception { 421 when(mRunUtil.runCmdInBackground(Mockito.argThat(contains("shell", "screenrecord")))) 422 .thenReturn(Mockito.mock(Process.class)); 423 when(mDevice.executeShellV2Command(Mockito.startsWith("ls"))) 424 .thenReturn(createSuccessfulCommandResultWithStdout("")); 425 DeviceUtils sut = createSubjectUnderTest(); 426 DeviceUtils.RunnableThrowingDeviceNotAvailable job = 427 () -> { 428 throw new RuntimeException(); 429 }; 430 AtomicBoolean handled = new AtomicBoolean(false); 431 432 assertThrows( 433 RuntimeException.class, 434 () -> sut.runWithScreenRecording(job, (video, time) -> handled.set(true))); 435 436 assertThat(handled.get()).isTrue(); 437 } 438 439 @Test getSdkLevel_returnsSdkLevelInteger()440 public void getSdkLevel_returnsSdkLevelInteger() throws DeviceNotAvailableException { 441 DeviceUtils sut = createSubjectUnderTest(); 442 int sdkLevel = 30; 443 when(mDevice.executeShellV2Command(Mockito.eq("getprop ro.build.version.sdk"))) 444 .thenReturn(createSuccessfulCommandResultWithStdout("" + sdkLevel)); 445 446 int result = sut.getSdkLevel(); 447 448 assertThat(result).isEqualTo(sdkLevel); 449 } 450 451 @Test getPackageVersionName_deviceCommandFailed_returnsUnknown()452 public void getPackageVersionName_deviceCommandFailed_returnsUnknown() throws Exception { 453 DeviceUtils sut = createSubjectUnderTest(); 454 when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionName"))) 455 .thenReturn(createFailedCommandResult()); 456 457 String result = sut.getPackageVersionName("any"); 458 459 assertThat(result).isEqualTo(DeviceUtils.UNKNOWN); 460 } 461 462 @Test getPackageVersionName_deviceCommandReturnsUnexpected_returnsUnknown()463 public void getPackageVersionName_deviceCommandReturnsUnexpected_returnsUnknown() 464 throws Exception { 465 DeviceUtils sut = createSubjectUnderTest(); 466 when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionName"))) 467 .thenReturn( 468 createSuccessfulCommandResultWithStdout( 469 "unexpected " + DeviceUtils.VERSION_NAME_PREFIX)); 470 471 String result = sut.getPackageVersionName("any"); 472 473 assertThat(result).isEqualTo(DeviceUtils.UNKNOWN); 474 } 475 476 @Test getPackageVersionName_deviceCommandSucceed_returnsVersionName()477 public void getPackageVersionName_deviceCommandSucceed_returnsVersionName() throws Exception { 478 DeviceUtils sut = createSubjectUnderTest(); 479 when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionName"))) 480 .thenReturn( 481 createSuccessfulCommandResultWithStdout( 482 " " + DeviceUtils.VERSION_NAME_PREFIX + "123")); 483 484 String result = sut.getPackageVersionName("any"); 485 486 assertThat(result).isEqualTo("123"); 487 } 488 489 @Test getPackageVersionCode_deviceCommandFailed_returnsUnknown()490 public void getPackageVersionCode_deviceCommandFailed_returnsUnknown() throws Exception { 491 DeviceUtils sut = createSubjectUnderTest(); 492 when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionCode"))) 493 .thenReturn(createFailedCommandResult()); 494 495 String result = sut.getPackageVersionCode("any"); 496 497 assertThat(result).isEqualTo(DeviceUtils.UNKNOWN); 498 } 499 500 @Test getPackageVersionCode_deviceCommandReturnsUnexpected_returnsUnknown()501 public void getPackageVersionCode_deviceCommandReturnsUnexpected_returnsUnknown() 502 throws Exception { 503 DeviceUtils sut = createSubjectUnderTest(); 504 when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionCode"))) 505 .thenReturn( 506 createSuccessfulCommandResultWithStdout( 507 "unexpected " + DeviceUtils.VERSION_CODE_PREFIX)); 508 509 String result = sut.getPackageVersionCode("any"); 510 511 assertThat(result).isEqualTo(DeviceUtils.UNKNOWN); 512 } 513 514 @Test getPackageVersionCode_deviceCommandSucceed_returnVersionCode()515 public void getPackageVersionCode_deviceCommandSucceed_returnVersionCode() throws Exception { 516 DeviceUtils sut = createSubjectUnderTest(); 517 when(mDevice.executeShellV2Command(Mockito.endsWith("grep versionCode"))) 518 .thenReturn( 519 createSuccessfulCommandResultWithStdout( 520 " " + DeviceUtils.VERSION_CODE_PREFIX + "123")); 521 522 String result = sut.getPackageVersionCode("any"); 523 524 assertThat(result).isEqualTo("123"); 525 } 526 527 @Test isDropboxEntryFromPackageProcess_cmdlineMatched_returnsTrue()528 public void isDropboxEntryFromPackageProcess_cmdlineMatched_returnsTrue() throws Exception { 529 String dropboxEntryData = "Cmd line: com.app.package"; 530 String packageName = "com.app.package"; 531 DeviceUtils sut = createSubjectUnderTest(); 532 533 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 534 535 assertThat(res).isTrue(); 536 } 537 538 @Test isDropboxEntryFromPackageProcess_processMatched_returnsTrue()539 public void isDropboxEntryFromPackageProcess_processMatched_returnsTrue() throws Exception { 540 String dropboxEntryData = "Process: com.app.package"; 541 String packageName = "com.app.package"; 542 DeviceUtils sut = createSubjectUnderTest(); 543 544 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 545 546 assertThat(res).isTrue(); 547 } 548 549 @Test isDropboxEntryFromPackageProcess_processMatchedInLines_returnsTrue()550 public void isDropboxEntryFromPackageProcess_processMatchedInLines_returnsTrue() 551 throws Exception { 552 String dropboxEntryData = "line\nProcess: com.app.package\nline"; 553 String packageName = "com.app.package"; 554 DeviceUtils sut = createSubjectUnderTest(); 555 556 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 557 558 assertThat(res).isTrue(); 559 } 560 561 @Test isDropboxEntryFromPackageProcess_processNameFollowedByOtherChar_returnsTrue()562 public void isDropboxEntryFromPackageProcess_processNameFollowedByOtherChar_returnsTrue() 563 throws Exception { 564 String dropboxEntryData = "line\nProcess: com.app.package, (time)\nline"; 565 String packageName = "com.app.package"; 566 DeviceUtils sut = createSubjectUnderTest(); 567 568 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 569 570 assertThat(res).isTrue(); 571 } 572 573 @Test isDropboxEntryFromPackageProcess_processNameFollowedByDot_returnsFalse()574 public void isDropboxEntryFromPackageProcess_processNameFollowedByDot_returnsFalse() 575 throws Exception { 576 String dropboxEntryData = "line\nProcess: com.app.package.sub, (time)\nline"; 577 String packageName = "com.app.package"; 578 DeviceUtils sut = createSubjectUnderTest(); 579 580 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 581 582 assertThat(res).isFalse(); 583 } 584 585 @Test isDropboxEntryFromPackageProcess_processNameFollowedByColon_returnsTrue()586 public void isDropboxEntryFromPackageProcess_processNameFollowedByColon_returnsTrue() 587 throws Exception { 588 String dropboxEntryData = "line\nProcess: com.app.package:sub, (time)\nline"; 589 String packageName = "com.app.package"; 590 DeviceUtils sut = createSubjectUnderTest(); 591 592 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 593 594 assertThat(res).isTrue(); 595 } 596 597 @Test isDropboxEntryFromPackageProcess_processNameFollowedByUnderscore_returnsFalse()598 public void isDropboxEntryFromPackageProcess_processNameFollowedByUnderscore_returnsFalse() 599 throws Exception { 600 String dropboxEntryData = "line\nProcess: com.app.package_sub, (time)\nline"; 601 String packageName = "com.app.package"; 602 DeviceUtils sut = createSubjectUnderTest(); 603 604 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 605 606 assertThat(res).isFalse(); 607 } 608 609 @Test isDropboxEntryFromPackageProcess_doesNotContainPackageName_returnsFalse()610 public void isDropboxEntryFromPackageProcess_doesNotContainPackageName_returnsFalse() 611 throws Exception { 612 String dropboxEntryData = "line\n"; 613 String packageName = "com.app.package"; 614 DeviceUtils sut = createSubjectUnderTest(); 615 616 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 617 618 assertThat(res).isFalse(); 619 } 620 621 @Test isDropboxEntryFromPackageProcess_packageNameWithUnderscorePrefix_returnsFalse()622 public void isDropboxEntryFromPackageProcess_packageNameWithUnderscorePrefix_returnsFalse() 623 throws Exception { 624 String dropboxEntryData = "line\na_com.app.package\n"; 625 String packageName = "com.app.package"; 626 DeviceUtils sut = createSubjectUnderTest(); 627 628 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 629 630 assertThat(res).isFalse(); 631 } 632 633 @Test isDropboxEntryFromPackageProcess_packageNameWithUnderscorePostfix_returnsFalse()634 public void isDropboxEntryFromPackageProcess_packageNameWithUnderscorePostfix_returnsFalse() 635 throws Exception { 636 String dropboxEntryData = "line\ncom.app.package_a\n"; 637 String packageName = "com.app.package"; 638 DeviceUtils sut = createSubjectUnderTest(); 639 640 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 641 642 assertThat(res).isFalse(); 643 } 644 645 @Test isDropboxEntryFromPackageProcess_packageNameWithDotPrefix_returnsFalse()646 public void isDropboxEntryFromPackageProcess_packageNameWithDotPrefix_returnsFalse() 647 throws Exception { 648 String dropboxEntryData = "line\na.com.app.package\n"; 649 String packageName = "com.app.package"; 650 DeviceUtils sut = createSubjectUnderTest(); 651 652 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 653 654 assertThat(res).isFalse(); 655 } 656 657 @Test isDropboxEntryFromPackageProcess_packageNameWithDotPostfix_returnsFalse()658 public void isDropboxEntryFromPackageProcess_packageNameWithDotPostfix_returnsFalse() 659 throws Exception { 660 String dropboxEntryData = "line\ncom.app.package.a\n"; 661 String packageName = "com.app.package"; 662 DeviceUtils sut = createSubjectUnderTest(); 663 664 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 665 666 assertThat(res).isFalse(); 667 } 668 669 @Test isDropboxEntryFromPackageProcess_packageNameWithColonPostfix_returnsTrue()670 public void isDropboxEntryFromPackageProcess_packageNameWithColonPostfix_returnsTrue() 671 throws Exception { 672 String dropboxEntryData = "line\ncom.app.package:a\n"; 673 String packageName = "com.app.package"; 674 DeviceUtils sut = createSubjectUnderTest(); 675 676 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 677 678 assertThat(res).isTrue(); 679 } 680 681 @Test 682 public void isDropboxEntryFromPackageProcess_packageNameWithAcceptiblePrefixAndPostfix_returnsTrue()683 isDropboxEntryFromPackageProcess_packageNameWithAcceptiblePrefixAndPostfix_returnsTrue() 684 throws Exception { 685 String dropboxEntryData = "line\ncom.app.package)\n"; 686 String packageName = "com.app.package"; 687 DeviceUtils sut = createSubjectUnderTest(); 688 689 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 690 691 assertThat(res).isTrue(); 692 } 693 694 @Test 695 public void isDropboxEntryFromPackageProcess_wrongProcessNameWithCorrectPackageName_returnsFalse()696 isDropboxEntryFromPackageProcess_wrongProcessNameWithCorrectPackageName_returnsFalse() 697 throws Exception { 698 String dropboxEntryData = "line\nProcess: com.app.package_other\ncom.app.package"; 699 String packageName = "com.app.package"; 700 DeviceUtils sut = createSubjectUnderTest(); 701 702 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 703 704 assertThat(res).isFalse(); 705 } 706 707 @Test isDropboxEntryFromPackageProcess_MultipleProcessNamesWithOneMatching_returnsTrue()708 public void isDropboxEntryFromPackageProcess_MultipleProcessNamesWithOneMatching_returnsTrue() 709 throws Exception { 710 String dropboxEntryData = 711 "line\n" 712 + "Process: com.app.package_other\n" 713 + "Process: com.app.package\n" 714 + "Process: com.other"; 715 String packageName = "com.app.package"; 716 DeviceUtils sut = createSubjectUnderTest(); 717 718 boolean res = sut.isDropboxEntryFromPackageProcess(dropboxEntryData, packageName); 719 720 assertThat(res).isTrue(); 721 } 722 723 @Test getDropboxEntries_containsEntriesOutsideTimeRange_onlyReturnsNewEntries()724 public void getDropboxEntries_containsEntriesOutsideTimeRange_onlyReturnsNewEntries() 725 throws Exception { 726 DeviceUtils sut = Mockito.spy(createSubjectUnderTest()); 727 DeviceTimestamp startTime = new DeviceTimestamp(1); 728 DeviceTimestamp endTime = new DeviceTimestamp(3); 729 Mockito.doAnswer( 730 inv -> 731 List.of( 732 new DeviceUtils.DropboxEntry( 733 0, 734 DeviceUtils.DROPBOX_APP_CRASH_TAGS 735 .toArray( 736 new String 737 [DeviceUtils 738 .DROPBOX_APP_CRASH_TAGS 739 .size()])[0], 740 TEST_PACKAGE_NAME + " entry1"), 741 new DeviceUtils.DropboxEntry( 742 2, 743 DeviceUtils.DROPBOX_APP_CRASH_TAGS 744 .toArray( 745 new String 746 [DeviceUtils 747 .DROPBOX_APP_CRASH_TAGS 748 .size()])[0], 749 TEST_PACKAGE_NAME + " entry2"), 750 new DeviceUtils.DropboxEntry( 751 100, 752 DeviceUtils.DROPBOX_APP_CRASH_TAGS 753 .toArray( 754 new String 755 [DeviceUtils 756 .DROPBOX_APP_CRASH_TAGS 757 .size()])[0], 758 TEST_PACKAGE_NAME + " entry3"))) 759 .when(sut) 760 .getDropboxEntries(DeviceUtils.DROPBOX_APP_CRASH_TAGS); 761 762 String result = 763 sut 764 .getDropboxEntries( 765 DeviceUtils.DROPBOX_APP_CRASH_TAGS, 766 TEST_PACKAGE_NAME, 767 startTime, 768 endTime) 769 .stream() 770 .map(DropboxEntry::toString) 771 .collect(Collectors.joining("\n")); 772 773 assertThat(result).doesNotContain("entry1"); 774 assertThat(result).contains("entry2"); 775 assertThat(result).doesNotContain("entry3"); 776 } 777 778 @Test getDropboxEntries_containsOtherProcessEntries_onlyReturnsPackageEntries()779 public void getDropboxEntries_containsOtherProcessEntries_onlyReturnsPackageEntries() 780 throws Exception { 781 DeviceUtils sut = Mockito.spy(createSubjectUnderTest()); 782 DeviceTimestamp startTime = new DeviceTimestamp(1); 783 Mockito.doAnswer( 784 inv -> 785 List.of( 786 new DeviceUtils.DropboxEntry( 787 2, 788 DeviceUtils.DROPBOX_APP_CRASH_TAGS 789 .toArray( 790 new String 791 [DeviceUtils 792 .DROPBOX_APP_CRASH_TAGS 793 .size()])[0], 794 "other.package" + " entry1"), 795 new DeviceUtils.DropboxEntry( 796 2, 797 DeviceUtils.DROPBOX_APP_CRASH_TAGS 798 .toArray( 799 new String 800 [DeviceUtils 801 .DROPBOX_APP_CRASH_TAGS 802 .size()])[0], 803 TEST_PACKAGE_NAME + " entry2"))) 804 .when(sut) 805 .getDropboxEntries(DeviceUtils.DROPBOX_APP_CRASH_TAGS); 806 807 String result = 808 sut 809 .getDropboxEntries( 810 DeviceUtils.DROPBOX_APP_CRASH_TAGS, 811 TEST_PACKAGE_NAME, 812 startTime, 813 null) 814 .stream() 815 .map(DropboxEntry::toString) 816 .collect(Collectors.joining("\n")); 817 818 assertThat(result).doesNotContain("entry1"); 819 assertThat(result).contains("entry2"); 820 } 821 822 @Test getDropboxEntries_noEntries_returnsEmptyList()823 public void getDropboxEntries_noEntries_returnsEmptyList() throws Exception { 824 DeviceUtils sut = createSubjectUnderTest(); 825 when(mRunUtil.runTimedCmd( 826 Mockito.anyLong(), 827 Mockito.eq("sh"), 828 Mockito.eq("-c"), 829 Mockito.contains("dumpsys dropbox --proto"))) 830 .thenReturn(createSuccessfulCommandResultWithStdout("")); 831 832 List<DropboxEntry> result = sut.getDropboxEntries(Set.of("")); 833 834 assertThat(result).isEmpty(); 835 } 836 837 @Test getDropboxEntries_entryExists_returnsEntry()838 public void getDropboxEntries_entryExists_returnsEntry() throws Exception { 839 Path dumpFile = Files.createTempFile(mFileSystem.getPath("/"), "dropbox", ".proto"); 840 long time = 123; 841 String data = "abc"; 842 String tag = "tag"; 843 DropBoxManagerServiceDumpProto proto = 844 DropBoxManagerServiceDumpProto.newBuilder() 845 .addEntries( 846 DropBoxManagerServiceDumpProto.Entry.newBuilder() 847 .setTimeMs(time) 848 .setData(ByteString.copyFromUtf8(data))) 849 .build(); 850 Files.write(dumpFile, proto.toByteArray()); 851 DeviceUtils sut = createSubjectUnderTestWithTempFile(dumpFile); 852 when(mRunUtil.runTimedCmd( 853 Mockito.anyLong(), 854 Mockito.eq("sh"), 855 Mockito.eq("-c"), 856 Mockito.contains("dumpsys dropbox --proto"))) 857 .thenReturn(createSuccessfulCommandResultWithStdout("")); 858 859 List<DropboxEntry> result = sut.getDropboxEntries(Set.of(tag)); 860 861 assertThat(result.get(0).getTime()).isEqualTo(time); 862 assertThat(result.get(0).getData()).isEqualTo(data); 863 assertThat(result.get(0).getTag()).isEqualTo(tag); 864 } 865 866 @Test getDropboxEntriesFromStdout_entryExists_returnsEntry()867 public void getDropboxEntriesFromStdout_entryExists_returnsEntry() throws Exception { 868 when(mRunUtil.runTimedCmd( 869 Mockito.anyLong(), 870 Mockito.eq("sh"), 871 Mockito.eq("-c"), 872 Mockito.contains("dumpsys dropbox --file"))) 873 .thenReturn(createSuccessfulCommandResultWithStdout("")); 874 when(mRunUtil.runTimedCmd( 875 Mockito.anyLong(), 876 Mockito.eq("sh"), 877 Mockito.eq("-c"), 878 Mockito.contains("dumpsys dropbox --print"))) 879 .thenReturn(createSuccessfulCommandResultWithStdout("")); 880 Path fileDumpFile = Files.createTempFile(mFileSystem.getPath("/"), "file", ".dump"); 881 Path printDumpFile = Files.createTempFile(mFileSystem.getPath("/"), "print", ".dump"); 882 String fileResult = 883 "Drop box contents: 351 entries\n" 884 + "Max entries: 1000\n" 885 + "Low priority rate limit period: 2000 ms\n" 886 + "Low priority tags: {data_app_wtf, keymaster, system_server_wtf," 887 + " system_app_strictmode, system_app_wtf, system_server_strictmode," 888 + " data_app_strictmode, netstats}\n" 889 + "\n" 890 + "2022-09-05 04:17:21 system_server_wtf (text, 1730 bytes)\n" 891 + " /data/system/dropbox/system_server_wtf@1662351441269.txt\n" 892 + "2022-09-05 04:31:06 event_data (text, 39 bytes)\n" 893 + " /data/system/dropbox/event_data@1662352266197.txt\n"; 894 String printResult = 895 "Drop box contents: 351 entries\n" 896 + "Max entries: 1000\n" 897 + "Low priority rate limit period: 2000 ms\n" 898 + "Low priority tags: {data_app_wtf, keymaster, system_server_wtf," 899 + " system_app_strictmode, system_app_wtf, system_server_strictmode," 900 + " data_app_strictmode, netstats}\n" 901 + "\n" 902 + "========================================\n" 903 + "2022-09-05 04:17:21 system_server_wtf (text, 1730 bytes)\n" 904 + "Process: system_server\n" 905 + "Subject: ActivityManager\n" 906 + "Build:" 907 + " generic/cf_x86_64_phone/vsoc_x86_64:UpsideDownCake/MASTER/8990215:userdebug/dev-keys\n" 908 + "Dropped-Count: 0\n" 909 + "\n" 910 + "android.util.Log$TerribleFailure: Sending non-protected broadcast" 911 + " com.android.bluetooth.btservice.BLUETOOTH_COUNTER_METRICS_ACTION from" 912 + " system uid 1002 pkg com.android.bluetooth\n" 913 + " at android.util.Log.wtf(Log.java:332)\n" 914 + " at android.util.Log.wtf(Log.java:326)\n" 915 + " at" 916 + " com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:13609)\n" 917 + " at" 918 + " com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:14330)\n" 919 + " at" 920 + " com.android.server.am.ActivityManagerService.broadcastIntentInPackage(ActivityManagerService.java:14530)\n" 921 + " at" 922 + " com.android.server.am.ActivityManagerService$LocalService.broadcastIntentInPackage(ActivityManagerService.java:17065)\n" 923 + " at" 924 + " com.android.server.am.PendingIntentRecord.sendInner(PendingIntentRecord.java:526)\n" 925 + " at" 926 + " com.android.server.am.PendingIntentRecord.sendWithResult(PendingIntentRecord.java:311)\n" 927 + " at" 928 + " com.android.server.am.ActivityManagerService.sendIntentSender(ActivityManagerService.java:5379)\n" 929 + " at" 930 + " android.app.PendingIntent.sendAndReturnResult(PendingIntent.java:1012)\n" 931 + " at android.app.PendingIntent.send(PendingIntent.java:983)\n" 932 + " at" 933 + " com.android.server.alarm.AlarmManagerService$DeliveryTracker.deliverLocked(AlarmManagerService.java:5500)\n" 934 + " at" 935 + " com.android.server.alarm.AlarmManagerService.deliverAlarmsLocked(AlarmManagerService.java:4400)\n" 936 + " at" 937 + " com.android.server.alarm.AlarmManagerService$AlarmThread.run(AlarmManagerService.java:4711)\n" 938 + "Caused by: java.lang.Throwable\n" 939 + " at" 940 + " com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:13610)\n" 941 + " ... 11 more\n" 942 + "\n" 943 + "========================================\n" 944 + "2022-09-05 04:31:06 event_data (text, 39 bytes)\n" 945 + "start=1662350731248\n" 946 + "end=1662352266140\n" 947 + "\n"; 948 Files.write(fileDumpFile, fileResult.getBytes()); 949 Files.write(printDumpFile, printResult.getBytes()); 950 DeviceUtils sut = createSubjectUnderTestWithTempFile(fileDumpFile, printDumpFile); 951 952 List<DropboxEntry> result = sut.getDropboxEntriesFromStdout(Set.of("system_server_wtf")); 953 954 assertThat(result.get(0).getTime()).isEqualTo(1662351441269L); 955 assertThat(result.get(0).getData()).contains("Sending non-protected broadcast"); 956 assertThat(result.get(0).getTag()).isEqualTo("system_server_wtf"); 957 assertThat(result.size()).isEqualTo(1); 958 } 959 createSubjectUnderTestWithTempFile(Path... tempFiles)960 private DeviceUtils createSubjectUnderTestWithTempFile(Path... tempFiles) { 961 when(mDevice.getSerialNumber()).thenReturn("SERIAL"); 962 FakeClock fakeClock = new FakeClock(); 963 Iterator<Path> iter = Arrays.asList(tempFiles).iterator(); 964 return new DeviceUtils( 965 mDevice, fakeClock.getSleeper(), fakeClock, () -> mRunUtil, () -> iter.next()); 966 } 967 createSubjectUnderTest()968 private DeviceUtils createSubjectUnderTest() throws DeviceNotAvailableException { 969 when(mDevice.getSerialNumber()).thenReturn("SERIAL"); 970 when(mDevice.executeShellV2Command(Mockito.startsWith("echo ${EPOCHREALTIME"))) 971 .thenReturn(createSuccessfulCommandResultWithStdout("1")); 972 when(mDevice.executeShellV2Command(Mockito.eq("getprop ro.build.version.sdk"))) 973 .thenReturn(createSuccessfulCommandResultWithStdout("34")); 974 FakeClock fakeClock = new FakeClock(); 975 return new DeviceUtils( 976 mDevice, 977 fakeClock.getSleeper(), 978 fakeClock, 979 () -> mRunUtil, 980 () -> Files.createTempFile(mFileSystem.getPath("/"), "test", ".tmp")); 981 } 982 983 private static class FakeClock implements DeviceUtils.Clock { 984 private long mCurrentTime = System.currentTimeMillis(); 985 private DeviceUtils.Sleeper mSleeper = duration -> mCurrentTime += duration; 986 getSleeper()987 private DeviceUtils.Sleeper getSleeper() { 988 return mSleeper; 989 } 990 991 @Override currentTimeMillis()992 public long currentTimeMillis() { 993 return mCurrentTime += 1; 994 } 995 } 996 contains(String... args)997 private static ArgumentMatcher<String[]> contains(String... args) { 998 return array -> Arrays.asList(array).containsAll(Arrays.asList(args)); 999 } 1000 createSuccessfulCommandResultWithStdout(String stdout)1001 private static CommandResult createSuccessfulCommandResultWithStdout(String stdout) { 1002 CommandResult commandResult = new CommandResult(CommandStatus.SUCCESS); 1003 commandResult.setExitCode(0); 1004 commandResult.setStdout(stdout); 1005 commandResult.setStderr(""); 1006 return commandResult; 1007 } 1008 createFailedCommandResult()1009 private static CommandResult createFailedCommandResult() { 1010 CommandResult commandResult = new CommandResult(CommandStatus.FAILED); 1011 commandResult.setExitCode(1); 1012 commandResult.setStdout(""); 1013 commandResult.setStderr("error"); 1014 return commandResult; 1015 } 1016 } 1017