1 /* 2 * Copyright (C) 2010 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.tradefed.command; 17 18 import com.android.ddmlib.IDevice; 19 import com.android.tradefed.command.CommandFileParser.CommandLine; 20 import com.android.tradefed.command.CommandScheduler.CommandTracker; 21 import com.android.tradefed.command.CommandScheduler.CommandTrackerIdComparator; 22 import com.android.tradefed.command.ICommandScheduler.IScheduledInvocationListener; 23 import com.android.tradefed.config.ConfigurationDescriptor; 24 import com.android.tradefed.config.ConfigurationException; 25 import com.android.tradefed.config.ConfigurationFactory; 26 import com.android.tradefed.config.DeviceConfigurationHolder; 27 import com.android.tradefed.config.IConfiguration; 28 import com.android.tradefed.config.IConfigurationFactory; 29 import com.android.tradefed.config.IDeviceConfiguration; 30 import com.android.tradefed.config.IGlobalConfiguration; 31 import com.android.tradefed.config.OptionSetter; 32 import com.android.tradefed.device.DeviceNotAvailableException; 33 import com.android.tradefed.device.DeviceSelectionOptions; 34 import com.android.tradefed.device.FreeDeviceState; 35 import com.android.tradefed.device.IDeviceManager; 36 import com.android.tradefed.device.ITestDevice; 37 import com.android.tradefed.device.ITestDevice.RecoveryMode; 38 import com.android.tradefed.device.MockDeviceManager; 39 import com.android.tradefed.device.NoDeviceException; 40 import com.android.tradefed.device.StubDevice; 41 import com.android.tradefed.device.TcpDevice; 42 import com.android.tradefed.device.TestDeviceState; 43 import com.android.tradefed.invoker.IInvocationContext; 44 import com.android.tradefed.invoker.IRescheduler; 45 import com.android.tradefed.invoker.ITestInvocation; 46 import com.android.tradefed.invoker.InvocationContext; 47 import com.android.tradefed.log.ILogRegistry.EventType; 48 import com.android.tradefed.log.ITerribleFailureHandler; 49 import com.android.tradefed.log.LogUtil.CLog; 50 import com.android.tradefed.result.ITestInvocationListener; 51 import com.android.tradefed.util.FileUtil; 52 import com.android.tradefed.util.RunUtil; 53 import com.android.tradefed.util.keystore.DryRunKeyStore; 54 import com.android.tradefed.util.keystore.IKeyStoreClient; 55 56 import junit.framework.TestCase; 57 58 import org.easymock.EasyMock; 59 import org.easymock.IAnswer; 60 import org.json.JSONArray; 61 import org.json.JSONException; 62 import org.junit.Assert; 63 64 import java.io.ByteArrayOutputStream; 65 import java.io.File; 66 import java.io.OutputStream; 67 import java.io.PrintWriter; 68 import java.lang.Thread.UncaughtExceptionHandler; 69 import java.util.ArrayList; 70 import java.util.Arrays; 71 import java.util.Collections; 72 import java.util.List; 73 import java.util.Map; 74 75 76 /** 77 * Unit tests for {@link CommandScheduler}. 78 */ 79 public class CommandSchedulerTest extends TestCase { 80 81 private static final long SHORT_WAIT_MS = 100L; 82 83 private CommandScheduler mScheduler; 84 private ITestInvocation mMockInvocation; 85 private MockDeviceManager mMockManager; 86 private IConfigurationFactory mMockConfigFactory; 87 private IConfiguration mMockConfiguration; 88 private CommandOptions mCommandOptions; 89 private DeviceSelectionOptions mDeviceOptions; 90 private CommandFileParser mMockCmdFileParser; 91 private List<IDeviceConfiguration> mMockDeviceConfig; 92 private ConfigurationDescriptor mMockConfigDescriptor; 93 private IInvocationContext mContext; 94 95 class TestableCommandScheduler extends CommandScheduler { 96 97 @Override createRunInstance()98 ITestInvocation createRunInstance() { 99 return mMockInvocation; 100 } 101 102 @Override getDeviceManager()103 protected IDeviceManager getDeviceManager() { 104 return mMockManager; 105 } 106 107 @Override getConfigFactory()108 protected IConfigurationFactory getConfigFactory() { 109 return mMockConfigFactory; 110 } 111 112 @Override createInvocationContext()113 protected IInvocationContext createInvocationContext() { 114 return mContext; 115 } 116 117 @Override initLogging()118 protected void initLogging() { 119 // ignore 120 } 121 122 @Override cleanUp()123 protected void cleanUp() { 124 // ignore 125 } 126 127 @Override logEvent(EventType event, Map<String, String> args)128 void logEvent(EventType event, Map<String, String> args) { 129 // ignore 130 } 131 132 @Override checkInvocations()133 void checkInvocations() { 134 // ignore 135 } 136 137 @Override createCommandFileParser()138 CommandFileParser createCommandFileParser() { 139 return mMockCmdFileParser; 140 } 141 } 142 143 /** 144 * {@inheritDoc} 145 */ 146 @Override setUp()147 protected void setUp() throws Exception { 148 super.setUp(); 149 150 mMockInvocation = EasyMock.createMock(ITestInvocation.class); 151 mMockManager = new MockDeviceManager(0); 152 mMockConfigFactory = EasyMock.createMock(IConfigurationFactory.class); 153 mMockConfiguration = EasyMock.createMock(IConfiguration.class); 154 mCommandOptions = new CommandOptions(); 155 mDeviceOptions = new DeviceSelectionOptions(); 156 mMockDeviceConfig = new ArrayList<IDeviceConfiguration>(); 157 mMockConfigDescriptor = new ConfigurationDescriptor(); 158 mContext = new InvocationContext(); 159 160 mScheduler = new TestableCommandScheduler(); 161 // not starting the CommandScheduler yet because test methods need to setup mocks first 162 } 163 164 @Override tearDown()165 protected void tearDown() throws Exception { 166 if (mScheduler != null) { 167 mScheduler.shutdown(); 168 } 169 super.tearDown(); 170 } 171 172 /** 173 * Switch all mock objects to replay mode 174 */ replayMocks(Object... additionalMocks)175 private void replayMocks(Object... additionalMocks) { 176 EasyMock.replay(mMockConfigFactory, mMockConfiguration, mMockInvocation); 177 for (Object mock : additionalMocks) { 178 EasyMock.replay(mock); 179 } 180 } 181 182 /** 183 * Verify all mock objects 184 */ verifyMocks(Object... additionalMocks)185 private void verifyMocks(Object... additionalMocks) { 186 EasyMock.verify(mMockConfigFactory, mMockConfiguration, mMockInvocation); 187 for (Object mock : additionalMocks) { 188 EasyMock.verify(mock); 189 } 190 mMockManager.assertDevicesFreed(); 191 } 192 193 /** 194 * Test {@link CommandScheduler#run()} when no configs have been added 195 */ testRun_empty()196 public void testRun_empty() throws InterruptedException { 197 mMockManager.setNumDevices(1); 198 replayMocks(); 199 mScheduler.start(); 200 while (!mScheduler.isAlive()) { 201 Thread.sleep(10); 202 } 203 mScheduler.shutdown(); 204 // expect run not to block 205 mScheduler.join(); 206 verifyMocks(); 207 } 208 209 /** 210 * Test {@link CommandScheduler#addCommand(String[])} when help mode is specified 211 */ testAddConfig_configHelp()212 public void testAddConfig_configHelp() throws ConfigurationException { 213 String[] args = new String[] {}; 214 mCommandOptions.setHelpMode(true); 215 setCreateConfigExpectations(args, 1); 216 // expect 217 mMockConfigFactory.printHelpForConfig(EasyMock.aryEq(args), EasyMock.eq(true), 218 EasyMock.eq(System.out)); 219 replayMocks(); 220 mScheduler.start(); 221 mScheduler.addCommand(args); 222 verifyMocks(); 223 } 224 225 /** 226 * Test {@link CommandScheduler#addCommand(String[])} when json help mode is specified 227 */ testAddConfig_configJsonHelp()228 public void testAddConfig_configJsonHelp() throws ConfigurationException, JSONException { 229 String[] args = new String[] {}; 230 mCommandOptions.setJsonHelpMode(true); 231 setCreateConfigExpectations(args, 1); 232 // expect 233 EasyMock.expect(mMockConfiguration.getJsonCommandUsage()).andReturn(new JSONArray()); 234 replayMocks(); 235 mScheduler.start(); 236 mScheduler.addCommand(args); 237 verifyMocks(); 238 } 239 240 /** 241 * Test {@link CommandScheduler#run()} when one config has been added 242 */ testRun_oneConfig()243 public void testRun_oneConfig() throws Throwable { 244 String[] args = new String[] {}; 245 mMockManager.setNumDevices(2); 246 setCreateConfigExpectations(args, 1); 247 setExpectedInvokeCalls(1); 248 mMockConfiguration.validateOptions(); 249 replayMocks(); 250 mScheduler.start(); 251 mScheduler.addCommand(args); 252 mScheduler.shutdownOnEmpty(); 253 mScheduler.join(); 254 verifyMocks(); 255 } 256 257 /** 258 * Test {@link CommandScheduler#removeAllCommands()} for idle case, where command is waiting for 259 * device. 260 */ testRemoveAllCommands()261 public void testRemoveAllCommands() throws Throwable { 262 String[] args = new String[] {}; 263 mMockManager.setNumDevices(0); 264 setCreateConfigExpectations(args, 1); 265 mMockConfiguration.validateOptions(); 266 replayMocks(); 267 mScheduler.start(); 268 mScheduler.addCommand(args); 269 assertEquals(1, mScheduler.getAllCommandsSize()); 270 mScheduler.removeAllCommands(); 271 assertEquals(0, mScheduler.getAllCommandsSize()); 272 verifyMocks(); 273 } 274 275 /** 276 * Test {@link CommandScheduler#run()} when one config has been added in dry-run mode 277 */ testRun_dryRun()278 public void testRun_dryRun() throws Throwable { 279 String[] dryRunArgs = new String[] {"--dry-run"}; 280 mCommandOptions.setDryRunMode(true); 281 mMockManager.setNumDevices(2); 282 setCreateConfigExpectations(dryRunArgs, 1); 283 284 // add a second command, to verify the first dry-run command did not get added 285 String[] args2 = new String[] {}; 286 setCreateConfigExpectations(args2, 1); 287 setExpectedInvokeCalls(1); 288 mMockConfiguration.validateOptions(); 289 EasyMock.expectLastCall().times(2); 290 291 replayMocks(); 292 mScheduler.start(); 293 assertFalse(mScheduler.addCommand(dryRunArgs)); 294 // the same config object is being used, so clear its state 295 mCommandOptions.setDryRunMode(false); 296 assertTrue(mScheduler.addCommand(args2)); 297 mScheduler.shutdownOnEmpty(); 298 mScheduler.join(); 299 verifyMocks(); 300 } 301 302 /** 303 * Test {@link CommandScheduler#run()} when one config has been added in noisy-dry-run or 304 * dry-run mode the keystore is properly faked by a {@link DryRunKeyStore}. 305 */ testRun_dryRun_keystore()306 public void testRun_dryRun_keystore() throws Throwable { 307 mScheduler = 308 new TestableCommandScheduler() { 309 @Override 310 protected IConfigurationFactory getConfigFactory() { 311 // Use the real factory for that loading test. 312 return ConfigurationFactory.getInstance(); 313 } 314 }; 315 String[] dryRunArgs = 316 new String[] {"empty", "--noisy-dry-run", "--min-loop-time", "USE_KEYSTORE@fake"}; 317 mMockManager.setNumDevices(2); 318 //setCreateConfigExpectations(dryRunArgs, 1); 319 320 replayMocks(); 321 mScheduler.start(); 322 assertFalse(mScheduler.addCommand(dryRunArgs)); 323 mScheduler.shutdownOnEmpty(); 324 mScheduler.join(); 325 verifyMocks(); 326 } 327 328 /** 329 * Test simple case for 330 * {@link CommandScheduler#execCommand(IScheduledInvocationListener, ITestDevice, String[])} 331 */ 332 @SuppressWarnings("unchecked") testExecCommand()333 public void testExecCommand() throws Throwable { 334 String[] args = new String[] { 335 "foo" 336 }; 337 setCreateConfigExpectations(args, 1); 338 setExpectedInvokeCalls(1); 339 mMockConfiguration.validateOptions(); 340 IDevice mockIDevice = EasyMock.createMock(IDevice.class); 341 ITestDevice mockDevice = EasyMock.createMock(ITestDevice.class); 342 EasyMock.expect(mockDevice.getSerialNumber()).andStubReturn("serial"); 343 EasyMock.expect(mockDevice.getDeviceState()).andStubReturn(TestDeviceState.ONLINE); 344 mockDevice.setRecoveryMode(EasyMock.eq(RecoveryMode.AVAILABLE)); 345 EasyMock.expect(mockDevice.getIDevice()).andStubReturn(mockIDevice); 346 IScheduledInvocationListener mockListener = EasyMock 347 .createMock(IScheduledInvocationListener.class); 348 mockListener.invocationInitiated((IInvocationContext) EasyMock.anyObject()); 349 mockListener.invocationComplete((IInvocationContext)EasyMock.anyObject(), 350 (Map<ITestDevice, FreeDeviceState>)EasyMock.anyObject()); 351 EasyMock.expect(mockDevice.waitForDeviceShell(EasyMock.anyLong())).andReturn(true); 352 replayMocks(mockDevice, mockListener); 353 mScheduler.start(); 354 mScheduler.execCommand(mockListener, mockDevice, args); 355 mScheduler.shutdownOnEmpty(); 356 mScheduler.join(2*1000); 357 verifyMocks(mockListener); 358 } 359 360 /** 361 * Sets the number of expected 362 * {@link ITestInvocation#invoke(IInvocationContext, IConfiguration, IRescheduler, 363 * ITestInvocationListener[])} calls 364 * 365 * @param times 366 */ setExpectedInvokeCalls(int times)367 private void setExpectedInvokeCalls(int times) throws Throwable { 368 mMockInvocation.invoke((IInvocationContext)EasyMock.anyObject(), 369 (IConfiguration)EasyMock.anyObject(), (IRescheduler)EasyMock.anyObject(), 370 (ITestInvocationListener)EasyMock.anyObject()); 371 EasyMock.expectLastCall().times(times); 372 } 373 374 /** 375 * Sets up a object that will notify when the expected number of 376 * {@link ITestInvocation#invoke(IInvocationContext, IConfiguration, IRescheduler, 377 * ITestInvocationListener[])} calls occurs 378 * 379 * @param times 380 */ waitForExpectedInvokeCalls(final int times)381 private Object waitForExpectedInvokeCalls(final int times) throws Throwable { 382 IAnswer<Object> blockResult = new IAnswer<Object>() { 383 private int mCalls = 0; 384 @Override 385 public Object answer() throws Throwable { 386 synchronized(this) { 387 mCalls++; 388 if (times == mCalls) { 389 notifyAll(); 390 } 391 } 392 return null; 393 } 394 }; 395 mMockInvocation.invoke((IInvocationContext)EasyMock.anyObject(), 396 (IConfiguration)EasyMock.anyObject(), (IRescheduler)EasyMock.anyObject(), 397 (ITestInvocationListener)EasyMock.anyObject()); 398 EasyMock.expectLastCall().andAnswer(blockResult); 399 EasyMock.expectLastCall().andAnswer(blockResult); 400 return blockResult; 401 } 402 403 /** 404 * Test {@link CommandScheduler#run()} when one config has been added in a loop 405 */ testRun_oneConfigLoop()406 public void testRun_oneConfigLoop() throws Throwable { 407 String[] args = new String[] {}; 408 // track if exception occurs on scheduler thread 409 UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); 410 try { 411 ExceptionTracker tracker = new ExceptionTracker(); 412 Thread.setDefaultUncaughtExceptionHandler(tracker); 413 mMockManager.setNumDevices(1); 414 // config should only be created three times 415 setCreateConfigExpectations(args, 3); 416 mCommandOptions.setLoopMode(true); 417 mCommandOptions.setMinLoopTime(50); 418 Object notifier = waitForExpectedInvokeCalls(2); 419 mMockConfiguration.validateOptions(); 420 replayMocks(); 421 mScheduler.start(); 422 mScheduler.addCommand(args); 423 synchronized (notifier) { 424 notifier.wait(1 * 1000); 425 } 426 mScheduler.shutdown(); 427 mScheduler.join(); 428 // Wait a little for device to be released. 429 RunUtil.getDefault().sleep(SHORT_WAIT_MS); 430 verifyMocks(); 431 assertNull("exception occurred on background thread!", tracker.mThrowable); 432 } finally { 433 Thread.setDefaultUncaughtExceptionHandler(defaultHandler); 434 } 435 } 436 437 class ExceptionTracker implements UncaughtExceptionHandler { 438 439 private Throwable mThrowable = null; 440 441 /** 442 * {@inheritDoc} 443 */ 444 @Override uncaughtException(Thread t, Throwable e)445 public void uncaughtException(Thread t, Throwable e) { 446 e.printStackTrace(); 447 mThrowable = e; 448 } 449 } 450 451 /** 452 * Verify that scheduler goes into shutdown mode when a {@link FatalHostError} is thrown. 453 */ testRun_fatalError()454 public void testRun_fatalError() throws Throwable { 455 mMockInvocation.invoke((IInvocationContext)EasyMock.anyObject(), 456 (IConfiguration)EasyMock.anyObject(), (IRescheduler)EasyMock.anyObject(), 457 (ITestInvocationListener)EasyMock.anyObject()); 458 EasyMock.expectLastCall().andThrow(new FatalHostError("error")); 459 // set up a mock global config and wtfhandler to handle CLog.wtf when FatalHostError occurs 460 IGlobalConfiguration mockGc = EasyMock.createMock(IGlobalConfiguration.class); 461 CLog.setGlobalConfigInstance(mockGc); 462 try { 463 ITerribleFailureHandler mockWtf = EasyMock.createMock(ITerribleFailureHandler.class); 464 EasyMock.expect(mockGc.getWtfHandler()).andReturn(mockWtf).anyTimes(); 465 EasyMock.expect(mockWtf.onTerribleFailure((String)EasyMock.anyObject(), 466 (Throwable)EasyMock.anyObject())).andReturn(Boolean.TRUE); 467 String[] args = new String[] {}; 468 mMockManager.setNumDevices(2); 469 setCreateConfigExpectations(args, 1); 470 mMockConfiguration.validateOptions(); 471 replayMocks(mockGc, mockWtf); 472 mScheduler.start(); 473 mScheduler.addCommand(args); 474 // no need to call shutdown explicitly - scheduler should shutdown by itself 475 mScheduler.join(2 * 1000); 476 // We don't verify the mockManager for this test since after failure, the device might 477 // not have time to go back to list before shutdown on scheduler. 478 EasyMock.verify( 479 mMockConfigFactory, mMockConfiguration, mMockInvocation, mockGc, mockWtf); 480 } finally { 481 // reset global config to null, which means 'not overloaded/use default' 482 CLog.setGlobalConfigInstance(null); 483 } 484 } 485 486 /** 487 * Test{@link CommandScheduler#run()} when config is matched to a specific device serial number 488 * <p/> 489 * Adds two configs to run, and verify they both run on one device 490 */ testRun_configSerial()491 public void testRun_configSerial() throws Throwable { 492 String[] args = new String[] {}; 493 mMockManager.setNumDevices(2); 494 setCreateConfigExpectations(args, 2); 495 // allocate and free a device to get its serial 496 ITestDevice dev = mMockManager.allocateDevice(); 497 mDeviceOptions.addSerial(dev.getSerialNumber()); 498 setExpectedInvokeCalls(1); 499 mMockConfiguration.validateOptions(); 500 mMockConfiguration.validateOptions(); 501 replayMocks(); 502 mScheduler.start(); 503 mScheduler.addCommand(args); 504 mScheduler.addCommand(args); 505 mMockManager.freeDevice(dev, FreeDeviceState.AVAILABLE); 506 507 mScheduler.shutdownOnEmpty(); 508 mScheduler.join(); 509 verifyMocks(); 510 } 511 512 /** 513 * Test{@link CommandScheduler#run()} when config is matched to a exclude specific device serial 514 * number. 515 * <p/> 516 * Adds two configs to run, and verify they both run on the other device 517 */ testRun_configExcludeSerial()518 public void testRun_configExcludeSerial() throws Throwable { 519 String[] args = new String[] {}; 520 mMockManager.setNumDevices(2); 521 setCreateConfigExpectations(args, 2); 522 // allocate and free a device to get its serial 523 ITestDevice dev = mMockManager.allocateDevice(); 524 mDeviceOptions.addExcludeSerial(dev.getSerialNumber()); 525 ITestDevice expectedDevice = mMockManager.allocateDevice(); 526 setExpectedInvokeCalls(1); 527 mMockConfiguration.validateOptions(); 528 mMockConfiguration.validateOptions(); 529 replayMocks(); 530 mScheduler.start(); 531 mScheduler.addCommand(args); 532 mScheduler.addCommand(args); 533 mMockManager.freeDevice(dev, FreeDeviceState.AVAILABLE); 534 mMockManager.freeDevice(expectedDevice, FreeDeviceState.AVAILABLE); 535 mScheduler.shutdownOnEmpty(); 536 mScheduler.join(); 537 verifyMocks(); 538 } 539 540 /** 541 * Test {@link CommandScheduler#run()} when one config has been rescheduled 542 */ testRun_rescheduled()543 public void testRun_rescheduled() throws Throwable { 544 String[] args = new String[] {}; 545 mMockManager.setNumDevices(2); 546 setCreateConfigExpectations(args, 1); 547 mMockConfiguration.validateOptions(); 548 final IConfiguration rescheduledConfig = EasyMock.createMock(IConfiguration.class); 549 EasyMock.expect(rescheduledConfig.getCommandOptions()).andStubReturn(mCommandOptions); 550 EasyMock.expect(rescheduledConfig.getDeviceRequirements()).andStubReturn( 551 mDeviceOptions); 552 EasyMock.expect(rescheduledConfig.getDeviceConfig()).andStubReturn(mMockDeviceConfig); 553 EasyMock.expect(rescheduledConfig.getCommandLine()).andStubReturn(""); 554 EasyMock.expect(rescheduledConfig.getConfigurationDescription()) 555 .andStubReturn(mMockConfigDescriptor); 556 557 // an ITestInvocationn#invoke response for calling reschedule 558 IAnswer<Object> rescheduleAndThrowAnswer = new IAnswer<Object>() { 559 @Override 560 public Object answer() throws Throwable { 561 IRescheduler rescheduler = (IRescheduler) EasyMock.getCurrentArguments()[2]; 562 rescheduler.scheduleConfig(rescheduledConfig); 563 throw new DeviceNotAvailableException("not avail", "fakeserial"); 564 } 565 }; 566 567 mMockInvocation.invoke(EasyMock.<IInvocationContext>anyObject(), 568 EasyMock.<IConfiguration>anyObject(), EasyMock.<IRescheduler>anyObject(), 569 EasyMock.<ITestInvocationListener>anyObject()); 570 EasyMock.expectLastCall().andAnswer(rescheduleAndThrowAnswer); 571 572 // expect one more success call 573 setExpectedInvokeCalls(1); 574 575 replayMocks(rescheduledConfig); 576 mScheduler.start(); 577 mScheduler.addCommand(args); 578 mScheduler.shutdownOnEmpty(); 579 mScheduler.join(); 580 581 EasyMock.verify(mMockConfigFactory, mMockConfiguration, mMockInvocation); 582 } 583 584 /** 585 * Simple success case test for {@link CommandScheduler#addCommandFile(String, java.util.List)} 586 * @throws ConfigurationException 587 */ testAddCommandFile()588 public void testAddCommandFile() throws ConfigurationException { 589 // set number of devices to 0 so we can verify command presence 590 mMockManager.setNumDevices(0); 591 List<String> extraArgs = Arrays.asList("--bar"); 592 setCreateConfigExpectations(new String[] {"foo", "--bar"}, 1); 593 mMockConfiguration.validateOptions(); 594 final List<CommandLine> cmdFileContent = Arrays.asList(new CommandLine( 595 Arrays.asList("foo"), null, 0)); 596 mMockCmdFileParser = new CommandFileParser() { 597 @Override 598 public List<CommandLine> parseFile(File cmdFile) { 599 return cmdFileContent; 600 } 601 }; 602 replayMocks(); 603 604 mScheduler.start(); 605 mScheduler.addCommandFile("mycmd.txt", extraArgs); 606 List<CommandTracker> cmds = mScheduler.getCommandTrackers(); 607 assertEquals(1, cmds.size()); 608 assertEquals("foo", cmds.get(0).getArgs()[0]); 609 assertEquals("--bar", cmds.get(0).getArgs()[1]); 610 } 611 612 /** 613 * Simple success case test for auto reloading a command file 614 * 615 * @throws ConfigurationException 616 */ testAddCommandFile_reload()617 public void testAddCommandFile_reload() throws ConfigurationException { 618 // set number of devices to 0 so we can verify command presence 619 mMockManager.setNumDevices(0); 620 String[] addCommandArgs = new String[]{"fromcommand"}; 621 List<String> extraArgs = Arrays.asList("--bar"); 622 623 setCreateConfigExpectations(addCommandArgs, 1); 624 String[] cmdFile1Args = new String[] {"fromFile1", "--bar"}; 625 setCreateConfigExpectations(cmdFile1Args, 1); 626 String[] cmdFile2Args = new String[] {"fromFile2", "--bar"}; 627 setCreateConfigExpectations(cmdFile2Args, 1); 628 629 mMockConfiguration.validateOptions(); 630 EasyMock.expectLastCall().times(3); 631 632 final List<CommandLine> cmdFileContent1 = Arrays.asList(new CommandLine( 633 Arrays.asList("fromFile1"), null, 0)); 634 final List<CommandLine> cmdFileContent2 = Arrays.asList(new CommandLine( 635 Arrays.asList("fromFile2"), null, 0)); 636 mMockCmdFileParser = new CommandFileParser() { 637 boolean firstCall = true; 638 @Override 639 public List<CommandLine> parseFile(File cmdFile) { 640 if (firstCall) { 641 firstCall = false; 642 return cmdFileContent1; 643 } 644 return cmdFileContent2; 645 } 646 }; 647 replayMocks(); 648 mScheduler.start(); 649 mScheduler.setCommandFileReload(true); 650 mScheduler.addCommand(addCommandArgs); 651 mScheduler.addCommandFile("mycmd.txt", extraArgs); 652 653 List<CommandTracker> cmds = mScheduler.getCommandTrackers(); 654 assertEquals(2, cmds.size()); 655 Collections.sort(cmds, new CommandTrackerIdComparator()); 656 Assert.assertArrayEquals(addCommandArgs, cmds.get(0).getArgs()); 657 Assert.assertArrayEquals(cmdFile1Args, cmds.get(1).getArgs()); 658 659 // now reload the command file 660 mScheduler.notifyFileChanged(new File("mycmd.txt"), extraArgs); 661 662 cmds = mScheduler.getCommandTrackers(); 663 assertEquals(2, cmds.size()); 664 Collections.sort(cmds, new CommandTrackerIdComparator()); 665 Assert.assertArrayEquals(addCommandArgs, cmds.get(0).getArgs()); 666 Assert.assertArrayEquals(cmdFile2Args, cmds.get(1).getArgs()); 667 } 668 669 /** 670 * Verify attempts to add the same commmand file in reload mode are rejected 671 */ testAddCommandFile_twice()672 public void testAddCommandFile_twice() throws ConfigurationException { 673 // set number of devices to 0 so we can verify command presence 674 mMockManager.setNumDevices(0); 675 String[] cmdFile1Args = new String[] {"fromFile1"}; 676 setCreateConfigExpectations(cmdFile1Args, 1); 677 setCreateConfigExpectations(cmdFile1Args, 1); 678 mMockConfiguration.validateOptions(); 679 EasyMock.expectLastCall().times(2); 680 681 final List<CommandLine> cmdFileContent1 = Arrays.asList(new CommandLine( 682 Arrays.asList("fromFile1"), null, 0)); 683 mMockCmdFileParser = new CommandFileParser() { 684 @Override 685 public List<CommandLine> parseFile(File cmdFile) { 686 return cmdFileContent1; 687 } 688 }; 689 replayMocks(); 690 mScheduler.start(); 691 mScheduler.setCommandFileReload(true); 692 mScheduler.addCommandFile("mycmd.txt", Collections.<String>emptyList()); 693 694 List<CommandTracker> cmds = mScheduler.getCommandTrackers(); 695 assertEquals(1, cmds.size()); 696 Assert.assertArrayEquals(cmdFile1Args, cmds.get(0).getArgs()); 697 698 // now attempt to add the same command file 699 mScheduler.addCommandFile("mycmd.txt", Collections.<String>emptyList()); 700 701 // expect reload 702 // ensure same state as before 703 cmds = mScheduler.getCommandTrackers(); 704 assertEquals(1, cmds.size()); 705 Assert.assertArrayEquals(cmdFile1Args, cmds.get(0).getArgs()); 706 } 707 708 /** 709 * Test {@link CommandScheduler#shutdown()} when no devices are available. 710 */ testShutdown()711 public void testShutdown() throws Exception { 712 mMockManager.setNumDevices(0); 713 mScheduler.start(); 714 while (!mScheduler.isAlive()) { 715 Thread.sleep(10); 716 } 717 // hack - sleep a bit more to ensure allocateDevices is called 718 Thread.sleep(50); 719 mScheduler.shutdown(); 720 mScheduler.join(); 721 // test will hang if not successful 722 } 723 724 /** 725 * Set EasyMock expectations for a create configuration call. 726 */ setCreateConfigExpectations(String[] args, int times)727 private void setCreateConfigExpectations(String[] args, int times) 728 throws ConfigurationException { 729 List<String> nullArg = null; 730 EasyMock.expect( 731 mMockConfigFactory.createConfigurationFromArgs(EasyMock.aryEq(args), 732 EasyMock.eq(nullArg), (IKeyStoreClient)EasyMock.anyObject())) 733 .andReturn(mMockConfiguration) 734 .times(times); 735 EasyMock.expect(mMockConfiguration.getCommandOptions()).andStubReturn(mCommandOptions); 736 EasyMock.expect(mMockConfiguration.getDeviceRequirements()).andStubReturn( 737 mDeviceOptions); 738 EasyMock.expect(mMockConfiguration.getDeviceConfig()).andStubReturn(mMockDeviceConfig); 739 EasyMock.expect(mMockConfiguration.getCommandLine()).andStubReturn(""); 740 EasyMock.expect(mMockConfiguration.getConfigurationDescription()) 741 .andStubReturn(mMockConfigDescriptor); 742 743 // Assume all legacy test are single device 744 if (mMockDeviceConfig.isEmpty()) { 745 IDeviceConfiguration mockConfig = new DeviceConfigurationHolder("device"); 746 mockConfig.addSpecificConfig(mDeviceOptions); 747 mMockDeviceConfig.add(mockConfig); 748 } 749 } 750 751 /** 752 * Test that Available device at the end of a test are available to be reselected. 753 */ testDeviceReleased()754 public void testDeviceReleased() throws Throwable { 755 String[] args = new String[] {}; 756 mMockManager.setNumDevices(1); 757 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 1); 758 setCreateConfigExpectations(args, 1); 759 setExpectedInvokeCalls(1); 760 mMockConfiguration.validateOptions(); 761 replayMocks(); 762 mScheduler.start(); 763 mScheduler.addCommand(args); 764 mScheduler.shutdownOnEmpty(); 765 mScheduler.join(); 766 verifyMocks(); 767 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 1); 768 } 769 770 /** 771 * Test that NOT_AVAILABLE devices at the end of a test are not returned to the selectable 772 * devices. 773 */ testDeviceReleased_unavailable()774 public void testDeviceReleased_unavailable() throws Throwable { 775 String[] args = new String[] {}; 776 mMockManager.setNumDevicesCustom(1, TestDeviceState.NOT_AVAILABLE, IDevice.class); 777 assert(mMockManager.getQueueOfAvailableDeviceSize() == 1); 778 setCreateConfigExpectations(args, 1); 779 setExpectedInvokeCalls(1); 780 mMockConfiguration.validateOptions(); 781 replayMocks(); 782 mScheduler.start(); 783 mScheduler.addCommand(args); 784 mScheduler.shutdownOnEmpty(); 785 mScheduler.join(); 786 EasyMock.verify(mMockConfigFactory, mMockConfiguration, mMockInvocation); 787 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 0); 788 } 789 790 /** 791 * Test that only the device NOT_AVAILABLE, selected for invocation is not returned at the end. 792 */ testDeviceReleased_unavailableMulti()793 public void testDeviceReleased_unavailableMulti() throws Throwable { 794 String[] args = new String[] {}; 795 mMockManager.setNumDevicesCustom(2, TestDeviceState.NOT_AVAILABLE, IDevice.class); 796 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 2); 797 setCreateConfigExpectations(args, 1); 798 setExpectedInvokeCalls(1); 799 mMockConfiguration.validateOptions(); 800 replayMocks(); 801 mScheduler.start(); 802 mScheduler.addCommand(args); 803 mScheduler.shutdownOnEmpty(); 804 mScheduler.join(); 805 EasyMock.verify(mMockConfigFactory, mMockConfiguration, mMockInvocation); 806 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 1); 807 } 808 809 /** 810 * Test that the TCP device NOT available are NOT released. 811 */ testTcpDevice_NotReleased()812 public void testTcpDevice_NotReleased() throws Throwable { 813 String[] args = new String[] {}; 814 mMockManager.setNumDevicesStub(1, TestDeviceState.NOT_AVAILABLE, new TcpDevice("serial")); 815 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 1); 816 setCreateConfigExpectations(args, 1); 817 setExpectedInvokeCalls(1); 818 mMockConfiguration.validateOptions(); 819 replayMocks(); 820 mScheduler.start(); 821 mScheduler.addCommand(args); 822 mScheduler.shutdownOnEmpty(); 823 mScheduler.join(); 824 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 1); 825 EasyMock.verify(mMockConfigFactory, mMockConfiguration, mMockInvocation); 826 } 827 828 /** 829 * Test that the TCP device NOT available selected for a run is NOT released. 830 */ testTcpDevice_NotReleasedMulti()831 public void testTcpDevice_NotReleasedMulti() throws Throwable { 832 String[] args = new String[] {}; 833 mMockManager.setNumDevicesStub(2, TestDeviceState.NOT_AVAILABLE, new TcpDevice("serial")); 834 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 2); 835 setCreateConfigExpectations(args, 1); 836 setExpectedInvokeCalls(1); 837 mMockConfiguration.validateOptions(); 838 replayMocks(); 839 mScheduler.start(); 840 mScheduler.addCommand(args); 841 mScheduler.shutdownOnEmpty(); 842 mScheduler.join(); 843 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 2); 844 EasyMock.verify(mMockConfigFactory, mMockConfiguration, mMockInvocation); 845 } 846 847 /** 848 * Test that the Stub device NOT available are NOT released. 849 */ testStubDevice_NotReleased()850 public void testStubDevice_NotReleased() throws Throwable { 851 String[] args = new String[] {}; 852 IDevice stub = new StubDevice("emulator-5554", true); 853 mMockManager.setNumDevicesStub(1, TestDeviceState.NOT_AVAILABLE, stub); 854 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 1); 855 setCreateConfigExpectations(args, 1); 856 setExpectedInvokeCalls(1); 857 mMockConfiguration.validateOptions(); 858 replayMocks(); 859 mScheduler.start(); 860 mScheduler.addCommand(args); 861 mScheduler.shutdownOnEmpty(); 862 mScheduler.join(); 863 assertTrue(mMockManager.getQueueOfAvailableDeviceSize() == 1); 864 EasyMock.verify(mMockConfigFactory, mMockConfiguration, mMockInvocation); 865 } 866 867 /** 868 * Test that a device recovery state is reset when returned to the available queue. 869 */ testDeviceRecoveryState()870 public void testDeviceRecoveryState() throws Throwable { 871 String[] args = new String[] {}; 872 mMockManager.setNumDevicesCustomRealNoRecovery(1, IDevice.class); 873 assert(mMockManager.getQueueOfAvailableDeviceSize() == 1); 874 setCreateConfigExpectations(args, 1); 875 setExpectedInvokeCalls(1); 876 mMockConfiguration.validateOptions(); 877 replayMocks(); 878 mScheduler.start(); 879 mScheduler.addCommand(args); 880 mScheduler.shutdownOnEmpty(); 881 mScheduler.join(); 882 EasyMock.verify(mMockConfigFactory, mMockConfiguration, mMockInvocation); 883 assertEquals(1, mMockManager.getQueueOfAvailableDeviceSize()); 884 ITestDevice t = mMockManager.allocateDevice(); 885 assertTrue(t.getRecoveryMode().equals(RecoveryMode.AVAILABLE)); 886 } 887 888 /** 889 * Test that a device that is unresponsive at the end of an invocation is made unavailable. 890 */ testDevice_unresponsive()891 public void testDevice_unresponsive() throws Throwable { 892 String[] args = new String[] {}; 893 mMockManager.setNumDevicesUnresponsive(1); 894 assert(mMockManager.getQueueOfAvailableDeviceSize() == 1); 895 setCreateConfigExpectations(args, 1); 896 setExpectedInvokeCalls(1); 897 mMockConfiguration.validateOptions(); 898 replayMocks(); 899 mScheduler.start(); 900 mScheduler.addCommand(args); 901 mScheduler.shutdownOnEmpty(); 902 mScheduler.join(); 903 EasyMock.verify(mMockConfigFactory, mMockConfiguration, mMockInvocation); 904 // Device does not return to the list since it's unavailable. 905 assertEquals(0, mMockManager.getQueueOfAvailableDeviceSize()); 906 } 907 908 /** 909 * Test that {@link CommandScheduler#displayCommandQueue(PrintWriter)} is properly printing 910 * the state of a command. 911 */ testDisplayCommandQueue()912 public void testDisplayCommandQueue() throws Throwable { 913 String[] args = new String[] {"empty"}; 914 setCreateConfigExpectations(args, 1); 915 mMockConfiguration.validateOptions(); 916 replayMocks(); 917 mScheduler.start(); 918 mScheduler.addCommand(args); 919 OutputStream res = new ByteArrayOutputStream(); 920 PrintWriter pw = new PrintWriter(res); 921 mScheduler.displayCommandQueue(pw); 922 verifyMocks(); 923 pw.flush(); 924 assertEquals("Id Config Created Exec time State Sleep time Rescheduled " 925 + "Loop \n1 empty 0m:00 0m:00 Wait_for_device N/A false " 926 + " false \n", res.toString()); 927 mScheduler.shutdown(); 928 } 929 930 /** 931 * Test that {@link CommandScheduler#dumpCommandsXml(PrintWriter, String)} is properly printing 932 * the xml of a command. 933 */ testDumpCommandXml()934 public void testDumpCommandXml() throws Throwable { 935 String[] args = new String[] {"empty"}; 936 OutputStream res = new ByteArrayOutputStream(); 937 PrintWriter pw = new PrintWriter(res); 938 setCreateConfigExpectations(args, 1); 939 mMockConfiguration.validateOptions(); 940 mMockConfiguration.dumpXml(EasyMock.anyObject()); 941 replayMocks(); 942 mScheduler.start(); 943 mScheduler.addCommand(args); 944 mScheduler.dumpCommandsXml(pw, null); 945 verifyMocks(); 946 pw.flush(); 947 String filename = res.toString().replace("Saved command dump to ", "").trim(); 948 File test = new File(filename); 949 try { 950 assertTrue(test.exists()); 951 mScheduler.shutdown(); 952 } finally { 953 FileUtil.deleteFile(test); 954 } 955 } 956 957 /** 958 * Test that {@link CommandScheduler#displayCommandsInfo(PrintWriter, String)} is properly 959 * printing the command. 960 */ testDisplayCommandsInfo()961 public void testDisplayCommandsInfo() throws Throwable { 962 String[] args = new String[] {"empty"}; 963 setCreateConfigExpectations(args, 1); 964 mMockConfiguration.validateOptions(); 965 replayMocks(); 966 mScheduler.start(); 967 mScheduler.addCommand(args); 968 OutputStream res = new ByteArrayOutputStream(); 969 PrintWriter pw = new PrintWriter(res); 970 mScheduler.displayCommandsInfo(pw, null); 971 verifyMocks(); 972 pw.flush(); 973 assertEquals("Command 1: [0m:00] empty\n", res.toString()); 974 mScheduler.shutdown(); 975 } 976 977 /** 978 * Test that {@link CommandScheduler#getInvocationInfo(int)} is properly returning null if 979 * no invocation matching the id. 980 */ testGetInvocationInfo_null()981 public void testGetInvocationInfo_null() throws Throwable { 982 String[] args = new String[] {"empty", "test"}; 983 setCreateConfigExpectations(args, 1); 984 mMockConfiguration.validateOptions(); 985 replayMocks(); 986 mScheduler.start(); 987 mScheduler.addCommand(args); 988 assertNull(mScheduler.getInvocationInfo(999)); 989 mScheduler.shutdown(); 990 } 991 testAllocateDevices()992 public void testAllocateDevices() throws Exception { 993 String[] args = new String[] {"foo", "test"}; 994 mMockManager.setNumDevices(1); 995 setCreateConfigExpectations(args, 1); 996 mMockConfiguration.validateOptions(); 997 replayMocks(); 998 mScheduler.start(); 999 Map<String, ITestDevice> devices = mScheduler.allocateDevices( 1000 mMockConfiguration, mMockManager); 1001 assertEquals(1, devices.size()); 1002 mScheduler.shutdown(); 1003 } 1004 createDeviceConfig(String serial)1005 private IDeviceConfiguration createDeviceConfig(String serial) throws Exception { 1006 IDeviceConfiguration mockConfig = new DeviceConfigurationHolder(serial); 1007 DeviceSelectionOptions options = new DeviceSelectionOptions(); 1008 options.addSerial(serial); 1009 mockConfig.addSpecificConfig(options); 1010 return mockConfig; 1011 } 1012 testAllocateDevices_multipleDevices()1013 public void testAllocateDevices_multipleDevices() throws Exception { 1014 String[] args = new String[] {"foo", "test"}; 1015 1016 mMockManager.setNumDevices(2); 1017 mMockDeviceConfig.add(createDeviceConfig("serial0")); 1018 mMockDeviceConfig.add(createDeviceConfig("serial1")); 1019 1020 setCreateConfigExpectations(args, 1); 1021 mMockConfiguration.validateOptions(); 1022 replayMocks(); 1023 mScheduler.start(); 1024 Map<String, ITestDevice> devices = mScheduler.allocateDevices( 1025 mMockConfiguration, mMockManager); 1026 assertEquals(2, devices.size()); 1027 assertEquals(0, mMockManager.getQueueOfAvailableDeviceSize()); 1028 mScheduler.shutdown(); 1029 } 1030 testAllocateDevices_multipleDevices_failed()1031 public void testAllocateDevices_multipleDevices_failed() throws Exception { 1032 String[] args = new String[] {"foo", "test"}; 1033 1034 mMockManager.setNumDevices(2); 1035 mMockDeviceConfig.add(createDeviceConfig("serial0")); 1036 mMockDeviceConfig.add(createDeviceConfig("not_exist_serial")); 1037 1038 setCreateConfigExpectations(args, 1); 1039 mMockConfiguration.validateOptions(); 1040 replayMocks(); 1041 mScheduler.start(); 1042 Map<String, ITestDevice> devices = mScheduler.allocateDevices( 1043 mMockConfiguration, mMockManager); 1044 assertEquals(0, devices.size()); 1045 assertEquals(2, mMockManager.getQueueOfAvailableDeviceSize()); 1046 mScheduler.shutdown(); 1047 } 1048 1049 /** 1050 * Test case for execCommand with multiple devices. 1051 * {@link CommandScheduler#execCommand(IScheduledInvocationListener, String[])} 1052 */ 1053 @SuppressWarnings("unchecked") testExecCommand_multipleDevices()1054 public void testExecCommand_multipleDevices() throws Throwable { 1055 String[] args = new String[] { 1056 "foo" 1057 }; 1058 mMockManager.setNumDevices(2); 1059 mMockDeviceConfig.add(createDeviceConfig("serial0")); 1060 mMockDeviceConfig.add(createDeviceConfig("serial1")); 1061 setCreateConfigExpectations(args, 1); 1062 mMockConfiguration.validateOptions(); 1063 mMockInvocation.invoke((IInvocationContext)EasyMock.anyObject(), 1064 (IConfiguration)EasyMock.anyObject(), (IRescheduler)EasyMock.anyObject(), 1065 (ITestInvocationListener)EasyMock.anyObject(), 1066 // This is FreeDeviceHandler. 1067 (IScheduledInvocationListener)EasyMock.anyObject()); 1068 IScheduledInvocationListener mockListener = EasyMock 1069 .createMock(IScheduledInvocationListener.class); 1070 mockListener.invocationInitiated((IInvocationContext) EasyMock.anyObject()); 1071 mockListener.invocationComplete((IInvocationContext)EasyMock.anyObject(), 1072 (Map<ITestDevice, FreeDeviceState>)EasyMock.anyObject()); 1073 replayMocks(mockListener); 1074 1075 mScheduler.start(); 1076 mScheduler.execCommand(mockListener, args); 1077 mScheduler.shutdownOnEmpty(); 1078 mScheduler.join(2 * 1000); 1079 verifyMocks(mockListener); 1080 } 1081 1082 /** 1083 * Test case for execCommand with multiple devices but fail to allocate some device. 1084 * {@link CommandScheduler#execCommand(IScheduledInvocationListener, String[])} 1085 */ testExecCommand_multipleDevices_noDevice()1086 public void testExecCommand_multipleDevices_noDevice() throws Throwable { 1087 String[] args = new String[] { 1088 "foo" 1089 }; 1090 mMockManager.setNumDevices(2); 1091 mMockDeviceConfig.add(createDeviceConfig("serial0")); 1092 mMockDeviceConfig.add(createDeviceConfig("not_exist_serial")); 1093 setCreateConfigExpectations(args, 1); 1094 mMockConfiguration.validateOptions(); 1095 IScheduledInvocationListener mockListener = EasyMock 1096 .createMock(IScheduledInvocationListener.class); 1097 replayMocks(mockListener); 1098 1099 mScheduler.start(); 1100 try { 1101 mScheduler.execCommand(mockListener, args); 1102 fail(); 1103 } catch (NoDeviceException e) { 1104 // expect NoDeviceException 1105 } 1106 mScheduler.shutdownOnEmpty(); 1107 mScheduler.join(2 * 1000); 1108 verifyMocks(mockListener); 1109 } 1110 1111 /** 1112 * Test that when a command runs in the versioned subprocess with --invocation-data option we do 1113 * not add the attributes again 1114 */ testExecCommand_versioning()1115 public void testExecCommand_versioning() throws Throwable { 1116 String[] args = 1117 new String[] { 1118 "foo", "--invocation-data", "test", 1119 }; 1120 setCreateConfigExpectations(args, 1); 1121 OptionSetter setter = new OptionSetter(mCommandOptions); 1122 // If invocation-data are added and we are in a versioned invocation, the data should not 1123 // be added again. 1124 setter.setOptionValue("invocation-data", "key", "value"); 1125 mMockConfigDescriptor.setSandboxed(true); 1126 setExpectedInvokeCalls(1); 1127 mMockConfiguration.validateOptions(); 1128 IDevice mockIDevice = EasyMock.createMock(IDevice.class); 1129 ITestDevice mockDevice = EasyMock.createMock(ITestDevice.class); 1130 EasyMock.expect(mockDevice.getSerialNumber()).andStubReturn("serial"); 1131 EasyMock.expect(mockDevice.getDeviceState()).andStubReturn(TestDeviceState.ONLINE); 1132 mockDevice.setRecoveryMode(EasyMock.eq(RecoveryMode.AVAILABLE)); 1133 EasyMock.expect(mockDevice.getIDevice()).andStubReturn(mockIDevice); 1134 IScheduledInvocationListener mockListener = 1135 EasyMock.createMock(IScheduledInvocationListener.class); 1136 mockListener.invocationInitiated((InvocationContext) EasyMock.anyObject()); 1137 mockListener.invocationComplete( 1138 (IInvocationContext) EasyMock.anyObject(), EasyMock.anyObject()); 1139 EasyMock.expect(mockDevice.waitForDeviceShell(EasyMock.anyLong())).andReturn(true); 1140 replayMocks(mockDevice, mockListener); 1141 mScheduler.start(); 1142 mScheduler.execCommand(mockListener, mockDevice, args); 1143 mScheduler.shutdownOnEmpty(); 1144 mScheduler.join(2 * 1000); 1145 verifyMocks(mockListener); 1146 assertTrue(mContext.getAttributes().isEmpty()); 1147 } 1148 } 1149