datadog-ci 1.4.1 → 1.6.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 +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.
|