aboutsummaryrefslogtreecommitdiff
path: root/test/functional/test_runner.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/test_runner.py')
-rwxr-xr-xtest/functional/test_runner.py69
1 files changed, 50 insertions, 19 deletions
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 37a3c1c602..85a5de6976 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -29,7 +29,7 @@ import re
import logging
# Formatting. Default colors to empty strings.
-BOLD, BLUE, RED, GREY = ("", ""), ("", ""), ("", ""), ("", "")
+BOLD, GREEN, RED, GREY = ("", ""), ("", ""), ("", ""), ("", "")
try:
# Make sure python thinks it can write unicode to its stdout
"\u2713".encode("utf_8").decode(sys.stdout.encoding)
@@ -41,11 +41,27 @@ except UnicodeDecodeError:
CROSS = "x "
CIRCLE = "o "
-if os.name == 'posix':
+if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393):
+ if os.name == 'nt':
+ import ctypes
+ kernel32 = ctypes.windll.kernel32
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
+ STD_OUTPUT_HANDLE = -11
+ STD_ERROR_HANDLE = -12
+ # Enable ascii color control to stdout
+ stdout = kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
+ stdout_mode = ctypes.c_int32()
+ kernel32.GetConsoleMode(stdout, ctypes.byref(stdout_mode))
+ kernel32.SetConsoleMode(stdout, stdout_mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+ # Enable ascii color control to stderr
+ stderr = kernel32.GetStdHandle(STD_ERROR_HANDLE)
+ stderr_mode = ctypes.c_int32()
+ kernel32.GetConsoleMode(stderr, ctypes.byref(stderr_mode))
+ kernel32.SetConsoleMode(stderr, stderr_mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
# primitive formatting on supported
# terminal via ANSI escape sequences:
BOLD = ('\033[0m', '\033[1m')
- BLUE = ('\033[0m', '\033[0;34m')
+ GREEN = ('\033[0m', '\033[0;32m')
RED = ('\033[0m', '\033[0;31m')
GREY = ('\033[0m', '\033[1;30m')
@@ -136,7 +152,7 @@ BASE_SCRIPTS = [
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
'wallet_importprunedfunds.py',
- 'rpc_zmq.py',
+ 'p2p_leak_tx.py',
'rpc_signmessage.py',
'feature_nulldummy.py',
'mempool_accept.py',
@@ -159,6 +175,7 @@ BASE_SCRIPTS = [
'rpc_getblockstats.py',
'p2p_fingerprint.py',
'feature_uacomment.py',
+ 'feature_filelock.py',
'p2p_unrequested_blocks.py',
'feature_includeconf.py',
'rpc_scantxoutset.py',
@@ -205,7 +222,7 @@ def main():
parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit')
parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.')
parser.add_argument('--keepcache', '-k', action='store_true', help='the default behavior is to flush the cache directory on startup. --keepcache retains the cache from the previous testrun.')
- parser.add_argument('--quiet', '-q', action='store_true', help='only print results summary and failure logs')
+ parser.add_argument('--quiet', '-q', action='store_true', help='only print dots, results summary and failure logs')
parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs")
parser.add_argument('--failfast', action='store_true', help='stop execution after the first test failure')
args, unknown_args = parser.parse_known_args()
@@ -227,6 +244,11 @@ def main():
# Create base test directory
tmpdir = "%s/test_runner_₿_🏃_%s" % (args.tmpdirprefix, datetime.datetime.now().strftime("%Y%m%d_%H%M%S"))
+
+ # If we fixed the command-line and filename encoding issue on Windows, these two lines could be removed
+ if config["environment"]["EXEEXT"] == ".exe":
+ tmpdir = "%s/test_runner_%s" % (args.tmpdirprefix, datetime.datetime.now().strftime("%Y%m%d_%H%M%S"))
+
os.makedirs(tmpdir)
logging.debug("Temporary test directory at %s" % tmpdir)
@@ -264,11 +286,13 @@ def main():
# Remove the test cases that the user has explicitly asked to exclude.
if args.exclude:
- exclude_tests = [re.sub("\.py$", "", test) + ".py" for test in args.exclude.split(',')]
+ exclude_tests = [test.split('.py')[0] for test in args.exclude.split(',')]
for exclude_test in exclude_tests:
- if exclude_test in test_list:
- test_list.remove(exclude_test)
- else:
+ # Remove <test_name>.py and <test_name>.py --arg from the test list
+ exclude_list = [test for test in test_list if test.split('.py')[0] == exclude_test]
+ for exclude_item in exclude_list:
+ test_list.remove(exclude_item)
+ if not exclude_list:
print("{}WARNING!{} Test '{}' not found in current test list.".format(BOLD[1], BOLD[0], exclude_test))
if not test_list:
@@ -297,7 +321,7 @@ def main():
enable_coverage=args.coverage,
args=passon_args,
combined_logs_len=args.combinedlogslen,
- failfast=args.failfast,
+ failfast=args.failfast
)
def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False):
@@ -340,17 +364,17 @@ def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=Fal
test_results = []
max_len_name = len(max(test_list, key=len))
-
- for _ in range(len(test_list)):
+ test_count = len(test_list)
+ for i in range(test_count):
test_result, testdir, stdout, stderr = job_queue.get_next()
test_results.append(test_result)
-
+ done_str = "{}/{} - {}{}{}".format(i + 1, test_count, BOLD[1], test_result.name, BOLD[0])
if test_result.status == "Passed":
- logging.debug("\n%s%s%s passed, Duration: %s s" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
+ logging.debug("%s passed, Duration: %s s" % (done_str, test_result.time))
elif test_result.status == "Skipped":
- logging.debug("\n%s%s%s skipped" % (BOLD[1], test_result.name, BOLD[0]))
+ logging.debug("%s skipped" % (done_str))
else:
- print("\n%s%s%s failed, Duration: %s s\n" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
+ print("%s failed, Duration: %s s\n" % (done_str, test_result.time))
print(BOLD[1] + 'stdout:\n' + BOLD[0] + stdout + '\n')
print(BOLD[1] + 'stderr:\n' + BOLD[0] + stderr + '\n')
if combined_logs_len and os.path.isdir(testdir):
@@ -359,7 +383,10 @@ def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=Fal
print('\n============')
print('{}Combined log for {}:{}'.format(BOLD[1], testdir, BOLD[0]))
print('============\n')
- combined_logs, _ = subprocess.Popen([sys.executable, os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate()
+ combined_logs_args = [sys.executable, os.path.join(tests_dir, 'combine_logs.py'), testdir]
+ if BOLD[0]:
+ combined_logs_args += ['--color']
+ combined_logs, _ = subprocess.Popen(combined_logs_args, universal_newlines=True, stdout=subprocess.PIPE).communicate()
print("\n".join(deque(combined_logs.splitlines(), combined_logs_len)))
if failfast:
@@ -446,6 +473,7 @@ class TestHandler:
log_stderr))
if not self.jobs:
raise IndexError('pop from empty list')
+ dot_count = 0
while True:
# Return first proc that finishes
time.sleep(.5)
@@ -466,9 +494,12 @@ class TestHandler:
status = "Failed"
self.num_running -= 1
self.jobs.remove(job)
-
+ clearline = '\r' + (' ' * dot_count) + '\r'
+ print(clearline, end='', flush=True)
+ dot_count = 0
return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr
print('.', end='', flush=True)
+ dot_count += 1
def kill_and_join(self):
"""Send SIGKILL to all jobs and block until all have ended."""
@@ -498,7 +529,7 @@ class TestResult():
def __repr__(self):
if self.status == "Passed":
- color = BLUE
+ color = GREEN
glyph = TICK
elif self.status == "Failed":
color = RED