datadog-ci 1.23.3 → 1.24.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 +17 -2
- data/ext/datadog_ci_native/datadog_cov.c +48 -15
- data/lib/datadog/ci/ext/test.rb +14 -0
- data/lib/datadog/ci/test.rb +26 -0
- data/lib/datadog/ci/test_management/tests_properties.rb +2 -1
- data/lib/datadog/ci/test_retries/component.rb +14 -5
- data/lib/datadog/ci/test_visibility/component.rb +0 -15
- data/lib/datadog/ci/version.rb +2 -2
- 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: 30bc86afa5d05d656f9abfa7ffb5ff21d6f9a8fc5edeb5924651de4cdc6df10d
|
|
4
|
+
data.tar.gz: c846a97a47a74120d9926350360cefa4ca3fcdadea6819337ad2990f7856e267
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2e2a5b3efa12f1c64176e1751e86ebf803f8c894aba7c53453abb22732e0ed16a6087292538b2e03f0657dc9433ceba9e452f5899a95ace325a004ee12c37fbc
|
|
7
|
+
data.tar.gz: 461912b9cb9f406c4fd1cb9975ece506df7e979ed7b373b46c9e0060977f397b2da63095be353351d74b4273ea3cc1a354c12a4ef9dc7bf75a0a5ed065a16fa3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.24.0] - 2025-12-15
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
* Create final status tag on test events ([#433][])
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
* Add `branch` parameter to `/test-management/tests` request ([#436][])
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
* Fix an inheritance blind spot for Test Impact Analysis ([#440][])
|
|
13
|
+
|
|
3
14
|
## [1.23.3] - 2025-11-19
|
|
4
15
|
|
|
5
16
|
### Fixed
|
|
@@ -554,7 +565,8 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
|
554
565
|
|
|
555
566
|
- Ruby versions < 2.7 no longer supported ([#8][])
|
|
556
567
|
|
|
557
|
-
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.
|
|
568
|
+
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.24.0...main
|
|
569
|
+
[1.24.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.23.3...v1.24.0
|
|
558
570
|
[1.23.3]: https://github.com/DataDog/datadog-ci-rb/compare/v1.23.2...v1.23.3
|
|
559
571
|
[1.23.2]: https://github.com/DataDog/datadog-ci-rb/compare/v1.23.1...v1.23.2
|
|
560
572
|
[1.23.1]: https://github.com/DataDog/datadog-ci-rb/compare/v1.23.0...v1.23.1
|
|
@@ -786,4 +798,7 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
|
786
798
|
[#416]: https://github.com/DataDog/datadog-ci-rb/issues/416
|
|
787
799
|
[#418]: https://github.com/DataDog/datadog-ci-rb/issues/418
|
|
788
800
|
[#425]: https://github.com/DataDog/datadog-ci-rb/issues/425
|
|
789
|
-
[#430]: https://github.com/DataDog/datadog-ci-rb/issues/430
|
|
801
|
+
[#430]: https://github.com/DataDog/datadog-ci-rb/issues/430
|
|
802
|
+
[#433]: https://github.com/DataDog/datadog-ci-rb/issues/433
|
|
803
|
+
[#436]: https://github.com/DataDog/datadog-ci-rb/issues/436
|
|
804
|
+
[#440]: https://github.com/DataDog/datadog-ci-rb/issues/440
|
|
@@ -156,12 +156,12 @@ static VALUE dd_cov_allocate(VALUE klass) {
|
|
|
156
156
|
|
|
157
157
|
// Checks if the filename is located under the root folder of the project (but
|
|
158
158
|
// not in the ignored folder) and adds it to the impacted_files hash.
|
|
159
|
-
static
|
|
159
|
+
static bool record_impacted_file(struct dd_cov_data *dd_cov_data,
|
|
160
160
|
VALUE filename) {
|
|
161
161
|
char *filename_ptr = RSTRING_PTR(filename);
|
|
162
162
|
// if the current filename is not located under the root, we skip it
|
|
163
163
|
if (strncmp(dd_cov_data->root, filename_ptr, dd_cov_data->root_len) != 0) {
|
|
164
|
-
return;
|
|
164
|
+
return false;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
// if ignored_path is provided and the current filename is located under the
|
|
@@ -170,10 +170,11 @@ static void record_impacted_file(struct dd_cov_data *dd_cov_data,
|
|
|
170
170
|
if (dd_cov_data->ignored_path_len != 0 &&
|
|
171
171
|
strncmp(dd_cov_data->ignored_path, filename_ptr,
|
|
172
172
|
dd_cov_data->ignored_path_len) == 0) {
|
|
173
|
-
return;
|
|
173
|
+
return false;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
rb_hash_aset(dd_cov_data->impacted_files, filename, Qtrue);
|
|
177
|
+
return true;
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
// Executed on RUBY_EVENT_LINE event and captures the filename from
|
|
@@ -221,29 +222,61 @@ static VALUE safely_get_source_location(VALUE klass_name) {
|
|
|
221
222
|
return rescue_nil(get_source_location, klass_name);
|
|
222
223
|
}
|
|
223
224
|
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
VALUE klass = (VALUE)key;
|
|
229
|
-
struct dd_cov_data *dd_cov_data = (struct dd_cov_data *)data;
|
|
225
|
+
// Safely get class name, returns Qnil on any error
|
|
226
|
+
static VALUE safely_get_class_name(VALUE klass) {
|
|
227
|
+
return rescue_nil(rb_class_name, klass);
|
|
228
|
+
}
|
|
230
229
|
|
|
231
|
-
|
|
230
|
+
// Safely get module ancestors, returns Qnil on any error
|
|
231
|
+
static VALUE safely_get_mod_ancestors(VALUE klass) {
|
|
232
|
+
return rescue_nil(rb_mod_ancestors, klass);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static bool record_impacted_klass(struct dd_cov_data *dd_cov_data,
|
|
236
|
+
VALUE klass) {
|
|
237
|
+
VALUE klass_name = safely_get_class_name(klass);
|
|
232
238
|
if (klass_name == Qnil) {
|
|
233
|
-
return
|
|
239
|
+
return false;
|
|
234
240
|
}
|
|
235
241
|
|
|
236
242
|
VALUE source_location = safely_get_source_location(klass_name);
|
|
237
|
-
if (source_location == Qnil ||
|
|
238
|
-
|
|
243
|
+
if (source_location == Qnil || !RB_TYPE_P(source_location, T_ARRAY) ||
|
|
244
|
+
RARRAY_LEN(source_location) == 0) {
|
|
245
|
+
return false;
|
|
239
246
|
}
|
|
240
247
|
|
|
241
248
|
VALUE filename = RARRAY_AREF(source_location, 0);
|
|
242
249
|
if (filename == Qnil || !RB_TYPE_P(filename, T_STRING)) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return record_impacted_file(dd_cov_data, filename);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// This function is called for each class that was instantiated during the test
|
|
257
|
+
// run.
|
|
258
|
+
static int each_instantiated_klass(st_data_t key, st_data_t _value,
|
|
259
|
+
st_data_t data) {
|
|
260
|
+
VALUE klass = (VALUE)key;
|
|
261
|
+
struct dd_cov_data *dd_cov_data = (struct dd_cov_data *)data;
|
|
262
|
+
|
|
263
|
+
// rb_mod_ancestors returns an array containing the "klass" itself
|
|
264
|
+
// and all the parent classes and/or included/prepended modules
|
|
265
|
+
VALUE ancestors = safely_get_mod_ancestors(klass);
|
|
266
|
+
if (ancestors == Qnil || !RB_TYPE_P(ancestors, T_ARRAY)) {
|
|
243
267
|
return ST_CONTINUE;
|
|
244
268
|
}
|
|
245
269
|
|
|
246
|
-
|
|
270
|
+
long len = RARRAY_LEN(ancestors);
|
|
271
|
+
for (long i = 0; i < len; i++) {
|
|
272
|
+
VALUE mod = rb_ary_entry(ancestors, i);
|
|
273
|
+
if (mod == Qnil) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
record_impacted_klass(dd_cov_data, mod);
|
|
278
|
+
}
|
|
279
|
+
|
|
247
280
|
return ST_CONTINUE;
|
|
248
281
|
}
|
|
249
282
|
|
|
@@ -385,7 +418,7 @@ static VALUE dd_cov_stop(VALUE self) {
|
|
|
385
418
|
}
|
|
386
419
|
|
|
387
420
|
// process classes covered by allocation tracing
|
|
388
|
-
st_foreach(dd_cov_data->klasses_table,
|
|
421
|
+
st_foreach(dd_cov_data->klasses_table, each_instantiated_klass,
|
|
389
422
|
(st_data_t)dd_cov_data);
|
|
390
423
|
st_clear(dd_cov_data->klasses_table);
|
|
391
424
|
|
data/lib/datadog/ci/ext/test.rb
CHANGED
|
@@ -13,7 +13,21 @@ module Datadog
|
|
|
13
13
|
TAG_FRAMEWORK_VERSION = "test.framework_version"
|
|
14
14
|
TAG_NAME = "test.name"
|
|
15
15
|
TAG_SKIP_REASON = "test.skip_reason"
|
|
16
|
+
|
|
17
|
+
# Status is the result of a single test run
|
|
18
|
+
# See the [Datadog::CI::Ext::Test::Status] module for the list of possible values of this tag
|
|
16
19
|
TAG_STATUS = "test.status"
|
|
20
|
+
# Final status is the result that Datadog reports after all retries for a given test. It might be different
|
|
21
|
+
# from the status of the given test run:
|
|
22
|
+
#
|
|
23
|
+
# Example: new test was retried 10 times by Early Flake Detection. It succeeded 9 times and failed once.
|
|
24
|
+
# The final status will be "pass" because we keep CI green for flaky tests.
|
|
25
|
+
#
|
|
26
|
+
# This tag is useful to create monitors on hard failures: if test.final_status is "fail", then CI is red.
|
|
27
|
+
#
|
|
28
|
+
# See the [Datadog::CI::Ext::Test::Status] module for the list of possible values of this tag
|
|
29
|
+
TAG_FINAL_STATUS = "test.final_status"
|
|
30
|
+
|
|
17
31
|
TAG_SUITE = "test.suite"
|
|
18
32
|
TAG_MODULE = "test.module"
|
|
19
33
|
TAG_TYPE = "test.type"
|
data/lib/datadog/ci/test.rb
CHANGED
|
@@ -244,6 +244,32 @@ module Datadog
|
|
|
244
244
|
get_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR) == "true"
|
|
245
245
|
end
|
|
246
246
|
|
|
247
|
+
# @internal
|
|
248
|
+
def record_final_status
|
|
249
|
+
status = get_tag(Ext::Test::TAG_STATUS)
|
|
250
|
+
return if status.nil?
|
|
251
|
+
|
|
252
|
+
if [Ext::Test::Status::PASS, Ext::Test::Status::SKIP].include?(status)
|
|
253
|
+
set_tag(Ext::Test::TAG_FINAL_STATUS, status)
|
|
254
|
+
return
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
if should_ignore_failures?
|
|
258
|
+
set_tag(Ext::Test::TAG_FINAL_STATUS, Ext::Test::Status::PASS)
|
|
259
|
+
else
|
|
260
|
+
set_tag(Ext::Test::TAG_FINAL_STATUS, Ext::Test::Status::FAIL)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# @internal
|
|
265
|
+
def peek_duration
|
|
266
|
+
end_time = Core::Utils::Time.now.utc
|
|
267
|
+
start_time = tracer_span.start_time
|
|
268
|
+
|
|
269
|
+
return 0.0 if start_time.nil? || end_time.nil?
|
|
270
|
+
end_time - start_time
|
|
271
|
+
end
|
|
272
|
+
|
|
247
273
|
private
|
|
248
274
|
|
|
249
275
|
def record_test_result(datadog_status)
|
|
@@ -127,7 +127,8 @@ module Datadog
|
|
|
127
127
|
"attributes" => {
|
|
128
128
|
"repository_url" => test_session.git_repository_url,
|
|
129
129
|
"commit_message" => test_session.original_git_commit_message,
|
|
130
|
-
"sha" => test_session.original_git_commit_sha
|
|
130
|
+
"sha" => test_session.original_git_commit_sha,
|
|
131
|
+
"branch" => test_session.git_branch
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
}.to_json
|
|
@@ -94,18 +94,27 @@ module Datadog
|
|
|
94
94
|
|
|
95
95
|
def record_test_finished(test_span)
|
|
96
96
|
if current_retry_driver.nil?
|
|
97
|
-
#
|
|
97
|
+
# We always run test at least once and after the first pass create a correct retry driver
|
|
98
98
|
self.current_retry_driver = build_driver(test_span)
|
|
99
99
|
else
|
|
100
|
-
#
|
|
100
|
+
# After each retry we let the driver to record the result.
|
|
101
|
+
# Then the driver will decide if we should retry again.
|
|
101
102
|
current_retry_driver&.record_retry(test_span)
|
|
102
103
|
|
|
104
|
+
# We know that the test was already retried at least once so if we should not retry anymore, then this
|
|
105
|
+
# is the last retry.
|
|
103
106
|
tag_last_retry(test_span) unless should_retry?
|
|
104
107
|
end
|
|
105
|
-
end
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
+
# Some retry strategies such as Early Flake Detection change the number of retries based on
|
|
110
|
+
# how long the test was.
|
|
111
|
+
current_retry_driver&.record_duration(test_span.peek_duration)
|
|
112
|
+
|
|
113
|
+
# We need to set the final status of the test (what will be reported to the test framework) on the last execution
|
|
114
|
+
# no matter if test was retried or not
|
|
115
|
+
#
|
|
116
|
+
# If we should not retry at this point, it means that this execution is the last one (it might the only one as well).
|
|
117
|
+
test_span.record_final_status unless should_retry?
|
|
109
118
|
end
|
|
110
119
|
|
|
111
120
|
# this API is targeted on Cucumber instrumentation or any other that cannot leverage #with_retries method
|
|
@@ -121,8 +121,6 @@ module Datadog
|
|
|
121
121
|
|
|
122
122
|
if block
|
|
123
123
|
@context.trace_test(test_name, test_suite, service: service, tags: tags) do |test|
|
|
124
|
-
subscribe_to_after_stop_event(test.tracer_span)
|
|
125
|
-
|
|
126
124
|
on_test_started(test)
|
|
127
125
|
res = block.call(test)
|
|
128
126
|
on_test_finished(test)
|
|
@@ -130,7 +128,6 @@ module Datadog
|
|
|
130
128
|
end
|
|
131
129
|
else
|
|
132
130
|
test = @context.trace_test(test_name, test_suite, service: service, tags: tags)
|
|
133
|
-
subscribe_to_after_stop_event(test.tracer_span)
|
|
134
131
|
on_test_started(test)
|
|
135
132
|
test
|
|
136
133
|
end
|
|
@@ -340,10 +337,6 @@ module Datadog
|
|
|
340
337
|
Telemetry.event_finished(test)
|
|
341
338
|
end
|
|
342
339
|
|
|
343
|
-
def on_after_test_span_finished(tracer_span)
|
|
344
|
-
test_retries.record_test_span_duration(tracer_span)
|
|
345
|
-
end
|
|
346
|
-
|
|
347
340
|
# HELPERS
|
|
348
341
|
def single_active_test_suite
|
|
349
342
|
# when fetching test_suite to use as test's context, try local context instance first
|
|
@@ -357,14 +350,6 @@ module Datadog
|
|
|
357
350
|
block&.call(nil)
|
|
358
351
|
end
|
|
359
352
|
|
|
360
|
-
def subscribe_to_after_stop_event(tracer_span)
|
|
361
|
-
events = tracer_span.send(:events)
|
|
362
|
-
|
|
363
|
-
events.after_stop.subscribe do |span|
|
|
364
|
-
on_after_test_span_finished(span)
|
|
365
|
-
end
|
|
366
|
-
end
|
|
367
|
-
|
|
368
353
|
def set_codeowners(span)
|
|
369
354
|
source = span.source_file
|
|
370
355
|
owners = @codeowners.list_owners(source) if source
|
data/lib/datadog/ci/version.rb
CHANGED