Home | History | Annotate | Download | only in cts
      1 #!/usr/bin/python2.4
      2 
      3 # Copyright (C) 2009 The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #     http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 """Utility classes for CTS."""
     18 
     19 import re
     20 import xml.dom.minidom as minidom
     21 
     22 
     23 class TestPackage(object):
     24   """This class represents a test package.
     25 
     26   Each test package consists of one or more suites, each containing one or more subsuites and/or
     27   one or more test cases. Each test case contains one or more tests.
     28 
     29   The package structure is currently stored using Python dictionaries and lists. Translation
     30   to XML is done via a DOM tree that gets created on demand.
     31 
     32   TODO: Instead of using an internal data structure, using a DOM tree directly would increase
     33   the usability. For example, one could easily create an instance by parsing an existing XML.
     34   """
     35 
     36   class TestSuite(object):
     37     """A test suite."""
     38 
     39     def __init__(self, is_root=False):
     40       self.is_root = is_root
     41       self.test_cases = {}
     42       self.test_suites = {}
     43 
     44     def Add(self, names):
     45       if len(names) == 2:
     46         # names contains the names of the test case and the test
     47         test_case = self.test_cases.setdefault(names[0], [])
     48         test_case.append(names[1])
     49       else:
     50         sub_suite = self.test_suites.setdefault(names[0], TestPackage.TestSuite())
     51         sub_suite.Add(names[1:])
     52 
     53     def WriteDescription(self, doc, parent):
     54       """Recursively append all suites and testcases to the parent tag."""
     55       for (suite_name, suite) in self.test_suites.iteritems():
     56         child = doc.createElement('TestSuite')
     57         child.setAttribute('name', suite_name)
     58         parent.appendChild(child)
     59         # recurse into child suites
     60         suite.WriteDescription(doc, child)
     61       for (case_name, test_list) in self.test_cases.iteritems():
     62         child = doc.createElement('TestCase')
     63         child.setAttribute('name', case_name)
     64         parent.appendChild(child)
     65         for test_name in test_list:
     66           test = doc.createElement('Test')
     67           test.setAttribute('name', test_name)
     68           child.appendChild(test)
     69 
     70   def __init__(self, package_name, app_package_name=''):
     71     self.encoding = 'UTF-8'
     72     self.attributes = {'name': package_name, 'AndroidFramework': 'Android 1.0',
     73                        'version': '1.0', 'targetNameSpace': '', 'targetBinaryName': '',
     74                        'jarPath': '', 'appPackageName': app_package_name}
     75     self.root_suite = self.TestSuite(is_root=True)
     76 
     77   def AddTest(self, name):
     78     """Add a test to the package.
     79 
     80     Test names are given in the form "testSuiteName.testSuiteName.TestCaseName.testName".
     81     Test suites can be nested to any depth.
     82 
     83     Args:
     84       name: The name of the test to add.
     85     """
     86     parts = name.split('.')
     87     self.root_suite.Add(parts)
     88 
     89   def AddAttribute(self, name, value):
     90     """Add an attribute to the test package itself."""
     91     self.attributes[name] = value
     92 
     93   def GetDocument(self):
     94     """Returns a minidom Document representing the test package structure."""
     95     doc = minidom.Document()
     96     package = doc.createElement('TestPackage')
     97     for (attr, value) in self.attributes.iteritems():
     98       package.setAttribute(attr, value)
     99     self.root_suite.WriteDescription(doc, package)
    100     doc.appendChild(package)
    101     return doc
    102 
    103   def WriteDescription(self, writer):
    104     """Write the description as XML to the given writer."""
    105     doc = self.GetDocument()
    106     doc.writexml(writer, addindent='  ', newl='\n', encoding=self.encoding)
    107     doc.unlink()
    108 
    109 
    110 class TestPlan(object):
    111   """A CTS test plan generator."""
    112 
    113   def __init__(self, all_packages):
    114     """Instantiate a test plan with a list of available package names.
    115 
    116     Args:
    117       all_packages: The full list of available packages. Subsequent calls to Exclude() and
    118           Include() select from the packages given here.
    119     """
    120     self.all_packages = all_packages
    121     self.map = None
    122 
    123   def Exclude(self, pattern):
    124     """Exclude all packages matching the given regular expression from the plan.
    125 
    126     If this is the first call to Exclude() or Include(), all packages are selected before applying
    127     the exclusion.
    128 
    129     Args:
    130       pattern: A regular expression selecting the package names to exclude.
    131     """
    132     if not self.map:
    133       self.Include('.*')
    134     exp = re.compile(pattern)
    135     for package in self.all_packages:
    136       if exp.match(package):
    137         self.map[package] = False
    138 
    139   def Include(self, pattern):
    140     """Include all packages matching the given regular expressions in the plan.
    141 
    142     Args:
    143       pattern: A regular expression selecting the package names to include.
    144     """
    145     if not self.map:
    146       self.map = {}
    147       for package in self.all_packages:
    148         self.map[package] = False
    149     exp = re.compile(pattern)
    150     for package in self.all_packages:
    151       if exp.match(package):
    152         self.map[package] = True
    153 
    154   def Write(self, file_name):
    155     """Write the test plan to the given file.
    156 
    157     Requires Include() or Exclude() to be called prior to calling this method.
    158 
    159     Args:
    160       file_name: The name of the file into which the test plan should be written.
    161     """
    162     doc = minidom.Document()
    163     plan = doc.createElement('TestPlan')
    164     plan.setAttribute('version', '1.0')
    165     doc.appendChild(plan)
    166     for package in self.all_packages:
    167       if self.map[package]:
    168         entry = doc.createElement('Entry')
    169         entry.setAttribute('uri', package)
    170         plan.appendChild(entry)
    171     stream = open(file_name, 'w')
    172     doc.writexml(stream, addindent='  ', newl='\n', encoding='UTF-8')
    173     stream.close()
    174 
    175 
    176 class XmlFile(object):
    177   """This class parses Xml files and allows reading attribute values by tag and attribute name."""
    178 
    179   def __init__(self, path):
    180     """Instantiate the class using the manifest file denoted by path."""
    181     self.doc = minidom.parse(path)
    182 
    183   def GetAndroidAttr(self, tag, attr_name):
    184     """Get the value of the given attribute in the first matching tag.
    185 
    186     Args:
    187       tag: The name of the tag to search.
    188       attr_name: An attribute name in the android manifest namespace.
    189 
    190     Returns:
    191       The value of the given attribute in the first matching tag.
    192     """
    193     element = self.doc.getElementsByTagName(tag)[0]
    194     return element.getAttributeNS('http://schemas.android.com/apk/res/android', attr_name)
    195 
    196   def GetAttr(self, tag, attr_name):
    197     """Return the value of the given attribute in the first matching tag.
    198 
    199     Args:
    200       tag: The name of the tag to search.
    201       attr_name: An attribute name in the default namespace.
    202 
    203     Returns:
    204       The value of the given attribute in the first matching tag.
    205     """
    206     element = self.doc.getElementsByTagName(tag)[0]
    207     return element.getAttribute(attr_name)
    208