Skip to content

Commit 9940839

Browse files
committed
Reworking the reporting system a bit
Fixes summary report colors #44 Partially addresses #30
1 parent 28d3c40 commit 9940839

File tree

5 files changed

+264
-184
lines changed

5 files changed

+264
-184
lines changed

specter/reporting/console.py

Lines changed: 68 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,12 @@
1-
import re
2-
import six
31
from specter.spec import TestEvent, DescribeEvent, DataDescribe
4-
from specter import _
52
from specter.reporting import AbstractConsoleReporter, AbstractSerialReporter
6-
7-
8-
class TestStatus():
9-
PASS = 'passed'
10-
FAIL = 'failed'
11-
SKIP = 'skipped'
12-
INCOMPLETE = 'incomplete'
13-
ERROR = 'error'
14-
15-
16-
class ConsoleColors():
17-
BLACK = 30
18-
RED = 31
19-
GREEN = 32
20-
YELLOW = 33
21-
BLUE = 34
22-
MAGENTA = 35
23-
CYAN = 36
24-
WHITE = 37
25-
26-
# REMOVE THIS GLOBAL WHEN WE REFACTOR THIS
27-
USE_COLOR = True
3+
from specter.reporting.utils import (
4+
TestStatus, print_expects, print_to_screen, get_color_from_status,
5+
print_test_args, get_item_level, print_indent_msg, ConsoleColors)
286

297

308
class ConsoleReporter(AbstractConsoleReporter, AbstractSerialReporter):
31-
""" Temporary console reporter.
32-
At least until I can get something else written.
33-
"""
9+
""" Simple BDD Style console reporter. """
3410

3511
def __init__(self, output_docstrings=False, use_color=True):
3612
super(ConsoleReporter, self).__init__()
@@ -45,28 +21,17 @@ def __init__(self, output_docstrings=False, use_color=True):
4521
self.output_docstrings = output_docstrings
4622

4723
def get_name(self):
48-
return 'Temporary Serial console reporter'
24+
return 'Simple BDD Serial console reporter'
4925

5026
def process_arguments(self, args):
5127
if args.no_color:
5228
self.use_color = False
53-
global USE_COLOR
54-
USE_COLOR = False
55-
56-
def subscribe_to_describe(self, describe):
57-
describe.add_listener(TestEvent.COMPLETE, self.event_received)
58-
describe.add_listener(DescribeEvent.START, self.start_describe)
59-
60-
def event_received(self, evt):
61-
test_case = evt.payload
62-
level = get_item_level(test_case)
63-
name = test_case.pretty_name
64-
if level > 0:
65-
name = u'\u221F {0}'.format(name)
6629

30+
def get_test_case_status(self, test_case, name):
6731
status = TestStatus.FAIL
6832
if (test_case.success
69-
and not test_case.skipped and not test_case.incomplete):
33+
and not test_case.skipped
34+
and not test_case.incomplete):
7035
status = TestStatus.PASS
7136
elif test_case.incomplete:
7237
status = TestStatus.INCOMPLETE
@@ -78,24 +43,13 @@ def event_received(self, evt):
7843
elif test_case.error:
7944
status = TestStatus.ERROR
8045

81-
print_test_msg(name, level, status)
82-
83-
print_test_args(test_case.execute_kwargs, level, status)
84-
85-
if test_case.doc and self.output_docstrings:
86-
print_indent_msg(test_case.doc, level+1, status)
87-
88-
# Print error if it exists
89-
if test_case.error:
90-
for line in test_case.error:
91-
print_test_msg(line, level+2, TestStatus.FAIL)
92-
93-
print_expects(test_case, level)
46+
return status, name
9447

95-
# Add test to totals
48+
def add_to_totals(self, test_case):
9649
self.test_total += 1
9750
if (test_case.success
98-
and not test_case.skipped and not test_case.incomplete):
51+
and not test_case.skipped
52+
and not test_case.incomplete):
9953
self.passed_tests += 1
10054
elif test_case.skipped:
10155
self.skipped_tests += 1
@@ -107,19 +61,67 @@ def event_received(self, evt):
10761
self.failed_tests += 1
10862
self.test_expects += len(test_case.expects)
10963

