CTS测试框架 -- 命令执行

来源:互联网 发布:网络打印机新添加纸张 编辑:程序博客网 时间:2024/06/05 22:35

1.命令执行

经过了前面对于命令的调度,开启真正命令的执行,在TestInvocation中把configuration中的所有组件都取出来执行。
入口:TestInvocation.invoke

public void invoke(        IInvocationContext context, IConfiguration config, IRescheduler rescheduler,        ITestInvocationListener... extraListeners)                throws DeviceNotAvailableException, Throwable {    // 添加监听器,有device状态,log等    List<ITestInvocationListener> allListeners =            new ArrayList<>(config.getTestInvocationListeners().size() + extraListeners.length);    allListeners.addAll(config.getTestInvocationListeners());    allListeners.addAll(Arrays.asList(extraListeners));    if (config.getProfiler() != null) {        allListeners.add(new AggregatingProfilerListener(config.getProfiler()));    }    // 初始化log监听器    ITestInvocationListener listener = new LogSaverResultForwarder(config.getLogSaver(),            allListeners);    String currentDeviceName = null;    try {        mStatus = "fetching build";        config.getLogOutput().init();        getLogRegistry().registerLogger(config.getLogOutput());        // 按照前面的分析,命令的执行是以设备为单位的        // 这里需要知道所有执行命令的设备,逐个去在设备上执行        for (String deviceName : context.getDeviceConfigNames()) {            context.getDevice(deviceName).clearLastConnectedWifiNetwork();            // 添加命令行参数option            context.getDevice(deviceName).setOptions(                    config.getDeviceConfigByName(deviceName).getDeviceOptions());            if (config.getDeviceConfigByName(deviceName).getDeviceOptions()                    .isLogcatCaptureEnabled()) {                if (!(context.getDevice(deviceName).getIDevice() instanceof StubDevice)) {                    context.getDevice(deviceName).startLogcat();                }            }        }        String cmdLineArgs = config.getCommandLine();        if (cmdLineArgs != null) {            CLog.i("Invocation was started with cmd: %s", cmdLineArgs);        }        updateInvocationContext(context, config);        for (String deviceName : context.getDeviceConfigNames()) {            currentDeviceName = deviceName;            // 从configuration中取出需要的组件            IBuildInfo info = null;            ITestDevice device = context.getDevice(deviceName);            IDeviceConfiguration deviceConfig = config.getDeviceConfigByName(deviceName);            IBuildProvider provider = deviceConfig.getBuildProvider();            // Set the provider test tag            if (provider instanceof IInvocationContextReceiver) {                ((IInvocationContextReceiver)provider).setInvocationContext(context);            }            // Get the build            if (provider instanceof IDeviceBuildProvider) {                info = ((IDeviceBuildProvider)provider).getBuild(device);            } else {                info = provider.getBuild();            }            if (info != null) {                // 执行命令的设备的Serial                info.setDeviceSerial(device.getSerialNumber());                context.addDeviceBuildInfo(deviceName, info);                device.setRecovery(deviceConfig.getDeviceRecovery());            } else {                mStatus = "(no build to test)";                CLog.logAndDisplay(                        LogLevel.WARN,                        "No build found to test for device: %s",                        device.getSerialNumber());                rescheduleTest(config, rescheduler);                // save current log contents to global log                getLogRegistry().dumpToGlobalLog(config.getLogOutput());                // Set the exit code to error                setExitCode(ExitCode.NO_BUILD,                        new BuildRetrievalError("No build found to test."));                return;            }            // TODO: remove build update when reporting is done on context            updateBuild(info, config);        }        if (shardConfig(config, context, rescheduler)) {            CLog.i("Invocation for %s has been sharded, rescheduling",                    context.getSerials().toString());        } else {            if (config.getTests() == null || config.getTests().isEmpty()) {                CLog.e("No tests to run");            } else {                // 真正的执行                performInvocation(config, context, rescheduler, listener);                setExitCode(ExitCode.NO_ERROR, null);            }        }    } catch (BuildRetrievalError e) {        ...    } finally {        ...        // 停止继续logcat,保存        for (String deviceName : context.getDeviceConfigNames()) {            if (!(context.getDevice(deviceName).getIDevice() instanceof StubDevice)) {                context.getDevice(deviceName).stopLogcat();            }        }        ...    }}

performInvocation

private void performInvocation(IConfiguration config, IInvocationContext context,        IRescheduler rescheduler, ITestInvocationListener listener) throws Throwable {    boolean resumed = false;    String bugreportName = null;    long startTime = System.currentTimeMillis();    long elapsedTime = -1;    Throwable exception = null;    Throwable tearDownException = null;    ITestDevice badDevice = null;    startInvocation(config, context, listener);    try {        logDeviceBatteryLevel(context, "initial");        // 执行命令        prepareAndRun(config, context, listener);    } catch (BuildError e) {        ...    } finally {        ...        try {            // 执行doTeardown清理模板            doTeardown(config, context, exception);        } catch (Throwable e) {            tearDownException = e;            if (exception == null) {                // only report when the exception is new during tear down                reportFailure(tearDownException, listener, config, context, rescheduler);            }        }        // 执行clean以及保存log        ... cleanUp    if (tearDownException != null) {        throw tearDownException;    }}

prepareAndRun

private void prepareAndRun(        IConfiguration config, IInvocationContext context, ITestInvocationListener listener)        throws Throwable {    getRunUtil().allowInterrupt(true);    logDeviceBatteryLevel(context, "initial -> setup");    doSetup(config, context, listener);    logDeviceBatteryLevel(context, "setup -> test");    runTests(context, config, listener);    logDeviceBatteryLevel(context, "after test");}

doSetup:因为支持的测试种类很多,通过instanceof关键字去判断需要执行的测试到底是哪种接口的子类,就执行该模板的setup方法。

void doSetup(        IConfiguration config,        IInvocationContext context,        final ITestInvocationListener listener)        throws TargetSetupError, BuildError, DeviceNotAvailableException {    for (String deviceName : context.getDeviceConfigNames()) {        ITestDevice device = context.getDevice(deviceName);        if (device instanceof ITestLoggerReceiver) {            ((ITestLoggerReceiver) context.getDevice(deviceName))                    .setTestLogger(listener);        }        if (!config.getCommandOptions().shouldSkipPreDeviceSetup()) {            device.preInvocationSetup(context.getBuildInfo(deviceName));        }        for (ITargetPreparer preparer : config.getDeviceConfigByName(deviceName)                .getTargetPreparers()) {            if (preparer instanceof ITestLoggerReceiver) {                ((ITestLoggerReceiver) preparer).setTestLogger(listener);            }            preparer.setUp(device, context.getBuildInfo(deviceName));        }    }    for (IMultiTargetPreparer multipreparer : config.getMultiTargetPreparers()) {        if (multipreparer instanceof ITestLoggerReceiver) {            ((ITestLoggerReceiver) multipreparer).setTestLogger(listener);        }        multipreparer.setUp(context);    }    if (config.getProfiler() != null) {        config.getProfiler().setUp(context);    }    for (String deviceName : context.getDeviceConfigNames()) {        reportLogs(context.getDevice(deviceName), listener, Stage.SETUP);    }}

runTests:类似与doSetup,根据不同的情况,看需要执行的测试case到底哪种,进行相应的预处理,最后调用接口的run方法。

private void runTests(IInvocationContext context, IConfiguration config,        ITestInvocationListener listener) throws DeviceNotAvailableException {    for (IRemoteTest test : config.getTests()) {        if (test instanceof IDeviceTest) {            ((IDeviceTest)test).setDevice(context.getDevices().get(0));        }        if (test instanceof IBuildReceiver) {            ((IBuildReceiver)test).setBuild(context.getBuildInfo(                    context.getDevices().get(0)));        }        if (test instanceof ISystemStatusCheckerReceiver) {            ((ISystemStatusCheckerReceiver) test).setSystemStatusChecker(                    config.getSystemStatusCheckers());        }        if (test instanceof IMultiDeviceTest) {            ((IMultiDeviceTest)test).setDeviceInfos(context.getDeviceBuildMap());        }        if (test instanceof IInvocationContextReceiver) {            ((IInvocationContextReceiver)test).setInvocationContext(context);        }        test.run(listener);    }}

doTearDown:执行各种teardown接口,并进行清理工作

private void doTeardown(IConfiguration config, IInvocationContext context,        Throwable exception) throws Throwable {    Throwable throwable = null;    List<IMultiTargetPreparer> multiPreparers = config.getMultiTargetPreparers();    ListIterator<IMultiTargetPreparer> iterator =            multiPreparers.listIterator(multiPreparers.size());    while (iterator.hasPrevious()) {        IMultiTargetPreparer multipreparer = iterator.previous();        multipreparer.tearDown(context, throwable);    }    for (String deviceName : context.getDeviceConfigNames()) {        ITestDevice device = context.getDevice(deviceName);        device.clearLastConnectedWifiNetwork();        List<ITargetPreparer> preparers =                config.getDeviceConfigByName(deviceName).getTargetPreparers();        ListIterator<ITargetPreparer> itr = preparers.listIterator(preparers.size());        while (itr.hasPrevious()) {            ITargetPreparer preparer = itr.previous();            if(preparer instanceof ITargetCleaner) {                ITargetCleaner cleaner = (ITargetCleaner) preparer;                if (cleaner != null) {                    try {                        device.getSerialNumber());                        cleaner.tearDown(device, context.getBuildInfo(deviceName), exception);                    } catch (Throwable e) {                        throwable = e;                    }                }            }        }        device.postInvocationTearDown();    }    if (throwable != null) {        throw throwable;    }}

一般测试case都会实现setup,run,teardown,分别在其中做初始化,执行测试以及最后的收尾工作,通过反射的方式都拿到了测试实例,执行模板接口,所以真正执行case的时候只需要复写模板中定义好的方法即可。

总结

到这里,基础框架就介绍的差不多了,可以返回去再看下基础框架启动中最开始的一张大图,整体的流程从main的启动到最后这部分test的执行的逻辑都在里面,再梳理一遍。

现在再看整个基础框架Trade-Federation,虽然这是一个java程序,但是其实就是为Android设备量身定做的,各种命令的运行其实都需要Android设备的配合,整个基础框架的功能已经很强大了,可以说,只要写好了case,很多测试case在这个基础框架的基础上就可以直接运行。另外,其中还有很多关于设备的管理,recorvery,host-log以及device-log的收集等,这些主要是通过前面提到的各种listener中实现的,有兴趣可以自己去详细了解下。

下一篇开始介绍在基础框架上封装了一层的CTS测试框架

原创粉丝点击