Cts框架解析(7)-任务执行的调度室

TestInvocation

/**
	 * {@inheritDoc}
	 */
	@Override
	public void invoke(ITestDevice device, IConfiguration config, IRescheduler rescheduler) throws DeviceNotAvailableException, Throwable {
		try {
			mStatus = "fetching build";
			config.getLogOutput().init();
			getLogRegistry().registerLogger(config.getLogOutput());
			IBuildInfo info = null;
			if (config.getBuildProvider() instanceof IDeviceBuildProvider) {
				info = ((IDeviceBuildProvider) config.getBuildProvider()).getBuild(device);
			} else if (config.getBuildProvider() instanceof IDeviceConfigBuildProvider) {
				// 调用config获得cts.xml文件中的<build_provider>标签中对应的类,然后通过调用getBuild得到IBuildInfo对象
				info = ((IDeviceConfigBuildProvider) config.getBuildProvider()).getBuild(device, config);
			} else {
				info = config.getBuildProvider().getBuild();
			}
			if (info != null) {
				// System.out.println(String.format("setup: %s tearDown: %s",
				// config.getCommandOptions().isNeedPrepare(),
				// config.getCommandOptions().isNeedTearDown()));
				CLog.logAndDisplay(LogLevel.INFO,
						String.format("setup: %s tearDown: %s", config.getCommandOptions().isNeedPrepare(), config.getCommandOptions().isNeedTearDown()));
				// 获取<test>配置项里的测试选项,并注入到info中
				injectBuild(info, config.getTests());
				if (shardConfig(config, info, rescheduler)) {
					CLog.i("Invocation for %s has been sharded, rescheduling", device.getSerialNumber());
				} else {
					device.setRecovery(config.getDeviceRecovery());
					// 准备刷机,启动case
					performInvocation(config, device, info, rescheduler);
					// exit here, depend on performInvocation to deregister
					// logger
					return;
				}
			} else {
				mStatus = "(no build to test)";
				CLog.d("No build to test");
				rescheduleTest(config, rescheduler);
			}
		} catch (BuildRetrievalError e) {
			CLog.e(e);
			/*
			 * because this is BuildRetrievalError, so do not generate test
			 * result // report an empty invocation, so this error is sent to
			 * listeners startInvocation(config, device, e.getBuildInfo()); //
			 * don't want to use #reportFailure, since that will call
			 * buildNotTested for (ITestInvocationListener listener :
			 * config.getTestInvocationListeners()) {
			 * listener.invocationFailed(e); } reportLogs(device,
			 * config.getTestInvocationListeners(), config.getLogOutput());
			 * InvocationSummaryHelper.reportInvocationEnded(
			 * config.getTestInvocationListeners(), 0); return;
			 */
		} catch (IOException e) {
			CLog.e(e);
		}
		// save current log contents to global log
		getLogRegistry().dumpToGlobalLog(config.getLogOutput());
		getLogRegistry().unregisterLogger();
		config.getLogOutput().closeLog();
	}

之所以称为调度室,因为它就是去一步一步的运行9大组件的剩余组件,组件与组件之间并不知道对方的存在,只有TestInvocation自己知道。它通过反射的机制到的对象,别创建对象,然后调用其中的接口方法得到它想要的,然后再传给下一个组件。以上方法就可以看出,它先得到buildprovider对象。调用getBuild对象得到IBuildInfo对象。咱们这个程序中,其实就是调用了CtsBuildProvider的getBuild方法。

如果info不为null:然后给测试任务注入IBuildInfo,判断是否可以分拆任务,如果不能分拆的话,注册设备恢复类,然后跳转到performInvocation方法中。

如果info为null:提示没有可执行的build,调用IRescheduler.rescheduleCommand()重试。