110-
def start_describe(self, evt):
64+
def output_test_case_result(self, test_case, level):
65+
name = test_case.pretty_name
66+
if level > 0:
67+
name = u'\u221F {0}'.format(name)
68+
69+
status, name = self.get_test_case_status(test_case, name)
70+
71+
self.output(name, level, status)
72+
print_test_args(test_case.execute_kwargs, level, status,
73+
self.use_color)
74+
75+
if test_case.doc and self.output_docstrings:
76+
print_indent_msg(test_case.doc, level+1, status)
77+
78+
# Print error if it exists
79+
if test_case.error:
80+
for line in test_case.error:
81+
self.output(line, level+2, TestStatus.FAIL)
82+
83+
#if status == TestStatus.FAIL:
84+
print_expects(test_case, level, self.use_color)
85+
86+
def subscribe_to_describe(self, describe):
87+
describe.add_listener(TestEvent.COMPLETE, self.test_complete)
88+
describe.add_listener(DescribeEvent.START, self.start_spec)
89+
90+
def test_complete(self, evt):
91+
test_case = evt.payload
92+
level = get_item_level(test_case)
93+
94+
self.output_test_case_result(test_case, level)
95+
96+
self.add_to_totals(test_case)
97+
98+
def start_spec(self, evt):
11199
level = get_item_level(evt.payload)
112100
name = evt.payload.name
113101
if level > 0:
114102
name = u'\u221F {0}'.format(name)
115-
print_indent_msg(name, level, color=ConsoleColors.GREEN)
103+
104+
# Output Spec name
105+
color = ConsoleColors.GREEN if self.use_color else None
106+
print_indent_msg(name, level, color=color)
107+
108+
# Output Docstrings if enabled
116109
if evt.payload.doc and self.output_docstrings:
117110
print_indent_msg(evt.payload.doc, level+1)
118111

112+
# Warn of duplicates
119113
if isinstance(evt.payload, DataDescribe) and evt.payload.dup_count:
114+
color = ConsoleColors.YELLOW if self.use_color else None
120115
print_indent_msg('Warning: Noticed {0} duplicate data '
121116
'set(s)'.format(evt.payload.dup_count),
122-
level+1, color=ConsoleColors.YELLOW)
117+
level+1, color=color)
118+
119+
def output(self, msg, indent, status=None):
120+
""" Alias for print_indent_msg with color determined by status."""
121+
color = None
122+
if self.use_color:
123+
color = get_color_from_status(status)
124+
print_indent_msg(msg, indent, color)
123125

