datadog-ci 1.22.0 → 1.23.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 +26 -2
- data/lib/datadog/ci/contrib/rspec/example_group.rb +0 -1
- data/lib/datadog/ci/contrib/rspec/runner.rb +2 -2
- data/lib/datadog/ci/ext/dd_test.rb +18 -0
- data/lib/datadog/ci/ext/test_discovery.rb +1 -1
- data/lib/datadog/ci/remote/component.rb +16 -2
- data/lib/datadog/ci/remote/library_settings.rb +11 -3
- data/lib/datadog/ci/remote/library_settings_client.rb +2 -2
- data/lib/datadog/ci/test_discovery/component.rb +1 -0
- data/lib/datadog/ci/test_management/component.rb +33 -5
- data/lib/datadog/ci/test_management/tests_properties.rb +12 -4
- data/lib/datadog/ci/test_optimisation/component.rb +87 -1
- data/lib/datadog/ci/test_optimisation/skippable.rb +13 -5
- data/lib/datadog/ci/test_visibility/component.rb +34 -0
- data/lib/datadog/ci/test_visibility/known_tests.rb +12 -4
- data/lib/datadog/ci/test_visibility/store/process.rb +16 -6
- data/lib/datadog/ci/transport/http.rb +34 -7
- data/lib/datadog/ci/utils/stateful.rb +30 -0
- data/lib/datadog/ci/version.rb +1 -1
- data/lib/datadog/ci/worker.rb +1 -2
- metadata +2 -2
- data/lib/datadog/ci/contrib/rspec/helpers.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a07d37d208941128fa6c8e31303578021e07a2974e9b5fb27cd0bb7dd64baea
|
4
|
+
data.tar.gz: c9e258132d7e8fc0278db80c6659b96a2c2d68ac6f7c7a0f2b6720f98052dc38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 445771d8c3192ed674a30541c9b89444d4ecf3c95728f156cceac42d08c1fb800ca163876546b8cd5fe25dcabe17a2a47471efa51a71d539385795d9264de813
|
7
|
+
data.tar.gz: 62a95307714de058754837a753aed50564e8490fdfebc75472a11b464c417ed7020d59cbad18e69b622863e45a3699b5628229cd3b7fcd7781fe2a4d37feb3fb
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.23.0] - 2025-10-08
|
4
|
+
|
5
|
+
|
6
|
+
### Changed
|
7
|
+
* In test discovery mode output suiteSourceFile for each test ([#400][])
|
8
|
+
* DDTest integration changes ([#407][])
|
9
|
+
* Support context propagation from Datadog test runner ([#399][])
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
* Fix issue when parallel_tests doesn't track test sessions when using manual instrumentation ([#410][])
|
13
|
+
|
14
|
+
## [1.22.1] - 2025-08-25
|
15
|
+
|
16
|
+
### Fixed
|
17
|
+
|
18
|
+
* Fix configuration race condition that caused undefined library behaviour (marking old tests as new as an example) ([#402][])
|
19
|
+
|
3
20
|
## [1.22.0] - 2025-08-12
|
4
21
|
|
5
22
|
### Added
|
@@ -514,7 +531,9 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
514
531
|
|
515
532
|
- Ruby versions < 2.7 no longer supported ([#8][])
|
516
533
|
|
517
|
-
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.
|
534
|
+
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.23.0...main
|
535
|
+
[1.23.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.22.1...v1.23.0
|
536
|
+
[1.22.1]: https://github.com/DataDog/datadog-ci-rb/compare/v1.22.0...v1.22.1
|
518
537
|
[1.22.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.21.1...v1.22.0
|
519
538
|
[1.21.1]: https://github.com/DataDog/datadog-ci-rb/compare/v1.21.0...v1.21.1
|
520
539
|
[1.21.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.20.2...v1.21.0
|
@@ -731,4 +750,9 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
731
750
|
[#391]: https://github.com/DataDog/datadog-ci-rb/issues/391
|
732
751
|
[#393]: https://github.com/DataDog/datadog-ci-rb/issues/393
|
733
752
|
[#394]: https://github.com/DataDog/datadog-ci-rb/issues/394
|
734
|
-
[#396]: https://github.com/DataDog/datadog-ci-rb/issues/396
|
753
|
+
[#396]: https://github.com/DataDog/datadog-ci-rb/issues/396
|
754
|
+
[#399]: https://github.com/DataDog/datadog-ci-rb/issues/399
|
755
|
+
[#400]: https://github.com/DataDog/datadog-ci-rb/issues/400
|
756
|
+
[#402]: https://github.com/DataDog/datadog-ci-rb/issues/402
|
757
|
+
[#407]: https://github.com/DataDog/datadog-ci-rb/issues/407
|
758
|
+
[#410]: https://github.com/DataDog/datadog-ci-rb/issues/410
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require_relative "../../ext/test"
|
4
4
|
require_relative "../instrumentation"
|
5
5
|
require_relative "ext"
|
6
|
-
require_relative "helpers"
|
7
6
|
|
8
7
|
module Datadog
|
9
8
|
module CI
|
@@ -33,7 +32,8 @@ module Datadog
|
|
33
32
|
|
34
33
|
result = super
|
35
34
|
return result unless test_module && test_session
|
36
|
-
|
35
|
+
# distributed test session must end in the parent process (for RSpec it would be parallel_tests CLI)
|
36
|
+
return result if test_session.distributed
|
37
37
|
|
38
38
|
if result != 0
|
39
39
|
test_module.failed!
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module CI
|
5
|
+
module Ext
|
6
|
+
# Constants for integration with DDTest tool: https://github.com/DataDog/ddtest
|
7
|
+
module DDTest
|
8
|
+
PLAN_FOLDER = ".testoptimization"
|
9
|
+
TESTOPTIMIZATION_CACHE_PATH = "#{PLAN_FOLDER}/cache"
|
10
|
+
|
11
|
+
SETTINGS_FILE_NAME = "settings.json"
|
12
|
+
KNOWN_TESTS_FILE_NAME = "known_tests.json"
|
13
|
+
TEST_MANAGEMENT_TESTS_FILE_NAME = "test_management_tests.json"
|
14
|
+
SKIPPABLE_TESTS_FILE_NAME = "skippable_tests.json"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -6,7 +6,7 @@ module Datadog
|
|
6
6
|
# Defines constants for test discovery mode
|
7
7
|
module TestDiscovery
|
8
8
|
# Default output path for test discovery mode
|
9
|
-
DEFAULT_OUTPUT_PATH = "
|
9
|
+
DEFAULT_OUTPUT_PATH = "./#{DDTest::PLAN_FOLDER}/test_discovery/tests.json"
|
10
10
|
|
11
11
|
# Maximum buffer size before writing to file
|
12
12
|
MAX_BUFFER_SIZE = 10_000
|
@@ -35,7 +35,7 @@ module Datadog
|
|
35
35
|
# launch configuration workers
|
36
36
|
configuration_workers.each(&:perform)
|
37
37
|
|
38
|
-
# block until all workers are done
|
38
|
+
# block until all workers are done
|
39
39
|
configuration_workers.each(&:wait_until_done)
|
40
40
|
end
|
41
41
|
|
@@ -54,11 +54,25 @@ module Datadog
|
|
54
54
|
FILE_STORAGE_KEY
|
55
55
|
end
|
56
56
|
|
57
|
+
def restore_state_from_datadog_test_runner
|
58
|
+
Datadog.logger.debug { "Restoring library configuration from DDTest cache" }
|
59
|
+
|
60
|
+
settings = load_json(Ext::DDTest::SETTINGS_FILE_NAME)
|
61
|
+
if settings.nil?
|
62
|
+
Datadog.logger.debug { "Restoring library configuration failed, will request again" }
|
63
|
+
return false
|
64
|
+
end
|
65
|
+
|
66
|
+
Datadog.logger.debug { "Restored library configuration from DDTest: #{settings}" }
|
67
|
+
@library_configuration = LibrarySettings.from_json(settings)
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
57
71
|
private
|
58
72
|
|
59
73
|
def fetch_library_configuration(test_session)
|
60
74
|
# In test discovery mode, skip backend fetching and use default settings (everything is disabled)
|
61
|
-
return @library_configuration = LibrarySettings.
|
75
|
+
return @library_configuration = LibrarySettings.from_http_response(nil) if @test_discovery_enabled
|
62
76
|
|
63
77
|
# skip backend request if library configuration was loaded by a different process and stored on disk
|
64
78
|
library_configuration_loaded = load_component_state
|
@@ -14,9 +14,12 @@ module Datadog
|
|
14
14
|
module Remote
|
15
15
|
# Wrapper around the settings HTTP response
|
16
16
|
class LibrarySettings
|
17
|
-
def
|
18
|
-
|
19
|
-
|
17
|
+
def self.from_http_response(http_response)
|
18
|
+
new(http_response, nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.from_json(json)
|
22
|
+
new(nil, json)
|
20
23
|
end
|
21
24
|
|
22
25
|
def ok?
|
@@ -148,6 +151,11 @@ module Datadog
|
|
148
151
|
|
149
152
|
private
|
150
153
|
|
154
|
+
def initialize(http_response, json)
|
155
|
+
@http_response = http_response
|
156
|
+
@json = json
|
157
|
+
end
|
158
|
+
|
151
159
|
def test_management_payload
|
152
160
|
payload.fetch(
|
153
161
|
Ext::Transport::DD_API_SETTINGS_RESPONSE_TEST_MANAGEMENT_KEY,
|
@@ -25,7 +25,7 @@ module Datadog
|
|
25
25
|
|
26
26
|
def fetch(test_session)
|
27
27
|
api = @api
|
28
|
-
return LibrarySettings.
|
28
|
+
return LibrarySettings.from_http_response(nil) unless api
|
29
29
|
|
30
30
|
request_payload = payload(test_session)
|
31
31
|
Datadog.logger.debug("Fetching library settings with request: #{request_payload}")
|
@@ -51,7 +51,7 @@ module Datadog
|
|
51
51
|
)
|
52
52
|
end
|
53
53
|
|
54
|
-
library_settings = LibrarySettings.
|
54
|
+
library_settings = LibrarySettings.from_http_response(http_response)
|
55
55
|
|
56
56
|
Utils::Telemetry.inc(
|
57
57
|
Ext::Telemetry::METRIC_GIT_REQUESTS_SETTINGS_RESPONSE,
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative "../ext/telemetry"
|
4
4
|
require_relative "../ext/test"
|
5
|
+
require_relative "../ext/dd_test"
|
5
6
|
require_relative "../utils/stateful"
|
6
7
|
require_relative "../utils/telemetry"
|
7
8
|
require_relative "../utils/test_run"
|
@@ -35,11 +36,11 @@ module Datadog
|
|
35
36
|
|
36
37
|
test_session.set_tag(Ext::Test::TAG_TEST_MANAGEMENT_ENABLED, "true")
|
37
38
|
|
38
|
-
|
39
|
-
if
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
return if restore_state_from_datadog_test_runner
|
40
|
+
return if load_component_state
|
41
|
+
|
42
|
+
@tests_properties = @tests_properties_client.fetch(test_session)
|
43
|
+
store_component_state if test_session.distributed
|
43
44
|
|
44
45
|
Utils::Telemetry.distribution(
|
45
46
|
Ext::Telemetry::METRIC_TEST_MANAGEMENT_TESTS_RESPONSE_TESTS,
|
@@ -74,6 +75,33 @@ module Datadog
|
|
74
75
|
test_properties.fetch("attempt_to_fix", false)
|
75
76
|
end
|
76
77
|
|
78
|
+
def restore_state_from_datadog_test_runner
|
79
|
+
Datadog.logger.debug { "Restoring test management tests from DDTest cache" }
|
80
|
+
|
81
|
+
test_management_data = load_json(Ext::DDTest::TEST_MANAGEMENT_TESTS_FILE_NAME)
|
82
|
+
if test_management_data.nil?
|
83
|
+
Datadog.logger.debug { "Restoring test management tests failed, will request again" }
|
84
|
+
return false
|
85
|
+
end
|
86
|
+
|
87
|
+
Datadog.logger.debug { "Restored test management tests from DDTest: #{test_management_data}" }
|
88
|
+
|
89
|
+
# Use the TestsProperties::Response class method to parse the JSON data
|
90
|
+
# Wrap the data in the expected backend API format
|
91
|
+
wrapped_data = {
|
92
|
+
"data" => {
|
93
|
+
"attributes" => test_management_data
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
tests_properties_response = TestsProperties::Response.from_json(wrapped_data)
|
98
|
+
@tests_properties = tests_properties_response.tests
|
99
|
+
|
100
|
+
Datadog.logger.debug { "Found [#{@tests_properties.size}] test management tests from context" }
|
101
|
+
|
102
|
+
true
|
103
|
+
end
|
104
|
+
|
77
105
|
# Implementation of Stateful interface
|
78
106
|
def serialize_state
|
79
107
|
{
|
@@ -15,9 +15,12 @@ module Datadog
|
|
15
15
|
# fetches and stores a map of tests to their test management properties from the backend
|
16
16
|
class TestsProperties
|
17
17
|
class Response
|
18
|
-
def
|
19
|
-
|
20
|
-
|
18
|
+
def self.from_http_response(http_response)
|
19
|
+
new(http_response, nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.from_json(json)
|
23
|
+
new(nil, json)
|
21
24
|
end
|
22
25
|
|
23
26
|
def ok?
|
@@ -51,6 +54,11 @@ module Datadog
|
|
51
54
|
|
52
55
|
private
|
53
56
|
|
57
|
+
def initialize(http_response, json)
|
58
|
+
@http_response = http_response
|
59
|
+
@json = json
|
60
|
+
end
|
61
|
+
|
54
62
|
def payload
|
55
63
|
cached = @json
|
56
64
|
return cached unless cached.nil?
|
@@ -106,7 +114,7 @@ module Datadog
|
|
106
114
|
)
|
107
115
|
end
|
108
116
|
|
109
|
-
Response.
|
117
|
+
Response.from_http_response(http_response).tests
|
110
118
|
end
|
111
119
|
|
112
120
|
private
|
@@ -6,6 +6,7 @@ require "datadog/core/telemetry/logging"
|
|
6
6
|
|
7
7
|
require_relative "../ext/test"
|
8
8
|
require_relative "../ext/telemetry"
|
9
|
+
require_relative "../ext/dd_test"
|
9
10
|
|
10
11
|
require_relative "../git/local_repository"
|
11
12
|
|
@@ -84,7 +85,11 @@ module Datadog
|
|
84
85
|
load_datadog_cov! if @code_coverage_enabled
|
85
86
|
|
86
87
|
# Load component state first, and if successful, skip fetching skippable tests
|
87
|
-
|
88
|
+
# Also try to restore from DDTest cache if available
|
89
|
+
if skipping_tests?
|
90
|
+
return if load_component_state
|
91
|
+
return if restore_state_from_datadog_test_runner
|
92
|
+
|
88
93
|
fetch_skippable_tests(test_session)
|
89
94
|
store_component_state if test_session.distributed
|
90
95
|
end
|
@@ -210,8 +215,89 @@ module Datadog
|
|
210
215
|
FILE_STORAGE_KEY
|
211
216
|
end
|
212
217
|
|
218
|
+
def restore_state_from_datadog_test_runner
|
219
|
+
Datadog.logger.debug { "Restoring skippable tests from DDTest cache" }
|
220
|
+
|
221
|
+
skippable_tests_data = load_json(Ext::DDTest::SKIPPABLE_TESTS_FILE_NAME)
|
222
|
+
if skippable_tests_data.nil?
|
223
|
+
Datadog.logger.debug { "Restoring skippable tests failed, will request again" }
|
224
|
+
return false
|
225
|
+
end
|
226
|
+
|
227
|
+
Datadog.logger.debug { "Restored skippable tests from DDTest: #{skippable_tests_data}" }
|
228
|
+
|
229
|
+
transformed_data = transform_test_runner_data(skippable_tests_data)
|
230
|
+
|
231
|
+
Datadog.logger.debug { "Skippable tests after transformation: #{transformed_data}" }
|
232
|
+
|
233
|
+
# Use the Skippable::Response class to parse the transformed data
|
234
|
+
skippable_response = Skippable::Response.from_json(transformed_data)
|
235
|
+
|
236
|
+
@mutex.synchronize do
|
237
|
+
@correlation_id = skippable_response.correlation_id
|
238
|
+
@skippable_tests = skippable_response.tests
|
239
|
+
end
|
240
|
+
|
241
|
+
Datadog.logger.debug { "Found [#{@skippable_tests.size}] skippable tests from context" }
|
242
|
+
Datadog.logger.debug { "ITR correlation ID from context: #{@correlation_id}" }
|
243
|
+
|
244
|
+
true
|
245
|
+
end
|
246
|
+
|
213
247
|
private
|
214
248
|
|
249
|
+
# Transforms Test Runner skippable tests data format to the format expected by Skippable::Response
|
250
|
+
#
|
251
|
+
# Test Runner format:
|
252
|
+
# {
|
253
|
+
# "correlationId": "abc123",
|
254
|
+
# "skippableTests": {
|
255
|
+
# "suite_name": {
|
256
|
+
# "test_name": [{"suite": "suite_name", "name": "test_name", "parameters": "{...}", "configurations": {}}]
|
257
|
+
# }
|
258
|
+
# }
|
259
|
+
# }
|
260
|
+
#
|
261
|
+
# Expected format:
|
262
|
+
# {
|
263
|
+
# "meta": {"correlation_id": "abc123"},
|
264
|
+
# "data": [{"type": "test", "attributes": {"suite": "suite_name", "name": "test_name", "parameters": "{...}"}}]
|
265
|
+
# }
|
266
|
+
def transform_test_runner_data(skippable_tests_data)
|
267
|
+
skippable_tests = skippable_tests_data.fetch("skippableTests", {})
|
268
|
+
|
269
|
+
# Pre-calculate array size for better memory allocation
|
270
|
+
total_test_configs = skippable_tests.sum do |_, tests_hash|
|
271
|
+
tests_hash.sum { |_, test_configs| test_configs.size }
|
272
|
+
end
|
273
|
+
|
274
|
+
data_array = Array.new(total_test_configs)
|
275
|
+
index = 0
|
276
|
+
|
277
|
+
skippable_tests.each_value do |tests_hash|
|
278
|
+
tests_hash.each_value do |test_configs|
|
279
|
+
test_configs.each do |test_config|
|
280
|
+
data_array[index] = {
|
281
|
+
"type" => Ext::Test::ITR_TEST_SKIPPING_MODE,
|
282
|
+
"attributes" => {
|
283
|
+
"suite" => test_config["suite"],
|
284
|
+
"name" => test_config["name"],
|
285
|
+
"parameters" => test_config["parameters"]
|
286
|
+
}
|
287
|
+
}
|
288
|
+
index += 1
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
{
|
294
|
+
"meta" => {
|
295
|
+
"correlation_id" => skippable_tests_data["correlationId"]
|
296
|
+
},
|
297
|
+
"data" => data_array
|
298
|
+
}
|
299
|
+
end
|
300
|
+
|
215
301
|
def write(event)
|
216
302
|
# skip sending events if writer is not configured
|
217
303
|
@coverage_writer&.write(event)
|
@@ -14,9 +14,12 @@ module Datadog
|
|
14
14
|
module TestOptimisation
|
15
15
|
class Skippable
|
16
16
|
class Response
|
17
|
-
def
|
18
|
-
|
19
|
-
|
17
|
+
def self.from_http_response(http_response)
|
18
|
+
new(http_response, nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.from_json(json)
|
22
|
+
new(nil, json)
|
20
23
|
end
|
21
24
|
|
22
25
|
def ok?
|
@@ -50,6 +53,11 @@ module Datadog
|
|
50
53
|
|
51
54
|
private
|
52
55
|
|
56
|
+
def initialize(http_response, json)
|
57
|
+
@http_response = http_response
|
58
|
+
@json = json
|
59
|
+
end
|
60
|
+
|
53
61
|
def payload
|
54
62
|
cached = @json
|
55
63
|
return cached unless cached.nil?
|
@@ -74,7 +82,7 @@ module Datadog
|
|
74
82
|
|
75
83
|
def fetch_skippable_tests(test_session)
|
76
84
|
api = @api
|
77
|
-
return Response.
|
85
|
+
return Response.from_http_response(nil) unless api
|
78
86
|
|
79
87
|
request_payload = payload(test_session)
|
80
88
|
Datadog.logger.debug("Fetching skippable tests with request: #{request_payload}")
|
@@ -105,7 +113,7 @@ module Datadog
|
|
105
113
|
)
|
106
114
|
end
|
107
115
|
|
108
|
-
Response.
|
116
|
+
Response.from_http_response(http_response)
|
109
117
|
end
|
110
118
|
|
111
119
|
private
|
@@ -6,6 +6,7 @@ require "rbconfig"
|
|
6
6
|
require "datadog/core/utils/forking"
|
7
7
|
|
8
8
|
require_relative "context"
|
9
|
+
require_relative "known_tests"
|
9
10
|
require_relative "telemetry"
|
10
11
|
require_relative "total_coverage"
|
11
12
|
|
@@ -226,6 +227,36 @@ module Datadog
|
|
226
227
|
(forked? && !@context_service_uri.nil? && !@context_service_uri.empty?) || @is_client_process
|
227
228
|
end
|
228
229
|
|
230
|
+
def restore_state_from_datadog_test_runner
|
231
|
+
Datadog.logger.debug { "Restoring known tests from DDTest cache" }
|
232
|
+
|
233
|
+
known_tests_data = load_json(Ext::DDTest::KNOWN_TESTS_FILE_NAME)
|
234
|
+
if known_tests_data.nil?
|
235
|
+
Datadog.logger.debug { "Restoring known tests failed, will request again" }
|
236
|
+
return false
|
237
|
+
end
|
238
|
+
|
239
|
+
Datadog.logger.debug { "Restored known tests from DDTest: #{known_tests_data}" }
|
240
|
+
|
241
|
+
# Use the KnownTests class method to parse the JSON data
|
242
|
+
known_tests_data = {
|
243
|
+
"data" => {
|
244
|
+
"attributes" => known_tests_data
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
@known_tests = KnownTests::Response.from_json(known_tests_data).tests
|
249
|
+
@known_tests_enabled = !@known_tests.empty?
|
250
|
+
|
251
|
+
unless @known_tests_enabled
|
252
|
+
Datadog.logger.debug("Empty set of known tests from the DDTest cache file")
|
253
|
+
end
|
254
|
+
|
255
|
+
Datadog.logger.debug { "Found [#{@known_tests.size}] known tests from context" }
|
256
|
+
|
257
|
+
true
|
258
|
+
end
|
259
|
+
|
229
260
|
private
|
230
261
|
|
231
262
|
# DOMAIN EVENTS
|
@@ -398,6 +429,9 @@ module Datadog
|
|
398
429
|
end
|
399
430
|
|
400
431
|
def new_test?(test_span)
|
432
|
+
# check if @known_tests set is empty again
|
433
|
+
# to ensure that we don't tag tests as new unnecessarily
|
434
|
+
@known_tests_enabled = false if @known_tests_enabled && @known_tests.empty?
|
401
435
|
return false unless @known_tests_enabled
|
402
436
|
|
403
437
|
test_id = Utils::TestRun.datadog_test_id(test_span.name, test_span.test_suite_name)
|
@@ -14,9 +14,12 @@ module Datadog
|
|
14
14
|
# fetches and stores a list of known tests from the backend
|
15
15
|
class KnownTests
|
16
16
|
class Response
|
17
|
-
def
|
18
|
-
|
19
|
-
|
17
|
+
def self.from_http_response(http_response)
|
18
|
+
new(http_response, nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.from_json(json)
|
22
|
+
new(nil, json)
|
20
23
|
end
|
21
24
|
|
22
25
|
def ok?
|
@@ -44,6 +47,11 @@ module Datadog
|
|
44
47
|
|
45
48
|
private
|
46
49
|
|
50
|
+
def initialize(http_response, json)
|
51
|
+
@http_response = http_response
|
52
|
+
@json = json
|
53
|
+
end
|
54
|
+
|
47
55
|
def payload
|
48
56
|
cached = @json
|
49
57
|
return cached unless cached.nil?
|
@@ -99,7 +107,7 @@ module Datadog
|
|
99
107
|
)
|
100
108
|
end
|
101
109
|
|
102
|
-
Response.
|
110
|
+
Response.from_http_response(http_response).tests
|
103
111
|
end
|
104
112
|
|
105
113
|
private
|
@@ -9,8 +9,6 @@ module Datadog
|
|
9
9
|
module Store
|
10
10
|
# This context is shared between threads and represents the current test session and test module.
|
11
11
|
class Process
|
12
|
-
attr_reader :readonly_test_session, :readonly_test_module
|
13
|
-
|
14
12
|
def initialize
|
15
13
|
# we are using Monitor instead of Mutex because it is reentrant
|
16
14
|
@mutex = Monitor.new
|
@@ -52,11 +50,11 @@ module Datadog
|
|
52
50
|
end
|
53
51
|
|
54
52
|
def active_test_module
|
55
|
-
@test_module
|
53
|
+
@mutex.synchronize { @test_module }
|
56
54
|
end
|
57
55
|
|
58
56
|
def active_test_session
|
59
|
-
@test_session
|
57
|
+
@mutex.synchronize { @test_session }
|
60
58
|
end
|
61
59
|
|
62
60
|
def active_test_suite(test_suite_name)
|
@@ -82,16 +80,28 @@ module Datadog
|
|
82
80
|
@mutex.synchronize { @test_suites.delete(test_suite_name) }
|
83
81
|
end
|
84
82
|
|
83
|
+
def readonly_test_session
|
84
|
+
@mutex.synchronize { @readonly_test_session }
|
85
|
+
end
|
86
|
+
|
87
|
+
def readonly_test_module
|
88
|
+
@mutex.synchronize { @readonly_test_module }
|
89
|
+
end
|
90
|
+
|
85
91
|
def set_readonly_test_session(remote_test_session)
|
86
92
|
return if remote_test_session.nil?
|
87
93
|
|
88
|
-
@
|
94
|
+
@mutex.synchronize do
|
95
|
+
@readonly_test_session = Datadog::CI::ReadonlyTestSession.new(remote_test_session)
|
96
|
+
end
|
89
97
|
end
|
90
98
|
|
91
99
|
def set_readonly_test_module(remote_test_module)
|
92
100
|
return if remote_test_module.nil?
|
93
101
|
|
94
|
-
@
|
102
|
+
@mutex.synchronize do
|
103
|
+
@readonly_test_module = Datadog::CI::ReadonlyTestModule.new(remote_test_module)
|
104
|
+
end
|
95
105
|
end
|
96
106
|
end
|
97
107
|
end
|
@@ -24,6 +24,21 @@ module Datadog
|
|
24
24
|
MAX_RETRIES = 3
|
25
25
|
INITIAL_BACKOFF = 1
|
26
26
|
MAX_BACKOFF = 30
|
27
|
+
MAX_RETRY_TIME = 50
|
28
|
+
|
29
|
+
# Errors that should not be retried - fail fast
|
30
|
+
NON_RETRIABLE_ERRORS = [
|
31
|
+
Timeout::Error, # Don't slow down customers with timeouts
|
32
|
+
Errno::EINVAL, # Invalid argument
|
33
|
+
Net::HTTPBadResponse # Malformed response - likely persistent issue
|
34
|
+
].freeze
|
35
|
+
|
36
|
+
# Errors that can be retried - transient network issues
|
37
|
+
RETRIABLE_ERRORS = [
|
38
|
+
Errno::ECONNRESET, # Connection reset by peer
|
39
|
+
EOFError, # Unexpected connection close
|
40
|
+
SocketError # DNS/network issues
|
41
|
+
].freeze
|
27
42
|
|
28
43
|
def initialize(host:, port:, timeout: DEFAULT_TIMEOUT, ssl: true, compress: false)
|
29
44
|
@host = host
|
@@ -78,7 +93,7 @@ module Datadog
|
|
78
93
|
|
79
94
|
private
|
80
95
|
|
81
|
-
def perform_http_call(path:, payload:, headers:, verb:, retries: MAX_RETRIES, backoff: INITIAL_BACKOFF)
|
96
|
+
def perform_http_call(path:, payload:, headers:, verb:, retries: MAX_RETRIES, backoff: INITIAL_BACKOFF, retry_start_time: Core::Utils::Time.get_time)
|
82
97
|
response = nil
|
83
98
|
|
84
99
|
begin
|
@@ -98,12 +113,23 @@ module Datadog
|
|
98
113
|
else
|
99
114
|
return response
|
100
115
|
end
|
101
|
-
rescue
|
102
|
-
Datadog.logger.debug { "Failed to send request with #{e} (#{e.message})" }
|
103
|
-
|
116
|
+
rescue *NON_RETRIABLE_ERRORS => e
|
117
|
+
Datadog.logger.debug { "Failed to send request with non-retriable error #{e} (#{e.message})" }
|
118
|
+
return ErrorResponse.new(e)
|
119
|
+
rescue *RETRIABLE_ERRORS => e
|
120
|
+
Datadog.logger.debug { "Failed to send request with retriable error #{e} (#{e.message})" }
|
104
121
|
response = ErrorResponse.new(e)
|
105
122
|
end
|
106
123
|
|
124
|
+
# Check if we've exceeded the maximum retry time
|
125
|
+
elapsed_time_seconds = Core::Utils::Time.get_time - retry_start_time
|
126
|
+
if elapsed_time_seconds >= MAX_RETRY_TIME
|
127
|
+
Datadog.logger.debug(
|
128
|
+
"Failed to send request to #{path} after #{elapsed_time_seconds.round(2)} seconds (exceeded MAX_RETRY_TIME of #{MAX_RETRY_TIME}s)"
|
129
|
+
)
|
130
|
+
return response
|
131
|
+
end
|
132
|
+
|
107
133
|
if retries.positive? && backoff <= MAX_BACKOFF
|
108
134
|
sleep(backoff)
|
109
135
|
|
@@ -113,11 +139,12 @@ module Datadog
|
|
113
139
|
headers: headers,
|
114
140
|
verb: verb,
|
115
141
|
retries: retries - 1,
|
116
|
-
backoff: backoff * 2
|
142
|
+
backoff: backoff * 2,
|
143
|
+
retry_start_time: retry_start_time
|
117
144
|
)
|
118
145
|
else
|
119
|
-
Datadog.logger.
|
120
|
-
"Failed to send request after #{MAX_RETRIES - retries} retries (current backoff value #{backoff})"
|
146
|
+
Datadog.logger.debug(
|
147
|
+
"Failed to send request to #{path} after #{MAX_RETRIES - retries} retries (current backoff value #{backoff})"
|
121
148
|
)
|
122
149
|
|
123
150
|
response
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "json"
|
3
4
|
require_relative "file_storage"
|
5
|
+
require_relative "../ext/dd_test"
|
4
6
|
|
5
7
|
module Datadog
|
6
8
|
module CI
|
@@ -19,6 +21,12 @@ module Datadog
|
|
19
21
|
|
20
22
|
# Load component state
|
21
23
|
def load_component_state
|
24
|
+
# Check for DDTest cache first
|
25
|
+
if Dir.exist?(Ext::DDTest::TESTOPTIMIZATION_CACHE_PATH)
|
26
|
+
Datadog.logger.debug { "DDTest cache found" }
|
27
|
+
return true if restore_state_from_datadog_test_runner
|
28
|
+
end
|
29
|
+
|
22
30
|
test_visibility_component = Datadog.send(:components).test_visibility
|
23
31
|
return false unless test_visibility_component.client_process?
|
24
32
|
|
@@ -46,6 +54,28 @@ module Datadog
|
|
46
54
|
def storage_key
|
47
55
|
raise NotImplementedError, "Components must implement #storage_key"
|
48
56
|
end
|
57
|
+
|
58
|
+
def restore_state_from_datadog_test_runner
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
def load_json(file_name)
|
63
|
+
file_path = File.join(Ext::DDTest::TESTOPTIMIZATION_CACHE_PATH, file_name)
|
64
|
+
|
65
|
+
unless File.exist?(file_path)
|
66
|
+
Datadog.logger.debug { "JSON file not found: #{file_path}" }
|
67
|
+
return nil
|
68
|
+
end
|
69
|
+
|
70
|
+
content = File.read(file_path)
|
71
|
+
JSON.parse(content)
|
72
|
+
rescue JSON::ParserError => e
|
73
|
+
Datadog.logger.debug { "Failed to parse JSON file #{file_path}: #{e.message}" }
|
74
|
+
nil
|
75
|
+
rescue => e
|
76
|
+
Datadog.logger.debug { "Failed to load JSON file #{file_path}: #{e.message}" }
|
77
|
+
nil
|
78
|
+
end
|
49
79
|
end
|
50
80
|
end
|
51
81
|
end
|
data/lib/datadog/ci/version.rb
CHANGED
data/lib/datadog/ci/worker.rb
CHANGED
@@ -11,13 +11,12 @@ module Datadog
|
|
11
11
|
include Datadog::Core::Workers::Async::Thread
|
12
12
|
|
13
13
|
DEFAULT_SHUTDOWN_TIMEOUT = 60
|
14
|
-
DEFAULT_WAIT_TIMEOUT = 60
|
15
14
|
|
16
15
|
def stop(timeout = DEFAULT_SHUTDOWN_TIMEOUT)
|
17
16
|
join(timeout)
|
18
17
|
end
|
19
18
|
|
20
|
-
def wait_until_done(timeout =
|
19
|
+
def wait_until_done(timeout = nil)
|
21
20
|
join(timeout)
|
22
21
|
end
|
23
22
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: datadog-ci
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.23.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Datadog, Inc.
|
@@ -143,7 +143,6 @@ files:
|
|
143
143
|
- lib/datadog/ci/contrib/rspec/example.rb
|
144
144
|
- lib/datadog/ci/contrib/rspec/example_group.rb
|
145
145
|
- lib/datadog/ci/contrib/rspec/ext.rb
|
146
|
-
- lib/datadog/ci/contrib/rspec/helpers.rb
|
147
146
|
- lib/datadog/ci/contrib/rspec/integration.rb
|
148
147
|
- lib/datadog/ci/contrib/rspec/patcher.rb
|
149
148
|
- lib/datadog/ci/contrib/rspec/runner.rb
|
@@ -166,6 +165,7 @@ files:
|
|
166
165
|
- lib/datadog/ci/contrib/simplecov/patcher.rb
|
167
166
|
- lib/datadog/ci/contrib/simplecov/result_extractor.rb
|
168
167
|
- lib/datadog/ci/ext/app_types.rb
|
168
|
+
- lib/datadog/ci/ext/dd_test.rb
|
169
169
|
- lib/datadog/ci/ext/environment.rb
|
170
170
|
- lib/datadog/ci/ext/environment/configuration_discrepancy_checker.rb
|
171
171
|
- lib/datadog/ci/ext/environment/extractor.rb
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Datadog
|
4
|
-
module CI
|
5
|
-
module Contrib
|
6
|
-
module RSpec
|
7
|
-
# Helper methods for RSpec instrumentation
|
8
|
-
module Helpers
|
9
|
-
def self.parallel_tests?
|
10
|
-
!!ENV.fetch("TEST_ENV_NUMBER", nil) && !!ENV.fetch("PARALLEL_TEST_GROUPS", nil)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|