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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -2
  3. data/lib/datadog/ci/configuration/components.rb +27 -15
  4. data/lib/datadog/ci/configuration/settings.rb +12 -0
  5. data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +5 -2
  6. data/lib/datadog/ci/contrib/minitest/test.rb +3 -3
  7. data/lib/datadog/ci/contrib/rspec/example.rb +3 -3
  8. data/lib/datadog/ci/ext/app_types.rb +1 -1
  9. data/lib/datadog/ci/ext/settings.rb +2 -0
  10. data/lib/datadog/ci/ext/telemetry.rb +17 -5
  11. data/lib/datadog/ci/ext/test.rb +42 -4
  12. data/lib/datadog/ci/ext/transport.rb +6 -0
  13. data/lib/datadog/ci/remote/component.rb +11 -1
  14. data/lib/datadog/ci/remote/library_settings.rb +31 -0
  15. data/lib/datadog/ci/remote/library_settings_client.rb +2 -1
  16. data/lib/datadog/ci/test.rb +67 -9
  17. data/lib/datadog/ci/test_management/component.rb +61 -0
  18. data/lib/datadog/ci/test_management/null_component.rb +25 -0
  19. data/lib/datadog/ci/test_management/tests_properties.rb +128 -0
  20. data/lib/datadog/ci/test_optimisation/component.rb +3 -3
  21. data/lib/datadog/ci/test_retries/component.rb +37 -7
  22. data/lib/datadog/ci/test_retries/driver/base.rb +5 -0
  23. data/lib/datadog/ci/test_retries/driver/retry_failed.rb +4 -0
  24. data/lib/datadog/ci/test_retries/driver/retry_flaky_fixed.rb +38 -0
  25. data/lib/datadog/ci/test_retries/driver/retry_new.rb +2 -7
  26. data/lib/datadog/ci/test_retries/strategy/retry_flaky_fixed.rb +43 -0
  27. data/lib/datadog/ci/test_retries/strategy/retry_new.rb +4 -46
  28. data/lib/datadog/ci/test_suite.rb +21 -2
  29. data/lib/datadog/ci/test_visibility/capabilities.rb +36 -0
  30. data/lib/datadog/ci/test_visibility/component.rb +70 -2
  31. data/lib/datadog/ci/test_visibility/context.rb +0 -4
  32. data/lib/datadog/ci/{test_retries/unique_tests_client.rb → test_visibility/known_tests.rb} +10 -10
  33. data/lib/datadog/ci/test_visibility/null_component.rb +3 -0
  34. data/lib/datadog/ci/test_visibility/serializers/factories/test_level.rb +1 -1
  35. data/lib/datadog/ci/test_visibility/telemetry.rb +11 -2
  36. data/lib/datadog/ci/test_visibility/transport.rb +15 -2
  37. data/lib/datadog/ci/version.rb +1 -1
  38. data/lib/datadog/ci.rb +13 -5
  39. 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
- # Intelligent test runner implementation
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 { "Intelligent test runner is not supported for forking test runners yet" }
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.skipped_by_itr?
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
- unique_tests_client:
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
- # order is important, we should try to retry new tests first
44
- @retry_strategies = [retry_new_strategy, retry_failed_strategy, no_retries_strategy]
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
@@ -30,6 +30,10 @@ module Datadog
30
30
 
31
31
  Datadog.logger.debug { "Retry Attempts [#{@attempts} / #{@max_attempts}], Passed: [#{@passed_once}]" }
32
32
  end
33
+
34
+ def retry_reason
35
+ Ext::Test::RetryReason::RETRY_FAILED
36
+ end
33
37
  end
34
38
  end
35
39
  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
- private
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, :unique_tests_set, :total_limit, :retried_count
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? && is_new_test?(test_span)
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