am 3447c407: Merge change I8f6ac3c9 into eclair

Merge commit '3447c407dc70a365d6d7faade0f3725315d05e54' into eclair-mr2

* commit '3447c407dc70a365d6d7faade0f3725315d05e54':
  Refactor runtest test_defs to allow programmatic creation.
This commit is contained in:
Brett Chabot
2009-10-22 10:47:38 -07:00
committed by Android Git Automerger
7 changed files with 319 additions and 190 deletions

View File

@@ -1,113 +0,0 @@
#!/usr/bin/python2.4
#
#
# Copyright 2009, 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.
"""Abstract Android test suite."""
# Python imports
import xml.dom.minidom
import xml.parsers
# local imports
import errors
class AbstractTestSuite(object):
"""Represents a generic test suite definition parsed from xml.
This class will parse the XML attributes common to all TestSuite's.
"""
# name of xml tag a test suite handles. subclasses must define this.
TAG_NAME = "unspecified"
_NAME_ATTR = "name"
_BUILD_ATTR = "build_path"
_CONTINUOUS_ATTR = "continuous"
_CTS_ATTR = "cts"
_DESCRIPTION_ATTR = "description"
_EXTRA_BUILD_ARGS_ATTR = "extra_build_args"
def __init__(self):
self._attr_map = {}
def Parse(self, suite_element):
"""Populates this instance's data from given suite xml element.
Raises:
ParseError if a required attribute is missing.
"""
# parse name first so it can be used for error reporting
self._ParseAttribute(suite_element, self._NAME_ATTR, True)
self._ParseAttribute(suite_element, self._BUILD_ATTR, True)
self._ParseAttribute(suite_element, self._CONTINUOUS_ATTR, False,
default_value=False)
self._ParseAttribute(suite_element, self._CTS_ATTR, False,
default_value=False)
self._ParseAttribute(suite_element, self._DESCRIPTION_ATTR, False,
default_value="")
self._ParseAttribute(suite_element, self._EXTRA_BUILD_ARGS_ATTR, False,
default_value="")
def _ParseAttribute(self, suite_element, attribute_name, mandatory,
default_value=None):
if suite_element.hasAttribute(attribute_name):
self._attr_map[attribute_name] = \
suite_element.getAttribute(attribute_name)
elif mandatory:
error_msg = ("Could not find attribute %s in %s %s" %
(attribute_name, self.TAG_NAME, self.GetName()))
raise errors.ParseError(msg=error_msg)
else:
self._attr_map[attribute_name] = default_value
def GetName(self):
return self._GetAttribute(self._NAME_ATTR)
def GetBuildPath(self):
"""Returns the build path of this test, relative to source tree root."""
return self._GetAttribute(self._BUILD_ATTR)
def GetBuildDependencies(self, options):
"""Returns a list of dependent build paths."""
return []
def IsContinuous(self):
"""Returns true if test is flagged as being part of the continuous tests"""
return self._GetAttribute(self._CONTINUOUS_ATTR)
def IsCts(self):
"""Returns true if test is part of the compatibility test suite"""
return self._GetAttribute(self._CTS_ATTR)
def GetDescription(self):
"""Returns a description if available, an empty string otherwise."""
return self._GetAttribute(self._DESCRIPTION_ATTR)
def GetExtraBuildArgs(self):
"""Returns the extra build args if available, an empty string otherwise."""
return self._GetAttribute(self._EXTRA_BUILD_ARGS_ATTR)
def _GetAttribute(self, attribute_name):
return self._attr_map.get(attribute_name)
def Run(self, options, adb):
"""Runs the test.
Subclasses must implement this.
Args:
options: global command line options
"""
raise NotImplementedError

View File

