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 17 package com.android.tradefed.command; 18 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertTrue; 21 import static org.mockito.Mockito.mock; 22 import static org.mockito.Mockito.when; 23 24 import com.android.ddmlib.IDevice; 25 import com.android.tradefed.config.ConfigurationDescriptor; 26 import com.android.tradefed.config.ConfigurationException; 27 import com.android.tradefed.config.DeviceConfigurationHolder; 28 import com.android.tradefed.config.GlobalConfiguration; 29 import com.android.tradefed.config.IConfiguration; 30 import com.android.tradefed.config.IConfigurationFactory; 31 import com.android.tradefed.config.IDeviceConfiguration; 32 import com.android.tradefed.device.DeviceNotAvailableException; 33 import com.android.tradefed.device.DeviceSelectionOptions; 34 import com.android.tradefed.device.IDeviceManager; 35 import com.android.tradefed.device.ITestDevice; 36 import com.android.tradefed.device.MockDeviceManager; 37 import com.android.tradefed.device.StubDevice; 38 import com.android.tradefed.device.TestDeviceOptions; 39 import com.android.tradefed.device.TestDeviceState; 40 import com.android.tradefed.invoker.IInvocationContext; 41 import com.android.tradefed.invoker.IRescheduler; 42 import com.android.tradefed.invoker.ITestInvocation; 43 import com.android.tradefed.log.ILeveledLogOutput; 44 import com.android.tradefed.log.LogUtil.CLog; 45 import com.android.tradefed.result.ILogSaver; 46 import com.android.tradefed.result.ITestInvocationListener; 47 import com.android.tradefed.result.error.ErrorIdentifier; 48 import com.android.tradefed.util.RunInterruptedException; 49 import com.android.tradefed.util.RunUtil; 50 import com.android.tradefed.util.keystore.IKeyStoreClient; 51 52 import com.google.common.truth.Truth; 53 54 import org.junit.After; 55 import org.junit.Before; 56 import org.junit.BeforeClass; 57 import org.junit.Test; 58 import org.junit.runner.RunWith; 59 import org.junit.runners.JUnit4; 60 import org.mockito.AdditionalMatchers; 61 import org.mockito.Mock; 62 import org.mockito.Mockito; 63 import org.mockito.MockitoAnnotations; 64 65 import java.util.ArrayList; 66 import java.util.List; 67 68 /** Longer running test for {@link CommandScheduler} */ 69 @RunWith(JUnit4.class) 70 public class CommandSchedulerFuncTest { 71 72 private static final long WAIT_TIMEOUT_MS = 30 * 1000; 73 /** the {@link CommandScheduler} under test, with all dependencies mocked out */ 74 private CommandScheduler mCommandScheduler; 75 76 private MeasuredInvocation mMockTestInvoker; 77 private MockDeviceManager mMockDeviceManager; 78 private List<IDeviceConfiguration> mMockDeviceConfig; 79 @Mock IConfiguration mSlowConfig; 80 @Mock IConfiguration mFastConfig; 81 @Mock IConfigurationFactory mMockConfigFactory; 82 @Mock ILeveledLogOutput mMockLogLevel; 83 @Mock ILogSaver mMockLogSaver; 84 private CommandOptions mCommandOptions; 85 private DeviceSelectionOptions mDeviceOptions; 86 private boolean mInterruptible = false; 87 private IDeviceConfiguration mMockConfig; 88 89 @BeforeClass setUpClass()90 public static void setUpClass() throws ConfigurationException { 91 try { 92 GlobalConfiguration.createGlobalConfiguration(new String[] {"empty"}); 93 } catch (IllegalStateException e) { 94 // ignore 95 } 96 } 97 98 @Before setUp()99 public void setUp() throws Exception { 100 MockitoAnnotations.initMocks(this); 101 102 mDeviceOptions = new DeviceSelectionOptions(); 103 mMockDeviceConfig = new ArrayList<IDeviceConfiguration>(); 104 mMockConfig = new DeviceConfigurationHolder("device"); 105 mMockConfig.addSpecificConfig(mDeviceOptions); 106 mMockConfig.addSpecificConfig(new TestDeviceOptions()); 107 mMockDeviceConfig.add(mMockConfig); 108 109 mInterruptible = false; 110 111 mMockDeviceManager = new MockDeviceManager(1); 112 mMockTestInvoker = new MeasuredInvocation(); 113 114 mCommandOptions = new CommandOptions(); 115 mCommandOptions.setLoopMode(true); 116 mCommandOptions.setMinLoopTime(0); 117 when(mSlowConfig.getCommandOptions()).thenReturn(mCommandOptions); 118 when(mSlowConfig.getTestInvocationListeners()) 119 .thenReturn(new ArrayList<ITestInvocationListener>()); 120 when(mFastConfig.getCommandOptions()).thenReturn(mCommandOptions); 121 when(mFastConfig.getTestInvocationListeners()) 122 .thenReturn(new ArrayList<ITestInvocationListener>()); 123 when(mSlowConfig.getDeviceRequirements()).thenReturn(new DeviceSelectionOptions()); 124 when(mFastConfig.getDeviceRequirements()).thenReturn(new DeviceSelectionOptions()); 125 when(mSlowConfig.getDeviceConfig()).thenReturn(mMockDeviceConfig); 126 when(mSlowConfig.getDeviceConfigByName(Mockito.eq("device"))).thenReturn(mMockConfig); 127 when(mSlowConfig.getCommandLine()).thenReturn(""); 128 when(mFastConfig.getDeviceConfigByName(Mockito.eq("device"))).thenReturn(mMockConfig); 129 when(mFastConfig.getDeviceConfig()).thenReturn(mMockDeviceConfig); 130 when(mFastConfig.getCommandLine()).thenReturn(""); 131 when(mSlowConfig.getConfigurationDescription()).thenReturn(new ConfigurationDescriptor()); 132 when(mFastConfig.getConfigurationDescription()).thenReturn(new ConfigurationDescriptor()); 133 when(mFastConfig.getTests()).thenReturn(new ArrayList<>()); 134 when(mSlowConfig.getTests()).thenReturn(new ArrayList<>()); 135 when(mFastConfig.getLogSaver()).thenReturn(mMockLogSaver); 136 when(mSlowConfig.getLogSaver()).thenReturn(mMockLogSaver); 137 when(mFastConfig.getLogOutput()).thenReturn(mMockLogLevel); 138 when(mSlowConfig.getLogOutput()).thenReturn(mMockLogLevel); 139 140 mCommandScheduler = 141 new CommandScheduler() { 142 @Override 143 ITestInvocation createRunInstance() { 144 return mMockTestInvoker; 145 } 146 147 @Override 148 protected IDeviceManager getDeviceManager() { 149 return mMockDeviceManager; 150 } 151 152 @Override 153 protected IConfigurationFactory getConfigFactory() { 154 if (mInterruptible) { 155 // simulate the invocation becoming interruptible 156 RunUtil.getDefault().allowInterrupt(true); 157 } 158 return mMockConfigFactory; 159 } 160 161 @Override 162 protected void initLogging() { 163 // ignore 164 } 165 166 @Override 167 protected void cleanUp() { 168 // ignore 169 } 170 }; 171 } 172 173 @After tearDown()174 public void tearDown() throws Exception { 175 if (mCommandScheduler != null) { 176 mCommandScheduler.shutdownOnEmpty(); 177 } 178 } 179 180 /** 181 * Test config priority scheduling. Verifies that configs are prioritized according to their 182 * total run time. 183 * 184 * <p>This test continually executes two configs in loop mode. One config executes quickly (ie 185 * "fast config"). The other config (ie "slow config") takes ~ 2 * fast config time to execute. 186 * 187 * <p>The run is stopped after the slow config is executed 20 times. At the end of the test, it 188 * is expected that "fast config" has executed roughly twice as much as the "slow config". 189 */ 190 @Test testRun_scheduling()191 public void testRun_scheduling() throws Exception { 192 String[] fastConfigArgs = new String[] {"fastConfig"}; 193 String[] slowConfigArgs = new String[] {"slowConfig"}; 194 List<String> nullArg = null; 195 when(mMockConfigFactory.createConfigurationFromArgs( 196 AdditionalMatchers.aryEq(fastConfigArgs), 197 Mockito.eq(nullArg), 198 (IKeyStoreClient) Mockito.any())) 199 .thenReturn(mFastConfig); 200 when(mMockConfigFactory.createConfigurationFromArgs( 201 AdditionalMatchers.aryEq(slowConfigArgs), 202 Mockito.eq(nullArg), 203 (IKeyStoreClient) Mockito.any())) 204 .thenReturn(mSlowConfig); 205 206 mCommandScheduler.start(); 207 mCommandScheduler.addCommand(fastConfigArgs); 208 mCommandScheduler.addCommand(slowConfigArgs); 209 210 synchronized (mMockTestInvoker) { 211 mMockTestInvoker.wait(WAIT_TIMEOUT_MS); 212 } 213 mCommandScheduler.shutdown(); 214 mCommandScheduler.join(WAIT_TIMEOUT_MS); 215 216 CLog.i( 217 "fast times %d slow times %d", 218 mMockTestInvoker.mFastCount, mMockTestInvoker.mSlowCount); 219 Truth.assertThat(mMockTestInvoker.mFastCount).isGreaterThan(mMockTestInvoker.mSlowCount); 220 assertFalse(mMockTestInvoker.runInterrupted); 221 } 222 223 private class MeasuredInvocation implements ITestInvocation { 224 private final Object mSlowCountLock = new Object(); 225 int mSlowCount = 0; 226 private final Object mFastCountLock = new Object(); 227 int mFastCount = 0; 228 int mSlowCountLimit = 40; 229 public boolean runInterrupted = false; 230 public boolean printedStop = false; 231 public long mSleepTimMs = 200L; 232 233 @Override invoke( IInvocationContext metadata, IConfiguration config, IRescheduler rescheduler, ITestInvocationListener... listeners)234 public void invoke( 235 IInvocationContext metadata, 236 IConfiguration config, 237 IRescheduler rescheduler, 238 ITestInvocationListener... listeners) 239 throws DeviceNotAvailableException { 240 try { 241 if (mInterruptible) { 242 // simulate the invocation becoming interruptible 243 RunUtil.getDefault().allowInterrupt(true); 244 } 245 if (config.equals(mSlowConfig)) { 246 // sleep for 2 * fast config time 247 RunUtil.getDefault().sleep(mSleepTimMs); 248 synchronized (mSlowCountLock) { 249 mSlowCount++; 250 } 251 if (mSlowCount >= mSlowCountLimit) { 252 synchronized (this) { 253 notify(); 254 } 255 } 256 } else if (config.equals(mFastConfig)) { 257 RunUtil.getDefault().sleep(100); 258 synchronized (mFastCountLock) { 259 mFastCount++; 260 } 261 } else { 262 throw new IllegalArgumentException("unknown config"); 263 } 264 } catch (RunInterruptedException e) { 265 CLog.e(e); 266 // Yield right away if an exception occur due to an interrupt. 267 runInterrupted = true; 268 synchronized (this) { 269 notify(); 270 } 271 } 272 } 273 274 @Override notifyInvocationForceStopped(String message, ErrorIdentifier errorId)275 public void notifyInvocationForceStopped(String message, ErrorIdentifier errorId) { 276 runInterrupted = true; 277 printedStop = true; 278 CLog.d("#notifyInvocationForceStopped"); 279 } 280 281 @Override notifyInvocationStopped(String message)282 public void notifyInvocationStopped(String message) { 283 CLog.d("#notifyInvocationStopped"); 284 } 285 } 286 287 /** Test that the Invocation is not interruptible even when Battery is low. */ 288 @Test testBatteryLowLevel()289 public void testBatteryLowLevel() throws Throwable { 290 ITestDevice mockDevice = mock(ITestDevice.class); 291 when(mockDevice.getSerialNumber()).thenReturn("serial"); 292 IDevice mockIDevice = new StubDevice("serial"); 293 when(mockDevice.getIDevice()).thenReturn(mockIDevice); 294 when(mockDevice.getDeviceState()).thenReturn(TestDeviceState.ONLINE); 295 296 TestDeviceOptions testDeviceOptions = new TestDeviceOptions(); 297 testDeviceOptions.setCutoffBattery(20); 298 mMockConfig.addSpecificConfig(testDeviceOptions); 299 assertTrue(testDeviceOptions.getCutoffBattery() == 20); 300 when(mSlowConfig.getDeviceOptions()).thenReturn(testDeviceOptions); 301 302 mMockDeviceManager.clearAllDevices(); 303 mMockDeviceManager.addDevice(mockDevice); 304 305 String[] slowConfigArgs = new String[] {"slowConfig"}; 306 List<String> nullArg = null; 307 when(mMockConfigFactory.createConfigurationFromArgs( 308 AdditionalMatchers.aryEq(slowConfigArgs), 309 Mockito.eq(nullArg), 310 (IKeyStoreClient) Mockito.any())) 311 .thenReturn(mSlowConfig); 312 313 mCommandScheduler.start(); 314 mCommandScheduler.addCommand(slowConfigArgs); 315 316 synchronized (mMockTestInvoker) { 317 mMockTestInvoker.wait(WAIT_TIMEOUT_MS); 318 } 319 320 mCommandScheduler.shutdown(); 321 mCommandScheduler.join(WAIT_TIMEOUT_MS); 322 assertFalse(mMockTestInvoker.runInterrupted); 323 // Notify was not sent to the invocation because it was not forced shutdown. 324 assertFalse(mMockTestInvoker.printedStop); 325 } 326 327 /** Test that the Invocation is interruptible when Battery is low. */ 328 @Test testBatteryLowLevel_interruptible()329 public void testBatteryLowLevel_interruptible() throws Throwable { 330 ITestDevice mockDevice = mock(ITestDevice.class); 331 when(mockDevice.getSerialNumber()).thenReturn("serial"); 332 IDevice mockIDevice = new StubDevice("serial"); 333 when(mockDevice.getBattery()).thenReturn(10); 334 when(mockDevice.getIDevice()).thenReturn(mockIDevice); 335 when(mockDevice.getDeviceState()).thenReturn(TestDeviceState.ONLINE); 336 337 TestDeviceOptions testDeviceOptions = new TestDeviceOptions(); 338 testDeviceOptions.setCutoffBattery(20); 339 mMockConfig.addSpecificConfig(testDeviceOptions); 340 when(mSlowConfig.getDeviceOptions()).thenReturn(testDeviceOptions); 341 342 mMockDeviceManager.clearAllDevices(); 343 mMockDeviceManager.addDevice(mockDevice); 344 345 String[] slowConfigArgs = new String[] {"slowConfig"}; 346 List<String> nullArg = null; 347 when(mMockConfigFactory.createConfigurationFromArgs( 348 AdditionalMatchers.aryEq(slowConfigArgs), 349 Mockito.eq(nullArg), 350 (IKeyStoreClient) Mockito.any())) 351 .thenReturn(mSlowConfig); 352 353 mCommandScheduler.start(); 354 mInterruptible = true; 355 mCommandScheduler.addCommand(slowConfigArgs); 356 357 synchronized (mMockTestInvoker) { 358 mMockTestInvoker.wait(WAIT_TIMEOUT_MS); 359 } 360 361 mCommandScheduler.shutdown(); 362 mCommandScheduler.join(WAIT_TIMEOUT_MS); 363 assertTrue(mMockTestInvoker.runInterrupted); 364 } 365 366 /** 367 * Test that the Invocation is interrupted by the shutdownHard and finishes with an 368 * interruption. {@link CommandScheduler#shutdownHard()} 369 */ 370 @Test testShutdown_interruptible()371 public void testShutdown_interruptible() throws Throwable { 372 String[] slowConfigArgs = new String[] {"slowConfig"}; 373 mMockTestInvoker.mSleepTimMs = 10000L; // Sleep much longer than expected interrupt 374 List<String> nullArg = null; 375 when(mMockConfigFactory.createConfigurationFromArgs( 376 AdditionalMatchers.aryEq(slowConfigArgs), 377 Mockito.eq(nullArg), 378 (IKeyStoreClient) Mockito.any())) 379 .thenReturn(mSlowConfig); 380 381 mCommandScheduler.start(); 382 mInterruptible = true; 383 mCommandScheduler.addCommand(slowConfigArgs); 384 385 Thread test = 386 new Thread( 387 new Runnable() { 388 @Override 389 public void run() { 390 RunUtil.getDefault().sleep(500); 391 mCommandScheduler.shutdownHard(); 392 } 393 }); 394 test.setName("CommandSchedulerFuncTest#testShutdown_interruptible"); 395 test.start(); 396 synchronized (mMockTestInvoker) { 397 mMockTestInvoker.wait(WAIT_TIMEOUT_MS); 398 } 399 test.join(); 400 mCommandScheduler.join(WAIT_TIMEOUT_MS); 401 // Was interrupted during execution. 402 assertTrue(mMockTestInvoker.runInterrupted); 403 // Notify was sent to the invocation 404 assertTrue(mMockTestInvoker.printedStop); 405 } 406 407 /** 408 * Test that the Invocation is not interrupted by shutdownHard. Invocation terminate then 409 * scheduler finishes. {@link CommandScheduler#shutdownHard()} 410 */ 411 @Test testShutdown_notInterruptible()412 public void testShutdown_notInterruptible() throws Throwable { 413 final LongInvocation li = new LongInvocation(5); 414 mCommandOptions.setLoopMode(false); 415 mCommandScheduler = 416 new CommandScheduler() { 417 @Override 418 ITestInvocation createRunInstance() { 419 return li; 420 } 421 422 @Override 423 protected IDeviceManager getDeviceManager() { 424 return mMockDeviceManager; 425 } 426 427 @Override 428 protected IConfigurationFactory getConfigFactory() { 429 if (mInterruptible) { 430 // simulate the invocation becoming interruptible 431 RunUtil.getDefault().allowInterrupt(true); 432 } 433 return mMockConfigFactory; 434 } 435 436 @Override 437 protected void initLogging() { 438 // ignore 439 } 440 441 @Override 442 protected void cleanUp() { 443 // ignore 444 } 445 446 @Override 447 public long getShutdownTimeout() { 448 return 30000; 449 } 450 }; 451 String[] slowConfigArgs = new String[] {"slowConfig"}; 452 List<String> nullArg = null; 453 when(mMockConfigFactory.createConfigurationFromArgs( 454 AdditionalMatchers.aryEq(slowConfigArgs), 455 Mockito.eq(nullArg), 456 (IKeyStoreClient) Mockito.any())) 457 .thenReturn(mSlowConfig); 458 459 mCommandScheduler.start(); 460 mInterruptible = false; 461 mCommandScheduler.addCommand(slowConfigArgs); 462 463 Thread shutdownThread = 464 new Thread( 465 new Runnable() { 466 @Override 467 public void run() { 468 RunUtil.getDefault().sleep(1000); 469 mCommandScheduler.shutdownHard(); 470 } 471 }); 472 shutdownThread.setName("CommandSchedulerFuncTest#testShutdown_notInterruptible"); 473 shutdownThread.start(); 474 synchronized (li) { 475 // Invocation will finish first because shorter than shutdownHard final timeout 476 li.wait(WAIT_TIMEOUT_MS); 477 } 478 shutdownThread.join(); 479 mCommandScheduler.join(WAIT_TIMEOUT_MS); 480 // Stop but was not interrupted 481 assertFalse(mMockTestInvoker.runInterrupted); 482 // Notify was not sent to the invocation because it was not interrupted. 483 assertFalse(mMockTestInvoker.printedStop); 484 } 485 486 private class LongInvocation implements ITestInvocation { 487 public boolean runInterrupted = false; 488 private int mIteration = 15; 489 LongInvocation(int iteration)490 public LongInvocation(int iteration) { 491 mIteration = iteration; 492 } 493 494 @Override invoke( IInvocationContext metadata, IConfiguration config, IRescheduler rescheduler, ITestInvocationListener... listeners)495 public void invoke( 496 IInvocationContext metadata, 497 IConfiguration config, 498 IRescheduler rescheduler, 499 ITestInvocationListener... listeners) 500 throws DeviceNotAvailableException { 501 try { 502 if (mInterruptible) { 503 // simulate the invocation becoming interruptible 504 RunUtil.getDefault().allowInterrupt(true); 505 } 506 for (int i = 0; i < mIteration; i++) { 507 RunUtil.getDefault().sleep(2000); 508 } 509 synchronized (this) { 510 notify(); 511 } 512 } catch (RunInterruptedException e) { 513 CLog.e(e); 514 // Yield right away if an exception occur due to an interrupt. 515 runInterrupted = true; 516 synchronized (this) { 517 notify(); 518 } 519 } 520 } 521 522 @Override notifyInvocationForceStopped(String message, ErrorIdentifier errorId)523 public void notifyInvocationForceStopped(String message, ErrorIdentifier errorId) { 524 runInterrupted = true; 525 CLog.d("#notifyInvocationForceStopped"); 526 } 527 528 @Override notifyInvocationStopped(String message)529 public void notifyInvocationStopped(String message) { 530 CLog.d("#notifyInvocationStopped"); 531 } 532 } 533 534 /** 535 * Test that the Invocation is interrupted by {@link CommandScheduler#shutdownHard()} but only 536 * after the shutdown timeout is expired because the invocation was uninterruptible so we only 537 * allow for so much time before shutting down. 538 */ 539 @Test testShutdown_notInterruptible_timeout()540 public void testShutdown_notInterruptible_timeout() throws Throwable { 541 final LongInvocation li = new LongInvocation(15); 542 mCommandOptions.setLoopMode(false); 543 mCommandScheduler = 544 new CommandScheduler() { 545 @Override 546 ITestInvocation createRunInstance() { 547 return li; 548 } 549 550 @Override 551 protected IDeviceManager getDeviceManager() { 552 return mMockDeviceManager; 553 } 554 555 @Override 556 protected IConfigurationFactory getConfigFactory() { 557 if (mInterruptible) { 558 // simulate the invocation becoming interruptible 559 RunUtil.getDefault().allowInterrupt(true); 560 } 561 return mMockConfigFactory; 562 } 563 564 @Override 565 protected void initLogging() { 566 // ignore 567 } 568 569 @Override 570 protected void cleanUp() { 571 // ignore 572 } 573 574 @Override 575 public long getShutdownTimeout() { 576 return 5000; 577 } 578 }; 579 String[] slowConfigArgs = new String[] {"slowConfig"}; 580 List<String> nullArg = null; 581 when(mMockConfigFactory.createConfigurationFromArgs( 582 AdditionalMatchers.aryEq(slowConfigArgs), 583 Mockito.eq(nullArg), 584 (IKeyStoreClient) Mockito.any())) 585 .thenReturn(mSlowConfig); 586 587 mCommandScheduler.start(); 588 mInterruptible = false; 589 mCommandScheduler.addCommand(slowConfigArgs); 590 591 Thread shutdownThread = 592 new Thread( 593 new Runnable() { 594 @Override 595 public void run() { 596 RunUtil.getDefault().sleep(1000); 597 mCommandScheduler.shutdownHard(); 598 } 599 }); 600 shutdownThread.setName("CommandSchedulerFuncTest#testShutdown_notInterruptible_timeout"); 601 shutdownThread.start(); 602 synchronized (li) { 603 // Setting a timeout longer than the shutdown timeout. 604 li.wait(WAIT_TIMEOUT_MS); 605 } 606 shutdownThread.join(); 607 mCommandScheduler.join(WAIT_TIMEOUT_MS); 608 // Stop and was interrupted by timeout of shutdownHard() 609 assertTrue(li.runInterrupted); 610 } 611 612 /** Test that if the invocation run time goes over the timeout, it will be forced stopped. */ 613 @Test testShutdown_invocation_timeout()614 public void testShutdown_invocation_timeout() throws Throwable { 615 final LongInvocation li = new LongInvocation(2); 616 mCommandOptions.setLoopMode(false); 617 mCommandOptions.setInvocationTimeout(1000L); 618 mCommandScheduler = 619 new CommandScheduler() { 620 @Override 621 ITestInvocation createRunInstance() { 622 return li; 623 } 624 625 @Override 626 protected IDeviceManager getDeviceManager() { 627 return mMockDeviceManager; 628 } 629 630 @Override 631 protected IConfigurationFactory getConfigFactory() { 632 return mMockConfigFactory; 633 } 634 635 @Override 636 protected void initLogging() { 637 // ignore 638 } 639 640 @Override 641 protected void cleanUp() { 642 // ignore 643 } 644 }; 645 String[] slowConfigArgs = new String[] {"slowConfig"}; 646 List<String> nullArg = null; 647 when(mMockConfigFactory.createConfigurationFromArgs( 648 AdditionalMatchers.aryEq(slowConfigArgs), 649 Mockito.eq(nullArg), 650 (IKeyStoreClient) Mockito.any())) 651 .thenReturn(mSlowConfig); 652 653 mCommandScheduler.start(); 654 mInterruptible = true; 655 mCommandScheduler.addCommand(slowConfigArgs); 656 mCommandScheduler.join(mCommandOptions.getInvocationTimeout() * 3); 657 // Stop and was interrupted by timeout 658 assertTrue(li.runInterrupted); 659 } 660 } 661