/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.command;
import com.android.ddmlib.Log;
import com.android.tradefed.config.IConfiguration;
import com.android.tradefed.config.IConfigurationFactory;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.DeviceSelectionOptions;
import com.android.tradefed.device.IDeviceManager;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.MockDeviceManager;
import com.android.tradefed.invoker.IRescheduler;
import com.android.tradefed.invoker.ITestInvocation;
import com.android.tradefed.util.RunUtil;
import junit.framework.TestCase;
import org.easymock.EasyMock;
/**
* Longer running test for {@link CommandScheduler}
*/
public class CommandSchedulerFuncTest extends TestCase {
private static final String LOG_TAG = "CommandSchedulerFuncTest";
/** the {@link CommandScheduler} under test, with all dependencies mocked out */
private CommandScheduler mCommandScheduler;
private MeasuredInvocation mMockTestInvoker;
private IDeviceManager mMockDeviceManager;
private IConfiguration mSlowConfig;
private IConfiguration mFastConfig;
private IConfigurationFactory mMockConfigFactory;
private CommandOptions mCommandOptions;
@Override
protected void setUp() throws Exception {
super.setUp();
mSlowConfig = EasyMock.createNiceMock(IConfiguration.class);
mFastConfig = EasyMock.createNiceMock(IConfiguration.class);
mMockDeviceManager = new MockDeviceManager(3);
mMockTestInvoker = new MeasuredInvocation();
mMockConfigFactory = EasyMock.createMock(IConfigurationFactory.class);
mCommandOptions = new CommandOptions();
mCommandOptions.setLoopMode(true);
mCommandOptions.setMinLoopTime(0);
EasyMock.expect(mSlowConfig.getCommandOptions()).andStubReturn(mCommandOptions);
EasyMock.expect(mFastConfig.getCommandOptions()).andStubReturn(mCommandOptions);
EasyMock.expect(mSlowConfig.getDeviceRequirements()).andStubReturn(
new DeviceSelectionOptions());
EasyMock.expect(mFastConfig.getDeviceRequirements()).andStubReturn(
new DeviceSelectionOptions());
mCommandScheduler = new CommandScheduler() {
@Override
ITestInvocation createRunInstance() {
return mMockTestInvoker;
}
@Override
IDeviceManager getDeviceManager() {
return mMockDeviceManager;
}
@Override
IConfigurationFactory getConfigFactory() {
return mMockConfigFactory;
}
@Override
void initLogging() {
// ignore
}
@Override
void cleanUp() {
// ignore
}
};
}
/**
* Test config priority scheduling. Verifies that configs are prioritized according to their
* total run time.
* <p/>
* This test continually executes two configs in loop mode. One config executes quickly (ie
* "fast config"). The other config (ie "slow config") takes ~ 2 * fast config time to execute.
* <p/>
* The run is stopped after the slow config is executed 20 times. At the end of the test, it is
* expected that "fast config" has executed roughly twice as much as the "slow config".
*/
public void testRun_scheduling() throws Exception {
String[] fastConfigArgs = new String[] {"fastConfig"};
String[] slowConfigArgs = new String[] {"slowConfig"};
EasyMock.expect(
mMockConfigFactory.createConfigurationFromArgs(EasyMock.aryEq(fastConfigArgs)))
.andReturn(mFastConfig).anyTimes();
EasyMock.expect(
mMockConfigFactory.createConfigurationFromArgs(EasyMock.aryEq(slowConfigArgs)))
.andReturn(mSlowConfig).anyTimes();
EasyMock.replay(mFastConfig, mSlowConfig, mMockConfigFactory);
mCommandScheduler.addCommand(fastConfigArgs);
mCommandScheduler.addCommand(slowConfigArgs);
mCommandScheduler.start();
synchronized (mMockTestInvoker) {
mMockTestInvoker.wait();
}
mCommandScheduler.shutdown();
mCommandScheduler.join();
Log.i(LOG_TAG, String.format("fast times %d slow times %d",
mMockTestInvoker.mFastCount, mMockTestInvoker.mSlowCount));
// assert that fast config has executed roughly twice as much as slow config. Allow for
// some variance since the execution time of each config (governed via Thread.sleep) will
// not be 100% accurate
assertEquals(mMockTestInvoker.mFastCount, mMockTestInvoker.mSlowCount * 2, 5);
}
private class MeasuredInvocation implements ITestInvocation {
Integer mSlowCount = 0;
Integer mFastCount = 0;
Integer mSlowCountLimit = 20;
@Override
public void invoke(ITestDevice device, IConfiguration config, IRescheduler rescheduler)
throws DeviceNotAvailableException {
if (config.equals(mSlowConfig)) {
// sleep for 2 * fast config time
RunUtil.getDefault().sleep(200);
synchronized (mSlowCount) {
mSlowCount++;
}
if (mSlowCount >= mSlowCountLimit) {
synchronized (this) {
notify();
}
}
} else if (config.equals(mFastConfig)) {
RunUtil.getDefault().sleep(100);
synchronized (mFastCount) {
mFastCount++;
}
} else {
throw new IllegalArgumentException("unknown config");
}
}
}
}