Skip to content

Commit

Permalink
Reworking the reporting system a bit
Browse files Browse the repository at this point in the history
Fixes summary report colors #44
Partially addresses #30
  • Loading branch information
jmvrbanac committed Sep 8, 2014
1 parent 28d3c40 commit 9940839
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 184 deletions.
240 changes: 68 additions & 172 deletions specter/reporting/console.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,12 @@
import re
import six
from specter.spec import TestEvent, DescribeEvent, DataDescribe
from specter import _
from specter.reporting import AbstractConsoleReporter, AbstractSerialReporter


class TestStatus():
PASS = 'passed'
FAIL = 'failed'
SKIP = 'skipped'
INCOMPLETE = 'incomplete'
ERROR = 'error'


class ConsoleColors():
BLACK = 30
RED = 31
GREEN = 32
YELLOW = 33
BLUE = 34
MAGENTA = 35
CYAN = 36
WHITE = 37

# REMOVE THIS GLOBAL WHEN WE REFACTOR THIS
USE_COLOR = True
from specter.reporting.utils import (
TestStatus, print_expects, print_to_screen, get_color_from_status,
print_test_args, get_item_level, print_indent_msg, ConsoleColors)


class ConsoleReporter(AbstractConsoleReporter, AbstractSerialReporter):
""" Temporary console reporter.
At least until I can get something else written.
"""
""" Simple BDD Style console reporter. """

def __init__(self, output_docstrings=False, use_color=True):
super(ConsoleReporter, self).__init__()
Expand All @@ -45,28 +21,17 @@ def __init__(self, output_docstrings=False, use_color=True):
self.output_docstrings = output_docstrings

def get_name(self):
return 'Temporary Serial console reporter'
return 'Simple BDD Serial console reporter'

def process_arguments(self, args):
if args.no_color:
self.use_color = False
global USE_COLOR
USE_COLOR = False

def subscribe_to_describe(self, describe):
describe.add_listener(TestEvent.COMPLETE, self.event_received)
describe.add_listener(DescribeEvent.START, self.start_describe)

def event_received(self, evt):
test_case = evt.payload
level = get_item_level(test_case)
name = test_case.pretty_name
if level > 0:
name = u'\u221F {0}'.format(name)

def get_test_case_status(self, test_case, name):
status = TestStatus.FAIL
if (test_case.success
and not test_case.skipped and not test_case.incomplete):
and not test_case.skipped
and not test_case.incomplete):
status = TestStatus.PASS
elif test_case.incomplete:
status = TestStatus.INCOMPLETE
Expand All @@ -78,24 +43,13 @@ def event_received(self, evt):
elif test_case.error:
status = TestStatus.ERROR

print_test_msg(name, level, status)

print_test_args(test_case.execute_kwargs, level, status)

if test_case.doc and self.output_docstrings:
print_indent_msg(test_case.doc, level+1, status)

# Print error if it exists
if test_case.error:
for line in test_case.error:
print_test_msg(line, level+2, TestStatus.FAIL)

print_expects(test_case, level)
return status, name

# Add test to totals
def add_to_totals(self, test_case):
self.test_total += 1
if (test_case.success
and not test_case.skipped and not test_case.incomplete):
and not test_case.skipped
and not test_case.incomplete):
self.passed_tests += 1
elif test_case.skipped:
self.skipped_tests += 1
Expand All @@ -107,19 +61,67 @@ def event_received(self, evt):
self.failed_tests += 1
self.test_expects += len(test_case.expects)

def start_describe(self, evt):
def output_test_case_result(self, test_case, level):
name = test_case.pretty_name
if level > 0:
name = u'\u221F {0}'.format(name)

status, name = self.get_test_case_status(test_case, name)

self.output(name, level, status)
print_test_args(test_case.execute_kwargs, level, status,
self.use_color)

if test_case.doc and self.output_docstrings:
print_indent_msg(test_case.doc, level+1, status)

# Print error if it exists
if test_case.error:
for line in test_case.error:
self.output(line, level+2, TestStatus.FAIL)

#if status == TestStatus.FAIL:
print_expects(test_case, level, self.use_color)

def subscribe_to_describe(self, describe):
describe.add_listener(TestEvent.COMPLETE, self.test_complete)
describe.add_listener(DescribeEvent.START, self.start_spec)

def test_complete(self, evt):
test_case = evt.payload
level = get_item_level(test_case)

self.output_test_case_result(test_case, level)

self.add_to_totals(test_case)

def start_spec(self, evt):
level = get_item_level(evt.payload)
name = evt.payload.name
if level > 0:
name = u'\u221F {0}'.format(name)
print_indent_msg(name, level, color=ConsoleColors.GREEN)

# Output Spec name
color = ConsoleColors.GREEN if self.use_color else None
print_indent_msg(name, level, color=color)

# Output Docstrings if enabled
if evt.payload.doc and self.output_docstrings:
print_indent_msg(evt.payload.doc, level+1)

# Warn of duplicates
if isinstance(evt.payload, DataDescribe) and evt.payload.dup_count:
color = ConsoleColors.YELLOW if self.use_color else None
print_indent_msg('Warning: Noticed {0} duplicate data '
'set(s)'.format(evt.payload.dup_count),
level+1, color=ConsoleColors.YELLOW)
level+1, color=color)

def output(self, msg, indent, status=None):
""" Alias for print_indent_msg with color determined by status."""
color = None
if self.use_color:
color = get_color_from_status(status)
print_indent_msg(msg, indent, color)

