AI 144412: am: CL 144340 Added support to run native tests on the device.
The tests name must start with 'test_'. Tests should return 0 on success, 1 on failure. * development/testrunner/test_defs.xml: Added new element to represent native tests. * development/testrunner/test_defs.py: Added handling of the new <test-native> element. The testsuite has new IsNative method. TestDefinition's iterator is ordered by test names. Added GetDescription() method to access the optional description. * development/testrunner/runtest.py: Print the description next to the test name if applicable (runtest_py -l) Added a _RunNativeTest method to run a test on the target, report the status and clean up the test after the run. Added Original author: niko Automated import of CL 144412
This commit is contained in:
committed by
The Android Open Source Project
parent
2962188689
commit
247286697f
@@ -23,6 +23,7 @@ Based on previous <androidroot>/development/tools/runtest shell script.
|
|||||||
import glob
|
import glob
|
||||||
import optparse
|
import optparse
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
from sets import Set
|
from sets import Set
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ class TestRunner(object):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# disable logging of timestamp
|
# disable logging of timestamp
|
||||||
|
self._root_path = android_build.GetTop()
|
||||||
logger.SetTimestampLogging(False)
|
logger.SetTimestampLogging(False)
|
||||||
|
|
||||||
def _ProcessOptions(self):
|
def _ProcessOptions(self):
|
||||||
@@ -137,8 +139,6 @@ class TestRunner(object):
|
|||||||
if self._options.verbose:
|
if self._options.verbose:
|
||||||
logger.SetVerbose(True)
|
logger.SetVerbose(True)
|
||||||
|
|
||||||
self._root_path = android_build.GetTop()
|
|
||||||
|
|
||||||
self._known_tests = self._ReadTests()
|
self._known_tests = self._ReadTests()
|
||||||
|
|
||||||
self._coverage_gen = coverage.CoverageGenerator(
|
self._coverage_gen = coverage.CoverageGenerator(
|
||||||
@@ -172,7 +172,7 @@ class TestRunner(object):
|
|||||||
"""Prints out set of defined tests."""
|
"""Prints out set of defined tests."""
|
||||||
print "The following tests are currently defined:"
|
print "The following tests are currently defined:"
|
||||||
for test in self._known_tests:
|
for test in self._known_tests:
|
||||||
print test.GetName()
|
print "%-15s %s" % (test.GetName(), test.GetDescription())
|
||||||
|
|
||||||
def _DoBuild(self):
|
def _DoBuild(self):
|
||||||
logger.SilentLog("Building tests...")
|
logger.SilentLog("Building tests...")
|
||||||
@@ -261,6 +261,37 @@ class TestRunner(object):
|
|||||||
if coverage_file is not None:
|
if coverage_file is not None:
|
||||||
logger.Log("Coverage report generated at %s" % coverage_file)
|
logger.Log("Coverage report generated at %s" % coverage_file)
|
||||||
|
|
||||||
|
def _RunNativeTest(self, test_suite):
|
||||||
|
"""Run the provided *native* test suite.
|
||||||
|
|
||||||
|
The test_suite must contain a build path where the native test files are.
|
||||||
|
Each test's name must start with 'test_' and have a .cc or .cpp extension.
|
||||||
|
A successful test must return 0. Any other value will be considered
|
||||||
|
as an error.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
test_suite: TestSuite to run
|
||||||
|
"""
|
||||||
|
# find all test files, convert unicode names to ascii, take the basename
|
||||||
|
# and drop the .cc/.cpp extension.
|
||||||
|
file_pattern = os.path.join(test_suite.GetBuildPath(), "test_*")
|
||||||
|
file_list = []
|
||||||
|
for f in map(str, glob.glob(file_pattern)):
|
||||||
|
f = os.path.basename(f)
|
||||||
|
f = re.split(".[cp]+$", f)[0]
|
||||||
|
file_list.append(f)
|
||||||
|
|
||||||
|
for f in file_list:
|
||||||
|
full_path = "/system/bin/%s" % f
|
||||||
|
|
||||||
|
# Run
|
||||||
|
status = self._adb.SendShellCommand("%s >/dev/null 2>&1;echo -n $?" %
|
||||||
|
full_path)
|
||||||
|
logger.Log("%s... %s" % (f, status == "0" and "ok" or "failed"))
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
self._adb.SendShellCommand("rm %s" % full_path)
|
||||||
|
|
||||||
def RunTests(self):
|
def RunTests(self):
|
||||||
"""Main entry method - executes the tests according to command line args."""
|
"""Main entry method - executes the tests according to command line args."""
|
||||||
try:
|
try:
|
||||||
@@ -278,7 +309,10 @@ class TestRunner(object):
|
|||||||
self._DoBuild()
|
self._DoBuild()
|
||||||
|
|
||||||
for test_suite in self._GetTestsToRun():
|
for test_suite in self._GetTestsToRun():
|
||||||
self._RunTest(test_suite)
|
if test_suite.IsNative():
|
||||||
|
self._RunNativeTest(test_suite)
|
||||||
|
else:
|
||||||
|
self._RunTest(test_suite)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.Log("Exiting...")
|
logger.Log("Exiting...")
|
||||||
except errors.AbortError:
|
except errors.AbortError:
|
||||||
|
|||||||
@@ -38,7 +38,14 @@ class TestDefinitions(object):
|
|||||||
[class=""]
|
[class=""]
|
||||||
[coverage_target=""]
|
[coverage_target=""]
|
||||||
[build_path=""]
|
[build_path=""]
|
||||||
[continuous]
|
[continuous=false]
|
||||||
|
[description=""]
|
||||||
|
/>
|
||||||
|
<test-native
|
||||||
|
name=""
|
||||||
|
build_path=""
|
||||||
|
[continuous=false]
|
||||||
|
[description=""]
|
||||||
/>
|
/>
|
||||||
<test ...
|
<test ...
|
||||||
</test-definitions>
|
</test-definitions>
|
||||||
@@ -48,13 +55,17 @@ class TestDefinitions(object):
|
|||||||
|
|
||||||
# tag/attribute constants
|
# tag/attribute constants
|
||||||
_TEST_TAG_NAME = "test"
|
_TEST_TAG_NAME = "test"
|
||||||
|
_TEST_NATIVE_TAG_NAME = "test-native"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# dictionary of test name to tests
|
# dictionary of test name to tests
|
||||||
self._testname_map = {}
|
self._testname_map = {}
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self._testname_map.values())
|
ordered_list = []
|
||||||
|
for k in sorted(self._testname_map):
|
||||||
|
ordered_list.append(self._testname_map[k])
|
||||||
|
return iter(ordered_list)
|
||||||
|
|
||||||
def Parse(self, file_path):
|
def Parse(self, file_path):
|
||||||
"""Parse the test suite data from from given file path.
|
"""Parse the test suite data from from given file path.
|
||||||
@@ -87,6 +98,12 @@ class TestDefinitions(object):
|
|||||||
test = self._ParseTestSuite(suite_element)
|
test = self._ParseTestSuite(suite_element)
|
||||||
self._AddTest(test)
|
self._AddTest(test)
|
||||||
|
|
||||||
|
suite_elements = doc.getElementsByTagName(self._TEST_NATIVE_TAG_NAME)
|
||||||
|
|
||||||
|
for suite_element in suite_elements:
|
||||||
|
test = self._ParseNativeTestSuite(suite_element)
|
||||||
|
self._AddTest(test)
|
||||||
|
|
||||||
def _ParseTestSuite(self, suite_element):
|
def _ParseTestSuite(self, suite_element):
|
||||||
"""Parse the suite element.
|
"""Parse the suite element.
|
||||||
|
|
||||||
@@ -96,6 +113,17 @@ class TestDefinitions(object):
|
|||||||
test = TestSuite(suite_element)
|
test = TestSuite(suite_element)
|
||||||
return test
|
return test
|
||||||
|
|
||||||
|
def _ParseNativeTestSuite(self, suite_element):
|
||||||
|
"""Parse the native test element.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
a TestSuite object, populated with parsed data
|
||||||
|
Raises:
|
||||||
|
ParseError if some required attribute is missing.
|
||||||
|
"""
|
||||||
|
test = TestSuite(suite_element, native=True)
|
||||||
|
return test
|
||||||
|
|
||||||
def _AddTest(self, test):
|
def _AddTest(self, test):
|
||||||
"""Adds a test to this TestManifest.
|
"""Adds a test to this TestManifest.
|
||||||
|
|
||||||
@@ -129,13 +157,27 @@ class TestSuite(object):
|
|||||||
_TARGET_ATTR = "coverage_target"
|
_TARGET_ATTR = "coverage_target"
|
||||||
_BUILD_ATTR = "build_path"
|
_BUILD_ATTR = "build_path"
|
||||||
_CONTINUOUS_ATTR = "continuous"
|
_CONTINUOUS_ATTR = "continuous"
|
||||||
|
_DESCRIPTION_ATTR = "description"
|
||||||
|
|
||||||
_DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
|
_DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
|
||||||
|
|
||||||
def __init__(self, suite_element):
|
def __init__(self, suite_element, native=False):
|
||||||
"""Populates this instance's data from given suite xml element."""
|
"""Populates this instance's data from given suite xml element.
|
||||||
|
Raises:
|
||||||
|
ParseError if some required attribute is missing.
|
||||||
|
"""
|
||||||
|
self._native = native
|
||||||
self._name = suite_element.getAttribute(self._NAME_ATTR)
|
self._name = suite_element.getAttribute(self._NAME_ATTR)
|
||||||
self._package = suite_element.getAttribute(self._PKG_ATTR)
|
|
||||||
|
if self._native:
|
||||||
|
# For native runs, _BUILD_ATTR is required
|
||||||
|
if not suite_element.hasAttribute(self._BUILD_ATTR):
|
||||||
|
logger.Log("Error: %s is missing required build_path attribute" %
|
||||||
|
self._name)
|
||||||
|
raise errors.ParseError
|
||||||
|
else:
|
||||||
|
self._package = suite_element.getAttribute(self._PKG_ATTR)
|
||||||
|
|
||||||
if suite_element.hasAttribute(self._RUNNER_ATTR):
|
if suite_element.hasAttribute(self._RUNNER_ATTR):
|
||||||
self._runner = suite_element.getAttribute(self._RUNNER_ATTR)
|
self._runner = suite_element.getAttribute(self._RUNNER_ATTR)
|
||||||
else:
|
else:
|
||||||
@@ -156,6 +198,10 @@ class TestSuite(object):
|
|||||||
self._continuous = suite_element.getAttribute(self._CONTINUOUS_ATTR)
|
self._continuous = suite_element.getAttribute(self._CONTINUOUS_ATTR)
|
||||||
else:
|
else:
|
||||||
self._continuous = False
|
self._continuous = False
|
||||||
|
if suite_element.hasAttribute(self._DESCRIPTION_ATTR):
|
||||||
|
self._description = suite_element.getAttribute(self._DESCRIPTION_ATTR)
|
||||||
|
else:
|
||||||
|
self._description = ""
|
||||||
|
|
||||||
def GetName(self):
|
def GetName(self):
|
||||||
return self._name
|
return self._name
|
||||||
@@ -184,6 +230,13 @@ class TestSuite(object):
|
|||||||
"""Returns true if test is flagged as being part of the continuous tests"""
|
"""Returns true if test is flagged as being part of the continuous tests"""
|
||||||
return self._continuous
|
return self._continuous
|
||||||
|
|
||||||
|
def IsNative(self):
|
||||||
|
"""Returns true if test is a native one."""
|
||||||
|
return self._native
|
||||||
|
|
||||||
|
def GetDescription(self):
|
||||||
|
return self._description
|
||||||
|
|
||||||
def Parse(file_path):
|
def Parse(file_path):
|
||||||
"""Parses out a TestDefinitions from given path to xml file.
|
"""Parses out a TestDefinitions from given path to xml file.
|
||||||
|
|
||||||
|
|||||||
@@ -17,32 +17,65 @@
|
|||||||
<!--
|
<!--
|
||||||
This file contains standard test definitions for the Android platform
|
This file contains standard test definitions for the Android platform
|
||||||
|
|
||||||
Tests are defined by <test> tags with the following attributes
|
Java tests are defined by <test> tags and native ones (C/C++) are defined by
|
||||||
|
<test-native> tags.
|
||||||
|
|
||||||
name package [class runner build_path coverage_target continuous]
|
JAVA/application tests:
|
||||||
|
=======================
|
||||||
|
The java <test> element has the following attributes
|
||||||
|
|
||||||
Where:
|
name package [class runner build_path coverage_target continuous description]
|
||||||
name: Self-descriptive name used to uniquely identify the test
|
|
||||||
build_path: File system path, relative to Android build root, to this package's
|
|
||||||
Android.mk file. If omitted, build/sync step for this test will be skipped
|
|
||||||
package: Android application package that contains the tests
|
|
||||||
class: Optional. Fully qualified Java test class to run.
|
|
||||||
runner: Fully qualified InstrumentationTestRunner to execute. If omitted,
|
|
||||||
will default to android.test.InstrumentationTestRunner
|
|
||||||
coverage_target: Build name of Android package this test targets - these targets
|
|
||||||
are defined in the coverage_targets.xml file. Used as basis for code
|
|
||||||
coverage metrics. If omitted, code coverage will not be supported for this
|
|
||||||
test
|
|
||||||
continuous: Optional boolean. Default is false. Set to true if tests are known
|
|
||||||
to be reliable, and should be included in a continuous test system. false if
|
|
||||||
they are under development.
|
|
||||||
|
|
||||||
These attributes map to the following commands:
|
Where:
|
||||||
(if class is defined)
|
name: Self-descriptive name used to uniquely identify the test
|
||||||
adb shell am instrument -w <package>/<runner>
|
build_path: File system path, relative to Android build root, to this
|
||||||
(else)
|
package's Android.mk file. If omitted, build/sync step for this test will
|
||||||
adb shell am instrument -w -e class <class> <package>/<runner>
|
be skipped.
|
||||||
|
package: Android application package that contains the tests
|
||||||
|
class: Optional. Fully qualified Java test class to run.
|
||||||
|
runner: Fully qualified InstrumentationTestRunner to execute. If omitted,
|
||||||
|
will default to android.test.InstrumentationTestRunner.
|
||||||
|
coverage_target: Build name of Android package this test targets - these
|
||||||
|
targets are defined in the coverage_targets.xml file. Used as basis for
|
||||||
|
code coverage metrics. If omitted, code coverage will not be supported for
|
||||||
|
this test.
|
||||||
|
continuous: Optional boolean. Default is false. Set to true if tests are known
|
||||||
|
to be reliable, and should be included in a continuous test system. false if
|
||||||
|
they are under development.
|
||||||
|
|
||||||
|
description: Optional string. Default is empty. Short description (typically
|
||||||
|
less than 60 characters) about this test.
|
||||||
|
|
||||||
|
These attributes map to the following commands:
|
||||||
|
(if class is defined)
|
||||||
|
adb shell am instrument -w <package>/<runner>
|
||||||
|
(else)
|
||||||
|
adb shell am instrument -w -e class <class> <package>/<runner>
|
||||||
|
|
||||||
|
Native tests:
|
||||||
|
=============
|
||||||
|
The <test-native> element has the following attributes
|
||||||
|
|
||||||
|
name build_path [continuous description]
|
||||||
|
|
||||||
|
Where:
|
||||||
|
name: Self-descriptive name used to uniquely identify the test
|
||||||
|
build_path: File system path, relative to Android build root, to this
|
||||||
|
package's Android.mk file. By convention the name of a test starts with
|
||||||
|
'test_'.
|
||||||
|
continuous: Optional boolean. Default is false. Set to true if tests are known
|
||||||
|
to be reliable, and should be included in a continuous test system.
|
||||||
|
false if they are under development.
|
||||||
|
description: Optional string. Default is empty. Short description (typically
|
||||||
|
less than 60 characters) about this test.
|
||||||
|
|
||||||
|
These attributes map to the following commands:
|
||||||
|
make <build_path>/Android.mk
|
||||||
|
adb sync
|
||||||
|
for test_prog in <tests built>; do
|
||||||
|
adb shell "/system/bin/${test_prog} >/dev/null 2>&1;echo \$?"
|
||||||
|
adb shell "rm /system/bin/${test_prog}"
|
||||||
|
done
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<test-definitions version="1">
|
<test-definitions version="1">
|
||||||
@@ -223,4 +256,10 @@ These attributes map to the following commands:
|
|||||||
coverage_target="Settings" />
|
coverage_target="Settings" />
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<!-- native tests -->
|
||||||
|
<test-native name="libstdcpp"
|
||||||
|
build_path="system/extras/tests/bionic/libstdc++"
|
||||||
|
description="Bionic libstdc++." />
|
||||||
|
|
||||||
|
|
||||||
</test-definitions>
|
</test-definitions>
|
||||||
|
|||||||
Reference in New Issue
Block a user