am a5422006: Merge "Support Native Tests in CTS" into ics-mr1
* commit 'a542200653601eccd20b0382162267cd91673bfc':
Support Native Tests in CTS
diff --git a/Android.mk b/Android.mk
index e3f9ba8..159bda0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,6 +14,6 @@
# limitations under the License.
#
+include cts/CtsNativeTestCase.mk
include cts/CtsTestCoverage.mk
-
include $(call all-subdir-makefiles)
diff --git a/CtsNativeTestCase.mk b/CtsNativeTestCase.mk
new file mode 100644
index 0000000..a648fa0
--- /dev/null
+++ b/CtsNativeTestCase.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2011 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.
+
+# Include this file to gain access to functions to build native CTS
+# test packages. Replace "include $(BUILD_EXECUTABLE)" with
+# "include $(BUILD_CTS_EXECUTABLE)".
+
+LOCAL_PATH := $(call my-dir)
+BUILD_CTS_EXECUTABLE := $(LOCAL_PATH)/tools/build/test_executable.mk
+
+CTS_NATIVE_XML_OUT := $(HOST_OUT)/cts-native-xml
+
+CTS_NATIVE_XML_GENERATOR := $(HOST_OUT_EXECUTABLES)/cts-native-xml-generator
+
+define cts-get-native-paths
+ $(foreach exe,$(1),$(call intermediates-dir-for,EXECUTABLES,$(1))/$(1))
+endef
+
+define cts-get-native-xmls
+ $(foreach exe,$(1),$(CTS_NATIVE_XML_OUT)/$(exe).xml)
+endef
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 3923aac..060e824 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -75,5 +75,12 @@
$(CTS_COVERAGE_TEST_CASE_LIST) \
$(CTS_SECURITY_APPS_LIST)
+CTS_NATIVE_EXES := \
+ CtsNativeMediaTestCases
+
+CTS_TEST_CASES := $(call cts-get-native-paths,$(CTS_NATIVE_EXES))
+
+CTS_TEST_XMLS := $(call cts-get-native-xmls,$(CTS_NATIVE_EXES))
+
# The following files will be placed in the tools directory of the CTS distribution
CTS_TOOLS_LIST :=
diff --git a/development/ide/eclipse/.classpath b/development/ide/eclipse/.classpath
index a38f53a..30c63d8 100644
--- a/development/ide/eclipse/.classpath
+++ b/development/ide/eclipse/.classpath
@@ -58,6 +58,7 @@
<classpathentry kind="src" path="cts/tests/tests/webkit/src"/>
<classpathentry kind="src" path="cts/tests/tests/widget/src"/>
<classpathentry kind="src" path="cts/tools/cts-api-coverage/src"/>
+ <classpathentry kind="src" path="cts/tools/cts-native-xml-generator/src"/>
<classpathentry kind="src" path="cts/tools/cts-reference-app-lib/src"/>
<classpathentry kind="src" path="cts/tools/dasm/src"/>
<classpathentry kind="src" path="cts/tools/device-setup/TestDeviceSetup/src"/>
diff --git a/tests/tests/nativemedia/Android.mk b/tests/tests/nativemedia/Android.mk
index 928ebcd..94bc83e 100644
--- a/tests/tests/nativemedia/Android.mk
+++ b/tests/tests/nativemedia/Android.mk
@@ -3,7 +3,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
LOCAL_C_INCLUDES:= \
bionic \
@@ -27,8 +27,9 @@
LOCAL_CFLAGS += -DXP_UNIX
-LOCAL_MODULE:= NativeMediaTest
+LOCAL_MODULE := CtsNativeMediaTestCases
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
-include $(BUILD_EXECUTABLE)
\ No newline at end of file
+LOCAL_CTS_TEST_PACKAGE := android.nativemedia
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/tools/build/test_executable.mk b/tools/build/test_executable.mk
new file mode 100644
index 0000000..bfd5bee
--- /dev/null
+++ b/tools/build/test_executable.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2011 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.
+
+#
+# Builds an executable and defines a rule to generate the associated test
+# package XML needed by CTS.
+#
+# 1. Replace "include $(BUILD_EXECUTABLE)"
+# with "include $(BUILD_CTS_EXECUTABLE)"
+#
+# 2. Define LOCAL_CTS_TEST_PACKAGE to group the tests under a package
+# as needed by CTS.
+#
+
+include $(BUILD_EXECUTABLE)
+
+cts_executable_xml := $(CTS_NATIVE_XML_OUT)/$(LOCAL_MODULE).xml
+
+$(cts_executable_xml): PRIVATE_PATH := $(LOCAL_PATH)
+$(cts_executable_xml): PRIVATE_TEST_PACKAGE := $(LOCAL_CTS_TEST_PACKAGE)
+$(cts_executable_xml): PRIVATE_EXECUTABLE := $(LOCAL_MODULE)
+$(cts_executable_xml): $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES)) | $(CTS_NATIVE_XML_GENERATOR)
+ $(hide) echo Generating test description for native package $(PRIVATE_TEST_PACKAGE)
+ $(hide) $(CTS_NATIVE_XML_GENERATOR) -p $(PRIVATE_TEST_PACKAGE) \
+ -n $(PRIVATE_EXECUTABLE) \
+ -s $(PRIVATE_PATH) \
+ -o $@
diff --git a/tools/cts-native-xml-generator/Android.mk b/tools/cts-native-xml-generator/Android.mk
new file mode 100644
index 0000000..c02d745
--- /dev/null
+++ b/tools/cts-native-xml-generator/Android.mk
@@ -0,0 +1,43 @@
+# Copyright (C) 2011 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# We use copy-file-to-new-target so that the installed
+# script file's timestamp is at least as new as the
+# .jar file it wraps.
+
+# the hat script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := cts-native-xml-generator
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/$(LOCAL_MODULE)$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/$(LOCAL_MODULE) | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
+
+# the other stuff
+# ============================================================
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+ src \
+ ))
+
+include $(subdirs)
diff --git a/tools/cts-native-xml-generator/etc/cts-native-xml-generator b/tools/cts-native-xml-generator/etc/cts-native-xml-generator
new file mode 100644
index 0000000..cae5f4a
--- /dev/null
+++ b/tools/cts-native-xml-generator/etc/cts-native-xml-generator
@@ -0,0 +1,46 @@
+#!/bin/bash
+#
+# Copyright (C) 2011, 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+javaOpts=""
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+exec java $javaOpts -jar $libdir/cts-native-xml-generator.jar "$@"
diff --git a/tools/cts-native-xml-generator/src/Android.mk b/tools/cts-native-xml-generator/src/Android.mk
new file mode 100644
index 0000000..70b9f49
--- /dev/null
+++ b/tools/cts-native-xml-generator/src/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2011 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.
+
+LOCAL_PATH := $(call my-dir)
+
+
+# cts-native-xml-generator java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAVA_RESOURCE_DIRS := res
+LOCAL_JAR_MANIFEST := MANIFEST.mf
+
+LOCAL_MODULE := cts-native-xml-generator
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/cts-native-xml-generator/src/MANIFEST.mf b/tools/cts-native-xml-generator/src/MANIFEST.mf
new file mode 100644
index 0000000..b8bde72
--- /dev/null
+++ b/tools/cts-native-xml-generator/src/MANIFEST.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Main-Class: com.android.cts.nativexml.CtsNativeXmlGenerator
diff --git a/tools/cts-native-xml-generator/src/com/android/cts/nativexml/CtsNativeXmlGenerator.java b/tools/cts-native-xml-generator/src/com/android/cts/nativexml/CtsNativeXmlGenerator.java
new file mode 100644
index 0000000..1a436dd
--- /dev/null
+++ b/tools/cts-native-xml-generator/src/com/android/cts/nativexml/CtsNativeXmlGenerator.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011 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.nativexml;
+
+import java.io.File;
+import java.util.Arrays;
+
+/**
+ * Class that searches a source directory for native gTests and outputs a
+ * test package xml.
+ */
+public class CtsNativeXmlGenerator {
+
+ private static void usage(String[] args) {
+ System.err.println("Arguments: " + Arrays.asList(args));
+ System.err.println("Usage: cts-native-xml-generator -p PACKAGE_NAME -n EXECUTABLE_NAME "
+ + "-s SOURCE_DIR [-o OUTPUT_FILE]");
+ System.exit(1);
+ }
+
+ public static void main(String[] args) throws Exception {
+ String appPackageName = null;
+ String name = null;
+ File sourceDir = null;
+ String outputPath = null;
+
+ for (int i = 0; i < args.length; i++) {
+ if ("-p".equals(args[i])) {
+ if (i + 1 < args.length) {
+ appPackageName = args[++i];
+ } else {
+ System.err.println("Missing value for test package");
+ usage(args);
+ }
+ } else if ("-n".equals(args[i])) {
+ if (i + 1 < args.length) {
+ name = args[++i];
+ } else {
+ System.err.println("Missing value for executable name");
+ usage(args);
+ }
+ } else if ("-o".equals(args[i])) {
+ if (i + 1 < args.length) {
+ outputPath = args[++i];
+ } else {
+ System.err.println("Missing value for output file");
+ usage(args);
+ }
+ } else if ("-s".equals(args[i])) {
+ if (i + 1 < args.length) {
+ sourceDir = new File(args[++i]);
+ } else {
+ System.err.println("Missing value for source directory");
+ usage(args);
+ }
+ } else {
+ System.err.println("Unsupported flag: " + args[i]);
+ usage(args);
+ }
+ }
+
+ if (appPackageName == null) {
+ System.out.println("Package name is required");
+ usage(args);
+ } else if (name == null) {
+ System.out.println("Executable name is required");
+ usage(args);
+ } else if (sourceDir == null) {
+ System.out.println("Source directory is required");
+ usage(args);
+ }
+
+ Generator generator = new Generator(appPackageName, name, sourceDir, outputPath);
+ generator.writePackageXml();
+ }
+}
diff --git a/tools/cts-native-xml-generator/src/com/android/cts/nativexml/Generator.java b/tools/cts-native-xml-generator/src/com/android/cts/nativexml/Generator.java
new file mode 100644
index 0000000..3a75e49
--- /dev/null
+++ b/tools/cts-native-xml-generator/src/com/android/cts/nativexml/Generator.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2011 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.nativexml;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Scanner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Generator of TestPackage XML files for native gTests.
+ *
+ * It scours all the C++ source files in a given source directory looking
+ * for test declarations and outputs a XML test listing.
+ */
+class Generator {
+
+ /** Test package name like "android.nativemedia" to group the tests. */
+ private final String mAppPackageName;
+
+ /** Name of the native executable. */
+ private final String mName;
+
+ /** Directory to recursively scan for gTest test declarations. */
+ private final File mSourceDir;
+
+ /** Path to output file or null to just dump to standard out. */
+ private final String mOutputPath;
+
+ Generator(String appPackageName, String name, File sourceDir, String outputPath) {
+ mAppPackageName = appPackageName;
+ mName = name;
+ mSourceDir = sourceDir;
+ mOutputPath = outputPath;
+ }
+
+ public void writePackageXml() throws IOException {
+ OutputStream output = System.out;
+ if (mOutputPath != null) {
+ File outputFile = new File(mOutputPath);
+ File outputDir = outputFile.getParentFile();
+ if (!outputDir.exists() && !outputDir.mkdirs()) {
+ System.err.println("Couldn't make output directory: " + mOutputPath);
+ System.exit(1);
+ }
+ output = new FileOutputStream(outputFile);
+ }
+
+ PrintWriter writer = null;
+ try {
+ writer = new PrintWriter(output);
+ writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ writeTestPackage(writer);
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ }
+
+ private void writeTestPackage(PrintWriter writer) throws FileNotFoundException {
+ writer.append("<TestPackage appPackageName=\"")
+ .append(mAppPackageName)
+ .append("\" name=\"")
+ .append(mName)
+ .println("\" testType=\"native\" version=\"1.0\">");
+ writeTestSuite(writer);
+ writer.println("</TestPackage>");
+ }
+
+ private void writeTestSuite(PrintWriter writer) throws FileNotFoundException {
+ /*
+ * Given "android.foo.bar.baz"...
+ *
+ * <TestSuite name="android">
+ * <TestSuite name="foo">
+ * <TestSuite name="bar">
+ * <TestSuite name="baz">
+ */
+ Scanner scanner = null;
+ try {
+ scanner = new Scanner(mAppPackageName);
+ scanner.useDelimiter("\\.");
+
+ int numLevels = 0;
+ for (; scanner.hasNext(); numLevels++) {
+ String packagePart = scanner.next();
+ writer.append("<TestSuite name=\"").append(packagePart).println("\">");
+ }
+
+ writeTestCases(writer, mSourceDir);
+
+ for (; numLevels > 0; numLevels--) {
+ writer.println("</TestSuite>");
+ }
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+ }
+
+ private void writeTestCases(PrintWriter writer, File dir) throws FileNotFoundException {
+ // Find both C++ files to find tests and directories to look for more tests!
+ File[] files = dir.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String filename) {
+ return filename.endsWith(".cpp") || new File(dir, filename).isDirectory();
+ }
+ });
+
+ for (int i = 0; i < files.length; i++) {
+ File file = files[i];
+ if (file.isDirectory()) {
+ writeTestCases(writer, file);
+ } else {
+ // Take the test name from the name of the file. It's probably
+ // more accurate to take the name from inside the file...
+ String fileName = file.getName();
+ int extension = fileName.lastIndexOf('.');
+ if (extension != -1) {
+ fileName = fileName.substring(0, extension);
+ }
+
+ writer.append("<TestCase name=\"").append(fileName).println("\">");
+ writeTests(writer, file);
+ writer.println("</TestCase>");
+ }
+ }
+ }
+
+ // We want to find lines like TEST_F(SLObjectCreationTest, testAudioPlayerFromFdCreation) { ...
+ // and extract the "testAudioPlayerFromFdCreation" as group #1
+ private static final Pattern TEST_REGEX = Pattern.compile("\\s*TEST_F\\(\\w+,\\s*(\\w+)\\).*");
+
+ private void writeTests(PrintWriter writer, File file) throws FileNotFoundException {
+ Scanner scanner = null;
+ try {
+ scanner = new Scanner(file);
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ Matcher matcher = TEST_REGEX.matcher(line);
+ if (matcher.matches()) {
+ String name = matcher.group(1);
+ writer.append("<Test name=\"").append(name).println("\" />");
+ }
+ }
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+ }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java
new file mode 100644
index 0000000..5cfafc6
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 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.ddmlib.testrunner.ITestRunListener;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+
+import java.io.File;
+
+/**
+ * Test runner for native gTests.
+ *
+ * TODO: This is similar to Tradefed's existing GTest, but it doesn't confirm
+ * each directory segment exists using ddmlib's file service. This was
+ * a problem since /data is not visible on a user build, but it is
+ * executable. It's also a lot more verbose when it comes to errors.
+ */
+public class GeeTest implements IBuildReceiver, IDeviceTest, IRemoteTest {
+
+ private static final String NATIVE_TESTS_DIRECTORY = "/data/local/tmp/cts-native-tests";
+
+ private int mMaxTestTimeMs = 1 * 60 * 1000;
+
+ private CtsBuildHelper mCtsBuild;
+ private ITestDevice mDevice;
+
+ private final String mPackageName;
+ private final String mExeName;
+
+ public GeeTest(String packageName, String exeName) {
+ mPackageName = packageName;
+ mExeName = exeName;
+ }
+
+ @Override
+ public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+ if (installTest()) {
+ runTest(listener);
+ } else {
+ CLog.e("Failed to install native tests");
+ }
+ }
+
+ private boolean installTest() throws DeviceNotAvailableException {
+ if (!createRemoteDir(NATIVE_TESTS_DIRECTORY)) {
+ CLog.e("Could not create directory for native tests: " + NATIVE_TESTS_DIRECTORY);
+ return false;
+ }
+
+ File nativeExe = new File(mCtsBuild.getTestCasesDir(), mExeName);
+ if (!nativeExe.exists()) {
+ CLog.e("Native test not found: " + nativeExe);
+ return false;
+ }
+
+ File devicePath = new File(NATIVE_TESTS_DIRECTORY, mExeName);
+ if (!mDevice.pushFile(nativeExe, devicePath.toString())) {
+ CLog.e("Failed to push native test to device");
+ return false;
+ }
+ return true;
+ }
+
+ private boolean createRemoteDir(String remoteFilePath) throws DeviceNotAvailableException {
+ if (mDevice.doesFileExist(remoteFilePath)) {
+ return true;
+ }
+ File remoteFile = new File(remoteFilePath);
+ String parentPath = remoteFile.getParent();
+ if (parentPath != null) {
+ if (!createRemoteDir(parentPath)) {
+ return false;
+ }
+ }
+ mDevice.executeShellCommand(String.format("mkdir %s", remoteFilePath));
+ return mDevice.doesFileExist(remoteFilePath);
+ }
+
+ void runTest(ITestRunListener listener) throws DeviceNotAvailableException {
+ GeeTestResultParser resultParser = new GeeTestResultParser(mPackageName, listener);
+ resultParser.setFakePackagePrefix(mPackageName + ".");
+
+ String fullPath = NATIVE_TESTS_DIRECTORY + File.separator + mExeName;
+ String flags = "";
+ CLog.v("Running gtest %s %s on %s", fullPath, flags, mDevice.getSerialNumber());
+ // force file to be executable
+ CLog.v("%s", mDevice.executeShellCommand(String.format("chmod 755 %s", fullPath)));
+
+ try {
+ mDevice.executeShellCommand(String.format("%s %s", fullPath, flags), resultParser,
+ mMaxTestTimeMs /* maxTimeToShellOutputResponse */,
+ 0 /* retryAttempts */);
+ } catch (DeviceNotAvailableException e) {
+ resultParser.flush();
+ throw e;
+ } catch (RuntimeException e) {
+ resultParser.flush();
+ throw e;
+ }
+ }
+
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+ }
+
+ @Override
+ public void setDevice(ITestDevice device) {
+ mDevice = device;
+ }
+
+ @Override
+ public ITestDevice getDevice() {
+ return mDevice;
+ }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTestResultParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTestResultParser.java
new file mode 100644
index 0000000..c01da20
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTestResultParser.java
@@ -0,0 +1,670 @@
+/*
+ * Copyright (C) 2011 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.IShellOutputReceiver;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.MultiLineReceiver;
+import com.android.ddmlib.testrunner.ITestRunListener;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.testtype.testdefs.XmlDefsTest;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * TODO: Merge change to add package prefix to tradefed's GTestResultParser.
+ * Otherwise this file should be the same as TradeFed's version.
+ *
+ * Parses the 'raw output mode' results of native tests using GTest that run from shell, and informs
+ * a ITestRunListener of the results.
+ * <p>Sample format of output expected:
+ *
+ * <pre>
+ * [==========] Running 15 tests from 1 test case.
+ * [----------] Global test environment set-up.
+ * [----------] 15 tests from MessageTest
+ * [ RUN ] MessageTest.DefaultConstructor
+ * [ OK ] MessageTest.DefaultConstructor (1 ms)
+ * [ RUN ] MessageTest.CopyConstructor
+ * external/gtest/test/gtest-message_test.cc:67: Failure
+ * Value of: 5
+ * Expected: 2
+ * external/gtest/test/gtest-message_test.cc:68: Failure
+ * Value of: 1 == 1
+ * Actual: true
+ * Expected: false
+ * [ FAILED ] MessageTest.CopyConstructor (2 ms)
+ * ...
+ * [ RUN ] MessageTest.DoesNotTakeUpMuchStackSpace
+ * [ OK ] MessageTest.DoesNotTakeUpMuchStackSpace (0 ms)
+ * [----------] 15 tests from MessageTest (26 ms total)
+ *
+ * [----------] Global test environment tear-down
+ * [==========] 15 tests from 1 test case ran. (26 ms total)
+ * [ PASSED ] 6 tests.
+ * [ FAILED ] 9 tests, listed below:
+ * [ FAILED ] MessageTest.CopyConstructor
+ * [ FAILED ] MessageTest.ConstructsFromCString
+ * [ FAILED ] MessageTest.StreamsCString
+ * [ FAILED ] MessageTest.StreamsNullCString
+ * [ FAILED ] MessageTest.StreamsString
+ * [ FAILED ] MessageTest.StreamsStringWithEmbeddedNUL
+ * [ FAILED ] MessageTest.StreamsNULChar
+ * [ FAILED ] MessageTest.StreamsInt
+ * [ FAILED ] MessageTest.StreamsBasicIoManip
+ * 9 FAILED TESTS
+ * </pre>
+ *
+ * <p>where the following tags are used to signal certain events:
+ * <pre>
+ * [==========]: the first occurrence indicates a new run started, including the number of tests
+ * to be expected in this run
+ * [ RUN ]: indicates a new test has started to run; a series of zero or more lines may
+ * follow a test start, and will be captured in case of a test failure or error
+ * [ OK ]: the preceding test has completed successfully, optionally including the time it
+ * took to run (in ms)
+ * [ FAILED ]: the preceding test has failed, optionally including the time it took to run (in ms)
+ * [==========]: the preceding test run has completed, optionally including the time it took to run
+ * (in ms)
+ * </pre>
+ *
+ * All other lines are ignored.
+ */
+public class GeeTestResultParser extends MultiLineReceiver {
+ private static final String LOG_TAG = "GTestResultParser";
+
+ // Variables to keep track of state
+ private TestResult mCurrentTestResult = null;
+ private int mNumTestsRun = 0;
+ private int mNumTestsExpected = 0;
+ private long mTotalRunTime = 0;
+ private boolean mTestInProgress = false;
+ private boolean mTestRunInProgress = false;
+ private final String mTestRunName;
+ private final Collection<ITestRunListener> mTestListeners;
+
+ /** Fake adding a package prefix if the test listener needs it. */
+ private String mFakePackagePrefix = "";
+
+ /** True if start of test has already been reported to listener. */
+ private boolean mTestRunStartReported = false;
+
+ /** True if current test run has been canceled by user. */
+ private boolean mIsCancelled = false;
+
+ private String mCoverageTarget = null;
+
+ /**
+ * Test result data
+ */
+ private static class TestResult {
+ private String mTestName = null;
+ private String mTestClass = null;
+ private StringBuilder mStackTrace = null;
+ @SuppressWarnings("unused")
+ private Long mRunTime = null;
+
+ /** Returns whether expected values have been parsed
+ *
+ * @return true if all expected values have been parsed
+ */
+ boolean isComplete() {
+ return mTestName != null && mTestClass != null;
+ }
+
+ /** Returns whether there is currently a stack trace
+ *
+ * @return true if there is currently a stack trace, false otherwise
+ */
+ boolean hasStackTrace() {
+ return mStackTrace != null;
+ }
+
+ /**
+ * Returns the stack trace of the current test.
+ *
+ * @return a String representation of the current test's stack trace; if there is not
+ * a current stack trace, it returns an error string. Use {@link TestResult#hasStackTrace}
+ * if you need to know whether there is a stack trace.
+ */
+ String getTrace() {
+ if (hasStackTrace()) {
+ return mStackTrace.toString();
+ } else {
+ Log.e(LOG_TAG, "Could not find stack trace for failed test");
+ return new Throwable("Unknown failure").toString();
+ }
+ }
+
+ /** Provides a more user readable string for TestResult, if possible */
+ @Override
+ public String toString() {
+ StringBuilder output = new StringBuilder();
+ if (mTestClass != null ) {
+ output.append(mTestClass);
+ output.append('#');
+ }
+ if (mTestName != null) {
+ output.append(mTestName);
+ }
+ if (output.length() > 0) {
+ return output.toString();
+ }
+ return "unknown result";
+ }
+ }
+
+ /** Internal helper struct to store parsed test info. */
+ private static class ParsedTestInfo {
+ String mTestName = null;
+ String mTestClassName = null;
+ String mTestRunTime = null;
+
+ public ParsedTestInfo(String testName, String testClassName, String testRunTime) {
+ mTestName = testName;
+ mTestClassName = testClassName;
+ mTestRunTime = testRunTime;
+ }
+ }
+
+ /** Prefixes used to demarcate and identify output. */
+ private static class Prefixes {
+ @SuppressWarnings("unused")
+ private static final String INFORMATIONAL_MARKER = "[----------]";
+ private static final String START_TEST_RUN_MARKER = "[==========] Running";
+ private static final String TEST_RUN_MARKER = "[==========]";
+ private static final String START_TEST_MARKER = "[ RUN ]";
+ private static final String OK_TEST_MARKER = "[ OK ]";
+ private static final String FAILED_TEST_MARKER = "[ FAILED ]";
+ }
+
+ /**
+ * Creates the GTestResultParser.
+ *
+ * @param testRunName the test run name to provide to
+ * {@link ITestRunListener#testRunStarted(String, int)}
+ * @param listeners informed of test results as the tests are executing
+ */
+ public GeeTestResultParser(String testRunName, Collection<ITestRunListener> listeners) {
+ mTestRunName = testRunName;
+ mTestListeners = new ArrayList<ITestRunListener>(listeners);
+ }
+
+ /**
+ * Creates the GTestResultParser for a single listener.
+ *
+ * @param testRunName the test run name to provide to
+ * {@link ITestRunListener#testRunStarted(String, int)}
+ * @param listener informed of test results as the tests are executing
+ */
+ public GeeTestResultParser(String testRunName, ITestRunListener listener) {
+ mTestRunName = testRunName;
+ mTestListeners = new ArrayList<ITestRunListener>(1);
+ mTestListeners.add(listener);
+ }
+
+ /**
+ * Package prefix to be added to test names when they are reported like
+ * "android.nativemedia." You may need to add the dot if you need it.
+ */
+ public void setFakePackagePrefix(String prefix) {
+ mFakePackagePrefix = prefix;
+ }
+
+ /**
+ * Returns the current TestResult for test in progress, or a new default one.
+ *
+ * @return The TestResult for the current test run
+ */
+ private TestResult getCurrentTestResult() {
+ if (mCurrentTestResult == null) {
+ mCurrentTestResult = new TestResult();
+ }
+ return mCurrentTestResult;
+ }
+
+
+ /**
+ * Clears out the current TestResult.
+ */
+ private void clearCurrentTestResult() {
+ mCurrentTestResult = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void processNewLines(String[] lines) {
+ for (String line : lines) {
+ parse(line);
+ }
+ }
+
+ /**
+ * Parse an individual output line.
+ *
+ * @param line Text output line
+ */
+ private void parse(String line) {
+ String message = null;
+
+ if (mTestRunInProgress || line.startsWith(Prefixes.TEST_RUN_MARKER)) {
+ if (line.startsWith(Prefixes.START_TEST_MARKER)) {
+ // Individual test started
+ message = line.substring(Prefixes.START_TEST_MARKER.length()).trim();
+ processTestStartedTag(message);
+ }
+ else if (line.startsWith(Prefixes.OK_TEST_MARKER)) {
+ // Individual test completed successfully
+ message = line.substring(Prefixes.OK_TEST_MARKER.length()).trim();
+ processOKTag(message);
+ clearCurrentTestResult();
+ }
+ else if (line.startsWith(Prefixes.FAILED_TEST_MARKER)) {
+ // Individual test completed with failure
+ message = line.substring(Prefixes.FAILED_TEST_MARKER.length()).trim();
+ processFailedTag(message);
+ clearCurrentTestResult();
+ }
+ else if (line.startsWith(Prefixes.START_TEST_RUN_MARKER)) {
+ // Test run started
+ // Make sure to leave the "Running" in the string
+ message = line.substring(Prefixes.TEST_RUN_MARKER.length()).trim();
+ processRunStartedTag(message);
+ }
+ else if (line.startsWith(Prefixes.TEST_RUN_MARKER)) {
+ // Test run ended
+ // This is for the end of the test suite run, so make sure this else-if is after the
+ // check for START_TEST_SUITE_MARKER
+ message = line.substring(Prefixes.TEST_RUN_MARKER.length()).trim();
+ processRunCompletedTag(message);
+ }
+ else if (testInProgress()) {
+ // Note this does not handle the case of an error outside an actual test run
+ appendTestOutputLine(line);
+ }
+ }
+ }
+
+ /**
+ * Returns true if test run canceled.
+ *
+ * @see IShellOutputReceiver#isCancelled()
+ */
+ public boolean isCancelled() {
+ return mIsCancelled;
+ }
+
+ /**
+ * Requests cancellation of test run.
+ */
+ public void cancel() {
+ mIsCancelled = true;
+ }
+
+ /**
+ * Returns whether we're in the middle of running a test.
+ *
+ * @return True if a test was started, false otherwise
+ */
+ private boolean testInProgress() {
+ return mTestInProgress;
+ }
+
+ /**
+ * Set state to indicate we've started running a test.
+ *
+ */
+ private void setTestStarted() {
+ mTestInProgress = true;
+ }
+
+ /**
+ * Set state to indicate we've started running a test.
+ *
+ */
+ private void setTestEnded() {
+ mTestInProgress = false;
+ }
+
+ /**
+ * Reports the start of a test run, and the total test count, if it has not been previously
+ * reported.
+ */
+ private void reportTestRunStarted() {
+ // if start test run not reported yet
+ if (!mTestRunStartReported) {
+ for (ITestRunListener listener : mTestListeners) {
+ listener.testRunStarted(mTestRunName, mNumTestsExpected);
+ }
+ mTestRunStartReported = true;
+ }
+ }
+
+ /**
+ * Reports the end of a test run, and resets that test
+ */
+ private void reportTestRunEnded() {
+ for (ITestRunListener listener : mTestListeners) {
+ listener.testRunEnded(mTotalRunTime, getRunMetrics());
+ }
+ mTestRunStartReported = false;
+ }
+
+ /**
+ * Create the run metrics {@link Map} to report.
+ *
+ * @return a {@link Map} of run metrics data
+ */
+ private Map<String, String> getRunMetrics() {
+ Map<String, String> metricsMap = new HashMap<String, String>();
+ if (mCoverageTarget != null) {
+ metricsMap.put(XmlDefsTest.COVERAGE_TARGET_KEY, mCoverageTarget);
+ }
+ return metricsMap;
+ }
+
+ /**
+ * Parse the test identifier (class and test name), and optional time info.
+ *
+ * @param identifier Raw identifier of the form classname.testname, with an optional time
+ * element in the format of (XX ms) at the end
+ * @return A ParsedTestInfo representing the parsed info from the identifier string.
+ *
+ * If no time tag was detected, then the third element in the array (time_in_ms) will
+ * be null. If the line failed to parse properly (eg: could not determine name of
+ * test/class) then an "UNKNOWN" string value will be returned for the classname and
+ * testname. This method guarantees a string will always be returned for the class and
+ * test names (but not for the time value).
+ */
+ private ParsedTestInfo parseTestIdentifier(String identifier) {
+ ParsedTestInfo returnInfo = new ParsedTestInfo("UNKNOWN_CLASS", "UNKNOWN_TEST", null);
+
+ Pattern timePattern = Pattern.compile(".*(\\((\\d+) ms\\))"); // eg: (XX ms)
+ Matcher time = timePattern.matcher(identifier);
+
+ // Try to find a time
+ if (time.find()) {
+ String timeString = time.group(2); // the "XX" in "(XX ms)"
+ String discardPortion = time.group(1); // everything after the test class/name
+ identifier = identifier.substring(0, identifier.lastIndexOf(discardPortion)).trim();
+ returnInfo.mTestRunTime = timeString;
+ }
+
+ String[] testId = identifier.split("\\.");
+ if (testId.length < 2) {
+ Log.e(LOG_TAG, "Could not detect the test class and test name, received: " +
+ identifier);
+ }
+ else {
+ returnInfo.mTestClassName = testId[0];
+ returnInfo.mTestName = testId[1];
+ }
+ return returnInfo;
+ }
+
+ /**
+ * Parses and stores the test identifier (class and test name).
+ *
+ * @param identifier Raw identifier
+ */
+ private void processRunStartedTag(String identifier) {
+ // eg: (Running XX tests from 1 test case.)
+ Pattern numTestsPattern = Pattern.compile("Running (\\d+) test[s]? from .*");
+ Matcher numTests = numTestsPattern.matcher(identifier);
+
+ // Try to find number of tests
+ if (numTests.find()) {
+ try {
+ mNumTestsExpected = Integer.parseInt(numTests.group(1));
+ }
+ catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Unable to determine number of tests expected, received: " +
+ numTests.group(1));
+ }
+ }
+ if (mNumTestsExpected > 0) {
+ reportTestRunStarted();
+ mNumTestsRun = 0;
+ mTestRunInProgress = true;
+ }
+ }
+
+ /**
+ * Processes and informs listener when we encounter a tag indicating that a test suite is done.
+ *
+ * @param identifier Raw log output from the suite ended tag
+ */
+ private void processRunCompletedTag(String identifier) {
+ Pattern timePattern = Pattern.compile(".*\\((\\d+) ms total\\)"); // eg: (XX ms total)
+ Matcher time = timePattern.matcher(identifier);
+
+ // Try to find the total run time
+ if (time.find()) {
+ try {
+ mTotalRunTime = Long.parseLong(time.group(1));
+ }
+ catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Unable to determine the total running time, received: " +
+ time.group(1));
+ }
+ }
+ reportTestRunEnded();
+ mTestRunInProgress = false;
+ }
+
+ /**
+ * Processes and informs listener when we encounter a tag indicating that a test has started.
+ *
+ * @param identifier Raw log output of the form classname.testname, with an optional time (x ms)
+ */
+ private void processTestStartedTag(String identifier) {
+ ParsedTestInfo parsedResults = parseTestIdentifier(identifier);
+ TestResult testResult = getCurrentTestResult();
+ testResult.mTestClass = parsedResults.mTestClassName;
+ testResult.mTestName = parsedResults.mTestName;
+
+ TestIdentifier testId = new TestIdentifier(mFakePackagePrefix + testResult.mTestClass,
+ testResult.mTestName);
+
+ for (ITestRunListener listener : mTestListeners) {
+ listener.testStarted(testId);
+ }
+ setTestStarted();
+ }
+
+ /**
+ * Helper method to do the work necessary when a test has ended.
+ *
+ * @param identifier Raw log output of the form "classname.testname" with an optional (XX ms)
+ * at the end indicating the running time.
+ * @param testPassed Indicates whether the test passed or failed (set to true if passed, false
+ * if failed)
+ */
+ private void doTestEnded(String identifier, boolean testPassed) {
+ ParsedTestInfo parsedResults = parseTestIdentifier(identifier);
+ TestResult testResult = getCurrentTestResult();
+ TestIdentifier testId = new TestIdentifier(mFakePackagePrefix + testResult.mTestClass,
+ testResult.mTestName);
+
+ // Error - trying to end a test when one isn't in progress
+ if (!testInProgress()) {
+ Log.e(LOG_TAG, "Test currently not in progress when trying to end test: " + identifier);
+ return;
+ }
+
+ // Save the run time for this test if one exists
+ if (parsedResults.mTestRunTime != null) {
+ try {
+ testResult.mRunTime = new Long(parsedResults.mTestRunTime);
+ }
+ catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Test run time value is invalid, received: " +
+ parsedResults.mTestRunTime);
+ }
+ }
+
+ // Check that the test result is for the same test/class we're expecting it to be for
+ boolean encounteredUnexpectedTest = false;
+ if (!testResult.isComplete()) {
+ Log.e(LOG_TAG, "No test/class name is currently recorded as running!");
+ }
+ else {
+ if (testResult.mTestClass.compareTo(parsedResults.mTestClassName) != 0) {
+ Log.e(LOG_TAG, "Name for current test class does not match class we started " +
+ "with, expected: " + testResult.mTestClass + " but got: " +
+ parsedResults.mTestClassName);
+ encounteredUnexpectedTest = true;
+ }
+ if (testResult.mTestName.compareTo(parsedResults.mTestName) != 0) {
+ Log.e(LOG_TAG, "Name for current test does not match test we started with," +
+ "expected: " + testResult.mTestName + " bug got: " +
+ parsedResults.mTestName);
+ encounteredUnexpectedTest = true;
+ }
+ }
+
+ if (encounteredUnexpectedTest) {
+ // If the test name of the result changed from what we started with, report that
+ // the last known test failed, regardless of whether we received a pass or fail tag.
+ for (ITestRunListener listener : mTestListeners) {
+ listener.testFailed(ITestRunListener.TestFailure.ERROR, testId,
+ mCurrentTestResult.getTrace());
+ }
+ }
+ else if (!testPassed) { // test failed
+ for (ITestRunListener listener : mTestListeners) {
+ listener.testFailed(ITestRunListener.TestFailure.FAILURE, testId,
+ mCurrentTestResult.getTrace());
+ }
+ }
+ // For all cases (pass or fail), we ultimately need to report test has ended
+ Map <String, String> emptyMap = Collections.emptyMap();
+ for (ITestRunListener listener : mTestListeners) {
+ // @TODO: Add reporting of test run time to ITestRunListener
+ listener.testEnded(testId, emptyMap);
+ }
+
+ setTestEnded();
+ ++mNumTestsRun;
+ }
+
+ /**
+ * Processes and informs listener when we encounter the OK tag.
+ *
+ * @param identifier Raw log output of the form "classname.testname" with an optional (XX ms)
+ * at the end indicating the running time.
+ */
+ private void processOKTag(String identifier) {
+ doTestEnded(identifier, true);
+ }
+
+ /**
+ * Processes and informs listener when we encounter the FAILED tag.
+ *
+ * @param identifier Raw log output of the form "classname.testname" with an optional (XX ms)
+ * at the end indicating the running time.
+ */
+ private void processFailedTag(String identifier) {
+ doTestEnded(identifier, false);
+ }
+
+ /**
+ * Appends the test output to the current TestResult.
+ *
+ * @param line Raw test result line of output.
+ */
+ private void appendTestOutputLine(String line) {
+ TestResult testResult = getCurrentTestResult();
+ if (testResult.mStackTrace == null) {
+ testResult.mStackTrace = new StringBuilder();
+ }
+ else {
+ testResult.mStackTrace.append("\r\n");
+ }
+ testResult.mStackTrace.append(line);
+ }
+
+ /**
+ * Process an instrumentation run failure
+ *
+ * @param errorMsg The message to output about the nature of the error
+ */
+ private void handleTestRunFailed(String errorMsg) {
+ errorMsg = (errorMsg == null ? "Unknown error" : errorMsg);
+ Log.i(LOG_TAG, String.format("Test run failed: %s", errorMsg));
+ String testRunStackTrace = "";
+
+ // Report that the last known test failed
+ if ((mCurrentTestResult != null) && (mCurrentTestResult.isComplete())) {
+ // current test results are cleared out after every complete test run,
+ // if it's not null, assume the last test caused this and report as a test failure
+ TestIdentifier testId = new TestIdentifier(mCurrentTestResult.mTestClass,
+ mCurrentTestResult.mTestName);
+
+ // If there was any stack trace during the test run, append it to the "test failed"
+ // error message so we have an idea of what caused the crash/failure.
+ Map<String, String> emptyMap = Collections.emptyMap();
+ if (mCurrentTestResult.hasStackTrace()) {
+ testRunStackTrace = mCurrentTestResult.getTrace();
+ }
+ for (ITestRunListener listener : mTestListeners) {
+ listener.testFailed(ITestRunListener.TestFailure.ERROR, testId,
+ "No test results.\r\n" + testRunStackTrace);
+ listener.testEnded(testId, emptyMap);
+ }
+ clearCurrentTestResult();
+ }
+ // Report the test run failed
+ for (ITestRunListener listener : mTestListeners) {
+ listener.testRunFailed(errorMsg);
+ listener.testRunEnded(mTotalRunTime, getRunMetrics());
+ }
+ }
+
+ /**
+ * Called by parent when adb session is complete.
+ */
+ @Override
+ public void done() {
+ super.done();
+ if (mNumTestsExpected > mNumTestsRun) {
+ handleTestRunFailed(String.format("Test run incomplete. Expected %d tests, received %d",
+ mNumTestsExpected, mNumTestsRun));
+ }
+ else if (mTestRunInProgress) {
+ handleTestRunFailed("No test results");
+ }
+ }
+
+ /**
+ * Sets the coverage target for this test.
+ * <p/>
+ * Will be sent as a metric to test listeners.
+ *
+ * @param coverageTarget the coverage target
+ */
+ public void setCoverageTarget(String coverageTarget) {
+ mCoverageTarget = coverageTarget;
+ }
+}
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
index 8844667..8cccab0 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
@@ -50,6 +50,7 @@
private String mRunner = null;
private boolean mIsHostSideTest = false;
private boolean mIsVMHostTest = false;
+ private String mTestType = null;
private String mJarPath = null;
private boolean mIsSignatureTest = false;
private boolean mIsReferenceAppTest = false;
@@ -78,6 +79,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public String getUri() {
return mUri;
}
@@ -128,6 +130,10 @@
return mIsVMHostTest;
}
+ void setTestType(String testType) {
+ mTestType = testType;
+ }
+
void setJarPath(String jarPath) {
mJarPath = jarPath;
}
@@ -229,6 +235,8 @@
vmHostTest.setTests(mTests);
mDigest = generateDigest(testCaseDir, mJarPath);
return vmHostTest;
+ } else if ("native".equals(mTestType)) {
+ return new GeeTest(mUri, mName);
} else if (mIsSignatureTest) {
// TODO: hardcode the runner/class/method for now, since current package xml points to
// specialized instrumentation. Eventually this special case for signatureTest can be
@@ -302,6 +310,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public boolean isKnownTest(TestIdentifier testDef) {
return mTests.contains(testDef);
}
@@ -309,6 +318,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public boolean isKnownTestClass(String className) {
return mTestClasses.contains(className);
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
index 64706a2..f1b6ed0 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
@@ -68,6 +68,7 @@
final String runnerName = attributes.getValue("runner");
final String hostSideTest = attributes.getValue("hostSideOnly");
final String vmHostTest = attributes.getValue("vmHostTest");
+ final String testType = attributes.getValue("testType");
final String jarPath = attributes.getValue("jarPath");
final String signatureCheck = attributes.getValue("signatureCheck");
final String referenceApp = attributes.getValue("referenceAppTest");
@@ -84,6 +85,7 @@
mPackageDef.setRunner(runnerName);
mPackageDef.setIsHostSideTest(parseBoolean(hostSideTest));
mPackageDef.setIsVMHostTest(parseBoolean(vmHostTest));
+ mPackageDef.setTestType(testType);
mPackageDef.setJarPath(jarPath);
mPackageDef.setIsSignatureCheck(parseBoolean(signatureCheck));
mPackageDef.setIsReferenceApp(parseBoolean(referenceApp));