mirror of
https://github.com/GothenburgBitFactory/timewarrior.git
synced 2025-06-26 10:54:28 +02:00

When some of the individual tests fail to run, the `make test` target would still pass since test/problems would not return an error which could hide the fact that there are problems in the test. With this change, if you do not have the python dateutil package, you will now get output like: 'test_totals.t' failed to run any tests. Passed: 975 Failed: 1 Unexpected successes: 0 Skipped: 0 Expected failures: 0 Runtime: 4.83 seconds Or, if you do not have UTF-8 encoding set in your language you will get something like: 'track.t' failed to run all tests. 'stop.t' failed to run all tests. 'start.t' failed to run all tests. 'test_totals.t' failed to run all tests. Passed: 941 Failed: 50 Unexpected successes: 0 Skipped: 0 Expected failures: 0 Runtime: 4.55 seconds
175 lines
5.4 KiB
Python
Executable file
175 lines
5.4 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
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(r"^# (?:./)?(\S+\.t)(?:\.exe)?$")
|
|
timestamp = re.compile(r"^# (\d+(?:\.\d+)?) ==>.*$")
|
|
|
|
expected_fail = re.compile(r"^not ok.*?#\s*TODO", re.I)
|
|
unexpected_pass = re.compile(r"^ok .*?#\s*TODO", re.I)
|
|
skip = re.compile(r"^ok .*?#\s*skip", re.I)
|
|
ok = re.compile(r"^ok ", re.I)
|
|
not_ok = re.compile(r"^not ok", re.I)
|
|
comment = re.compile(r"^#")
|
|
plan = re.compile(r"^1..(\d+)\s*(?:#.*)?$")
|
|
|
|
start = None
|
|
stop = None
|
|
filename = None
|
|
expected_test_count = 0
|
|
need_plan = False
|
|
|
|
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:
|
|
if filename:
|
|
if expected_test_count > 0:
|
|
print(color("'{}' failed to run all tests.".format(filename), "red"), file=sys.stderr)
|
|
errors[filename] += expected_test_count
|
|
elif need_plan:
|
|
print(color("'{}' failed to run any tests.".format(filename), "red"), file=sys.stderr)
|
|
errors[filename] += 1
|
|
filename = match.group(1)
|
|
need_plan = True
|
|
continue
|
|
|
|
match = plan.match(line)
|
|
if match:
|
|
expected_test_count = int(match.group(1))
|
|
need_plan = False
|
|
continue
|
|
|
|
match = expected_fail.match(line)
|
|
if match:
|
|
expected[filename] += 1
|
|
expected_test_count -= 1
|
|
continue
|
|
|
|
match = unexpected_pass.match(line)
|
|
if match:
|
|
unexpected[filename] += 1
|
|
expected_test_count -= 1
|
|
continue
|
|
|
|
match = skip.match(line)
|
|
if match:
|
|
skipped[filename] += 1
|
|
expected_test_count -= 1
|
|
continue
|
|
|
|
# It's important these come last, since they're subpatterns of the above
|
|
|
|
match = ok.match(line)
|
|
if match:
|
|
passed[filename] += 1
|
|
expected_test_count -= 1
|
|
continue
|
|
|
|
match = not_ok.match(line)
|
|
if match:
|
|
errors[filename] += 1
|
|
expected_test_count -= 1
|
|
continue
|
|
|
|
match = comment.match(line)
|
|
if match:
|
|
continue
|
|
|
|
# Uncomment if you want to see malformed things we caught as well...
|
|
# print(color("Malformed TAP (" + filename + "): " + line, "red"))
|
|
|
|
# Last line contains the ending timestamp
|
|
stop = float(timestamp.match(line).group(1))
|
|
|
|
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 encountered any failures, return non-zero code
|
|
sys.exit(1 if int(error_int) or int(unexpected_int) else 0)
|