• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
19import re
20import xml.dom.minidom as minidom
21
22
23class 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
110class 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
176class 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