Merge #16561: tests: Use colors and dots in test_runner.py output only if standard output is a terminal

37f2784952 tests: Use colors and dots in test_runner.py output only if standard output is a terminal -- allows for using the test runner output as input to other programs (practicalswift)

Pull request description:

  Use colors and dots in `test_runner.py` output only if standard output is a terminal -- allows for using the test runner output as input to other programs.

  I found the need for this when parsing `test_runner.py` output while investigating intermittent functional test failures.

  Before:

  ```
  $ test/functional/test_runner.py wallet_hd.py > output 2>&1
  $ less output
  Temporary test directory at /tmp/test_runner_₿_🏃_20190807_074115
  ESC[1mWARNING!ESC[0m There is already a bitcoind process running on this system. Tests may fail unexpectedly due to resource contention!
  Remaining jobs: [wallet_hd.py]
  .......................................^M                                       ^M1/1 - ESC[1mwallet_hd.pyESC[0m passed, Duration: 20 s

  ESC[1mTEST         | STATUS    | DURATION

  ESC[0mESC[0;32mwallet_hd.py | ✓ Passed  | 20 s
  ESC[0mESC[1m
  ALL          | ✓ Passed  | 20 s (accumulated)
  ESC[0mRuntime: 20 s
  ```

  After:

  ```
  $ test/functional/test_runner.py wallet_hd.py > output 2>&1
  $ less output
  Temporary test directory at /tmp/test_runner_₿_🏃_20190807_074244
  1/1 - wallet_hd.py passed, Duration: 20 s
  WARNING! There is already a bitcoind process running on this system. Tests may fail unexpectedly due to resource contention!
  Remaining jobs: [wallet_hd.py]

  TEST         | STATUS    | DURATION

  wallet_hd.py | ✓ Passed  | 20 s

  ALL          | ✓ Passed  | 20 s (accumulated)
  Runtime: 20 s
  ```

ACKs for top commit:
  laanwj:
    ACK 37f2784952

Tree-SHA512: f15d95f9e07de2954c326d63d7a4bcd2971faeaa00386600dec2fb915ec89475aeef1dbc968b2c12aa5e988d4b3ed1974d6da0b6a3f1e1a105cfd90e8cb97cf6
This commit is contained in:
MarcoFalke 2019-08-15 07:42:20 -04:00
commit e00501e00c
No known key found for this signature in database
GPG key ID: D2EA4850E7528B25

View file

@ -228,6 +228,7 @@ def main():
epilog=''' epilog='''
Help text and arguments for individual test script:''', Help text and arguments for individual test script:''',
formatter_class=argparse.RawTextHelpFormatter) formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('--ansi', action='store_true', default=sys.stdout.isatty(), help="Use ANSI colors and dots in output (enabled by default when standard output is a TTY)")
parser.add_argument('--combinedlogslen', '-c', type=int, default=0, metavar='n', help='On failure, print a log (of length n lines) to the console, combined from the test framework and all test nodes.') parser.add_argument('--combinedlogslen', '-c', type=int, default=0, metavar='n', help='On failure, print a log (of length n lines) to the console, combined from the test framework and all test nodes.')
parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface') parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface')
parser.add_argument('--ci', action='store_true', help='Run checks and code that are usually only enabled in a continuous integration environment') parser.add_argument('--ci', action='store_true', help='Run checks and code that are usually only enabled in a continuous integration environment')
@ -240,7 +241,14 @@ def main():
parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs") 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') parser.add_argument('--failfast', action='store_true', help='stop execution after the first test failure')
parser.add_argument('--filter', help='filter scripts to run by regular expression') parser.add_argument('--filter', help='filter scripts to run by regular expression')
args, unknown_args = parser.parse_known_args() args, unknown_args = parser.parse_known_args()
if not args.ansi:
global BOLD, GREEN, RED, GREY
BOLD = ("", "")
GREEN = ("", "")
RED = ("", "")
GREY = ("", "")
# args to be passed on always start with two dashes; tests are the remaining unknown args # args to be passed on always start with two dashes; tests are the remaining unknown args
tests = [arg for arg in unknown_args if arg[:2] != "--"] tests = [arg for arg in unknown_args if arg[:2] != "--"]
@ -342,9 +350,10 @@ def main():
combined_logs_len=args.combinedlogslen, combined_logs_len=args.combinedlogslen,
failfast=args.failfast, failfast=args.failfast,
runs_ci=args.ci, runs_ci=args.ci,
use_term_control=args.ansi,
) )
def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci): def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci, use_term_control):
args = args or [] args = args or []
# Warn if bitcoind is already running (unix only) # Warn if bitcoind is already running (unix only)
@ -386,6 +395,7 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
test_list=test_list, test_list=test_list,
flags=flags, flags=flags,
timeout_duration=40 * 60 if runs_ci else float('inf'), # in seconds timeout_duration=40 * 60 if runs_ci else float('inf'), # in seconds
use_term_control=use_term_control,
) )
start_time = time.time() start_time = time.time()
test_results = [] test_results = []
@ -469,7 +479,7 @@ class TestHandler:
Trigger the test scripts passed in via the list. Trigger the test scripts passed in via the list.
""" """
def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, timeout_duration): def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, timeout_duration, use_term_control):
assert num_tests_parallel >= 1 assert num_tests_parallel >= 1
self.num_jobs = num_tests_parallel self.num_jobs = num_tests_parallel
self.tests_dir = tests_dir self.tests_dir = tests_dir
@ -479,6 +489,7 @@ class TestHandler:
self.flags = flags self.flags = flags
self.num_running = 0 self.num_running = 0
self.jobs = [] self.jobs = []
self.use_term_control = use_term_control
def get_next(self): def get_next(self):
while self.num_running < self.num_jobs and self.test_list: while self.num_running < self.num_jobs and self.test_list:
@ -530,10 +541,12 @@ class TestHandler:
status = "Failed" status = "Failed"
self.num_running -= 1 self.num_running -= 1
self.jobs.remove(job) self.jobs.remove(job)
if self.use_term_control:
clearline = '\r' + (' ' * dot_count) + '\r' clearline = '\r' + (' ' * dot_count) + '\r'
print(clearline, end='', flush=True) print(clearline, end='', flush=True)
dot_count = 0 dot_count = 0
return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr
if self.use_term_control:
print('.', end='', flush=True) print('.', end='', flush=True)
dot_count += 1 dot_count += 1