Skip to content

Commit 1f1ac6a

Browse files
authored
ci: add [pyX.Y] suffix to test names (#15191)
## Description Workaround for our Flaky Test Management product, since a test that is flaky for a single Python version would be quarantined for all Python versions at the moment (as it is quarantined by name). The workaround consists in adding the python version as a suffix, so the same test for other Python versions would not be quarantined. ## Testing Manually tested that all the tests are sent with the suffix. ## Risks It effectively changes the test names, so it will impact reporting, etc. It also complicates some bits, as we want the original test name for snapshots and similar tests. ## Additional Notes <!-- Any other information that would be helpful for reviewers -->
1 parent 51b4246 commit 1f1ac6a

File tree

7 files changed

+86
-9
lines changed

7 files changed

+86
-9
lines changed

riotfile.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ def select_pys(min_version: str = MIN_PYTHON_VERSION, max_version: str = MAX_PYT
544544
env={
545545
"DD_INSTRUMENTATION_TELEMETRY_ENABLED": "0",
546546
"DD_CIVISIBILITY_ITR_ENABLED": "0",
547+
"DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED": "0", # DEV: Temporary, remove once merged
547548
},
548549
command="pytest -v {cmdargs} tests/internal/",
549550
pkgs={

tests/appsec/integrations/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from tests.appsec.integrations.utils_testagent import clear_session
44
from tests.appsec.integrations.utils_testagent import start_trace
5+
from tests.conftest import get_original_test_name
56

67

78
@pytest.fixture
@@ -13,7 +14,7 @@ def iast_test_token(request):
1314
afterwards. This centralizes the repeated start/clear logic used by
1415
testagent-based IAST tests.
1516
"""
16-
token = request.node.name
17+
token = get_original_test_name(request)
1718
_ = start_trace(token)
1819
try:
1920
yield token

tests/conftest.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from typing import Generator # noqa:F401
2525
from typing import List
2626
from typing import Tuple # noqa:F401
27+
from unittest import TestCase
2728
from unittest import mock
2829
from urllib import parse
2930
import warnings
@@ -47,11 +48,53 @@
4748
from tests.utils import snapshot_context as _snapshot_context
4849

4950

51+
try:
52+
from pytest import StashKey
53+
except ImportError:
54+
StashKey = None
55+
56+
5057
code_to_pyc = getattr(importlib._bootstrap_external, "_code_to_timestamp_pyc")
5158

5259

5360
DEFAULT_DDTRACE_SUBPROCESS_TEST_SERVICE_NAME = "ddtrace_subprocess_dir"
5461

62+
# Stash keys for storing original test name and nodeid before Python version suffix is added
63+
# For pytest >= 7.1.0, use StashKey; for older versions, use attribute names
64+
if StashKey:
65+
original_test_name_key = StashKey[str]()
66+
original_test_nodeid_key = StashKey[str]()
67+
else:
68+
# Fallback attribute names for pytest < 7.1.0
69+
original_test_name_key = "_ddtrace_original_name"
70+
original_test_nodeid_key = "_ddtrace_original_nodeid"
71+
72+
73+
def get_original_test_name(request_or_item):
74+
"""Get the original test name (before Python version suffix was added).
75+
76+
Works with both pytest >= 7.1.0 (using stash) and older versions (using attributes).
77+
78+
Args:
79+
request_or_item: Either a pytest.FixtureRequest or pytest.Item
80+
81+
Returns:
82+
The original test name string, or the current name if not found
83+
"""
84+
if hasattr(request_or_item, "node"):
85+
# It's a FixtureRequest
86+
item = request_or_item.node
87+
else:
88+
# It's an Item
89+
item = request_or_item
90+
91+
if StashKey:
92+
# pytest >= 7.1.0: use stash
93+
return item.stash.get(original_test_name_key, item.name)
94+
else:
95+
# pytest < 7.1.0: use attribute
96+
return getattr(item, original_test_name_key, item.name)
97+
5598

5699
# Hack to try and capture more logging data from pytest failing on `internal` jobs on
57100
# pytest shutdown. This is a temporary workaround until we can figure out... why....
@@ -404,7 +447,13 @@ def _subprocess_wrapper():
404447

405448
@pytest.hookimpl(tryfirst=True)
406449
def pytest_collection_modifyitems(session, config, items):
407-
"""Don't let ITR skip tests that use the subprocess marker because coverage collection in subprocesses is broken"""
450+
"""
451+
Don't let ITR skip tests that use the subprocess marker
452+
because coverage collection in subprocesses is broken.
453+
454+
Also: add py39 - py314 suffix as parametrization in test names
455+
"""
456+
py_tag = f"py{sys.version_info.major}.{sys.version_info.minor}"
408457
for item in items:
409458
if item.get_closest_marker("subprocess"):
410459
if item.get_closest_marker("skipif"):
@@ -413,6 +462,26 @@ def pytest_collection_modifyitems(session, config, items):
413462
unskippable = pytest.mark.skipif(False, reason="datadog_itr_unskippable")
414463
item.add_marker(unskippable)
415464

465+
# Store original name and nodeid before modification
466+
if StashKey:
467+
# pytest >= 7.1.0: use stash
468+
item.stash[original_test_name_key] = item.name
469+
item.stash[original_test_nodeid_key] = item.nodeid
470+
else:
471+
# pytest < 7.1.0: use attributes
472+
setattr(item, original_test_name_key, item.name)
473+
setattr(item, original_test_nodeid_key, item.nodeid)
474+
475+
name_base = item.name
476+
nodeid_base = item.nodeid
477+
478+
item._nodeid = f"{nodeid_base}[{py_tag}]"
479+
480+
cls = getattr(item, "cls", None)
481+
is_unittest = isinstance(cls, type) and issubclass(cls, TestCase)
482+
if not is_unittest:
483+
item.name = f"{name_base}[{py_tag}]"
484+
416485

417486
def pytest_generate_tests(metafunc):
418487
marker = metafunc.definition.get_closest_marker("subprocess")

tests/contrib/kafka/test_kafka.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from ddtrace.internal.utils.retry import fibonacci_backoff_with_jitter
2727
from ddtrace.trace import TraceFilter
2828
from ddtrace.trace import tracer as ddtracer
29+
from tests.conftest import get_original_test_name
2930
from tests.contrib.config import KAFKA_CONFIG
3031
from tests.datastreams.test_public_api import MockedTracer
3132
from tests.utils import DummyTracer
@@ -58,7 +59,7 @@ def process_trace(self, trace):
5859
@pytest.fixture()
5960
def kafka_topic(request):
6061
# todo: add a UUID, but it makes snapshot tests fail.
61-
topic_name = request.node.name.replace("[", "_").replace("]", "")
62+
topic_name = get_original_test_name(request).replace("[", "_").replace("]", "")
6263

6364
client = kafka_admin.AdminClient({"bootstrap.servers": BOOTSTRAP_SERVERS})
6465
for _, future in client.create_topics([kafka_admin.NewTopic(topic_name, 1, 1)]).items():
@@ -74,7 +75,7 @@ def empty_kafka_topic(request):
7475
"""
7576
Deletes a kafka topic to clear message if it exists.
7677
"""
77-
topic_name = request.node.name.replace("[", "_").replace("]", "")
78+
topic_name = get_original_test_name(request).replace("[", "_").replace("]", "")
7879
client = kafka_admin.AdminClient({"bootstrap.servers": BOOTSTRAP_SERVERS})
7980
for _, future in client.delete_topics([topic_name]).items():
8081
try:

tests/llmobs/test_experiments.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ def test_dataset_records() -> List[DatasetRecord]:
6969

7070
@pytest.fixture
7171
def test_dataset_name(request) -> str:
72-
return f"test-dataset-{request.node.name}"
72+
from tests.conftest import get_original_test_name
73+
74+
return f"test-dataset-{get_original_test_name(request)}"
7375

7476

7577
@pytest.fixture

tests/profiling/collector/test_stack.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from ddtrace.internal.datadog.profiling import ddup
1616
from ddtrace.profiling import _threading
1717
from ddtrace.profiling.collector import stack
18+
from tests.conftest import get_original_test_name
1819
from tests.profiling.collector import pprof_utils
1920

2021
from . import test_collector
@@ -656,7 +657,7 @@ def test_exception_collection_trace(tmp_path, tracer):
656657
# if you don't need to check the output profile, you can use this fixture
657658
@pytest.fixture
658659
def tracer_and_collector(tracer, request, tmp_path):
659-
test_name = request.node.name
660+
test_name = get_original_test_name(request)
660661
pprof_prefix = str(tmp_path / test_name)
661662

662663
assert ddup.is_available
@@ -785,7 +786,7 @@ def test_collect_span_id(tracer, tmp_path):
785786

786787

787788
def test_collect_span_resource_after_finish(tracer, tmp_path, request):
788-
test_name = request.node.name
789+
test_name = get_original_test_name(request)
789790
pprof_prefix = str(tmp_path / test_name)
790791
output_filename = pprof_prefix + "." + str(os.getpid())
791792

@@ -861,7 +862,7 @@ def test_resource_not_collected(tmp_path, tracer):
861862

862863

863864
def test_collect_nested_span_id(tmp_path, tracer, request):
864-
test_name = request.node.name
865+
test_name = get_original_test_name(request)
865866
pprof_prefix = str(tmp_path / test_name)
866867
output_filename = pprof_prefix + "." + str(os.getpid())
867868

tests/utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1439,10 +1439,12 @@ def call_program(*args, **kwargs):
14391439

14401440
def request_token(request):
14411441
# type: (pytest.FixtureRequest) -> str
1442+
from tests.conftest import get_original_test_name
1443+
14421444
token = ""
14431445
token += request.module.__name__
14441446
token += ".%s" % request.cls.__name__ if request.cls else ""
1445-
token += ".%s" % request.node.name
1447+
token += ".%s" % get_original_test_name(request)
14461448
return token
14471449

14481450

0 commit comments

Comments
 (0)