Skip to content

Commit df1ca9e

Browse files
committed
Merge #10159: [tests] color test results and sort alphabetically
d80baaa fixup - align summary row correctly and make colors/glyphs globals (John Newbery) bb92d83 [tests] Add unicode symbols for tests passing/failing/skipping (John Newbery) 63062bd [tests] color test results and sort alphabetically (John Newbery) Tree-SHA512: a5b85c05480722abd6e483d1817b7527ca487b8bb8292bc81efba158df5a619b8103ed43b790396071ab0710f39457895a79460480044324798c81331bbade5a
2 parents a5fd746 + d80baaa commit df1ca9e

File tree

1 file changed

+68
-25
lines changed

1 file changed

+68
-25
lines changed

test/functional/test_runner.py

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@
2525
import re
2626
import logging
2727

28+
# Formatting. Default colors to empty strings.
29+
BOLD, BLUE, RED, GREY = ("", ""), ("", ""), ("", ""), ("", "")
30+
TICK = "✓ "
31+
CROSS = "✖ "
32+
CIRCLE = "○ "
33+
if os.name == 'posix':
34+
# primitive formatting on supported
35+
# terminal via ANSI escape sequences:
36+
BOLD = ('\033[0m', '\033[1m')
37+
BLUE = ('\033[0m', '\033[0;34m')
38+
RED = ('\033[0m', '\033[0;31m')
39+
GREY = ('\033[0m', '\033[1;30m')
40+
2841
TEST_EXIT_PASSED = 0
2942
TEST_EXIT_SKIPPED = 77
3043

@@ -231,11 +244,6 @@ def main():
231244
run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], args.jobs, args.coverage, passon_args)
232245

233246
def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=False, args=[]):
234-
BOLD = ("","")
235-
if os.name == 'posix':
236-
# primitive formatting on supported
237-
# terminal via ANSI escape sequences:
238-
BOLD = ('\033[0m', '\033[1m')
239247

240248
#Set env vars
241249
if "BITCOIND" not in os.environ:
@@ -258,42 +266,55 @@ def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=Fal
258266
subprocess.check_output([tests_dir + 'create_cache.py'] + flags)
259267

260268
#Run Tests
261-
all_passed = True
262-
time_sum = 0
263-
time0 = time.time()
264-
265269
job_queue = TestHandler(jobs, tests_dir, test_list, flags)
270+
time0 = time.time()
271+
test_results = []
266272

267273
max_len_name = len(max(test_list, key=len))
268-
results = "\n" + BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "STATUS ", "DURATION") + BOLD[0]
274+
269275
for _ in range(len(test_list)):
270-
(name, stdout, stderr, status, duration) = job_queue.get_next()
271-
all_passed = all_passed and status != "Failed"
272-
time_sum += duration
273-
274-
if status == "Passed":
275-
logging.debug("\n%s%s%s passed, Duration: %s s" % (BOLD[1], name, BOLD[0], duration))
276-
elif status == "Skipped":
277-
logging.debug("\n%s%s%s skipped" % (BOLD[1], name, BOLD[0]))
276+
test_result, stdout, stderr = job_queue.get_next()
277+
test_results.append(test_result)
278+
279+
if test_result.status == "Passed":
280+
logging.debug("\n%s%s%s passed, Duration: %s s" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
281+
elif test_result.status == "Skipped":
282+
logging.debug("\n%s%s%s skipped" % (BOLD[1], test_result.name, BOLD[0]))
278283
else:
279-
print("\n%s%s%s failed, Duration: %s s\n" % (BOLD[1], name, BOLD[0], duration))
284+
print("\n%s%s%s failed, Duration: %s s\n" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
280285
print(BOLD[1] + 'stdout:\n' + BOLD[0] + stdout + '\n')
281286
print(BOLD[1] + 'stderr:\n' + BOLD[0] + stderr + '\n')
282287

283-
results += "%s | %s | %s s\n" % (name.ljust(max_len_name), status.ljust(7), duration)
284-
285-
results += BOLD[1] + "\n%s | %s | %s s (accumulated)" % ("ALL".ljust(max_len_name), str(all_passed).ljust(7), time_sum) + BOLD[0]
286-
print(results)
287-
print("\nRuntime: %s s" % (int(time.time() - time0)))
288+
print_results(test_results, max_len_name, (int(time.time() - time0)))
288289

289290
if coverage:
290291
coverage.report_rpc_coverage()
291292

292293
logging.debug("Cleaning up coverage data")
293294
coverage.cleanup()
294295

296+
all_passed = all(map(lambda test_result: test_result.status == "Passed", test_results))
297+
295298
sys.exit(not all_passed)
296299

300+
def print_results(test_results, max_len_name, runtime):
301+
results = "\n" + BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "STATUS ", "DURATION") + BOLD[0]
302+
303+
test_results.sort(key=lambda result: result.name.lower())
304+
all_passed = True
305+
time_sum = 0
306+
307+
for test_result in test_results:
308+
all_passed = all_passed and test_result.status != "Failed"
309+
time_sum += test_result.time
310+
test_result.padding = max_len_name
311+
results += str(test_result)
312+
313+
status = TICK + "Passed" if all_passed else CROSS + "Failed"
314+
results += BOLD[1] + "\n%s | %s | %s s (accumulated) \n" % ("ALL".ljust(max_len_name), status.ljust(9), time_sum) + BOLD[0]
315+
results += "Runtime: %s s\n" % (runtime)
316+
print(results)
317+
297318
class TestHandler:
298319
"""
299320
Trigger the testscrips passed in via the list.
@@ -348,9 +369,31 @@ def get_next(self):
348369
status = "Failed"
349370
self.num_running -= 1
350371
self.jobs.remove(j)
351-
return name, stdout, stderr, status, int(time.time() - time0)
372+
373+
return TestResult(name, status, int(time.time() - time0)), stdout, stderr
352374
print('.', end='', flush=True)
353375

376+
class TestResult():
377+
def __init__(self, name, status, time):
378+
self.name = name
379+
self.status = status
380+
self.time = time
381+
self.padding = 0
382+
383+
def __repr__(self):
384+
if self.status == "Passed":
385+
color = BLUE
386+
glyph = TICK
387+
elif self.status == "Failed":
388+
color = RED
389+
glyph = CROSS
390+
elif self.status == "Skipped":
391+
color = GREY
392+
glyph = CIRCLE
393+
394+
return color[1] + "%s | %s%s | %s s\n" % (self.name.ljust(self.padding), glyph, self.status.ljust(7), self.time) + color[0]
395+
396+
354397
def check_script_list(src_dir):
355398
"""Check scripts directory.
356399

0 commit comments

Comments
 (0)