| #!/usr/bin/python2.4 |
| # |
| # |
| # Copyright 2008, 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. |
| |
| """Parser for test definition xml files.""" |
| |
| # Python imports |
| import xml.dom.minidom |
| import xml.parsers |
| |
| # local imports |
| import errors |
| import logger |
| |
| |
| class TestDefinitions(object): |
| """Accessor for a test definitions xml file data. |
| |
| Expected format is: |
| <test-definitions> |
| <test |
| name="" |
| package="" |
| [runner=""] |
| [class=""] |
| [coverage_target=""] |
| [build_path=""] |
| [continuous] |
| /> |
| <test ... |
| </test-definitions> |
| |
| TODO: add format checking. |
| """ |
| |
| # tag/attribute constants |
| _TEST_TAG_NAME = "test" |
| |
| def __init__(self): |
| # dictionary of test name to tests |
| self._testname_map = {} |
| |
| def __iter__(self): |
| return iter(self._testname_map.values()) |
| |
| def Parse(self, file_path): |
| """Parse the test suite data from from given file path. |
| |
| Args: |
| file_path: absolute file path to parse |
| Raises: |
| ParseError if file_path cannot be parsed |
| """ |
| try: |
| doc = xml.dom.minidom.parse(file_path) |
| except IOError: |
| logger.Log("test file %s does not exist" % file_path) |
| raise errors.ParseError |
| except xml.parsers.expat.ExpatError: |
| logger.Log("Error Parsing xml file: %s " % file_path) |
| raise errors.ParseError |
| self._ParseDoc(doc) |
| |
| def ParseString(self, xml_string): |
| """Alternate parse method that accepts a string of the xml data.""" |
| doc = xml.dom.minidom.parseString(xml_string) |
| # TODO: catch exceptions and raise ParseError |
| return self._ParseDoc(doc) |
| |
| def _ParseDoc(self, doc): |
| suite_elements = doc.getElementsByTagName(self._TEST_TAG_NAME) |
| |
| for suite_element in suite_elements: |
| test = self._ParseTestSuite(suite_element) |
| self._AddTest(test) |
| |
| def _ParseTestSuite(self, suite_element): |
| """Parse the suite element. |
| |
| Returns: |
| a TestSuite object, populated with parsed data |
| """ |
| test = TestSuite(suite_element) |
| return test |
| |
| def _AddTest(self, test): |
| """Adds a test to this TestManifest. |
| |
| If a test already exists with the same name, it overrides it. |
| |
| Args: |
| test: TestSuite to add |
| """ |
| self._testname_map[test.GetName()] = test |
| |
| def GetTests(self): |
| return self._testname_map.values() |
| |
| def GetContinuousTests(self): |
| con_tests = [] |
| for test in self.GetTests(): |
| if test.IsContinuous(): |
| con_tests.append(test) |
| return con_tests |
| |
| def GetTest(self, name): |
| return self._testname_map.get(name, None) |
| |
| class TestSuite(object): |
| """Represents one test suite definition parsed from xml.""" |
| |
| _NAME_ATTR = "name" |
| _PKG_ATTR = "package" |
| _RUNNER_ATTR = "runner" |
| _CLASS_ATTR = "class" |
| _TARGET_ATTR = "coverage_target" |
| _BUILD_ATTR = "build_path" |
| _CONTINUOUS_ATTR = "continuous" |
| |
| _DEFAULT_RUNNER = "android.test.InstrumentationTestRunner" |
| |
| def __init__(self, suite_element): |
| """Populates this instance's data from given suite xml element.""" |
| self._name = suite_element.getAttribute(self._NAME_ATTR) |
| self._package = suite_element.getAttribute(self._PKG_ATTR) |
| if suite_element.hasAttribute(self._RUNNER_ATTR): |
| self._runner = suite_element.getAttribute(self._RUNNER_ATTR) |
| else: |
| self._runner = self._DEFAULT_RUNNER |
| if suite_element.hasAttribute(self._CLASS_ATTR): |
| self._class = suite_element.getAttribute(self._CLASS_ATTR) |
| else: |
| self._class = None |
| if suite_element.hasAttribute(self._TARGET_ATTR): |
| self._target_name = suite_element.getAttribute(self._TARGET_ATTR) |
| else: |
| self._target_name = None |
| if suite_element.hasAttribute(self._BUILD_ATTR): |
| self._build_path = suite_element.getAttribute(self._BUILD_ATTR) |
| else: |
| self._build_path = None |
| if suite_element.hasAttribute(self._CONTINUOUS_ATTR): |
| self._continuous = suite_element.getAttribute(self._CONTINUOUS_ATTR) |
| else: |
| self._continuous = False |
| |
| def GetName(self): |
| return self._name |
| |
| def GetPackageName(self): |
| return self._package |
| |
| def GetRunnerName(self): |
| return self._runner |
| |
| def GetClassName(self): |
| return self._class |
| |
| def GetTargetName(self): |
| """Retrieve module that this test is targeting. |
| |
| Used for generating code coverage metrics. |
| """ |
| return self._target_name |
| |
| def GetBuildPath(self): |
| """Returns the build path of this test, relative to source tree root.""" |
| return self._build_path |
| |
| def IsContinuous(self): |
| """Returns true if test is flagged as being part of the continuous tests""" |
| return self._continuous |
| |
| def Parse(file_path): |
| """Parses out a TestDefinitions from given path to xml file. |
| |
| Args: |
| file_path: string absolute file path |
| Returns: |
| a TestDefinitions object containing data parsed from file_path |
| Raises: |
| ParseError if xml format is not recognized |
| """ |
| tests_result = TestDefinitions() |
| tests_result.Parse(file_path) |
| return tests_result |