Validate code changes by intelligently selecting and running the appropriate test suites...
This skill helps you efficiently validate code changes by running the appropriate subset of the test suite. It uses scripts/run-tests to intelligently discover affected tests and run only what's necessary for validation.
Use this skill when you have:
scripts/run-tests--list first - see matching environments before executingdocs/contributing-testing.rst is the source of truth for testing proceduresFirst, determine which files were modified:
I'll use the scripts/run-tests script to discover what test suites match your changes:
scripts/run-tests --list <edited-files>
This outputs JSON showing:
The JSON goes to stdout, but status lines (for example 📁 Checking explicitly provided files: ...)
go to stderr. When piping into a JSON parser, do not use 2>&1, or the status line lands before
the JSON and parsing fails with Expecting value: line 1 column 1 (char 0). Use 2>/dev/null instead:
scripts/run-tests --list <edited-files> 2>/dev/null | jq '.suites[].venvs[] | {hash, python_version}'
Rather than running ALL available venvs (which could take hours), I'll select the minimal set needed to validate your changes:
When you modify files like:
ddtrace/internal/core/*, ddtrace/_trace/*, ddtrace/trace/*ddtrace/_monkey.py, ddtrace/settings/*ddtrace/constants.pyStrategy: Run core tracer + internal tests with 1 venv each
tracer suite with latest Python + internal suite with latest PythonWhen you modify files like:
ddtrace/contrib/flask/*, ddtrace/contrib/django/*, etc.ddtrace/contrib/*/patch.py or integration-specific codeStrategy: Run ONLY the affected integration suite with 1-2 venvs
contrib::flask suite with latest PythonWhen you modify tests/ files (but not test infrastructure):
-- -k test_nameWhen you modify:
tests/conftest.py, suite or environment definitions, dependency locks, or scripts/run-testsStrategy: Run a quick smoke test suite
internal suite with 1 venv as a sanity checkThe runner creates a missing or outdated environment and reuses a matching environment by default:
scripts/run-tests --venv <hash1> --venv <hash2>
Pass -r to rebuild the environment and refresh the editable ddtrace package:
scripts/run-tests -r --venv <hash1> --venv <hash2>
When to use -r:
.pyx, .pxd), or CMake files were modified (e.g., under ddtrace/internal/, ddtrace/appsec/_iast/_taint_tracking/, src/native/)setup.py, pyproject.toml, or setup.cfg were modifiedSelected dependency locks and environment definitions invalidate the cache automatically, but not all source and build inputs are tracked; use -r for the cases above.
This will:
If tests pass: ✅ Your changes are validated!
If tests fail: 🔴 I'll:
For re-running specific tests, use the default cached environment:
scripts/run-tests --venv <hash> -- -vv -k test_name
When you encounter test failures, follow this systematic approach:
-vv or -vvv for detailed outputFrom scripts/run-tests --list, you'll see output like:
{
"suites": [
{
"name": "tracer",
"venvs": [
{
"hash": "abc123",
"python_version": "3.8",
"packages": "..."
},
{
"hash": "def456",
"python_version": "3.11",
"packages": "..."
}
]
}
]
}
Latest Python version is your default choice
One venv per suite is usually enough for iteration
Minimize total venvs
Consider test runtime
--venv DirectlyWhen you have a specific venv hash you want to run, you can use it directly without specifying file paths:
scripts/run-tests --venv e06abee
The --venv flag automatically searches all available venvs across all suites, so it works regardless of what files you have locally changed. This is useful when:
Changed file: ddtrace/contrib/internal/flask/patch.py
scripts/run-tests --list ddtrace/contrib/internal/flask/patch.py
# Output shows: contrib::flask suite available
# Select output (latest Python):
# Suite: contrib::flask
# Venv: hash=e06abee, Python 3.13, flask
scripts/run-tests --venv e06abee
# Rebuild after changing native sources or build configuration
scripts/run-tests -r --venv e06abee
Changed file: ddtrace/_trace/tracer.py
scripts/run-tests --list ddtrace/_trace/tracer.py
# Output shows: tracer suite, internal suite available
# Select strategy:
# - tracer: latest Python (e.g., abc123)
# - internal: latest Python (e.g., def456)
scripts/run-tests --venv abc123 --venv def456
# Explicitly rebuild both environments when needed
scripts/run-tests -r --venv abc123 --venv def456
Changed file: tests/contrib/flask/test_views.py
scripts/run-tests --list tests/contrib/flask/test_views.py
# Output shows: contrib::flask suite
scripts/run-tests --venv flask_py311 -- -vv tests/contrib/flask/test_views.py
# The next run reuses the matching environment automatically
scripts/run-tests --venv flask_py311 -- -vv tests/contrib/flask/test_views.py
After the first run shows a test failing, rerun it directly:
scripts/run-tests --venv flask_py311 -- -vv -k test_view_called_twice
# Focused on the specific failing test with verbose output
-r for native changes: Refresh ddtrace after changing native sources or build configuration-k filter when re-running failuresgit status-r after native changes: Cached extensions may be staleFor comprehensive testing guidance, refer to the contributing documentation:
docs/contributing-testing.rst - Detailed testing guidelines
scripts/run-tests usage examplesdocs/contributing.rst - PR and testing requirements
docs/contributing-design.rst - Test architecture context
When to reference these docs:
contributing-testing.rstcontributing.rstcontributing-design.rst# Manually check/stop services:
docker compose ps
docker compose down
tests/suitespec.yml to understand suite patterns-k to run subset of testsThe scripts/run-tests system:
tests/suitespec.yml-- separator.Primary suites for validation:
tracer: Core tracing functionality testsinternal: Internal component testscontrib::*: Integration with specific libraries (flask, django, etc.)integration_*: Cross-library integration scenariostelemetry, profiling, appsec, llmobs, etc.Some suites require environment setup:
DD_TRACE_AGENT_URL: For snapshot-based testsYou can limit CPU and memory resources to simulate resource-constrained CI environments where multiple jobs run in parallel. This helps reproduce flaky tests that fail due to timing issues, race conditions, or resource exhaustion.
Environment Variables:
DD_TEST_CPUS: CPU limit (e.g., 0.25, 0.5, 1.0, 2.0)DD_TEST_MEMORY: Memory limit with unit (e.g., 512m, 1g, 2g)Usage:
# Run tests with resource constraints
DD_TEST_CPUS=0.5 DD_TEST_MEMORY=1g scripts/run-tests --venv <hash>
# Run specific test file with heavy constraints
DD_TEST_CPUS=0.25 DD_TEST_MEMORY=1g scripts/run-tests tests/path/to/test.py
# Multiple runs to catch intermittent failures
for i in {1..10}; do
DD_TEST_CPUS=0.5 DD_TEST_MEMORY=1g scripts/run-tests --venv <hash> -- --randomly-seed=$RANDOM
done
Recommended Resource Limits:
Moderate Load (Typical Shared CI):
DD_TEST_CPUS=2.0 DD_TEST_MEMORY=4g
Simulates a CI runner with some other jobs running. Good for initial testing.
Heavy Load (Busy CI Server):
DD_TEST_CPUS=1.0 DD_TEST_MEMORY=2g
Simulates a heavily loaded CI server with many concurrent jobs. Recommended starting point for reproducing flaky tests.
Extreme Load (Stress Testing):
DD_TEST_CPUS=0.5 DD_TEST_MEMORY=1g
Simulates extreme resource contention. Good for surfacing timing issues and race conditions.
Critical Failure Conditions:
DD_TEST_CPUS=0.25 DD_TEST_MEMORY=512m
Forces maximum resource pressure. Use this to find the breaking point or reproduce worst-case scenarios.
When to Use Resource Limits:
Verifying Limits Are Applied:
# Check configuration before running
DD_TEST_CPUS=0.5 DD_TEST_MEMORY=1g docker compose config | grep -A 5 testrunner
# Monitor actual resource usage during test run (in another terminal)
docker stats
Example: Testing a Flaky Test
# Run a known flaky test 20 times with resource constraints
DD_TEST_CPUS=0.5 DD_TEST_MEMORY=1g scripts/run-tests \
tests/appsec/integrations/flask_tests/test_iast_flask_testagent.py::test_iast_unvalidated_redirect \
-- --count=20