Skip to content

Commit 176b574

Browse files
committed
ci: move DI exploration tests to GitLab
We extract the exploration tests from the rest of the framework tests and move them to GitLab.
1 parent 1cb2ea4 commit 176b574

File tree

10 files changed

+128
-99
lines changed

10 files changed

+128
-99
lines changed

.github/workflows/test_frameworks.yml

Lines changed: 6 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
run: |
3434
git fetch origin ${{ github.event.pull_request.base.sha }}
3535
export PATHS=$(git diff --name-only HEAD ${{ github.event.pull_request.base.sha }})
36-
python -c "import os,sys,fnmatch;sys.exit(not bool([_ for pattern in {'ddtrace/*', 'setup*', 'pyproject.toml', '.github/workflows/test_frameworks.yml', 'tests/debugging/exploration/*'} for _ in fnmatch.filter(os.environ['PATHS'].splitlines(), pattern)]))"
36+
python -c "import os,sys,fnmatch;sys.exit(not bool([_ for pattern in {'ddtrace/*', 'setup*', 'pyproject.toml', '.github/workflows/test_frameworks.yml'} for _ in fnmatch.filter(os.environ['PATHS'].splitlines(), pattern)]))"
3737
continue-on-error: true
3838

3939
bottle-testsuite:
@@ -65,7 +65,6 @@ jobs:
6565
DD_APPSEC_ENABLED: ${{ matrix.appsec }}
6666
DD_TESTING_RAISE: true
6767
CMAKE_BUILD_PARALLEL_LEVEL: 12
68-
DD_DEBUGGER_EXPL_OUTPUT_FILE: debugger-expl.txt
6968
DD_BYTECODE_INJECTION_OUTPUT_FILE: bytecode-injection.txt
7069

7170
defaults:
@@ -107,10 +106,7 @@ jobs:
107106
if: needs.needs-run.outputs.outcome == 'success'
108107
# Disable all test_simple tests because they check for
109108
# log output and it contains phony error messages.
110-
run: PYTHONPATH=../ddtrace/tests/debugging/exploration/ ddtrace-run pytest test --continue-on-collection-errors -v -k 'not test_simple'
111-
- name: Debugger exploration result
112-
if: needs.needs-run.outputs.outcome == 'success'
113-
run: cat debugger-expl.txt
109+
run: ddtrace-run pytest test --continue-on-collection-errors -v -k 'not test_simple'
114110
- name: Run Bytecode injection tests
115111
if: needs.needs-run.outputs.outcome == 'success'
116112
# Disable all test_simple tests because they check for
@@ -124,34 +120,16 @@ jobs:
124120
strategy:
125121
matrix:
126122
include:
127-
#- suffix: DI profiler
128-
# expl_profiler: 1
129-
# expl_coverage: 0
130-
# profiling: 1
131-
# iast: 0
132-
# appsec: 0
133-
#- suffix: DI coverage
134-
# expl_profiler: 0
135-
# expl_coverage: 1
136-
# profiling: 1
137-
# iast: 0
138-
# appsec: 0
139123
# Disabled while the bug is investigated: APPSEC-53222
140124
# - suffix: IAST
141-
# expl_profiler: 0
142-
# expl_coverage: 0
143125
# profiling: 0
144126
# iast: 1
145127
# appsec: 0
146128
- suffix: APPSEC
147-
expl_profiler: 0
148-
expl_coverage: 0
149129
profiling: 0
150130
iast: 0
151131
appsec: 1
152132
- suffix: Tracer only
153-
expl_profiler: 0
154-
expl_coverage: 0
155133
profiling: 0
156134
iast: 0
157135
appsec: 0
@@ -166,13 +144,6 @@ jobs:
166144
DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING: disabled # To avoid a couple failures due to the extra query
167145
DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED: false # To avoid a couple failures due to the extra query
168146
DD_TESTING_RAISE: true
169-
DD_DEBUGGER_EXPL_ENCODE: 0 # Disabled to speed up
170-
DD_DEBUGGER_EXPL_PROFILER_ENABLED: ${{ matrix.expl_profiler }}
171-
DD_DEBUGGER_EXPL_PROFILER_DELETE_FUNCTION_PROBES: 1 # Delete to speed up
172-
DD_DEBUGGER_EXPL_COVERAGE_ENABLED: ${{ matrix.expl_coverage }}
173-
DD_DEBUGGER_EXPL_COVERAGE_DELETE_LINE_PROBES: 1 # Delete to speed up
174-
DD_DEBUGGER_EXPL_CONSERVATIVE: 1
175-
DD_DEBUGGER_EXPL_OUTPUT_FILE: debugger-expl.txt
176147
DD_BYTECODE_INJECTION_OUTPUT_FILE: bytecode-injection.txt
177148
CMAKE_BUILD_PARALLEL_LEVEL: 12
178149
defaults:
@@ -237,11 +208,7 @@ jobs:
237208
- name: Run tests
238209
if: needs.needs-run.outputs.outcome == 'success'
239210
# django.tests.requests module interferes with requests library patching in the tracer -> disable requests patch
240-
run: PYTHONPATH=../ddtrace/tests/debugging/exploration/:. DD_PATCH_MODULES=unittest:no DD_TRACE_REQUESTS_ENABLED=0 ddtrace-run tests/runtests.py --parallel 1
241-
242-
- name: Debugger exploration results
243-
if: needs.needs-run.outputs.outcome == 'success'
244-
run: cat debugger-expl.txt
211+
run: PYTHONPATH=. DD_PATCH_MODULES=unittest:no DD_TRACE_REQUESTS_ENABLED=0 ddtrace-run tests/runtests.py --parallel 1
245212

