• 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的工作。







  • 相关阅读:
    Django + Vue cli 3.0 访问静态资源问题
    服务器宕机,mysql无法启动,job for mysql.service failed because the process exited with error code,数据库备份与恢复
    Git 深度学习填坑之旅三(分支branch、远程操作)
    Git 深度学习填坑之旅二(文件三种状态、打标签)
    2017-2018学年校历
    gson
    Android adb.exe程序启动不起来,如何处理
    Android LayoutInflater详解
    android genymation eclipse安装
    视频
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4050783.html
Copyright © 2020-2023  润新知