datadog-ci 1.4.0 → 1.5.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 +25 -2
- data/README.md +1 -0
- data/ext/datadog_cov/datadog_cov.c +1 -1
- data/lib/datadog/ci/configuration/components.rb +12 -6
- data/lib/datadog/ci/configuration/settings.rb +6 -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 +1 -0
- data/lib/datadog/ci/ext/telemetry.rb +9 -0
- data/lib/datadog/ci/ext/test.rb +6 -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/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 +2 -0
- data/lib/datadog/ci/test_suite.rb +8 -0
- data/lib/datadog/ci/test_visibility/component.rb +23 -13
- data/lib/datadog/ci/test_visibility/null_component.rb +1 -1
- data/lib/datadog/ci/test_visibility/telemetry.rb +9 -0
- data/lib/datadog/ci/utils/test_run.rb +1 -1
- data/lib/datadog/ci/version.rb +1 -1
- 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
@@ -99,6 +99,7 @@ module Datadog
|
|
99
99
|
|
100
100
|
def start_coverage(test)
|
101
101
|
return if !enabled? || !code_coverage?
|
102
|
+
|
102
103
|
Telemetry.code_coverage_started(test)
|
103
104
|
coverage_collector&.start
|
104
105
|
end
|
@@ -109,13 +110,15 @@ module Datadog
|
|
109
110
|
Telemetry.code_coverage_finished(test)
|
110
111
|
|
111
112
|
coverage = coverage_collector&.stop
|
113
|
+
|
114
|
+
# if test was skipped, we discard coverage data
|
115
|
+
return if test.skipped?
|
116
|
+
|
112
117
|
if coverage.nil? || coverage.empty?
|
113
118
|
Telemetry.code_coverage_is_empty
|
114
119
|
return
|
115
120
|
end
|
116
121
|
|
117
|
-
return if test.skipped?
|
118
|
-
|
119
122
|
test_source_file = test.source_file
|
120
123
|
|
121
124
|
# cucumber's gherkin files are not covered by the code coverage collector
|
@@ -140,8 +143,8 @@ module Datadog
|
|
140
143
|
def mark_if_skippable(test)
|
141
144
|
return if !enabled? || !skipping_tests?
|
142
145
|
|
143
|
-
|
144
|
-
if @skippable_tests.include?(
|
146
|
+
datadog_test_id = Utils::TestRun.datadog_test_id(test.name, test.test_suite_name, test.parameters)
|
147
|
+
if @skippable_tests.include?(datadog_test_id)
|
145
148
|
if forked?
|
146
149
|
Datadog.logger.warn { "Intelligent test runner is not supported for forking test runners yet" }
|
147
150
|
return
|
@@ -149,9 +152,9 @@ module Datadog
|
|
149
152
|
|
150
153
|
test.set_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR, "true")
|
151
154
|
|
152
|
-
Datadog.logger.debug { "Marked test as skippable: #{
|
155
|
+
Datadog.logger.debug { "Marked test as skippable: #{datadog_test_id}" }
|
153
156
|
else
|
154
|
-
Datadog.logger.debug { "Test is not skippable: #{
|
157
|
+
Datadog.logger.debug { "Test is not skippable: #{datadog_test_id}" }
|
155
158
|
end
|
156
159
|
end
|
157
160
|
|
@@ -36,7 +36,7 @@ module Datadog
|
|
36
36
|
next unless test_data["type"] == Ext::Test::ITR_TEST_SKIPPING_MODE
|
37
37
|
|
38
38
|
attrs = test_data["attributes"] || {}
|
39
|
-
res << Utils::TestRun.
|
39
|
+
res << Utils::TestRun.datadog_test_id(attrs["name"], attrs["suite"], attrs["parameters"])
|
40
40
|
end
|
41
41
|
|
42
42
|
res
|
@@ -1,7 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "driver/no_retry"
|
4
|
+
require_relative "driver/retry_failed"
|
5
|
+
require_relative "driver/retry_new"
|
6
|
+
|
3
7
|
require_relative "strategy/no_retry"
|
4
8
|
require_relative "strategy/retry_failed"
|
9
|
+
require_relative "strategy/retry_new"
|
10
|
+
|
11
|
+
require_relative "../ext/telemetry"
|
12
|
+
require_relative "../utils/telemetry"
|
5
13
|
|
6
14
|
module Datadog
|
7
15
|
module CI
|
@@ -10,73 +18,94 @@ module Datadog
|
|
10
18
|
# - retrying failed tests - improve success rate of CI pipelines
|
11
19
|
# - retrying new tests - detect flaky tests as early as possible to prevent them from being merged
|
12
20
|
class Component
|
13
|
-
|
14
|
-
:retry_failed_tests_total_limit, :retry_failed_tests_count
|
21
|
+
FIBER_LOCAL_CURRENT_RETRY_DRIVER_KEY = :__dd_current_retry_driver
|
15
22
|
|
16
23
|
def initialize(
|
17
24
|
retry_failed_tests_enabled:,
|
18
25
|
retry_failed_tests_max_attempts:,
|
19
|
-
retry_failed_tests_total_limit
|
26
|
+
retry_failed_tests_total_limit:,
|
27
|
+
retry_new_tests_enabled:,
|
28
|
+
unique_tests_client:
|
20
29
|
)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
30
|
+
no_retries_strategy = Strategy::NoRetry.new
|
31
|
+
|
32
|
+
retry_failed_strategy = Strategy::RetryFailed.new(
|
33
|
+
enabled: retry_failed_tests_enabled,
|
34
|
+
max_attempts: retry_failed_tests_max_attempts,
|
35
|
+
total_limit: retry_failed_tests_total_limit
|
36
|
+
)
|
26
37
|
|
38
|
+
retry_new_strategy = Strategy::RetryNew.new(
|
39
|
+
enabled: retry_new_tests_enabled,
|
40
|
+
unique_tests_client: unique_tests_client
|
41
|
+
)
|
42
|
+
|
43
|
+
# order is important, we should try to retry new tests first
|
44
|
+
@retry_strategies = [retry_new_strategy, retry_failed_strategy, no_retries_strategy]
|
27
45
|
@mutex = Mutex.new
|
28
46
|
end
|
29
47
|
|
30
|
-
def configure(library_settings)
|
31
|
-
|
48
|
+
def configure(library_settings, test_session)
|
49
|
+
# let all strategies configure themselves
|
50
|
+
@retry_strategies.each do |strategy|
|
51
|
+
strategy.configure(library_settings, test_session)
|
52
|
+
end
|
32
53
|
end
|
33
54
|
|
34
55
|
def with_retries(&block)
|
35
|
-
|
36
|
-
retry_strategy = nil
|
37
|
-
|
38
|
-
test_finished_callback = lambda do |test_span|
|
39
|
-
if retry_strategy.nil?
|
40
|
-
# we always run test at least once and after first pass create a correct retry strategy
|
41
|
-
retry_strategy = build_strategy(test_span)
|
42
|
-
else
|
43
|
-
# after each retry we record the result, strategy will decide if we should retry again
|
44
|
-
retry_strategy&.record_retry(test_span)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
test_visibility_component.set_test_finished_callback(test_finished_callback)
|
56
|
+
reset_retries!
|
49
57
|
|
50
58
|
loop do
|
51
59
|
yield
|
52
60
|
|
53
|
-
break unless
|
61
|
+
break unless should_retry?
|
54
62
|
end
|
55
63
|
ensure
|
56
|
-
|
64
|
+
reset_retries!
|
57
65
|
end
|
58
66
|
|
59
|
-
def
|
67
|
+
def build_driver(test_span)
|
60
68
|
@mutex.synchronize do
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
Strategy::NoRetry.new
|
68
|
-
end
|
69
|
+
# find the first strategy that covers the test span and let it build the driver
|
70
|
+
strategy = @retry_strategies.find { |strategy| strategy.covers?(test_span) }
|
71
|
+
|
72
|
+
raise "No retry strategy found for test span: #{test_span.name}" if strategy.nil?
|
73
|
+
|
74
|
+
strategy.build_driver(test_span)
|
69
75
|
end
|
70
76
|
end
|
71
77
|
|
78
|
+
def record_test_finished(test_span)
|
79
|
+
if current_retry_driver.nil?
|
80
|
+
# we always run test at least once and after the first pass create a correct retry driver
|
81
|
+
self.current_retry_driver = build_driver(test_span)
|
82
|
+
else
|
83
|
+
# after each retry we record the result, the driver will decide if we should retry again
|
84
|
+
current_retry_driver&.record_retry(test_span)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def record_test_span_duration(tracer_span)
|
89
|
+
current_retry_driver&.record_duration(tracer_span.duration)
|
90
|
+
end
|
91
|
+
|
92
|
+
# this API is targeted on Cucumber instrumentation or any other that cannot leverage #with_retries method
|
93
|
+
def reset_retries!
|
94
|
+
self.current_retry_driver = nil
|
95
|
+
end
|
96
|
+
|
97
|
+
def should_retry?
|
98
|
+
!!current_retry_driver&.should_retry?
|
99
|
+
end
|
100
|
+
|
72
101
|
private
|
73
102
|
|
74
|
-
def
|
75
|
-
|
103
|
+
def current_retry_driver
|
104
|
+
Thread.current[FIBER_LOCAL_CURRENT_RETRY_DRIVER_KEY]
|
76
105
|
end
|
77
106
|
|
78
|
-
def
|
79
|
-
|
107
|
+
def current_retry_driver=(driver)
|
108
|
+
Thread.current[FIBER_LOCAL_CURRENT_RETRY_DRIVER_KEY] = driver
|
80
109
|
end
|
81
110
|
end
|
82
111
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module CI
|
5
|
+
module TestRetries
|
6
|
+
module Driver
|
7
|
+
# Driver is the class responsible for the current test retry mechanism.
|
8
|
+
# It receives signals about each retry execution and steers the current retry strategy.
|
9
|
+
class Base
|
10
|
+
def should_retry?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def record_retry(test_span)
|
15
|
+
test_span&.set_tag(Ext::Test::TAG_IS_RETRY, "true")
|
16
|
+
end
|
17
|
+
|
18
|
+
# duration in float seconds
|
19
|
+
def record_duration(duration)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
require_relative "../../ext/test"
|
6
|
+
|
7
|
+
module Datadog
|
8
|
+
module CI
|
9
|
+
module TestRetries
|
10
|
+
module Driver
|
11
|
+
class RetryFailed < Base
|
12
|
+
attr_reader :max_attempts
|
13
|
+
|
14
|
+
def initialize(max_attempts:)
|
15
|
+
@max_attempts = max_attempts
|
16
|
+
|
17
|
+
@attempts = 0
|
18
|
+
@passed_once = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def should_retry?
|
22
|
+
@attempts < @max_attempts && !@passed_once
|
23
|
+
end
|
24
|
+
|
25
|
+
def record_retry(test_span)
|
26
|
+
super
|
27
|
+
|
28
|
+
@attempts += 1
|
29
|
+
@passed_once = true if test_span&.passed?
|
30
|
+
|
31
|
+
Datadog.logger.debug { "Retry Attempts [#{@attempts} / #{@max_attempts}], Passed: [#{@passed_once}]" }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
require_relative "../../ext/test"
|
6
|
+
|
7
|
+
module Datadog
|
8
|
+
module CI
|
9
|
+
module TestRetries
|
10
|
+
module Driver
|
11
|
+
# retry every new test up to 10 times (early flake detection)
|
12
|
+
class RetryNew < Base
|
13
|
+
def initialize(test_span, max_attempts_thresholds:)
|
14
|
+
@max_attempts_thresholds = max_attempts_thresholds
|
15
|
+
@attempts = 0
|
16
|
+
# will be changed based on test span duration
|
17
|
+
@max_attempts = 10
|
18
|
+
|
19
|
+
mark_new_test(test_span)
|
20
|
+
end
|
21
|
+
|
22
|
+
def should_retry?
|
23
|
+
@attempts < @max_attempts
|
24
|
+
end
|
25
|
+
|
26
|
+
def record_retry(test_span)
|
27
|
+
super
|
28
|
+
|
29
|
+
@attempts += 1
|
30
|
+
mark_new_test(test_span)
|
31
|
+
|
32
|
+
Datadog.logger.debug { "Retry Attempts [#{@attempts} / #{@max_attempts}]" }
|
33
|
+
end
|
34
|
+
|
35
|
+
def record_duration(duration)
|
36
|
+
@max_attempts = @max_attempts_thresholds.max_attempts_for_duration(duration)
|
37
|
+
|
38
|
+
Datadog.logger.debug { "Recorded test duration of [#{duration}], new Max Attempts value is [#{@max_attempts}]" }
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def mark_new_test(test_span)
|
44
|
+
test_span.set_tag(Ext::Test::TAG_IS_NEW, "true")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -6,13 +6,7 @@ module Datadog
|
|
6
6
|
module CI
|
7
7
|
module TestRetries
|
8
8
|
class NullComponent < Component
|
9
|
-
attr_reader :retry_failed_tests_enabled, :retry_failed_tests_max_attempts, :retry_failed_tests_total_limit
|
10
|
-
|
11
9
|
def initialize
|
12
|
-
# enabled only by remote settings
|
13
|
-
@retry_failed_tests_enabled = false
|
14
|
-
@retry_failed_tests_max_attempts = 0
|
15
|
-
@retry_failed_tests_total_limit = 0
|
16
10
|
end
|
17
11
|
|
18
12
|
def configure(library_settings)
|
@@ -22,6 +16,13 @@ module Datadog
|
|
22
16
|
no_action = proc {}
|
23
17
|
yield no_action
|
24
18
|
end
|
19
|
+
|
20
|
+
def reset_retries!
|
21
|
+
end
|
22
|
+
|
23
|
+
def should_retry?
|
24
|
+
false
|
25
|
+
end
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
@@ -1,16 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "../driver/no_retry"
|
4
|
+
|
3
5
|
module Datadog
|
4
6
|
module CI
|
5
7
|
module TestRetries
|
6
8
|
module Strategy
|
9
|
+
# Strategies are subcomponents of the retry mechanism. They are responsible for
|
10
|
+
# determining which tests should be retried and how.
|
7
11
|
class Base
|
8
|
-
def
|
9
|
-
|
12
|
+
def covers?(test_span)
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def configure(_library_settings, _test_session)
|
10
17
|
end
|
11
18
|
|
12
|
-
def
|
13
|
-
|
19
|
+
def build_driver(_test_span)
|
20
|
+
Driver::NoRetry.new
|
14
21
|
end
|
15
22
|
end
|
16
23
|
end
|
@@ -2,33 +2,50 @@
|
|
2
2
|
|
3
3
|
require_relative "base"
|
4
4
|
|
5
|
-
require_relative "
|
5
|
+
require_relative "../driver/retry_failed"
|
6
6
|
|
7
7
|
module Datadog
|
8
8
|
module CI
|
9
9
|
module TestRetries
|
10
10
|
module Strategy
|
11
11
|
class RetryFailed < Base
|
12
|
-
attr_reader :max_attempts
|
13
|
-
|
14
|
-
|
12
|
+
attr_reader :enabled, :max_attempts,
|
13
|
+
:total_limit, :retried_count
|
14
|
+
|
15
|
+
def initialize(
|
16
|
+
enabled:,
|
17
|
+
max_attempts:,
|
18
|
+
total_limit:
|
19
|
+
)
|
20
|
+
@enabled = enabled
|
15
21
|
@max_attempts = max_attempts
|
22
|
+
@total_limit = total_limit
|
23
|
+
@retried_count = 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def covers?(test_span)
|
27
|
+
return false unless @enabled
|
28
|
+
|
29
|
+
if @retried_count >= @total_limit
|
30
|
+
Datadog.logger.debug do
|
31
|
+
"Retry failed tests limit reached: [#{@retried_count}] out of [#{@total_limit}]"
|
32
|
+
end
|
33
|
+
@enabled = false
|
34
|
+
end
|
16
35
|
|
17
|
-
@
|
18
|
-
@passed_once = false
|
36
|
+
@enabled && !!test_span&.failed?
|
19
37
|
end
|
20
38
|
|
21
|
-
def
|
22
|
-
@
|
39
|
+
def configure(library_settings, test_session)
|
40
|
+
@enabled &&= library_settings.flaky_test_retries_enabled?
|
23
41
|
end
|
24
42
|
|
25
|
-
def
|
26
|
-
|
43
|
+
def build_driver(test_span)
|
44
|
+
Datadog.logger.debug { "#{test_span.name} failed, will be retried" }
|
27
45
|
|
28
|
-
@
|
29
|
-
@passed_once = true if test_span&.passed?
|
46
|
+
@retried_count += 1
|
30
47
|
|
31
|
-
|
48
|
+
Driver::RetryFailed.new(max_attempts: max_attempts)
|
32
49
|
end
|
33
50
|
end
|
34
51
|
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
require_relative "../driver/retry_new"
|
6
|
+
|
7
|
+
module Datadog
|
8
|
+
module CI
|
9
|
+
module TestRetries
|
10
|
+
module Strategy
|
11
|
+
class RetryNew < Base
|
12
|
+
DEFAULT_TOTAL_TESTS_COUNT = 100
|
13
|
+
|
14
|
+
attr_reader :enabled, :max_attempts_thresholds, :unique_tests_set, :total_limit, :retried_count
|
15
|
+
|
16
|
+
def initialize(
|
17
|
+
enabled:,
|
18
|
+
unique_tests_client:
|
19
|
+
)
|
20
|
+
@enabled = enabled
|
21
|
+
@unique_tests_set = Set.new
|
22
|
+
# total maximum number of new tests to retry (will be set based on the total number of tests in the session)
|
23
|
+
@total_limit = 0
|
24
|
+
@retried_count = 0
|
25
|
+
|
26
|
+
@unique_tests_client = unique_tests_client
|
27
|
+
end
|
28
|
+
|
29
|
+
def covers?(test_span)
|
30
|
+
return false unless @enabled
|
31
|
+
|
32
|
+
if @retried_count >= @total_limit
|
33
|
+
Datadog.logger.debug do
|
34
|
+
"Retry new tests limit reached: [#{@retried_count}] out of [#{@total_limit}]"
|
35
|
+
end
|
36
|
+
@enabled = false
|
37
|
+
mark_test_session_faulty(Datadog::CI.active_test_session)
|
38
|
+
end
|
39
|
+
|
40
|
+
@enabled && !test_span.skipped? && is_new_test?(test_span)
|
41
|
+
end
|
42
|
+
|
43
|
+
def configure(library_settings, test_session)
|
44
|
+
@enabled &&= library_settings.early_flake_detection_enabled?
|
45
|
+
|
46
|
+
return unless @enabled
|
47
|
+
|
48
|
+
# mark early flake detection enabled for test session
|
49
|
+
test_session.set_tag(Ext::Test::TAG_EARLY_FLAKE_ENABLED, "true")
|
50
|
+
|
51
|
+
set_max_attempts_thresholds(library_settings)
|
52
|
+
calculate_total_retries_limit(library_settings, test_session)
|
53
|
+
fetch_known_unique_tests(test_session)
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_driver(test_span)
|
57
|
+
Datadog.logger.debug do
|
58
|
+
"#{test_span.name} is new, will be retried"
|
59
|
+
end
|
60
|
+
@retried_count += 1
|
61
|
+
|
62
|
+
Driver::RetryNew.new(test_span, max_attempts_thresholds: @max_attempts_thresholds)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def mark_test_session_faulty(test_session)
|
68
|
+
test_session&.set_tag(Ext::Test::TAG_EARLY_FLAKE_ABORT_REASON, Ext::Test::EARLY_FLAKE_FAULTY)
|
69
|
+
end
|
70
|
+
|
71
|
+
def is_new_test?(test_span)
|
72
|
+
test_id = Utils::TestRun.datadog_test_id(test_span.name, test_span.test_suite_name)
|
73
|
+
|
74
|
+
result = !@unique_tests_set.include?(test_id)
|
75
|
+
|
76
|
+
if result
|
77
|
+
Datadog.logger.debug do
|
78
|
+
"#{test_id} is not found in the unique tests set, it is a new test"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
result
|
83
|
+
end
|
84
|
+
|
85
|
+
def set_max_attempts_thresholds(library_settings)
|
86
|
+
@max_attempts_thresholds = library_settings.slow_test_retries
|
87
|
+
Datadog.logger.debug do
|
88
|
+
"Slow test retries thresholds: #{@max_attempts_thresholds.entries}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def calculate_total_retries_limit(library_settings, test_session)
|
93
|
+
percentage_limit = library_settings.faulty_session_threshold
|
94
|
+
tests_count = test_session.total_tests_count.to_i
|
95
|
+
if tests_count.zero?
|
96
|
+
Datadog.logger.debug do
|
97
|
+
"Total tests count is zero, using default value for the total number of tests: [#{DEFAULT_TOTAL_TESTS_COUNT}]"
|
98
|
+
end
|
99
|
+
|
100
|
+
tests_count = DEFAULT_TOTAL_TESTS_COUNT
|
101
|
+
end
|
102
|
+
@total_limit = (tests_count * percentage_limit / 100.0).ceil
|
103
|
+
Datadog.logger.debug do
|
104
|
+
"Retry new tests total limit is [#{@total_limit}] (#{percentage_limit}%) of #{tests_count}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def fetch_known_unique_tests(test_session)
|
109
|
+
@unique_tests_set = @unique_tests_client.fetch_unique_tests(test_session)
|
110
|
+
if @unique_tests_set.empty?
|
111
|
+
@enabled = false
|
112
|
+
mark_test_session_faulty(test_session)
|
113
|
+
|
114
|
+
Datadog.logger.warn(
|
115
|
+
"Disabling early flake detection because there are no known tests (possible reason: no test runs in default branch)"
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
# report how many unique tests were found
|
120
|
+
Datadog.logger.debug do
|
121
|
+
"Found [#{@unique_tests_set.size}] known unique tests"
|
122
|
+
end
|
123
|
+
Utils::Telemetry.distribution(
|
124
|
+
Ext::Telemetry::METRIC_EFD_UNIQUE_TESTS_RESPONSE_TESTS,
|
125
|
+
@unique_tests_set.size.to_f
|
126
|
+
)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|