246213
- name: Run Bytecode injection tests
247214
if: needs.needs-run.outputs.outcome == 'success'
@@ -279,8 +246,7 @@ jobs:
279246
DD_PROFILING_ENABLED: ${{ matrix.profiling }}
280247
DD_IAST_ENABLED: ${{ matrix.iast }}
281248
DD_APPSEC_ENABLED: ${{ matrix.appsec }}
282-
PYTHONPATH: ../ddtrace/tests/debugging/exploration/:.
283-
DD_DEBUGGER_EXPL_OUTPUT_FILE: debugger-expl.txt
249+
PYTHONPATH: .
284250
CMAKE_BUILD_PARALLEL_LEVEL: 12
285251
defaults:
286252
run:
@@ -318,9 +284,6 @@ jobs:
318284
- name: Run tests
319285
if: needs.needs-run.outputs.outcome == 'success'
320286
run: ddtrace-run pytest graphene
321-
- name: Debugger exploration results
322-
if: needs.needs-run.outputs.outcome == 'success'
323-
run: cat debugger-expl.txt
324287

325288
fastapi-testsuite-0_92:
326289
strategy:
@@ -351,7 +314,6 @@ jobs:
351314
DD_IAST_ENABLED: ${{ matrix.iast }}
352315
DD_APPSEC_ENABLED: ${{ matrix.appsec }}
353316
CMAKE_BUILD_PARALLEL_LEVEL: 12
354-
DD_DEBUGGER_EXPL_OUTPUT_FILE: debugger-expl.txt
355317
DD_BYTECODE_INJECTION_OUTPUT_FILE: bytecode-injection.txt
356318
defaults:
357319
run:
@@ -388,10 +350,7 @@ jobs:
388350
- name: Test
389351
if: needs.needs-run.outputs.outcome == 'success'
390352
# https://github.com/tiangolo/fastapi/pull/10876
391-
run: PYTHONPATH=../ddtrace/tests/debugging/exploration/ ddtrace-run pytest -p no:warnings tests -k 'not test_warn_duplicate_operation_id'
392-
- name: Debugger exploration results
393-
if: needs.needs-run.outputs.outcome == 'success'
394-
run: cat debugger-expl.txt
353+
run: ddtrace-run pytest -p no:warnings tests -k 'not test_warn_duplicate_operation_id'
395354
- name: Bytecode injection Test
396355
if: needs.needs-run.outputs.outcome == 'success'
397356
# https://github.com/tiangolo/fastapi/pull/10876
@@ -429,7 +388,6 @@ jobs:
429388
DD_PROFILING_ENABLED: ${{ matrix.profiling }}
430389
DD_IAST_ENABLED: ${{ matrix.iast }}
431390
DD_APPSEC_ENABLED: ${{ matrix.appsec }}
432-
PYTHONPATH: ../ddtrace/tests/debugging/exploration/
433391
CMAKE_BUILD_PARALLEL_LEVEL: 12
434392
defaults:
435393
run:
@@ -519,8 +477,6 @@ jobs:
519477
env:
520478
# Disabled distributed tracing since there are a lot of tests that assert on headers
521479
DD_HTTPX_DISTRIBUTED_TRACING: "false"
522-
# Debugger exploration testing does not work in CI
523-
# PYTHONPATH: ../ddtrace/tests/debugging/exploration/
524480
DD_IAST_ENABLED: ${{ matrix.iast }}
525481
DD_APPSEC_ENABLED: ${{ matrix.appsec }}
526482
# test_pool_timeout raises RuntimeError: The connection pool was closed while 1 HTTP requests/responses were still in-flight
@@ -555,7 +511,6 @@ jobs:
555511
DD_PROFILING_ENABLED: ${{ matrix.profiling }}
556512
DD_IAST_ENABLED: ${{ matrix.iast }}
557513
DD_APPSEC_ENABLED: ${{ matrix.appsec }}
558-
PYTHONPATH: ../ddtrace/tests/debugging/exploration/
559514
CMAKE_BUILD_PARALLEL_LEVEL: 12
560515
defaults:
561516
run:
@@ -622,8 +577,6 @@ jobs:
622577
DD_PROFILING_ENABLED: ${{ matrix.profiling }}
623578
DD_IAST_ENABLED: ${{ matrix.iast }}
624579
DD_APPSEC_ENABLED: ${{ matrix.appsec }}
625-
PYTHONPATH: ../ddtrace/tests/debugging/exploration/
626-
DD_DEBUGGER_EXPL_OUTPUT_FILE: debugger-expl.txt
627580
CMAKE_BUILD_PARALLEL_LEVEL: 12
628581
defaults:
629582
run:
@@ -658,9 +611,6 @@ jobs:
658611
- name: Run tests
659612
if: needs.needs-run.outputs.outcome == 'success'
660613
run: pytest -W ignore --ddtrace-patch-all tests -k 'not test_request_headers and not test_subdomain_route and not test_websocket_headers and not test_staticfiles_with_invalid_dir_permissions_returns_401 and not test_contextvars[asyncio-CustomMiddlewareUsingBaseHTTPMiddleware]'
661-
- name: Debugger exploration results
662-
if: needs.needs-run.outputs.outcome == 'success'
663-
run: cat debugger-expl.txt
664614

