[SDTEST-3057] Track constants usage in each Ruby file as a static dependency to enrich Test Impact Analysis data#442
Merged
anmarchenko merged 32 commits intomainfrom Jan 9, 2026
Conversation
|
✅ Tests 🎉 All green!❄️ No new flaky tests detected 🎯 Code Coverage 🔗 Commit SHA: 4edd3b7 | Docs | Datadog PR Page | Was this helpful? Give us feedback! |
d1d20a4 to
9694345
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
2a37c96 to
ee43cab
Compare
ivoanjo
reviewed
Jan 6, 2026
Member
ivoanjo
left a comment
There was a problem hiding this comment.
Gave it a pass, left some notes ;)
…lepath is tracked by coverage engine
79ecd41 to
2ff1e30
Compare
…ependencies (native extension available + Ruby >= 3.2)
gnufede
approved these changes
Jan 8, 2026
Member
Author
|
did another round of testing here: anmarchenko/forem#7 the tests that depend on constants now being correctly not skipped |
Member
Author
|
we can merge it now |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Motivation
Bug report from a customer: tests are skipped when the code they test is using a constant defined in a separate file.
What does this PR do?
Test Impact Analysis relies on line-based code coverage. When Ruby executes the code, the coverage collector records which lines were hit. But here's the catch: constant access is not a line of code execution.
In a test like that:
When Ruby runs this test, the coverage collector sees the expect() call on that line. But
MyApp::Config::THRESHOLD? That's resolved through Ruby's constant lookup mechanism - it doesn't "execute" a line in config.rb. The constant value was loaded when the file was required, and now it's retrieved from Ruby's constants table.To solve this, we need to find constant references before the test runs. The only place that information exists? Ruby's compiled bytecode.
When Ruby parses the code, it compiles it into instructions for YARV. Among these instructions are two that handle constant access:
getconstant- Retrieves a constant by nameopt_getconstant_path- An optimized instruction for fully-qualified paths (e.g., Foo::Bar::Baz)You can read more about these instructions in Kevin Newton's excellent Advent of YARV.
I expanded the native C extension with an ability to walk the Ruby object space to find all live Instruction Sequences (ISeqs) - this is done similar to how DI propose to do it: DataDog/dd-trace-rb#5111
Then, we process the resulting iseqs array in Ruby to:
getconstantandopt_getconstant_pathinstructions{source_file => {dependency_file => true}}When a test finishes, we take the line-based coverage and enrich it with static dependencies. If your test file references
Constants::Config::THRESHOLD, we add config.rb to the coverage even though no lines in it were executed.The
opt_getconstant_pathinstruction stores the constant path as an array of symbols in its cache entry:opt_getconstant_path <ic:0 Constants::Config::THRESHOLD>We extract
[:Constants, :Config, :THRESHOLD], build the string"Constants::Config::THRESHOLD", then callObject.const_source_location("Constants::Config::THRESHOLD")to get the file path where that constant is defined.For getconstant, we get just the symbol name (e.g.,
:BaseConstant), which we try to resolve the same way—though this has limitations (see below).This feature is experimental and enabled with environment variable
DD_TEST_OPTIMIZATION_TIA_STATIC_DEPS_COVERAGE_ENABLEDLimitations
This will work only when the code is eager loaded in tests, otherwise the source location for constants will not be resolved correctly.
Class-level constant Resolution - inheritance and module inclusion happen at class definition time, not inside method bodies
It doesn't work for unqualified constant names (for now):
Ruby version compatibility
This feature works only for Ruby versions 3.2 and newer because it relies on
opt_getconstant_pathYARV instruction to be present.In Ruby 3.1:
In Ruby 3.2:
How to test the change?
There is a comprehensive testing suite provided