3, 任务调度
CommandScheduler流程图如下,
CommandScheduler的run方法主要代码如下,
IDeviceManager manager = getDeviceManager();
startRemoteManager();
this.mRunLatch.countDown();
manager.addDeviceMonitor(new AvailDeviceMonitor(null)); // 测试的手机设备
while (!isShutdown())
{
this.mCommandProcessWait.waitAndReset(this.mPollTime);
checkInvocations();
processReadyCommands(manager);
}
不断循环调用processReadyCommands方法,从mReadyCommands中读取任务,然后执行。processReadyCommands方法如下,
protected void processReadyCommands(IDeviceManager manager)
{
Map<ExecutableCommand, ITestDevice> scheduledCommandMap = new HashMap();
synchronized (this)
{
Collections.sort(this.mReadyCommands, new ExecutableCommandComparator(null));
Iterator<ExecutableCommand> cmdIter = this.mReadyCommands.iterator();
while (cmdIter.hasNext())
{
ExecutableCommand cmd = (ExecutableCommand)cmdIter.next();
ITestDevice device = manager.allocateDevice(cmd.getConfiguration().getDeviceRequirements());
if (device != null)
{
cmdIter.remove();
this.mExecutingCommands.add(cmd);
scheduledCommandMap.put(cmd, device);
}
}
}
for (Map.Entry<ExecutableCommand, ITestDevice> cmdDeviceEntry : scheduledCommandMap.entrySet())
{
ExecutableCommand cmd = (ExecutableCommand)cmdDeviceEntry.getKey();
startInvocation(new FreeDeviceHandler(getDeviceManager(), new ICommandScheduler.IScheduledInvocationListener[0]), (ITestDevice)cmdDeviceEntry.getValue(), cmd);
if (cmd.isLoopMode()) {
addNewExecCommandToQueue(cmd.getCommandTracker());
}
}
}
首先将任务打包到mExecutingCommands中,然后利用for循环逐个取出调用startInvocation进行处理。
private void startInvocation(ICommandScheduler.IScheduledInvocationListener listener, ITestDevice device, ExecutableCommand cmd)
{
if (hasInvocationThread(device)) {
throw new IllegalStateException(String.format("Attempting invocation on device %s when one is already running", new Object[] { device.getSerialNumber() }));
}
LogUtil.CLog.d("starting invocation for command id %d", new Object[] { Integer.valueOf(cmd.getCommandTracker().getId()) });
String invocationName = String.format("Invocation-%s", new Object[] { device.getSerialNumber() });
InvocationThread invocationThread = new InvocationThread(invocationName, listener, device, cmd);
invocationThread.start();
addInvocationThread(invocationThread);
}
InvocationThread是CommandScheduler的内部类,也继承于Thread。
startInvocation方法根据测试指令启动InvocationThread线程去进行测试。
InvocationThread线程的流程图如下,
InvocationThread中的run部分代码如下,
this.mCmd.commandStarted();
instance.invoke(this.mDevice, config,
new CommandScheduler.Rescheduler(CommandScheduler.this, this.mCmd.getCommandTracker()),
new ITestInvocationListener[] { this.mListener });
TestInvocation称为CTS的任务调度室, 因为它就是去一步一步的运行组件,
组件与组件之间并不知道对方的存在,只有TestInvocation自己知道。
Invoke方法通过反射的机制得到组件对象,然后调用其中的接口方法,然后再传给下一个组件。
performInvocation主要包括以下步骤:
1,首先调用startInvocation方法启动所有监听器,主要是通知作用。
2, 调用prepareAndRun方法进行CTS测试。
3,测试完成之后给出报告结果,并且调用doTeardown进行后续处理。
runtest方法如下,
private void runTests(ITestDevice device, IConfiguration config, ITestInvocationListener listener)
throws DeviceNotAvailableException
{
for (IRemoteTest test : config.getTests())
{
if ((test instanceof IDeviceTest)) {
((IDeviceTest)test).setDevice(device);
}
test.run(listener);
}
}
这个IdeviceTest对象在此处特指CtsTest对象, CtsTest定义如下,
public class CtsTest implements IDeviceTest, IResumableTest, IShardableTest, IBuildReceiver {
CtsTest的run方法逻辑如下
1,根据测试指令解析测试项。
2,从所有解析的测试项中抽出CTS测试指令plan的测试项。
3,对每个测试项进行测试。