| /* |
| * 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 License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.cts.tradefed.testtype; |
| |
| import com.android.cts.tradefed.build.CtsBuildHelper; |
| import com.android.cts.tradefed.device.DeviceInfoCollector; |
| import com.android.cts.tradefed.result.CtsTestStatus; |
| import com.android.cts.tradefed.result.PlanCreator; |
| import com.android.ddmlib.Log; |
| import com.android.ddmlib.Log.LogLevel; |
| import com.android.ddmlib.testrunner.TestIdentifier; |
| import com.android.tradefed.build.IBuildInfo; |
| import com.android.tradefed.config.ConfigurationException; |
| import com.android.tradefed.config.Option; |
| import com.android.tradefed.config.Option.Importance; |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.device.ITestDevice; |
| import com.android.tradefed.device.TestDeviceOptions; |
| import com.android.tradefed.log.LogUtil.CLog; |
| import com.android.tradefed.result.ITestInvocationListener; |
| import com.android.tradefed.result.InputStreamSource; |
| import com.android.tradefed.result.LogDataType; |
| import com.android.tradefed.result.ResultForwarder; |
| import com.android.tradefed.testtype.IBuildReceiver; |
| import com.android.tradefed.testtype.IDeviceTest; |
| import com.android.tradefed.testtype.IRemoteTest; |
| import com.android.tradefed.testtype.IResumableTest; |
| import com.android.tradefed.testtype.IShardableTest; |
| import com.android.tradefed.util.xml.AbstractXmlParser.ParseException; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.InputStream; |
| import java.lang.InterruptedException; |
| import java.lang.System; |
| import java.lang.Thread; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Queue; |
| import java.util.Set; |
| |
| import junit.framework.Test; |
| |
| /** |
| * A {@link Test} for running CTS tests. |
| * <p/> |
| * Supports running all the tests contained in a CTS plan, or individual test packages. |
| */ |
| public class CtsTest implements IDeviceTest, IResumableTest, IShardableTest, IBuildReceiver { |
| private static final String LOG_TAG = "CtsTest"; |
| |
| public static final String PLAN_OPTION = "plan"; |
| private static final String PACKAGE_OPTION = "package"; |
| private static final String CLASS_OPTION = "class"; |
| private static final String METHOD_OPTION = "method"; |
| public static final String CONTINUE_OPTION = "continue-session"; |
| public static final String RUN_KNOWN_FAILURES_OPTION = "run-known-failures"; |
| |
| public static final String PACKAGE_NAME_METRIC = "packageName"; |
| public static final String PACKAGE_DIGEST_METRIC = "packageDigest"; |
| |
| private ITestDevice mDevice; |
| |
| @Option(name = PLAN_OPTION, description = "the test plan to run.", |
| importance = Importance.IF_UNSET) |
| private String mPlanName = null; |
| |
| @Option(name = PACKAGE_OPTION, shortName = 'p', description = "the test packages(s) to run.", |
| importance = Importance.IF_UNSET) |
| private Collection<String> mPackageNames = new ArrayList<String>(); |
| |
| @Option(name = "exclude-package", description = "the test packages(s) to exclude from the run.") |
| private Collection<String> mExcludedPackageNames = new ArrayList<String>(); |
| |
| @Option(name = CLASS_OPTION, shortName = 'c', description = "run a specific test class.", |
| importance = Importance.IF_UNSET) |
| private String mClassName = null; |
| |
| @Option(name = METHOD_OPTION, shortName = 'm', |
| description = "run a specific test method, from given --class.", |
| importance = Importance.IF_UNSET) |
| private String mMethodName = null; |
| |
| @Option(name = CONTINUE_OPTION, |
| description = "continue a previous test session.", |
| importance = Importance.IF_UNSET) |
| private Integer mContinueSessionId = null; |
| |
| @Option(name = "skip-device-info", shortName = 'd', description = |
| "flag to control whether to collect info from device. Providing this flag will speed up " + |
| "test execution for short test runs but will result in required data being omitted from " + |
| "the test report.") |
| private boolean mSkipDeviceInfo = false; |
| |
| @Option(name = "resume", description = |
| "flag to attempt to automatically resume aborted test run on another connected device. ") |
| private boolean mResume = false; |
| |
| @Option(name = "shards", description = |
| "shard the tests to run into separately runnable chunks to execute on multiple devices " + |
| "concurrently.") |
| private int mShards = 1; |
| |
| @Option(name = "screenshot", description = |
| "flag for taking a screenshot of the device when test execution is complete.") |
| private boolean mScreenshot = false; |
| |
| @Option(name = "bugreport", shortName = 'b', description = |
| "take a bugreport after each failed test. " + |
| "Warning: can potentially use a lot of disk space.") |
| private boolean mBugreport = false; |
| |
| @Option(name = RUN_KNOWN_FAILURES_OPTION, shortName = 'k', description = |
| "run tests including known failures") |
| private boolean mIncludeKnownFailures; |
| |
| @Option(name = "reboot-per-package", description = |
| "Reboot after each package run") |
| private boolean mRebootPerPackage = false; |
| |
| @Option(name = "reboot-wait-time", description = |
| "Additional wait time in ms after boot complete. Meaningful only with reboot-per-package option") |
| private int mRebootWaitTimeMSec = 2 * 60 * 1000; |
| |
| @Option(name = "reboot-interval", description = |
| "Interval between each reboot in min. Meaningful only with reboot-per-package option") |
| private int mRebootIntervalMin = 30; |
| |
| /** data structure for a {@link IRemoteTest} and its known tests */ |
| class TestPackage { |
| private final IRemoteTest mTestForPackage; |
| private final Collection<TestIdentifier> mKnownTests; |
| private final ITestPackageDef mPackageDef; |
| |
| TestPackage(ITestPackageDef packageDef, IRemoteTest testForPackage, |
| Collection<TestIdentifier> knownTests) { |
| mPackageDef = packageDef; |
| mTestForPackage = testForPackage; |
| mKnownTests = knownTests; |
| } |
| |
| IRemoteTest getTestForPackage() { |
| return mTestForPackage; |
| } |
| |
| Collection<TestIdentifier> getKnownTests() { |
| return mKnownTests; |
| } |
| |
| ITestPackageDef getPackageDef() { |
| return mPackageDef; |
| } |
| |
| /** |
| * Return the test run name that should be used for the TestPackage |
| */ |
| String getTestRunName() { |
| return mPackageDef.getUri(); |
| } |
| } |
| |
| /** |
| * 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; |
| } |
| |
| @Override |
| public void testFailed(TestFailure status, TestIdentifier test, String trace) { |
| super.testFailed(status, test, trace); |
| InputStreamSource bugSource = mDevice.getBugreport(); |
| super.testLog(String.format("bug-%s", test.toString()), LogDataType.TEXT, |
| bugSource); |
| bugSource.cancel(); |
| } |
| } |
| |
| /** list of remaining tests to execute */ |
| private List<TestPackage> mRemainingTestPkgs = null; |
| |
| private CtsBuildHelper mCtsBuild = null; |
| private IBuildInfo mBuildInfo = null; |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public ITestDevice getDevice() { |
| return mDevice; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setDevice(ITestDevice device) { |
| mDevice = device; |
| } |
| |
| /** |
| * Set the plan name to run. |
| * <p/> |
| * Exposed for unit testing |
| */ |
| void setPlanName(String planName) { |
| mPlanName = planName; |
| } |
| |
| /** |
| * Set the skip collect device info flag. |
| * <p/> |
| * Exposed for unit testing |
| */ |
| void setSkipDeviceInfo(boolean skipDeviceInfo) { |
| mSkipDeviceInfo = skipDeviceInfo; |
| } |
| |
| /** |
| * Adds a package name to the list of test packages to run. |
| * <p/> |
| * Exposed for unit testing |
| */ |
| void addPackageName(String packageName) { |
| mPackageNames.add(packageName); |
| } |
| |
| /** |
| * Adds a package name to the list of test packages to exclude. |
| * <p/> |
| * Exposed for unit testing |
| */ |
| void addExcludedPackageName(String packageName) { |
| mExcludedPackageNames.add(packageName); |
| } |
| |
| /** |
| * Set the test class name to run. |
| * <p/> |
| * Exposed for unit testing |
| */ |
| void setClassName(String className) { |
| mClassName = className; |
| } |
| |
| /** |
| * Set the test method name to run. |
| * <p/> |
| * Exposed for unit testing |
| */ |
| void setMethodName(String methodName) { |
| mMethodName = methodName; |
| } |
| |
| /** |
| * Sets the test session id to continue. |
| * <p/> |
| * Exposed for unit testing |
| */ |
| void setContinueSessionId(int sessionId) { |
| mContinueSessionId = sessionId; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean isResumable() { |
| return mResume; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setBuild(IBuildInfo build) { |
| mCtsBuild = CtsBuildHelper.createBuildHelper(build); |
| mBuildInfo = build; |
| } |
| |
| /** |
| * Set the CTS build container. |
| * <p/> |
| * Exposed so unit tests can mock the provided build. |
| * |
| * @param buildHelper |
| */ |
| void setBuildHelper(CtsBuildHelper buildHelper) { |
| mCtsBuild = buildHelper; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { |
| if (getDevice() == null) { |
| throw new IllegalArgumentException("missing device"); |
| } |
| |
| if (mRemainingTestPkgs == null) { |
| checkFields(); |
| mRemainingTestPkgs = buildTestsToRun(); |
| } |
| if (mBugreport) { |
| FailedTestBugreportGenerator bugListener = new FailedTestBugreportGenerator(listener, |
| getDevice()); |
| listener = bugListener; |
| } |
| |
| // collect and install the prerequisiteApks first, to save time when multiple test |
| // packages are using the same prerequisite apk (I'm looking at you, CtsTestStubs!) |
| Collection<String> prerequisiteApks = getPrerequisiteApks(mRemainingTestPkgs); |
| Collection<String> uninstallPackages = getPrerequisitePackageNames(mRemainingTestPkgs); |
| ResultFilter filter = new ResultFilter(listener, mRemainingTestPkgs); |
| |
| try { |
| installPrerequisiteApks(prerequisiteApks); |
| |
| // always collect the device info, even for resumed runs, since test will likely be |
| // running on a different device |
| collectDeviceInfo(getDevice(), mCtsBuild, listener); |
| if (mRemainingTestPkgs.size() > 1) { |
| Log.i(LOG_TAG, "Initial reboot for multiple packages"); |
| rebootDevice(); |
| } |
| long prevTime = System.currentTimeMillis(); |
| long intervalInMSec = mRebootIntervalMin * 60 * 1000; |
| while (!mRemainingTestPkgs.isEmpty()) { |
| TestPackage knownTests = mRemainingTestPkgs.get(0); |
| |
| IRemoteTest test = knownTests.getTestForPackage(); |
| if (test instanceof IDeviceTest) { |
| ((IDeviceTest)test).setDevice(getDevice()); |
| } |
| if (test instanceof IBuildReceiver) { |
| ((IBuildReceiver)test).setBuild(mBuildInfo); |
| } |
| |
| forwardPackageDetails(knownTests.getPackageDef(), listener); |
| test.run(filter); |
| mRemainingTestPkgs.remove(0); |
| if (mRemainingTestPkgs.size() > 0) { |
| if (mRebootPerPackage) { |
| long currentTime = System.currentTimeMillis(); |
| if ((currentTime - prevTime) > intervalInMSec) { |
| Log.i(LOG_TAG, String.format("Rebooting after running package %s", |
| knownTests.getPackageDef().getName())); |
| rebootDevice(); |
| prevTime = System.currentTimeMillis(); |
| } |
| } |
| // remove artifacts like status bar from the previous test. |
| // But this cannot dismiss dialog popped-up. |
| changeToHomeScreen(); |
| } |
| } |
| |
| if (mScreenshot) { |
| InputStreamSource screenshotSource = getDevice().getScreenshot(); |
| try { |
| listener.testLog("screenshot", LogDataType.PNG, screenshotSource); |
| } finally { |
| screenshotSource.cancel(); |
| } |
| } |
| |
| uninstallPrequisiteApks(uninstallPackages); |
| |
| } finally { |
| filter.reportUnexecutedTests(); |
| } |
| } |
| |
| private void rebootDevice() throws DeviceNotAvailableException { |
| final int TIMEOUT_MS = 4 * 60 * 1000; |
| TestDeviceOptions options = mDevice.getOptions(); |
| // store default value and increase time-out for reboot |
| int rebootTimeout = options.getRebootTimeout(); |
| long onlineTimeout = options.getOnlineTimeout(); |
| options.setRebootTimeout(TIMEOUT_MS); |
| options.setOnlineTimeout(TIMEOUT_MS); |
| mDevice.setOptions(options); |
| |
| mDevice.reboot(); |
| |
| // restore default values |
| options.setRebootTimeout(rebootTimeout); |
| options.setOnlineTimeout(onlineTimeout); |
| mDevice.setOptions(options); |
| Log.i(LOG_TAG, "Rebooting done"); |
| try { |
| Thread.sleep(mRebootWaitTimeMSec); |
| } catch (InterruptedException e) { |
| Log.i(LOG_TAG, "Boot wait interrupted"); |
| } |
| } |
| |
| private void changeToHomeScreen() throws DeviceNotAvailableException { |
| final String homeCmd = "input keyevent 3"; |
| |
| mDevice.executeShellCommand(homeCmd); |
| try { |
| Thread.sleep(1000); |
| } catch (InterruptedException e) { |
| //ignore |
| } |
| } |
| /** |
| * Build the list of test packages to run |
| */ |
| private List<TestPackage> buildTestsToRun() { |
| List<TestPackage> testPkgList = new LinkedList<TestPackage>(); |
| try { |
| ITestPackageRepo testRepo = createTestCaseRepo(); |
| Collection<ITestPackageDef> testPkgDefs = getTestPackagesToRun(testRepo); |
| |
| for (ITestPackageDef testPkgDef : testPkgDefs) { |
| addTestPackage(testPkgList, testPkgDef); |
| } |
| if (testPkgList.isEmpty()) { |
| Log.logAndDisplay(LogLevel.WARN, LOG_TAG, "No tests to run"); |
| } |
| } catch (FileNotFoundException e) { |
| throw new IllegalArgumentException("failed to find CTS plan file", e); |
| } catch (ParseException e) { |
| throw new IllegalArgumentException("failed to parse CTS plan file", e); |
| } catch (ConfigurationException e) { |
| throw new IllegalArgumentException("failed to process arguments", e); |
| } |
| return testPkgList; |
| } |
| |
| /** |
| * Adds a test package to the list of packages to test |
| * |
| * @param testList |
| * @param testPkgDef |
| */ |
| private void addTestPackage(List<TestPackage> testList, ITestPackageDef testPkgDef) { |
| IRemoteTest testForPackage = testPkgDef.createTest(mCtsBuild.getTestCasesDir()); |
| if (testForPackage != null) { |
| Collection<TestIdentifier> knownTests = testPkgDef.getTests(); |
| testList.add(new TestPackage(testPkgDef, testForPackage, knownTests)); |
| } |
| } |
| |
| /** |
| * Return the list of test package defs to run |
| * |
| * @return the list of test package defs to run |
| * @throws ParseException |
| * @throws FileNotFoundException |
| * @throws ConfigurationException |
| */ |
| private Collection<ITestPackageDef> getTestPackagesToRun(ITestPackageRepo testRepo) |
| throws ParseException, FileNotFoundException, ConfigurationException { |
| // use LinkedHashSet to have predictable iteration order |
| Set<ITestPackageDef> testPkgDefs = new LinkedHashSet<ITestPackageDef>(); |
| if (mPlanName != null) { |
| Log.i(LOG_TAG, String.format("Executing CTS test plan %s", mPlanName)); |
| File ctsPlanFile = mCtsBuild.getTestPlanFile(mPlanName); |
| ITestPlan plan = createPlan(mPlanName); |
| plan.parse(createXmlStream(ctsPlanFile)); |
| for (String uri : plan.getTestUris()) { |
| if (!mExcludedPackageNames.contains(uri)) { |
| ITestPackageDef testPackage = testRepo.getTestPackage(uri); |
| testPackage.setExcludedTestFilter(plan.getExcludedTestFilter(uri)); |
| testPkgDefs.add(testPackage); |
| } |
| } |
| } else if (mPackageNames.size() > 0){ |
| Log.i(LOG_TAG, String.format("Executing CTS test packages %s", mPackageNames)); |
| for (String uri : mPackageNames) { |
| ITestPackageDef testPackage = testRepo.getTestPackage(uri); |
| if (testPackage != null) { |
| testPkgDefs.add(testPackage); |
| } else { |
| throw new IllegalArgumentException(String.format( |
| "Could not find test package %s. " + |
| "Use 'list packages' to see available packages." , uri)); |
| } |
| } |
| } else if (mClassName != null) { |
| Log.i(LOG_TAG, String.format("Executing CTS test class %s", mClassName)); |
| // try to find package to run from class name |
| String packageUri = testRepo.findPackageForTest(mClassName); |
| if (packageUri != null) { |
| ITestPackageDef testPackageDef = testRepo.getTestPackage(packageUri); |
| testPackageDef.setClassName(mClassName, mMethodName); |
| testPkgDefs.add(testPackageDef); |
| } else { |
| Log.logAndDisplay(LogLevel.WARN, LOG_TAG, String.format( |
| "Could not find package for test class %s", mClassName)); |
| } |
| } else if (mContinueSessionId != null) { |
| // create an in-memory derived plan that contains the notExecuted tests from previous |
| // session |
| // use timestamp as plan name so it will hopefully be unique |
| String uniquePlanName = Long.toString(System.currentTimeMillis()); |
| PlanCreator planCreator = new PlanCreator(uniquePlanName, mContinueSessionId, |
| CtsTestStatus.NOT_EXECUTED); |
| ITestPlan plan = createPlan(planCreator); |
| for (String uri : plan.getTestUris()) { |
| if (!mExcludedPackageNames.contains(uri)) { |
| ITestPackageDef testPackage = testRepo.getTestPackage(uri); |
| testPackage.setExcludedTestFilter(plan.getExcludedTestFilter(uri)); |
| testPkgDefs.add(testPackage); |
| } |
| } |
| } else { |
| // should never get here - was checkFields() not called? |
| throw new IllegalStateException("nothing to run?"); |
| } |
| return testPkgDefs; |
| } |
| |
| /** |
| * Return the list of unique prerequisite Android package names |
| * @param testPackages |
| */ |
| private Collection<String> getPrerequisitePackageNames(List<TestPackage> testPackages) { |
| Set<String> pkgNames = new HashSet<String>(); |
| for (TestPackage testPkg : testPackages) { |
| String pkgName = testPkg.mPackageDef.getTargetPackageName(); |
| if (pkgName != null) { |
| pkgNames.add(pkgName); |
| } |
| } |
| return pkgNames; |
| } |
| |
| /** |
| * Return the list of unique prerequisite apks to install |
| * @param testPackages |
| */ |
| private Collection<String> getPrerequisiteApks(List<TestPackage> testPackages) { |
| Set<String> apkNames = new HashSet<String>(); |
| for (TestPackage testPkg : testPackages) { |
| String apkName = testPkg.mPackageDef.getTargetApkName(); |
| if (apkName != null) { |
| apkNames.add(apkName); |
| } |
| } |
| return apkNames; |
| } |
| |
| /** |
| * Install the collection of test apk file names |
| * |
| * @param prerequisiteApks |
| * @throws DeviceNotAvailableException |
| */ |
| private void installPrerequisiteApks(Collection<String> prerequisiteApks) |
| throws DeviceNotAvailableException { |
| for (String apkName : prerequisiteApks) { |
| try { |
| File apkFile = mCtsBuild.getTestApp(apkName); |
| String errorCode = getDevice().installPackage(apkFile, true); |
| if (errorCode != null) { |
| CLog.e("Failed to install %s. Reason: %s", apkName, errorCode); |
| } |
| } catch (FileNotFoundException e) { |
| CLog.e("Could not find test apk %s", apkName); |
| } |
| } |
| } |
| |
| /** |
| * Uninstalls the collection of android package names from device. |
| * |
| * @param uninstallPackages |
| */ |
| private void uninstallPrequisiteApks(Collection<String> uninstallPackages) |
| throws DeviceNotAvailableException { |
| for (String pkgName : uninstallPackages) { |
| getDevice().uninstallPackage(pkgName); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Collection<IRemoteTest> split() { |
| if (mShards <= 1) { |
| return null; |
| } |
| checkFields(); |
| List<TestPackage> allTests = buildTestsToRun(); |
| |
| if (allTests.size() <= 1) { |
| Log.w(LOG_TAG, "no tests to shard!"); |
| return null; |
| } |
| |
| // treat shardQueue as a circular queue, to sequentially distribute tests among shards |
| Queue<IRemoteTest> shardQueue = new LinkedList<IRemoteTest>(); |
| // don't create more shards than the number of tests we have! |
| for (int i = 0; i < mShards && i < allTests.size(); i++) { |
| CtsTest shard = new CtsTest(); |
| shard.mRemainingTestPkgs = new LinkedList<TestPackage>(); |
| shardQueue.add(shard); |
| } |
| while (!allTests.isEmpty()) { |
| TestPackage testPair = allTests.remove(0); |
| CtsTest shard = (CtsTest)shardQueue.poll(); |
| shard.mRemainingTestPkgs.add(testPair); |
| shardQueue.add(shard); |
| } |
| return shardQueue; |
| } |
| |
| /** |
| * Runs the device info collector instrumentation on device, and forwards it to test listeners |
| * as run metrics. |
| * <p/> |
| * Exposed so unit tests can mock. |
| * |
| * @throws DeviceNotAvailableException |
| */ |
| void collectDeviceInfo(ITestDevice device, CtsBuildHelper ctsBuild, |
| ITestInvocationListener listener) throws DeviceNotAvailableException { |
| if (!mSkipDeviceInfo) { |
| DeviceInfoCollector.collectDeviceInfo(device, ctsBuild.getTestCasesDir(), listener); |
| } |
| } |
| |
| /** |
| * Factory method for creating a {@link ITestPackageRepo}. |
| * <p/> |
| * Exposed for unit testing |
| */ |
| ITestPackageRepo createTestCaseRepo() { |
| return new TestPackageRepo(mCtsBuild.getTestCasesDir(), mIncludeKnownFailures); |
| } |
| |
| /** |
| * Factory method for creating a {@link TestPlan}. |
| * <p/> |
| * Exposed for unit testing |
| */ |
| ITestPlan createPlan(String planName) { |
| return new TestPlan(planName); |
| } |
| |
| /** |
| * Factory method for creating a {@link TestPlan} from a {@link PlanCreator}. |
| * <p/> |
| * Exposed for unit testing |
| * @throws ConfigurationException |
| */ |
| ITestPlan createPlan(PlanCreator planCreator) throws ConfigurationException { |
| return planCreator.createDerivedPlan(mCtsBuild); |
| } |
| |
| /** |
| * Factory method for creating a {@link InputStream} from a plan xml file. |
| * <p/> |
| * Exposed for unit testing |
| */ |
| InputStream createXmlStream(File xmlFile) throws FileNotFoundException { |
| return new BufferedInputStream(new FileInputStream(xmlFile)); |
| } |
| |
| private void checkFields() { |
| // for simplicity of command line usage, make --plan, --package, and --class mutually |
| // exclusive |
| boolean mutualExclusiveArgs = xor(mPlanName != null, mPackageNames.size() > 0, |
| mClassName != null, mContinueSessionId != null); |
| |
| if (!mutualExclusiveArgs) { |
| throw new IllegalArgumentException(String.format( |
| "Ambiguous or missing arguments. " + |
| "One and only one of --%s --%s(s), --%s or --%s to run can be specified", |
| PLAN_OPTION, PACKAGE_OPTION, CLASS_OPTION, CONTINUE_OPTION)); |
| } |
| if (mMethodName != null && mClassName == null) { |
| throw new IllegalArgumentException(String.format( |
| "Must specify --%s when --%s is used", CLASS_OPTION, METHOD_OPTION)); |
| } |
| if (mCtsBuild == null) { |
| throw new IllegalArgumentException("missing CTS build"); |
| } |
| } |
| |
| /** |
| * Helper method to perform exclusive or on list of boolean arguments |
| * |
| * @param args set of booleans on which to perform exclusive or |
| * @return <code>true</code> if one and only one of <var>args</code> is <code>true</code>. |
| * Otherwise return <code>false</code>. |
| */ |
| private boolean xor(boolean... args) { |
| boolean currentVal = args[0]; |
| for (int i=1; i < args.length; i++) { |
| if (currentVal && args[i]) { |
| return false; |
| } |
| currentVal |= args[i]; |
| } |
| return currentVal; |
| } |
| |
| /** |
| * Forward the digest and package name to the listener as a metric |
| * |
| * @param listener |
| */ |
| private void forwardPackageDetails(ITestPackageDef def, ITestInvocationListener listener) { |
| Map<String, String> metrics = new HashMap<String, String>(2); |
| metrics.put(PACKAGE_NAME_METRIC, def.getName()); |
| metrics.put(PACKAGE_DIGEST_METRIC, def.getDigest()); |
| listener.testRunStarted(def.getUri(), 0); |
| listener.testRunEnded(0, metrics); |
| } |
| } |