665615
requests-testsuite-2_26_0:
666616
strategy:
@@ -691,7 +641,6 @@ jobs:
691641
DD_IAST_ENABLED: ${{ matrix.iast }}
692642
DD_APPSEC_ENABLED: ${{ matrix.appsec }}
693643
CMAKE_BUILD_PARALLEL_LEVEL: 12
694-
DD_DEBUGGER_EXPL_OUTPUT_FILE: debugger-expl.txt
695644
defaults:
696645
run:
697646
working-directory: requests
@@ -726,10 +675,7 @@ jobs:
726675
run: pip install --upgrade pytest==5.4.3
727676
- name: Run tests
728677
if: needs.needs-run.outputs.outcome == 'success'
729-
run: PYTHONPATH=../ddtrace/tests/debugging/exploration/ ddtrace-run pytest -p no:warnings tests
730-
- name: Debugger exploration results
731-
if: needs.needs-run.outputs.outcome == 'success'
732-
run: cat debugger-expl.txt
678+
run: ddtrace-run pytest -p no:warnings tests
733679

734680
asyncpg-testsuite:
735681
# https://github.com/MagicStack/asyncpg/blob/v0.25.0/.github/workflows/tests.yml#L125
@@ -816,7 +762,6 @@ jobs:
816762
DD_TESTING_RAISE: true
817763
DD_IAST_ENABLED: ${{ matrix.iast }}
818764
DD_APPSEC_ENABLED: ${{ matrix.appsec }}
819-
# PYTHONPATH: ../ddtrace/tests/debugging/exploration/
820765
CMAKE_BUILD_PARALLEL_LEVEL: 12
821766
defaults:
822767
run:
@@ -874,8 +819,6 @@ jobs:
874819
DD_PROFILING_ENABLED: ${{ matrix.profiling }}
875820
DD_IAST_ENABLED: ${{ matrix.iast }}
876821
DD_APPSEC_ENABLED: ${{ matrix.appsec }}
877-
PYTHONPATH: ../ddtrace/tests/debugging/exploration/
878-
DD_DEBUGGER_EXPL_OUTPUT_FILE: debugger-expl.txt
879822
CMAKE_BUILD_PARALLEL_LEVEL: 12
880823
defaults:
881824
run:
@@ -924,6 +867,3 @@ jobs:
924867
- name: Run deadlock tests
925868
if: needs.needs-run.outputs.outcome == 'success'
926869
run: ddtrace-run ./tests/gh-deadlocks.sh python39
927-
- name: Debugger exploration results
928-
if: needs.needs-run.outputs.outcome == 'success'
929-
run: cat debugger-expl.txt

