Skip to content

Commit 74254fe

Browse files
author
MarcoFalke
committed
Merge #14683: tests: better combine_logs.py behavior
4aabadb tests: have combine_logs default to most recent test dir (James O'Beirne) Pull request description: Have `combine_logs.py` default to the most recent test directory if no argument is provided. This allows you to avoid an annoying copy-paste when iterating on a failing test, since you can do something like ```sh alias testlogs='./test/functional/combine_logs.py -c | less' ./test/functional/some_test.py # fails testlogs ``` Tree-SHA512: 919642ab09c314888a23c9491963b35b9da87e60deb740d1d5e816444aa9bdda5e519dc8ca131669f2d563167ef5f5abb14e22f20f47bf8362915ed578181846
2 parents 60b20c8 + 4aabadb commit 74254fe

File tree

2 files changed

+51
-9
lines changed

2 files changed

+51
-9
lines changed

test/functional/combine_logs.py

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
"""Combine logs from multiple bitcoin nodes as well as the test_framework log.
33
44
This streams the combined log output to stdout. Use combine_logs.py > outputfile
5-
to write to an outputfile."""
5+
to write to an outputfile.
6+
7+
If no argument is provided, the most recent test directory will be used."""
68

79
import argparse
810
from collections import defaultdict, namedtuple
@@ -11,6 +13,13 @@
1113
import os
1214
import re
1315
import sys
16+
import tempfile
17+
18+
# N.B.: don't import any local modules here - this script must remain executable
19+
# without the parent module installed.
20+
21+
# Should match same symbol in `test_framework.test_framework`.
22+
TMPDIR_PREFIX = "bitcoin_func_test_"
1423

1524
# Matches on the date format at the start of the log event
1625
TIMESTAMP_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{6})?Z")
@@ -19,22 +28,30 @@
1928

2029
def main():
2130
"""Main function. Parses args, reads the log files and renders them as text or html."""
22-
23-
parser = argparse.ArgumentParser(usage='%(prog)s [options] <test temporary directory>', description=__doc__)
31+
parser = argparse.ArgumentParser(
32+
description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
33+
parser.add_argument(
34+
'testdir', nargs='?', default='',
35+
help=('temporary test directory to combine logs from. '
36+
'Defaults to the most recent'))
2437
parser.add_argument('-c', '--color', dest='color', action='store_true', help='outputs the combined log with events colored by source (requires posix terminal colors. Use less -r for viewing)')
2538
parser.add_argument('--html', dest='html', action='store_true', help='outputs the combined log as html. Requires jinja2. pip install jinja2')
26-
args, unknown_args = parser.parse_known_args()
39+
args = parser.parse_args()
2740

2841
if args.html and args.color:
2942
print("Only one out of --color or --html should be specified")
3043
sys.exit(1)
3144

32-
# There should only be one unknown argument - the path of the temporary test directory
33-
if len(unknown_args) != 1:
34-
print("Unexpected arguments" + str(unknown_args))
45+
testdir = args.testdir or find_latest_test_dir()
46+
47+
if not testdir:
48+
print("No test directories found")
3549
sys.exit(1)
3650

37-
log_events = read_logs(unknown_args[0])
51+
if not args.testdir:
52+
print("Opening latest test directory: {}".format(testdir), file=sys.stderr)
53+
54+
log_events = read_logs(testdir)
3855

3956
print_logs(log_events, color=args.color, html=args.html)
4057

@@ -53,6 +70,29 @@ def read_logs(tmp_dir):
5370

5471
return heapq.merge(*[get_log_events(source, f) for source, f in files])
5572

73+
74+
def find_latest_test_dir():
75+
"""Returns the latest tmpfile test directory prefix."""
76+
tmpdir = tempfile.gettempdir()
77+
78+
def join_tmp(basename):
79+
return os.path.join(tmpdir, basename)
80+
81+
def is_valid_test_tmpdir(basename):
82+
fullpath = join_tmp(basename)
83+
return (
84+
os.path.isdir(fullpath)
85+
and basename.startswith(TMPDIR_PREFIX)
86+
and os.access(fullpath, os.R_OK)
87+
)
88+
89+
testdir_paths = [
90+
join_tmp(name) for name in os.listdir(tmpdir) if is_valid_test_tmpdir(name)
91+
]
92+
93+
return max(testdir_paths, key=os.path.getmtime) if testdir_paths else None
94+
95+
5696
def get_log_events(source, logfile):
5797
"""Generator function that returns individual log events.
5898

test/functional/test_framework/test_framework.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class TestStatus(Enum):
4343
TEST_EXIT_FAILED = 1
4444
TEST_EXIT_SKIPPED = 77
4545

46+
TMPDIR_PREFIX = "bitcoin_func_test_"
47+
4648

4749
class SkipTest(Exception):
4850
"""This exception is raised to skip a test"""
@@ -151,7 +153,7 @@ def main(self):
151153
self.options.tmpdir = os.path.abspath(self.options.tmpdir)
152154
os.makedirs(self.options.tmpdir, exist_ok=False)
153155
else:
154-
self.options.tmpdir = tempfile.mkdtemp(prefix="test")
156+
self.options.tmpdir = tempfile.mkdtemp(prefix=TMPDIR_PREFIX)
155157
self._start_logging()
156158

157159
self.log.debug('Setting up network thread')

0 commit comments

Comments
 (0)