#!/usr/bin/env python from __future__ import print_function import sys import re import argparse from collections import defaultdict def color(text, c): """ Add color on the keyword that identifies the state of the test """ if sys.stdout.isatty(): clear = "\033[0m" colors = { "red": "\033[1m\033[91m", "yellow": "\033[1m\033[93m", "green": "\033[1m\033[92m", } return colors[c] + text + clear else: return text def parse_args(): parser = argparse.ArgumentParser(description="Report on test results") parser.add_argument('--summary', action="store_true", help="Display only the totals in each category") parser.add_argument('tapfile', default="all.log", nargs="?", help="File containing TAP output") return parser.parse_args() def print_category(tests): if not cmd_args.summary: for key in sorted(tests): print("%-32s %4d" % (key, tests[key])) def pad(i): return " " * i if __name__ == "__main__": cmd_args = parse_args() errors = defaultdict(int) skipped = defaultdict(int) expected = defaultdict(int) unexpected = defaultdict(int) passed = defaultdict(int) file = re.compile("^# (?:./)?(\S+\.t)(?:\.exe)?$") timestamp = re.compile("^# (\d+(?:\.\d+)?) ==>.*$") start = None stop = None with open(cmd_args.tapfile) as fh: for line in fh: if start is None: # First line contains the starting timestamp start = float(timestamp.match(line).group(1)) continue match = file.match(line) if match: filename = match.group(1) if line.startswith("ok "): passed[filename] += 1 if line.startswith("not "): errors[filename] += 1 if line.startswith("skip "): skipped[filename] += 1 if line.startswith("# EXPECTED_FAILURE:"): expected[filename] += 1 if line.startswith("# UNEXPECTED_SUCCESS:"): unexpected[filename] += 1 # Last line contains the ending timestamp stop = float(timestamp.match(line).group(1)) # Remove expected failures from the skipped tests category for filename, value in expected.items(): if skipped[filename] == value: del skipped[filename] else: skipped[filename] -= value v = "{0:>5d}" passed_str = "Passed:" + pad(24) passed_int = v.format(sum(passed.values())) error_str = "Failed:" + pad(24) error_int = v.format(sum(errors.values())) unexpected_str = "Unexpected successes:" + pad(10) unexpected_int = v.format(sum(unexpected.values())) skipped_str = "Skipped:" + pad(23) skipped_int = v.format(sum(skipped.values())) expected_str = "Expected failures:" + pad(13) expected_int = v.format(sum(expected.values())) runtime_str = "Runtime:" + pad(20) runtime_int = "{0:>8.2f} seconds".format(stop - start) if cmd_args.summary: print(color(passed_str, "green"), passed_int) print(color(error_str, "red"), error_int) print(color(unexpected_str, "red"), unexpected_int) print(color(skipped_str, "yellow"), skipped_int) print(color(expected_str, "yellow"), expected_int) print(runtime_str, runtime_int) else: print(color(error_str, "red")) print_category(errors) print() print(color(unexpected_str, "red")) print_category(unexpected) print() print(color(skipped_str, "yellow")) print_category(skipped) print() print(color(expected_str, "yellow")) print_category(expected) # If we encoutered any failures, return non-zero code sys.exit(1 if error_int or unexpected_int else 0)