Support running a CTS test plan of instrumentation tests.
Change-Id: I30473d74c9d1030b22d3aad66fa1ee317681ff97
diff --git a/tools/tradefed-host/.classpath b/tools/tradefed-host/.classpath
index 67d42b5..b96e0da 100644
--- a/tools/tradefed-host/.classpath
+++ b/tools/tradefed-host/.classpath
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="res"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry combineaccessrules="false" kind="src" path="/tradefed-prebuilt"/>
- <classpathentry combineaccessrules="false" kind="src" path="/ddmlib-prebuilt"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/tradefederation"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/tools/tradefed-host/Android.mk b/tools/tradefed-host/Android.mk
index a05ed40..f2a740d 100644
--- a/tools/tradefed-host/Android.mk
+++ b/tools/tradefed-host/Android.mk
@@ -18,6 +18,7 @@
# Only compile source java files in this lib.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := res
LOCAL_MODULE := cts-tradefed
LOCAL_MODULE_TAGS := optional
diff --git a/tools/tradefed-host/res/config/cts.xml b/tools/tradefed-host/res/config/cts.xml
new file mode 100644
index 0000000..2ada89e
--- /dev/null
+++ b/tools/tradefed-host/res/config/cts.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration
+ description="Runs a CTS plan from a pre-existing CTS installation">
+
+ <build_provider class="com.android.cts.tradefed.targetsetup.CtsBuildProvider" />
+ <device_recovery class="com.android.tradefed.device.WaitDeviceRecovery" />
+ <target_preparer class="com.android.cts.tradefed.targetsetup.CtsSetup" />
+ <test class="com.android.cts.tradefed.testtype.PlanTest" />
+ <logger class="com.android.tradefed.log.FileLogger" />
+ <!-- TODO: change this to the CTS XML result reporter when available -->
+ <result_reporter class="com.android.tradefed.result.XmlResultReporter" />
+
+</configuration>
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsBuildHelper.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsBuildHelper.java
index d8aabc5..9597109 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsBuildHelper.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsBuildHelper.java
@@ -67,7 +67,7 @@
* @throws FileNotFoundException if file does not exist
*/
public File getTestApp(String appFileName) throws FileNotFoundException {
- File apkFile = new File(new File(getRepositoryDir(), "testcases"), appFileName);
+ File apkFile = new File(getTestCasesDir(), appFileName);
if (!apkFile.exists()) {
throw new FileNotFoundException(String.format("CTS test app file %s does not exist",
apkFile.getAbsolutePath()));
@@ -80,22 +80,35 @@
}
/**
- * @return a {@link File} representing the test plan file with given name
- * @throws FileNotFoundException if file does not exist
- */
- public File getTestPlan(String testPlanFileName) throws FileNotFoundException {
- File planFile = new File(new File(getRepositoryDir(), "plans"), testPlanFileName);
- if (!planFile.exists()) {
- throw new FileNotFoundException(String.format("CTS test plan file %s does not exist",
- planFile.getAbsolutePath()));
- }
- return planFile;
- }
-
- /**
* @return a {@link File} representing the results directory.
*/
public File getResultsDir() {
return new File(getRepositoryDir(), "results");
}
+
+ /**
+ * @return a {@link File} representing the test cases directory
+ * @throws FileNotFoundException if dir does not exist
+ */
+ public File getTestCasesDir() throws FileNotFoundException {
+ File dir = new File(getRepositoryDir(), "testcases");
+ if (!dir.exists()) {
+ throw new FileNotFoundException(String.format(
+ "CTS test cases directory %s does not exist", dir.getAbsolutePath()));
+ }
+ return dir;
+ }
+
+ /**
+ * @return a {@link File} representing the test plan directory
+ * @throws FileNotFoundException if dir does not exist
+ */
+ public File getTestPlansDir() throws FileNotFoundException {
+ File dir = new File(getRepositoryDir(), "plans");
+ if (!dir.exists()) {
+ throw new FileNotFoundException(String.format(
+ "CTS test plans directory %s does not exist", dir.getAbsolutePath()));
+ }
+ return dir;
+ }
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsDeviceSetup.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsDeviceSetup.java
index 65f09f3..121c02a 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsDeviceSetup.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsDeviceSetup.java
@@ -16,6 +16,8 @@
package com.android.cts.tradefed.targetsetup;
import com.android.ddmlib.Log;
+import com.android.tradefed.config.IConfiguration;
+import com.android.tradefed.config.IConfigurationReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.targetsetup.BuildError;
@@ -35,7 +37,7 @@
* This class is NOT intended for 'official' CTS runs against a production device as the steps
* performed by this class require a debug build (aka 'adb root' must succeed).
*/
-public class CtsDeviceSetup extends DeviceSetup {
+public class CtsDeviceSetup extends DeviceSetup implements IConfigurationReceiver {
private static final String LOG_TAG = "CtsDeviceSetup";
@@ -43,6 +45,15 @@
private static final String ACCESSIBILITY_SERVICE_APK_FILE_NAME =
"CtsDelegatingAccessibilityService.apk";
+ private IConfiguration mConfiguration = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setConfiguration(IConfiguration configuration) {
+ mConfiguration = configuration;
+ }
+
/**
* {@inheritDoc}
*/
@@ -65,6 +76,7 @@
// TODO: turn on mock locations
CtsSetup ctsSetup = new CtsSetup();
+ ctsSetup.setConfiguration(mConfiguration);
enableAccessibilityService(device, buildHelper, ctsSetup);
// end root setup steps
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsSetup.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsSetup.java
index 7af703b..1ac2ab9 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsSetup.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetsetup/CtsSetup.java
@@ -15,6 +15,10 @@
*/
package com.android.cts.tradefed.targetsetup;
+import com.android.cts.tradefed.testtype.PlanTest;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.IConfiguration;
+import com.android.tradefed.config.IConfigurationReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.targetsetup.BuildError;
@@ -31,12 +35,14 @@
* <p/>
* All the actions performed in this class must work on a production device.
*/
-public class CtsSetup implements ITargetPreparer {
+public class CtsSetup implements ITargetPreparer, IConfigurationReceiver {
private static final String RUNNER_APK_NAME = "android.core.tests.runner.apk";
// TODO: read this from configuration file rather than hardcoding
private static final String TEST_STUBS_APK = "CtsTestStubs.apk";
+ private IConfiguration mConfiguration = null;
+
/**
* Factory method to create a {@link CtsBuildHelper}.
* <p/>
@@ -49,18 +55,35 @@
/**
* {@inheritDoc}
*/
+ public void setConfiguration(IConfiguration configuration) {
+ mConfiguration = configuration;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
BuildError, DeviceNotAvailableException {
if (!(buildInfo instanceof IFolderBuildInfo)) {
throw new IllegalArgumentException("Provided buildInfo is not a IFolderBuildInfo");
}
+ if (mConfiguration == null) {
+ throw new IllegalStateException("setConfiguration() was not called before setUp");
+ }
IFolderBuildInfo ctsBuildInfo = (IFolderBuildInfo)buildInfo;
try {
CtsBuildHelper buildHelper = createBuildHelper(ctsBuildInfo.getRootDir());
+ // pass necessary build information to the other config objects
+ mConfiguration.injectOptionValue(PlanTest.TEST_CASES_DIR_OPTION,
+ buildHelper.getTestCasesDir().getAbsolutePath());
+ mConfiguration.injectOptionValue(PlanTest.TEST_PLANS_DIR_OPTION,
+ buildHelper.getTestPlansDir().getAbsolutePath());
installCtsPrereqs(device, buildHelper);
gatherDeviceStats(device, buildHelper);
} catch (FileNotFoundException e) {
throw new TargetSetupError("Invalid CTS installation", e);
+ } catch (ConfigurationException e) {
+ throw new TargetSetupError("Failed to set repository directory options", e);
}
}
@@ -98,7 +121,7 @@
private void gatherDeviceStats(ITestDevice device, CtsBuildHelper ctsBuild) {
// TODO: implement this
- // install TestDeviceSetup.apk, run its instrumentation, parse results, store them in the
- // CtsBuildInfo, then uninstall TestDeviceSetup.apk
+ // install DeviceInfoCollector.apk, run its instrumentation, parse results, store them in
+ // the ctsBuild, then uninstall TestDeviceSetup.apk
}
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/IPlanXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/IPlanXmlParser.java
new file mode 100644
index 0000000..f7d4ab1
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/IPlanXmlParser.java
@@ -0,0 +1,42 @@
+/*
+ * 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.tradefed.util.xml.AbstractXmlParser.ParseException;
+
+import java.io.InputStream;
+import java.util.Collection;
+
+/**
+ * Interface for accessing test plan data.
+ */
+interface IPlanXmlParser {
+
+ /**
+ * Parse the test plan data from given stream.
+ *
+ * @param xmlStream the {@link InputStream} that contains the test plan xml.
+ */
+ public void parse(InputStream xmlStream) throws ParseException;
+
+ /**
+ * Gets the list of test uris parsed from the plan.
+ * <p/>
+ * Must be called after {@link IPlanXmlParser#parse(InputStream)}.
+ */
+ public Collection<String> getTestUris();
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestCaseRepo.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestCaseRepo.java
new file mode 100644
index 0000000..18ea7cf
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestCaseRepo.java
@@ -0,0 +1,36 @@
+/*
+ * 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.tradefed.testtype.IRemoteTest;
+
+import java.util.Collection;
+
+/**
+ * Interface for accessing tests from the CTS repository.
+ */
+interface ITestCaseRepo {
+
+ /**
+ * Gets a list of tests identified by given list of uris
+ *
+ * @param testUris the string uris
+ * @return a {@link Collection} of {@link IRemoteTest}
+ */
+ public Collection<IRemoteTest> getTests(Collection<String> testUris);
+
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanTest.java
new file mode 100644
index 0000000..65932b8
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.ddmlib.Log;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.AbstractRemoteTest;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+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.util.Collection;
+import java.util.List;
+
+import junit.framework.Test;
+
+/**
+ * A {@link Test} that runs all the tests in the CTS test plan with given name
+ */
+public class PlanTest extends AbstractRemoteTest implements IDeviceTest, IRemoteTest {
+
+ private static final String LOG_TAG = "PlanTest";
+
+ public static final String TEST_CASES_DIR_OPTION = "test-cases-path";
+ public static final String TEST_PLANS_DIR_OPTION = "test-plans-path";
+
+ private ITestDevice mDevice;
+
+ @Option(name = "plan", description = "the test plan to run")
+ private String mPlanName = null;
+
+ @Option(name = TEST_CASES_DIR_OPTION, description =
+ "file path to directory containing CTS test cases")
+ private File mTestCaseDir = null;
+
+ @Option(name = TEST_PLANS_DIR_OPTION, description =
+ "file path to directory containing CTS test plans")
+ private File mTestPlanDir = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public ITestDevice getDevice() {
+ return mDevice;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setDevice(ITestDevice device) {
+ mDevice = device;
+ }
+
+ /**
+ * Set the test plan directory.
+ * <p/>
+ * Exposed for unit testing
+ */
+ void setTestPlanDir(File planDir) {
+ mTestPlanDir = planDir;
+ }
+
+ /**
+ * Set the test case directory.
+ * <p/>
+ * Exposed for unit testing
+ */
+ void setTestCaseDir(File testCaseDir) {
+ mTestCaseDir = testCaseDir;
+ }
+
+ /**
+ * Set the plan name to run.
+ * <p/>
+ * Exposed for unit testing
+ */
+ void setPlanName(String planName) {
+ mPlanName = planName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void run(List<ITestInvocationListener> listeners) throws DeviceNotAvailableException {
+ if (mPlanName == null) {
+ throw new IllegalArgumentException("missing --plan option");
+ }
+ if (getDevice() == null) {
+ throw new IllegalArgumentException("missing device");
+ }
+ if (mTestCaseDir == null) {
+ throw new IllegalArgumentException(String.format("missing %s option",
+ TEST_CASES_DIR_OPTION));
+ }
+ if (mTestPlanDir == null) {
+ throw new IllegalArgumentException(String.format("missing %s", TEST_PLANS_DIR_OPTION));
+ }
+
+ Log.i(LOG_TAG, String.format("Executing CTS test plan %s", mPlanName));
+
+ try {
+ String ctsPlanRelativePath = String.format("%s.xml", mPlanName);
+ File ctsPlanFile = new File(mTestPlanDir, ctsPlanRelativePath);
+ IPlanXmlParser parser = createXmlParser();
+ parser.parse(createXmlStream(ctsPlanFile));
+ Collection<String> testUris = parser.getTestUris();
+ ITestCaseRepo testRepo = createTestCaseRepo();
+ Collection<IRemoteTest> tests = testRepo.getTests(testUris);
+ for (IRemoteTest test : tests) {
+ if (test instanceof IDeviceTest) {
+ ((IDeviceTest)test).setDevice(getDevice());
+ }
+ test.run(listeners);
+ }
+ } 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);
+ }
+ }
+
+ /**
+ * Factory method for creating a {@link ITestCaseRepo}.
+ * <p/>
+ * Exposed for unit testing
+ */
+ ITestCaseRepo createTestCaseRepo() {
+ return new TestCaseRepo(mTestCaseDir);
+ }
+
+ /**
+ * Factory method for creating a {@link PlanXmlParser}.
+ * <p/>
+ * Exposed for unit testing
+ */
+ IPlanXmlParser createXmlParser() {
+ return new PlanXmlParser();
+ }
+
+ /**
+ * 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));
+ }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanXmlParser.java
new file mode 100644
index 0000000..fd3dcb8
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PlanXmlParser.java
@@ -0,0 +1,72 @@
+/*
+ * 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.tradefed.util.xml.AbstractXmlParser;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Parses a test plan xml file.
+ */
+class PlanXmlParser extends AbstractXmlParser implements IPlanXmlParser {
+
+ private Set<String> mUris;
+
+ /**
+ * SAX callback object. Handles parsing data from the xml tags.
+ */
+ private class EntryHandler extends DefaultHandler {
+
+ private static final String ENTRY_TAG = "Entry";
+
+ @Override
+ public void startElement(String uri, String localName, String name, Attributes attributes)
+ throws SAXException {
+ if (ENTRY_TAG.equals(localName)) {
+ final String entryUriValue = attributes.getValue("uri");
+ mUris.add(entryUriValue);
+ }
+ }
+ }
+
+ PlanXmlParser() {
+ // Uses a LinkedHashSet to have predictable iteration order
+ mUris = new LinkedHashSet<String>();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<String> getTestUris() {
+ return mUris;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected DefaultHandler createXmlHandler() {
+ return new EntryHandler();
+ }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestCaseRepo.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestCaseRepo.java
new file mode 100644
index 0000000..a171dc9
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestCaseRepo.java
@@ -0,0 +1,135 @@
+/*
+ * 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.ddmlib.Log;
+import com.android.tradefed.testtype.IRemoteTest;
+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.FilenameFilter;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * Retrieves CTS test case definitions from the repository.
+ */
+class TestCaseRepo implements ITestCaseRepo {
+
+ private static final String LOG_TAG = "TestCaseRepo";
+
+ private File mTestCaseDir;
+
+ /** mapping of uri to test definition */
+ private Map<String, TestPackageDef> mTestMap;
+
+ /**
+ * Creates a {@link TestCaseRepo}, initialized from provided repo files
+ *
+ * @param testCaseDir directory containing all test case definition xml and build files
+ */
+ public TestCaseRepo(File testCaseDir) {
+ mTestCaseDir = testCaseDir;
+ mTestMap = new Hashtable<String, TestPackageDef>();
+ parse(mTestCaseDir);
+ }
+
+ /**
+ * Builds mTestMap based on directory contents
+ */
+ private void parse(File dir) {
+ File[] xmlFiles = dir.listFiles(new XmlFilter());
+ for (File xmlFile : xmlFiles) {
+ parseTestFromXml(xmlFile);
+ }
+ }
+
+ /**
+ * @param xmlFile
+ * @throws ParseException
+ */
+ private void parseTestFromXml(File xmlFile) {
+ TestCaseXmlParser parser = new TestCaseXmlParser();
+ try {
+ parser.parse(createStreamFromFile(xmlFile));
+ TestPackageDef def = parser.getTestPackageDef();
+ if (def != null) {
+ mTestMap.put(def.getUri(), def);
+ } else {
+ Log.w(LOG_TAG, String.format("Could not find test package info in xml file %s",
+ xmlFile.getAbsolutePath()));
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(LOG_TAG, String.format("Could not find test case xml file %s",
+ xmlFile.getAbsolutePath()));
+ Log.e(LOG_TAG, e);
+ } catch (ParseException e) {
+ Log.e(LOG_TAG, String.format("Failed to parse test case xml file %s",
+ xmlFile.getAbsolutePath()));
+ Log.e(LOG_TAG, e);
+ }
+ }
+
+ /**
+ * Helper method to create a stream to read data from given file
+ * <p/>
+ * Exposed for unit testing
+ *
+ * @param xmlFile
+ * @return
+ *
+ */
+ InputStream createStreamFromFile(File xmlFile) throws FileNotFoundException {
+ return new BufferedInputStream(new FileInputStream(xmlFile));
+ }
+
+ private static class XmlFilter implements FilenameFilter {
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".xml");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<IRemoteTest> getTests(Collection<String> testUris) {
+ Collection<IRemoteTest> tests = new ArrayList<IRemoteTest>(testUris.size());
+ for (String uri : testUris) {
+ TestPackageDef def = mTestMap.get(uri);
+ if (def != null) {
+ IRemoteTest test = def.createTest(mTestCaseDir);
+ if (test != null) {
+ tests.add(test);
+ } else {
+ Log.w(LOG_TAG, String.format("Failed to create test from package uri %s", uri));
+ }
+ } else {
+ Log.w(LOG_TAG, String.format("Could not find test with uri %s", uri));
+ }
+ }
+ return tests;
+ }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestCaseXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestCaseXmlParser.java
new file mode 100644
index 0000000..636d8c1
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestCaseXmlParser.java
@@ -0,0 +1,91 @@
+/*
+ * 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.tradefed.util.xml.AbstractXmlParser;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Parser for CTS test case XML.
+ * <p/>
+ * Dumb parser that just retrieves data from in the test case xml and stuff it into a
+ * {@link TestPackageDef}. Currently performs limited error checking.
+ */
+public class TestCaseXmlParser extends AbstractXmlParser {
+
+ private TestPackageDef mDef;
+
+ /**
+ * SAX callback object. Handles parsing 'TestPackage' data from the xml tags.
+ */
+ private class TestPackageHandler extends DefaultHandler {
+
+ private static final String TEST_PACKAGE_TAG = "TestPackage";
+
+ @Override
+ public void startElement(String uri, String localName, String name, Attributes attributes)
+ throws SAXException {
+ if (TEST_PACKAGE_TAG.equals(localName)) {
+ // appPackageName is used as the uri
+ final String entryUriValue = attributes.getValue("appPackageName");
+ final String testPackageNameSpace = attributes.getValue("appNameSpace");
+ final String packageName = attributes.getValue("name");
+ final String runnerName = attributes.getValue("runner");
+ final String hostSideTest = attributes.getValue("hostSideOnly");
+ final String jarPath = attributes.getValue("jarPath");
+ final String signatureCheck = attributes.getValue("signatureCheck");
+ final String referenceApp = attributes.getValue("referenceAppTest");
+
+ mDef = new TestPackageDef();
+ mDef.setUri(entryUriValue);
+ mDef.setAppNameSpace(testPackageNameSpace);
+ mDef.setName(packageName);
+ mDef.setRunner(runnerName);
+ mDef.setIsHostSideTest(parseBoolean(hostSideTest));
+ mDef.setJarPath(jarPath);
+ mDef.setIsSignatureCheck(parseBoolean(signatureCheck));
+ mDef.setIsReferenceApp(parseBoolean(referenceApp));
+ }
+ }
+
+ /**
+ * Parse a boolean attribute value
+] */
+ private boolean parseBoolean(final String stringValue) {
+ return stringValue != null &&
+ Boolean.parseBoolean(stringValue);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected DefaultHandler createXmlHandler() {
+ return new TestPackageHandler();
+ }
+
+ /**
+ * @returns the {@link TestPackageDef} containing data parsed from xml or <code>null</code> if
+ * xml did not contain the correct information.
+ */
+ public TestPackageDef getTestPackageDef() {
+ return mDef;
+ }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
new file mode 100644
index 0000000..879a5f9
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
@@ -0,0 +1,150 @@
+/*
+ * 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.ddmlib.Log;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.testtype.InstrumentationTest;
+
+import java.io.File;
+
+/**
+ * Container for CTS test info.
+ * <p/>
+ * Knows how to translate this info into a runnable {@link IRemoteTest}.
+ */
+public class TestPackageDef {
+
+ private static final String LOG_TAG = "TestPackageDef";
+
+ private String mUri = null;
+ private String mAppNameSpace = null;
+ private String mName = null;
+ private String mRunner = null;
+ private boolean mIsHostSideTest = false;
+ private String mJarPath = null;
+ private boolean mIsSignatureTest = false;
+ private boolean mIsReferenceAppTest = false;
+
+ void setUri(String uri) {
+ mUri = uri;
+ }
+
+ /**
+ * Get the unique URI of the test package.
+ * @return the {@link String} uri
+ */
+ public String getUri() {
+ return mUri;
+ }
+
+ void setAppNameSpace(String appNameSpace) {
+ mAppNameSpace = appNameSpace;
+ }
+
+ String getAppNameSpace() {
+ return mAppNameSpace;
+ }
+
+ void setName(String name) {
+ mName = name;
+ }
+
+ String getName() {
+ return mName;
+ }
+
+ void setRunner(String runnerName) {
+ mRunner = runnerName;
+ }
+
+ String getRunner() {
+ return mRunner;
+ }
+
+ void setIsHostSideTest(boolean hostSideTest) {
+ mIsHostSideTest = hostSideTest;
+
+ }
+
+ boolean isHostSideTest() {
+ return mIsHostSideTest;
+ }
+
+ void setJarPath(String jarPath) {
+ mJarPath = jarPath;
+ }
+
+ String getJarPath() {
+ return mJarPath;
+ }
+
+ void setIsSignatureCheck(boolean isSignatureCheckTest) {
+ mIsSignatureTest = isSignatureCheckTest;
+ }
+
+ boolean isSignatureCheck() {
+ return mIsSignatureTest;
+ }
+
+ void setIsReferenceApp(boolean isReferenceApp) {
+ mIsReferenceAppTest = isReferenceApp;
+ }
+
+ boolean isReferenceApp() {
+ return mIsReferenceAppTest;
+ }
+
+ /**
+ * Creates a runnable {@link IRemoteTest} from info stored in this definition.
+ *
+ * @param testCaseDir {@link File} representing directory of test case data
+ * @return a {@link IRemoteTest} with all necessary data populated to run the test or
+ * <code>null</code> if test could not be created
+ */
+ public IRemoteTest createTest(File testCaseDir) {
+ if (mIsHostSideTest) {
+ // TODO: implement this
+ Log.w(LOG_TAG, String.format("Skipping currently unsupported host side test %s",
+ mName));
+ return null;
+ } else if (mIsSignatureTest) {
+ // TODO: implement this
+ Log.w(LOG_TAG, String.format("Skipping currently unsupported signature test %s",
+ mName));
+ return null;
+ } else if (mIsReferenceAppTest) {
+ // TODO: implement this
+ Log.w(LOG_TAG, String.format("Skipping currently unsupported reference app test %s",
+ mName));
+ return null;
+ } else {
+ Log.d(LOG_TAG, String.format("Creating instrumentation test for %s", mName));
+ InstrumentationTest instrTest = new InstrumentationTest();
+ instrTest.setPackageName(mAppNameSpace);
+ instrTest.setRunnerName(mRunner);
+ // mName means 'apk file name' for instrumentation tests
+ File apkFile = new File(testCaseDir, String.format("%s.apk", mName));
+ if (!apkFile.exists()) {
+ Log.w(LOG_TAG, String.format("Could not find apk file %s",
+ apkFile.getAbsolutePath()));
+ return null;
+ }
+ instrTest.setInstallFile(apkFile);
+ return instrTest;
+ }
+ }
+}
diff --git a/tools/tradefed-host/tests/.classpath b/tools/tradefed-host/tests/.classpath
index d14e02b..1fefb7b 100644
--- a/tools/tradefed-host/tests/.classpath
+++ b/tools/tradefed-host/tests/.classpath
@@ -5,7 +5,7 @@
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry combineaccessrules="false" kind="src" path="/cts-tradefed-host"/>
<classpathentry combineaccessrules="false" kind="src" path="/easymock"/>
- <classpathentry combineaccessrules="false" kind="src" path="/ddmlib-prebuilt"/>
- <classpathentry combineaccessrules="false" kind="src" path="/tradefed-prebuilt"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/tradefederation"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetsetup/StubCtsBuildHelper.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetsetup/StubCtsBuildHelper.java
index f17bd5a..53a79be 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetsetup/StubCtsBuildHelper.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetsetup/StubCtsBuildHelper.java
@@ -43,7 +43,12 @@
}
@Override
- public File getTestPlan(String testPlanFileName) throws FileNotFoundException {
+ public File getTestPlansDir() throws FileNotFoundException {
+ return new File("tmp");
+ }
+
+ @Override
+ public File getTestCasesDir() {
return new File("tmp");
}
}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanTestTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanTestTest.java
new file mode 100644
index 0000000..1c2b4b6
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanTestTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+
+import org.easymock.EasyMock;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link PlanTest}.
+ */
+public class PlanTestTest extends TestCase {
+
+ /** the test fixture under test, with all external dependencies mocked out */
+ private PlanTest mPlanTest;
+ private ITestCaseRepo mMockRepo;
+ private IPlanXmlParser mMockPlanParser;
+ private ITestDevice mMockDevice;
+ private ITestInvocationListener mMockListener;
+
+ private static final String PLAN_NAME = "CTS";
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mMockRepo = EasyMock.createMock(ITestCaseRepo.class);
+ mMockPlanParser = EasyMock.createMock(IPlanXmlParser.class);
+ mMockDevice = EasyMock.createMock(ITestDevice.class);
+ mMockListener = EasyMock.createNiceMock(ITestInvocationListener.class);
+
+ mPlanTest = new PlanTest() {
+ @Override
+ ITestCaseRepo createTestCaseRepo() {
+ return mMockRepo;
+ }
+
+ @Override
+ IPlanXmlParser createXmlParser() {
+ return mMockPlanParser;
+ }
+
+ @Override
+ InputStream createXmlStream(File xmlFile) throws FileNotFoundException {
+ // return empty stream, not used
+ return new ByteArrayInputStream(new byte[0]);
+ }
+ };
+ mPlanTest.setDevice(mMockDevice);
+ // not used, but needs to be non-null
+ mPlanTest.setTestCaseDir(new File("tmp"));
+ mPlanTest.setTestPlanDir(new File("tmp"));
+ mPlanTest.setPlanName(PLAN_NAME);
+ }
+
+ /**
+ * Test normal case {@link PlanTest#run(java.util.List)}.
+ * <p/>
+ * Not that interesting of a test in its current form, but sets the stage for testing more
+ * complicated scenarios.
+ */
+ @SuppressWarnings("unchecked")
+ public void testRun() throws DeviceNotAvailableException, ParseException {
+ // expect
+ mMockPlanParser.parse((InputStream)EasyMock.anyObject());
+ Collection<String> uris = new ArrayList<String>(1);
+ uris.add("test-uri");
+ EasyMock.expect(mMockPlanParser.getTestUris()).andReturn(uris);
+
+ IRemoteTest mockTest = EasyMock.createMock(IRemoteTest.class);
+ Collection<IRemoteTest> tests = new ArrayList<IRemoteTest>(1);
+ tests.add(mockTest);
+ EasyMock.expect(mMockRepo.getTests(uris)).andReturn(tests);
+
+ // expect
+ mockTest.run((List<ITestInvocationListener>)EasyMock.anyObject());
+
+ replayMocks();
+ EasyMock.replay(mockTest);
+ mPlanTest.run(mMockListener);
+ verifyMocks();
+ }
+
+ private void replayMocks() {
+ EasyMock.replay(mMockRepo, mMockPlanParser, mMockDevice, mMockListener);
+ }
+
+ private void verifyMocks() {
+ EasyMock.verify(mMockRepo, mMockPlanParser, mMockDevice, mMockListener);
+ }
+}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanXmlParserTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanXmlParserTest.java
new file mode 100644
index 0000000..803b230
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/PlanXmlParserTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.tradefed.util.xml.AbstractXmlParser.ParseException;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link PlanXmlParser}.
+ */
+public class PlanXmlParserTest extends TestCase {
+
+ private static final String TEST_URI1 = "foo";
+ private static final String TEST_URI2 = "foo2";
+
+ static final String TEST_DATA =
+ "<TestPlan version=\"1.0\">" +
+ String.format("<Entry uri=\"%s\" />", TEST_URI1) +
+ String.format("<Entry uri=\"%s\" />", TEST_URI2) +
+ "</TestPlan>";
+
+ /**
+ * Simple test for parsing a plan containing two uris
+ */
+ public void testParse() throws ParseException {
+ PlanXmlParser parser = new PlanXmlParser();
+ parser.parse(getStringAsStream(TEST_DATA));
+ assertEquals(2, parser.getTestUris().size());
+ Iterator<String> iter = parser.getTestUris().iterator();
+ // assert uris in order
+ assertEquals(TEST_URI1, iter.next());
+ assertEquals(TEST_URI2, iter.next());
+ }
+
+ private InputStream getStringAsStream(String input) {
+ return new ByteArrayInputStream(input.getBytes());
+ }
+}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestCaseXmlParserTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestCaseXmlParserTest.java
new file mode 100644
index 0000000..5b96dbb
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestCaseXmlParserTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.tradefed.util.xml.AbstractXmlParser.ParseException;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link TestCaseXmlParser}.
+ */
+public class TestCaseXmlParserTest extends TestCase {
+
+ private static String INSTR_TEST_DATA =
+ "<TestPackage AndroidFramework=\"Android 1.0\" appNameSpace=\"com.android.cts.gesture\" " +
+ "appPackageName=\"android.gesture\" name=\"CtsGestureTestCases\" " +
+ "runner=\"android.test.InstrumentationTestRunner\" version=\"1.0\">" +
+ "</TestPackage>";
+
+ private static String HOST_TEST_DATA =
+ "<TestPackage hostSideOnly=\"true\" >" +
+ "</TestPackage>";
+
+ private static String BAD_HOST_TEST_DATA =
+ "<TestPackage hostSideOnly=\"blah\" >" +
+ "</TestPackage>";
+
+ private static String NO_TEST_DATA =
+ "<invalid />";
+
+ /**
+ * Test parsing test case xml containing an instrumentation test definition.
+ */
+ public void testParse_instrPackage() throws ParseException {
+ TestCaseXmlParser parser = new TestCaseXmlParser();
+ parser.parse(getStringAsStream(INSTR_TEST_DATA));
+ TestPackageDef def = parser.getTestPackageDef();
+ assertEquals("com.android.cts.gesture", def.getAppNameSpace());
+ assertEquals("android.gesture", def.getUri());
+ assertEquals("android.test.InstrumentationTestRunner", def.getRunner());
+ }
+
+ /**
+ * Test parsing test case xml containing an host test attribute.
+ */
+ public void testParse_hostTest() throws ParseException {
+ TestCaseXmlParser parser = new TestCaseXmlParser();
+ parser.parse(getStringAsStream(HOST_TEST_DATA));
+ TestPackageDef def = parser.getTestPackageDef();
+ assertTrue(def.isHostSideTest());
+ }
+
+ /**
+ * Test parsing test case xml containing an invalid host test attribute.
+ */
+ public void testParse_badHostTest() throws ParseException {
+ TestCaseXmlParser parser = new TestCaseXmlParser();
+ parser.parse(getStringAsStream(BAD_HOST_TEST_DATA));
+ TestPackageDef def = parser.getTestPackageDef();
+ assertFalse(def.isHostSideTest());
+ }
+
+ /**
+ * Test parsing a test case xml with no test package data.
+ */
+ public void testParse_noData() throws ParseException {
+ TestCaseXmlParser parser = new TestCaseXmlParser();
+ parser.parse(getStringAsStream(NO_TEST_DATA));
+ assertNull(parser.getTestPackageDef());
+ }
+
+ private InputStream getStringAsStream(String input) {
+ return new ByteArrayInputStream(input.getBytes());
+ }
+}