datadog-ci 1.4.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -2
- data/README.md +1 -0
- data/lib/datadog/ci/configuration/components.rb +14 -7
- data/lib/datadog/ci/configuration/settings.rb +11 -0
- data/lib/datadog/ci/contrib/cucumber/filter.rb +40 -0
- data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +15 -1
- data/lib/datadog/ci/contrib/cucumber/patcher.rb +0 -2
- data/lib/datadog/ci/contrib/minitest/runner.rb +6 -1
- data/lib/datadog/ci/contrib/minitest/test.rb +4 -0
- data/lib/datadog/ci/contrib/rspec/example.rb +11 -10
- data/lib/datadog/ci/contrib/rspec/runner.rb +2 -1
- data/lib/datadog/ci/ext/settings.rb +2 -0
- data/lib/datadog/ci/ext/telemetry.rb +9 -0
- data/lib/datadog/ci/ext/test.rb +12 -1
- data/lib/datadog/ci/ext/transport.rb +7 -0
- data/lib/datadog/ci/remote/component.rb +13 -2
- data/lib/datadog/ci/remote/library_settings.rb +48 -7
- data/lib/datadog/ci/remote/library_settings_client.rb +2 -1
- data/lib/datadog/ci/remote/slow_test_retries.rb +53 -0
- data/lib/datadog/ci/span.rb +7 -0
- data/lib/datadog/ci/test.rb +15 -2
- data/lib/datadog/ci/test_optimisation/component.rb +9 -6
- data/lib/datadog/ci/test_optimisation/skippable.rb +1 -1
- data/lib/datadog/ci/test_retries/component.rb +68 -39
- data/lib/datadog/ci/test_retries/driver/base.rb +25 -0
- data/lib/datadog/ci/test_retries/driver/no_retry.rb +16 -0
- data/lib/datadog/ci/test_retries/driver/retry_failed.rb +37 -0
- data/lib/datadog/ci/test_retries/driver/retry_new.rb +50 -0
- data/lib/datadog/ci/test_retries/null_component.rb +7 -6
- data/lib/datadog/ci/test_retries/strategy/base.rb +11 -4
- data/lib/datadog/ci/test_retries/strategy/no_retry.rb +0 -2
- data/lib/datadog/ci/test_retries/strategy/retry_failed.rb +30 -13
- data/lib/datadog/ci/test_retries/strategy/retry_new.rb +132 -0
- data/lib/datadog/ci/test_retries/unique_tests_client.rb +132 -0
- data/lib/datadog/ci/test_session.rb +20 -0
- data/lib/datadog/ci/test_suite.rb +8 -0
- data/lib/datadog/ci/test_visibility/component.rb +38 -15
- data/lib/datadog/ci/test_visibility/context.rb +4 -0
- data/lib/datadog/ci/test_visibility/null_component.rb +8 -1
- data/lib/datadog/ci/test_visibility/telemetry.rb +10 -3
- data/lib/datadog/ci/test_visibility/transport.rb +21 -3
- data/lib/datadog/ci/utils/test_run.rb +9 -1
- data/lib/datadog/ci/version.rb +2 -2
- data/lib/datadog/ci.rb +6 -3
- metadata +11 -5
- data/lib/datadog/ci/contrib/cucumber/configuration_override.rb +0 -37
- data/lib/datadog/ci/utils/identity.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f38fbb3ab8e2c8c1b058d7cbefb3ac30c59cd138fb2a69929d2c7309763b0c6e
|
4
|
+
data.tar.gz: 862f8c10065b47fb8763701399de65cf0da652f36f0d301ba3b0cd4e1c12080c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4be4dec3f2b5a11d0a822a3cd17d422eedab4262445dd42ba4d4efa96d4ee3f92049e5b37e1b6497928cf1fe980a847e4749538275092ca9fecd24700db3c62f
|
7
|
+
data.tar.gz: 2ebcae9e728ffd517d2c4f583808ade0170cb705967b59710213633bc73d1cd5d529c2b22323f8f70d45f657a8d848e856560aacc53c3ec7aea6e3aff45110af
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.6.0] - 2024-09-20
|
4
|
+
|
5
|
+
|
6
|
+
### Added
|
7
|
+
* support logical names for test sessions ([#235][])
|
8
|
+
* Send internal vCPU count metric ([#236][])
|
9
|
+
|
10
|
+
## [1.5.0] - 2024-09-18
|
11
|
+
|
12
|
+
### Added
|
13
|
+
* Retry new tests - parse remote configuration and fetch unique known tests ([#227][])
|
14
|
+
* early flake detection support for rspec and minitest ([#229][])
|
15
|
+
* Early flake detection support for Cucumber ([#231][])
|
16
|
+
|
17
|
+
### Fixed
|
18
|
+
* Minor telemetry fixes ([#226][])
|
19
|
+
|
3
20
|
## [1.4.1] - 2024-08-28
|
4
21
|
|
5
22
|
### Fixed
|
@@ -315,7 +332,9 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
315
332
|
|
316
333
|
- Ruby versions < 2.7 no longer supported ([#8][])
|
317
334
|
|
318
|
-
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.
|
335
|
+
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.6.0...main
|
336
|
+
[1.6.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.5.0...v1.6.0
|
337
|
+
[1.5.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.4.1...v1.5.0
|
319
338
|
[1.4.1]: https://github.com/DataDog/datadog-ci-rb/compare/v1.4.0...v1.4.1
|
320
339
|
[1.4.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.3.0...v1.4.0
|
321
340
|
[1.3.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.2.0...v1.3.0
|
@@ -452,4 +471,10 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
452
471
|
[#219]: https://github.com/DataDog/datadog-ci-rb/issues/219
|
453
472
|
[#220]: https://github.com/DataDog/datadog-ci-rb/issues/220
|
454
473
|
[#221]: https://github.com/DataDog/datadog-ci-rb/issues/221
|
455
|
-
[#224]: https://github.com/DataDog/datadog-ci-rb/issues/224
|
474
|
+
[#224]: https://github.com/DataDog/datadog-ci-rb/issues/224
|
475
|
+
[#226]: https://github.com/DataDog/datadog-ci-rb/issues/226
|
476
|
+
[#227]: https://github.com/DataDog/datadog-ci-rb/issues/227
|
477
|
+
[#229]: https://github.com/DataDog/datadog-ci-rb/issues/229
|
478
|
+
[#231]: https://github.com/DataDog/datadog-ci-rb/issues/231
|
479
|
+
[#235]: https://github.com/DataDog/datadog-ci-rb/issues/235
|
480
|
+
[#236]: https://github.com/DataDog/datadog-ci-rb/issues/236
|
data/README.md
CHANGED
@@ -13,6 +13,7 @@ Learn more on our [official website](https://docs.datadoghq.com/tests/) and chec
|
|
13
13
|
- [Test Visibility](https://docs.datadoghq.com/tests/) - collect metrics and results for your tests
|
14
14
|
- [Intelligent test runner](https://docs.datadoghq.com/intelligent_test_runner/) - save time by selectively running only tests affected by code changes
|
15
15
|
- [Auto test retries](https://docs.datadoghq.com/tests/auto_test_retries/?tab=ruby) - retrying failing tests up to N times to avoid failing your build due to flaky tests
|
16
|
+
- [Early flake detection](https://docs.datadoghq.com/tests/early_flake_detection?tab=ruby) - Datadog’s test flakiness solution that identifies flakes early by running newly added tests multiple times
|
16
17
|
- [Search and manage CI tests](https://docs.datadoghq.com/tests/search/)
|
17
18
|
- [Enhance developer workflows](https://docs.datadoghq.com/tests/developer_workflows)
|
18
19
|
- [Flaky test management](https://docs.datadoghq.com/tests/guides/flaky_test_management/)
|
@@ -11,6 +11,7 @@ require_relative "../test_optimisation/coverage/transport"
|
|
11
11
|
require_relative "../test_optimisation/coverage/writer"
|
12
12
|
require_relative "../test_retries/component"
|
13
13
|
require_relative "../test_retries/null_component"
|
14
|
+
require_relative "../test_retries/unique_tests_client"
|
14
15
|
require_relative "../test_visibility/component"
|
15
16
|
require_relative "../test_visibility/flush"
|
16
17
|
require_relative "../test_visibility/null_component"
|
@@ -19,7 +20,6 @@ require_relative "../test_visibility/serializers/factories/test_suite_level"
|
|
19
20
|
require_relative "../test_visibility/transport"
|
20
21
|
require_relative "../transport/adapters/telemetry_webmock_safe_adapter"
|
21
22
|
require_relative "../transport/api/builder"
|
22
|
-
require_relative "../utils/identity"
|
23
23
|
require_relative "../utils/parsing"
|
24
24
|
require_relative "../utils/test_run"
|
25
25
|
require_relative "../worker"
|
@@ -116,12 +116,15 @@ module Datadog
|
|
116
116
|
@test_retries = TestRetries::Component.new(
|
117
117
|
retry_failed_tests_enabled: settings.ci.retry_failed_tests_enabled,
|
118
118
|
retry_failed_tests_max_attempts: settings.ci.retry_failed_tests_max_attempts,
|
119
|
-
retry_failed_tests_total_limit: settings.ci.retry_failed_tests_total_limit
|
119
|
+
retry_failed_tests_total_limit: settings.ci.retry_failed_tests_total_limit,
|
120
|
+
retry_new_tests_enabled: settings.ci.retry_new_tests_enabled,
|
121
|
+
unique_tests_client: build_unique_tests_client(settings, test_visibility_api)
|
120
122
|
)
|
121
123
|
# @type ivar @test_optimisation: Datadog::CI::TestOptimisation::Component
|
122
124
|
@test_optimisation = build_test_optimisation(settings, test_visibility_api)
|
123
125
|
@test_visibility = TestVisibility::Component.new(
|
124
|
-
test_suite_level_visibility_enabled: !settings.ci.force_test_level_visibility
|
126
|
+
test_suite_level_visibility_enabled: !settings.ci.force_test_level_visibility,
|
127
|
+
logical_test_session_name: settings.ci.test_session_name
|
125
128
|
)
|
126
129
|
end
|
127
130
|
|
@@ -236,6 +239,14 @@ module Datadog
|
|
236
239
|
)
|
237
240
|
end
|
238
241
|
|
242
|
+
def build_unique_tests_client(settings, api)
|
243
|
+
TestRetries::UniqueTestsClient.new(
|
244
|
+
api: api,
|
245
|
+
dd_env: settings.env,
|
246
|
+
config_tags: custom_configuration(settings)
|
247
|
+
)
|
248
|
+
end
|
249
|
+
|
239
250
|
# fetch custom tags provided by the user in DD_TAGS env var
|
240
251
|
# with prefix test.configuration.
|
241
252
|
def custom_configuration(settings)
|
@@ -274,12 +285,8 @@ module Datadog
|
|
274
285
|
settings.telemetry.shutdown_timeout_seconds = 60.0
|
275
286
|
|
276
287
|
begin
|
277
|
-
require "datadog/core/environment/identity"
|
278
288
|
require "datadog/core/telemetry/http/adapters/net"
|
279
289
|
|
280
|
-
# patch gem's identity to report datadog-ci library version instead of datadog gem version
|
281
|
-
Core::Environment::Identity.include(CI::Utils::Identity)
|
282
|
-
|
283
290
|
# patch gem's telemetry transport layer to use Net::HTTP instead of WebMock's Net::HTTP
|
284
291
|
Core::Telemetry::Http::Adapters::Net.include(CI::Transport::Adapters::TelemetryWebmockSafeAdapter)
|
285
292
|
rescue => e
|
@@ -24,6 +24,11 @@ module Datadog
|
|
24
24
|
o.default false
|
25
25
|
end
|
26
26
|
|
27
|
+
option :test_session_name do |o|
|
28
|
+
o.type :string, nilable: true
|
29
|
+
o.env CI::Ext::Settings::ENV_TEST_SESSION_NAME
|
30
|
+
end
|
31
|
+
|
27
32
|
option :agentless_mode_enabled do |o|
|
28
33
|
o.type :bool
|
29
34
|
o.env CI::Ext::Settings::ENV_AGENTLESS_MODE_ENABLED
|
@@ -106,6 +111,12 @@ module Datadog
|
|
106
111
|
o.default 1000
|
107
112
|
end
|
108
113
|
|
114
|
+
option :retry_new_tests_enabled do |o|
|
115
|
+
o.type :bool
|
116
|
+
o.env CI::Ext::Settings::ENV_RETRY_NEW_TESTS_ENABLED
|
117
|
+
o.default true
|
118
|
+
end
|
119
|
+
|
109
120
|
define_method(:instrument) do |integration_name, options = {}, &block|
|
110
121
|
return unless enabled
|
111
122
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module CI
|
5
|
+
module Contrib
|
6
|
+
module Cucumber
|
7
|
+
class Filter < ::Cucumber::Core::Filter.new(:configuration)
|
8
|
+
def test_case(test_case)
|
9
|
+
test_retries_component.reset_retries! unless test_case_seen[test_case]
|
10
|
+
test_case_seen[test_case] = true
|
11
|
+
|
12
|
+
configuration.on_event(:test_case_finished) do |event|
|
13
|
+
next unless retry_required?(test_case, event)
|
14
|
+
|
15
|
+
test_case.describe_to(receiver)
|
16
|
+
end
|
17
|
+
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def retry_required?(test_case, event)
|
24
|
+
return false unless event.test_case == test_case
|
25
|
+
|
26
|
+
test_retries_component.should_retry?
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_case_seen
|
30
|
+
@test_case_seen ||= Hash.new { |h, k| h[k] = false }
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_retries_component
|
34
|
+
@test_retries_component ||= Datadog.send(:components).test_retries
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -18,10 +18,24 @@ module Datadog
|
|
18
18
|
|
19
19
|
def formatters
|
20
20
|
existing_formatters = super
|
21
|
-
@datadog_formatter ||=
|
21
|
+
@datadog_formatter ||= Formatter.new(@configuration)
|
22
22
|
[@datadog_formatter] + existing_formatters
|
23
23
|
end
|
24
24
|
|
25
|
+
def filters
|
26
|
+
require_relative "filter"
|
27
|
+
|
28
|
+
filters_list = super
|
29
|
+
datadog_filter = Filter.new(@configuration)
|
30
|
+
unless @configuration.dry_run?
|
31
|
+
# insert our filter the pre-last position because Cucumber::Filters::PrepareWorld must be the last one
|
32
|
+
# see:
|
33
|
+
# https://github.com/cucumber/cucumber-ruby/blob/58dd8f12c0ac5f4e607335ff2e7d385c1ed25899/lib/cucumber/runtime.rb#L266
|
34
|
+
filters_list.insert(-2, datadog_filter)
|
35
|
+
end
|
36
|
+
filters_list
|
37
|
+
end
|
38
|
+
|
25
39
|
def begin_scenario(test_case)
|
26
40
|
if Datadog::CI.active_test&.skipped_by_itr?
|
27
41
|
raise ::Cucumber::Core::Test::Result::Skipped, CI::Ext::Test::ITR_TEST_SKIP_REASON
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require "datadog/tracing/contrib/patcher"
|
4
4
|
|
5
5
|
require_relative "instrumentation"
|
6
|
-
require_relative "configuration_override"
|
7
6
|
|
8
7
|
module Datadog
|
9
8
|
module CI
|
@@ -21,7 +20,6 @@ module Datadog
|
|
21
20
|
|
22
21
|
def patch
|
23
22
|
::Cucumber::Runtime.include(Instrumentation)
|
24
|
-
::Cucumber::Configuration.include(ConfigurationOverride)
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
@@ -8,6 +8,8 @@ module Datadog
|
|
8
8
|
module Contrib
|
9
9
|
module Minitest
|
10
10
|
module Runner
|
11
|
+
DD_ESTIMATED_TESTS_PER_SUITE = 5
|
12
|
+
|
11
13
|
def self.included(base)
|
12
14
|
base.singleton_class.prepend(ClassMethods)
|
13
15
|
end
|
@@ -18,12 +20,15 @@ module Datadog
|
|
18
20
|
|
19
21
|
return unless datadog_configuration[:enabled]
|
20
22
|
|
23
|
+
# minitest does not store the total number of tests, so we can't pass it to the test session
|
24
|
+
# instead, we use the number of test suites * DD_ESTIMATED_TESTS_PER_SUITE as a rough estimate
|
21
25
|
test_visibility_component.start_test_session(
|
22
26
|
tags: {
|
23
27
|
CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
|
24
28
|
CI::Ext::Test::TAG_FRAMEWORK_VERSION => CI::Contrib::Minitest::Integration.version.to_s
|
25
29
|
},
|
26
|
-
service: datadog_configuration[:service_name]
|
30
|
+
service: datadog_configuration[:service_name],
|
31
|
+
total_tests_count: (DD_ESTIMATED_TESTS_PER_SUITE * ::Minitest::Runnable.runnables.size).to_i
|
27
32
|
)
|
28
33
|
test_visibility_component.start_test_module(Ext::FRAMEWORK)
|
29
34
|
end
|
@@ -51,6 +51,10 @@ module Datadog
|
|
51
51
|
return super unless test_span
|
52
52
|
|
53
53
|
finish_with_result(test_span, result_code)
|
54
|
+
|
55
|
+
# remove failures if test passed at least once on retries
|
56
|
+
self.failures = [] if test_span.any_retry_passed?
|
57
|
+
|
54
58
|
if Helpers.parallel?(self.class)
|
55
59
|
finish_with_result(test_span.test_suite, result_code)
|
56
60
|
end
|
@@ -64,9 +64,9 @@ module Datadog
|
|
64
64
|
|
65
65
|
result = super
|
66
66
|
|
67
|
-
#
|
67
|
+
# When test job is canceled and RSpec is quitting we don't want to report the last test
|
68
68
|
# before RSpec context unwinds. This test might have some unrelated errors that we don't want to
|
69
|
-
#
|
69
|
+
# see in Datadog.
|
70
70
|
return result if ::RSpec.world.wants_to_quit
|
71
71
|
|
72
72
|
case execution_result.status
|
@@ -74,6 +74,8 @@ module Datadog
|
|
74
74
|
test_span&.passed!
|
75
75
|
when :failed
|
76
76
|
test_span&.failed!(exception: execution_result.exception)
|
77
|
+
# if any of the retries passed, we don't fail the test run
|
78
|
+
@exception = nil if test_span&.any_retry_passed?
|
77
79
|
else
|
78
80
|
# :pending or nil
|
79
81
|
test_span&.skipped!(
|
@@ -84,21 +86,20 @@ module Datadog
|
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
87
|
-
# after retries are done, we can report the test to RSpec
|
88
|
-
@skip_reporting = false
|
89
|
-
|
90
89
|
# this is a special case for ci-queue, we need to finish the test suite span
|
91
90
|
ci_queue_test_span&.finish
|
92
91
|
|
93
|
-
#
|
94
|
-
|
95
|
-
# if test passed at least once
|
92
|
+
# after retries are done, we can finally report the test to RSpec
|
93
|
+
@skip_reporting = false
|
96
94
|
finish(reporter)
|
97
95
|
end
|
98
96
|
|
99
97
|
def finish(reporter)
|
100
|
-
#
|
101
|
-
# it is going to be reported once after retries are done
|
98
|
+
# By default finish test but do not report it to RSpec::Core::Reporter
|
99
|
+
# it is going to be reported once after retries are done.
|
100
|
+
#
|
101
|
+
# We need to do this because RSpec breaks when we try to report the same example multiple times with different
|
102
|
+
# results.
|
102
103
|
return super unless @skip_reporting
|
103
104
|
|
104
105
|
super(::RSpec::Core::NullReporter)
|
@@ -23,7 +23,8 @@ module Datadog
|
|
23
23
|
CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
|
24
24
|
CI::Ext::Test::TAG_FRAMEWORK_VERSION => CI::Contrib::RSpec::Integration.version.to_s
|
25
25
|
},
|
26
|
-
service: datadog_configuration[:service_name]
|
26
|
+
service: datadog_configuration[:service_name],
|
27
|
+
total_tests_count: ::RSpec.world.example_count
|
27
28
|
)
|
28
29
|
|
29
30
|
test_module = test_visibility_component.start_test_module(Ext::FRAMEWORK)
|
@@ -18,6 +18,8 @@ module Datadog
|
|
18
18
|
ENV_RETRY_FAILED_TESTS_ENABLED = "DD_CIVISIBILITY_FLAKY_RETRY_ENABLED"
|
19
19
|
ENV_RETRY_FAILED_TESTS_MAX_ATTEMPTS = "DD_CIVISIBILITY_FLAKY_RETRY_COUNT"
|
20
20
|
ENV_RETRY_FAILED_TESTS_TOTAL_LIMIT = "DD_CIVISIBILITY_TOTAL_FLAKY_RETRY_COUNT"
|
21
|
+
ENV_RETRY_NEW_TESTS_ENABLED = "DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED"
|
22
|
+
ENV_TEST_SESSION_NAME = "DD_TEST_SESSION_NAME"
|
21
23
|
|
22
24
|
# Source: https://docs.datadoghq.com/getting_started/site/
|
23
25
|
DD_SITE_ALLOWLIST = %w[
|
@@ -54,6 +54,12 @@ module Datadog
|
|
54
54
|
METRIC_CODE_COVERAGE_IS_EMPTY = "code_coverage.is_empty"
|
55
55
|
METRIC_CODE_COVERAGE_FILES = "code_coverage.files"
|
56
56
|
|
57
|
+
METRIC_EFD_UNIQUE_TESTS_REQUEST = "early_flake_detection.request"
|
58
|
+
METRIC_EFD_UNIQUE_TESTS_REQUEST_MS = "early_flake_detection.request_ms"
|
59
|
+
METRIC_EFD_UNIQUE_TESTS_REQUEST_ERRORS = "early_flake_detection.request_errors"
|
60
|
+
METRIC_EFD_UNIQUE_TESTS_RESPONSE_BYTES = "early_flake_detection.response_bytes"
|
61
|
+
METRIC_EFD_UNIQUE_TESTS_RESPONSE_TESTS = "early_flake_detection.response_tests"
|
62
|
+
|
57
63
|
METRIC_TEST_SESSION = "test_session"
|
58
64
|
|
59
65
|
TAG_TEST_FRAMEWORK = "test_framework"
|
@@ -63,6 +69,7 @@ module Datadog
|
|
63
69
|
TAG_BROWSER_DRIVER = "browser_driver"
|
64
70
|
TAG_IS_RUM = "is_rum"
|
65
71
|
TAG_IS_RETRY = "is_retry"
|
72
|
+
TAG_IS_NEW = "is_new"
|
66
73
|
TAG_LIBRARY = "library"
|
67
74
|
TAG_ENDPOINT = "endpoint"
|
68
75
|
TAG_ERROR_TYPE = "error_type"
|
@@ -73,6 +80,8 @@ module Datadog
|
|
73
80
|
TAG_COMMAND = "command"
|
74
81
|
TAG_COVERAGE_ENABLED = "coverage_enabled"
|
75
82
|
TAG_ITR_SKIP_ENABLED = "itrskip_enabled"
|
83
|
+
TAG_EARLY_FLAKE_DETECTION_ENABLED = "early_flake_detection_enabled"
|
84
|
+
TAG_EARLY_FLAKE_DETECTION_ABORT_REASON = "early_flake_detection_abort_reason"
|
76
85
|
TAG_PROVIDER = "provider"
|
77
86
|
TAG_AUTO_INJECTED = "auto_injected"
|
78
87
|
|
data/lib/datadog/ci/ext/test.rb
CHANGED
@@ -58,13 +58,22 @@ module Datadog
|
|
58
58
|
# version of the browser, if multiple browsers or multiple versions then this tag is empty
|
59
59
|
TAG_BROWSER_VERSION = "test.browser.version"
|
60
60
|
|
61
|
-
# Tags for
|
61
|
+
# Tags for retries
|
62
62
|
TAG_IS_RETRY = "test.is_retry" # true if test was retried by datadog-ci library
|
63
|
+
TAG_IS_NEW = "test.is_new" # true if test was marked as new by new test retries (early flake detection)
|
64
|
+
TAG_EARLY_FLAKE_ENABLED = "test.early_flake.enabled" # true if early flake detection is enabled
|
65
|
+
TAG_EARLY_FLAKE_ABORT_REASON = "test.early_flake.abort_reason" # reason why early flake detection was aborted
|
63
66
|
|
64
67
|
# internal APM tag to mark a span as a test span
|
65
68
|
TAG_SPAN_KIND = "span.kind"
|
66
69
|
SPAN_KIND_TEST = "test"
|
67
70
|
|
71
|
+
# common tags that are serialized directly in msgpack header in metadata field
|
72
|
+
METADATA_TAG_TEST_SESSION_NAME = "test_session.name"
|
73
|
+
|
74
|
+
# internal metric with the number of virtual CPUs
|
75
|
+
METRIC_CPU_COUNT = "_dd.host.vcpu_count"
|
76
|
+
|
68
77
|
# tags that are common for the whole session and can be inherited from the test session
|
69
78
|
INHERITABLE_TAGS = [TAG_FRAMEWORK, TAG_FRAMEWORK_VERSION].freeze
|
70
79
|
|
@@ -73,6 +82,8 @@ module Datadog
|
|
73
82
|
ITR_TEST_SKIP_REASON = "Skipped by Datadog's intelligent test runner"
|
74
83
|
ITR_UNSKIPPABLE_OPTION = :datadog_itr_unskippable
|
75
84
|
|
85
|
+
EARLY_FLAKE_FAULTY = "faulty"
|
86
|
+
|
76
87
|
# test status as recognized by Datadog
|
77
88
|
module Status
|
78
89
|
PASS = "pass"
|
@@ -37,6 +37,10 @@ module Datadog
|
|
37
37
|
DD_API_SETTINGS_RESPONSE_TESTS_SKIPPING_KEY = "tests_skipping"
|
38
38
|
DD_API_SETTINGS_RESPONSE_REQUIRE_GIT_KEY = "require_git"
|
39
39
|
DD_API_SETTINGS_RESPONSE_FLAKY_TEST_RETRIES_KEY = "flaky_test_retries_enabled"
|
40
|
+
DD_API_SETTINGS_RESPONSE_EARLY_FLAKE_DETECTION_KEY = "early_flake_detection"
|
41
|
+
DD_API_SETTINGS_RESPONSE_ENABLED_KEY = "enabled"
|
42
|
+
DD_API_SETTINGS_RESPONSE_SLOW_TEST_RETRIES_KEY = "slow_test_retries"
|
43
|
+
DD_API_SETTINGS_RESPONSE_FAULTY_SESSION_THRESHOLD_KEY = "faulty_session_threshold"
|
40
44
|
DD_API_SETTINGS_RESPONSE_DEFAULT = {DD_API_SETTINGS_RESPONSE_ITR_ENABLED_KEY => false}.freeze
|
41
45
|
|
42
46
|
DD_API_GIT_SEARCH_COMMITS_PATH = "/api/v2/git/repository/search_commits"
|
@@ -46,6 +50,9 @@ module Datadog
|
|
46
50
|
DD_API_SKIPPABLE_TESTS_PATH = "/api/v2/ci/tests/skippable"
|
47
51
|
DD_API_SKIPPABLE_TESTS_TYPE = "test_params"
|
48
52
|
|
53
|
+
DD_API_UNIQUE_TESTS_PATH = "/api/v2/ci/libraries/tests"
|
54
|
+
DD_API_UNIQUE_TESTS_TYPE = "ci_app_libraries_tests_request"
|
55
|
+
|
49
56
|
CONTENT_TYPE_MESSAGEPACK = "application/msgpack"
|
50
57
|
CONTENT_TYPE_JSON = "application/json"
|
51
58
|
CONTENT_TYPE_MULTIPART_FORM_DATA = "multipart/form-data"
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "../worker"
|
4
|
+
|
3
5
|
module Datadog
|
4
6
|
module CI
|
5
7
|
module Remote
|
@@ -27,8 +29,17 @@ module Datadog
|
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
+
# configure different components in parallel because they might block on HTTP requests
|
33
|
+
configuration_workers = [
|
34
|
+
Worker.new { test_optimisation.configure(library_configuration, test_session) },
|
35
|
+
Worker.new { test_retries.configure(library_configuration, test_session) }
|
36
|
+
]
|
37
|
+
|
38
|
+
# launch configuration workers
|
39
|
+
configuration_workers.each(&:perform)
|
40
|
+
|
41
|
+
# block until all workers are done (or 60 seconds has passed)
|
42
|
+
configuration_workers.each(&:wait_until_done)
|
32
43
|
end
|
33
44
|
|
34
45
|
private
|
@@ -7,6 +7,8 @@ require_relative "../ext/transport"
|
|
7
7
|
require_relative "../transport/telemetry"
|
8
8
|
require_relative "../utils/parsing"
|
9
9
|
|
10
|
+
require_relative "slow_test_retries"
|
11
|
+
|
10
12
|
module Datadog
|
11
13
|
module CI
|
12
14
|
module Remote
|
@@ -49,37 +51,76 @@ module Datadog
|
|
49
51
|
def require_git?
|
50
52
|
return @require_git if defined?(@require_git)
|
51
53
|
|
52
|
-
@require_git =
|
54
|
+
@require_git = Utils::Parsing.convert_to_bool(
|
55
|
+
payload.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_REQUIRE_GIT_KEY, false)
|
56
|
+
)
|
53
57
|
end
|
54
58
|
|
55
59
|
def itr_enabled?
|
56
60
|
return @itr_enabled if defined?(@itr_enabled)
|
57
61
|
|
58
|
-
@itr_enabled =
|
62
|
+
@itr_enabled = Utils::Parsing.convert_to_bool(
|
63
|
+
payload.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_ITR_ENABLED_KEY, false)
|
64
|
+
)
|
59
65
|
end
|
60
66
|
|
61
67
|
def code_coverage_enabled?
|
62
68
|
return @code_coverage_enabled if defined?(@code_coverage_enabled)
|
63
69
|
|
64
|
-
@code_coverage_enabled =
|
70
|
+
@code_coverage_enabled = Utils::Parsing.convert_to_bool(
|
71
|
+
payload.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_CODE_COVERAGE_KEY, false)
|
72
|
+
)
|
65
73
|
end
|
66
74
|
|
67
75
|
def tests_skipping_enabled?
|
68
76
|
return @tests_skipping_enabled if defined?(@tests_skipping_enabled)
|
69
77
|
|
70
|
-
@tests_skipping_enabled =
|
78
|
+
@tests_skipping_enabled = Utils::Parsing.convert_to_bool(
|
79
|
+
payload.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_TESTS_SKIPPING_KEY, false)
|
80
|
+
)
|
71
81
|
end
|
72
82
|
|
73
83
|
def flaky_test_retries_enabled?
|
74
84
|
return @flaky_test_retries_enabled if defined?(@flaky_test_retries_enabled)
|
75
85
|
|
76
|
-
@flaky_test_retries_enabled =
|
86
|
+
@flaky_test_retries_enabled = Utils::Parsing.convert_to_bool(
|
87
|
+
payload.fetch(
|
88
|
+
Ext::Transport::DD_API_SETTINGS_RESPONSE_FLAKY_TEST_RETRIES_KEY, false
|
89
|
+
)
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
def early_flake_detection_enabled?
|
94
|
+
return @early_flake_detection_enabled if defined?(@early_flake_detection_enabled)
|
95
|
+
|
96
|
+
@early_flake_detection_enabled = Utils::Parsing.convert_to_bool(
|
97
|
+
early_flake_detection_payload.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_ENABLED_KEY, false)
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
def slow_test_retries
|
102
|
+
return @slow_test_retries if defined?(@slow_test_retries)
|
103
|
+
|
104
|
+
@slow_test_retries = SlowTestRetries.new(
|
105
|
+
early_flake_detection_payload.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_SLOW_TEST_RETRIES_KEY, {})
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
def faulty_session_threshold
|
110
|
+
return @faulty_session_threshold if defined?(@faulty_session_threshold)
|
111
|
+
|
112
|
+
@faulty_session_threshold = early_flake_detection_payload.fetch(
|
113
|
+
Ext::Transport::DD_API_SETTINGS_RESPONSE_FAULTY_SESSION_THRESHOLD_KEY, 0
|
114
|
+
)
|
77
115
|
end
|
78
116
|
|
79
117
|
private
|
80
118
|
|
81
|
-
def
|
82
|
-
|
119
|
+
def early_flake_detection_payload
|
120
|
+
payload.fetch(
|
121
|
+
Ext::Transport::DD_API_SETTINGS_RESPONSE_EARLY_FLAKE_DETECTION_KEY,
|
122
|
+
{}
|
123
|
+
)
|
83
124
|
end
|
84
125
|
|
85
126
|
def default_payload
|
@@ -58,7 +58,8 @@ module Datadog
|
|
58
58
|
1,
|
59
59
|
{
|
60
60
|
Ext::Telemetry::TAG_COVERAGE_ENABLED => library_settings.code_coverage_enabled?.to_s,
|
61
|
-
Ext::Telemetry::TAG_ITR_SKIP_ENABLED => library_settings.tests_skipping_enabled?.to_s
|
61
|
+
Ext::Telemetry::TAG_ITR_SKIP_ENABLED => library_settings.tests_skipping_enabled?.to_s,
|
62
|
+
Ext::Telemetry::TAG_EARLY_FLAKE_DETECTION_ENABLED => library_settings.early_flake_detection_enabled?.to_s
|
62
63
|
}
|
63
64
|
)
|
64
65
|
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module CI
|
5
|
+
module Remote
|
6
|
+
# Parses "slow_test_retries" payload for early flake detection settings
|
7
|
+
#
|
8
|
+
# Example payload:
|
9
|
+
# {
|
10
|
+
# "5s" => 10,
|
11
|
+
# "10s" => 5,
|
12
|
+
# "30s" => 3,
|
13
|
+
# "5m" => 2
|
14
|
+
# }
|
15
|
+
#
|
16
|
+
# The payload above means that for tests that run less than 5 seconds, we should retry them 10 times,
|
17
|
+
# for tests that run less than 10 seconds, we should retry them 5 times, and so on.
|
18
|
+
class SlowTestRetries
|
19
|
+
attr_reader :entries
|
20
|
+
|
21
|
+
Entry = Struct.new(:duration, :max_attempts)
|
22
|
+
|
23
|
+
DURATION_MEASURES = {
|
24
|
+
"s" => 1,
|
25
|
+
"m" => 60
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
def initialize(payload)
|
29
|
+
@entries = parse(payload)
|
30
|
+
end
|
31
|
+
|
32
|
+
def max_attempts_for_duration(duration)
|
33
|
+
@entries.each do |entry|
|
34
|
+
return entry.max_attempts if duration < entry.duration
|
35
|
+
end
|
36
|
+
|
37
|
+
0
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def parse(payload)
|
43
|
+
(payload || {}).keys.filter_map do |key|
|
44
|
+
duration, measure = key.match(/(\d+)(\w+)/)&.captures
|
45
|
+
next if duration.nil? || measure.nil? || !DURATION_MEASURES.key?(measure)
|
46
|
+
|
47
|
+
Entry.new(duration.to_f * DURATION_MEASURES.fetch(measure, 1), payload[key].to_i)
|
48
|
+
end.sort_by(&:duration)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/datadog/ci/span.rb
CHANGED
@@ -114,6 +114,13 @@ module Datadog
|
|
114
114
|
tracer_span.clear_tag(key)
|
115
115
|
end
|
116
116
|
|
117
|
+
# Gets metric value by key.
|
118
|
+
# @param [String] key the key of the metric.
|
119
|
+
# @return [Numeric] value the value of the metric.
|
120
|
+
def get_metric(key)
|
121
|
+
tracer_span.get_metric(key)
|
122
|
+
end
|
123
|
+
|
117
124
|
# Sets metric value by key.
|
118
125
|
# @param [String] key the key of the metric.
|
119
126
|
# @param [Numeric] value the value of the metric.
|