datadog-ci 1.12.0 → 1.13.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 +12 -2
- data/lib/datadog/ci/configuration/components.rb +27 -15
- data/lib/datadog/ci/configuration/settings.rb +12 -0
- data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +5 -2
- data/lib/datadog/ci/contrib/minitest/test.rb +3 -3
- data/lib/datadog/ci/contrib/rspec/example.rb +3 -3
- data/lib/datadog/ci/ext/app_types.rb +1 -1
- data/lib/datadog/ci/ext/settings.rb +2 -0
- data/lib/datadog/ci/ext/telemetry.rb +17 -5
- data/lib/datadog/ci/ext/test.rb +42 -4
- data/lib/datadog/ci/ext/transport.rb +6 -0
- data/lib/datadog/ci/remote/component.rb +11 -1
- data/lib/datadog/ci/remote/library_settings.rb +31 -0
- data/lib/datadog/ci/remote/library_settings_client.rb +2 -1
- data/lib/datadog/ci/test.rb +67 -9
- data/lib/datadog/ci/test_management/component.rb +61 -0
- data/lib/datadog/ci/test_management/null_component.rb +25 -0
- data/lib/datadog/ci/test_management/tests_properties.rb +128 -0
- data/lib/datadog/ci/test_optimisation/component.rb +3 -3
- data/lib/datadog/ci/test_retries/component.rb +37 -7
- data/lib/datadog/ci/test_retries/driver/base.rb +5 -0
- data/lib/datadog/ci/test_retries/driver/retry_failed.rb +4 -0
- data/lib/datadog/ci/test_retries/driver/retry_flaky_fixed.rb +38 -0
- data/lib/datadog/ci/test_retries/driver/retry_new.rb +2 -7
- data/lib/datadog/ci/test_retries/strategy/retry_flaky_fixed.rb +43 -0
- data/lib/datadog/ci/test_retries/strategy/retry_new.rb +4 -46
- data/lib/datadog/ci/test_suite.rb +21 -2
- data/lib/datadog/ci/test_visibility/capabilities.rb +36 -0
- data/lib/datadog/ci/test_visibility/component.rb +70 -2
- data/lib/datadog/ci/test_visibility/context.rb +0 -4
- data/lib/datadog/ci/{test_retries/unique_tests_client.rb → test_visibility/known_tests.rb} +10 -10
- data/lib/datadog/ci/test_visibility/null_component.rb +3 -0
- data/lib/datadog/ci/test_visibility/serializers/factories/test_level.rb +1 -1
- data/lib/datadog/ci/test_visibility/telemetry.rb +11 -2
- data/lib/datadog/ci/test_visibility/transport.rb +15 -2
- data/lib/datadog/ci/version.rb +1 -1
- data/lib/datadog/ci.rb +13 -5
- metadata +11 -5
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../ext/telemetry"
|
4
|
+
require_relative "../ext/test"
|
5
|
+
require_relative "../utils/telemetry"
|
6
|
+
require_relative "../utils/test_run"
|
7
|
+
|
8
|
+
module Datadog
|
9
|
+
module CI
|
10
|
+
module TestManagement
|
11
|
+
# Test management is a feature that lets people manage their flaky tests in Datadog.
|
12
|
+
# It includes:
|
13
|
+
# - marking test as quarantined causes test to continue running but not failing the build
|
14
|
+
# - marking test as disabled causes test to be skipped
|
15
|
+
# - marking test as "attempted to fix" causes test to be retried many times to confirm that fix worked
|
16
|
+
class Component
|
17
|
+
attr_reader :enabled, :tests_properties
|
18
|
+
|
19
|
+
def initialize(enabled:, tests_properties_client:)
|
20
|
+
@enabled = enabled
|
21
|
+
|
22
|
+
@tests_properties_client = tests_properties_client
|
23
|
+
@tests_properties = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def configure(library_settings, test_session)
|
27
|
+
@enabled &&= library_settings.test_management_enabled?
|
28
|
+
|
29
|
+
return unless @enabled
|
30
|
+
|
31
|
+
test_session.set_tag(Ext::Test::TAG_TEST_MANAGEMENT_ENABLED, "true")
|
32
|
+
|
33
|
+
@tests_properties = @tests_properties_client.fetch(test_session)
|
34
|
+
|
35
|
+
Utils::Telemetry.distribution(
|
36
|
+
Ext::Telemetry::METRIC_TEST_MANAGEMENT_TESTS_RESPONSE_TESTS,
|
37
|
+
@tests_properties.count.to_f
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def tag_test_from_properties(test_span)
|
42
|
+
return unless @enabled
|
43
|
+
|
44
|
+
datadog_test_id = Utils::TestRun.datadog_test_id(test_span.name, test_span.test_suite_name)
|
45
|
+
test_properties = @tests_properties[datadog_test_id]
|
46
|
+
|
47
|
+
if test_properties.nil?
|
48
|
+
Datadog.logger.debug { "Test properties not found for test: #{datadog_test_id}" }
|
49
|
+
return
|
50
|
+
end
|
51
|
+
|
52
|
+
Datadog.logger.debug { "Test properties for test #{datadog_test_id} are: [#{test_properties}]" }
|
53
|
+
|
54
|
+
test_span.set_tag(Ext::Test::TAG_IS_QUARANTINED, "true") if test_properties["quarantined"]
|
55
|
+
test_span.set_tag(Ext::Test::TAG_IS_TEST_DISABLED, "true") if test_properties["disabled"]
|
56
|
+
test_span.set_tag(Ext::Test::TAG_IS_ATTEMPT_TO_FIX, "true") if test_properties["attempt_to_fix"]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../ext/telemetry"
|
4
|
+
require_relative "../utils/telemetry"
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module CI
|
8
|
+
module TestManagement
|
9
|
+
class NullComponent
|
10
|
+
attr_reader :enabled, :tests_properties
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@enabled = false
|
14
|
+
@tests_properties = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def configure(_, _)
|
18
|
+
end
|
19
|
+
|
20
|
+
def tag_test_from_properties(_)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
require_relative "../ext/telemetry"
|
6
|
+
require_relative "../ext/transport"
|
7
|
+
require_relative "../transport/telemetry"
|
8
|
+
require_relative "../utils/parsing"
|
9
|
+
require_relative "../utils/telemetry"
|
10
|
+
require_relative "../utils/test_run"
|
11
|
+
|
12
|
+
module Datadog
|
13
|
+
module CI
|
14
|
+
module TestManagement
|
15
|
+
# fetches and stores a map of tests to their test management properties from the backend
|
16
|
+
class TestsProperties
|
17
|
+
class Response
|
18
|
+
def initialize(http_response)
|
19
|
+
@http_response = http_response
|
20
|
+
@json = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def ok?
|
24
|
+
resp = @http_response
|
25
|
+
!resp.nil? && resp.ok?
|
26
|
+
end
|
27
|
+
|
28
|
+
def tests
|
29
|
+
tests_map = {}
|
30
|
+
|
31
|
+
payload
|
32
|
+
.fetch("data", {})
|
33
|
+
.fetch("attributes", {})
|
34
|
+
.fetch("modules", {})
|
35
|
+
.each do |_test_module, module_hash|
|
36
|
+
module_hash
|
37
|
+
.fetch("suites", {})
|
38
|
+
.each do |test_suite, suite_hash|
|
39
|
+
suite_hash.fetch("tests", {})
|
40
|
+
.each do |test_name, properties_hash|
|
41
|
+
properties = properties_hash.fetch("properties", {})
|
42
|
+
properties.transform_values! { |v| Utils::Parsing.convert_to_bool(v) }
|
43
|
+
|
44
|
+
tests_map[Utils::TestRun.datadog_test_id(test_name, test_suite)] = properties
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
tests_map
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def payload
|
55
|
+
cached = @json
|
56
|
+
return cached unless cached.nil?
|
57
|
+
|
58
|
+
resp = @http_response
|
59
|
+
return @json = {} if resp.nil? || !ok?
|
60
|
+
|
61
|
+
begin
|
62
|
+
@json = JSON.parse(resp.payload)
|
63
|
+
rescue JSON::ParserError => e
|
64
|
+
Datadog.logger.error(
|
65
|
+
"Failed to parse test management tests response payload: #{e}. Payload was: #{resp.payload}"
|
66
|
+
)
|
67
|
+
@json = {}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(api: nil)
|
73
|
+
@api = api
|
74
|
+
end
|
75
|
+
|
76
|
+
def fetch(test_session)
|
77
|
+
api = @api
|
78
|
+
return {} unless api
|
79
|
+
|
80
|
+
request_payload = payload(test_session)
|
81
|
+
Datadog.logger.debug("Fetching test management tests with request: #{request_payload}")
|
82
|
+
|
83
|
+
http_response = api.api_request(
|
84
|
+
path: Ext::Transport::DD_API_TEST_MANAGEMENT_TESTS_PATH,
|
85
|
+
payload: request_payload
|
86
|
+
)
|
87
|
+
|
88
|
+
CI::Transport::Telemetry.api_requests(
|
89
|
+
Ext::Telemetry::METRIC_TEST_MANAGEMENT_TESTS_REQUEST,
|
90
|
+
1,
|
91
|
+
compressed: http_response.request_compressed
|
92
|
+
)
|
93
|
+
Utils::Telemetry.distribution(Ext::Telemetry::METRIC_TEST_MANAGEMENT_TESTS_REQUEST_MS, http_response.duration_ms)
|
94
|
+
Utils::Telemetry.distribution(
|
95
|
+
Ext::Telemetry::METRIC_TEST_MANAGEMENT_TESTS_RESPONSE_BYTES,
|
96
|
+
http_response.response_size.to_f,
|
97
|
+
{Ext::Telemetry::TAG_RESPONSE_COMPRESSED => http_response.gzipped_content?.to_s}
|
98
|
+
)
|
99
|
+
|
100
|
+
unless http_response.ok?
|
101
|
+
CI::Transport::Telemetry.api_requests_errors(
|
102
|
+
Ext::Telemetry::METRIC_TEST_MANAGEMENT_TESTS_REQUEST_ERRORS,
|
103
|
+
1,
|
104
|
+
error_type: http_response.telemetry_error_type,
|
105
|
+
status_code: http_response.code
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
Response.new(http_response).tests
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def payload(test_session)
|
115
|
+
{
|
116
|
+
"data" => {
|
117
|
+
"id" => Datadog::Core::Environment::Identity.id,
|
118
|
+
"type" => Ext::Transport::DD_API_TEST_MANAGEMENT_TESTS_TYPE,
|
119
|
+
"attributes" => {
|
120
|
+
"repository_url" => test_session.git_repository_url
|
121
|
+
}
|
122
|
+
}
|
123
|
+
}.to_json
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -20,7 +20,7 @@ require_relative "telemetry"
|
|
20
20
|
module Datadog
|
21
21
|
module CI
|
22
22
|
module TestOptimisation
|
23
|
-
#
|
23
|
+
# Test Impact Analysis implementation
|
24
24
|
# Integrates with backend to provide test impact analysis data and
|
25
25
|
# skip tests that are not impacted by the changes
|
26
26
|
class Component
|
@@ -156,7 +156,7 @@ module Datadog
|
|
156
156
|
|
157
157
|
if skippable?(test)
|
158
158
|
if forked?
|
159
|
-
Datadog.logger.warn { "
|
159
|
+
Datadog.logger.warn { "Test Impact Analysis is not supported for forking test runners yet" }
|
160
160
|
return
|
161
161
|
end
|
162
162
|
|
@@ -172,7 +172,7 @@ module Datadog
|
|
172
172
|
@mutex.synchronize do
|
173
173
|
@total_tests_count += 1
|
174
174
|
|
175
|
-
return if !test.skipped? || !test.
|
175
|
+
return if !test.skipped? || !test.skipped_by_test_impact_analysis?
|
176
176
|
|
177
177
|
if forked?
|
178
178
|
Datadog.logger.warn { "ITR is not supported for forking test runners yet" }
|
@@ -7,6 +7,7 @@ require_relative "driver/retry_new"
|
|
7
7
|
require_relative "strategy/no_retry"
|
8
8
|
require_relative "strategy/retry_failed"
|
9
9
|
require_relative "strategy/retry_new"
|
10
|
+
require_relative "strategy/retry_flaky_fixed"
|
10
11
|
|
11
12
|
require_relative "../ext/telemetry"
|
12
13
|
require_relative "../utils/telemetry"
|
@@ -25,23 +26,33 @@ module Datadog
|
|
25
26
|
retry_failed_tests_max_attempts:,
|
26
27
|
retry_failed_tests_total_limit:,
|
27
28
|
retry_new_tests_enabled:,
|
28
|
-
|
29
|
+
retry_flaky_fixed_tests_enabled:,
|
30
|
+
retry_flaky_fixed_tests_max_attempts:
|
29
31
|
)
|
30
32
|
no_retries_strategy = Strategy::NoRetry.new
|
31
33
|
|
32
|
-
retry_failed_strategy = Strategy::RetryFailed.new(
|
34
|
+
@retry_failed_strategy = Strategy::RetryFailed.new(
|
33
35
|
enabled: retry_failed_tests_enabled,
|
34
36
|
max_attempts: retry_failed_tests_max_attempts,
|
35
37
|
total_limit: retry_failed_tests_total_limit
|
36
38
|
)
|
37
39
|
|
38
|
-
retry_new_strategy = Strategy::RetryNew.new(
|
39
|
-
enabled: retry_new_tests_enabled
|
40
|
-
unique_tests_client: unique_tests_client
|
40
|
+
@retry_new_strategy = Strategy::RetryNew.new(
|
41
|
+
enabled: retry_new_tests_enabled
|
41
42
|
)
|
42
43
|
|
43
|
-
|
44
|
-
|
44
|
+
retry_flaky_fixed_strategy = Strategy::RetryFlakyFixed.new(
|
45
|
+
enabled: retry_flaky_fixed_tests_enabled,
|
46
|
+
max_attempts: retry_flaky_fixed_tests_max_attempts
|
47
|
+
)
|
48
|
+
|
49
|
+
# order is important, we apply the first matching strategy
|
50
|
+
@retry_strategies = [
|
51
|
+
retry_flaky_fixed_strategy,
|
52
|
+
@retry_new_strategy,
|
53
|
+
@retry_failed_strategy,
|
54
|
+
no_retries_strategy
|
55
|
+
]
|
45
56
|
@mutex = Mutex.new
|
46
57
|
end
|
47
58
|
|
@@ -82,6 +93,8 @@ module Datadog
|
|
82
93
|
else
|
83
94
|
# after each retry we record the result, the driver will decide if we should retry again
|
84
95
|
current_retry_driver&.record_retry(test_span)
|
96
|
+
|
97
|
+
tag_last_retry(test_span) unless should_retry?
|
85
98
|
end
|
86
99
|
end
|
87
100
|
|
@@ -94,10 +107,27 @@ module Datadog
|
|
94
107
|
self.current_retry_driver = nil
|
95
108
|
end
|
96
109
|
|
110
|
+
def tag_last_retry(test_span)
|
111
|
+
test_span&.set_tag(Ext::Test::TAG_HAS_FAILED_ALL_RETRIES, "true") if test_span&.all_executions_failed?
|
112
|
+
|
113
|
+
# if we are attempting to fix the test and all retries passed, we indicate that the fix might have worked
|
114
|
+
if test_span&.attempt_to_fix? && test_span.all_executions_passed?
|
115
|
+
test_span&.set_tag(Ext::Test::TAG_ATTEMPT_TO_FIX_PASSED, "true")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
97
119
|
def should_retry?
|
98
120
|
!!current_retry_driver&.should_retry?
|
99
121
|
end
|
100
122
|
|
123
|
+
def auto_test_retries_feature_enabled
|
124
|
+
@retry_failed_strategy.enabled
|
125
|
+
end
|
126
|
+
|
127
|
+
def early_flake_detection_feature_enabled
|
128
|
+
@retry_new_strategy.enabled
|
129
|
+
end
|
130
|
+
|
101
131
|
private
|
102
132
|
|
103
133
|
def current_retry_driver
|
@@ -13,11 +13,16 @@ module Datadog
|
|
13
13
|
|
14
14
|
def record_retry(test_span)
|
15
15
|
test_span&.set_tag(Ext::Test::TAG_IS_RETRY, "true")
|
16
|
+
test_span&.set_tag(Ext::Test::TAG_RETRY_REASON, retry_reason)
|
16
17
|
end
|
17
18
|
|
18
19
|
# duration in float seconds
|
19
20
|
def record_duration(duration)
|
20
21
|
end
|
22
|
+
|
23
|
+
def retry_reason
|
24
|
+
"unknown"
|
25
|
+
end
|
21
26
|
end
|
22
27
|
end
|
23
28
|
end
|
@@ -0,0 +1,38 @@
|
|
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 RetryFlakyFixed < Base
|
12
|
+
attr_reader :max_attempts
|
13
|
+
|
14
|
+
def initialize(max_attempts:)
|
15
|
+
@attempts = 0
|
16
|
+
@max_attempts = max_attempts
|
17
|
+
end
|
18
|
+
|
19
|
+
def should_retry?
|
20
|
+
@attempts < @max_attempts
|
21
|
+
end
|
22
|
+
|
23
|
+
def record_retry(test_span)
|
24
|
+
super
|
25
|
+
|
26
|
+
@attempts += 1
|
27
|
+
|
28
|
+
Datadog.logger.debug { "Retry Attempts [#{@attempts} / #{@max_attempts}]" }
|
29
|
+
end
|
30
|
+
|
31
|
+
def retry_reason
|
32
|
+
Ext::Test::RetryReason::RETRY_FLAKY_FIXED
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -15,8 +15,6 @@ module Datadog
|
|
15
15
|
@attempts = 0
|
16
16
|
# will be changed based on test span duration
|
17
17
|
@max_attempts = 10
|
18
|
-
|
19
|
-
mark_new_test(test_span)
|
20
18
|
end
|
21
19
|
|
22
20
|
def should_retry?
|
@@ -27,7 +25,6 @@ module Datadog
|
|
27
25
|
super
|
28
26
|
|
29
27
|
@attempts += 1
|
30
|
-
mark_new_test(test_span)
|
31
28
|
|
32
29
|
Datadog.logger.debug { "Retry Attempts [#{@attempts} / #{@max_attempts}]" }
|
33
30
|
end
|
@@ -38,10 +35,8 @@ module Datadog
|
|
38
35
|
Datadog.logger.debug { "Recorded test duration of [#{duration}], new Max Attempts value is [#{@max_attempts}]" }
|
39
36
|
end
|
40
37
|
|
41
|
-
|
42
|
-
|
43
|
-
def mark_new_test(test_span)
|
44
|
-
test_span.set_tag(Ext::Test::TAG_IS_NEW, "true")
|
38
|
+
def retry_reason
|
39
|
+
Ext::Test::RetryReason::RETRY_NEW
|
45
40
|
end
|
46
41
|
end
|
47
42
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
require_relative "../driver/retry_flaky_fixed"
|
6
|
+
|
7
|
+
module Datadog
|
8
|
+
module CI
|
9
|
+
module TestRetries
|
10
|
+
module Strategy
|
11
|
+
# This strategy retries tests that are flaky and were marked as attempted to be fixed in Datadog Test Management UI.
|
12
|
+
class RetryFlakyFixed < Base
|
13
|
+
attr_reader :enabled, :max_attempts
|
14
|
+
|
15
|
+
def initialize(
|
16
|
+
enabled:,
|
17
|
+
max_attempts:
|
18
|
+
)
|
19
|
+
@enabled = enabled
|
20
|
+
@max_attempts = max_attempts
|
21
|
+
end
|
22
|
+
|
23
|
+
def configure(library_settings, test_session)
|
24
|
+
@enabled &&= library_settings.test_management_enabled?
|
25
|
+
@max_attempts = library_settings.attempt_to_fix_retries_count || @max_attempts
|
26
|
+
end
|
27
|
+
|
28
|
+
def covers?(test_span)
|
29
|
+
return false unless @enabled
|
30
|
+
|
31
|
+
!!test_span&.attempt_to_fix?
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_driver(test_span)
|
35
|
+
Datadog.logger.debug { "#{test_span.name} is attempt_to_fix, will be retried" }
|
36
|
+
|
37
|
+
Driver::RetryFlakyFixed.new(max_attempts: max_attempts)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -11,19 +11,13 @@ module Datadog
|
|
11
11
|
class RetryNew < Base
|
12
12
|
DEFAULT_TOTAL_TESTS_COUNT = 100
|
13
13
|
|
14
|
-
attr_reader :enabled, :max_attempts_thresholds, :
|
14
|
+
attr_reader :enabled, :max_attempts_thresholds, :total_limit, :retried_count
|
15
15
|
|
16
|
-
def initialize(
|
17
|
-
enabled:,
|
18
|
-
unique_tests_client:
|
19
|
-
)
|
16
|
+
def initialize(enabled:)
|
20
17
|
@enabled = enabled
|
21
|
-
@unique_tests_set = Set.new
|
22
18
|
# total maximum number of new tests to retry (will be set based on the total number of tests in the session)
|
23
19
|
@total_limit = 0
|
24
20
|
@retried_count = 0
|
25
|
-
|
26
|
-
@unique_tests_client = unique_tests_client
|
27
21
|
end
|
28
22
|
|
29
23
|
def covers?(test_span)
|
@@ -37,11 +31,11 @@ module Datadog
|
|
37
31
|
mark_test_session_faulty(Datadog::CI.active_test_session)
|
38
32
|
end
|
39
33
|
|
40
|
-
@enabled && !test_span.skipped? &&
|
34
|
+
@enabled && !test_span.skipped? && test_span.is_new?
|
41
35
|
end
|
42
36
|
|
43
37
|
def configure(library_settings, test_session)
|
44
|
-
@enabled &&= library_settings.early_flake_detection_enabled?
|
38
|
+
@enabled &&= library_settings.early_flake_detection_enabled? && library_settings.known_tests_enabled?
|
45
39
|
|
46
40
|
return unless @enabled
|
47
41
|
|
@@ -50,7 +44,6 @@ module Datadog
|
|
50
44
|
|
51
45
|
set_max_attempts_thresholds(library_settings)
|
52
46
|
calculate_total_retries_limit(library_settings, test_session)
|
53
|
-
fetch_known_unique_tests(test_session)
|
54
47
|
end
|
55
48
|
|
56
49
|
def build_driver(test_span)
|
@@ -68,20 +61,6 @@ module Datadog
|
|
68
61
|
test_session&.set_tag(Ext::Test::TAG_EARLY_FLAKE_ABORT_REASON, Ext::Test::EARLY_FLAKE_FAULTY)
|
69
62
|
end
|
70
63
|
|
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
64
|
def set_max_attempts_thresholds(library_settings)
|
86
65
|
@max_attempts_thresholds = library_settings.slow_test_retries
|
87
66
|
Datadog.logger.debug do
|
@@ -104,27 +83,6 @@ module Datadog
|
|
104
83
|
"Retry new tests total limit is [#{@total_limit}] (#{percentage_limit}% of #{tests_count})"
|
105
84
|
end
|
106
85
|
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
86
|
end
|
129
87
|
end
|
130
88
|
end
|
@@ -59,6 +59,24 @@ module Datadog
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
# @internal
|
63
|
+
def all_executions_failed?(test_id)
|
64
|
+
synchronize do
|
65
|
+
stats = @execution_stats_per_test[test_id]
|
66
|
+
stats && (stats[Ext::Test::Status::FAIL] > 0 || stats[Ext::Test::ExecutionStatsStatus::FAIL_IGNORED] > 0) &&
|
67
|
+
stats[Ext::Test::Status::PASS] == 0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# @internal
|
72
|
+
def all_executions_passed?(test_id)
|
73
|
+
synchronize do
|
74
|
+
stats = @execution_stats_per_test[test_id]
|
75
|
+
stats && stats[Ext::Test::Status::PASS] > 0 && stats[Ext::Test::Status::FAIL] == 0 &&
|
76
|
+
stats[Ext::Test::ExecutionStatsStatus::FAIL_IGNORED] == 0
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
62
80
|
# @internal
|
63
81
|
def test_executed?(test_id)
|
64
82
|
synchronize do
|
@@ -89,8 +107,9 @@ module Datadog
|
|
89
107
|
end
|
90
108
|
|
91
109
|
def derive_test_status_from_execution_stats(test_execution_stats)
|
92
|
-
# test is passed if it passed at least once
|
93
|
-
if test_execution_stats[Ext::Test::Status::PASS] > 0
|
110
|
+
# test is passed if it passed at least once or it failed but fail was ignored
|
111
|
+
if test_execution_stats[Ext::Test::Status::PASS] > 0 ||
|
112
|
+
test_execution_stats[Ext::Test::ExecutionStatsStatus::FAIL_IGNORED] > 0
|
94
113
|
Ext::Test::Status::PASS
|
95
114
|
# if test was never passed, it is failed if it failed at least once
|
96
115
|
elsif test_execution_stats[Ext::Test::Status::FAIL] > 0
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../ext/test"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module CI
|
7
|
+
module TestVisibility
|
8
|
+
# Generates internal tags for library capabilities
|
9
|
+
module Capabilities
|
10
|
+
def self.tags
|
11
|
+
tags = {}
|
12
|
+
|
13
|
+
test_optimisation = Datadog::CI.send(:test_optimisation)
|
14
|
+
tags[Ext::Test::LibraryCapabilities::TAG_TEST_IMPACT_ANALYSIS] = test_optimisation.enabled.to_s
|
15
|
+
|
16
|
+
test_management = Datadog::CI.send(:test_management)
|
17
|
+
test_management_tag_value = test_management.enabled.to_s
|
18
|
+
|
19
|
+
[
|
20
|
+
Ext::Test::LibraryCapabilities::TAG_TEST_MANAGEMENT_ATTEMPT_TO_FIX,
|
21
|
+
Ext::Test::LibraryCapabilities::TAG_TEST_MANAGEMENT_QUARANTINE,
|
22
|
+
Ext::Test::LibraryCapabilities::TAG_TEST_MANAGEMENT_DISABLE
|
23
|
+
].each do |tag|
|
24
|
+
tags[tag] = test_management_tag_value
|
25
|
+
end
|
26
|
+
|
27
|
+
test_retries = Datadog::CI.send(:test_retries)
|
28
|
+
tags[Ext::Test::LibraryCapabilities::TAG_AUTO_TEST_RETRIES] = test_retries.auto_test_retries_feature_enabled.to_s
|
29
|
+
tags[Ext::Test::LibraryCapabilities::TAG_EARLY_FLAKE_DETECTION] = test_retries.early_flake_detection_feature_enabled.to_s
|
30
|
+
|
31
|
+
tags
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|