/**
	 * Performs the invocation
	 *
	 * @param config
	 *            the {@link IConfiguration}
	 * @param device
	 *            the {@link ITestDevice} to use. May be <code>null</code>
	 * @param info
	 *            the {@link IBuildInfo}
	 */
	private void performInvocation(IConfiguration config, ITestDevice device, IBuildInfo info, IRescheduler rescheduler) throws Throwable {

		boolean resumed = false;
		long startTime = System.currentTimeMillis();
		long elapsedTime = -1;

		info.setDeviceSerial(device.getSerialNumber());
		startInvocation(config, device, info);
		try {
			device.setOptions(config.getDeviceOptions());
			// 准备build和跑case
			prepareAndRun(config, device, info, rescheduler);
		} catch (BuildError e) {
			CLog.w("Build %s failed on device %s. Reason: %s", info.getBuildId(), device.getSerialNumber(), e.toString());
			takeBugreport(device, config.getTestInvocationListeners(), BUILD_ERROR_BUGREPORT_NAME);
			reportFailure(e, config.getTestInvocationListeners(), config, info, rescheduler);
		} catch (TargetSetupError e) {
			CLog.e("Caught exception while running invocation");
			CLog.e(e);
			reportFailure(e, config.getTestInvocationListeners(), config, info, rescheduler);
			// maybe test device if offline, check it
			device.waitForDeviceOnline();
		} catch (DeviceNotAvailableException e) {
			// log a warning here so its captured before reportLogs is called
			CLog.w("Invocation did not complete due to device %s becoming not available. " + "Reason: %s", device.getSerialNumber(), e.getMessage());
			if ((e instanceof DeviceUnresponsiveException) && TestDeviceState.ONLINE.equals(device.getDeviceState())) {
				// under certain cases it might still be possible to grab a
				// bugreport
				takeBugreport(device, config.getTestInvocationListeners(), DEVICE_UNRESPONSIVE_BUGREPORT_NAME);
			}
			resumed = resume(config, info, rescheduler, System.currentTimeMillis() - startTime);
			if (!resumed) {
				reportFailure(e, config.getTestInvocationListeners(), config, info, rescheduler);
			} else {
				CLog.i("Rescheduled failed invocation for resume");
			}
			throw e;
		} catch (RuntimeException e) {
			// log a warning here so its captured before reportLogs is called
			CLog.w("Unexpected exception when running invocation: %s", e.toString());
			reportFailure(e, config.getTestInvocationListeners(), config, info, rescheduler);
			throw e;
		} catch (AssertionError e) {
			CLog.w("Caught AssertionError while running invocation: ", e.toString());
			reportFailure(e, config.getTestInvocationListeners(), config, info, rescheduler);
		} finally {
			mStatus = "done running tests";
			try {
				//
				reportLogs(device, config.getTestInvocationListeners(), config.getLogOutput());
				elapsedTime = System.currentTimeMillis() - startTime;
				if (!resumed) {
					// 发送报告
					InvocationSummaryHelper.reportInvocationEnded(config.getTestInvocationListeners(), elapsedTime);
				}

			} finally {
				config.getBuildProvider().cleanUp(info);
			}
		}
	}

首先启动所有监听器的start方法,起一个通知开始的作用。

/**
	 * Starts the invocation.
	 * <p/>
	 * Starts logging, and informs listeners that invocation has been started.
	 *
	 * @param config
	 * @param device
	 * @param info
	 */
	private void startInvocation(IConfiguration config, ITestDevice device, IBuildInfo info) {
		logStartInvocation(info, device);
		for (ITestInvocationListener listener : config.getTestInvocationListeners()) {
			try {
				listener.invocationStarted(info);
			} catch (RuntimeException e) {
				// don't let one listener leave the invocation in a bad state
				CLog.e("Caught runtime exception from ITestInvocationListener");
				CLog.e(e);
			}
		}
	}

然后得到device_options对象,设置到ITestDevice中。再调用prepareAndRun(config, device, info, rescheduler);方法。

private void prepareAndRun(IConfiguration config, ITestDevice device, IBuildInfo info, IRescheduler rescheduler) throws Throwable {
		// use the JUnit3 logic for handling exceptions when running tests
		Throwable exception = null;

		try {
			//
			if (config.getCommandOptions().isNeedPrepare()&&!isRepeat) {
				doSetup(config, device, info);
				//下次启动的时候,不再刷机
				isRepeat = true;
			}else{
				CLog.logAndDisplay(LogLevel.DEBUG, String.format("No need to flash,derect to run case"));
			}
			// 跑case
			runTests(device, info, config, rescheduler);
		} catch (Throwable running) {
			exception = running;
		} finally {
			try {
				if (config.getCommandOptions().isNeedTearDown()) {
					doTeardown(config, device, info, exception);
				}
			} catch (Throwable tearingDown) {
				if (exception == null) {
					exception = tearingDown;
				}
			}
		}
		if (exception != null) {
			throw exception;
		}
	}

在该方法中先得到根据cmd_options得到是否需要prepare,如果需要,就调用target_prepare类来做准备工作,如果不需要,直接调用runTests方法

