From 097bf59dd25a631beed06e29c984bb3ddef0b16f Mon Sep 17 00:00:00 2001 From: Kirill Zhdanov Date: Mon, 12 Feb 2024 15:09:30 +0700 Subject: [PATCH] fix: parse tb for teardown exception (#117) --- pytest_playwright/pytest_playwright.py | 98 +++++++------------------- 1 file changed, 25 insertions(+), 73 deletions(-) diff --git a/pytest_playwright/pytest_playwright.py b/pytest_playwright/pytest_playwright.py index 1cbae79..ecec6fe 100644 --- a/pytest_playwright/pytest_playwright.py +++ b/pytest_playwright/pytest_playwright.py @@ -16,9 +16,12 @@ import shutil import os import sys + +import traceback + import warnings from typing import Any, Callable, Dict, Generator, List, Optional -import logging + import pytest from playwright.sync_api import ( Browser, @@ -33,8 +36,6 @@ import tempfile -log = logging.getLogger(__name__) - artifacts_folder = tempfile.TemporaryDirectory(prefix="playwright-pytest-") @@ -76,34 +77,6 @@ def pytest_configure(config: Any) -> None: "markers", "browser_context_args(**kwargs): provide additional arguments to browser.new_context()", ) - log.debug("pytest_configure") - class Teardown: - failed = False - setattr(config, "teardown", Teardown) - - -def pytest_runtest_teardown(item): - # import faulthandler - # faulthandler.dump_traceback(all_threads=True) - log.debug("pytest_runtest_teardown") - item.config.teardown.failed = True - - -def pytest_sessionfinish(session, exitstatus): - log.debug("pytest_runtest_teardown") - log.debug(f"{exitstatus=}") - - -def pytest_exception_interact(node, call, report): - log.debug("1pytest_exception_interact") - log.debug(f"{node.config.teardown.failed=}") - log.debug(f"{node.session.testsfailed=}") - excinfo = call.exc_info if hasattr(call, "exc_info") else None - log.debug(f"{excinfo=}") - - if call.when == "teardown": - log.debug("2pytest_exception_interact") - node.config.teardown.failed = True # Making test result information available in fixtures @@ -286,52 +259,33 @@ def context( ) yield context - # import traceback - # log.debug(f"{dir(traceback)=}") - # log.debug(f"{traceback.print_last()=}") - # stack = traceback.extract_stack() - # log.debug(f"{stack=}") - # for frame_summary in stack: - # log.debug(f"{frame_summary.filename=}") - # log.debug(f"{frame_summary.name=}") - # log.debug(f"{frame_summary.colno=}") - - - # log.debug(f"{traceback.print_stack()=}") - - # log.debug(f"{traceback.print_exc()=}") - - log.debug(f"{request.session.testscollected=}") - log.debug(f"{request.session.exitstatus=}") - log.debug(f"{request.session.testsfailed=}") - - if request.session.testsfailed: - log.debug("Only print if failed") - - log.debug("context") # If request.node is missing rep_call, then some error happened during execution # that prevented teardown, but should still be counted as a failure - failed_setup = request.node.rep_setup.failed if hasattr(request.node, "rep_setup") else False - failed_call = request.node.rep_call.failed if hasattr(request.node, "rep_call") else False - failed_teardown = request.node.rep_teardown.failed if hasattr(request.node, "rep_teardown") else False - - failed_xteardown = request.config.teardown.failed if hasattr(request.config, "teardown") else False - - - failed = failed_setup or failed_call or failed_xteardown - log.debug(f"{failed=}") - log.debug(f"{failed_setup=}") - log.debug(f"{failed_call=}") - log.debug(f"{failed_teardown=}") - log.debug(f"{failed_xteardown=}") + failed_setup = ( + request.node.rep_setup.failed if hasattr(request.node, "rep_setup") else False + ) + failed_call = ( + request.node.rep_call.failed if hasattr(request.node, "rep_call") else False + ) + passed_setup = ( + request.node.rep_setup.passed if hasattr(request.node, "rep_setup") else False + ) + passed_call = ( + request.node.rep_call.passed if hasattr(request.node, "rep_call") else False + ) - log.debug(f"{hasattr(request.node, 'rep_setup')=}") - log.debug(f"{hasattr(request.node, 'rep_call')=}") - log.debug(f"{hasattr(request.node, 'rep_teardown')=}") + failed_xteardown = False - log.debug(f"{hasattr(request.config, 'teardown')=}") + if (passed_setup or passed_call) and not (failed_setup or failed_call): + # check tb under stack if any other teardown was failed, False by default + # looks like workaround for https://github.com/pytest-dev/pytest/issues/9909 + for trace, _ in traceback.walk_stack(None): + if trace.f_locals.get("these_exceptions"): + failed_xteardown = True + break + failed = failed_setup or failed_call or failed_xteardown if capture_trace: retain_trace = tracing_option == "on" or ( @@ -344,11 +298,9 @@ def context( context.tracing.stop() screenshot_option = pytestconfig.getoption("--screenshot") - log.debug(f"{screenshot_option=}") capture_screenshot = screenshot_option == "on" or ( failed and screenshot_option == "only-on-failure" ) - log.debug(f"{capture_screenshot=}") if capture_screenshot: for index, page in enumerate(pages): human_readable_status = "failed" if failed else "finished"