.gitlab-ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ include:
3737
- local: ".gitlab/testrunner.yml"
3838
- local: ".gitlab/benchmarks/serverless.yml"
3939
- local: ".gitlab/native.yml"
40+
- local: ".gitlab/debugging/exploration.yml"
4041

4142
tests-gen:
4243
stage: tests
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"debugging::exploration::boto3":
2+
stage: tests
3+
extends: .cached_testrunner
4+
variables:
5+
DD_DEBUGGER_EXPL_INCLUDE: "boto3"
6+
DD_DEBUGGER_EXPL_OUTPUT_FILE: debugger-expl.txt
7+
DD_DEBUGGER_EXPL_STATUS_MESSAGES: 1
8+
PYTEST_PLUGINS: "exploration"
9+
PYTHONPATH: "${{CI_PROJECT_DIR}}/tests/debugging/exploration/pytest"
10+
parallel:
11+
matrix:
12+
- PYTHON_VERSION: "3.8"
13+
BOTO3_TAG: 1.37.38
14+
- PYTHON_VERSION: ["3.9", "3.10", "3.11", "3.12", "3.13"]
15+
BOTO3_TAG: 1.38.44
16+
script: |
17+
python${{PYTHON_VERSION}} -m pip install -e .
18+
git clone --depth 1 --branch ${BOTO3_TAG} https://github.com/boto/boto3.git
19+
cd boto3
20+
python${{PYTHON_VERSION}} scripts/ci/install
21+
python${{PYTHON_VERSION}} scripts/ci/run-tests
22+
cat debugger-expl.txt
23+
needs: []
24+
artifacts:
25+
paths:
26+
- debugger-expl.txt

scripts/gen_gitlab_config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,16 @@ def gen_build_base_venvs() -> None:
265265
f.write(template("build-base-venvs"))
266266

267267

268+
def gen_debugger_exploration() -> None:
269+
"""Generate the cached testrunner job.
270+
271+
We need to generate this dynamically from a template because it depends
272+
on the cached testrunner job, which is also generated dynamically.
273+
"""
274+
with TESTS_GEN.open("a") as f:
275+
f.write(template("debugger/exploration"))
276+
277+
268278
# -----------------------------------------------------------------------------
269279

270280
# The code below is the boilerplate that makes the script work. There is

tests/debugging/exploration/_config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class ExplorationConfig(DDConfig):
2929
encode = DDConfig.v(
3030
bool,
3131
"dd.debugger.expl.encode",
32-
default=True,
32+
default=False,
3333
help="Whether to encode the snapshots",
3434
)
3535

@@ -40,7 +40,7 @@ class ExplorationConfig(DDConfig):
4040
help="Whether to print exploration debugger status messages",
4141
)
4242

