datadog-ci 1.33.0 → 1.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -2
- data/lib/datadog/ci/configuration/components.rb +2 -0
- data/lib/datadog/ci/configuration/settings.rb +11 -0
- data/lib/datadog/ci/contrib/cucumber/formatter.rb +51 -1
- data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +5 -0
- data/lib/datadog/ci/contrib/minitest/helpers.rb +33 -0
- data/lib/datadog/ci/contrib/minitest/runnable.rb +5 -0
- data/lib/datadog/ci/contrib/minitest/runnable_minitest_6.rb +5 -0
- data/lib/datadog/ci/contrib/minitest/test.rb +21 -27
- data/lib/datadog/ci/contrib/patcher.rb +0 -4
- data/lib/datadog/ci/contrib/rspec/example_group.rb +11 -1
- data/lib/datadog/ci/ext/settings.rb +1 -0
- data/lib/datadog/ci/ext/telemetry.rb +1 -0
- data/lib/datadog/ci/ext/test.rb +8 -1
- data/lib/datadog/ci/remote/library_settings_client.rb +3 -2
- data/lib/datadog/ci/source_code/method_inspect.rb +0 -3
- data/lib/datadog/ci/test_impact_analysis/component.rb +176 -59
- data/lib/datadog/ci/test_impact_analysis/coverage/event.rb +6 -4
- data/lib/datadog/ci/test_impact_analysis/null_component.rb +25 -2
- data/lib/datadog/ci/test_impact_analysis/skippable.rb +20 -5
- data/lib/datadog/ci/test_impact_analysis/skippable_percentage/estimator.rb +3 -3
- data/lib/datadog/ci/test_optimization_cache/readers/legacy.rb +1 -1
- data/lib/datadog/ci/test_suite.rb +20 -0
- data/lib/datadog/ci/test_tracing/component.rb +2 -0
- data/lib/datadog/ci/utils/configuration.rb +12 -0
- data/lib/datadog/ci/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d2adf2fb252fdff7c6d0e1e73c2b6ade74c53bc5e8a238d447793da6833837c0
|
|
4
|
+
data.tar.gz: a49b2d09ae48bb2dc90c9846c6aae128a05d75353a2e7dc9df248960f165f556
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75d99396decbd20bb93cc2e061295be1e55c033b41980216bc0bb9229fd36269e7ad8bd5ad1104203ca10a32bed668aa65f41d01ddd5c8ebef34d206397a7c25
|
|
7
|
+
data.tar.gz: 3a0efeaaad0042cd836c51a7232a0348b377acf959bc6e10670e01ed5d7ea863caf60853142cb7e2d563da1c64ef1dadee5c3735a69bf482dfdb7b611239767d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.34.0] - 2026-06-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
* Add configurable TIA skipping mode ([#530][])
|
|
8
|
+
|
|
3
9
|
## [1.33.0] - 2026-06-19
|
|
4
10
|
|
|
5
11
|
### Added
|
|
@@ -659,7 +665,8 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
|
659
665
|
|
|
660
666
|
- Ruby versions < 2.7 no longer supported ([#8][])
|
|
661
667
|
|
|
662
|
-
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.
|
|
668
|
+
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.34.0...main
|
|
669
|
+
[1.34.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.33.0...v1.34.0
|
|
663
670
|
[1.33.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.32.0...v1.33.0
|
|
664
671
|
[1.32.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.31.0...v1.32.0
|
|
665
672
|
[1.31.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.30.0...v1.31.0
|
|
@@ -933,4 +940,5 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
|
933
940
|
[#516]: https://github.com/DataDog/datadog-ci-rb/issues/516
|
|
934
941
|
[#521]: https://github.com/DataDog/datadog-ci-rb/issues/521
|
|
935
942
|
[#522]: https://github.com/DataDog/datadog-ci-rb/issues/522
|
|
936
|
-
[#527]: https://github.com/DataDog/datadog-ci-rb/issues/527
|
|
943
|
+
[#527]: https://github.com/DataDog/datadog-ci-rb/issues/527
|
|
944
|
+
[#530]: https://github.com/DataDog/datadog-ci-rb/issues/530
|
|
@@ -210,6 +210,7 @@ module Datadog
|
|
|
210
210
|
api: test_visibility_api,
|
|
211
211
|
dd_env: settings.env,
|
|
212
212
|
config_tags: custom_configuration(settings),
|
|
213
|
+
test_skipping_mode: settings.ci.tia_test_skipping_mode,
|
|
213
214
|
coverage_writer: build_coverage_writer(settings, test_visibility_api),
|
|
214
215
|
enabled: settings.ci.enabled && settings.ci.itr_enabled,
|
|
215
216
|
bundle_location: settings.ci.itr_code_coverage_excluded_bundle_path,
|
|
@@ -293,6 +294,7 @@ module Datadog
|
|
|
293
294
|
Remote::LibrarySettingsClient.new(
|
|
294
295
|
api: api,
|
|
295
296
|
dd_env: settings.env,
|
|
297
|
+
test_skipping_mode: settings.ci.tia_test_skipping_mode,
|
|
296
298
|
config_tags: custom_configuration(settings)
|
|
297
299
|
)
|
|
298
300
|
end
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../contrib/instrumentation"
|
|
4
4
|
require_relative "../ext/settings"
|
|
5
|
+
require_relative "../ext/test"
|
|
5
6
|
require_relative "../ext/test_optimization_cache"
|
|
6
7
|
require_relative "../utils/bundle"
|
|
8
|
+
require_relative "../utils/configuration"
|
|
7
9
|
require_relative "../utils/parsing"
|
|
8
10
|
|
|
9
11
|
module Datadog
|
|
@@ -79,6 +81,15 @@ module Datadog
|
|
|
79
81
|
o.default true
|
|
80
82
|
end
|
|
81
83
|
|
|
84
|
+
option :tia_test_skipping_mode do |o|
|
|
85
|
+
o.type :string
|
|
86
|
+
o.env CI::Ext::Settings::ENV_TIA_TEST_SKIPPING_MODE
|
|
87
|
+
o.default CI::Ext::Test::TIATestSkippingMode::TEST
|
|
88
|
+
o.setter do |mode|
|
|
89
|
+
Utils::Configuration.normalize_tia_test_skipping_mode(mode)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
82
93
|
option :git_metadata_upload_enabled do |o|
|
|
83
94
|
o.type :bool
|
|
84
95
|
o.env CI::Ext::Settings::ENV_GIT_METADATA_UPLOAD_ENABLED
|
|
@@ -72,9 +72,14 @@ module Datadog
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
unless same_test_suite_as_current?(test_suite_name)
|
|
75
|
+
suite_tags = test_suite_source_file_tags(event.test_case)
|
|
76
|
+
if test_suite_unskippable?(event.test_case)
|
|
77
|
+
suite_tags[CI::Ext::Test::TAG_ITR_UNSKIPPABLE] = "true"
|
|
78
|
+
end
|
|
79
|
+
|
|
75
80
|
start_test_suite(
|
|
76
81
|
test_suite_name,
|
|
77
|
-
tags:
|
|
82
|
+
tags: suite_tags
|
|
78
83
|
)
|
|
79
84
|
end
|
|
80
85
|
|
|
@@ -228,6 +233,51 @@ module Datadog
|
|
|
228
233
|
CI::Ext::Test::TAG_SOURCE_START => line_number.to_s
|
|
229
234
|
}
|
|
230
235
|
end
|
|
236
|
+
|
|
237
|
+
def test_suite_unskippable?(test_case)
|
|
238
|
+
feature = if test_case.respond_to?(:feature)
|
|
239
|
+
test_case.feature
|
|
240
|
+
else
|
|
241
|
+
@ast_lookup&.gherkin_document(test_case.location.file)&.feature
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
node_has_unskippable_tag?(feature) || test_case_unskippable?(test_case)
|
|
245
|
+
rescue
|
|
246
|
+
test_case_unskippable?(test_case)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def node_has_unskippable_tag?(node)
|
|
250
|
+
return false if node.nil?
|
|
251
|
+
return true if node_tags_include_unskippable?(node)
|
|
252
|
+
|
|
253
|
+
if node.respond_to?(:scenario) && node.scenario
|
|
254
|
+
return true if node_has_unskippable_tag?(node.scenario)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
if node.respond_to?(:rule) && node.rule
|
|
258
|
+
return true if node_has_unskippable_tag?(node.rule)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
return false unless node.respond_to?(:children)
|
|
262
|
+
|
|
263
|
+
node.children.any? { |child| node_has_unskippable_tag?(child) }
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def node_tags_include_unskippable?(node)
|
|
267
|
+
return false unless node.respond_to?(:tags)
|
|
268
|
+
|
|
269
|
+
node.tags.any? { |tag| tag.respond_to?(:name) && tag.name == datadog_itr_unskippable_tag }
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def test_case_unskippable?(test_case)
|
|
273
|
+
test_case.match_tags?(datadog_itr_unskippable_tag)
|
|
274
|
+
rescue
|
|
275
|
+
false
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def datadog_itr_unskippable_tag
|
|
279
|
+
"@#{CI::Ext::Test::ITR_UNSKIPPABLE_OPTION}"
|
|
280
|
+
end
|
|
231
281
|
end
|
|
232
282
|
end
|
|
233
283
|
end
|
|
@@ -37,6 +37,11 @@ module Datadog
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def begin_scenario(test_case)
|
|
40
|
+
active_suite = Datadog::CI.active_test_suite
|
|
41
|
+
if active_suite&.should_skip?
|
|
42
|
+
raise ::Cucumber::Core::Test::Result::Skipped, active_suite.datadog_skip_reason
|
|
43
|
+
end
|
|
44
|
+
|
|
40
45
|
datadog_test = Datadog::CI.active_test
|
|
41
46
|
|
|
42
47
|
# special case for cucumber-ruby: we skip quarantined tests, thus for cucumber quarantined is the same as disabled.
|
|
@@ -7,6 +7,31 @@ module Datadog
|
|
|
7
7
|
module Contrib
|
|
8
8
|
module Minitest
|
|
9
9
|
module Helpers
|
|
10
|
+
module RunnableClassMethods
|
|
11
|
+
def datadog_itr_unskippable(*args)
|
|
12
|
+
if args.nil? || args.empty?
|
|
13
|
+
@datadog_itr_unskippable_suite = true
|
|
14
|
+
else
|
|
15
|
+
@datadog_itr_unskippable_tests = args
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def dd_suite_unskippable?
|
|
20
|
+
@datadog_itr_unskippable_suite
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def dd_test_unskippable?(test_name)
|
|
24
|
+
tests = @datadog_itr_unskippable_tests
|
|
25
|
+
return false unless tests
|
|
26
|
+
|
|
27
|
+
tests.include?(test_name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def dd_any_unskippable?
|
|
31
|
+
dd_suite_unskippable? || !!@datadog_itr_unskippable_tests
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
10
35
|
def self.start_test_suite(klass)
|
|
11
36
|
method = klass.runnable_methods.first
|
|
12
37
|
return nil if method.nil?
|
|
@@ -22,6 +47,9 @@ module Datadog
|
|
|
22
47
|
else
|
|
23
48
|
{}
|
|
24
49
|
end
|
|
50
|
+
if klass.dd_any_unskippable?
|
|
51
|
+
test_suite_tags[CI::Ext::Test::TAG_ITR_UNSKIPPABLE] = "true"
|
|
52
|
+
end
|
|
25
53
|
|
|
26
54
|
test_tracing_component = Datadog.send(:components).test_tracing
|
|
27
55
|
test_suite = test_tracing_component.start_test_suite(
|
|
@@ -60,6 +88,11 @@ module Datadog
|
|
|
60
88
|
source_location
|
|
61
89
|
end
|
|
62
90
|
|
|
91
|
+
def self.skip_test_suite(test_suite)
|
|
92
|
+
test_suite&.finish
|
|
93
|
+
[]
|
|
94
|
+
end
|
|
95
|
+
|
|
63
96
|
def self.parallel?(klass)
|
|
64
97
|
klass.ancestors.include?(::Minitest::Parallel::Test) || ci_queue?
|
|
65
98
|
end
|
|
@@ -10,11 +10,16 @@ module Datadog
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
module ClassMethods
|
|
13
|
+
include Helpers::RunnableClassMethods
|
|
14
|
+
|
|
13
15
|
def run(*args)
|
|
14
16
|
return super unless datadog_configuration[:enabled]
|
|
15
17
|
return super if Helpers.parallel?(self)
|
|
16
18
|
|
|
17
19
|
test_suite = Helpers.start_test_suite(self)
|
|
20
|
+
if test_suite&.should_skip?
|
|
21
|
+
return Helpers.skip_test_suite(test_suite)
|
|
22
|
+
end
|
|
18
23
|
|
|
19
24
|
results = super
|
|
20
25
|
return results unless test_suite
|
|
@@ -10,11 +10,16 @@ module Datadog
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
module ClassMethods
|
|
13
|
+
include Helpers::RunnableClassMethods
|
|
14
|
+
|
|
13
15
|
def run_suite(*args)
|
|
14
16
|
return super unless datadog_configuration[:enabled]
|
|
15
17
|
return super if Helpers.parallel?(self)
|
|
16
18
|
|
|
17
19
|
test_suite = Helpers.start_test_suite(self)
|
|
20
|
+
if test_suite&.should_skip?
|
|
21
|
+
return Helpers.skip_test_suite(test_suite)
|
|
22
|
+
end
|
|
18
23
|
|
|
19
24
|
results = super
|
|
20
25
|
return results unless test_suite
|
|
@@ -37,6 +37,11 @@ module Datadog
|
|
|
37
37
|
|
|
38
38
|
return run_without_datadog_reentry if datadog_run_reentered?
|
|
39
39
|
|
|
40
|
+
test_suite = start_datadog_test_suite_if_parallel
|
|
41
|
+
if test_suite&.should_skip?
|
|
42
|
+
return skip_datadog_suite(test_suite)
|
|
43
|
+
end
|
|
44
|
+
|
|
40
45
|
test_span = start_datadog_test
|
|
41
46
|
return skip_datadog_test(test_span) if test_span&.should_skip?
|
|
42
47
|
|
|
@@ -76,10 +81,6 @@ module Datadog
|
|
|
76
81
|
end
|
|
77
82
|
|
|
78
83
|
def start_datadog_test
|
|
79
|
-
if Helpers.parallel?(self.class)
|
|
80
|
-
Helpers.start_test_suite(self.class)
|
|
81
|
-
end
|
|
82
|
-
|
|
83
84
|
test_suite_name = Helpers.test_suite_name(self.class, name)
|
|
84
85
|
|
|
85
86
|
# @type var tags : Hash[String, String]
|
|
@@ -103,15 +104,17 @@ module Datadog
|
|
|
103
104
|
tags: tags,
|
|
104
105
|
service: datadog_configuration[:service_name]
|
|
105
106
|
)
|
|
106
|
-
# Steep type checker doesn't know that we patched Minitest::Test class definition
|
|
107
|
-
#
|
|
108
|
-
# steep:ignore:start
|
|
109
107
|
test_span&.itr_unskippable! if self.class.dd_suite_unskippable? || self.class.dd_test_unskippable?(name)
|
|
110
|
-
# steep:ignore:end
|
|
111
108
|
|
|
112
109
|
test_span
|
|
113
110
|
end
|
|
114
111
|
|
|
112
|
+
def start_datadog_test_suite_if_parallel
|
|
113
|
+
return unless Helpers.parallel?(self.class)
|
|
114
|
+
|
|
115
|
+
Helpers.start_test_suite(self.class)
|
|
116
|
+
end
|
|
117
|
+
|
|
115
118
|
def skip_datadog_test(test_span)
|
|
116
119
|
time_it do
|
|
117
120
|
capture_exceptions do
|
|
@@ -124,6 +127,16 @@ module Datadog
|
|
|
124
127
|
::Minitest::Result.from(self)
|
|
125
128
|
end
|
|
126
129
|
|
|
130
|
+
def skip_datadog_suite(test_suite)
|
|
131
|
+
time_it do
|
|
132
|
+
capture_exceptions do
|
|
133
|
+
skip(test_suite.datadog_skip_reason)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
::Minitest::Result.from(self)
|
|
138
|
+
end
|
|
139
|
+
|
|
127
140
|
def finish_with_result(span, result_code)
|
|
128
141
|
return unless span
|
|
129
142
|
|
|
@@ -158,25 +171,6 @@ module Datadog
|
|
|
158
171
|
ensure
|
|
159
172
|
super
|
|
160
173
|
end
|
|
161
|
-
|
|
162
|
-
def datadog_itr_unskippable(*args)
|
|
163
|
-
if args.nil? || args.empty?
|
|
164
|
-
@datadog_itr_unskippable_suite = true
|
|
165
|
-
else
|
|
166
|
-
@datadog_itr_unskippable_tests = args
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def dd_suite_unskippable?
|
|
171
|
-
@datadog_itr_unskippable_suite
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def dd_test_unskippable?(test_name)
|
|
175
|
-
tests = @datadog_itr_unskippable_tests
|
|
176
|
-
return false unless tests
|
|
177
|
-
|
|
178
|
-
tests.include?(test_name)
|
|
179
|
-
end
|
|
180
174
|
end
|
|
181
175
|
end
|
|
182
176
|
end
|
|
@@ -29,13 +29,9 @@ module Datadog
|
|
|
29
29
|
return unless defined?(super)
|
|
30
30
|
|
|
31
31
|
patch_only_once.run do
|
|
32
|
-
# There is no way to tell Steep that we prepend these methods to modules that have .patch method
|
|
33
|
-
#
|
|
34
|
-
# steep:ignore:start
|
|
35
32
|
super.tap do
|
|
36
33
|
@patch_successful = true
|
|
37
34
|
end
|
|
38
|
-
# steep:ignore:end
|
|
39
35
|
rescue => e
|
|
40
36
|
on_patch_error(e)
|
|
41
37
|
end
|
|
@@ -24,7 +24,7 @@ module Datadog
|
|
|
24
24
|
# even if it is a nested context
|
|
25
25
|
metadata[:skip] = true if all_examples_skipped_by_datadog?
|
|
26
26
|
|
|
27
|
-
# Start context coverage for this example group (for TIA
|
|
27
|
+
# Start context coverage for this example group (for TIA context coverage).
|
|
28
28
|
# This captures code executed in before(:context)/before(:all) hooks.
|
|
29
29
|
# The context_id uses scoped_id which is a stable identifier for RSpec example groups.
|
|
30
30
|
context_id = datadog_context_id
|
|
@@ -39,6 +39,9 @@ module Datadog
|
|
|
39
39
|
CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(metadata[:file_path]),
|
|
40
40
|
CI::Ext::Test::TAG_SOURCE_START => metadata[:line_number].to_s
|
|
41
41
|
}
|
|
42
|
+
if descendant_filtered_examples.any?(&:datadog_unskippable?)
|
|
43
|
+
suite_tags = suite_tags.merge(CI::Ext::Test::TAG_ITR_UNSKIPPABLE => "true")
|
|
44
|
+
end
|
|
42
45
|
|
|
43
46
|
test_suite =
|
|
44
47
|
test_tracing_component&.start_test_suite(
|
|
@@ -47,6 +50,8 @@ module Datadog
|
|
|
47
50
|
service: datadog_configuration[:service_name]
|
|
48
51
|
)
|
|
49
52
|
|
|
53
|
+
return skip_test_suite(test_suite) if test_suite&.should_skip?
|
|
54
|
+
|
|
50
55
|
success = super
|
|
51
56
|
|
|
52
57
|
return success unless test_suite
|
|
@@ -61,6 +66,11 @@ module Datadog
|
|
|
61
66
|
|
|
62
67
|
private
|
|
63
68
|
|
|
69
|
+
def skip_test_suite(test_suite)
|
|
70
|
+
test_suite&.finish
|
|
71
|
+
true
|
|
72
|
+
end
|
|
73
|
+
|
|
64
74
|
def all_examples_skipped_by_datadog?
|
|
65
75
|
descendant_filtered_examples.all? do |example|
|
|
66
76
|
datadog_test_id = example.datadog_test_id
|
|
@@ -12,6 +12,7 @@ module Datadog
|
|
|
12
12
|
ENV_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED = "DD_CIVISIBILITY_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED"
|
|
13
13
|
ENV_FORCE_TEST_LEVEL_VISIBILITY = "DD_CIVISIBILITY_FORCE_TEST_LEVEL_VISIBILITY"
|
|
14
14
|
ENV_ITR_ENABLED = "DD_CIVISIBILITY_ITR_ENABLED"
|
|
15
|
+
ENV_TIA_TEST_SKIPPING_MODE = "DD_TESTOPTIMIZATION_TIA_TEST_SKIPPING_MODE"
|
|
15
16
|
ENV_GIT_METADATA_UPLOAD_ENABLED = "DD_CIVISIBILITY_GIT_METADATA_UPLOAD_ENABLED"
|
|
16
17
|
ENV_ITR_CODE_COVERAGE_EXCLUDED_BUNDLE_PATH = "DD_CIVISIBILITY_ITR_CODE_COVERAGE_EXCLUDED_BUNDLE_PATH"
|
|
17
18
|
ENV_ITR_CODE_COVERAGE_USE_SINGLE_THREADED_MODE = "DD_CIVISIBILITY_ITR_CODE_COVERAGE_USE_SINGLE_THREADED_MODE"
|
|
@@ -44,6 +44,7 @@ module Datadog
|
|
|
44
44
|
METRIC_ITR_SKIPPABLE_TESTS_REQUEST_ERRORS = "itr_skippable_tests.request_errors"
|
|
45
45
|
METRIC_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES = "itr_skippable_tests.response_bytes"
|
|
46
46
|
METRIC_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS = "itr_skippable_tests.response_tests"
|
|
47
|
+
METRIC_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES = "itr_skippable_tests.response_suites"
|
|
47
48
|
|
|
48
49
|
METRIC_ITR_SKIPPED = "itr_skipped"
|
|
49
50
|
METRIC_ITR_UNSKIPPABLE = "itr_unskippable"
|
data/lib/datadog/ci/ext/test.rb
CHANGED
|
@@ -162,7 +162,14 @@ module Datadog
|
|
|
162
162
|
].freeze
|
|
163
163
|
|
|
164
164
|
# could be either "test" or "suite" depending on whether we skip individual tests or whole suites
|
|
165
|
-
|
|
165
|
+
module TIATestSkippingMode
|
|
166
|
+
TEST = "test"
|
|
167
|
+
SUITE = "suite"
|
|
168
|
+
|
|
169
|
+
ALL = [TEST, SUITE].freeze
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
DEFAULT_TIA_TEST_SKIPPING_MODE = TIATestSkippingMode::TEST
|
|
166
173
|
ITR_UNSKIPPABLE_OPTION = :datadog_itr_unskippable
|
|
167
174
|
|
|
168
175
|
EARLY_FLAKE_FAULTY = "faulty"
|
|
@@ -17,10 +17,11 @@ module Datadog
|
|
|
17
17
|
module Remote
|
|
18
18
|
# Calls settings endpoint to fetch library settings for given service and env
|
|
19
19
|
class LibrarySettingsClient
|
|
20
|
-
def initialize(dd_env:, api: nil, config_tags: {})
|
|
20
|
+
def initialize(dd_env:, api: nil, config_tags: {}, test_skipping_mode: Ext::Test::TIATestSkippingMode::TEST)
|
|
21
21
|
@api = api
|
|
22
22
|
@dd_env = dd_env
|
|
23
23
|
@config_tags = config_tags || {}
|
|
24
|
+
@test_skipping_mode = test_skipping_mode
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
def fetch(test_session)
|
|
@@ -87,7 +88,7 @@ module Datadog
|
|
|
87
88
|
"repository_url" => test_session.git_repository_url,
|
|
88
89
|
"branch" => test_session.git_branch || test_session.git_tag,
|
|
89
90
|
"sha" => test_session.git_commit_sha,
|
|
90
|
-
"test_level" =>
|
|
91
|
+
"test_level" => @test_skipping_mode,
|
|
91
92
|
"configurations" => {
|
|
92
93
|
Ext::Test::TAG_OS_PLATFORM => test_session.os_platform,
|
|
93
94
|
Ext::Test::TAG_OS_ARCHITECTURE => test_session.os_architecture,
|
|
@@ -16,11 +16,8 @@ module Datadog
|
|
|
16
16
|
return nil if target.nil?
|
|
17
17
|
return nil unless LAST_LINE_AVAILABLE
|
|
18
18
|
|
|
19
|
-
# Ruby has outdated RBS for RubyVM::InstructionSequence where method `of` is not defined
|
|
20
|
-
# steep:ignore:start
|
|
21
19
|
iseq = RubyVM::InstructionSequence.of(target)
|
|
22
20
|
return nil unless iseq.is_a?(RubyVM::InstructionSequence)
|
|
23
|
-
# steep:ignore:end
|
|
24
21
|
|
|
25
22
|
# this function is implemented in ext/datadog_ci_native/datadog_method_inspect.c
|
|
26
23
|
_native_last_line_from_iseq(iseq)
|
|
@@ -30,8 +30,8 @@ module Datadog
|
|
|
30
30
|
|
|
31
31
|
FILE_STORAGE_KEY = "test_impact_analysis_component_state"
|
|
32
32
|
|
|
33
|
-
attr_reader :correlation_id, :skippable_tests, :skippable_tests_fetch_error,
|
|
34
|
-
:enabled, :test_skipping_enabled, :code_coverage_enabled
|
|
33
|
+
attr_reader :correlation_id, :skippable_tests, :skippable_suites, :skippable_tests_fetch_error,
|
|
34
|
+
:enabled, :test_skipping_enabled, :code_coverage_enabled, :test_skipping_mode
|
|
35
35
|
|
|
36
36
|
def initialize(
|
|
37
37
|
dd_env:,
|
|
@@ -39,6 +39,7 @@ module Datadog
|
|
|
39
39
|
api: nil,
|
|
40
40
|
coverage_writer: nil,
|
|
41
41
|
enabled: false,
|
|
42
|
+
test_skipping_mode: Ext::Test::TIATestSkippingMode::TEST,
|
|
42
43
|
bundle_location: nil,
|
|
43
44
|
use_single_threaded_coverage: false,
|
|
44
45
|
use_allocation_tracing: true,
|
|
@@ -48,6 +49,7 @@ module Datadog
|
|
|
48
49
|
@api = api
|
|
49
50
|
@dd_env = dd_env
|
|
50
51
|
@config_tags = config_tags || {}
|
|
52
|
+
@test_skipping_mode = test_skipping_mode
|
|
51
53
|
|
|
52
54
|
@bundle_location = if bundle_location && !File.absolute_path?(bundle_location)
|
|
53
55
|
File.join(Git::LocalRepository.root, bundle_location)
|
|
@@ -65,6 +67,7 @@ module Datadog
|
|
|
65
67
|
|
|
66
68
|
@correlation_id = nil
|
|
67
69
|
@skippable_tests = Set.new
|
|
70
|
+
@skippable_suites = Set.new
|
|
68
71
|
|
|
69
72
|
@mutex = Mutex.new
|
|
70
73
|
|
|
@@ -92,8 +95,7 @@ module Datadog
|
|
|
92
95
|
|
|
93
96
|
test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_ENABLED, @test_skipping_enabled)
|
|
94
97
|
test_session.set_tag(Ext::Test::TAG_CODE_COVERAGE_ENABLED, @code_coverage_enabled)
|
|
95
|
-
|
|
96
|
-
test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_TYPE, Ext::Test::ITR_TEST_SKIPPING_MODE)
|
|
98
|
+
test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_TYPE, @test_skipping_mode)
|
|
97
99
|
|
|
98
100
|
if @code_coverage_enabled
|
|
99
101
|
load_datadog_cov!
|
|
@@ -102,10 +104,10 @@ module Datadog
|
|
|
102
104
|
end
|
|
103
105
|
|
|
104
106
|
# Load external cache or component state first, and if successful, skip fetching skippable tests
|
|
105
|
-
if skipping_tests?
|
|
107
|
+
if skipping_tests? || skipping_suites?
|
|
106
108
|
return if load_component_state
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
fetch_skippables(test_session)
|
|
109
111
|
store_component_state if test_session.distributed
|
|
110
112
|
end
|
|
111
113
|
|
|
@@ -117,7 +119,19 @@ module Datadog
|
|
|
117
119
|
end
|
|
118
120
|
|
|
119
121
|
def skipping_tests?
|
|
120
|
-
@test_skipping_enabled
|
|
122
|
+
@test_skipping_enabled && test_skipping_mode?
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def skipping_suites?
|
|
126
|
+
@test_skipping_enabled && suite_skipping_mode?
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def test_skipping_mode?
|
|
130
|
+
@test_skipping_mode == Ext::Test::TIATestSkippingMode::TEST
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def suite_skipping_mode?
|
|
134
|
+
@test_skipping_mode == Ext::Test::TIATestSkippingMode::SUITE
|
|
121
135
|
end
|
|
122
136
|
|
|
123
137
|
def code_coverage?
|
|
@@ -174,6 +188,7 @@ module Datadog
|
|
|
174
188
|
# @return [void]
|
|
175
189
|
def on_test_started(test)
|
|
176
190
|
return if !enabled? || !code_coverage?
|
|
191
|
+
return if suite_skipping_mode?
|
|
177
192
|
|
|
178
193
|
# Stop any in-progress context coverage and store it
|
|
179
194
|
stop_context_coverage_and_store
|
|
@@ -200,6 +215,7 @@ module Datadog
|
|
|
200
215
|
# @return [Datadog::CI::TestImpactAnalysis::Coverage::Event, nil] The coverage event or nil
|
|
201
216
|
def on_test_finished(test, context)
|
|
202
217
|
return unless enabled?
|
|
218
|
+
return if suite_skipping_mode?
|
|
203
219
|
|
|
204
220
|
# Handle ITR statistics
|
|
205
221
|
if test.skipped_by_test_impact_analysis?
|
|
@@ -222,33 +238,13 @@ module Datadog
|
|
|
222
238
|
context_ids = test.context_ids || []
|
|
223
239
|
merge_context_coverages_into_test(coverage, context_ids)
|
|
224
240
|
|
|
225
|
-
|
|
226
|
-
Telemetry.code_coverage_is_empty
|
|
227
|
-
return
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
# cucumber's gherkin files are not covered by the code coverage collector - we add them here explicitly
|
|
231
|
-
test_source_file = test.source_file
|
|
232
|
-
ensure_test_source_covered(test_source_file, coverage) unless test_source_file.nil?
|
|
233
|
-
|
|
234
|
-
# if we have static dependencies tracking enabled then we can make the coverage
|
|
235
|
-
# more precise by fetching which files we depend on based on constants usage
|
|
236
|
-
enrich_coverage_with_static_dependencies(coverage)
|
|
237
|
-
|
|
238
|
-
Telemetry.code_coverage_files(coverage.size)
|
|
239
|
-
|
|
240
|
-
coverage_event = Coverage::Event.new(
|
|
241
|
+
write_coverage_event(
|
|
241
242
|
test_id: test.id.to_s,
|
|
242
243
|
test_suite_id: test.test_suite_id.to_s,
|
|
243
244
|
test_session_id: test.test_session_id.to_s,
|
|
245
|
+
source_file: test.source_file,
|
|
244
246
|
coverage: coverage
|
|
245
247
|
)
|
|
246
|
-
|
|
247
|
-
Datadog.logger.debug { "Writing coverage event \n #{coverage_event.pretty_inspect}" }
|
|
248
|
-
|
|
249
|
-
write(coverage_event)
|
|
250
|
-
|
|
251
|
-
coverage_event
|
|
252
248
|
end
|
|
253
249
|
|
|
254
250
|
# Clears stored context coverage for a specific context.
|
|
@@ -271,7 +267,7 @@ module Datadog
|
|
|
271
267
|
#
|
|
272
268
|
# @return [Boolean]
|
|
273
269
|
def context_coverage_enabled?
|
|
274
|
-
enabled? && code_coverage? && !@use_single_threaded_coverage
|
|
270
|
+
enabled? && code_coverage? && !suite_skipping_mode? && !@use_single_threaded_coverage
|
|
275
271
|
end
|
|
276
272
|
|
|
277
273
|
def skippable?(datadog_test_id)
|
|
@@ -280,6 +276,12 @@ module Datadog
|
|
|
280
276
|
@mutex.synchronize { @skippable_tests.include?(datadog_test_id) }
|
|
281
277
|
end
|
|
282
278
|
|
|
279
|
+
def skippable_suite?(test_suite_name)
|
|
280
|
+
return false if !enabled? || !skipping_suites?
|
|
281
|
+
|
|
282
|
+
@mutex.synchronize { @skippable_suites.include?(test_suite_name) }
|
|
283
|
+
end
|
|
284
|
+
|
|
283
285
|
def mark_if_skippable(test)
|
|
284
286
|
return if !enabled? || !skipping_tests?
|
|
285
287
|
|
|
@@ -292,6 +294,66 @@ module Datadog
|
|
|
292
294
|
end
|
|
293
295
|
end
|
|
294
296
|
|
|
297
|
+
def mark_if_suite_skippable(test_suite)
|
|
298
|
+
return if !enabled? || !skipping_suites?
|
|
299
|
+
|
|
300
|
+
unskippable = test_suite.itr_unskippable?
|
|
301
|
+
Telemetry.itr_unskippable if unskippable
|
|
302
|
+
|
|
303
|
+
unless skippable_suite?(test_suite.name)
|
|
304
|
+
Datadog.logger.debug { "Test suite is not skippable: #{test_suite.name}" }
|
|
305
|
+
return
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
if unskippable
|
|
309
|
+
Telemetry.itr_forced_run
|
|
310
|
+
test_suite.set_tag(Ext::Test::TAG_ITR_FORCED_RUN, "true")
|
|
311
|
+
|
|
312
|
+
Datadog.logger.debug { "Forced run of skippable test suite: #{test_suite.name}" }
|
|
313
|
+
return
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
test_suite.set_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR, "true")
|
|
317
|
+
test_suite.skipped!(reason: Ext::Test::SkipReason::TEST_IMPACT_ANALYSIS)
|
|
318
|
+
|
|
319
|
+
Datadog.logger.debug { "Marked test suite as skippable: #{test_suite.name}" }
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def on_test_suite_started(test_suite)
|
|
323
|
+
return unless enabled? && suite_skipping_mode?
|
|
324
|
+
|
|
325
|
+
mark_if_suite_skippable(test_suite)
|
|
326
|
+
return if test_suite.should_skip?
|
|
327
|
+
return unless code_coverage?
|
|
328
|
+
|
|
329
|
+
Telemetry.code_coverage_started(test_suite)
|
|
330
|
+
coverage_collector&.start
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def on_test_suite_finished(test_suite, context)
|
|
334
|
+
return unless enabled? && suite_skipping_mode?
|
|
335
|
+
|
|
336
|
+
if test_suite.skipped_by_test_impact_analysis?
|
|
337
|
+
Telemetry.itr_skipped
|
|
338
|
+
context.incr_tests_skipped_by_tia_count
|
|
339
|
+
return
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
return unless code_coverage?
|
|
343
|
+
|
|
344
|
+
Telemetry.code_coverage_finished(test_suite)
|
|
345
|
+
|
|
346
|
+
coverage = coverage_collector&.stop
|
|
347
|
+
|
|
348
|
+
write_coverage_event(
|
|
349
|
+
test_id: nil,
|
|
350
|
+
test_suite_id: test_suite.id.to_s,
|
|
351
|
+
test_session_id: test_suite.get_tag(Ext::Test::TAG_TEST_SESSION_ID).to_s,
|
|
352
|
+
source_file: test_suite.source_file,
|
|
353
|
+
coverage: coverage
|
|
354
|
+
)
|
|
355
|
+
end
|
|
356
|
+
|
|
295
357
|
def write_test_session_tags(test_session, skipped_tests_count)
|
|
296
358
|
return if !enabled?
|
|
297
359
|
|
|
@@ -302,8 +364,8 @@ module Datadog
|
|
|
302
364
|
test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_COUNT, skipped_tests_count)
|
|
303
365
|
end
|
|
304
366
|
|
|
305
|
-
def
|
|
306
|
-
|
|
367
|
+
def skippables_count
|
|
368
|
+
current_skippables.count
|
|
307
369
|
end
|
|
308
370
|
|
|
309
371
|
def shutdown!
|
|
@@ -314,15 +376,17 @@ module Datadog
|
|
|
314
376
|
def serialize_state
|
|
315
377
|
{
|
|
316
378
|
correlation_id: @correlation_id,
|
|
317
|
-
skippable_tests: @skippable_tests
|
|
379
|
+
skippable_tests: @skippable_tests,
|
|
380
|
+
skippable_suites: @skippable_suites
|
|
318
381
|
}
|
|
319
382
|
end
|
|
320
383
|
|
|
321
384
|
def restore_state(state)
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
385
|
+
set_skippables(
|
|
386
|
+
correlation_id: state[:correlation_id],
|
|
387
|
+
tests: state[:skippable_tests] || Set.new,
|
|
388
|
+
suites: state[:skippable_suites] || Set.new
|
|
389
|
+
)
|
|
326
390
|
end
|
|
327
391
|
|
|
328
392
|
def storage_key
|
|
@@ -330,24 +394,20 @@ module Datadog
|
|
|
330
394
|
end
|
|
331
395
|
|
|
332
396
|
def restore_state_from_datadog_test_runner
|
|
333
|
-
Datadog.logger.debug { "Restoring
|
|
397
|
+
Datadog.logger.debug { "Restoring skippables from Test Optimization cache" }
|
|
334
398
|
|
|
335
|
-
|
|
336
|
-
if
|
|
337
|
-
Datadog.logger.debug { "Restoring
|
|
399
|
+
skippables_data = load_cached_skippable_tests
|
|
400
|
+
if skippables_data.nil?
|
|
401
|
+
Datadog.logger.debug { "Restoring skippables failed, will request again" }
|
|
338
402
|
return false
|
|
339
403
|
end
|
|
340
404
|
|
|
341
|
-
Datadog.logger.debug { "Restored
|
|
405
|
+
Datadog.logger.debug { "Restored skippables from Test Optimization: #{skippables_data}" }
|
|
342
406
|
|
|
343
|
-
skippable_response = Skippable::Response.from_json(
|
|
407
|
+
skippable_response = Skippable::Response.from_json(skippables_data)
|
|
408
|
+
apply_skippable_response(skippable_response)
|
|
344
409
|
|
|
345
|
-
|
|
346
|
-
@correlation_id = skippable_response.correlation_id
|
|
347
|
-
@skippable_tests = skippable_response.tests
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
Datadog.logger.debug { "Found [#{@skippable_tests.size}] skippable tests from context" }
|
|
410
|
+
Datadog.logger.debug { "Found [#{skippables_count}] skippable #{@test_skipping_mode}s from context" }
|
|
351
411
|
Datadog.logger.debug { "ITR correlation ID from context: #{@correlation_id}" }
|
|
352
412
|
|
|
353
413
|
true
|
|
@@ -406,27 +466,84 @@ module Datadog
|
|
|
406
466
|
coverage[absolute_test_source_file_path] = true
|
|
407
467
|
end
|
|
408
468
|
|
|
409
|
-
def
|
|
410
|
-
|
|
469
|
+
def write_coverage_event(test_id:, test_suite_id:, test_session_id:, source_file:, coverage:)
|
|
470
|
+
coverage ||= {}
|
|
471
|
+
if coverage.empty?
|
|
472
|
+
Telemetry.code_coverage_is_empty
|
|
473
|
+
return
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
ensure_test_source_covered(source_file, coverage) unless source_file.nil?
|
|
477
|
+
|
|
478
|
+
enrich_coverage_with_static_dependencies(coverage)
|
|
479
|
+
|
|
480
|
+
Telemetry.code_coverage_files(coverage.size)
|
|
481
|
+
|
|
482
|
+
coverage_event = Coverage::Event.new(
|
|
483
|
+
test_id: test_id,
|
|
484
|
+
test_suite_id: test_suite_id,
|
|
485
|
+
test_session_id: test_session_id,
|
|
486
|
+
coverage: coverage
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
Datadog.logger.debug { "Writing coverage event \n #{coverage_event.pretty_inspect}" }
|
|
490
|
+
|
|
491
|
+
write(coverage_event)
|
|
492
|
+
|
|
493
|
+
coverage_event
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def fetch_skippables(test_session)
|
|
497
|
+
return unless skipping_tests? || skipping_suites?
|
|
411
498
|
|
|
412
499
|
# we can only request skippable tests if git metadata is already uploaded
|
|
413
500
|
git_tree_upload_worker.wait_until_done
|
|
414
501
|
|
|
415
502
|
skippable_response =
|
|
416
|
-
Skippable.new(
|
|
417
|
-
|
|
503
|
+
Skippable.new(
|
|
504
|
+
api: @api,
|
|
505
|
+
dd_env: @dd_env,
|
|
506
|
+
config_tags: @config_tags,
|
|
507
|
+
test_skipping_mode: @test_skipping_mode
|
|
508
|
+
)
|
|
509
|
+
.fetch_skippables(test_session)
|
|
418
510
|
@skippable_tests_fetch_error = skippable_response.error_message unless skippable_response.ok?
|
|
419
511
|
|
|
512
|
+
apply_skippable_response(skippable_response)
|
|
513
|
+
|
|
514
|
+
Datadog.logger.debug { "Fetched skippable #{@test_skipping_mode}s: \n #{current_skippables}" }
|
|
515
|
+
Datadog.logger.debug { "Found #{skippables_count} skippable #{@test_skipping_mode}s." }
|
|
516
|
+
Datadog.logger.debug { "ITR correlation ID: #{@correlation_id}" }
|
|
517
|
+
|
|
518
|
+
Utils::Telemetry.inc(skippable_response_metric, skippables_count)
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def apply_skippable_response(skippable_response)
|
|
522
|
+
set_skippables(
|
|
523
|
+
correlation_id: skippable_response.correlation_id,
|
|
524
|
+
tests: skippable_response.tests,
|
|
525
|
+
suites: skippable_response.suites
|
|
526
|
+
)
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def set_skippables(correlation_id:, tests:, suites:)
|
|
420
530
|
@mutex.synchronize do
|
|
421
|
-
@correlation_id =
|
|
422
|
-
@skippable_tests =
|
|
531
|
+
@correlation_id = correlation_id
|
|
532
|
+
@skippable_tests = tests
|
|
533
|
+
@skippable_suites = suites
|
|
423
534
|
end
|
|
535
|
+
end
|
|
424
536
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
537
|
+
def current_skippables
|
|
538
|
+
suite_skipping_mode? ? @skippable_suites : @skippable_tests
|
|
539
|
+
end
|
|
428
540
|
|
|
429
|
-
|
|
541
|
+
def skippable_response_metric
|
|
542
|
+
if skipping_suites?
|
|
543
|
+
Ext::Telemetry::METRIC_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES
|
|
544
|
+
else
|
|
545
|
+
Ext::Telemetry::METRIC_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS
|
|
546
|
+
end
|
|
430
547
|
end
|
|
431
548
|
|
|
432
549
|
def code_coverage_mode
|
|
@@ -21,7 +21,7 @@ module Datadog
|
|
|
21
21
|
def valid?
|
|
22
22
|
valid = true
|
|
23
23
|
|
|
24
|
-
%i[
|
|
24
|
+
%i[test_suite_id test_session_id coverage].each do |key|
|
|
25
25
|
next unless send(key).nil?
|
|
26
26
|
|
|
27
27
|
Datadog.logger.warn("citestcov event is invalid: [#{key}] is nil. Event: #{self}")
|
|
@@ -34,7 +34,7 @@ module Datadog
|
|
|
34
34
|
def to_msgpack(packer = nil)
|
|
35
35
|
packer ||= MessagePack::Packer.new
|
|
36
36
|
|
|
37
|
-
packer.write_map_header(4)
|
|
37
|
+
packer.write_map_header(test_id.nil? ? 3 : 4)
|
|
38
38
|
|
|
39
39
|
packer.write("test_session_id")
|
|
40
40
|
packer.write(test_session_id.to_i)
|
|
@@ -42,8 +42,10 @@ module Datadog
|
|
|
42
42
|
packer.write("test_suite_id")
|
|
43
43
|
packer.write(test_suite_id.to_i)
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
unless test_id.nil?
|
|
46
|
+
packer.write("span_id")
|
|
47
|
+
packer.write(test_id.to_i)
|
|
48
|
+
end
|
|
47
49
|
|
|
48
50
|
files = coverage.keys
|
|
49
51
|
packer.write("files")
|
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
require "set"
|
|
4
4
|
|
|
5
|
+
require_relative "../ext/test"
|
|
6
|
+
|
|
5
7
|
module Datadog
|
|
6
8
|
module CI
|
|
7
9
|
module TestImpactAnalysis
|
|
8
10
|
# No-op implementation used when test impact analysis is disabled.
|
|
9
11
|
class NullComponent
|
|
10
12
|
attr_reader :enabled, :skippable_tests_fetch_error, :test_skipping_enabled,
|
|
11
|
-
:code_coverage_enabled, :skippable_tests, :correlation_id
|
|
13
|
+
:code_coverage_enabled, :skippable_tests, :skippable_suites, :correlation_id,
|
|
14
|
+
:test_skipping_mode
|
|
12
15
|
|
|
13
16
|
def initialize
|
|
14
17
|
@enabled = false
|
|
@@ -16,7 +19,9 @@ module Datadog
|
|
|
16
19
|
@code_coverage_enabled = false
|
|
17
20
|
@skippable_tests_fetch_error = nil
|
|
18
21
|
@skippable_tests = Set.new
|
|
22
|
+
@skippable_suites = Set.new
|
|
19
23
|
@correlation_id = nil
|
|
24
|
+
@test_skipping_mode = Ext::Test::TIATestSkippingMode::TEST
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
def configure(_remote_configuration = nil, _test_session = nil)
|
|
@@ -30,6 +35,10 @@ module Datadog
|
|
|
30
35
|
false
|
|
31
36
|
end
|
|
32
37
|
|
|
38
|
+
def skipping_suites?
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
33
42
|
def code_coverage?
|
|
34
43
|
false
|
|
35
44
|
end
|
|
@@ -51,6 +60,13 @@ module Datadog
|
|
|
51
60
|
nil
|
|
52
61
|
end
|
|
53
62
|
|
|
63
|
+
def on_test_suite_started(_test_suite)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def on_test_suite_finished(_test_suite, _context)
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
|
|
54
70
|
def clear_context_coverage(_context_id)
|
|
55
71
|
end
|
|
56
72
|
|
|
@@ -65,10 +81,17 @@ module Datadog
|
|
|
65
81
|
false
|
|
66
82
|
end
|
|
67
83
|
|
|
84
|
+
def skippable_suite?(_test_suite_name)
|
|
85
|
+
false
|
|
86
|
+
end
|
|
87
|
+
|
|
68
88
|
def write_test_session_tags(_test_session, _skipped_tests_count)
|
|
69
89
|
end
|
|
70
90
|
|
|
71
|
-
def
|
|
91
|
+
def mark_if_suite_skippable(_test_suite)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def skippables_count
|
|
72
95
|
0
|
|
73
96
|
end
|
|
74
97
|
|
|
@@ -36,7 +36,7 @@ module Datadog
|
|
|
36
36
|
|
|
37
37
|
payload.fetch("data", [])
|
|
38
38
|
.each do |test_data|
|
|
39
|
-
next unless test_data["type"] == Ext::Test::
|
|
39
|
+
next unless test_data["type"] == Ext::Test::TIATestSkippingMode::TEST
|
|
40
40
|
|
|
41
41
|
attrs = test_data["attributes"] || {}
|
|
42
42
|
res << Utils::TestRun.datadog_test_id(attrs["name"], attrs["suite"], attrs["parameters"])
|
|
@@ -45,6 +45,20 @@ module Datadog
|
|
|
45
45
|
res
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def suites
|
|
49
|
+
res = Set.new
|
|
50
|
+
|
|
51
|
+
payload.fetch("data", [])
|
|
52
|
+
.each do |test_data|
|
|
53
|
+
next unless test_data["type"] == Ext::Test::TIATestSkippingMode::SUITE
|
|
54
|
+
|
|
55
|
+
suite = (test_data["attributes"] || {})["suite"]
|
|
56
|
+
res << suite unless suite.nil?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
res
|
|
60
|
+
end
|
|
61
|
+
|
|
48
62
|
def error_message
|
|
49
63
|
return nil if ok?
|
|
50
64
|
|
|
@@ -74,18 +88,19 @@ module Datadog
|
|
|
74
88
|
end
|
|
75
89
|
end
|
|
76
90
|
|
|
77
|
-
def initialize(dd_env:, api: nil, config_tags: {})
|
|
91
|
+
def initialize(dd_env:, api: nil, config_tags: {}, test_skipping_mode: Ext::Test::TIATestSkippingMode::TEST)
|
|
78
92
|
@api = api
|
|
79
93
|
@dd_env = dd_env
|
|
80
94
|
@config_tags = config_tags
|
|
95
|
+
@test_skipping_mode = test_skipping_mode
|
|
81
96
|
end
|
|
82
97
|
|
|
83
|
-
def
|
|
98
|
+
def fetch_skippables(test_session)
|
|
84
99
|
api = @api
|
|
85
100
|
return Response.from_http_response(nil) unless api
|
|
86
101
|
|
|
87
102
|
request_payload = payload(test_session)
|
|
88
|
-
Datadog.logger.debug("Fetching skippable
|
|
103
|
+
Datadog.logger.debug("Fetching skippable #{@test_skipping_mode}s with request: #{request_payload}")
|
|
89
104
|
|
|
90
105
|
http_response = api.api_request(
|
|
91
106
|
path: Ext::Transport::DD_API_SKIPPABLE_TESTS_PATH,
|
|
@@ -127,7 +142,7 @@ module Datadog
|
|
|
127
142
|
"data" => {
|
|
128
143
|
"type" => Ext::Transport::DD_API_SKIPPABLE_TESTS_TYPE,
|
|
129
144
|
"attributes" => {
|
|
130
|
-
"test_level" =>
|
|
145
|
+
"test_level" => @test_skipping_mode,
|
|
131
146
|
"service" => test_session.service,
|
|
132
147
|
"env" => @dd_env,
|
|
133
148
|
"repository_url" => test_session.git_repository_url,
|
|
@@ -48,13 +48,13 @@ module Datadog
|
|
|
48
48
|
|
|
49
49
|
# starting and finishing a test session is required to get the skippable tests response
|
|
50
50
|
Datadog::CI.start_test_session(total_tests_count: estimated_tests_count)&.finish
|
|
51
|
-
|
|
51
|
+
skippables_count = test_impact_analysis.skippables_count
|
|
52
52
|
|
|
53
53
|
log("Estimated tests count: #{estimated_tests_count}")
|
|
54
|
-
log("
|
|
54
|
+
log("Skippables count: #{skippables_count}")
|
|
55
55
|
validate_test_impact_analysis_state!
|
|
56
56
|
|
|
57
|
-
[(
|
|
57
|
+
[(skippables_count.to_f / estimated_tests_count).floor(2), 0.99].min || 0.0
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
end
|
|
@@ -49,7 +49,7 @@ module Datadog
|
|
|
49
49
|
tests_hash.each_value.flat_map do |test_configs|
|
|
50
50
|
test_configs.map do |test_config|
|
|
51
51
|
{
|
|
52
|
-
"type" => Ext::Test::
|
|
52
|
+
"type" => Ext::Test::DEFAULT_TIA_TEST_SKIPPING_MODE,
|
|
53
53
|
"attributes" => {
|
|
54
54
|
"suite" => test_config["suite"],
|
|
55
55
|
"name" => test_config["name"],
|
|
@@ -111,6 +111,26 @@ module Datadog
|
|
|
111
111
|
end
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
# @internal
|
|
115
|
+
def datadog_skip_reason
|
|
116
|
+
get_tag(Ext::Test::TAG_SKIP_REASON)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @internal
|
|
120
|
+
def should_skip?
|
|
121
|
+
skipped_by_test_impact_analysis?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# @internal
|
|
125
|
+
def skipped_by_test_impact_analysis?
|
|
126
|
+
get_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR) == "true"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# @internal
|
|
130
|
+
def itr_unskippable?
|
|
131
|
+
get_tag(Ext::Test::TAG_ITR_UNSKIPPABLE) == "true"
|
|
132
|
+
end
|
|
133
|
+
|
|
114
134
|
private
|
|
115
135
|
|
|
116
136
|
def set_status_from_stats!
|
|
@@ -272,6 +272,7 @@ module Datadog
|
|
|
272
272
|
|
|
273
273
|
def on_test_suite_started(test_suite)
|
|
274
274
|
set_codeowners(test_suite)
|
|
275
|
+
test_impact_analysis.on_test_suite_started(test_suite)
|
|
275
276
|
end
|
|
276
277
|
|
|
277
278
|
def on_test_started(test)
|
|
@@ -318,6 +319,7 @@ module Datadog
|
|
|
318
319
|
end
|
|
319
320
|
|
|
320
321
|
def on_test_suite_finished(test_suite)
|
|
322
|
+
test_impact_analysis.on_test_suite_finished(test_suite, maybe_remote_context)
|
|
321
323
|
Telemetry.event_finished(test_suite)
|
|
322
324
|
end
|
|
323
325
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../ext/test"
|
|
3
4
|
require_relative "../git/local_repository"
|
|
4
5
|
|
|
5
6
|
module Datadog
|
|
@@ -13,6 +14,17 @@ module Datadog
|
|
|
13
14
|
def self.service_name_provided_by_user?
|
|
14
15
|
!!Datadog.configuration.service_without_fallback
|
|
15
16
|
end
|
|
17
|
+
|
|
18
|
+
def self.normalize_tia_test_skipping_mode(mode)
|
|
19
|
+
return mode if Ext::Test::TIATestSkippingMode::ALL.include?(mode)
|
|
20
|
+
|
|
21
|
+
Datadog.logger.warn(
|
|
22
|
+
"Invalid Test Impact Analysis skipping mode #{mode.inspect}. " \
|
|
23
|
+
"Falling back to #{Ext::Test::TIATestSkippingMode::TEST.inspect}."
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
Ext::Test::TIATestSkippingMode::TEST
|
|
27
|
+
end
|
|
16
28
|
end
|
|
17
29
|
end
|
|
18
30
|
end
|
data/lib/datadog/ci/version.rb
CHANGED