From 22fcb7f3c197016fd225f901580dc69046b86918 Mon Sep 17 00:00:00 2001 From: Romain Komorn <136473744+romainkomorndatadog@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:30:42 +0100 Subject: [PATCH] fix(ci_visibility): support pytest 8.0.0 [backport 2.5] (#8482) Backport 78d5b98c58691c750cecc6954dc4bd6b452f0226 from #8357 to 2.5. Addresses https://github.com/DataDog/dd-trace-py/issues/8220 and fixes an issue with `pytest` `8.x` and above (brought by https://github.com/pytest-dev/pytest/issues/11137 ) where `pytest.Package` objects no longer have an attached `module` attribute. This also changes the testing matrix to include version `~=8.0`, but maintains `~=7.0` as a separate testing environment. ## Checklist - [x] Change(s) are motivated and described in the PR description - [x] Testing strategy is described if automated tests are not included in the PR - [x] Risks are described (performance impact, potential for breakage, maintainability) - [x] Change is maintainable (easy to change, telemetry, documentation) - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed or label `changelog/no-changelog` is set - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)) - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) - [x] If this PR changes the public interface, I've notified `@DataDog/apm-tees`. - [x] If change touches code that signs or publishes builds or packages, or handles credentials of any kind, I've requested a review from `@DataDog/security-design-and-guidance`. ## Reviewer Checklist - [x] Title is accurate - [x] All changes are related to the pull request's stated goal - [x] Description motivates each change - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - [x] Testing strategy adequately addresses listed risks - [x] Change is maintainable (easy to change, telemetry, documentation) - [x] Release note makes sense to a user of the library - [x] Author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - [x] Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) Co-authored-by: Federico Mon --- .circleci/config.templ.yml | 2 +- .riot/requirements/117f119.txt | 10 +- .riot/requirements/1254841.txt | 22 ++ .riot/requirements/135c565.txt | 4 +- .riot/requirements/14d5757.txt | 22 ++ .riot/requirements/166af91.txt | 8 +- .riot/requirements/1727339.txt | 8 +- .riot/requirements/17ecd2b.txt | 8 +- .riot/requirements/18cd4dd.txt | 26 +++ .riot/requirements/1cc7f49.txt | 14 +- .riot/requirements/1ce9a95.txt | 16 +- .../requirements/{1002189.txt => 1ceca0e.txt} | 8 +- .riot/requirements/1d44438.txt | 14 +- .riot/requirements/1e0c0d6.txt | 8 +- .../requirements/{c485ca7.txt => 1e49987.txt} | 8 +- .riot/requirements/1e535fe.txt | 14 +- .riot/requirements/1e67852.txt | 24 ++ .../requirements/{10075b5.txt => 2a81450.txt} | 8 +- .riot/requirements/2b9f1e7.txt | 16 +- .riot/requirements/32540b6.txt | 10 +- .riot/requirements/3e6ea76.txt | 8 +- .riot/requirements/429d258.txt | 8 +- .riot/requirements/6710b57.txt | 8 +- .riot/requirements/764e316.txt | 25 +++ .riot/requirements/77724eb.txt | 25 +++ .riot/requirements/7e7551c.txt | 16 +- .riot/requirements/8a19bdc.txt | 14 +- .riot/requirements/9b8c47e.txt | 10 +- .riot/requirements/b908e36.txt | 4 +- .riot/requirements/c426f16.txt | 2 +- .riot/requirements/cde42be.txt | 8 +- .riot/requirements/db02b05.txt | 10 +- .riot/requirements/f066985.txt | 14 +- .riot/requirements/fb47988.txt | 14 +- .riot/requirements/ff88ab0.txt | 16 +- ddtrace/contrib/pytest/plugin.py | 114 ++++++++-- ...pytest-8.0.0-support-88fcb7b5144d7efa.yaml | 3 + riotfile.py | 5 +- tests/contrib/pytest/test_pytest.py | 209 +++++++++++++++--- 39 files changed, 558 insertions(+), 205 deletions(-) create mode 100644 .riot/requirements/1254841.txt create mode 100644 .riot/requirements/14d5757.txt create mode 100644 .riot/requirements/18cd4dd.txt rename .riot/requirements/{1002189.txt => 1ceca0e.txt} (76%) rename .riot/requirements/{c485ca7.txt => 1e49987.txt} (76%) create mode 100644 .riot/requirements/1e67852.txt rename .riot/requirements/{10075b5.txt => 2a81450.txt} (76%) create mode 100644 .riot/requirements/764e316.txt create mode 100644 .riot/requirements/77724eb.txt create mode 100644 releasenotes/notes/ci_visibility-fix-pytest-8.0.0-support-88fcb7b5144d7efa.yaml diff --git a/.circleci/config.templ.yml b/.circleci/config.templ.yml index 740eea5fe4b..f5228c442d5 100644 --- a/.circleci/config.templ.yml +++ b/.circleci/config.templ.yml @@ -916,7 +916,7 @@ jobs: pytest: <<: *machine_executor - parallelism: 4 + parallelism: 10 steps: - run_test: pattern: 'pytest' diff --git a/.riot/requirements/117f119.txt b/.riot/requirements/117f119.txt index 87e86a18f55..b71c5320c7d 100644 --- a/.riot/requirements/117f119.txt +++ b/.riot/requirements/117f119.txt @@ -4,19 +4,19 @@ # # pip-compile --no-annotate .riot/requirements/117f119.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 hypothesis==6.45.0 -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 iniconfig==2.0.0 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 +pluggy==1.4.0 +pytest==8.0.0 pytest-cov==2.12.0 pytest-mock==2.0.0 pytest-randomly==3.15.0 diff --git a/.riot/requirements/1254841.txt b/.riot/requirements/1254841.txt new file mode 100644 index 00000000000..3639eb9fb05 --- /dev/null +++ b/.riot/requirements/1254841.txt @@ -0,0 +1,22 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile --no-annotate .riot/requirements/1254841.in +# +asynctest==0.13.0 +attrs==23.2.0 +coverage[toml]==7.4.1 +hypothesis==6.45.0 +iniconfig==2.0.0 +mock==5.1.0 +more-itertools==8.10.0 +msgpack==1.0.7 +opentracing==2.4.0 +packaging==23.2 +pluggy==1.4.0 +pytest==7.4.4 +pytest-cov==4.1.0 +pytest-mock==3.12.0 +pytest-randomly==3.15.0 +sortedcontainers==2.4.0 diff --git a/.riot/requirements/135c565.txt b/.riot/requirements/135c565.txt index 317e9786b4d..caf60f987cf 100644 --- a/.riot/requirements/135c565.txt +++ b/.riot/requirements/135c565.txt @@ -4,7 +4,7 @@ # # pip-compile --config=pyproject.toml --no-annotate --resolver=backtracking .riot/requirements/135c565.in # -attrs==23.1.0 +attrs==23.2.0 coverage[toml]==7.2.7 exceptiongroup==1.2.0 hypothesis==6.45.0 @@ -16,7 +16,7 @@ opentracing==2.4.0 packaging==23.2 pluggy==1.2.0 py-cpuinfo==9.0.0 -pytest==7.4.3 +pytest==7.4.4 pytest-benchmark==4.0.0 pytest-cov==4.1.0 pytest-mock==3.11.1 diff --git a/.riot/requirements/14d5757.txt b/.riot/requirements/14d5757.txt new file mode 100644 index 00000000000..36432ffa62c --- /dev/null +++ b/.riot/requirements/14d5757.txt @@ -0,0 +1,22 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --no-annotate .riot/requirements/14d5757.in +# +asynctest==0.13.0 +attrs==23.2.0 +coverage[toml]==7.4.1 +hypothesis==6.45.0 +iniconfig==2.0.0 +mock==5.1.0 +more-itertools==8.10.0 +msgpack==1.0.7 +opentracing==2.4.0 +packaging==23.2 +pluggy==1.4.0 +pytest==7.4.4 +pytest-cov==4.1.0 +pytest-mock==3.12.0 +pytest-randomly==3.15.0 +sortedcontainers==2.4.0 diff --git a/.riot/requirements/166af91.txt b/.riot/requirements/166af91.txt index bb766c76aab..3ea3c4ab9b6 100644 --- a/.riot/requirements/166af91.txt +++ b/.riot/requirements/166af91.txt @@ -4,8 +4,8 @@ # # pip-compile --no-annotate .riot/requirements/166af91.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 hypothesis==6.45.0 iniconfig==2.0.0 @@ -13,9 +13,9 @@ mock==5.1.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 +pluggy==1.4.0 py-cpuinfo==9.0.0 -pytest==7.4.3 +pytest==8.0.0 pytest-benchmark==4.0.0 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/1727339.txt b/.riot/requirements/1727339.txt index 2947bc31462..4934c8cbc73 100644 --- a/.riot/requirements/1727339.txt +++ b/.riot/requirements/1727339.txt @@ -4,7 +4,7 @@ # # pip-compile --config=pyproject.toml --no-annotate --resolver=backtracking .riot/requirements/1727339.in # -attrs==23.1.0 +attrs==23.2.0 coverage[toml]==7.2.7 exceptiongroup==1.2.0 glob2==0.7 @@ -12,17 +12,17 @@ hypothesis==6.45.0 importlib-metadata==6.7.0 iniconfig==2.0.0 mako==1.2.4 -markupsafe==2.1.3 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.5 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 pluggy==1.2.0 py==1.11.0 -pytest==7.4.3 +pytest==7.4.4 pytest-bdd==6.0.1 pytest-cov==4.1.0 pytest-mock==3.11.1 diff --git a/.riot/requirements/17ecd2b.txt b/.riot/requirements/17ecd2b.txt index 23f3295c560..bb56b30b3e9 100644 --- a/.riot/requirements/17ecd2b.txt +++ b/.riot/requirements/17ecd2b.txt @@ -5,8 +5,8 @@ # pip-compile --no-annotate .riot/requirements/17ecd2b.in # asynctest==0.13.0 -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 hypothesis==6.45.0 iniconfig==2.0.0 mock==5.1.0 @@ -14,8 +14,8 @@ more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 +pluggy==1.4.0 +pytest==8.0.0 pytest-cov==4.1.0 pytest-mock==3.12.0 pytest-randomly==3.15.0 diff --git a/.riot/requirements/18cd4dd.txt b/.riot/requirements/18cd4dd.txt new file mode 100644 index 00000000000..2cf6ca97907 --- /dev/null +++ b/.riot/requirements/18cd4dd.txt @@ -0,0 +1,26 @@ +# +# This file is autogenerated by pip-compile with Python 3.7 +# by the following command: +# +# pip-compile --config=pyproject.toml --no-annotate --resolver=backtracking .riot/requirements/18cd4dd.in +# +attrs==23.2.0 +coverage[toml]==7.2.7 +exceptiongroup==1.2.0 +hypothesis==6.45.0 +importlib-metadata==6.7.0 +iniconfig==2.0.0 +mock==5.1.0 +more-itertools==8.10.0 +msgpack==1.0.5 +opentracing==2.4.0 +packaging==23.2 +pluggy==1.2.0 +pytest==7.4.4 +pytest-cov==2.12.0 +pytest-mock==2.0.0 +pytest-randomly==3.12.0 +sortedcontainers==2.4.0 +tomli==2.0.1 +typing-extensions==4.7.1 +zipp==3.15.0 diff --git a/.riot/requirements/1cc7f49.txt b/.riot/requirements/1cc7f49.txt index 36f0891d8f7..4c33ca5b861 100644 --- a/.riot/requirements/1cc7f49.txt +++ b/.riot/requirements/1cc7f49.txt @@ -4,23 +4,23 @@ # # pip-compile --no-annotate .riot/requirements/1cc7f49.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 glob2==0.7 hypothesis==6.45.0 iniconfig==2.0.0 -mako==1.3.0 -markupsafe==2.1.3 +mako==1.3.2 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 -pytest==7.4.3 +pytest==8.0.0 pytest-bdd==6.0.1 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/1ce9a95.txt b/.riot/requirements/1ce9a95.txt index 1e0887929cb..363ae1fa333 100644 --- a/.riot/requirements/1ce9a95.txt +++ b/.riot/requirements/1ce9a95.txt @@ -4,25 +4,25 @@ # # pip-compile --no-annotate .riot/requirements/1ce9a95.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 glob2==0.7 hypothesis==6.45.0 -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 iniconfig==2.0.0 -mako==1.3.0 -markupsafe==2.1.3 +mako==1.3.2 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 -pytest==7.4.3 +pytest==8.0.0 pytest-bdd==4.1.0 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/1002189.txt b/.riot/requirements/1ceca0e.txt similarity index 76% rename from .riot/requirements/1002189.txt rename to .riot/requirements/1ceca0e.txt index dbff9a47e4f..52bfc2d3b6c 100644 --- a/.riot/requirements/1002189.txt +++ b/.riot/requirements/1ceca0e.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile --no-annotate .riot/requirements/1002189.in +# pip-compile --no-annotate .riot/requirements/1ceca0e.in # asynctest==0.13.0 -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 hypothesis==6.45.0 iniconfig==2.0.0 mock==5.1.0 @@ -14,7 +14,7 @@ more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 pytest==6.2.5 pytest-cov==4.1.0 diff --git a/.riot/requirements/1d44438.txt b/.riot/requirements/1d44438.txt index 57a2a73fbf2..c067e47f7a9 100644 --- a/.riot/requirements/1d44438.txt +++ b/.riot/requirements/1d44438.txt @@ -4,23 +4,23 @@ # # pip-compile --no-annotate .riot/requirements/1d44438.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 glob2==0.7 hypothesis==6.45.0 iniconfig==2.0.0 -mako==1.3.0 -markupsafe==2.1.3 +mako==1.3.2 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 -pytest==7.4.3 +pytest==8.0.0 pytest-bdd==6.0.1 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/1e0c0d6.txt b/.riot/requirements/1e0c0d6.txt index a3620a47f65..73febff7227 100644 --- a/.riot/requirements/1e0c0d6.txt +++ b/.riot/requirements/1e0c0d6.txt @@ -5,8 +5,8 @@ # pip-compile --no-annotate .riot/requirements/1e0c0d6.in # asynctest==0.13.0 -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 hypothesis==6.45.0 iniconfig==2.0.0 mock==5.1.0 @@ -14,8 +14,8 @@ more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 +pluggy==1.4.0 +pytest==8.0.0 pytest-cov==4.1.0 pytest-mock==3.12.0 pytest-randomly==3.15.0 diff --git a/.riot/requirements/c485ca7.txt b/.riot/requirements/1e49987.txt similarity index 76% rename from .riot/requirements/c485ca7.txt rename to .riot/requirements/1e49987.txt index 9dc75ef22b1..f12227707fa 100644 --- a/.riot/requirements/c485ca7.txt +++ b/.riot/requirements/1e49987.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # -# pip-compile --no-annotate .riot/requirements/c485ca7.in +# pip-compile --no-annotate .riot/requirements/1e49987.in # asynctest==0.13.0 -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 hypothesis==6.45.0 iniconfig==2.0.0 mock==5.1.0 @@ -14,7 +14,7 @@ more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 pytest==6.2.5 pytest-cov==4.1.0 diff --git a/.riot/requirements/1e535fe.txt b/.riot/requirements/1e535fe.txt index c0f88ad8ee7..f04e722a5ff 100644 --- a/.riot/requirements/1e535fe.txt +++ b/.riot/requirements/1e535fe.txt @@ -4,23 +4,23 @@ # # pip-compile --no-annotate .riot/requirements/1e535fe.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 glob2==0.7 hypothesis==6.45.0 iniconfig==2.0.0 -mako==1.3.0 -markupsafe==2.1.3 +mako==1.3.2 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 -pytest==7.4.3 +pytest==8.0.0 pytest-bdd==4.1.0 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/1e67852.txt b/.riot/requirements/1e67852.txt new file mode 100644 index 00000000000..21bd5692752 --- /dev/null +++ b/.riot/requirements/1e67852.txt @@ -0,0 +1,24 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# pip-compile --no-annotate .riot/requirements/1e67852.in +# +asynctest==0.13.0 +attrs==23.2.0 +coverage[toml]==7.4.1 +exceptiongroup==1.2.0 +hypothesis==6.45.0 +iniconfig==2.0.0 +mock==5.1.0 +more-itertools==8.10.0 +msgpack==1.0.7 +opentracing==2.4.0 +packaging==23.2 +pluggy==1.4.0 +pytest==7.4.4 +pytest-cov==4.1.0 +pytest-mock==3.12.0 +pytest-randomly==3.15.0 +sortedcontainers==2.4.0 +tomli==2.0.1 diff --git a/.riot/requirements/10075b5.txt b/.riot/requirements/2a81450.txt similarity index 76% rename from .riot/requirements/10075b5.txt rename to .riot/requirements/2a81450.txt index 8c2e593e3ba..c9dbb88511a 100644 --- a/.riot/requirements/10075b5.txt +++ b/.riot/requirements/2a81450.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.12 # by the following command: # -# pip-compile --no-annotate .riot/requirements/10075b5.in +# pip-compile --no-annotate .riot/requirements/2a81450.in # asynctest==0.13.0 -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 hypothesis==6.45.0 iniconfig==2.0.0 mock==5.1.0 @@ -14,7 +14,7 @@ more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 pytest==6.2.5 pytest-cov==4.1.0 diff --git a/.riot/requirements/2b9f1e7.txt b/.riot/requirements/2b9f1e7.txt index 0b4335a5196..be8c122b5f8 100644 --- a/.riot/requirements/2b9f1e7.txt +++ b/.riot/requirements/2b9f1e7.txt @@ -4,25 +4,25 @@ # # pip-compile --no-annotate .riot/requirements/2b9f1e7.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 glob2==0.7 hypothesis==6.45.0 -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 iniconfig==2.0.0 -mako==1.3.0 -markupsafe==2.1.3 +mako==1.3.2 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 -pytest==7.4.3 +pytest==8.0.0 pytest-bdd==6.0.1 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/32540b6.txt b/.riot/requirements/32540b6.txt index f469f7af121..346a6890b0f 100644 --- a/.riot/requirements/32540b6.txt +++ b/.riot/requirements/32540b6.txt @@ -4,19 +4,19 @@ # # pip-compile --no-annotate .riot/requirements/32540b6.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 hypothesis==6.45.0 -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 iniconfig==2.0.0 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 +pluggy==1.4.0 +pytest==8.0.0 pytest-cov==2.12.0 pytest-mock==2.0.0 pytest-randomly==3.15.0 diff --git a/.riot/requirements/3e6ea76.txt b/.riot/requirements/3e6ea76.txt index c25b8507640..9f2f450e61f 100644 --- a/.riot/requirements/3e6ea76.txt +++ b/.riot/requirements/3e6ea76.txt @@ -4,17 +4,17 @@ # # pip-compile --no-annotate .riot/requirements/3e6ea76.in # -attrs==23.1.0 -coverage==7.3.4 +attrs==23.2.0 +coverage==7.4.1 hypothesis==6.45.0 -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 iniconfig==2.0.0 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 pytest==6.2.5 pytest-cov==2.9.0 diff --git a/.riot/requirements/429d258.txt b/.riot/requirements/429d258.txt index 6d6d51ef103..232f6791850 100644 --- a/.riot/requirements/429d258.txt +++ b/.riot/requirements/429d258.txt @@ -4,7 +4,7 @@ # # pip-compile --config=pyproject.toml --no-annotate --resolver=backtracking .riot/requirements/429d258.in # -attrs==23.1.0 +attrs==23.2.0 coverage[toml]==7.2.7 exceptiongroup==1.2.0 glob2==0.7 @@ -12,17 +12,17 @@ hypothesis==6.45.0 importlib-metadata==6.7.0 iniconfig==2.0.0 mako==1.2.4 -markupsafe==2.1.3 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.5 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 pluggy==1.2.0 py==1.11.0 -pytest==7.4.3 +pytest==7.4.4 pytest-bdd==4.1.0 pytest-cov==4.1.0 pytest-mock==3.11.1 diff --git a/.riot/requirements/6710b57.txt b/.riot/requirements/6710b57.txt index ed9eb8a8628..abc0c7a3a4d 100644 --- a/.riot/requirements/6710b57.txt +++ b/.riot/requirements/6710b57.txt @@ -4,17 +4,17 @@ # # pip-compile --no-annotate .riot/requirements/6710b57.in # -attrs==23.1.0 -coverage==7.3.4 +attrs==23.2.0 +coverage==7.4.1 hypothesis==6.45.0 -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 iniconfig==2.0.0 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 pytest==6.2.5 pytest-cov==2.9.0 diff --git a/.riot/requirements/764e316.txt b/.riot/requirements/764e316.txt new file mode 100644 index 00000000000..ebece799c14 --- /dev/null +++ b/.riot/requirements/764e316.txt @@ -0,0 +1,25 @@ +# +# This file is autogenerated by pip-compile with Python 3.8 +# by the following command: +# +# pip-compile --no-annotate .riot/requirements/764e316.in +# +attrs==23.2.0 +coverage[toml]==7.4.1 +exceptiongroup==1.2.0 +hypothesis==6.45.0 +importlib-metadata==7.0.1 +iniconfig==2.0.0 +mock==5.1.0 +more-itertools==8.10.0 +msgpack==1.0.7 +opentracing==2.4.0 +packaging==23.2 +pluggy==1.4.0 +pytest==7.4.4 +pytest-cov==2.12.0 +pytest-mock==2.0.0 +pytest-randomly==3.15.0 +sortedcontainers==2.4.0 +tomli==2.0.1 +zipp==3.17.0 diff --git a/.riot/requirements/77724eb.txt b/.riot/requirements/77724eb.txt new file mode 100644 index 00000000000..327cbb26b12 --- /dev/null +++ b/.riot/requirements/77724eb.txt @@ -0,0 +1,25 @@ +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --no-annotate .riot/requirements/77724eb.in +# +attrs==23.2.0 +coverage[toml]==7.4.1 +exceptiongroup==1.2.0 +hypothesis==6.45.0 +importlib-metadata==7.0.1 +iniconfig==2.0.0 +mock==5.1.0 +more-itertools==8.10.0 +msgpack==1.0.7 +opentracing==2.4.0 +packaging==23.2 +pluggy==1.4.0 +pytest==7.4.4 +pytest-cov==2.12.0 +pytest-mock==2.0.0 +pytest-randomly==3.15.0 +sortedcontainers==2.4.0 +tomli==2.0.1 +zipp==3.17.0 diff --git a/.riot/requirements/7e7551c.txt b/.riot/requirements/7e7551c.txt index 4bf94028b85..945877903ec 100644 --- a/.riot/requirements/7e7551c.txt +++ b/.riot/requirements/7e7551c.txt @@ -4,25 +4,25 @@ # # pip-compile --no-annotate .riot/requirements/7e7551c.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 glob2==0.7 hypothesis==6.45.0 -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 iniconfig==2.0.0 -mako==1.3.0 -markupsafe==2.1.3 +mako==1.3.2 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 -pytest==7.4.3 +pytest==8.0.0 pytest-bdd==6.0.1 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/8a19bdc.txt b/.riot/requirements/8a19bdc.txt index 1ef2da30b89..4561c44afa0 100644 --- a/.riot/requirements/8a19bdc.txt +++ b/.riot/requirements/8a19bdc.txt @@ -4,23 +4,23 @@ # # pip-compile --no-annotate .riot/requirements/8a19bdc.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 glob2==0.7 hypothesis==6.45.0 iniconfig==2.0.0 -mako==1.3.0 -markupsafe==2.1.3 +mako==1.3.2 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 -pytest==7.4.3 +pytest==8.0.0 pytest-bdd==4.1.0 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/9b8c47e.txt b/.riot/requirements/9b8c47e.txt index 9f064179f8e..1786a038a59 100644 --- a/.riot/requirements/9b8c47e.txt +++ b/.riot/requirements/9b8c47e.txt @@ -4,19 +4,19 @@ # # pip-compile --no-annotate .riot/requirements/9b8c47e.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 hypothesis==6.45.0 -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 iniconfig==2.0.0 mock==5.1.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 +pluggy==1.4.0 py-cpuinfo==9.0.0 -pytest==7.4.3 +pytest==8.0.0 pytest-benchmark==4.0.0 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/b908e36.txt b/.riot/requirements/b908e36.txt index 409f1495b01..d210b2430f5 100644 --- a/.riot/requirements/b908e36.txt +++ b/.riot/requirements/b908e36.txt @@ -4,7 +4,7 @@ # # pip-compile --config=pyproject.toml --no-annotate --resolver=backtracking .riot/requirements/b908e36.in # -attrs==23.1.0 +attrs==23.2.0 coverage[toml]==7.2.7 exceptiongroup==1.2.0 hypothesis==6.45.0 @@ -16,7 +16,7 @@ msgpack==1.0.5 opentracing==2.4.0 packaging==23.2 pluggy==1.2.0 -pytest==7.4.3 +pytest==7.4.4 pytest-cov==2.12.0 pytest-mock==2.0.0 pytest-randomly==3.12.0 diff --git a/.riot/requirements/c426f16.txt b/.riot/requirements/c426f16.txt index 608ca7a7a63..4ee102b0707 100644 --- a/.riot/requirements/c426f16.txt +++ b/.riot/requirements/c426f16.txt @@ -4,7 +4,7 @@ # # pip-compile --config=pyproject.toml --no-annotate --resolver=backtracking .riot/requirements/c426f16.in # -attrs==23.1.0 +attrs==23.2.0 coverage==7.2.7 hypothesis==6.45.0 importlib-metadata==6.7.0 diff --git a/.riot/requirements/cde42be.txt b/.riot/requirements/cde42be.txt index d8ad7ca6cfd..3ec46eba267 100644 --- a/.riot/requirements/cde42be.txt +++ b/.riot/requirements/cde42be.txt @@ -5,8 +5,8 @@ # pip-compile --no-annotate .riot/requirements/cde42be.in # asynctest==0.13.0 -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 hypothesis==6.45.0 iniconfig==2.0.0 @@ -15,8 +15,8 @@ more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 +pluggy==1.4.0 +pytest==8.0.0 pytest-cov==4.1.0 pytest-mock==3.12.0 pytest-randomly==3.15.0 diff --git a/.riot/requirements/db02b05.txt b/.riot/requirements/db02b05.txt index 77b92edc327..77c96200bd2 100644 --- a/.riot/requirements/db02b05.txt +++ b/.riot/requirements/db02b05.txt @@ -4,19 +4,19 @@ # # pip-compile --no-annotate .riot/requirements/db02b05.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 hypothesis==6.45.0 -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 iniconfig==2.0.0 mock==5.1.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -pluggy==1.3.0 +pluggy==1.4.0 py-cpuinfo==9.0.0 -pytest==7.4.3 +pytest==8.0.0 pytest-benchmark==4.0.0 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/f066985.txt b/.riot/requirements/f066985.txt index 594320f4587..debd8ebffab 100644 --- a/.riot/requirements/f066985.txt +++ b/.riot/requirements/f066985.txt @@ -4,24 +4,24 @@ # # pip-compile --no-annotate .riot/requirements/f066985.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 glob2==0.7 hypothesis==6.45.0 iniconfig==2.0.0 -mako==1.3.0 -markupsafe==2.1.3 +mako==1.3.2 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 -pytest==7.4.3 +pytest==8.0.0 pytest-bdd==4.1.0 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/fb47988.txt b/.riot/requirements/fb47988.txt index ef0b672a48a..5bb283612e2 100644 --- a/.riot/requirements/fb47988.txt +++ b/.riot/requirements/fb47988.txt @@ -4,24 +4,24 @@ # # pip-compile --no-annotate .riot/requirements/fb47988.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 glob2==0.7 hypothesis==6.45.0 iniconfig==2.0.0 -mako==1.3.0 -markupsafe==2.1.3 +mako==1.3.2 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 -pytest==7.4.3 +pytest==8.0.0 pytest-bdd==6.0.1 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/.riot/requirements/ff88ab0.txt b/.riot/requirements/ff88ab0.txt index a8632059a91..69b41f473b8 100644 --- a/.riot/requirements/ff88ab0.txt +++ b/.riot/requirements/ff88ab0.txt @@ -4,25 +4,25 @@ # # pip-compile --no-annotate .riot/requirements/ff88ab0.in # -attrs==23.1.0 -coverage[toml]==7.3.4 +attrs==23.2.0 +coverage[toml]==7.4.1 exceptiongroup==1.2.0 glob2==0.7 hypothesis==6.45.0 -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 iniconfig==2.0.0 -mako==1.3.0 -markupsafe==2.1.3 +mako==1.3.2 +markupsafe==2.1.5 mock==5.1.0 more-itertools==8.10.0 msgpack==1.0.7 opentracing==2.4.0 packaging==23.2 -parse==1.20.0 +parse==1.20.1 parse-type==0.6.2 -pluggy==1.3.0 +pluggy==1.4.0 py==1.11.0 -pytest==7.4.3 +pytest==8.0.0 pytest-bdd==4.1.0 pytest-cov==4.1.0 pytest-mock==3.12.0 diff --git a/ddtrace/contrib/pytest/plugin.py b/ddtrace/contrib/pytest/plugin.py index 6d841cadcc1..8d4e1bf9ea9 100644 --- a/ddtrace/contrib/pytest/plugin.py +++ b/ddtrace/contrib/pytest/plugin.py @@ -70,6 +70,12 @@ _global_skipped_elements = 0 +def _is_pytest_8_or_later(): + if hasattr(pytest, "version_tuple"): + return pytest.version_tuple >= (8, 0, 0) + return False + + def encode_test_parameter(parameter): param_repr = repr(parameter) # if the representation includes an id() we'll remove it @@ -115,7 +121,16 @@ def _extract_ancestor_module_span(item): module_span = _extract_module_span(item) or _extract_span(item) if module_span is not None and module_span.name == "pytest.test_module": return module_span - item = item.parent + item = _get_parent(item) + + +def _extract_ancestor_suite_span(item): + """Return the first ancestor suite span found""" + while item: + suite_span = _extract_span(item) + if suite_span is not None and suite_span.name == "pytest.test_suite": + return suite_span + item = _get_parent(item) def _store_module_span(item, span): @@ -125,8 +140,9 @@ def _store_module_span(item, span): def _mark_failed(item): """Store test failed status at `pytest.Item` instance.""" - if item.parent: - _mark_failed(item.parent) + item_parent = _get_parent(item) + if item_parent: + _mark_failed(item_parent) item._failed = True @@ -137,11 +153,44 @@ def _check_failed(item): def _mark_not_skipped(item): """Mark test suite/module/session `pytest.Item` as not skipped.""" - if item.parent: - _mark_not_skipped(item.parent) + item_parent = _get_parent(item) + if item_parent: + _mark_not_skipped(item_parent) + item._fully_skipped = False + + +def _mark_not_skipped(item): + """Mark test suite/module/session `pytest.Item` as not skipped.""" + + item_parent = _get_parent(item) + + if item_parent: + _mark_not_skipped(item_parent) item._fully_skipped = False +def _get_parent(item): + """Fetches the nearest parent that is not a directory. + + This is introduced as a workaround for pytest 8.0's introduction pytest.Dir objects. + """ + if item is None or item.parent is None: + return None + + if _is_pytest_8_or_later(): + # In pytest 8.0, the parent of a Package can be another Package. In previous versions, the parent was always + # a session. + if isinstance(item, pytest.Package): + while item.parent is not None and not isinstance(item.parent, pytest.Session): + item = item.parent + return item.parent + + while item.parent is not None and isinstance(item.parent, pytest.Dir): + item = item.parent + + return item.parent + + def _mark_test_forced(test_item): # type: (pytest.Test) -> None test_span = _extract_span(test_item) @@ -181,19 +230,21 @@ def _mark_test_status(item, span): """ Given a `pytest.Item`, determine and set the test status of the corresponding span. """ + item_parent = _get_parent(item) + # If any child has failed, mark span as failed. if _check_failed(item): status = test.Status.FAIL.value - if item.parent: - _mark_failed(item.parent) - _mark_not_skipped(item.parent) + if item_parent: + _mark_failed(item_parent) + _mark_not_skipped(item_parent) # If all children have been skipped, mark span as skipped. elif _check_fully_skipped(item): status = test.Status.SKIP.value else: status = test.Status.PASS.value - if item.parent: - _mark_not_skipped(item.parent) + if item_parent: + _mark_not_skipped(item_parent) span.set_tag_str(test.STATUS, status) @@ -215,15 +266,14 @@ def _get_module_path(item): # type (pytest.Item) -> str if not isinstance(item, (pytest.Package, pytest.Module)): return None - return item.nodeid.rpartition("/")[0] + if _is_pytest_8_or_later() and isinstance(item, pytest.Package): + module_path = item.nodeid -def _get_module_name(item, is_package=True): - """Extract module name (fully qualified) from a `pytest.Item` instance.""" - # type (pytest.Item) -> str - if is_package: - return item.module.__name__ - return item.nodeid.rpartition("/")[0].replace("/", ".") + else: + module_path = item.nodeid.rpartition("/")[0] + + return module_path def _is_test_unskippable(item): @@ -239,6 +289,10 @@ def _is_test_unskippable(item): def _module_is_package(pytest_package_item=None, pytest_module_item=None): + # Pytest 8+ module items have a pytest.Dir object as their parent instead of the session object + if _is_pytest_8_or_later(): + return isinstance(pytest_module_item.parent, pytest.Package) + if pytest_package_item is None and pytest_module_item is not None: return False return True @@ -497,7 +551,7 @@ def _find_pytest_item(item, pytest_item_type): return None if pytest_item_type not in [pytest.Package, pytest.Module]: return None - parent = item.parent + parent = _get_parent(item) while not isinstance(parent, pytest_item_type) and parent is not None: parent = parent.parent return parent @@ -508,7 +562,7 @@ def _get_test_class_hierarchy(item): Given a `pytest.Item` function item, traverse upwards to collect and return a string listing the test class hierarchy, or an empty string if there are no test classes. """ - parent = item.parent + parent = _get_parent(item) test_class_hierarchy = [] while parent is not None: if isinstance(parent, pytest.Class): @@ -784,7 +838,7 @@ def pytest_runtest_makereport(item, call): if xfail and not has_skip_keyword: # XFail tests that fail are recorded skipped by pytest, should be passed instead span.set_tag_str(test.STATUS, test.Status.PASS.value) - _mark_not_skipped(item.parent) + _mark_not_skipped(_get_parent(item)) if not item.config.option.runxfail: span.set_tag_str(test.RESULT, test.Status.XFAIL.value) span.set_tag_str(XFAIL_REASON, getattr(result, "wasxfail", "XFail")) @@ -800,7 +854,7 @@ def pytest_runtest_makereport(item, call): suite_span.set_tag_str(test.ITR_SKIPPED, "true") span.set_tag_str(test.ITR_SKIPPED, "true") elif result.passed: - _mark_not_skipped(item.parent) + _mark_not_skipped(_get_parent(item)) span.set_tag_str(test.STATUS, test.Status.PASS.value) if xfail and not has_skip_keyword and not item.config.option.runxfail: # XPass (strict=False) are recorded passed by pytest @@ -808,8 +862,8 @@ def pytest_runtest_makereport(item, call): span.set_tag_str(test.RESULT, test.Status.XPASS.value) else: # Store failure in test suite `pytest.Item` to propagate to test suite spans - _mark_failed(item.parent) - _mark_not_skipped(item.parent) + _mark_failed(_get_parent(item)) + _mark_not_skipped(_get_parent(item)) span.set_tag_str(test.STATUS, test.Status.FAIL.value) if xfail and not has_skip_keyword and not item.config.option.runxfail: # XPass (strict=True) are recorded failed by pytest, longrepr contains reason @@ -832,7 +886,20 @@ def pytest_ddtrace_get_item_module_name(item): pytest_package_item = _find_pytest_item(pytest_module_item, pytest.Package) if _module_is_package(pytest_package_item, pytest_module_item): + if _is_pytest_8_or_later(): + # pytest 8.0.0 no longer treats Packages as Module/File, so we replicate legacy behavior by concatenating + # parent package names in reverse until we hit a non-Package-type item + # https://github.com/pytest-dev/pytest/issues/11137 + package_names = [] + current_package = pytest_package_item + while isinstance(current_package, pytest.Package): + package_names.append(str(current_package.name)) + current_package = current_package.parent + + return ".".join(package_names[::-1]) + return pytest_package_item.module.__name__ + return pytest_module_item.nodeid.rpartition("/")[0].replace("/", ".") @@ -842,7 +909,6 @@ def pytest_ddtrace_get_item_suite_name(item): Extract suite name from a `pytest.Item` instance. If the module path doesn't exist, the suite path will be reported in full. """ - # breakpoint() pytest_module_item = _find_pytest_item(item, pytest.Module) test_module_path = _get_module_path(pytest_module_item) if test_module_path: diff --git a/releasenotes/notes/ci_visibility-fix-pytest-8.0.0-support-88fcb7b5144d7efa.yaml b/releasenotes/notes/ci_visibility-fix-pytest-8.0.0-support-88fcb7b5144d7efa.yaml new file mode 100644 index 00000000000..5243922068b --- /dev/null +++ b/releasenotes/notes/ci_visibility-fix-pytest-8.0.0-support-88fcb7b5144d7efa.yaml @@ -0,0 +1,3 @@ +fixes: + - | + CI Visibility: fixes issues with pytest~=8.0 that would case crashes in certain scenarios, and returned different module names diff --git a/riotfile.py b/riotfile.py index ed884323947..01c0a7e4f91 100644 --- a/riotfile.py +++ b/riotfile.py @@ -1530,7 +1530,7 @@ def select_pys(min_version=MIN_PYTHON_VERSION, max_version=MAX_PYTHON_VERSION): ), Venv( pkgs={ - "pytest": [latest], + "pytest": ["~=7.0", latest], "pytest-cov": "==2.12.0", }, ), @@ -1540,7 +1540,8 @@ def select_pys(min_version=MIN_PYTHON_VERSION, max_version=MAX_PYTHON_VERSION): pys=select_pys(min_version="3.10"), pkgs={ "pytest": [ - ">=6.0,<7.0", + "~=6.0", + "~=7.0", latest, ], "msgpack": latest, diff --git a/tests/contrib/pytest/test_pytest.py b/tests/contrib/pytest/test_pytest.py index 1ccfdcbb379..4f2b4667127 100644 --- a/tests/contrib/pytest/test_pytest.py +++ b/tests/contrib/pytest/test_pytest.py @@ -1256,8 +1256,8 @@ def test_pytest_module(self): assert test_module_span.get_tag("test_session_id") == str(test_session_span.span_id) assert test_module_span.get_tag("test_module_id") == str(test_module_span.span_id) assert test_module_span.get_tag("test.command") == "pytest -p no:randomly --ddtrace" - assert test_module_span.get_tag("test.module") == str(package_a_dir).split("/")[-1] - assert test_module_span.get_tag("test.module_path") == str(package_a_dir).split("/")[-1] + assert test_module_span.get_tag("test.module") == "test_package_a" + assert test_module_span.get_tag("test.module_path") == "test_package_a" def test_pytest_modules(self): """ @@ -1390,14 +1390,19 @@ def test_pytest_module_path(self): spans = self.pop_spans() assert len(spans) == 7 - test_module_spans = [span for span in spans if span.get_tag("type") == "test_module_end"] + test_module_spans = sorted( + [span for span in spans if span.get_tag("type") == "test_module_end"], + key=lambda s: s.get_tag("test.module"), + ) assert test_module_spans[0].get_tag("test.module") == "test_outer_package" assert test_module_spans[0].get_tag("test.module_path") == "test_outer_package" assert test_module_spans[1].get_tag("test.module") == "test_outer_package.test_inner_package" assert test_module_spans[1].get_tag("test.module_path") == "test_outer_package/test_inner_package" - test_suite_spans = [span for span in spans if span.get_tag("type") == "test_suite_end"] - assert test_suite_spans[0].get_tag("test.suite") == "test_outer_abc.py" - assert test_suite_spans[1].get_tag("test.suite") == "test_inner_abc.py" + test_suite_spans = sorted( + [span for span in spans if span.get_tag("type") == "test_suite_end"], key=lambda s: s.get_tag("test.suite") + ) + assert test_suite_spans[0].get_tag("test.suite") == "test_inner_abc.py" + assert test_suite_spans[1].get_tag("test.suite") == "test_outer_abc.py" def test_pytest_module_path_empty(self): """ @@ -1864,12 +1869,13 @@ def test_pytest_skip_tests_by_path(self): "test_outer_package/test_inner_package/test_inner_abc.py": [], }, ): - self.inline_run("--ddtrace") + self.inline_run("--ddtrace", "-vv") spans = self.pop_spans() assert len(spans) == 7 session_span = [span for span in spans if span.get_tag("type") == "test_session_end"][0] + assert session_span.get_tag("test.status") == "pass" assert session_span.get_tag("test.itr.tests_skipping.enabled") == "true" assert session_span.get_tag("test.itr.tests_skipping.tests_skipped") == "true" assert session_span.get_tag("_dd.ci.itr.tests_skipped") == "true" @@ -1879,14 +1885,17 @@ def test_pytest_skip_tests_by_path(self): module_spans = [span for span in spans if span.get_tag("type") == "test_module_end"] assert len(module_spans) == 2 outer_module_span = [span for span in module_spans if span.get_tag("test.module") == "test_outer_package"][0] + assert outer_module_span.get_tag("test.status") == "skip" assert outer_module_span.get_tag("test.itr.tests_skipping.enabled") == "true" assert outer_module_span.get_tag("test.itr.tests_skipping.tests_skipped") == "true" assert outer_module_span.get_tag("_dd.ci.itr.tests_skipped") == "true" assert outer_module_span.get_tag("test.itr.tests_skipping.type") == "test" assert outer_module_span.get_metric("test.itr.tests_skipping.count") == 1 + inner_module_span = [ span for span in module_spans if span.get_tag("test.module") == "test_outer_package.test_inner_package" ][0] + assert inner_module_span.get_tag("test.status") == "pass" assert inner_module_span.get_tag("test.itr.tests_skipping.enabled") == "true" assert inner_module_span.get_tag("test.itr.tests_skipping.tests_skipped") == "false" assert inner_module_span.get_tag("_dd.ci.itr.tests_skipped") == "false" @@ -3004,57 +3013,186 @@ def test_inner_wasnot_going_to_skip_skipif(): assert inner_module_span.get_tag("test.itr.forced_run") == "true" def test_pytest_ddtrace_test_names(self): - package_outer_dir = self.testdir.mkpydir("test_package") - os.chdir(str(package_outer_dir)) - with open("test_names.py", "w+") as fd: - fd.write( + """Tests that the default naming behavior for pytest works as expected + + The test structure is: + test_outermost_tests.py + test_outer_package/__init__.py + test_outer_package/test_outer_package_tests.py + test_outer_package/test_outer_package_module/test_outer_package_module_tests.py + test_outer_package/test_inner_package/__init__.py + test_outer_package/test_inner_package/test_inner_package_tests.py + test_outer_package/test_inner_package/test_inner_package_module/test_inner_package_module_tests.py + + 26 total spans + 1 session + - 5 modules: + - (empty string) + - 1 suite: test_outermost_tests.py: 3 tests + - test_outer_package + - 1 suite: test_outer_package_tests.py: 3 tests + - test_outer_package.test_outer_package_module + - 1 suite: test_outer_package_module_tests.py: 3 tests + - test_outer_package.test_inner_package + - 1 suite: test_inner_package_tests.py: 3 tests + - test_outer_package.test_inner_package.test_inner_package_module + - 1 suite: test_inner_package_module_tests.py: 3 tests + """ + self.testdir.chdir() + with open("test_outermost_tests.py", "w") as test_outermost_tests_fd: + test_outermost_tests_fd.write( textwrap.dedent( ( """ - def test_ok(): + def test_outermost_test_ok(): assert True - class TestClassOne(): - def test_ok(self): + class TestOuterMostClassOne(): + def test_outermost_ok(self): assert True - class TestClassTwo(): - def test_ok(self): + class TestOuterMostClassTwo(): + def test_outermost_ok(self): assert True """ ) ) ) + _ = self.testdir.mkpydir("test_outer_package") + with open("test_outer_package/test_outer_package_tests.py", "w") as outer_fd: + outer_fd.write( + textwrap.dedent( + ( + """ + def test_outer_package_ok(): + assert True + + class TestOuterPackageClassOne(): + def test_outer_package_class_one_ok(self): + assert True + + class TestOuterPackageClassTwo(): + def test_outer_package_class_two_ok(self): + assert True + """ + ) + ) + ) + + _ = self.testdir.mkdir("test_outer_package/test_outer_package_module") + with open( + "test_outer_package/test_outer_package_module/test_outer_package_module_tests.py", "w" + ) as outer_package_module_tests_fd: + outer_package_module_tests_fd.write( + textwrap.dedent( + """ + def test_outer_package_module_ok(): + assert True + + class TestOuterPackageModuleClassOne(): + def test_outer_package_module_class_one_ok(self): + assert True + + class TestOuterPackageModuleClassTwo(): + def test_outer_package_module_class_two_ok(self): + assert True + """ + ) + ) + + _ = self.testdir.mkpydir("test_outer_package/test_inner_package") + with open("test_outer_package/test_inner_package/test_inner_package_tests.py", "w") as inner_package_tests_fd: + inner_package_tests_fd.write( + textwrap.dedent( + """ + def test_inner_package_ok(): + assert True + + class TestInnerPackageClassOne(): + def test_inner_package_class_one_ok(self): + assert True + + class TestInnerPackageClassTwo(): + def test_inner_package_class_two_ok(self): + assert True + """ + ) + ) + + _ = self.testdir.mkdir("test_outer_package/test_inner_package/test_inner_package_module") + with open( + "test_outer_package/test_inner_package/test_inner_package_module/test_inner_package_module_tests.py", "w" + ) as inner_package_module_tests_fd: + inner_package_module_tests_fd.write( + textwrap.dedent( + """ + def test_inner_package_module_test_ok(): + assert True + + class TestInnerPackageModuleClassOne(): + def test_inner_package_module_class_one_ok(self): + assert True + + class TestInnerPackageModuleClassTwo(): + def test_inner_package_module_class_two_ok(self): + assert True + """ + ) + ) + self.testdir.chdir() self.inline_run("--ddtrace") spans = self.pop_spans() - assert len(spans) == 6 + assert len(spans) == 26 session_span = [span for span in spans if span.get_tag("type") == "test_session_end"][0] assert session_span.get_tag("test.status") == "pass" - module_span = [span for span in spans if span.get_tag("type") == "test_module_end"][0] - assert module_span.get_tag("test.module") == "test_package" - - suite_span = [span for span in spans if span.get_tag("type") == "test_suite_end"][0] - assert suite_span.get_tag("test.module") == "test_package" - assert suite_span.get_tag("test.suite") == "test_names.py" - - test_spans = [span for span in spans if span.get_tag("type") == "test"] - assert len(test_spans) == 3 - assert test_spans[0].get_tag("test.module") == "test_package" - assert test_spans[0].get_tag("test.suite") == "test_names.py" - assert test_spans[0].get_tag("test.name") == "test_ok" + sorted_module_names = sorted( + [span.get_tag("test.module") for span in spans if span.get_tag("type") == "test_module_end"] + ) + assert len(sorted_module_names) == 5 + assert sorted_module_names == [ + "", + "test_outer_package", + "test_outer_package.test_inner_package", + "test_outer_package.test_inner_package.test_inner_package_module", + "test_outer_package.test_outer_package_module", + ] - assert test_spans[1].get_tag("test.module") == "test_package" - assert test_spans[1].get_tag("test.suite") == "test_names.py" - assert test_spans[1].get_tag("test.name") == "test_ok" + sorted_suite_names = sorted( + [span.get_tag("test.suite") for span in spans if span.get_tag("type") == "test_suite_end"] + ) + assert len(sorted_suite_names) == 5 + assert sorted_suite_names == [ + "test_inner_package_module_tests.py", + "test_inner_package_tests.py", + "test_outer_package_module_tests.py", + "test_outer_package_tests.py", + "test_outermost_tests.py", + ] - assert test_spans[2].get_tag("test.module") == "test_package" - assert test_spans[2].get_tag("test.suite") == "test_names.py" - assert test_spans[2].get_tag("test.name") == "test_ok" + sorted_test_names = sorted([span.get_tag("test.name") for span in spans if span.get_tag("type") == "test"]) + assert len(sorted_test_names) == 15 + assert sorted_test_names == [ + "test_inner_package_class_one_ok", + "test_inner_package_class_two_ok", + "test_inner_package_module_class_one_ok", + "test_inner_package_module_class_two_ok", + "test_inner_package_module_test_ok", + "test_inner_package_ok", + "test_outer_package_class_one_ok", + "test_outer_package_class_two_ok", + "test_outer_package_module_class_one_ok", + "test_outer_package_module_class_two_ok", + "test_outer_package_module_ok", + "test_outer_package_ok", + "test_outermost_ok", + "test_outermost_ok", + "test_outermost_test_ok", + ] def test_pytest_ddtrace_test_names_include_class_opt(self): package_outer_dir = self.testdir.mkpydir("test_package") @@ -3110,6 +3248,7 @@ def test_ok(self): assert test_spans[2].get_tag("test.name") == "TestClassTwo.test_ok" def test_pytest_ddtrace_name_hooks(self): + """This only tests that whatever hooks a user defines are being used""" with open("conftest.py", "w") as fd: fd.write( textwrap.dedent(