-
Notifications
You must be signed in to change notification settings - Fork 25
/
testreporter.py
192 lines (163 loc) · 7.02 KB
/
testreporter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/python2
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
#
# Copyright (C) 2012- Keith Dart <keith@dartworks.biz>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
from __future__ import absolute_import
from __future__ import print_function
from __future__ import division
"""
Report on tests, and maintain database synchronization.
"""
import sys
import os
from pycopia import methods
from pycopia.db import models
from pycopia.QA import core
from pycopia.QA import testinspector
ModuleType = type(sys)
TypeType = type
class TestReporterError(Exception):
pass
REPORT_MODULE="""
Test module named "{name}",
code loaded from file {file}. """
REPORT_TESTSUITE = """
A test suite object {objname}, from module "{module}", named "%Y{name}%N", """
REPORT_TEST = """
A test named {module}.%y{name}%N.
The execute method signature is '{sig}'.
It may be added to a suite like this:
suite.add_test({name}, {args})"""
REPORT_CLASS = """
A class {module}.%g{name}%N,
The file where it is defined is {file}. """
REPORT_GLOBAL = """
The base directory where test run artifacts are placed is {resultsdirbase!r}.
The base of the URL where a web browser may access them is {baseurl!r}.
The 'documentroot' value, where other web pages are placed, is {documentroot!r}.
"""
class TestReporter(object):
"""Reports on test objects.
The style is similar to a test runner, but instead of running runnable
objects it reports on them to the userinterface object.
"""
def __init__(self, config):
self.config = config
config.username = os.environ["USER"]
def report_global(self):
"""Report common information.
Send some information to the user interface about the available
parameters that a user may provide to run a test.
"""
cf = self.config
db = cf.session
ui = cf.UI
ui.printf("%YAvailable report names for the '%G--reportname=%N' %Yoption%N:")
ui.print_list(sorted(cf.reports.keys()))
ui.Print("\n")
ui.printf("%YAvailable environment names for the '%G--environmentname=%N' %Yoption%N:")
ui.print_list(sorted([env.name for env in db.query(models.Environment).all()]))
ui.printf(REPORT_GLOBAL.format(
resultsdirbase=cf.get("resultsdirbase"),
baseurl=cf.get("baseurl"),
documentroot=cf.get("documentroot"),
))
def report_objects(self, objects):
for obj in objects:
objecttype = type(obj)
if objecttype is ModuleType and hasattr(obj, "run"):
self.report_module(obj)
elif objecttype is TypeType and issubclass(obj, core.Test):
self.report_test(obj)
elif objecttype is TypeType and hasattr(obj, "run"):
# A bare class uses as a subcontainer of test or suite
# constructor, typically a UseCase class.
self.report_class(obj)
elif isinstance(obj, core.TestSuite):
self.report_testsuite(obj)
else:
self.config.UI.warning("{!r} is not a runnable object.".format(obj))
def report_tests(self, testcaselist):
for tc in testcaselist:
self.report_test(tc)
self.config.UI.Print("\n")
def report_errors(self, errlist):
ui = self.config.UI
ui.error('Some modules failed to load.')
for err in errlist:
ui.Print(" ", err)
def report_module(self, mod):
ui = self.config.UI
ui.printf(REPORT_MODULE.format(name=mod.__name__, file=mod.__file__))
ui.Print("This module defines the following test cases.")
clsdict = testinspector.find_classes(mod.__name__, findall=False)
for name, params in clsdict.items():
ui.printf(" %y{name}%N {using}".format(name=name, using="with config parameters:" if params else "using no config parameters."))
if params:
for var, cname, defaultval in params:
ui.Print(' variable "{var}" is "{cname}" from the config, default value is {default!r}'.format(
var=var,
cname=cname,
default=defaultval,
))
if self.config.flags.VERBOSE:
if hasattr(mod, "get_suite"):
ui.Print("This module builds the following test suite.")
suite = mod.get_suite(self.config)
self.report_testsuite(suite)
else:
ui.Print("No get_suite function in this module.")
def report_class(self, usecase):
ui = self.config.UI
ui.printf(REPORT_CLASS.format(name=usecase.__name__, module=usecase.__module__,
file=sys.modules[usecase.__module__].__file__))
if hasattr(usecase, "get_suite"):
ui.Print("It has a suite constructor (method named 'get_suite').")
if self.config.flags.VERBOSE:
ui.Print("It returns:")
suite = usecase.get_suite(self.config)
self.report_testsuite(suite)
else:
ui.Print("Some class that we don't know what to do with.")
def report_testsuite(self, suite, level=1):
ui = self.config.UI
indent = " " * level
cls = suite.__class__
ui.printf(REPORT_TESTSUITE.format(objname=cls.__name__, name=suite.test_name, module=cls.__module__))
ui.Print(" "*(level-1), "Suite has the following test cases added.")
for tcentry in suite:
ui.Print(indent, repr(tcentry))
if isinstance(tcentry, core.SuiteEntry):
ui.Print(indent, "Nested suite:")
self.report_testsuite(tcentry.inst, level+1)
def report_test(self, testcase):
ui = self.config.UI
exm = getattr(testcase, "execute")
sig = methods.MethodSignature(exm)
ui.printf(REPORT_TEST.format(
name=testcase.__name__,
module=testcase.__module__,
sig=sig,
args=", ".join(map(str, sig.args)+map(lambda t: "%s=%r" % t, sig.kwargs.items())),
))
params = testinspector.get_class(testcase)
if params:
ui.Print(" parameters from config:")
for var, cname, defaultval in params:
ui.Print(' variable "{var}" is "{cname}" from the config, default value is {default!r}'.format(
var=var,
cname=cname,
default=defaultval,
))
else:
ui.Print(" no config parameters used.")