def print_summary(self):
msg = """------- Summary --------
Expand All @@ -137,116 +139,10 @@ def print_summary(self):
errored=self.errored_tests)

status = TestStatus.FAIL
if self.failed_tests == 0:
if self.failed_tests == 0 and self.errored_tests == 0:
status = TestStatus.PASS

print_colored('\n')
print_test_msg('-'*24, 0, status)
print_test_msg(msg, 0, status)
print_test_msg('-'*24, 0, status)


def print_test_msg(msg, level, status=TestStatus.PASS):
color = ConsoleColors.RED
if status == TestStatus.PASS:
color = ConsoleColors.GREEN
elif status == TestStatus.SKIP:
color = ConsoleColors.YELLOW
elif status == TestStatus.INCOMPLETE:
color = ConsoleColors.MAGENTA

print_indent_msg(msg=msg, level=level, color=color)


def pretty_print_args(kwargs):
if kwargs is None:
return u'None'
first_seen = False
parts = []
for k, v in six.iteritems(kwargs):
if not first_seen:
first_seen = True
else:
parts.append(', ')
parts.append('{name} = {value}'.format(name=k, value=v))
return u''.join(parts)


def print_test_args(kwargs, level, status=TestStatus.PASS):
if kwargs and (status == TestStatus.ERROR or
status == TestStatus.FAIL):
msg = u''.join([u' Parameters: ', pretty_print_args(kwargs)])
print_test_msg(msg, level, status)


def print_indent_msg(msg, level=0, color=ConsoleColors.WHITE):
indent = u' ' * 2
msg = u'{0}{1}'.format(str(indent * level), msg)
print_colored(msg=msg, color=color)


def print_msg_list(msg_list, level, color=ConsoleColors.WHITE):
for msg in msg_list:
print_indent_msg(msg=msg, level=level, color=color)


def print_colored(msg, color=ConsoleColors.WHITE):
if USE_COLOR:
msg = u'\033[{color}m{msg}\033[0m'.format(color=color, msg=msg)

print(msg.encode('utf-8'))


def get_item_level(item):
levels = 0
parent_above = item.parent
while parent_above is not None:
levels += 1
parent_above = parent_above.parent
return levels


def print_expects(test_case, level):
# Print expects
for expect in test_case.expects:
mark = u'\u2718'

status = TestStatus.FAIL
if expect.success:
status = TestStatus.PASS
mark = u'\u2714'

expect_msg = u'{mark} {msg}'.format(mark=mark, msg=expect)

print_test_msg(expect_msg, level+1, status=status)

def hardcoded(param):
result = re.match('^(\'|"|\d)', str(param)) is not None
return result

def print_param(value, param, indent, prefix):
if not expect.success and not hardcoded(param):
msg_list = str(value).splitlines() or ['']
prefix = _('{0}: {1}').format(param or prefix, msg_list[0])
print_indent_msg(prefix, indent)
if len(msg_list) > 1:
print_msg_list(msg_list[1:], indent)

if expect.custom_msg:
print_test_msg(expect.custom_msg, level + 3, status=status)

# Print the target parameter
try:
print_param(expect.target, expect.target_src_param,
level + 3, 'Target')
except:
print_param('ERROR - Couldn\'t evaluate target value',
expect.target_src_param, level + 3, 'Target')

# Print the expected parameter
try:
print_param(expect.expected, expect.expected_src_param,
level + 3, 'Expected')
except:
print_param('ERROR - Couldn\'t evaluate expected value',
expect.expected_src_param, level + 3, 'Expected')
print_to_screen('\n')
self.output('-'*24, 0, status)
self.output(msg, 0, status)
self.output('-'*24, 0, status)
20 changes: 13 additions & 7 deletions specter/reporting/dots.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from specter.spec import TestEvent
from specter.reporting import AbstractConsoleReporter
from specter.reporting import AbstractParallelReporter
from specter.reporting.console import print_test_msg, print_test_args
from specter.reporting.console import print_expects, TestStatus
from specter.reporting.utils import print_test_msg, print_test_args
from specter.reporting.utils import print_expects, TestStatus


class DotsReporter(AbstractConsoleReporter, AbstractParallelReporter):
Expand All @@ -14,32 +14,38 @@ def __init__(self):
super(DotsReporter, self).__init__()
self.total = 0
self.failed_tests = []
self.use_color = True

def get_name(self):
return _('Dots Reporter')

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

def process_arguments(self, args):
if args.no_color:
self.use_color = False

def print_error(self, wrapper):
""" A crude way of output the errors for now. This needs to be
cleaned up into something better.
"""
level = 0
parent = wrapper.parent
while parent:
print_test_msg(parent.name, level, TestStatus.FAIL)
print_test_msg(parent.name, level, TestStatus.FAIL, self.use_color)
level += 1
parent = parent.parent

print_test_msg(wrapper.name, level, TestStatus.FAIL)
print_test_args(wrapper.execute_kwargs, level, TestStatus.FAIL)
print_test_msg(wrapper.name, level, TestStatus.FAIL, self.use_color)
print_test_args(wrapper.execute_kwargs, level, TestStatus.FAIL,
self.use_color)

if wrapper.error:
for line in wrapper.error:
print_test_msg(line, level+2, TestStatus.FAIL)
print_test_msg(line, level+2, TestStatus.FAIL, self.use_color)

print_expects(wrapper, level)
print_expects(wrapper, level, use_color=self.use_color)

def test_event(self, evt):
self.total += 1
Expand Down
Loading

0 comments on commit 9940839

Please sign in to comment.