datadog-ci 1.11.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 +20 -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/cuprite/configuration/settings.rb +34 -0
- data/lib/datadog/ci/contrib/cuprite/driver.rb +94 -0
- data/lib/datadog/ci/contrib/cuprite/ext.rb +15 -0
- data/lib/datadog/ci/contrib/cuprite/integration.rb +44 -0
- data/lib/datadog/ci/contrib/cuprite/patcher.rb +24 -0
- data/lib/datadog/ci/contrib/cuprite/script_executor.rb +32 -0
- data/lib/datadog/ci/contrib/minitest/test.rb +3 -3
- data/lib/datadog/ci/contrib/rspec/example.rb +3 -3
- data/lib/datadog/ci/contrib/selenium/capybara_driver.rb +4 -4
- data/lib/datadog/ci/contrib/selenium/configuration/settings.rb +3 -1
- data/lib/datadog/ci/contrib/selenium/driver.rb +4 -4
- data/lib/datadog/ci/contrib/selenium/ext.rb +0 -15
- data/lib/datadog/ci/contrib/selenium/navigation.rb +2 -2
- data/lib/datadog/ci/ext/app_types.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/github_actions.rb +3 -0
- data/lib/datadog/ci/ext/environment.rb +4 -0
- data/lib/datadog/ci/ext/rum.rb +26 -0
- 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 +5 -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 +29 -3
- data/lib/datadog/ci/utils/rum.rb +42 -0
- data/lib/datadog/ci/version.rb +1 -1
- data/lib/datadog/ci.rb +14 -5
- metadata +20 -10
- data/lib/datadog/ci/contrib/selenium/rum.rb +0 -43
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "datadog/core/telemetry/logging"
|
4
|
+
|
3
5
|
require_relative "git"
|
4
6
|
require_relative "environment/extractor"
|
5
7
|
|
@@ -73,6 +75,7 @@ module Datadog
|
|
73
75
|
return if !repo_url.nil? && !repo_url.empty?
|
74
76
|
|
75
77
|
Datadog.logger.error("DD_GIT_REPOSITORY_URL is not set or empty; no repo URL was automatically extracted")
|
78
|
+
Core::Telemetry::Logger.error("DD_GIT_REPOSITORY_URL is not set or empty; no repo URL was automatically extracted")
|
76
79
|
end
|
77
80
|
|
78
81
|
def validate_git_sha(git_sha)
|
@@ -89,6 +92,7 @@ module Datadog
|
|
89
92
|
end
|
90
93
|
|
91
94
|
Datadog.logger.error(message)
|
95
|
+
Core::Telemetry::Logger.error(message)
|
92
96
|
end
|
93
97
|
end
|
94
98
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module CI
|
5
|
+
module Ext
|
6
|
+
# Defines constants for Git tags
|
7
|
+
module RUM
|
8
|
+
ENV_RUM_FLUSH_WAIT_MILLIS = "DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS"
|
9
|
+
|
10
|
+
COOKIE_TEST_EXECUTION_ID = "datadog-ci-visibility-test-execution-id"
|
11
|
+
|
12
|
+
SCRIPT_IS_RUM_ACTIVE = <<~JS
|
13
|
+
return !!window.DD_RUM
|
14
|
+
JS
|
15
|
+
SCRIPT_STOP_RUM_SESSION = <<~JS
|
16
|
+
if (window.DD_RUM && window.DD_RUM.stopSession) {
|
17
|
+
window.DD_RUM.stopSession();
|
18
|
+
return true;
|
19
|
+
} else {
|
20
|
+
return false;
|
21
|
+
}
|
22
|
+
JS
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -20,6 +20,8 @@ module Datadog
|
|
20
20
|
ENV_RETRY_FAILED_TESTS_TOTAL_LIMIT = "DD_CIVISIBILITY_TOTAL_FLAKY_RETRY_COUNT"
|
21
21
|
ENV_RETRY_NEW_TESTS_ENABLED = "DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED"
|
22
22
|
ENV_TEST_SESSION_NAME = "DD_TEST_SESSION_NAME"
|
23
|
+
ENV_TEST_MANAGEMENT_ENABLED = "DD_TEST_MANAGEMENT_ENABLED"
|
24
|
+
ENV_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES = "DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES"
|
23
25
|
|
24
26
|
# Source: https://docs.datadoghq.com/getting_started/site/
|
25
27
|
DD_SITE_ALLOWLIST = %w[
|
@@ -54,11 +54,17 @@ 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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
57
|
+
METRIC_KNOWN_TESTS_REQUEST = "known_tests.request"
|
58
|
+
METRIC_KNOWN_TESTS_REQUEST_MS = "known_tests.request_ms"
|
59
|
+
METRIC_KNOWN_TESTS_REQUEST_ERRORS = "known_tests.request_errors"
|
60
|
+
METRIC_KNOWN_TESTS_RESPONSE_BYTES = "known_tests.response_bytes"
|
61
|
+
METRIC_KNOWN_TESTS_RESPONSE_TESTS = "known_tests.response_tests"
|
62
|
+
|
63
|
+
METRIC_TEST_MANAGEMENT_TESTS_REQUEST = "test_management_tests.request"
|
64
|
+
METRIC_TEST_MANAGEMENT_TESTS_REQUEST_MS = "test_management_tests.request_ms"
|
65
|
+
METRIC_TEST_MANAGEMENT_TESTS_REQUEST_ERRORS = "test_management_tests.request_errors"
|
66
|
+
METRIC_TEST_MANAGEMENT_TESTS_RESPONSE_BYTES = "test_management_tests.response_bytes"
|
67
|
+
METRIC_TEST_MANAGEMENT_TESTS_RESPONSE_TESTS = "test_management_tests.response_tests"
|
62
68
|
|
63
69
|
METRIC_TEST_SESSION = "test_session"
|
64
70
|
|
@@ -69,6 +75,7 @@ module Datadog
|
|
69
75
|
TAG_BROWSER_DRIVER = "browser_driver"
|
70
76
|
TAG_IS_RUM = "is_rum"
|
71
77
|
TAG_IS_RETRY = "is_retry"
|
78
|
+
TAG_RETRY_REASON = "retry_reason"
|
72
79
|
TAG_EARLY_FLAKE_DETECTION_ABORT_REASON = "early_flake_detection_abort_reason"
|
73
80
|
TAG_IS_NEW = "is_new"
|
74
81
|
TAG_LIBRARY = "library"
|
@@ -79,6 +86,10 @@ module Datadog
|
|
79
86
|
TAG_REQUEST_COMPRESSED = "rq_compressed"
|
80
87
|
TAG_RESPONSE_COMPRESSED = "rs_compressed"
|
81
88
|
TAG_COMMAND = "command"
|
89
|
+
TAG_IS_ATTEMPT_TO_FIX = "is_attempt_to_fix"
|
90
|
+
TAG_IS_QUARANTINED = "is_quarantined"
|
91
|
+
TAG_IS_TEST_DISABLED = "is_disabled"
|
92
|
+
TAG_HAS_FAILED_ALL_RETRIES = "has_failed_all_retries"
|
82
93
|
# tags for git_requests.settings_response metric
|
83
94
|
TAG_COVERAGE_ENABLED = "coverage_enabled"
|
84
95
|
TAG_ITR_ENABLED = "itr_enabled"
|
@@ -86,6 +97,7 @@ module Datadog
|
|
86
97
|
TAG_REQUIRE_GIT = "require_git"
|
87
98
|
TAG_EARLY_FLAKE_DETECTION_ENABLED = "early_flake_detection_enabled"
|
88
99
|
TAG_FLAKY_TEST_RETRIES_ENABLED = "flaky_test_retries_enabled"
|
100
|
+
TAG_KNOWN_TESTS_ENABLED = "known_tests_enabled"
|
89
101
|
# tags for test_session metric
|
90
102
|
TAG_PROVIDER = "provider"
|
91
103
|
TAG_AUTO_INJECTED = "auto_injected"
|
data/lib/datadog/ci/ext/test.rb
CHANGED
@@ -58,21 +58,42 @@ 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
|
+
# known and new tests
|
62
|
+
TAG_IS_NEW = "test.is_new" # true if test is new (it was not known to Datadog before)
|
63
|
+
|
61
64
|
# Tags for retries
|
62
65
|
TAG_IS_RETRY = "test.is_retry" # true if test was retried by datadog-ci library
|
63
|
-
|
66
|
+
TAG_RETRY_REASON = "test.retry_reason" # reason why test was retried
|
64
67
|
TAG_EARLY_FLAKE_ENABLED = "test.early_flake.enabled" # true if early flake detection is enabled
|
65
68
|
TAG_EARLY_FLAKE_ABORT_REASON = "test.early_flake.abort_reason" # reason why early flake detection was aborted
|
66
69
|
|
67
70
|
# Tags for total code coverage
|
68
71
|
TAG_CODE_COVERAGE_LINES_PCT = "test.code_coverage.lines_pct"
|
69
72
|
|
73
|
+
# Tags for test managament
|
74
|
+
TAG_TEST_MANAGEMENT_ENABLED = "test.test_management.enabled" # true if test management is enabled, set on test_session_end event
|
75
|
+
TAG_IS_ATTEMPT_TO_FIX = "test.test_management.is_attempt_to_fix" # true if test is marked as "attempted to fix"
|
76
|
+
TAG_IS_TEST_DISABLED = "test.test_management.is_test_disabled" # true if test is marked as disabled in test management view
|
77
|
+
TAG_IS_QUARANTINED = "test.test_management.is_quarantined" # true if test is quarantined in test management view
|
78
|
+
TAG_HAS_FAILED_ALL_RETRIES = "test.has_failed_all_retries" # true if test was retried and none of the retries passed
|
79
|
+
TAG_ATTEMPT_TO_FIX_PASSED = "test.test_management.attempt_to_fix_passed" # true if test was marked as "attempted to fix" and all of the retries passed
|
80
|
+
|
81
|
+
# a set of tag indicating which capabilities (features) are supported by the library
|
82
|
+
module LibraryCapabilities
|
83
|
+
TAG_TEST_IMPACT_ANALYSIS = "_dd.library_capabilities.test_impact_analysis"
|
84
|
+
TAG_EARLY_FLAKE_DETECTION = "_dd.library_capabilities.early_flake_detection"
|
85
|
+
TAG_AUTO_TEST_RETRIES = "_dd.library_capabilities.auto_test_retries"
|
86
|
+
TAG_TEST_MANAGEMENT_QUARANTINE = "_dd.library_capabilities.test_management.quarantine"
|
87
|
+
TAG_TEST_MANAGEMENT_DISABLE = "_dd.library_capabilities.test_management.disable"
|
88
|
+
TAG_TEST_MANAGEMENT_ATTEMPT_TO_FIX = "_dd.library_capabilities.test_management.attempt_to_fix"
|
89
|
+
end
|
90
|
+
|
70
91
|
# internal APM tag to mark a span as a test span
|
71
92
|
TAG_SPAN_KIND = "span.kind"
|
72
93
|
SPAN_KIND_TEST = "test"
|
73
94
|
|
74
|
-
#
|
75
|
-
|
95
|
+
# DD_TEST_SESSION_NAME value
|
96
|
+
TAG_TEST_SESSION_NAME = "test_session.name"
|
76
97
|
|
77
98
|
# internal tag indicating if datadog service was configured by the user
|
78
99
|
TAG_USER_PROVIDED_TEST_SERVICE = "_dd.test.is_user_provided_service"
|
@@ -85,7 +106,6 @@ module Datadog
|
|
85
106
|
|
86
107
|
# could be either "test" or "suite" depending on whether we skip individual tests or whole suites
|
87
108
|
ITR_TEST_SKIPPING_MODE = "test" # we always skip tests (not suites) in Ruby
|
88
|
-
ITR_TEST_SKIP_REASON = "Skipped by Datadog's intelligent test runner"
|
89
109
|
ITR_UNSKIPPABLE_OPTION = :datadog_itr_unskippable
|
90
110
|
|
91
111
|
EARLY_FLAKE_FAULTY = "faulty"
|
@@ -97,12 +117,30 @@ module Datadog
|
|
97
117
|
SKIP = "skip"
|
98
118
|
end
|
99
119
|
|
120
|
+
# test statuses that we use for execution stats but don't report to Datadog (e.g. fail_ignored)
|
121
|
+
module ExecutionStatsStatus
|
122
|
+
FAIL_IGNORED = "fail_ignored"
|
123
|
+
end
|
124
|
+
|
100
125
|
# test types (e.g. test, benchmark, browser)
|
101
126
|
module Type
|
102
127
|
TEST = "test"
|
103
128
|
BROWSER = "browser"
|
104
129
|
BENCHMARK = "benchmark" # DEV: not used yet, will be used when benchmarks are supported
|
105
130
|
end
|
131
|
+
|
132
|
+
# possible reasons why a test was retried
|
133
|
+
module RetryReason
|
134
|
+
RETRY_NEW = "efd"
|
135
|
+
RETRY_FAILED = "atr"
|
136
|
+
RETRY_FLAKY_FIXED = "attempt_to_fix"
|
137
|
+
end
|
138
|
+
|
139
|
+
# possible reasons why a test was skipped
|
140
|
+
module SkipReason
|
141
|
+
TEST_IMPACT_ANALYSIS = "Skipped by Datadog's Test Impact Analysis"
|
142
|
+
TEST_MANAGEMENT_DISABLED = "Flaky test is disabled by Datadog"
|
143
|
+
end
|
106
144
|
end
|
107
145
|
end
|
108
146
|
end
|
@@ -38,10 +38,13 @@ module Datadog
|
|
38
38
|
DD_API_SETTINGS_RESPONSE_TESTS_SKIPPING_KEY = "tests_skipping"
|
39
39
|
DD_API_SETTINGS_RESPONSE_REQUIRE_GIT_KEY = "require_git"
|
40
40
|
DD_API_SETTINGS_RESPONSE_FLAKY_TEST_RETRIES_KEY = "flaky_test_retries_enabled"
|
41
|
+
DD_API_SETTINGS_RESPONSE_KNOWN_TESTS_ENABLED_KEY = "known_tests_enabled"
|
41
42
|
DD_API_SETTINGS_RESPONSE_EARLY_FLAKE_DETECTION_KEY = "early_flake_detection"
|
42
43
|
DD_API_SETTINGS_RESPONSE_ENABLED_KEY = "enabled"
|
43
44
|
DD_API_SETTINGS_RESPONSE_SLOW_TEST_RETRIES_KEY = "slow_test_retries"
|
44
45
|
DD_API_SETTINGS_RESPONSE_FAULTY_SESSION_THRESHOLD_KEY = "faulty_session_threshold"
|
46
|
+
DD_API_SETTINGS_RESPONSE_TEST_MANAGEMENT_KEY = "test_management"
|
47
|
+
DD_API_SETTINGS_RESPONSE_ATTEMPT_TO_FIX_RETRIES_KEY = "attempt_to_fix_retries"
|
45
48
|
DD_API_SETTINGS_RESPONSE_DEFAULT = {DD_API_SETTINGS_RESPONSE_ITR_ENABLED_KEY => false}.freeze
|
46
49
|
|
47
50
|
DD_API_GIT_SEARCH_COMMITS_PATH = "/api/v2/git/repository/search_commits"
|
@@ -54,6 +57,9 @@ module Datadog
|
|
54
57
|
DD_API_UNIQUE_TESTS_PATH = "/api/v2/ci/libraries/tests"
|
55
58
|
DD_API_UNIQUE_TESTS_TYPE = "ci_app_libraries_tests_request"
|
56
59
|
|
60
|
+
DD_API_TEST_MANAGEMENT_TESTS_PATH = "/api/v2/test/libraries/test-management/tests"
|
61
|
+
DD_API_TEST_MANAGEMENT_TESTS_TYPE = "ci_app_libraries_tests_request"
|
62
|
+
|
57
63
|
CONTENT_TYPE_MESSAGEPACK = "application/msgpack"
|
58
64
|
CONTENT_TYPE_JSON = "application/json"
|
59
65
|
CONTENT_TYPE_MULTIPART_FORM_DATA = "multipart/form-data"
|
@@ -32,7 +32,9 @@ module Datadog
|
|
32
32
|
# configure different components in parallel because they might block on HTTP requests
|
33
33
|
configuration_workers = [
|
34
34
|
Worker.new { test_optimisation.configure(library_configuration, test_session) },
|
35
|
-
Worker.new { test_retries.configure(library_configuration, test_session) }
|
35
|
+
Worker.new { test_retries.configure(library_configuration, test_session) },
|
36
|
+
Worker.new { test_visibility.configure(library_configuration, test_session) },
|
37
|
+
Worker.new { test_management.configure(library_configuration, test_session) }
|
36
38
|
]
|
37
39
|
|
38
40
|
# launch configuration workers
|
@@ -44,6 +46,14 @@ module Datadog
|
|
44
46
|
|
45
47
|
private
|
46
48
|
|
49
|
+
def test_management
|
50
|
+
Datadog.send(:components).test_management
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_visibility
|
54
|
+
Datadog.send(:components).test_visibility
|
55
|
+
end
|
56
|
+
|
47
57
|
def test_optimisation
|
48
58
|
Datadog.send(:components).test_optimisation
|
49
59
|
end
|
@@ -98,6 +98,14 @@ module Datadog
|
|
98
98
|
)
|
99
99
|
end
|
100
100
|
|
101
|
+
def known_tests_enabled?
|
102
|
+
return @known_tests_enabled if defined?(@known_tests_enabled)
|
103
|
+
|
104
|
+
@known_tests_enabled = Utils::Parsing.convert_to_bool(
|
105
|
+
payload.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_KNOWN_TESTS_ENABLED_KEY, false)
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
101
109
|
def slow_test_retries
|
102
110
|
return @slow_test_retries if defined?(@slow_test_retries)
|
103
111
|
|
@@ -114,8 +122,31 @@ module Datadog
|
|
114
122
|
)
|
115
123
|
end
|
116
124
|
|
125
|
+
def test_management_enabled?
|
126
|
+
return @test_management_enabled if defined?(@test_management_enabled)
|
127
|
+
|
128
|
+
@test_management_enabled = Utils::Parsing.convert_to_bool(
|
129
|
+
test_management_payload.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_ENABLED_KEY, false)
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
def attempt_to_fix_retries_count
|
134
|
+
return @attempt_to_fix_retries_count if defined?(@attempt_to_fix_retries_count)
|
135
|
+
|
136
|
+
@attempt_to_fix_retries_count = test_management_payload.fetch(
|
137
|
+
Ext::Transport::DD_API_SETTINGS_RESPONSE_ATTEMPT_TO_FIX_RETRIES_KEY, nil
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
117
141
|
private
|
118
142
|
|
143
|
+
def test_management_payload
|
144
|
+
payload.fetch(
|
145
|
+
Ext::Transport::DD_API_SETTINGS_RESPONSE_TEST_MANAGEMENT_KEY,
|
146
|
+
{}
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
119
150
|
def early_flake_detection_payload
|
120
151
|
payload.fetch(
|
121
152
|
Ext::Transport::DD_API_SETTINGS_RESPONSE_EARLY_FLAKE_DETECTION_KEY,
|
@@ -62,7 +62,8 @@ module Datadog
|
|
62
62
|
Ext::Telemetry::TAG_EARLY_FLAKE_DETECTION_ENABLED => library_settings.early_flake_detection_enabled?.to_s,
|
63
63
|
Ext::Telemetry::TAG_FLAKY_TEST_RETRIES_ENABLED => library_settings.flaky_test_retries_enabled?.to_s,
|
64
64
|
Ext::Telemetry::TAG_ITR_ENABLED => library_settings.itr_enabled?.to_s,
|
65
|
-
Ext::Telemetry::TAG_REQUIRE_GIT => library_settings.require_git?.to_s
|
65
|
+
Ext::Telemetry::TAG_REQUIRE_GIT => library_settings.require_git?.to_s,
|
66
|
+
Ext::Telemetry::TAG_KNOWN_TESTS_ENABLED => library_settings.known_tests_enabled?.to_s
|
66
67
|
}
|
67
68
|
)
|
68
69
|
|
data/lib/datadog/ci/test.rb
CHANGED
@@ -62,19 +62,37 @@ module Datadog
|
|
62
62
|
get_tag(Ext::Test::TAG_TEST_SESSION_ID)
|
63
63
|
end
|
64
64
|
|
65
|
-
# Returns "true" if the test is skipped by the intelligent test runner.
|
66
|
-
# @return [Boolean] true if the test is skipped by the intelligent test runner, false otherwise.
|
67
|
-
def skipped_by_itr?
|
68
|
-
get_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR) == "true"
|
69
|
-
end
|
70
|
-
|
71
65
|
# Returns "true" if test span represents a retry.
|
72
66
|
# @return [Boolean] true if this test is a retry, false otherwise.
|
73
67
|
def is_retry?
|
74
68
|
get_tag(Ext::Test::TAG_IS_RETRY) == "true"
|
75
69
|
end
|
76
70
|
|
77
|
-
#
|
71
|
+
# Returns "true" if this span represents a test that wasn't known to Datadog before.
|
72
|
+
# @return [Boolean] true if this test is a new one, false otherwise.
|
73
|
+
def is_new?
|
74
|
+
get_tag(Ext::Test::TAG_IS_NEW) == "true"
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns "true" if this test is quarantined by Datadog test management.
|
78
|
+
# @return [Boolean] true if this test is quarantined, false otherwise.
|
79
|
+
def quarantined?
|
80
|
+
get_tag(Ext::Test::TAG_IS_QUARANTINED) == "true"
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns "true" if this test is disabled by Datadog test management.
|
84
|
+
# @return [Boolean] true if this test is disabled, false otherwise.
|
85
|
+
def disabled?
|
86
|
+
get_tag(Ext::Test::TAG_IS_TEST_DISABLED) == "true"
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns "true" if this flaky test has fixing attempts (determined by Datadog backend).
|
90
|
+
# @return [Boolean] true if this test is attempted to be fixed.
|
91
|
+
def attempt_to_fix?
|
92
|
+
get_tag(Ext::Test::TAG_IS_ATTEMPT_TO_FIX) == "true"
|
93
|
+
end
|
94
|
+
|
95
|
+
# Marks this test as unskippable by the Test Impact Analysis.
|
78
96
|
# This must be done before the test execution starts.
|
79
97
|
#
|
80
98
|
# Examples of tests that should be unskippable:
|
@@ -88,7 +106,7 @@ module Datadog
|
|
88
106
|
TestOptimisation::Telemetry.itr_unskippable
|
89
107
|
set_tag(Ext::Test::TAG_ITR_UNSKIPPABLE, "true")
|
90
108
|
|
91
|
-
if
|
109
|
+
if skipped_by_test_impact_analysis?
|
92
110
|
clear_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR)
|
93
111
|
|
94
112
|
TestOptimisation::Telemetry.itr_forced_run
|
@@ -110,7 +128,13 @@ module Datadog
|
|
110
128
|
def failed!(exception: nil)
|
111
129
|
super
|
112
130
|
|
113
|
-
|
131
|
+
# if we should ignore failures, we consider this test to be passed
|
132
|
+
if should_ignore_failures?
|
133
|
+
# use a special "fail_ignored" status to mark this test as failed but ignored
|
134
|
+
record_test_result(Ext::Test::ExecutionStatsStatus::FAIL_IGNORED)
|
135
|
+
else
|
136
|
+
record_test_result(Ext::Test::Status::FAIL)
|
137
|
+
end
|
114
138
|
end
|
115
139
|
|
116
140
|
# Sets the status of the span to "skip".
|
@@ -148,6 +172,40 @@ module Datadog
|
|
148
172
|
!!test_suite&.any_test_retry_passed?(datadog_test_id)
|
149
173
|
end
|
150
174
|
|
175
|
+
# @internal
|
176
|
+
def all_executions_failed?
|
177
|
+
!!test_suite&.all_executions_failed?(datadog_test_id)
|
178
|
+
end
|
179
|
+
|
180
|
+
# @internal
|
181
|
+
def all_executions_passed?
|
182
|
+
!!test_suite&.all_executions_passed?(datadog_test_id)
|
183
|
+
end
|
184
|
+
|
185
|
+
# @internal
|
186
|
+
def datadog_skip_reason
|
187
|
+
if skipped_by_test_impact_analysis?
|
188
|
+
Ext::Test::SkipReason::TEST_IMPACT_ANALYSIS
|
189
|
+
elsif disabled? || quarantined?
|
190
|
+
Ext::Test::SkipReason::TEST_MANAGEMENT_DISABLED
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# @internal
|
195
|
+
def should_skip?
|
196
|
+
skipped_by_test_impact_analysis? || (disabled? && !attempt_to_fix?)
|
197
|
+
end
|
198
|
+
|
199
|
+
# @internal
|
200
|
+
def should_ignore_failures?
|
201
|
+
quarantined? || disabled? || any_retry_passed?
|
202
|
+
end
|
203
|
+
|
204
|
+
# @internal
|
205
|
+
def skipped_by_test_impact_analysis?
|
206
|
+
get_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR) == "true"
|
207
|
+
end
|
208
|
+
|
151
209
|
private
|
152
210
|
|
153
211
|
def record_test_result(datadog_status)
|
@@ -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
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "pp"
|
4
4
|
|
5
|
+
require "datadog/core/telemetry/logging"
|
5
6
|
require "datadog/core/utils/forking"
|
6
7
|
|
7
8
|
require_relative "../ext/test"
|
@@ -19,7 +20,7 @@ require_relative "telemetry"
|
|
19
20
|
module Datadog
|
20
21
|
module CI
|
21
22
|
module TestOptimisation
|
22
|
-
#
|
23
|
+
# Test Impact Analysis implementation
|
23
24
|
# Integrates with backend to provide test impact analysis data and
|
24
25
|
# skip tests that are not impacted by the changes
|
25
26
|
class Component
|
@@ -155,7 +156,7 @@ module Datadog
|
|
155
156
|
|
156
157
|
if skippable?(test)
|
157
158
|
if forked?
|
158
|
-
Datadog.logger.warn { "
|
159
|
+
Datadog.logger.warn { "Test Impact Analysis is not supported for forking test runners yet" }
|
159
160
|
return
|
160
161
|
end
|
161
162
|
|
@@ -171,7 +172,7 @@ module Datadog
|
|
171
172
|
@mutex.synchronize do
|
172
173
|
@total_tests_count += 1
|
173
174
|
|
174
|
-
return if !test.skipped? || !test.
|
175
|
+
return if !test.skipped? || !test.skipped_by_test_impact_analysis?
|
175
176
|
|
176
177
|
if forked?
|
177
178
|
Datadog.logger.warn { "ITR is not supported for forking test runners yet" }
|
@@ -224,6 +225,7 @@ module Datadog
|
|
224
225
|
Datadog.logger.debug("Loaded Datadog code coverage collector, using coverage mode: #{code_coverage_mode}")
|
225
226
|
rescue LoadError => e
|
226
227
|
Datadog.logger.error("Failed to load coverage collector: #{e}. Code coverage will not be collected.")
|
228
|
+
Core::Telemetry::Logger.report(e, description: "Failed to load coverage collector")
|
227
229
|
|
228
230
|
@code_coverage_enabled = false
|
229
231
|
end
|