datadog-ci 1.5.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -2
  3. data/README.md +1 -0
  4. data/lib/datadog/ci/configuration/components.rb +2 -1
  5. data/lib/datadog/ci/configuration/settings.rb +5 -0
  6. data/lib/datadog/ci/contrib/cucumber/formatter.rb +25 -3
  7. data/lib/datadog/ci/contrib/minitest/helpers.rb +4 -4
  8. data/lib/datadog/ci/contrib/minitest/runnable.rb +15 -2
  9. data/lib/datadog/ci/contrib/rspec/example_group.rb +7 -1
  10. data/lib/datadog/ci/contrib/simplecov/configuration/settings.rb +26 -0
  11. data/lib/datadog/ci/contrib/simplecov/ext.rb +15 -0
  12. data/lib/datadog/ci/contrib/simplecov/integration.rb +47 -0
  13. data/lib/datadog/ci/contrib/simplecov/patcher.rb +28 -0
  14. data/lib/datadog/ci/contrib/simplecov/result_extractor.rb +36 -0
  15. data/lib/datadog/ci/ext/environment/extractor.rb +5 -0
  16. data/lib/datadog/ci/ext/environment/providers/base.rb +4 -0
  17. data/lib/datadog/ci/ext/environment/providers/github_actions.rb +24 -0
  18. data/lib/datadog/ci/ext/git.rb +5 -0
  19. data/lib/datadog/ci/ext/settings.rb +1 -0
  20. data/lib/datadog/ci/ext/test.rb +9 -0
  21. data/lib/datadog/ci/span.rb +14 -0
  22. data/lib/datadog/ci/test.rb +0 -7
  23. data/lib/datadog/ci/test_session.rb +18 -0
  24. data/lib/datadog/ci/test_visibility/component.rb +23 -5
  25. data/lib/datadog/ci/test_visibility/context.rb +4 -0
  26. data/lib/datadog/ci/test_visibility/null_component.rb +7 -0
  27. data/lib/datadog/ci/test_visibility/telemetry.rb +1 -3
  28. data/lib/datadog/ci/test_visibility/total_coverage.rb +36 -0
  29. data/lib/datadog/ci/test_visibility/transport.rb +21 -3
  30. data/lib/datadog/ci/utils/test_run.rb +8 -0
  31. data/lib/datadog/ci/version.rb +1 -1
  32. data/lib/datadog/ci.rb +1 -0
  33. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 876a0e7ada3aeaf87b19dade6ee73fec0fe8d37387cde5fc31b43aaad76e99c5
4
- data.tar.gz: 8c46ad7c35a621ae03b7b42faa5b3c6934066e422ff4d3a8ac2249cd6466ecd3
3
+ metadata.gz: f4cb94bf0ef94f54289e38a26649d24eeaaca99ba91229f5ebb096c046bdc582
4
+ data.tar.gz: 9a88847a58121a1516b95fc1abac1fa89d4a1bab5c4593f932fc804410ee8a57
5
5
  SHA512:
6
- metadata.gz: 8f312e6920cad6072665c00dc5d253e66a7c3272abda20e79f86cf11beb38fffe0be789f644affb6f8920e458f00f221c7969fb84c2ee6f2198af6a140eafc64
7
- data.tar.gz: f9b7c2b184e7a140269eaa2069cbf5062270d6bb07410fd9b46cab4c521a8d0bd694a676086164c58300c264357e53e0118611053f6bdf0c2dee757b487b4769
6
+ metadata.gz: 7830a7b52821efbea8e0f86e878588ef96718e845fd7a33575a4a5e92d350e26e36bb4b78f7ce78b58ff59654c62973c4eca58f21c63b958d44f4d8feff122cc
7
+ data.tar.gz: da8d30e84f9dea71eb498cccb6e03cb092e27e773008dc7533188020ee5cb59de774e4d2be32821e3fd82e28f4ccb426572695fd532dc7f7fff6479a392bca85
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.7.0] - 2024-09-25
4
+
5
+ ### Added
6
+ * Report total lines coverage percentage to Datadog ([#240][])
7
+ * add source location info to test suites ([#239][])
8
+ * Add pull_request extra tags for GitHub Actions ([#238][])
9
+
10
+ ## [1.6.0] - 2024-09-20
11
+
12
+
13
+ ### Added
14
+ * support logical names for test sessions ([#235][])
15
+ * Send internal vCPU count metric ([#236][])
16
+
3
17
  ## [1.5.0] - 2024-09-18
4
18
 
5
19
  ### Added
@@ -325,7 +339,9 @@ Currently test suite level visibility is not used by our instrumentation: it wil
325
339
 
326
340
  - Ruby versions < 2.7 no longer supported ([#8][])
327
341
 
328
- [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.5.0...main
342
+ [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.7.0...main
343
+ [1.7.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.6.0...v1.7.0
344
+ [1.6.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.5.0...v1.6.0
329
345
  [1.5.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.4.1...v1.5.0
330
346
  [1.4.1]: https://github.com/DataDog/datadog-ci-rb/compare/v1.4.0...v1.4.1
331
347
  [1.4.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.3.0...v1.4.0
@@ -467,4 +483,9 @@ Currently test suite level visibility is not used by our instrumentation: it wil
467
483
  [#226]: https://github.com/DataDog/datadog-ci-rb/issues/226
468
484
  [#227]: https://github.com/DataDog/datadog-ci-rb/issues/227
469
485
  [#229]: https://github.com/DataDog/datadog-ci-rb/issues/229
470
- [#231]: https://github.com/DataDog/datadog-ci-rb/issues/231
486
+ [#231]: https://github.com/DataDog/datadog-ci-rb/issues/231
487
+ [#235]: https://github.com/DataDog/datadog-ci-rb/issues/235
488
+ [#236]: https://github.com/DataDog/datadog-ci-rb/issues/236
489
+ [#238]: https://github.com/DataDog/datadog-ci-rb/issues/238
490
+ [#239]: https://github.com/DataDog/datadog-ci-rb/issues/239
491
+ [#240]: https://github.com/DataDog/datadog-ci-rb/issues/240
data/README.md CHANGED
@@ -13,6 +13,7 @@ Learn more on our [official website](https://docs.datadoghq.com/tests/) and chec
13
13
  - [Test Visibility](https://docs.datadoghq.com/tests/) - collect metrics and results for your tests
14
14
  - [Intelligent test runner](https://docs.datadoghq.com/intelligent_test_runner/) - save time by selectively running only tests affected by code changes
15
15
  - [Auto test retries](https://docs.datadoghq.com/tests/auto_test_retries/?tab=ruby) - retrying failing tests up to N times to avoid failing your build due to flaky tests
16
+ - [Early flake detection](https://docs.datadoghq.com/tests/early_flake_detection?tab=ruby) - Datadog’s test flakiness solution that identifies flakes early by running newly added tests multiple times
16
17
  - [Search and manage CI tests](https://docs.datadoghq.com/tests/search/)
17
18
  - [Enhance developer workflows](https://docs.datadoghq.com/tests/developer_workflows)
18
19
  - [Flaky test management](https://docs.datadoghq.com/tests/guides/flaky_test_management/)
@@ -123,7 +123,8 @@ module Datadog
123
123
  # @type ivar @test_optimisation: Datadog::CI::TestOptimisation::Component
124
124
  @test_optimisation = build_test_optimisation(settings, test_visibility_api)
125
125
  @test_visibility = TestVisibility::Component.new(
126
- test_suite_level_visibility_enabled: !settings.ci.force_test_level_visibility
126
+ test_suite_level_visibility_enabled: !settings.ci.force_test_level_visibility,
127
+ logical_test_session_name: settings.ci.test_session_name
127
128
  )
128
129
  end
129
130
 
@@ -24,6 +24,11 @@ module Datadog
24
24
  o.default false
25
25
  end
26
26
 
27
+ option :test_session_name do |o|
28
+ o.type :string, nilable: true
29
+ o.env CI::Ext::Settings::ENV_TEST_SESSION_NAME
30
+ end
31
+
27
32
  option :agentless_mode_enabled do |o|
28
33
  o.type :bool
29
34
  o.env CI::Ext::Settings::ENV_AGENTLESS_MODE_ENABLED
@@ -70,7 +70,12 @@ module Datadog
70
70
  tags[CI::Ext::Test::TAG_PARAMETERS] = Utils::TestRun.test_parameters(arguments: parameters)
71
71
  end
72
72
 
73
- start_test_suite(test_suite_name) unless same_test_suite_as_current?(test_suite_name)
73
+ unless same_test_suite_as_current?(test_suite_name)
74
+ start_test_suite(
75
+ test_suite_name,
76
+ tags: test_suite_source_file_tags(event.test_case)
77
+ )
78
+ end
74
79
 
75
80
  test_span = test_visibility_component.trace_test(
76
81
  event.test_case.name,
@@ -146,10 +151,10 @@ module Datadog
146
151
  test_session.finish
147
152
  end
148
153
 
149
- def start_test_suite(test_suite_name)
154
+ def start_test_suite(test_suite_name, tags: {})
150
155
  finish_current_test_suite
151
156
 
152
- @current_test_suite = test_visibility_component.start_test_suite(test_suite_name)
157
+ @current_test_suite = test_visibility_component.start_test_suite(test_suite_name, tags: tags)
153
158
  end
154
159
 
155
160
  def finish_current_test_suite
@@ -201,6 +206,23 @@ module Datadog
201
206
  def test_visibility_component
202
207
  Datadog.send(:components).test_visibility
203
208
  end
209
+
210
+ def test_suite_source_file_tags(test_case)
211
+ if test_case.respond_to?(:parent_locations)
212
+ # supported in cucumber >= 9.0
213
+ source_file = test_case.parent_locations.file
214
+ line_number = test_case.parent_locations.line.to_s
215
+ else
216
+ # fallback for cucumber < 9.0
217
+ source_file = test_case.location.file
218
+ line_number = "1"
219
+ end
220
+
221
+ {
222
+ CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(source_file),
223
+ CI::Ext::Test::TAG_SOURCE_START => line_number.to_s
224
+ }
225
+ end
204
226
  end
205
227
  end
206
228
  end
@@ -6,7 +6,7 @@ module Datadog
6
6
  module Minitest
7
7
  module Helpers
8
8
  def self.test_suite_name(klass, method_name)
9
- source_location = extract_source_location_from_class(klass)
9
+ source_location = extract_source_location_from_class(klass)&.first
10
10
  # if we are in anonymous class, fallback to the method source location
11
11
  if source_location.nil?
12
12
  source_location, = klass.instance_method(method_name).source_location
@@ -23,11 +23,11 @@ module Datadog
23
23
  end
24
24
 
25
25
  def self.extract_source_location_from_class(klass)
26
- return nil if klass.nil? || klass.name.nil?
26
+ return [] if klass.nil? || klass.name.nil?
27
27
 
28
- klass.const_source_location(klass.name)&.first
28
+ klass.const_source_location(klass.name)
29
29
  rescue
30
- nil
30
+ []
31
31
  end
32
32
  end
33
33
  end
@@ -18,8 +18,21 @@ module Datadog
18
18
  return super if method.nil?
19
19
 
20
20
  test_suite_name = Helpers.test_suite_name(self, method)
21
-
22
- test_suite = test_visibility_component.start_test_suite(test_suite_name)
21
+ source_file, line_number = Helpers.extract_source_location_from_class(self)
22
+
23
+ test_suite_tags = if source_file
24
+ {
25
+ CI::Ext::Test::TAG_SOURCE_FILE => (Git::LocalRepository.relative_to_root(source_file) if source_file),
26
+ CI::Ext::Test::TAG_SOURCE_START => line_number&.to_s
27
+ }
28
+ else
29
+ {}
30
+ end
31
+
32
+ test_suite = test_visibility_component.start_test_suite(
33
+ test_suite_name,
34
+ tags: test_suite_tags
35
+ )
23
36
 
24
37
  results = super
25
38
  return results unless test_suite
@@ -21,7 +21,13 @@ module Datadog
21
21
  return super unless top_level?
22
22
 
23
23
  suite_name = "#{description} at #{file_path}"
24
- test_suite = test_visibility_component.start_test_suite(suite_name)
24
+ test_suite = test_visibility_component.start_test_suite(
25
+ suite_name,
26
+ tags: {
27
+ CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(metadata[:file_path]),
28
+ CI::Ext::Test::TAG_SOURCE_START => metadata[:line_number].to_s
29
+ }
30
+ )
25
31
 
26
32
  success = super
27
33
  return success unless test_suite
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "datadog/core"
4
+
5
+ require_relative "../ext"
6
+ require_relative "../../settings"
7
+
8
+ module Datadog
9
+ module CI
10
+ module Contrib
11
+ module Simplecov
12
+ module Configuration
13
+ # Custom settings for the Simplecov integration
14
+ # @public_api
15
+ class Settings < Datadog::CI::Contrib::Settings
16
+ option :enabled do |o|
17
+ o.type :bool
18
+ o.env Ext::ENV_ENABLED
19
+ o.default true
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module CI
5
+ module Contrib
6
+ module Simplecov
7
+ # Simplecov integration constants
8
+ # @public_api
9
+ module Ext
10
+ ENV_ENABLED = "DD_CIVISIBILITY_SIMPLECOV_INSTRUMENTATION_ENABLED"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../integration"
4
+ require_relative "configuration/settings"
5
+ require_relative "patcher"
6
+
7
+ module Datadog
8
+ module CI
9
+ module Contrib
10
+ module Simplecov
11
+ # Description of Simplecov integration
12
+ class Integration
13
+ include Datadog::CI::Contrib::Integration
14
+
15
+ MINIMUM_VERSION = Gem::Version.new("0.18.0")
16
+
17
+ register_as :simplecov
18
+
19
+ def self.version
20
+ Gem.loaded_specs["simplecov"]&.version
21
+ end
22
+
23
+ def self.loaded?
24
+ !defined?(::SimpleCov).nil?
25
+ end
26
+
27
+ def self.compatible?
28
+ super && version >= MINIMUM_VERSION
29
+ end
30
+
31
+ # additional instrumentations for test helpers are auto instrumented on test session start
32
+ def auto_instrument?
33
+ true
34
+ end
35
+
36
+ def new_configuration
37
+ Configuration::Settings.new
38
+ end
39
+
40
+ def patcher
41
+ Patcher
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "datadog/tracing/contrib/patcher"
4
+
5
+ require_relative "result_extractor"
6
+
7
+ module Datadog
8
+ module CI
9
+ module Contrib
10
+ module Simplecov
11
+ # Patcher enables patching of 'SimpleCov' module.
12
+ module Patcher
13
+ include Datadog::Tracing::Contrib::Patcher
14
+
15
+ module_function
16
+
17
+ def target_version
18
+ Integration.version
19
+ end
20
+
21
+ def patch
22
+ ::SimpleCov.include(ResultExtractor)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "coverage"
4
+
5
+ module Datadog
6
+ module CI
7
+ module Contrib
8
+ module Simplecov
9
+ module ResultExtractor
10
+ def self.included(base)
11
+ base.singleton_class.prepend(ClassMethods)
12
+ end
13
+
14
+ module ClassMethods
15
+ def __dd_peek_result
16
+ unless datadog_configuration[:enabled]
17
+ Datadog.logger.debug("SimpleCov instrumentation is disabled")
18
+ return nil
19
+ end
20
+
21
+ result = ::SimpleCov::UselessResultsRemover.call(
22
+ ::SimpleCov::ResultAdapter.call(::Coverage.peek_result)
23
+ )
24
+
25
+ ::SimpleCov::Result.new(add_not_loaded_files(result))
26
+ end
27
+
28
+ def datadog_configuration
29
+ Datadog.configuration.ci[:simplecov]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -49,6 +49,11 @@ module Datadog
49
49
  Git::TAG_COMMIT_SHA => @provider.git_commit_sha
50
50
  }
51
51
 
52
+ # set additional tags if provider needs them
53
+ @provider.additional_tags.each do |key, value|
54
+ @tags[key] = value
55
+ end
56
+
52
57
  # Normalize Git references and filter sensitive data
53
58
  normalize_git!
54
59
  # Expand ~
@@ -96,6 +96,10 @@ module Datadog
96
96
  def git_commit_sha
97
97
  end
98
98
 
99
+ def additional_tags
100
+ {}
101
+ end
102
+
99
103
  private
100
104
 
101
105
  def set_branch_and_tag
@@ -75,6 +75,30 @@ module Datadog
75
75
  }.reject { |_, v| v.nil? }.to_json
76
76
  end
77
77
 
78
+ def additional_tags
79
+ base_ref = env["GITHUB_BASE_REF"]
80
+ return {} if base_ref.nil? || base_ref.empty?
81
+
82
+ # @type var result: Hash[String, String]
83
+ result = {
84
+ Git::TAG_PULL_REQUEST_BASE_BRANCH => base_ref
85
+ }
86
+
87
+ event_path = env["GITHUB_EVENT_PATH"]
88
+ event_json = JSON.parse(File.read(event_path))
89
+
90
+ head_sha = event_json.dig("pull_request", "head", "sha")
91
+ result[Git::TAG_COMMIT_HEAD_SHA] = head_sha if head_sha
92
+
93
+ base_sha = event_json.dig("pull_request", "base", "sha")
94
+ result[Git::TAG_PULL_REQUEST_BASE_BRANCH_SHA] = base_sha if base_sha
95
+
96
+ result
97
+ rescue => e
98
+ Datadog.logger.error("Failed to extract additional tags from GitHub Actions: #{e}")
99
+ {}
100
+ end
101
+
78
102
  private
79
103
 
80
104
  def github_server_url
@@ -20,6 +20,11 @@ module Datadog
20
20
  TAG_COMMIT_MESSAGE = "git.commit.message"
21
21
  TAG_COMMIT_SHA = "git.commit.sha"
22
22
 
23
+ # additional tags that we use for github actions jobs with "pull_request" target
24
+ TAG_COMMIT_HEAD_SHA = "git.commit.head_sha"
25
+ TAG_PULL_REQUEST_BASE_BRANCH = "git.pull_request.base_branch"
26
+ TAG_PULL_REQUEST_BASE_BRANCH_SHA = "git.pull_request.base_branch_sha"
27
+
23
28
  ENV_REPOSITORY_URL = "DD_GIT_REPOSITORY_URL"
24
29
  ENV_COMMIT_SHA = "DD_GIT_COMMIT_SHA"
25
30
  ENV_BRANCH = "DD_GIT_BRANCH"
@@ -19,6 +19,7 @@ module Datadog
19
19
  ENV_RETRY_FAILED_TESTS_MAX_ATTEMPTS = "DD_CIVISIBILITY_FLAKY_RETRY_COUNT"
20
20
  ENV_RETRY_FAILED_TESTS_TOTAL_LIMIT = "DD_CIVISIBILITY_TOTAL_FLAKY_RETRY_COUNT"
21
21
  ENV_RETRY_NEW_TESTS_ENABLED = "DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED"
22
+ ENV_TEST_SESSION_NAME = "DD_TEST_SESSION_NAME"
22
23
 
23
24
  # Source: https://docs.datadoghq.com/getting_started/site/
24
25
  DD_SITE_ALLOWLIST = %w[
@@ -64,10 +64,19 @@ module Datadog
64
64
  TAG_EARLY_FLAKE_ENABLED = "test.early_flake.enabled" # true if early flake detection is enabled
65
65
  TAG_EARLY_FLAKE_ABORT_REASON = "test.early_flake.abort_reason" # reason why early flake detection was aborted
66
66
 
67
+ # Tags for total code coverage
68
+ TAG_CODE_COVERAGE_LINES_PCT = "test.code_coverage.lines_pct"
69
+
67
70
  # internal APM tag to mark a span as a test span
68
71
  TAG_SPAN_KIND = "span.kind"
69
72
  SPAN_KIND_TEST = "test"
70
73
 
74
+ # common tags that are serialized directly in msgpack header in metadata field
75
+ METADATA_TAG_TEST_SESSION_NAME = "test_session.name"
76
+
77
+ # internal metric with the number of virtual CPUs
78
+ METRIC_CPU_COUNT = "_dd.host.vcpu_count"
79
+
71
80
  # tags that are common for the whole session and can be inherited from the test session
72
81
  INHERITABLE_TAGS = [TAG_FRAMEWORK, TAG_FRAMEWORK_VERSION].freeze
73
82
 
@@ -114,6 +114,13 @@ module Datadog
114
114
  tracer_span.clear_tag(key)
115
115
  end
116
116
 
117
+ # Gets metric value by key.
118
+ # @param [String] key the key of the metric.
119
+ # @return [Numeric] value the value of the metric.
120
+ def get_metric(key)
121
+ tracer_span.get_metric(key)
122
+ end
123
+
117
124
  # Sets metric value by key.
118
125
  # @param [String] key the key of the metric.
119
126
  # @param [Numeric] value the value of the metric.
@@ -183,6 +190,13 @@ module Datadog
183
190
  tracer_span.get_tag(Ext::Test::TAG_RUNTIME_VERSION)
184
191
  end
185
192
 
193
+ # Source file path where the test or test suite defined (relative to git repository root).
194
+ # @return [String] the source file path of the test
195
+ # @return [nil] if the source file path is not found
196
+ def source_file
197
+ get_tag(Ext::Test::TAG_SOURCE_FILE)
198
+ end
199
+
186
200
  def set_environment_runtime_tags
187
201
  tracer_span.set_tag(Ext::Test::TAG_OS_ARCHITECTURE, ::RbConfig::CONFIG["host_cpu"])
188
202
  tracer_span.set_tag(Ext::Test::TAG_OS_PLATFORM, ::RbConfig::CONFIG["host_os"])
@@ -57,13 +57,6 @@ module Datadog
57
57
  get_tag(Ext::Test::TAG_TEST_SESSION_ID)
58
58
  end
59
59
 
60
- # Source file path of the test relative to git repository root.
61
- # @return [String] the source file path of the test
62
- # @return [nil] if the source file path is not found
63
- def source_file
64
- get_tag(Ext::Test::TAG_SOURCE_FILE)
65
- end
66
-
67
60
  # Returns "true" if the test is skipped by the intelligent test runner.
68
61
  # @return [Boolean] true if the test is skipped by the intelligent test runner, false otherwise.
69
62
  def skipped_by_itr?
@@ -28,6 +28,24 @@ module Datadog
28
28
  get_tag(Ext::Test::TAG_COMMAND)
29
29
  end
30
30
 
31
+ # Return the test session's command used to run the tests
32
+ # @return [String] the command for this test session.
33
+ def test_command
34
+ get_tag(Ext::Test::TAG_COMMAND)
35
+ end
36
+
37
+ # Return the test session's CI provider name (e.g. "travis", "circleci", etc.)
38
+ # @return [String] the provider name for this test session.
39
+ def ci_provider
40
+ get_tag(Ext::Environment::TAG_PROVIDER_NAME)
41
+ end
42
+
43
+ # Return the test session's CI job name (e.g. "build", "test", etc.)
44
+ # @return [String] the job name for this test session.
45
+ def ci_job_name
46
+ get_tag(Ext::Environment::TAG_JOB_NAME)
47
+ end
48
+
31
49
  def skipping_tests?
32
50
  get_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_ENABLED) == "true"
33
51
  end
@@ -4,6 +4,7 @@ require "rbconfig"
4
4
 
5
5
  require_relative "context"
6
6
  require_relative "telemetry"
7
+ require_relative "total_coverage"
7
8
 
8
9
  require_relative "../codeowners/parser"
9
10
  require_relative "../contrib/contrib"
@@ -17,15 +18,17 @@ module Datadog
17
18
  module TestVisibility
18
19
  # Common behavior for CI tests
19
20
  class Component
20
- attr_reader :test_suite_level_visibility_enabled
21
+ attr_reader :test_suite_level_visibility_enabled, :logical_test_session_name
21
22
 
22
23
  def initialize(
23
24
  test_suite_level_visibility_enabled: false,
24
- codeowners: Codeowners::Parser.new(Git::LocalRepository.root).parse
25
+ codeowners: Codeowners::Parser.new(Git::LocalRepository.root).parse,
26
+ logical_test_session_name: nil
25
27
  )
26
28
  @test_suite_level_visibility_enabled = test_suite_level_visibility_enabled
27
29
  @context = Context.new
28
30
  @codeowners = codeowners
31
+ @logical_test_session_name = logical_test_session_name
29
32
  end
30
33
 
31
34
  def start_test_session(service: nil, tags: {}, total_tests_count: 0)
@@ -152,6 +155,9 @@ module Datadog
152
155
  Telemetry.test_session_started(test_session)
153
156
  Telemetry.event_created(test_session)
154
157
 
158
+ # sets logical test session name if none provided by the user
159
+ override_logical_test_session_name!(test_session) if logical_test_session_name.nil?
160
+
155
161
  # signal Remote::Component to configure the library
156
162
  remote.configure(test_session)
157
163
  end
@@ -161,6 +167,8 @@ module Datadog
161
167
  end
162
168
 
163
169
  def on_test_suite_started(test_suite)
170
+ set_codeowners(test_suite)
171
+
164
172
  Telemetry.event_created(test_suite)
165
173
  end
166
174
 
@@ -181,6 +189,8 @@ module Datadog
181
189
  def on_test_session_finished(test_session)
182
190
  test_optimisation.write_test_session_tags(test_session)
183
191
 
192
+ TotalCoverage.extract_lines_pct(test_session)
193
+
184
194
  Telemetry.event_finished(test_session)
185
195
  end
186
196
 
@@ -218,10 +228,10 @@ module Datadog
218
228
  end
219
229
  end
220
230
 
221
- def set_codeowners(test)
222
- source = test.source_file
231
+ def set_codeowners(span)
232
+ source = span.source_file
223
233
  owners = @codeowners.list_owners(source) if source
224
- test.set_tag(Ext::Test::TAG_CODEOWNERS, owners) unless owners.nil?
234
+ span.set_tag(Ext::Test::TAG_CODEOWNERS, owners) unless owners.nil?
225
235
  end
226
236
 
227
237
  def fix_test_suite!(test)
@@ -269,6 +279,14 @@ module Datadog
269
279
  end
270
280
  end
271
281
 
282
+ def override_logical_test_session_name!(test_session)
283
+ @logical_test_session_name = test_session.test_command
284
+ ci_job_name = test_session.ci_job_name
285
+ if ci_job_name
286
+ @logical_test_session_name = "#{ci_job_name}-#{@logical_test_session_name}"
287
+ end
288
+ end
289
+
272
290
  def test_optimisation
273
291
  Datadog.send(:components).test_optimisation
274
292
  end
@@ -11,6 +11,8 @@ require_relative "../ext/app_types"
11
11
  require_relative "../ext/environment"
12
12
  require_relative "../ext/test"
13
13
 
14
+ require_relative "../utils/test_run"
15
+
14
16
  require_relative "../span"
15
17
  require_relative "../test"
16
18
  require_relative "../test_session"
@@ -203,6 +205,8 @@ module Datadog
203
205
 
204
206
  ci_span.set_tags(tags)
205
207
  ci_span.set_tags(@environment_tags)
208
+
209
+ ci_span.set_metric(Ext::Test::METRIC_CPU_COUNT, Utils::TestRun.virtual_cpu_count)
206
210
  end
207
211
 
208
212
  # PROPAGATING CONTEXT FROM TOP-LEVEL TO THE LOWER LEVELS
@@ -53,6 +53,13 @@ module Datadog
53
53
  def remove_test_finished_callback
54
54
  end
55
55
 
56
+ def test_suite_level_visibility_enabled
57
+ false
58
+ end
59
+
60
+ def logical_test_session_name
61
+ end
62
+
56
63
  private
57
64
 
58
65
  def skip_tracing(block = nil)
@@ -34,9 +34,7 @@ module Datadog
34
34
  1,
35
35
  {
36
36
  Ext::Telemetry::TAG_AUTO_INJECTED => "false", # ruby doesn't support auto injection yet
37
- Ext::Telemetry::TAG_PROVIDER =>
38
- test_session.get_tag(Ext::Environment::TAG_PROVIDER_NAME) ||
39
- Ext::Telemetry::Provider::UNSUPPORTED
37
+ Ext::Telemetry::TAG_PROVIDER => test_session.ci_provider || Ext::Telemetry::Provider::UNSUPPORTED
40
38
  }
41
39
  )
42
40
  end
@@ -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
+ module TotalCoverage
9
+ def self.extract_lines_pct(test_session)
10
+ unless defined?(::SimpleCov)
11
+ Datadog.logger.debug("SimpleCov is not loaded, skipping code coverage extraction")
12
+ return
13
+ end
14
+
15
+ unless ::SimpleCov.running
16
+ Datadog.logger.debug("SimpleCov is not running, skipping code coverage extraction")
17
+ return
18
+ end
19
+
20
+ unless ::SimpleCov.respond_to?(:__dd_peek_result)
21
+ Datadog.logger.debug("SimpleCov is not patched, skipping code coverage extraction")
22
+ return
23
+ end
24
+
25
+ result = ::SimpleCov.__dd_peek_result
26
+ unless result
27
+ Datadog.logger.debug("SimpleCov result is nil, skipping code coverage extraction")
28
+ return
29
+ end
30
+
31
+ test_session.set_tag(Ext::Test::TAG_CODE_COVERAGE_LINES_PCT, result.covered_percent)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -3,6 +3,7 @@
3
3
  require "datadog/core/environment/identity"
4
4
 
5
5
  require_relative "serializers/factories/test_level"
6
+ require_relative "../ext/app_types"
6
7
  require_relative "../ext/telemetry"
7
8
  require_relative "../ext/transport"
8
9
  require_relative "../transport/event_platform_transport"
@@ -51,7 +52,12 @@ module Datadog
51
52
  end
52
53
 
53
54
  def encode_span(trace, span)
54
- serializer = serializers_factory.serializer(trace, span, options: {itr_correlation_id: itr&.correlation_id})
55
+ serializer = serializers_factory.serializer(
56
+ trace,
57
+ span,
58
+ options: {itr_correlation_id: test_optimisation&.correlation_id}
59
+ )
60
+
55
61
  if serializer.valid?
56
62
  encoded = encoder.encode(serializer)
57
63
  return nil if event_too_large?(span, encoded)
@@ -75,7 +81,7 @@ module Datadog
75
81
  packer.write(1)
76
82
 
77
83
  packer.write("metadata")
78
- packer.write_map_header(1)
84
+ packer.write_map_header(1 + Ext::AppTypes::CI_SPAN_TYPES.size)
79
85
 
80
86
  packer.write("*")
81
87
  metadata_fields_count = dd_env ? 4 : 3
@@ -95,12 +101,24 @@ module Datadog
95
101
  packer.write("library_version")
96
102
  packer.write(Datadog::CI::VERSION::STRING)
97
103
 
104
+ Ext::AppTypes::CI_SPAN_TYPES.each do |ci_span_type|
105
+ packer.write(ci_span_type)
106
+ packer.write_map_header(1)
107
+
108
+ packer.write(Ext::Test::METADATA_TAG_TEST_SESSION_NAME)
109
+ packer.write(test_visibility&.logical_test_session_name)
110
+ end
111
+
98
112
  packer.write("events")
99
113
  end
100
114
 
101
- def itr
115
+ def test_optimisation
102
116
  @test_optimisation ||= Datadog::CI.send(:test_optimisation)
103
117
  end
118
+
119
+ def test_visibility
120
+ @test_visibility ||= Datadog::CI.send(:test_visibility)
121
+ end
104
122
  end
105
123
  end
106
124
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "etc"
4
+
3
5
  module Datadog
4
6
  module CI
5
7
  module Utils
@@ -34,6 +36,12 @@ module Datadog
34
36
  end
35
37
  res
36
38
  end
39
+
40
+ def self.virtual_cpu_count
41
+ return @virtual_cpu_count if defined?(@virtual_cpu_count)
42
+
43
+ @virtual_cpu_count = ::Etc.nprocessors
44
+ end
37
45
  end
38
46
  end
39
47
  end
@@ -4,7 +4,7 @@ module Datadog
4
4
  module CI
5
5
  module VERSION
6
6
  MAJOR = 1
7
- MINOR = 5
7
+ MINOR = 7
8
8
  PATCH = 0
9
9
  PRE = nil
10
10
  BUILD = nil
data/lib/datadog/ci.rb CHANGED
@@ -410,6 +410,7 @@ require_relative "ci/contrib/cucumber/integration"
410
410
  require_relative "ci/contrib/rspec/integration"
411
411
  require_relative "ci/contrib/minitest/integration"
412
412
  require_relative "ci/contrib/selenium/integration"
413
+ require_relative "ci/contrib/simplecov/integration"
413
414
 
414
415
  # Configuration extensions
415
416
  require_relative "ci/configuration/extensions"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog-ci
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-18 00:00:00.000000000 Z
11
+ date: 2024-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: datadog
@@ -103,6 +103,11 @@ files:
103
103
  - lib/datadog/ci/contrib/selenium/patcher.rb
104
104
  - lib/datadog/ci/contrib/selenium/rum.rb
105
105
  - lib/datadog/ci/contrib/settings.rb
106
+ - lib/datadog/ci/contrib/simplecov/configuration/settings.rb
107
+ - lib/datadog/ci/contrib/simplecov/ext.rb
108
+ - lib/datadog/ci/contrib/simplecov/integration.rb
109
+ - lib/datadog/ci/contrib/simplecov/patcher.rb
110
+ - lib/datadog/ci/contrib/simplecov/result_extractor.rb
106
111
  - lib/datadog/ci/ext/app_types.rb
107
112
  - lib/datadog/ci/ext/environment.rb
108
113
  - lib/datadog/ci/ext/environment/extractor.rb
@@ -179,6 +184,7 @@ files:
179
184
  - lib/datadog/ci/test_visibility/store/global.rb
180
185
  - lib/datadog/ci/test_visibility/store/local.rb
181
186
  - lib/datadog/ci/test_visibility/telemetry.rb
187
+ - lib/datadog/ci/test_visibility/total_coverage.rb
182
188
  - lib/datadog/ci/test_visibility/transport.rb
183
189
  - lib/datadog/ci/transport/adapters/net.rb
184
190
  - lib/datadog/ci/transport/adapters/net_http_client.rb