@@ -20,23 +20,15 @@
# python imports
import os
# local imports
from abstract_test import AbstractTestSuite
import errors
import logger
import run_command
import test_suite
class HostTestSuite(AbstractTestSuite):
class HostTestSuite(test_suite.AbstractTestSuite):
"""A test suite for running hosttestlib java tests."""
TAG_NAME = "test-host"
_CLASS_ATTR = "class"
# TODO: consider obsoleting in favor of parsing the Android.mk to find the
# jar name
_JAR_ATTR = "jar_name"
_JUNIT_JAR_NAME = "junit.jar"
_HOSTTESTLIB_NAME = "hosttestlib.jar"
_DDMLIB_NAME = "ddmlib.jar"
@@ -54,21 +46,29 @@ class HostTestSuite(AbstractTestSuite):
# the test suite?
_TEST_RUNNER = "com.android.hosttest.DeviceTestRunner"
def Parse(self, suite_element):
super(HostTestSuite, self).Parse(suite_element)
self._ParseAttribute(suite_element, self._CLASS_ATTR, True)
self._ParseAttribute(suite_element, self._JAR_ATTR, True)
def __init__(self):
test_suite.AbstractTestSuite.__init__(self)
self._jar_name = None
self._class_name = None
def GetBuildDependencies(self, options):
"""Override parent to tag on building host libs."""
return self._LIB_BUILD_PATHS
def GetClass(self):
return self._GetAttribute(self._CLASS_ATTR)
def GetClassName(self):
return self._class_name
def SetClassName(self, class_name):
self._class_name = class_name
return self
def GetJarName(self):
"""Returns the name of the host jar that contains the tests."""
return self._GetAttribute(self._JAR_ATTR)
return self._jar_name
def SetJarName(self, jar_name):
self._jar_name = jar_name
return self
def Run(self, options, adb_interface):
"""Runs the host test.
@@ -77,11 +77,14 @@ class HostTestSuite(AbstractTestSuite):
Args:
options: command line options for running host tests. Expected member
fields:
fields:
host_lib_path: path to directory that contains host library files
test_data_path: path to directory that contains test data files
preview: if true, do not execute, display commands only
adb_interface: reference to device under test
Raises:
errors.AbortError: if fatal error occurs
"""
# get the serial number of the device under test, so it can be passed to
# hosttestlib.
@@ -100,7 +103,7 @@ class HostTestSuite(AbstractTestSuite):
# -p <test data path>
cmd = "java -cp %s %s %s -s %s -p %s" % (":".join(full_lib_paths),
self._TEST_RUNNER,
self.GetClass(), serial_number,
self.GetClassName(), serial_number,
options.test_data_path)
logger.Log(cmd)
if not options.preview:

View File

@@ -21,59 +21,66 @@
import os
# local imports
from abstract_test import AbstractTestSuite
import coverage
import errors
import logger
import test_suite
class InstrumentationTestSuite(AbstractTestSuite):
"""Represents a java instrumentation test suite definition run on Android device."""
class InstrumentationTestSuite(test_suite.AbstractTestSuite):
"""Represents a java instrumentation test suite definition run on device."""
# for legacy reasons, the xml tag name for java (device) tests is "test:
TAG_NAME = "test"
_PKG_ATTR = "package"
_RUNNER_ATTR = "runner"
_CLASS_ATTR = "class"
_TARGET_ATTR = "coverage_target"
_DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
# build path to Emma target Makefile
_EMMA_BUILD_PATH = os.path.join("external", "emma")
def _GetTagName(self):
return self._TAG_NAME
def __init__(self):
test_suite.AbstractTestSuite.__init__(self)
self._package_name = None
self._runner_name = self.DEFAULT_RUNNER
self._class_name = None
self._target_name = None
def GetPackageName(self):
return self._GetAttribute(self._PKG_ATTR)
return self._package_name
def SetPackageName(self, package_name):
self._package_name = package_name
return self
def GetRunnerName(self):
return self._GetAttribute(self._RUNNER_ATTR)
return self._runner_name
def SetRunnerName(self, runner_name):
self._runner_name = runner_name
return self
def GetClassName(self):
return self._GetAttribute(self._CLASS_ATTR)
return self._class_name
def SetClassName(self, class_name):
self._class_name = class_name
return self
def GetTargetName(self):
"""Retrieve module that this test is targeting.
Used for generating code coverage metrics.
Returns:
the module target name
"""
return self._GetAttribute(self._TARGET_ATTR)
return self._target_name
def SetTargetName(self, target_name):
self._target_name = target_name
return self
def GetBuildDependencies(self, options):
if options.coverage:
return [self._EMMA_BUILD_PATH]
return []
def Parse(self, suite_element):
super(InstrumentationTestSuite, self).Parse(suite_element)
self._ParseAttribute(suite_element, self._PKG_ATTR, True)
self._ParseAttribute(suite_element, self._RUNNER_ATTR, False, self._DEFAULT_RUNNER)
self._ParseAttribute(suite_element, self._CLASS_ATTR, False)
self._ParseAttribute(suite_element, self._TARGET_ATTR, False)
def Run(self, options, adb):
"""Run the provided test suite.
@@ -82,6 +89,9 @@ class InstrumentationTestSuite(AbstractTestSuite):
Args:
options: command line options to provide to test run
adb: adb_interface to device under test
Raises:
errors.AbortError: if fatal error occurs
"""
test_class = self.GetClassName()
@@ -122,10 +132,10 @@ class InstrumentationTestSuite(AbstractTestSuite):
logger.Log("Running in coverage mode, suppressing test output")
try:
(test_results, status_map) = adb.StartInstrumentationForPackage(
package_name=self.GetPackageName(),
runner_name=self.GetRunnerName(),
timeout_time=60*60,
instrumentation_args=instrumentation_args)
package_name=self.GetPackageName(),
runner_name=self.GetRunnerName(),
timeout_time=60*60,
instrumentation_args=instrumentation_args)
except errors.InstrumentationError, errors.DeviceUnresponsiveError:
return
self._PrintTestResults(test_results)
@@ -156,11 +166,11 @@ class InstrumentationTestSuite(AbstractTestSuite):
error_count = 0
fail_count = 0
for test_result in test_results:
if test_result.GetStatusCode() == -1: # error
if test_result.GetStatusCode() == -1: # error
logger.Log("Error in %s: %s" % (test_result.GetTestName(),
test_result.GetFailureReason()))
error_count+=1
elif test_result.GetStatusCode() == -2: # failure
elif test_result.GetStatusCode() == -2: # failure
logger.Log("Failure in %s: %s" % (test_result.GetTestName(),
test_result.GetFailureReason()))
fail_count+=1

