conftest.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import os
  2. import sys
  3. from collections import OrderedDict
  4. import pytest
  5. pytest_plugins = ["sentry.utils.pytest"]
  6. sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
  7. def pytest_configure(config):
  8. import warnings
  9. # XXX(dcramer): Kombu throws a warning due to transaction.commit_manually
  10. # being used
  11. warnings.filterwarnings("error", "", Warning, r"^(?!(|kombu|raven|sentry))")
  12. def pytest_addoption(parser):
  13. parser.addoption(
  14. "--itunes",
  15. action="store_true",
  16. help="Run iTunes tests, see tests/sentry/utils/appleconnect/itunes",
  17. )
  18. def pytest_runtest_setup(item):
  19. if item.get_closest_marker("itunes") and not item.config.getoption("--itunes"):
  20. pytest.skip("Test requires --itunes")
  21. # XXX: The below code is vendored code from https://github.com/utgwkk/pytest-github-actions-annotate-failures
  22. # so that we can add support for pytest_rerunfailures
  23. # retried tests will no longer be annotated in GHA
  24. #
  25. # Reference:
  26. # https://docs.pytest.org/en/latest/writing_plugins.html#hookwrapper-executing-around-other-hooks
  27. # https://docs.pytest.org/en/latest/writing_plugins.html#hook-function-ordering-call-example
  28. # https://docs.pytest.org/en/stable/reference.html#pytest.hookspec.pytest_runtest_makereport
  29. #
  30. # Inspired by:
  31. # https://github.com/pytest-dev/pytest/blob/master/src/_pytest/terminal.py
  32. @pytest.hookimpl(tryfirst=True, hookwrapper=True)
  33. def pytest_runtest_makereport(item, call):
  34. # execute all other hooks to obtain the report object
  35. outcome = yield
  36. report = outcome.get_result()
  37. # enable only in a workflow of GitHub Actions
  38. # ref: https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
  39. if os.environ.get("GITHUB_ACTIONS") != "true":
  40. return
  41. try:
  42. # If we have the pytest_rerunfailures plugin,
  43. # and there are still retries to be run,
  44. # then do not return the error
  45. import pytest_rerunfailures
  46. if item.execution_count <= pytest_rerunfailures.get_reruns_count(item):
  47. return
  48. except ImportError:
  49. pass
  50. if report.when == "call" and report.failed:
  51. # collect information to be annotated
  52. filesystempath, lineno, _ = report.location
  53. # try to convert to absolute path in GitHub Actions
  54. workspace = os.environ.get("GITHUB_WORKSPACE")
  55. if workspace:
  56. full_path = os.path.abspath(filesystempath)
  57. try:
  58. rel_path = os.path.relpath(full_path, workspace)
  59. except ValueError:
  60. # os.path.relpath() will raise ValueError on Windows
  61. # when full_path and workspace have different mount points.
  62. # https://github.com/utgwkk/pytest-github-actions-annotate-failures/issues/20
  63. rel_path = filesystempath
  64. if not rel_path.startswith(".."):
  65. filesystempath = rel_path
  66. # 0-index to 1-index
  67. lineno += 1
  68. # get the name of the current failed test, with parametrize info
  69. longrepr = report.head_line or item.name
  70. # get the error message and line number from the actual error
  71. try:
  72. longrepr += "\n\n" + report.longrepr.reprcrash.message
  73. lineno = report.longrepr.reprcrash.lineno
  74. except AttributeError:
  75. pass
  76. print( # noqa: B314
  77. _error_workflow_command(filesystempath, lineno, longrepr), file=sys.stderr
  78. )
  79. def _error_workflow_command(filesystempath, lineno, longrepr):
  80. # Build collection of arguments. Ordering is strict for easy testing
  81. details_dict = OrderedDict()
  82. details_dict["file"] = filesystempath
  83. if lineno is not None:
  84. details_dict["line"] = lineno
  85. details = ",".join(f"{k}={v}" for k, v in details_dict.items())
  86. if longrepr is None:
  87. return f"\n::error {details}"
  88. else:
  89. longrepr = _escape(longrepr)
  90. return f"\n::error {details}::{longrepr}"
  91. def _escape(s):
  92. return s.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")