Use the manifest files under the manifests folder to declare what will be tested vs skipped, and under what conditions. See glossary for terminology definitions (enabled, disabled, xfail, xpass, skipped).
A test is enabled if:
- Nothing is specified in the manifest file and there aren't conflicting in-line decorators (e.g, @bug, see skip-tests.md) on the test
labelcontains a valid [https://semver.org/] version number. See enable-test.md to enable a test.
A test is disabled if label contains one of these markers: bug, missing_feature, flaky, irrelevant, or incomplete_test_app.
See skip-tests.md to disable a test.
When executed locally, tests run against the latest version of dd-trace by default. In CI, the tests run against the main branch and the latest version.
- Entries in the manifest file must be sorted in alphabetically. This is done/validated by
./format.sh. - Manifest files are validated using JSON schema in system tests CI
- An error will occur if a manifest file refers to a directory/file/class/function that does not exist
- After modifying any manifest file, always run
./format.shto validate syntax and sort entries alphabetically.
Each component has its own manifest file in the manifests/ directory.
manifests/cpp.yml- C++ librarymanifests/cpp_httpd.yml- C++ Apache httpd modulemanifests/cpp_kong.yml- C++ Kong modulemanifests/cpp_nginx.yml- C++ Nginx modulemanifests/dotnet.yml- .NET librarymanifests/golang.yml- Go librarymanifests/java.yml- Java librarymanifests/java_lambda.yml- Java AWS Lambda librarymanifests/java_otel.yml- Java OpenTelemetrymanifests/nodejs.yml- Node.js librarymanifests/nodejs_lambda.yml- Node.js AWS Lambdamanifests/nodejs_otel.yml- Node.js OpenTelemetrymanifests/php.yml- PHP librarymanifests/python.yml- Python librarymanifests/python_lambda.yml- Python AWS Lambdamanifests/python_otel.yml- Python OpenTelemetrymanifests/ruby.yml- Ruby librarymanifests/rust.yml- Rust library
manifests/agent.yml- Datadog Agent version conditionsmanifests/k8s_cluster_agent.yml- Kubernetes Cluster Agentmanifests/envoy.yml- Envoy proxymanifests/haproxy.yml- HAProxy
Note: Do not create new manifest files without consulting the team in #apm-shared-testing Slack channel first.
Tests are identified by a node ID with three optional components:
tests/path/to/file.py # Entire file
tests/path/to/file.py::TestClassName # Entire class
tests/path/to/file.py::TestClassName::test_method # Specific method
Enable a test from a specific version. The vX.Y.Z syntax is equivalent to >=X.Y.Z:
tests/path/test.py::TestClass: v1.2.0 # Enabled for versions >= 1.2.0
tests/path/test.py::TestClass::test_method: v2.0.0Note: Using X.Y.Z without the v prefix (e.g., 1.2.0) targets only that exact version. This is rarely intended - use vX.Y.Z to enable for that version and all future versions.
Disable a test with a marker (applies to all versions):
tests/path/test.py::TestClass: irrelevant
tests/path/test.py::TestClass: missing_feature
tests/path/test.py::TestClass: bug (JIRA-123)
tests/path/test.py::TestClass: flaky (JIRA-123)
tests/path/test.py::TestClass: incomplete_test_app (reason)Include an explanation in parentheses:
tests/path/test.py::TestClass: irrelevant (Not applicable to this library)
tests/path/test.py::TestClass: missing_feature (Feature not implemented yet)Use when you need both a marker AND a version constraint:
tests/path/test.py::TestClass::test_method:
- declaration: bug (JIRA-123)
component_version: '>=5.0.0'
tests/path/test.py::TestClass::test_method:
- declaration: missing_feature (Feature description)
component_version: '<2.5.0'The component_version field supports the full npm semver range syntax:
component_version: '>=1.0.0' # Greater than or equal
component_version: '<2.0.0' # Less than
component_version: '>=1.0.0 <2.0.0' # Range (AND)
component_version: '^1.3.0 || >=2.3.0' # Multiple ranges (OR)
component_version: '^1.3.0' # Caret: >=1.3.0 <2.0.0Use when different weblogs have different behavior:
tests/path/test.py::TestClass:
- weblog_declaration:
"*": v1.0.0 # Default for all weblogs
flask-poc: v2.0.0 # Different version for flask
fastapi: missing_feature # Not available for fastapi
express4: bug (JIRA-123) # Bug on express4Values containing YAML special characters MUST be quoted:
| Character | Meaning in YAML |
|---|---|
> < |
Folded/literal block indicators |
: |
Key-value separator |
# |
Comment |
@ ! * & | { } [ ] |
Reserved characters |
Examples:
# WRONG - will cause YAML parsing errors
component_version: >=5.0.0
test: irrelevant (Issue: something with colon)
# CORRECT - properly quoted
component_version: '>=5.0.0'
test: 'irrelevant (Issue: something with colon)'
test: "bug (See ticket #123)"Tip: When in doubt, quote the value. Single quotes (') are preferred for version ranges.
Use only these markers: irrelevant, bug, flaky, missing_feature, incomplete_test_app
Always include a JIRA ticket reference for bug and flaky markers.
refs:
- &5_6_and_someid_backports '>=5.6 || ^4.3.0 || ^4.3.0'
manifest:
tests/specific.py: irrelevant (see this link) # let skip an entire file
tests/appsec/test_distributed.py::Test_FeatureA: v1.14 # declare a version for a class
tests/appsec/test_distributed.py::Test_FeatureB: flaky # skip a class with bug, flaky, irrelevant ...
tests/appsec/test_distributed.py::Test_FeatureC: # declare a version for a class, depending on weblog
- weblog_declaration:
'*': missing_feature # All other weblogs: not yet available
django: v1.2
flask: v1.3
uwsgi: bug (jira ticket) # For a weblog, skip it with bug, or flaky
# declare compatibility for multiple release lines
# the caret character locks the major version (ie: `(>=1.3.0 && <2.0.0) || >= 2.3.0`)
tests/appsec/test_distributed.py::Test_FeatureD: ^1.3.0 || >=2.3.0
# reference an alias to avoid repeating long or complex semver versions
tests/appsec/test_distributed.py::Test_FeatureE: *5_6_and_someid_backportsBeyond simple version declarations and weblog_declaration, manifests support more complex patterns for fine-grained control. For reference test manifests, see tests/test_the_test/manifests/.
- The wildcard
*is supported for weblog declarations. This will associate missing_feature/bug/flaky/etc. marking to all unspecified weblog variables. - Manifests support the full npm syntax for SemVer specification. See more at: https://github.com/npm/node-semver#ranges
Define reusable weblog lists at the top of your manifest to avoid repetition:
refs:
- &django "django-poc, django-py3.13, python3.12"
- &flask "flask-poc, uwsgi-poc, uds-flask"
manifest:
tests/appsec/test_endpoint.py::Test_Endpoint:
- weblog_declaration:
"*": missing_feature
*django: v3.12.0.dev # Apply to all django weblogsUse declaration with component_version for version-specific markers:
manifest:
# Missing feature only for versions below 3.11.0
tests/appsec/iast:
- component_version: "<3.11.0"
declaration: missing_feature (APPSEC-57830 reason here)
# Bug in a specific version range
tests/appsec/test_example.py::TestClass::test_method:
- declaration: bug (JIRA-123)
component_version: '>=1.9.0'Apply a declaration only to specific weblogs. You can use a single weblog, a list, or combine YAML anchors with arrays:
refs:
- &django_weblogs "django-poc, django-py3.13, python3.12"
manifest:
# Missing feature only for specific weblogs
tests/appsec/test_feature.py::TestFeature:
- declaration: missing_feature
weblog: [sinatra14, sinatra22, sinatra32]
# Bug specific to one weblog with version constraint
tests/appsec/test_other.py:
- declaration: bug (TICKET-456)
component_version: "<3.13.0-dev"
weblog: django-poc
# Combine YAML reference with additional weblogs
tests/appsec/iast/test:
- component_version: "<3.11.0"
declaration: irrelevant
weblog: [*django_weblogs, fastapi]Apply a declaration to all weblogs except those listed:
manifest:
# Missing feature for all weblogs EXCEPT rack
tests/appsec/api_security/test_schemas.py::Test_Scanners:
- declaration: missing_feature (performance impact)
excluded_weblog: [rack]Apply different declarations to different weblog groups using multiple declaration blocks:
manifest:
tests/appsec/test_event.py::Test_Event:
# First: specific declaration for certain weblogs
- declaration: irrelevant
weblog: [rack, rails42]
# Second: different declaration for remaining weblogs
- declaration: missing_feature
excluded_weblog: [rack, rails42]Note: You cannot use both weblog and excluded_weblog in the same declaration block. Use multiple declaration blocks instead.
Apply rules to entire directories. More specific rules override directory rules:
manifest:
# All tests in the sink subdirectory
tests/appsec/iast/sink: "missing_feature"
# More specific rules override directory rules
tests/appsec/iast/sink/test_specific.py: "v2.0.0"The agent.yml manifest uses the same syntax for agent version conditions:
manifest:
tests/otel_tracing_e2e/test_e2e.py::Test_OTelLogE2E: "v7.48.0"
tests/test_sampling_rates.py::Test_SamplingRates: "v7.33.0"
tests/test_telemetry.py::Test_APMOnboardingInstallID: "v7.50.0"Here's a comprehensive example combining multiple advanced features:
refs:
- &django "django-poc, django-py3.13, python3.12"
- &flask "flask-poc, uwsgi-poc, uds-flask"
- &v2_backports '>=2.6.0 || ^1.5.0'
manifest:
# Simple version declaration
tests/appsec/test_simple.py::TestSimple: v2.6.0
# Weblog-specific versions using anchors
tests/appsec/test_endpoint.py::Test_Endpoint:
- weblog_declaration:
"*": missing_feature
*django: v3.12.0.dev
# Version constraint with declaration
tests/appsec/iast/sink:
- component_version: "<3.11.0"
declaration: missing_feature (APPSEC-57830)
weblog: *django
# Exclusion pattern
tests/appsec/test_schemas.py::Test_Scanners:
- declaration: missing_feature (performance impact)
excluded_weblog: [rack]
# Multiple conditions for the same test
tests/test_config.py::Test_Config:
- declaration: bug (APMAPI-1702)
weblog: [rails52, rails80, rails61]
- declaration: missing_feature (unknown version)
excluded_weblog: [rails52, rails80, rails61, rack]
# Backport version reference
tests/appsec/test_backport.py::TestBackport: *v2_backports| Field | Type | Description |
|---|---|---|
declaration |
string | Marker with optional reason: bug (JIRA-123), missing_feature, irrelevant, flaky |
component_version |
string | SemVer range: <1.0.0, >=2.0.0, ^1.3.0 || >=2.0.0 |
weblog |
string or list | Include only these weblogs |
excluded_weblog |
list | Exclude these weblogs (applies to all others) |
weblog_declaration |
map | Per-weblog declarations with * as default |
Manifest files weren't always part of system-tests. Understanding their origin helps to appreciate their benefits:
Initially, system-tests used in-line decorators like @released to specify test compatibility. While this approach had the benefit of keeping feature support declarations close to the tests themselves, it created significant challenges as the project grew:
-
Merge conflicts: With multiple teams working on different features simultaneously, decorators in test files created frequent merge conflicts, as the same files were being modified at the same time by different teams.
-
CI pipeline delays: Resolving these conflicts meant restarting CI pipelines, which with growing CI durations could delay PRs for hours.
-
Approval bottlenecks: Changes to test files required cross-team reviews, creating bottlenecks in the development process.
The manifest system addresses these challenges by providing:
-
Reduced conflicts: By separating feature support declarations from test implementations, conflicts are dramatically reduced. Teams can update their support status without touching test files.
-
Guild ownership: Each language team can manage their own manifest without affecting other teams, providing clear ownership and reducing cross-team dependencies.
-
Feature visibility: Although you no longer see supported languages directly in test files, manifests provide a better view of what features are supported across versions for each language.
-
CI efficiency: Less conflicts means less CI restarts, resulting in faster PR iterations and shorter release cycles.
- Test structure: Design tests around features, with the general rule that one test class = one feature
- Manifest declarations: Use manifests to declare at which version of your library a feature should be working
- When to use decorators: Decorators still have their place for:
- Complex skip conditions that cannot be expressed with simple version requirements
- Force-skip behavior via
@slowor@scenario_crash, combined with a manifest declaration
The manifest approach has proven to be transformative for the system-tests workflow. Teams can independently update their support status, CI pipelines run more efficiently, and the development process is more streamlined.
For searching across libraries, a simple "Find in Files" for your class name in your IDE will show you where it's referenced in all manifests, providing a comprehensive view of support.
The legacy way to declare what will be tested or not are decorators (@released, @bug, @missing_feature...). This solution offers several advantages:
- Declarations are as close as possible to the test, making the link obvious
- Complex conditions (several components involved, complex version range...) are easy to implement, as they're declared as Python code
Unfortunately, it comes with a major drawback: as those declarations are in test files, among all other declarations, activating a single test can become a nightmare due to conflicts between PRs. It also requires approval from the R&P team, slowing the process.