43-
include = DDConfig.v(
43+
includes = DDConfig.v(
4444
list,
4545
"dd.debugger.expl.include",
4646
parser=lambda v: [path.split(".") for path in v.split(",")],
@@ -58,7 +58,7 @@ class ExplorationConfig(DDConfig):
5858
conservative = DDConfig.v(
5959
bool,
6060
"dd.debugger.expl.conservative",
61-
default=False,
61+
default=True,
6262
help="Use extremely low capture limits to reduce overhead",
6363
)
6464

tests/debugging/exploration/_coverage.py

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from collections import defaultdict
22
from pathlib import Path
3-
import sys
43
from types import ModuleType
54
import typing as t
65

@@ -22,27 +21,38 @@
2221

2322

2423
# Track all the covered modules and its lines. Indexed by module origin.
25-
_tracked_modules: t.Dict[str, t.Tuple[ModuleType, t.Set[int]]] = {}
24+
_tracked_modules: t.Dict[Path, t.Tuple[ModuleType, t.Set[int]]] = {}
2625

2726

2827
class LineCollector(ModuleCollector):
2928
def on_collect(self, discovery: FunctionDiscovery) -> None:
3029
o = origin(discovery._module)
30+
if o is None:
31+
status("[coverage] cannot collect lines from %s, no origin found" % discovery._module.__name__)
32+
return
33+
3134
status("[coverage] collecting lines from %s" % o)
3235
_tracked_modules[o] = (discovery._module, {_ for _ in discovery.keys()})
33-
LineCoverage.add_probes(
34-
[
35-
create_snapshot_line_probe(
36-
probe_id="@".join([str(hash(f)), str(line)]),
37-
source_file=origin(sys.modules[f.__module__]),
38-
line=line,
39-
rate=0.0,
40-
limits=expl_config.limits,
36+
probes = []
37+
for line, fcps in discovery.items():
38+
for fcp in fcps:
39+
try:
40+
fcp.resolve()
41+
except ValueError:
42+
# This function-code pair is not from a function, e.g. a
43+
# class.
44+
continue
45+
probe_id = f"{o}:{line}"
46+
probes.append(
47+
create_snapshot_line_probe(
48+
probe_id=probe_id,
49+
source_file=o,
50+
line=line,
51+
rate=0.0,
52+
limits=expl_config.limits,
53+
)
4154
)
42-
for line, functions in discovery.items()
43-
for f in functions
44-
]
45-
)
55+
LineCoverage.add_probes(probes)
4656

4757

4858
class LineCoverage(ExplorationDebugger):
@@ -72,7 +82,7 @@ def report_coverage(cls) -> None:
7282
total_covered += len(seen_lines)
7383
log(
7484
("{:<%d} {:>5} {: 6.0f}%%" % w).format(
75-
str(o.relative_to(CWD)),
85+
str(o),
7686
len(lines),
7787
len(seen_lines) * 100.0 / len(lines) if lines else 0,
7888
)

tests/debugging/exploration/_profiler.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,21 @@ class FunctionCollector(ModuleCollector):
2525
def on_collect(self, discovery: FunctionDiscovery) -> None:
2626
module = discovery._module
2727
status("[profiler] Collecting functions from %s" % module.__name__)
28-
for fname, f in discovery._fullname_index.items():
29-
if origin(module) != Path(f.__code__.co_filename).resolve():
28+
for fname, fcp in discovery._fullname_index.items():
29+
try:
30+
f = fcp.resolve()
31+
except ValueError:
32+
# This function-code pair is not from a function, e.g. a class.
33+
continue
34+
if (o := origin(module)) != Path(f.__code__.co_filename).resolve():
3035
# Do not wrap functions that do not belong to the module. We
3136
# will have a chance to wrap them when we discover the module
3237
# they belong to.
3338
continue
3439
_tracked_funcs[fname] = 0
3540
DeterministicProfiler.add_probe(
3641
create_snapshot_function_probe(
37-
probe_id=str(hash(f)),
42+
probe_id=f"{o}:{f.__code__.co_firstlineno}",
3843
module=module.__name__,
3944
func_qname=fname.replace(module.__name__, "").lstrip("."),
4045
rate=float("inf"),

0 commit comments

Comments
 (0)