2
This module contains the functionality related to the discovery of the test suites
5
import xml.etree.ElementTree as etree
11
Application description data
13
name_pattern = r"[a-z0-9][-_a-z0-9+.]*"
14
name_regex = re.compile(name_pattern)
18
def __init__(self, path, filenames):
20
self.filenames = filenames
22
self.name = os.path.basename(path)
25
def __eq__(self, other):
27
Two applications are considered to be equal if they have the
30
return (type(self) == type(other)
31
and self.name == other.name)
34
def name_matches(self):
36
Return True if the application name
37
honors the expected pattern
39
return self.name_regex.match(self.name)
44
Return a generator for all suites
46
return SuiteData.discover(self)
49
def get_target_directory(self, base_target_directory):
51
Return application target_directory
53
return os.path.join(base_target_directory,
57
def discover(cls, base_dirpaths):
59
Generator that discovers all applications under
60
a list of top directories
62
discovered_applications = []
63
for base_dirpath in base_dirpaths:
64
dirpaths = [os.path.join(base_dirpath, d)
65
for d in os.listdir(base_dirpath)
66
if os.path.isdir(os.path.join(base_dirpath, d))]
68
for dirpath in dirpaths:
71
for f in os.listdir(dirpath)
72
if os.path.isfile(os.path.join(dirpath, f))]
74
continue # Permission denied.
76
# Application directories are expected to honor
77
# the specified name pattern
78
app = cls(dirpath, filenames)
79
if not app.name_matches():
80
logging.debug("Application name %s does not match pattern: %s"
81
% (app.name, app.name_pattern))
84
# This check makes sure that the same application
85
# isn't discovered twice. That is to say, when discovering
86
# applications from multiple directories, the test cases
87
# from the application that is first found will be
88
# executed while the others will be discarded
89
if app in discovered_applications:
90
logging.debug("Application name %s has been already discovered"
94
# Return application only if there is no whitelist
95
# or if it matches any of the whitelist names
96
if cls.whitelist and not app.name in cls.whitelist:
97
logging.debug("Application name %s has not been whitelisted"
101
# At least one '.xml' file with a 'suite' root tag
102
# should be contained in the directory to be a valid application directory
103
if not any(app.suites()):
104
logging.debug("Application directory %s does't seem to contain a valid suite file"
108
discovered_applications.append(app)
114
Common methods for XML test data
119
Return suite name as written in the xml file
121
return self.root.attrib['name']
127
Return suite arguments as a dictionary
129
args_tag = self.root.find('args')
134
return dict([(arg.tag.encode('ascii'), (arg.text or '').strip())
135
for arg in args_tag])
139
def description(self):
141
Return suite description as written in the xml file
143
return self.root.find('description').text.strip()
146
def add_results(self, results):
148
Add results to the xml data of the test case
149
to generate later report easily
154
result_tag = etree.SubElement(self.root, 'result')
155
for key, values in results.items():
157
new_result_tag = etree.SubElement(result_tag, key)
158
new_result_tag.text = str(value)
161
class SuiteData(XmlData):
163
Suite description data
165
name_whitelist = None
166
filename_whitelist = None
169
def __init__(self, application, filename):
170
self.application = application
171
self.filename = filename
172
self.fullname = os.path.join(application.path,
176
self.tree = etree.parse(self.fullname)
177
self.root = self.tree.getroot()
185
Return suite instance from the python module
187
module_name, class_name = self.root.find('class').text.rsplit('.', 1)
188
logging.debug("Module name: %s", module_name)
190
# Suite file and module are expected to be in the same directory
191
load_args = imp.find_module(
192
module_name, [os.path.dirname(self.fullname)])
194
module = imp.load_module(module_name, *load_args)
196
cls = getattr(module, class_name)
197
return cls(**self.args)
200
def get_log_filename(self, application_target_directory):
202
Return log filename under application target directory
205
application_target_directory,
206
"%s.log" % os.path.basename(os.path.splitext(self.filename)[0]))
210
Generator for all test cases in the suite
212
return CaseData.discover(self)
215
def __eq__(self, other):
217
A suite is compared against its filename
218
or against its own name (useful for filtering)
220
if type(other) == str:
221
other_filename, other_ext = os.path.splitext(other)
224
return other == self.filename
226
return other_filename == os.path.splitext(self.filename)[0]
228
return self.filename == other.filename
231
def has_valid_xml(self):
233
Return true if xml could be parsed
234
and the root tag is 'suite'
237
and self.tree.getroot().tag == 'suite')
241
def discover(cls, app):
243
Discover suites inside of an application
245
# All test suites will be defined by an xml file
246
xml_filenames = (filename
247
for filename in app.filenames
248
if filename.endswith('xml'))
250
# Discovered suites must contain a valid xml content
251
# and at least one test cases
252
suites = (suite for suite in (cls(app, filename)
253
for filename in xml_filenames)
254
if suite.has_valid_xml() and any(suite.cases()))
257
# Filter suites using the whitelists provided through the
259
if cls.name_whitelist or cls.filename_whitelist:
260
suites = (suite for suite in suites
261
if suite.name in cls.name_whitelist
262
or suite in cls.filename_whitelist)
267
class CaseData(XmlData):
269
Test case description data
273
def __init__(self, suite, root):
279
def methodname(self):
281
Test method according to xml data
283
return self.root.find('method').text
287
def discover(cls, suite):
289
Discover all test cases in a suite
291
cases = (cls(suite, tree)
292
for tree in suite.tree.findall('case'))
295
cases = (case for case in cases
296
if case.name in cls.whitelist)
301
def discover_applications(top_directories,
302
filtering_applications,
303
filtering_suite_names,
304
filtering_suite_files,
307
Discover all applications and filter them properly
309
# Configure filtering options
310
ApplicationData.whitelist = filtering_applications
311
SuiteData.name_whitelist = filtering_suite_names
312
SuiteData.filename_whitelist = filtering_suite_files
313
CaseData.whitelist = filtering_cases
315
# Discover all applications under top directories
316
discovered_apps = ApplicationData.discover(top_directories)
318
return discovered_apps