From 4b9773e89d650e2dec95a7942aba09ff97ae5987 Mon Sep 17 00:00:00 2001 From: Pavel Minaev Date: Sun, 29 Sep 2019 21:51:22 -0700 Subject: [PATCH] Also capture full pytest logs with --ptvsd-logs. --- tests/_logs/README | 12 +++++------- tests/pytest_fixtures.py | 36 +++++++++++++++++++++++++++++++----- tests/pytest_hooks.py | 13 +++++++------ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/tests/_logs/README b/tests/_logs/README index ebe40597..d508f9d6 100644 --- a/tests/_logs/README +++ b/tests/_logs/README @@ -3,11 +3,9 @@ For example: tox -e py37 -- --ptvsd-logs "tests/ptvsd/server/test_run.py::test_run[launch-file]" -A separate ptvsd-{pid}.log file will be created for every ptvsd process spawned by -the tests. However, because process IDs are reused by OS, logs may end up overwriting -earlier logs. +A separate directory will be created for every test. In that directory, a separate +subdirectory will be created for every debug.Session instance, containing ptvsd logs +for the processes that it spawns, and pydevd log. -All pydevd logs go into the same file named pydevd.log - and subsequent tests will -overwrite earlier logs. - -Thus, it is best to use this when running a single test, or a small number of tests. +If the test has failed, an empty file called FAILED.log is additionally created in +the test log directory, to easily find all failed tests after a large run. diff --git a/tests/pytest_fixtures.py b/tests/pytest_fixtures.py index ef287fd9..b206347f 100644 --- a/tests/pytest_fixtures.py +++ b/tests/pytest_fixtures.py @@ -68,7 +68,33 @@ def test_wrapper(request, long_tmpdir): try: yield finally: - log.info("Test {0} completed.", request.node.name) + failed = False + for report_attr in ("setup_report", "call_report", "teardown_report"): + try: + report = getattr(request.node, report_attr) + except AttributeError: + continue + + failed |= report.failed + log.write_format( + "error" if report.failed else "info", + "{0} for test {1} {2}.", + report.when, + request.node.name, + report.outcome, + ) + + if options.log_dir is not None: + with open(os.path.join(options.log_dir, report_attr + ".log"), "w") as f: + f.write(report.longreprtext) + with open(os.path.join(options.log_dir, report_attr + ".stdout.log"), "w") as f: + f.write(report.capstdout) + with open(os.path.join(options.log_dir, report_attr + ".stderr.log"), "w") as f: + f.write(report.capstderr) + + if failed and options.log_dir is not None: + with open(os.path.join(options.log_dir, "FAILED.log"), "w"): + pass finally: if original_log_dir is not None: options.log_dir = original_log_dir @@ -85,10 +111,10 @@ def with_pydevd_log(request, tmpdir): with pydevd_log.enabled(filename): yield - if request.node.setup_result.passed: - if not request.node.call_result.failed: + if request.node.setup_report.passed: + if not request.node.call_report.failed: return - elif not request.node.setup_result.failed: + elif not request.node.setup_report.failed: return pydevd_log.dump("failed") @@ -113,7 +139,7 @@ def daemon(request): yield factory try: - failed = request.node.call_result.failed + failed = request.node.call_report.failed except AttributeError: pass else: diff --git a/tests/pytest_hooks.py b/tests/pytest_hooks.py index fe3affee..17400292 100644 --- a/tests/pytest_hooks.py +++ b/tests/pytest_hooks.py @@ -35,15 +35,16 @@ def pytest_report_header(config): @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_makereport(item, call): - # Adds attributes such as setup_result, call_result etc to the item after the - # corresponding scope finished running its tests. This can be used in function-level - # fixtures to detect failures, e.g.: + # Adds attributes setup_report, call_report, and teardown_report to the item, + # referencing TestReport instances for the corresponding phases, after the scope + # finished running its tests. This can be used in function-level fixtures to + # detect test failures, e.g.: # - # if request.node.call_result.failed: ... + # if request.node.call_report.failed: ... outcome = yield - result = outcome.get_result() - setattr(item, result.when + "_result", result) + report = outcome.get_result() + setattr(item, report.when + "_report", report) def pytest_make_parametrize_id(config, val):