Cts框架解析(21)-测试结果收集系统

1.UML图



2.解释


cts框架中将case的执行和case结果的收集分成了独立的2个部分,我们称case的结果的收集叫测试结果收集器。设计的思路来源为ddmlib中的ITestRunListener接口,该接口是一个抽象的观察者组件,cts创建有很多的具体的组件。但是这些组件不是一个一个去注册成为监听者的,而是在run的时候传递给ddmlib中RemoteAndroidTestRunner类的。所以只要我们继承了该接口,在run的时候把该接口的实例对象传过去,我们就能在对象实例的相应方法中接受信息。但是我们想要有多个地方都能接受到测试结果呢?没关系,cts为我们准备了ResultForwarder类。看具体实现细节:


3.ResultForwarder


public class ResultForwarder implements ITestInvocationListener {

	private List<ITestInvocationListener> mListeners;

	/**
	 * Create a {@link ResultForwarder} with deferred listener setting. Intended
	 * only for use by subclasses.
	 */
	protected ResultForwarder() {
		mListeners = Collections.emptyList();
	}

	/**
	 * Create a {@link ResultForwarder}.
	 * 
	 * @param listeners
	 *            the real {@link ITestInvocationListener}s to forward results
	 *            to
	 */
	public ResultForwarder(List<ITestInvocationListener> listeners) {
		mListeners = listeners;
	}

	/**
	 * Alternate variable arg constructor for {@link ResultForwarder}.
	 * 
	 * @param listeners
	 *            the real {@link ITestInvocationListener}s to forward results
	 *            to
	 */
	public ResultForwarder(ITestInvocationListener... listeners) {
		mListeners = Arrays.asList(listeners);
	}

	/**
	 * Set the listeners after construction. Intended only for use by
	 * subclasses.
	 * 
	 * @param listeners
	 *            the real {@link ITestInvocationListener}s to forward results
	 *            to
	 */
	protected void setListeners(List<ITestInvocationListener> listeners) {
		mListeners = listeners;
	}