View File

@@ -18,28 +18,19 @@
"""TestSuite for running native Android tests."""
# python imports
import re
import os
import re
# local imports
from abstract_test import AbstractTestSuite
import android_build
import logger
import run_command
import test_suite
class NativeTestSuite(AbstractTestSuite):
class NativeTestSuite(test_suite.AbstractTestSuite):
"""A test suite for running native aka C/C++ tests on device."""
TAG_NAME = "test-native"
def _GetTagName(self):
return self._TAG_NAME
def Parse(self, suite_element):
super(NativeTestSuite, self).Parse(suite_element)
def Run(self, options, adb):
"""Run the provided *native* test suite.

View File

@@ -24,9 +24,7 @@ import xml.parsers
# local imports
import errors
import logger
from instrumentation_test import InstrumentationTestSuite
from native_test import NativeTestSuite
from host_test import HostTestSuite
import xml_suite_helper
class TestDefinitions(object):
@@ -74,21 +72,13 @@ class TestDefinitions(object):
def _ParseDoc(self, doc):
root_element = self._GetRootElement(doc)
suite_parser = xml_suite_helper.XmlSuiteParser()
for element in root_element.childNodes:
if element.nodeType != xml.dom.Node.ELEMENT_NODE:
continue
test_suite = None
if element.nodeName == InstrumentationTestSuite.TAG_NAME:
test_suite = InstrumentationTestSuite()
elif element.nodeName == NativeTestSuite.TAG_NAME:
test_suite = NativeTestSuite()
elif element.nodeName == HostTestSuite.TAG_NAME:
test_suite = HostTestSuite()
else:
logger.Log("Unrecognized tag %s found" % element.nodeName)
continue
test_suite.Parse(element)
self._AddTest(test_suite)
test_suite = suite_parser.Parse(element)
if test_suite:
self._AddTest(test_suite)
def _GetRootElement(self, doc):
root_elements = doc.getElementsByTagName("test-definitions")

View File

@@ -0,0 +1,96 @@
#!/usr/bin/python2.4
#
#
# Copyright 2009, 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.
"""Abstract Android test suite."""
class AbstractTestSuite(object):
"""Represents a generic test suite definition."""
def __init__(self):
self._name = None
self._build_path = None
self._build_dependencies = []
self._is_continuous = False
self._is_cts = False
self._description = ''
self._extra_build_args = ''
def GetName(self):
return self._name
def SetName(self, name):
self._name = name
return self
def GetBuildPath(self):
"""Returns the build path of this test, relative to source tree root."""
return self._build_path
def SetBuildPath(self, build_path):
self._build_path = build_path
return self
def GetBuildDependencies(self, options):
"""Returns a list of dependent build paths."""
return self._build_dependencies
def SetBuildDependencies(self, build_dependencies):
self._build_dependencies = build_dependencies
return self
def IsContinuous(self):
"""Returns true if test is part of the continuous test."""
return self._is_continuous
def SetContinuous(self, continuous):
self._is_continuous = continuous
return self._is_continuous
def IsCts(self):
"""Returns true if test is part of the compatibility test suite"""
return self._is_cts
def SetCts(self, cts):
self._is_cts = cts
return self
def GetDescription(self):
"""Returns a description if available, an empty string otherwise."""
return self._description
def SetDescription(self, desc):
self._description = desc
return self
def GetExtraBuildArgs(self):
"""Returns the extra build args if available, an empty string otherwise."""
return self._extra_build_args
def SetExtraBuildArgs(self, build_args):
self._extra_build_args = build_args
return self
def Run(self, options, adb):
"""Runs the test.
Subclasses must implement this.
Args:
options: global command line options
adb: asdb_interface to device under test
"""
raise NotImplementedError

View File

@@ -0,0 +1,152 @@
#!/usr/bin/python2.4
#
#
# Copyright 2009, 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.
"""Utility to parse suite info from xml."""
# Python imports
import xml.dom.minidom
import xml.parsers
# local imports
import errors
import logger
import host_test
import instrumentation_test
import native_test
class XmlSuiteParser(object):
"""Parses XML attributes common to all TestSuite's."""
# common attributes
_NAME_ATTR = 'name'
_BUILD_ATTR = 'build_path'
_CONTINUOUS_ATTR = 'continuous'
_CTS_ATTR = 'cts'
_DESCRIPTION_ATTR = 'description'
_EXTRA_BUILD_ARGS_ATTR = 'extra_build_args'
def Parse(self, element):
"""Populates common suite attributes from given suite xml element.
Args:
element: xml node to parse
Raises:
ParseError if a required attribute is missing.
Returns:
parsed test suite or None
"""
parser = None
if element.nodeName == InstrumentationParser.TAG_NAME:
parser = InstrumentationParser()
elif element.nodeName == NativeParser.TAG_NAME:
parser = NativeParser()
elif element.nodeName == HostParser.TAG_NAME:
parser = HostParser()
else:
logger.Log('Unrecognized tag %s found' % element.nodeName)
return None
test_suite = parser.Parse(element)
return test_suite
def _ParseCommonAttributes(self, suite_element, test_suite):
test_suite.SetName(self._ParseAttribute(suite_element, self._NAME_ATTR,
True))
test_suite.SetBuildPath(self._ParseAttribute(suite_element,
self._BUILD_ATTR, True))
test_suite.SetContinuous(self._ParseAttribute(suite_element,
self._CONTINUOUS_ATTR,
False, default_value=False))
test_suite.SetCts(self._ParseAttribute(suite_element, self._CTS_ATTR, False,
default_value=False))
test_suite.SetDescription(self._ParseAttribute(suite_element,
self._DESCRIPTION_ATTR,
False,
default_value=''))
test_suite.SetExtraBuildArgs(self._ParseAttribute(
suite_element, self._EXTRA_BUILD_ARGS_ATTR, False, default_value=''))
def _ParseAttribute(self, suite_element, attribute_name, mandatory,
default_value=None):
if suite_element.hasAttribute(attribute_name):
value = suite_element.getAttribute(attribute_name)
elif mandatory:
error_msg = ('Could not find attribute %s in %s' %
(attribute_name, self.TAG_NAME))
raise errors.ParseError(msg=error_msg)
else:
value = default_value
return value
class InstrumentationParser(XmlSuiteParser):
"""Parses instrumentation suite attributes from xml."""
# for legacy reasons, the xml tag name for java (device) tests is 'test'
TAG_NAME = 'test'
_PKG_ATTR = 'package'
_RUNNER_ATTR = 'runner'
_CLASS_ATTR = 'class'
_TARGET_ATTR = 'coverage_target'
def Parse(self, suite_element):
"""Creates suite and populate with data from xml element."""
suite = instrumentation_test.InstrumentationTestSuite()
XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
suite.SetPackageName(self._ParseAttribute(suite_element, self._PKG_ATTR,
True))
suite.SetRunnerName(self._ParseAttribute(
suite_element, self._RUNNER_ATTR, False,
instrumentation_test.InstrumentationTestSuite.DEFAULT_RUNNER))
suite.SetClassName(self._ParseAttribute(suite_element, self._CLASS_ATTR,
False))
suite.SetTargetName(self._ParseAttribute(suite_element, self._TARGET_ATTR,
False))
return suite
class NativeParser(XmlSuiteParser):
"""Parses native suite attributes from xml."""
TAG_NAME = 'test-native'
def Parse(self, suite_element):
"""Creates suite and populate with data from xml element."""
suite = native_test.NativeTestSuite()
XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
return suite
class HostParser(XmlSuiteParser):
"""Parses host suite attributes from xml."""
TAG_NAME = 'test-host'
_CLASS_ATTR = 'class'
# TODO: consider obsoleting in favor of parsing the Android.mk to find the
# jar name
_JAR_ATTR = 'jar_name'
def Parse(self, suite_element):
"""Creates suite and populate with data from xml element."""
suite = host_test.HostTestSuite()
XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
suite.SetClassName(self._ParseAttribute(suite_element, self._CLASS_ATTR,
True))
suite.SetJarName(self._ParseAttribute(suite_element, self._JAR_ATTR, True))
return suite