124126
def print_summary(self):
125127
msg = """------- Summary --------
@@ -137,116 +139,10 @@ def print_summary(self):
137139
errored=self.errored_tests)
138140

139141
status = TestStatus.FAIL
140-
if self.failed_tests == 0:
142+
if self.failed_tests == 0 and self.errored_tests == 0:
141143
status = TestStatus.PASS
142144

143-
print_colored('\n')
144-
print_test_msg('-'*24, 0, status)
145-
print_test_msg(msg, 0, status)
146-
print_test_msg('-'*24, 0, status)
147-
148-
149-
def print_test_msg(msg, level, status=TestStatus.PASS):
150-
color = ConsoleColors.RED
151-
if status == TestStatus.PASS:
152-
color = ConsoleColors.GREEN
153-
elif status == TestStatus.SKIP:
154-
color = ConsoleColors.YELLOW
155-
elif status == TestStatus.INCOMPLETE:
156-
color = ConsoleColors.MAGENTA
157-
158-
print_indent_msg(msg=msg, level=level, color=color)
159-
160-
161-
def pretty_print_args(kwargs):
162-
if kwargs is None:
163-
return u'None'
164-
first_seen = False
165-
parts = []
166-
for k, v in six.iteritems(kwargs):
167-
if not first_seen:
168-
first_seen = True
169-
else:
170-
parts.append(', ')
171-
parts.append('{name} = {value}'.format(name=k, value=v))
172-
return u''.join(parts)
173-
174-
175-
def print_test_args(kwargs, level, status=TestStatus.PASS):
176-
if kwargs and (status == TestStatus.ERROR or
177-
status == TestStatus.FAIL):
178-
msg = u''.join([u' Parameters: ', pretty_print_args(kwargs)])
179-
print_test_msg(msg, level, status)
180-
181-
182-
def print_indent_msg(msg, level=0, color=ConsoleColors.WHITE):
183-
indent = u' ' * 2
184-
msg = u'{0}{1}'.format(str(indent * level), msg)
185-
print_colored(msg=msg, color=color)
186-
187-
188-
def print_msg_list(msg_list, level, color=ConsoleColors.WHITE):
189-
for msg in msg_list:
190-
print_indent_msg(msg=msg, level=level, color=color)
191-
192-
193-
def print_colored(msg, color=ConsoleColors.WHITE):
194-
if USE_COLOR:
195-
msg = u'\033[{color}m{msg}\033[0m'.format(color=color, msg=msg)
196-
197-
print(msg.encode('utf-8'))
198-
199-
200-
def get_item_level(item):
201-
levels = 0
202-
parent_above = item.parent
203-
while parent_above is not None:
204-
levels += 1
205-
parent_above = parent_above.parent
206-
return levels
207-
208-
209-
def print_expects(test_case, level):
210-
# Print expects
211-
for expect in test_case.expects:
212-
mark = u'\u2718'
213-
214-
status = TestStatus.FAIL
215-
if expect.success:
216-
status = TestStatus.PASS
217-
mark = u'\u2714'
218-
219-
expect_msg = u'{mark} {msg}'.format(mark=mark, msg=expect)
220-
221-
print_test_msg(expect_msg, level+1, status=status)
222-
223-
def hardcoded(param):
224-
result = re.match('^(\'|"|\d)', str(param)) is not None
225-
return result
226-
227-
def print_param(value, param, indent, prefix):
228-
if not expect.success and not hardcoded(param):
229-
msg_list = str(value).splitlines() or ['']
230-
prefix = _('{0}: {1}').format(param or prefix, msg_list[0])
231-
print_indent_msg(prefix, indent)
232-
if len(msg_list) > 1:
233-
print_msg_list(msg_list[1:], indent)
234-
235-
if expect.custom_msg:
236-
print_test_msg(expect.custom_msg, level + 3, status=status)
237-
238-
# Print the target parameter
239-
try:
240-
print_param(expect.target, expect.target_src_param,
241-
level + 3, 'Target')
242-
except:
243-
print_param('ERROR - Couldn\'t evaluate target value',
244-
expect.target_src_param, level + 3, 'Target')
245-
246-
# Print the expected parameter
247-
try:
248-
print_param(expect.expected, expect.expected_src_param,
249-
level + 3, 'Expected')
250-
except:
251-
print_param('ERROR - Couldn\'t evaluate expected value',
252-
expect.expected_src_param, level + 3, 'Expected')
145+
print_to_screen('\n')
146+
self.output('-'*24, 0, status)
147+
self.output(msg, 0, status)
148+
self.output('-'*24, 0, status)

specter/reporting/dots.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from specter.spec import TestEvent
55
from specter.reporting import AbstractConsoleReporter
66
from specter.reporting import AbstractParallelReporter
7-
from specter.reporting.console import print_test_msg, print_test_args
8-
from specter.reporting.console import print_expects, TestStatus
7+
from specter.reporting.utils import print_test_msg, print_test_args
8+
from specter.reporting.utils import print_expects, TestStatus
99

1010

1111
class DotsReporter(AbstractConsoleReporter, AbstractParallelReporter):
@@ -14,32 +14,38 @@ def __init__(self):
1414
super(DotsReporter, self).__init__()
1515
self.total = 0
1616
self.failed_tests = []
17+
self.use_color = True
1718

1819
def get_name(self):
1920
return _('Dots Reporter')
2021

2122
def subscribe_to_describe(self, describe):
2223
describe.add_listener(TestEvent.COMPLETE, self.test_event)
2324

25+
def process_arguments(self, args):
26+
if args.no_color:
27+
self.use_color = False
28+
2429
def print_error(self, wrapper):
2530
""" A crude way of output the errors for now. This needs to be
2631
cleaned up into something better.
2732
"""
2833
level = 0
2934
parent = wrapper.parent
3035
while parent:
31-
print_test_msg(parent.name, level, TestStatus.FAIL)
36+
print_test_msg(parent.name, level, TestStatus.FAIL, self.use_color)
3237
level += 1
3338
parent = parent.parent
3439

35-
print_test_msg(wrapper.name, level, TestStatus.FAIL)
36-
print_test_args(wrapper.execute_kwargs, level, TestStatus.FAIL)
40+
print_test_msg(wrapper.name, level, TestStatus.FAIL, self.use_color)
41+
print_test_args(wrapper.execute_kwargs, level, TestStatus.FAIL,
42+
self.use_color)
3743

3844
if wrapper.error:
3945
for line in wrapper.error:
40-
print_test_msg(line, level+2, TestStatus.FAIL)
46+
print_test_msg(line, level+2, TestStatus.FAIL, self.use_color)
4147

42-
print_expects(wrapper, level)
48+
print_expects(wrapper, level, use_color=self.use_color)
4349

4450
def test_event(self, evt):
4551
self.total += 1

0 commit comments

Comments
 (0)