	/**
	 * Set the listeners after construction. Intended only for use by
	 * subclasses.
	 * 
	 * @param listeners
	 *            the real {@link ITestInvocationListener}s to forward results
	 *            to
	 */
	protected void setListeners(ITestInvocationListener... listeners) {
		mListeners = Arrays.asList(listeners);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void invocationStarted(IBuildInfo buildInfo) {
		for (ITestInvocationListener listener : mListeners) {
			listener.invocationStarted(buildInfo);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void invocationFailed(Throwable cause) {
		for (ITestInvocationListener listener : mListeners) {
			listener.invocationFailed(cause);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void invocationEnded(long elapsedTime) {
		InvocationSummaryHelper.reportInvocationEnded(mListeners, elapsedTime);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public TestSummary getSummary() {
		// should never be called
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void testLog(String dataName, LogDataType dataType,
			InputStreamSource dataStream) {
//		CLog.logAndDisplay(LogLevel.INFO,
//				String.format("[testLog] dataName: %s", dataName));
		for (ITestInvocationListener listener : mListeners) {
			listener.testLog(dataName, dataType, dataStream);
		}
	}

	/**
	 * {@inheritDoc}case开始时显式的调用
	 */
	@Override
	public void testRunStarted(String runName, int testCount) {
//		CLog.logAndDisplay(LogLevel.INFO, String
//				.format("[testRunStarted] runName: %s testCount:%d", runName,
//						testCount));
		for (ITestInvocationListener listener : mListeners) {
			listener.testRunStarted(runName, testCount);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void testRunFailed(String errorMessage) {
//		CLog.logAndDisplay(LogLevel.INFO,
//				String.format("[testRunFailed] errorMessage: %s", errorMessage));
		for (ITestInvocationListener listener : mListeners) {
			listener.testRunFailed(errorMessage);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void testRunStopped(long elapsedTime) {
//		CLog.logAndDisplay(LogLevel.INFO,
//				String.format("[testRunStopped] elapsedTime: %d", elapsedTime));
		for (ITestInvocationListener listener : mListeners) {
			listener.testRunStopped(elapsedTime);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void testRunEnded(long elapsedTime, Map<String, String> runMetrics) {
//		CLog.logAndDisplay(LogLevel.INFO,
//				String.format("[testRunEnded] elapsedTime: %d", elapsedTime));
		for (ITestInvocationListener listener : mListeners) {
			listener.testRunEnded(elapsedTime, runMetrics);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void testStarted(TestIdentifier test) {
//		CLog.logAndDisplay(LogLevel.INFO,
//				String.format("[testStarted] %s", test.toString()));
		for (ITestInvocationListener listener : mListeners) {
			listener.testStarted(test);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void testFailed(TestFailure status, TestIdentifier test, String trace) {
//		CLog.logAndDisplay(LogLevel.INFO,
//				String.format("[testFailed] %s", test.toString()));
		for (ITestInvocationListener listener : mListeners) {
			listener.testFailed(status, test, trace);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
//		CLog.logAndDisplay(LogLevel.INFO,
//				String.format("[testEnded] %s", test.toString()));
		for (ITestInvocationListener listener : mListeners) {
			listener.testEnded(test, testMetrics);
		}
	}
}

该类中继承于ITestInvocationListener接口,ITestInvocationListener也继承与ITestRunListener接口,然后在ITestRunListener接口原有的方法上添加了一些cts所要用到的方法。而我们的ResultForwarder就是一个ITestInvocationListener接口,自然而然也是ITestRunListener对象。

而我们具体看看ResultForwarder构造方法你会发现,参数是多个ITestInvocationListener对象,传递进来保存到定义的集合中。这样当我们ResultForwarder传递到ddmlib内部的时候,ResultForwarder相应方法被调用后,里面的结果监听器集合里的每一个结果监听器都会收到结果。这样的设计难道不是很牛逼,我是觉得很牛逼啊!!!


4.ResultForwarder的子类


cts还给测试时配备了3个使用型的失败现场证据收集器,请看下面的三个类定义。


/**
	 * 保存bugreport日志 A {@link ResultForwarder} that will forward a bugreport on
	 * each failed test.
	 */
	private static class FailedTestBugreportGenerator extends ResultForwarder {
		private ITestDevice mDevice;

		public FailedTestBugreportGenerator(ITestInvocationListener listener, ITestDevice device) {
			super(listener);
			mDevice = device;
		}

		// 当ddmlib中ITestRunListener监听器触发testFailed方法时,该方法被调用.ITestRunListener的方法是由ddmlib内部调用的
		// 该方法将在android-cts\repository\logs生成bugreport信息的文本文件并打包
		@Override
		public void testFailed(TestFailure status, TestIdentifier test, String trace) {
			super.testFailed(status, test, trace);
			InputStreamSource bugSource = mDevice.getBugreport();
			// System.out.println("CtsTest.FailedTestBugreportGenerator.testFailed: 执行case失败,需要保存当前bugreport的信息");
			// CLog.logAndDisplay(LogLevel.INFO,
			// String.format("[testFailed] %s", test.toString()));
			super.testLog(String.format("bug-%s_%s", test.getClassName(), test.getTestName()), LogDataType.TEXT, bugSource);
			bugSource.cancel();
		}
	}

	/**
	 * 保存system缓冲区的日志 A {@link ResultForwarder} that will forward a logcat
	 * snapshot on each failed test.
	 */
	private static class FailedTestLogcatGenerator extends ResultForwarder {
		private ITestDevice mDevice;
		private int mNumLogcatBytes;

		public FailedTestLogcatGenerator(ITestInvocationListener listener, ITestDevice device, int maxLogcatBytes) {
			super(listener);
			mDevice = device;
			mNumLogcatBytes = maxLogcatBytes;
		}

		@Override
		public void testFailed(TestFailure status, TestIdentifier test, String trace) {
			super.testFailed(status, test, trace);
			// sleep a small amount of time to ensure test failure stack trace
			// makes it into logcat
			// capture
			RunUtil.getDefault().sleep(10);
			InputStreamSource logSource = mDevice.getLogcat(mNumLogcatBytes);
			super.testLog(String.format("logcat-%s_%s", test.getClassName(), test.getTestName()), LogDataType.TEXT, logSource);
			logSource.cancel();
		}
	}

	/**
	 * 保存截图 A {@link ResultForwarder} that will forward a screenshot on test
	 * failures.
	 */
	private static class FailedTestScreenshotGenerator extends ResultForwarder {
		private ITestDevice mDevice;

		public FailedTestScreenshotGenerator(ITestInvocationListener listener, ITestDevice device) {
			super(listener);
			mDevice = device;
		}

		@Override
		public void testFailed(TestFailure status, TestIdentifier test, String trace) {
			super.testFailed(status, test, trace);

			try {
				InputStreamSource screenSource = mDevice.getScreenshot();
				super.testLog(String.format("screenshot-%s_%s", test.getClassName(), test.getTestName()), LogDataType.PNG, screenSource);
				screenSource.cancel();
			} catch (DeviceNotAvailableException e) {
				// TODO: rethrow this somehow
				CLog.e("Device %s became unavailable while capturing screenshot, %s", mDevice.getSerialNumber(), e.toString());
			}
		}
	}

老大:bugreport信息保存

老二:logcat信息保存

老三:现场截图


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值