private void runTests(ITestDevice device, IBuildInfo buildInfo, IConfiguration config, IRescheduler rescheduler) throws DeviceNotAvailableException {
		List<ITestInvocationListener> listeners = config.getTestInvocationListeners();
		for (IRemoteTest test : config.getTests()) {
			if (test instanceof IDeviceTest) {
				((IDeviceTest) test).setDevice(device);
			}
			test.run(new ResultForwarder(listeners));
		}
	}

上面的方法调用tests配置的类来追个调用里面的run方法启动测试。测试完成后,来做tearDown的工作。

时间: 2024-10-31 23:38:47

Cts框架解析(7)-任务执行的调度室的相关文章

Cts框架解析(13)-任务执行过程

因为测试任务是个很复杂的过程,所以要单独拿出来讲,里面还涉及了result_reporter的内容.所以这是一个大块.首先把断点打在CtsTest的run方法中,删除其他断点,重新启动debug模式: 首先会调用checkFields检查一下命令行参数.然后生成plan里的包名信息.(要理解plan的意思,plan就是cts目录下plan文件下的xml文件,它里面配置的entry代表一个测试项,一个测试项里又包含多个测试的case).本程序执行的是Signature计划,我们就来看看这个xml文

Cts框架解析(15)-任务执行完

case执行完毕后,会回到CtsTest的run方法中: 这个时候会先将mRemainingTestPkgs列表的第一项移除,以便下一次取第一个的时候,取的是新的TestPackage对象,然后根据case的类别来做相应的重启操作,最后返回到主界面.最后,截图留下犯罪现场,下载之前安装的jar包.在finally语句块中report未执行的case.为执行的case是通过执行其testStarted方法,但是不执行testEnded方法,让监听器识别出这条case未执行. 当CtsTest的ru

Cts框架解析(5)

解析配置文件 Cts框架分为9大部分: cmd_options:命令行接受的参数选项,command包中. device_requirements:设备相关要求,device包中 device_options:设备参数,device包中 builde_provider:版本提供者,build包中 target_preparer:预置条件准备,targetprep包中 test:测试类型,存在testtype包中 device_recovery:任务执行过程中设备异常后的设备恢复,device包中

Cts框架解析(2)-cts调试环境的搭建

上一篇文章中说了怎样在windows搭建cts以及执行cts进行測试.这篇文章来讲讲怎样在eclipse中配置源代码,进行debug调试. 下载 cts源代码地址:https://android.googlesource.com/platform/cts 能够使用git下载到本地. 文件夹结构 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRmb290YmFsbA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFC

Cts框架解析(9)-IDeviceRecovery

当设备处于offline状态时,cts框架就要调用IDeviceRecovery接口类去做相应的恢复工作. 接口 /* * 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 Licens

Cts框架解析(6)-任务的执行

前两篇讲了任务的添加和9大项配置,这篇讲任务的执行. 任务的执行 任务的执行在CommandScheduler的run方法中,所以删除所有的断点,在run方法中打上断点,重启启动debug: 先看while循环下面的第一行代码 ExecutableCommand cmd = dequeueConfigCommand(); private ExecutableCommand dequeueConfigCommand() { try { // poll for a command, rather t

Cts框架解析(6)-任务的运行

前两篇讲了任务的加入和9大项配置,这篇讲任务的运行. 任务的运行 任务的运行在CommandScheduler的run方法中,所以删除全部的断点,在run方法中打上断点,重新启动启动debug: 先看while循环以下的第一行代码 ExecutableCommand cmd = dequeueConfigCommand(); private ExecutableCommand dequeueConfigCommand() { try { // poll for a command, rather

Cts框架解析(1)-windows下cts配置

环境搭建 下载 cts工具的下载地址:http://source.android.com/compatibility/downloads.html windows选择Android4.4 R3 Compatibility Test Suite (CTS) - ARM下载. 文件夹结构 解压后的文件夹结构例如以下: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRmb290YmFsbA==/font/5a6L5L2T/fontsize/400/fil

Cts框架解析(4)

Debug debug的入口在CtsConsole类,所以我们把第一个断点打在249行: Console console = new CtsConsole(); 按F6再按F5进入到Console.startConsole方法中. 按F5进入GlobalConfiguration.createGlobalConfiguration方法中. 该方法内主要是读取全局配置文件并设置IGlobalConfiguration接口对象sInstance.主要方法为95行读取文件的getGlobalConfi