Created
March 23, 2014 04:55
-
-
Save oakfang/9719014 to your computer and use it in GitHub Desktop.
pytest-tree
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import colorama as color | |
from os import path | |
import pytest | |
from _pytest.terminal import TerminalReporter | |
PASSED = 0 | |
FAILED = 2 | |
OTHER = 1 | |
COLORS = {PASSED: "GREEN", | |
FAILED: "RED", | |
OTHER: "YELLOW"} | |
STATE = {PASSED: "PASSED", | |
FAILED: "BROKEN", | |
OTHER: "INCOMPLETE"} | |
def pytest_addoption(parser): | |
group = parser.getgroup("terminal reporting", "reporting", after="general") | |
group._addoption( | |
'--tree', action="store_true", dest="vtest", default=False, | |
help=( | |
"show test results as a virtual tree (disabled by " | |
"default)." | |
) | |
) | |
@pytest.mark.trylast | |
def pytest_configure(config): | |
if hasattr(config, 'slaveinput'): | |
return # xdist slave, we are already active on the master | |
if config.option.vtest: | |
# Get the standard terminal reporter plugin... | |
standard_reporter = config.pluginmanager.getplugin('terminalreporter') | |
vtest_reporter = VtestTerminalReporter(standard_reporter) | |
config.pluginmanager.unregister(standard_reporter) | |
config.pluginmanager.register(vtest_reporter, 'terminalreporter') | |
class VtestTerminalReporter(TerminalReporter): | |
def __init__(self, reporter): | |
TerminalReporter.__init__(self, reporter.config) | |
self._tw = reporter._tw | |
def pytest_runtestloop(self, session): | |
if session.config.option.collectonly: | |
return True | |
#self.suite = Suite() | |
self.suite = ProjectNode("ROOT") | |
for i, item in enumerate(session.items): | |
try: | |
nextitem = session.items[i+1] | |
except IndexError: | |
nextitem = None | |
item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) | |
if nextitem is None: | |
print_test_tree(self.write_line, self.suite, session.config.option.verbose) | |
if session.shouldstop: | |
raise session.Interrupted(session.shouldstop) | |
return True | |
def pytest_runtest_logstart(nodeid, location): | |
pass | |
def pytest_runtest_logreport(self, report): | |
# Show failures and errors occuring during running a test | |
# instantly. | |
if report.when != 'call': | |
return | |
import pdb; pdb.set_trace() | |
test_path, line, test_name = report.location | |
result = report.outcome | |
directory, module = path.split(test_path) | |
self.suite.add_test(directory, module, test_name, result.upper()) | |
class Colorize(object): | |
def __enter__(self): | |
color.init() | |
return self | |
def __exit__(self, *args): | |
color.deinit() | |
def cprint(self, text, clr): | |
return getattr(color.Fore, clr) + text | |
def colorize_list(clz, lst, chr="#", prefix="[", suffix="]"): | |
buff = "" | |
if prefix: | |
buff += clz.cprint(prefix, "WHITE") | |
for item in lst: | |
buff += clz.cprint(chr, COLORS.get(item, "BLUE")) | |
if suffix: | |
buff += clz.cprint(suffix, "WHITE") | |
return buff | |
class Node(object): | |
def __init__(self, value, data=None): | |
self.value = value | |
self.__children = {} | |
self.parent = None | |
self.data = data | |
@property | |
def children(self): | |
return self.__children.values() | |
def add(self, node): | |
node.parent = self | |
if node.value not in self.__children: | |
self.__children[node.value] = node | |
return self.__children[node.value] | |
@property | |
def is_leaf(self): | |
return not bool(self.__children) | |
@property | |
def is_root(self): | |
return self.parent is None | |
@property | |
def nodes(self): | |
return filter(lambda n: not n.is_leaf, self.children) | |
@property | |
def leaves(self): | |
return filter(lambda n: n.is_leaf, not self.children) | |
@property | |
def root(self): | |
if self.parent is None: | |
return self | |
parent = self.parent | |
while parent.parent is not None: | |
parent = parent.parent | |
return parent | |
def __str__(self): | |
buff = str(self.value) | |
if self.data: | |
buff += "\t" + str(self.data) | |
return buff | |
def __hash__(self): | |
return hash(self.value) | |
class TestLeaf(Node): | |
def __init__(self, test, result): | |
Node.__init__(self, test, data=result) | |
@property | |
def state(self): | |
return COLORS[self.summary] | |
@property | |
def summary(self): | |
if self.data == "PASSED": | |
return PASSED | |
if self.data in ("ERROR", "FAILED"): | |
return FAILED | |
return OTHER | |
class ModuleNode(Node): | |
def add_test(self, test, result): | |
self.add(TestLeaf(test, result)) | |
@property | |
def state(self): | |
return COLORS[max(self.summary)] | |
@property | |
def summary(self): | |
return [node.summary for node in self.children] | |
class DirectoryNode(ModuleNode): | |
def add_test(self, module, test, result): | |
mod = self.add(ModuleNode(module)) | |
mod.add_test(test, result) | |
@property | |
def summary(self): | |
return [max(node.summary) for node in self.children] | |
class ProjectNode(DirectoryNode): | |
def add_test(self, directory, module, test, result): | |
dr = self.add(DirectoryNode(directory)) | |
dr.add_test(module, test, result) | |
def __str__(self): | |
return "PROJECT " + STATE[max(self.summary)] | |
def print_test_tree(writer, root, verbose=False, indent=""): | |
with Colorize() as clz: | |
title = clz.cprint(indent + str(root), root.state) | |
if verbose and not root.is_leaf: | |
title += " " + colorize_list(clz, root.summary) | |
writer(title) | |
if not root.is_root: | |
indent = indent + '\t' | |
for node in root.children: | |
print_test_tree(writer, node, verbose, indent) | |
#SETUP | |
from setuptools import setup | |
setup( | |
name="vtest", | |
py_modules=['vtest'], | |
# the following makes a plugin available to pytest | |
entry_points={ | |
'pytest11': [ | |
'vtest = vtest', | |
] | |
}, | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment