datadog-ci 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -2
  3. data/README.md +21 -2
  4. data/lib/datadog/ci/codeowners/matcher.rb +102 -0
  5. data/lib/datadog/ci/codeowners/parser.rb +42 -0
  6. data/lib/datadog/ci/codeowners/rule.rb +33 -0
  7. data/lib/datadog/ci/concurrent_span.rb +2 -1
  8. data/lib/datadog/ci/configuration/components.rb +59 -57
  9. data/lib/datadog/ci/configuration/settings.rb +21 -0
  10. data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +4 -1
  11. data/lib/datadog/ci/contrib/cucumber/formatter.rb +69 -38
  12. data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +2 -1
  13. data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +4 -1
  14. data/lib/datadog/ci/contrib/minitest/helpers.rb +2 -1
  15. data/lib/datadog/ci/contrib/minitest/hooks.rb +9 -22
  16. data/lib/datadog/ci/contrib/minitest/patcher.rb +9 -6
  17. data/lib/datadog/ci/contrib/minitest/reporter.rb +50 -0
  18. data/lib/datadog/ci/contrib/minitest/runnable.rb +1 -1
  19. data/lib/datadog/ci/contrib/minitest/runner.rb +41 -0
  20. data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +4 -1
  21. data/lib/datadog/ci/contrib/rspec/example.rb +55 -14
  22. data/lib/datadog/ci/contrib/rspec/example_group.rb +12 -7
  23. data/lib/datadog/ci/contrib/rspec/patcher.rb +10 -1
  24. data/lib/datadog/ci/contrib/rspec/runner.rb +7 -8
  25. data/lib/datadog/ci/ext/app_types.rb +2 -0
  26. data/lib/datadog/ci/ext/environment/providers/local_git.rb +8 -29
  27. data/lib/datadog/ci/ext/settings.rb +2 -0
  28. data/lib/datadog/ci/ext/test.rb +29 -7
  29. data/lib/datadog/ci/ext/transport.rb +19 -1
  30. data/lib/datadog/ci/itr/runner.rb +67 -0
  31. data/lib/datadog/ci/span.rb +51 -2
  32. data/lib/datadog/ci/test.rb +67 -2
  33. data/lib/datadog/ci/test_module.rb +1 -1
  34. data/lib/datadog/ci/test_session.rb +10 -2
  35. data/lib/datadog/ci/test_suite.rb +53 -2
  36. data/lib/datadog/ci/test_visibility/context/local.rb +3 -9
  37. data/lib/datadog/ci/test_visibility/null_recorder.rb +2 -22
  38. data/lib/datadog/ci/test_visibility/recorder.rb +46 -20
  39. data/lib/datadog/ci/test_visibility/serializers/base.rb +6 -5
  40. data/lib/datadog/ci/test_visibility/serializers/span.rb +1 -1
  41. data/lib/datadog/ci/test_visibility/serializers/test_module.rb +1 -1
  42. data/lib/datadog/ci/test_visibility/serializers/test_session.rb +1 -1
  43. data/lib/datadog/ci/test_visibility/serializers/test_suite.rb +1 -1
  44. data/lib/datadog/ci/test_visibility/serializers/test_v1.rb +1 -1
  45. data/lib/datadog/ci/test_visibility/transport.rb +1 -5
  46. data/lib/datadog/ci/transport/api/agentless.rb +63 -0
  47. data/lib/datadog/ci/transport/api/base.rb +10 -14
  48. data/lib/datadog/ci/transport/api/builder.rb +25 -22
  49. data/lib/datadog/ci/transport/api/evp_proxy.rb +47 -7
  50. data/lib/datadog/ci/transport/http.rb +7 -1
  51. data/lib/datadog/ci/transport/remote_settings_api.rb +96 -0
  52. data/lib/datadog/ci/utils/configuration.rb +15 -0
  53. data/lib/datadog/ci/utils/git.rb +70 -0
  54. data/lib/datadog/ci/version.rb +1 -1
  55. data/lib/datadog/ci.rb +40 -56
  56. metadata +12 -90
  57. data/lib/datadog/ci/contrib/minitest/plugin.rb +0 -73
  58. data/lib/datadog/ci/null_span.rb +0 -63
  59. data/lib/datadog/ci/transport/api/ci_test_cycle.rb +0 -30
  60. data/sig/datadog/ci/concurrent_span.rbs +0 -23
  61. data/sig/datadog/ci/configuration/components.rbs +0 -21
  62. data/sig/datadog/ci/configuration/extensions.rbs +0 -9
  63. data/sig/datadog/ci/configuration/settings.rbs +0 -16
  64. data/sig/datadog/ci/contrib/cucumber/configuration/settings.rbs +0 -12
  65. data/sig/datadog/ci/contrib/cucumber/ext.rbs +0 -21
  66. data/sig/datadog/ci/contrib/cucumber/formatter.rbs +0 -48
  67. data/sig/datadog/ci/contrib/cucumber/instrumentation.rbs +0 -16
  68. data/sig/datadog/ci/contrib/cucumber/integration.rbs +0 -26
  69. data/sig/datadog/ci/contrib/cucumber/patcher.rbs +0 -15
  70. data/sig/datadog/ci/contrib/integration.rbs +0 -44
  71. data/sig/datadog/ci/contrib/minitest/configuration/settings.rbs +0 -12
  72. data/sig/datadog/ci/contrib/minitest/ext.rbs +0 -19
  73. data/sig/datadog/ci/contrib/minitest/helpers.rbs +0 -13
  74. data/sig/datadog/ci/contrib/minitest/hooks.rbs +0 -27
  75. data/sig/datadog/ci/contrib/minitest/integration.rbs +0 -26
  76. data/sig/datadog/ci/contrib/minitest/patcher.rbs +0 -15
  77. data/sig/datadog/ci/contrib/minitest/plugin.rbs +0 -31
  78. data/sig/datadog/ci/contrib/minitest/runnable.rbs +0 -24
  79. data/sig/datadog/ci/contrib/rspec/configuration/settings.rbs +0 -12
  80. data/sig/datadog/ci/contrib/rspec/example.rbs +0 -20
  81. data/sig/datadog/ci/contrib/rspec/example_group.rbs +0 -21
  82. data/sig/datadog/ci/contrib/rspec/ext.rbs +0 -17
  83. data/sig/datadog/ci/contrib/rspec/integration.rbs +0 -26
  84. data/sig/datadog/ci/contrib/rspec/patcher.rbs +0 -15
  85. data/sig/datadog/ci/contrib/rspec/runner.rbs +0 -21
  86. data/sig/datadog/ci/contrib/settings.rbs +0 -25
  87. data/sig/datadog/ci/ext/app_types.rbs +0 -14
  88. data/sig/datadog/ci/ext/environment/extractor.rbs +0 -25
  89. data/sig/datadog/ci/ext/environment/providers/appveyor.rbs +0 -48
  90. data/sig/datadog/ci/ext/environment/providers/aws_code_pipeline.rbs +0 -19
  91. data/sig/datadog/ci/ext/environment/providers/azure.rbs +0 -56
  92. data/sig/datadog/ci/ext/environment/providers/base.rbs +0 -71
  93. data/sig/datadog/ci/ext/environment/providers/bitbucket.rbs +0 -37
  94. data/sig/datadog/ci/ext/environment/providers/bitrise.rbs +0 -41
  95. data/sig/datadog/ci/ext/environment/providers/buddy.rbs +0 -37
  96. data/sig/datadog/ci/ext/environment/providers/buildkite.rbs +0 -45
  97. data/sig/datadog/ci/ext/environment/providers/circleci.rbs +0 -41
  98. data/sig/datadog/ci/ext/environment/providers/codefresh.rbs +0 -25
  99. data/sig/datadog/ci/ext/environment/providers/github_actions.rbs +0 -42
  100. data/sig/datadog/ci/ext/environment/providers/gitlab.rbs +0 -57
  101. data/sig/datadog/ci/ext/environment/providers/jenkins.rbs +0 -35
  102. data/sig/datadog/ci/ext/environment/providers/local_git.rbs +0 -66
  103. data/sig/datadog/ci/ext/environment/providers/teamcity.rbs +0 -17
  104. data/sig/datadog/ci/ext/environment/providers/travis.rbs +0 -35
  105. data/sig/datadog/ci/ext/environment/providers/user_defined_tags.rbs +0 -33
  106. data/sig/datadog/ci/ext/environment/providers.rbs +0 -13
  107. data/sig/datadog/ci/ext/environment.rbs +0 -44
  108. data/sig/datadog/ci/ext/git.rbs +0 -53
  109. data/sig/datadog/ci/ext/settings.rbs +0 -14
  110. data/sig/datadog/ci/ext/test.rbs +0 -60
  111. data/sig/datadog/ci/ext/transport.rbs +0 -29
  112. data/sig/datadog/ci/null_span.rbs +0 -37
  113. data/sig/datadog/ci/span.rbs +0 -47
  114. data/sig/datadog/ci/test.rbs +0 -12
  115. data/sig/datadog/ci/test_module.rbs +0 -6
  116. data/sig/datadog/ci/test_session.rbs +0 -9
  117. data/sig/datadog/ci/test_suite.rbs +0 -6
  118. data/sig/datadog/ci/test_visibility/context/global.rbs +0 -39
  119. data/sig/datadog/ci/test_visibility/context/local.rbs +0 -23
  120. data/sig/datadog/ci/test_visibility/flush.rbs +0 -17
  121. data/sig/datadog/ci/test_visibility/null_recorder.rbs +0 -45
  122. data/sig/datadog/ci/test_visibility/recorder.rbs +0 -85
  123. data/sig/datadog/ci/test_visibility/serializers/base.rbs +0 -94
  124. data/sig/datadog/ci/test_visibility/serializers/factories/test_level.rbs +0 -13
  125. data/sig/datadog/ci/test_visibility/serializers/factories/test_suite_level.rbs +0 -13
  126. data/sig/datadog/ci/test_visibility/serializers/span.rbs +0 -18
  127. data/sig/datadog/ci/test_visibility/serializers/test_module.rbs +0 -26
  128. data/sig/datadog/ci/test_visibility/serializers/test_session.rbs +0 -26
  129. data/sig/datadog/ci/test_visibility/serializers/test_suite.rbs +0 -26
  130. data/sig/datadog/ci/test_visibility/serializers/test_v1.rbs +0 -23
  131. data/sig/datadog/ci/test_visibility/serializers/test_v2.rbs +0 -25
  132. data/sig/datadog/ci/test_visibility/transport.rbs +0 -35
  133. data/sig/datadog/ci/transport/api/base.rbs +0 -21
  134. data/sig/datadog/ci/transport/api/builder.rbs +0 -12
  135. data/sig/datadog/ci/transport/api/ci_test_cycle.rbs +0 -21
  136. data/sig/datadog/ci/transport/api/evp_proxy.rbs +0 -19
  137. data/sig/datadog/ci/transport/gzip.rbs +0 -9
  138. data/sig/datadog/ci/transport/http.rbs +0 -36
  139. data/sig/datadog/ci/utils/git.rbs +0 -11
  140. data/sig/datadog/ci/utils/test_run.rbs +0 -11
  141. data/sig/datadog/ci/utils/url.rbs +0 -9
  142. data/sig/datadog/ci/version.rbs +0 -16
  143. data/sig/datadog/ci.rbs +0 -37
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "open3"
4
-
5
3
  require_relative "base"
6
- require_relative "../../git"
4
+ require_relative "../../../utils/git"
7
5
 
8
6
  module Datadog
9
7
  module CI
@@ -13,7 +11,7 @@ module Datadog
13
11
  # As a fallback we try to fetch git information from the local git repository
14
12
  class LocalGit < Base
15
13
  def git_repository_url
16
- exec_git_command("git ls-remote --get-url")
14
+ Utils::Git.exec_git_command("git ls-remote --get-url")
17
15
  rescue => e
18
16
  Datadog.logger.debug(
19
17
  "Unable to read git repository url: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
@@ -22,7 +20,7 @@ module Datadog
22
20
  end
23
21
 
24
22
  def git_commit_sha
25
- exec_git_command("git rev-parse HEAD")
23
+ Utils::Git.exec_git_command("git rev-parse HEAD")
26
24
  rescue => e
27
25
  Datadog.logger.debug(
28
26
  "Unable to read git commit SHA: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
@@ -31,7 +29,7 @@ module Datadog
31
29
  end
32
30
 
33
31
  def git_branch
34
- exec_git_command("git rev-parse --abbrev-ref HEAD")
32
+ Utils::Git.exec_git_command("git rev-parse --abbrev-ref HEAD")
35
33
  rescue => e
36
34
  Datadog.logger.debug(
37
35
  "Unable to read git branch: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
@@ -40,7 +38,7 @@ module Datadog
40
38
  end
41
39
 
42
40
  def git_tag
43
- exec_git_command("git tag --points-at HEAD")
41
+ Utils::Git.exec_git_command("git tag --points-at HEAD")
44
42
  rescue => e
45
43
  Datadog.logger.debug(
46
44
  "Unable to read git tag: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
@@ -49,7 +47,7 @@ module Datadog
49
47
  end
50
48
 
51
49
  def git_commit_message
52
- exec_git_command("git show -s --format=%s")
50
+ Utils::Git.exec_git_command("git show -s --format=%s")
53
51
  rescue => e
54
52
  Datadog.logger.debug(
55
53
  "Unable to read git commit message: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
@@ -82,7 +80,7 @@ module Datadog
82
80
  end
83
81
 
84
82
  def workspace_path
85
- exec_git_command("git rev-parse --show-toplevel")
83
+ Utils::Git.exec_git_command("git rev-parse --show-toplevel")
86
84
  rescue => e
87
85
  Datadog.logger.debug(
88
86
  "Unable to read git base directory: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
@@ -92,25 +90,6 @@ module Datadog
92
90
 
93
91
  private
94
92
 
95
- def exec_git_command(cmd)
96
- out, status = Open3.capture2e(cmd)
97
-
98
- raise "Failed to run git command #{cmd}: #{out}" unless status.success?
99
-
100
- # Sometimes Encoding.default_external is somehow set to US-ASCII which breaks
101
- # commit messages with UTF-8 characters like emojis
102
- # We force output's encoding to be UTF-8 in this case
103
- # This is safe to do as UTF-8 is compatible with US-ASCII
104
- if Encoding.default_external == Encoding::US_ASCII
105
- out = out.force_encoding(Encoding::UTF_8)
106
- end
107
- out.strip! # There's always a "\n" at the end of the command output
108
-
109
- return nil if out.empty?
110
-
111
- out
112
- end
113
-
114
93
  def author
115
94
  return @author if defined?(@author)
116
95
 
@@ -127,7 +106,7 @@ module Datadog
127
106
 
128
107
  def set_git_commit_users
129
108
  # Get committer and author information in one command.
130
- output = exec_git_command("git show -s --format='%an\t%ae\t%at\t%cn\t%ce\t%ct'")
109
+ output = Utils::Git.exec_git_command("git show -s --format='%an\t%ae\t%at\t%cn\t%ce\t%ct'")
131
110
  unless output
132
111
  Datadog.logger.debug(
133
112
  "Unable to read git commit users: git command output is nil"
@@ -9,6 +9,8 @@ module Datadog
9
9
  ENV_AGENTLESS_MODE_ENABLED = "DD_CIVISIBILITY_AGENTLESS_ENABLED"
10
10
  ENV_AGENTLESS_URL = "DD_CIVISIBILITY_AGENTLESS_URL"
11
11
  ENV_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED = "DD_CIVISIBILITY_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED"
12
+ ENV_FORCE_TEST_LEVEL_VISIBILITY = "DD_CIVISIBILITY_FORCE_TEST_LEVEL_VISIBILITY"
13
+ ENV_ITR_ENABLED = "DD_CIVISIBILITY_ITR_ENABLED"
12
14
 
13
15
  # Source: https://docs.datadoghq.com/getting_started/site/
14
16
  DD_SITE_ALLOWLIST = [
@@ -12,24 +12,33 @@ module Datadog
12
12
  TAG_FRAMEWORK = "test.framework"
13
13
  TAG_FRAMEWORK_VERSION = "test.framework_version"
14
14
  TAG_NAME = "test.name"
15
- TAG_SKIP_REASON = "test.skip_reason" # DEV: Not populated yet
15
+ TAG_SKIP_REASON = "test.skip_reason"
16
16
  TAG_STATUS = "test.status"
17
17
  TAG_SUITE = "test.suite"
18
18
  TAG_MODULE = "test.module"
19
- TAG_TRAITS = "test.traits"
20
19
  TAG_TYPE = "test.type"
21
20
  TAG_COMMAND = "test.command"
21
+ TAG_SOURCE_FILE = "test.source.file"
22
+ TAG_SOURCE_START = "test.source.start"
23
+ TAG_CODEOWNERS = "test.codeowners"
24
+ TAG_PARAMETERS = "test.parameters"
22
25
 
23
- TEST_TYPE = "test"
26
+ # ITR tags
27
+ TAG_ITR_TEST_SKIPPING_ENABLED = "test.itr.tests_skipping.enabled"
28
+ TAG_ITR_TEST_SKIPPING_TYPE = "test.itr.tests_skipping.type"
24
29
 
25
- # those tags are special and they are used to correlate tests with the test sessions, suites, and modules
30
+ # Code coverage tags
31
+ TAG_CODE_COVERAGE_ENABLED = "test.code_coverage.enabled"
32
+
33
+ # those tags are special and used to correlate tests with the test sessions, suites, and modules
34
+ # they are transient and not sent to the backend
26
35
  TAG_TEST_SESSION_ID = "_test.session_id"
27
36
  TAG_TEST_MODULE_ID = "_test.module_id"
28
37
  TAG_TEST_SUITE_ID = "_test.suite_id"
29
- SPECIAL_TAGS = [TAG_TEST_SESSION_ID, TAG_TEST_MODULE_ID, TAG_TEST_SUITE_ID].freeze
38
+ TRANSIENT_TAGS = [TAG_TEST_SESSION_ID, TAG_TEST_MODULE_ID, TAG_TEST_SUITE_ID].freeze
30
39
 
31
- # tags that can be inherited from the test session
32
- INHERITABLE_TAGS = [TAG_FRAMEWORK, TAG_FRAMEWORK_VERSION, TAG_TYPE].freeze
40
+ # tags that are common for the whole session and can be inherited from the test session
41
+ INHERITABLE_TAGS = [TAG_FRAMEWORK, TAG_FRAMEWORK_VERSION].freeze
33
42
 
34
43
  # Environment runtime tags
35
44
  TAG_OS_ARCHITECTURE = "os.architecture"
@@ -37,13 +46,26 @@ module Datadog
37
46
  TAG_RUNTIME_NAME = "runtime.name"
38
47
  TAG_RUNTIME_VERSION = "runtime.version"
39
48
 
49
+ # internal APM tag to mark a span as a test span
40
50
  TAG_SPAN_KIND = "span.kind"
51
+ SPAN_KIND_TEST = "test"
52
+
53
+ # could be either "test" or "suite" depending on whether we skip individual tests or whole suites
54
+ # we use test skipping for Ruby
55
+ ITR_TEST_SKIPPING_MODE = "test"
41
56
 
57
+ # test status as recognized by Datadog
42
58
  module Status
43
59
  PASS = "pass"
44
60
  FAIL = "fail"
45
61
  SKIP = "skip"
46
62
  end
63
+
64
+ # test types (e.g. test, benchmark, browser)
65
+ module Type
66
+ TEST = "test"
67
+ BENCHMARK = "benchmark" # DEV: not used yet, will be used when benchmarks are supported
68
+ end
47
69
  end
48
70
  end
49
71
  end
@@ -12,11 +12,29 @@ module Datadog
12
12
  HEADER_EVP_SUBDOMAIN = "X-Datadog-EVP-Subdomain"
13
13
  HEADER_CONTAINER_ID = "Datadog-Container-ID"
14
14
 
15
- EVP_PROXY_PATH_PREFIX = "/evp_proxy/v2/"
15
+ EVP_PROXY_V2_PATH_PREFIX = "/evp_proxy/v2/"
16
+ EVP_PROXY_V4_PATH_PREFIX = "/evp_proxy/v4/"
17
+ EVP_PROXY_PATH_PREFIXES = [EVP_PROXY_V4_PATH_PREFIX, EVP_PROXY_V2_PATH_PREFIX].freeze
18
+ EVP_PROXY_COMPRESSION_SUPPORTED = {
19
+ EVP_PROXY_V4_PATH_PREFIX => true,
20
+ EVP_PROXY_V2_PATH_PREFIX => false
21
+ }
22
+
16
23
  TEST_VISIBILITY_INTAKE_HOST_PREFIX = "citestcycle-intake"
17
24
  TEST_VISIBILITY_INTAKE_PATH = "/api/v2/citestcycle"
18
25
 
26
+ DD_API_HOST_PREFIX = "api"
27
+ DD_API_SETTINGS_PATH = "/api/v2/libraries/tests/services/setting"
28
+ DD_API_SETTINGS_TYPE = "ci_app_test_service_libraries_settings"
29
+ DD_API_SETTINGS_RESPONSE_DIG_KEYS = %w[data attributes].freeze
30
+ DD_API_SETTINGS_RESPONSE_ITR_ENABLED_KEY = "itr_enabled"
31
+ DD_API_SETTINGS_RESPONSE_CODE_COVERAGE_KEY = "code_coverage"
32
+ DD_API_SETTINGS_RESPONSE_TESTS_SKIPPING_KEY = "tests_skipping"
33
+ DD_API_SETTINGS_RESPONSE_REQUIRE_GIT_KEY = "require_git"
34
+ DD_API_SETTINGS_RESPONSE_DEFAULT = {DD_API_SETTINGS_RESPONSE_ITR_ENABLED_KEY => false}.freeze
35
+
19
36
  CONTENT_TYPE_MESSAGEPACK = "application/msgpack"
37
+ CONTENT_TYPE_JSON = "application/json"
20
38
  CONTENT_ENCODING_GZIP = "gzip"
21
39
  end
22
40
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../ext/test"
4
+ require_relative "../ext/transport"
5
+
6
+ module Datadog
7
+ module CI
8
+ module ITR
9
+ # Intelligent test runner implementation
10
+ # Integrates with backend to provide test impact analysis data and
11
+ # skip tests that are not impacted by the changes
12
+ class Runner
13
+ def initialize(
14
+ enabled: false
15
+ )
16
+ @enabled = enabled
17
+ @test_skipping_enabled = false
18
+ @code_coverage_enabled = false
19
+
20
+ Datadog.logger.debug("ITR Runner initialized with enabled: #{@enabled}")
21
+ end
22
+
23
+ def configure(remote_configuration, test_session)
24
+ Datadog.logger.debug("Configuring ITR Runner with remote configuration: #{remote_configuration}")
25
+
26
+ @enabled = convert_to_bool(
27
+ remote_configuration.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_ITR_ENABLED_KEY, false)
28
+ )
29
+ @test_skipping_enabled = @enabled && convert_to_bool(
30
+ remote_configuration.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_TESTS_SKIPPING_KEY, false)
31
+ )
32
+ @code_coverage_enabled = @enabled && convert_to_bool(
33
+ remote_configuration.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_CODE_COVERAGE_KEY, false)
34
+ )
35
+
36
+ test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_ENABLED, @test_skipping_enabled)
37
+ # currently we set this tag when ITR requires collecting code coverage
38
+ # this will change as soon as we implement total code coverage support in this library
39
+ test_session.set_tag(Ext::Test::TAG_CODE_COVERAGE_ENABLED, @code_coverage_enabled)
40
+
41
+ # we skip tests, not suites
42
+ test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_TYPE, Ext::Test::ITR_TEST_SKIPPING_MODE)
43
+
44
+ Datadog.logger.debug("Configured ITR Runner with enabled: #{@enabled}, skipping_tests: #{@test_skipping_enabled}, code_coverage: #{@code_coverage_enabled}")
45
+ end
46
+
47
+ def enabled?
48
+ @enabled
49
+ end
50
+
51
+ def skipping_tests?
52
+ @test_skipping_enabled
53
+ end
54
+
55
+ def code_coverage?
56
+ @code_coverage_enabled
57
+ end
58
+
59
+ private
60
+
61
+ def convert_to_bool(value)
62
+ value.to_s == "true"
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -32,7 +32,7 @@ module Datadog
32
32
  end
33
33
 
34
34
  # @return [String] the type of the span (for example "test" or type that was provided to [Datadog::CI.trace]).
35
- def span_type
35
+ def type
36
36
  tracer_span.type
37
37
  end
38
38
 
@@ -121,6 +121,48 @@ module Datadog
121
121
  tracer_span.set_tags(tags)
122
122
  end
123
123
 
124
+ # Returns the git repository URL extracted from the environment.
125
+ # @return [String] the repository URL.
126
+ def git_repository_url
127
+ tracer_span.get_tag(Ext::Git::TAG_REPOSITORY_URL)
128
+ end
129
+
130
+ # Returns the latest commit SHA extracted from the environment.
131
+ # @return [String] the commit SHA of the last commit.
132
+ def git_commit_sha
133
+ tracer_span.get_tag(Ext::Git::TAG_COMMIT_SHA)
134
+ end
135
+
136
+ # Returns the git branch name extracted from the environment.
137
+ # @return [String] the branch.
138
+ def git_branch
139
+ tracer_span.get_tag(Ext::Git::TAG_BRANCH)
140
+ end
141
+
142
+ # Returns the OS architecture extracted from the environment.
143
+ # @return [String] OS arch.
144
+ def os_architecture
145
+ tracer_span.get_tag(Ext::Test::TAG_OS_ARCHITECTURE)
146
+ end
147
+
148
+ # Returns the OS platform extracted from the environment.
149
+ # @return [String] OS platform.
150
+ def os_platform
151
+ tracer_span.get_tag(Ext::Test::TAG_OS_PLATFORM)
152
+ end
153
+
154
+ # Returns the runtime name extracted from the environment.
155
+ # @return [String] runtime name.
156
+ def runtime_name
157
+ tracer_span.get_tag(Ext::Test::TAG_RUNTIME_NAME)
158
+ end
159
+
160
+ # Returns the runtime version extracted from the environment.
161
+ # @return [String] runtime version.
162
+ def runtime_version
163
+ tracer_span.get_tag(Ext::Test::TAG_RUNTIME_VERSION)
164
+ end
165
+
124
166
  def set_environment_runtime_tags
125
167
  tracer_span.set_tag(Ext::Test::TAG_OS_ARCHITECTURE, ::RbConfig::CONFIG["host_cpu"])
126
168
  tracer_span.set_tag(Ext::Test::TAG_OS_PLATFORM, ::RbConfig::CONFIG["host_os"])
@@ -130,12 +172,19 @@ module Datadog
130
172
  end
131
173
 
132
174
  def set_default_tags
133
- tracer_span.set_tag(Ext::Test::TAG_SPAN_KIND, Ext::AppTypes::TYPE_TEST)
175
+ tracer_span.set_tag(Ext::Test::TAG_SPAN_KIND, Ext::Test::SPAN_KIND_TEST)
134
176
  end
135
177
 
136
178
  def to_s
137
179
  "#{self.class}(name:#{name},tracer_span:#{@tracer_span})"
138
180
  end
181
+
182
+ private
183
+
184
+ # provides access to global CI recorder for CI models to deactivate themselves
185
+ def recorder
186
+ Datadog.send(:components).ci_recorder
187
+ end
139
188
  end
140
189
  end
141
190
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "json"
4
+
3
5
  require_relative "span"
4
6
 
5
7
  module Datadog
@@ -18,7 +20,7 @@ module Datadog
18
20
  def finish
19
21
  super
20
22
 
21
- CI.deactivate_test(self)
23
+ recorder.deactivate_test
22
24
  end
23
25
 
24
26
  # Running test suite that this test is part of (if any).
@@ -35,6 +37,8 @@ module Datadog
35
37
  get_tag(Ext::Test::TAG_TEST_SUITE_ID)
36
38
  end
37
39
 
40
+ # Name of the running test suite this test belongs to.
41
+ # @return [String] the name of the test suite.
38
42
  def test_suite_name
39
43
  get_tag(Ext::Test::TAG_SUITE)
40
44
  end
@@ -45,11 +49,72 @@ module Datadog
45
49
  get_tag(Ext::Test::TAG_TEST_MODULE_ID)
46
50
  end
47
51
 
48
- # Span id of the running test module this test belongs to.
52
+ # Span id of the running test session this test belongs to.
49
53
  # @return [String] the span id of the test session.
50
54
  def test_session_id
51
55
  get_tag(Ext::Test::TAG_TEST_SESSION_ID)
52
56
  end
57
+
58
+ # Source file path of the test relative to git repository root.
59
+ # @return [String] the source file path of the test
60
+ # @return [nil] if the source file path is not found
61
+ def source_file
62
+ get_tag(Ext::Test::TAG_SOURCE_FILE)
63
+ end
64
+
65
+ # Sets the status of the span to "pass".
66
+ # @return [void]
67
+ def passed!
68
+ super
69
+
70
+ record_test_result(Ext::Test::Status::PASS)
71
+ end
72
+
73
+ # Sets the status of the span to "fail".
74
+ # @param [Exception] exception the exception that caused the test to fail.
75
+ # @return [void]
76
+ def failed!(exception: nil)
77
+ super
78
+
79
+ record_test_result(Ext::Test::Status::FAIL)
80
+ end
81
+
82
+ # Sets the status of the span to "skip".
83
+ # @param [Exception] exception the exception that caused the test to fail.
84
+ # @param [String] reason the reason why the test was skipped.
85
+ # @return [void]
86
+ def skipped!(exception: nil, reason: nil)
87
+ super
88
+
89
+ record_test_result(Ext::Test::Status::SKIP)
90
+ end
91
+
92
+ # Sets the parameters for this test (e.g. Cucumber example or RSpec shared specs).
93
+ # Parameters are needed to compute test fingerprint to distinguish between different tests having same names.
94
+ #
95
+ # @param [Hash] arguments the arguments that test accepts as key-value hash
96
+ # @param [Hash] metadata optional metadata
97
+ # @return [void]
98
+ def set_parameters(arguments, metadata = {})
99
+ return if arguments.nil?
100
+
101
+ set_tag(
102
+ Ext::Test::TAG_PARAMETERS,
103
+ JSON.generate(
104
+ {
105
+ arguments: arguments,
106
+ metadata: metadata
107
+ }
108
+ )
109
+ )
110
+ end
111
+
112
+ private
113
+
114
+ def record_test_result(datadog_status)
115
+ suite = test_suite
116
+ suite.record_test_result(datadog_status) if suite
117
+ end
53
118
  end
54
119
  end
55
120
  end
@@ -16,7 +16,7 @@ module Datadog
16
16
  def finish
17
17
  super
18
18
 
19
- CI.deactivate_test_module
19
+ recorder.deactivate_test_module
20
20
  end
21
21
  end
22
22
  end
@@ -17,7 +17,7 @@ module Datadog
17
17
  def finish
18
18
  super
19
19
 
20
- CI.deactivate_test_session
20
+ recorder.deactivate_test_session
21
21
  end
22
22
 
23
23
  # Return the test session's name which is equal to test command used
@@ -26,13 +26,21 @@ module Datadog
26
26
  get_tag(Ext::Test::TAG_COMMAND)
27
27
  end
28
28
 
29
+ def skipping_tests?
30
+ get_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_ENABLED) == "true"
31
+ end
32
+
33
+ def code_coverage?
34
+ get_tag(Ext::Test::TAG_CODE_COVERAGE_ENABLED) == "true"
35
+ end
36
+
29
37
  # Return the test session tags that could be inherited by sub-spans
30
38
  # @return [Hash] the tags to be inherited by sub-spans.
31
39
  def inheritable_tags
32
40
  return @inheritable_tags if defined?(@inheritable_tags)
33
41
 
34
42
  # this method is not synchronized because it does not iterate over the tags collection, but rather
35
- # uses synchronized method to get each tag value
43
+ # uses synchronized method #get_tag to get each tag value
36
44
  res = {}
37
45
  Ext::Test::INHERITABLE_TAGS.each do |tag|
38
46
  res[tag] = get_tag(tag)
@@ -13,12 +13,63 @@ module Datadog
13
13
  #
14
14
  # @public_api
15
15
  class TestSuite < ConcurrentSpan
16
+ def initialize(tracer_span)
17
+ super
18
+
19
+ @test_suite_stats = Hash.new(0)
20
+ end
21
+
16
22
  # Finishes this test suite.
17
23
  # @return [void]
18
24
  def finish
19
- super
25
+ synchronize do
26
+ # we try to derive test suite status from execution stats if no status was set explicitly
27
+ set_status_from_stats! if undefined?
28
+
29
+ super
30
+
31
+ recorder.deactivate_test_suite(name)
32
+ end
33
+ end
34
+
35
+ # @internal
36
+ def record_test_result(datadog_test_status)
37
+ synchronize do
38
+ @test_suite_stats[datadog_test_status] += 1
39
+ end
40
+ end
41
+
42
+ # @internal
43
+ def passed_tests_count
44
+ synchronize do
45
+ @test_suite_stats[Ext::Test::Status::PASS]
46
+ end
47
+ end
48
+
49
+ # @internal
50
+ def skipped_tests_count
51
+ synchronize do
52
+ @test_suite_stats[Ext::Test::Status::SKIP]
53
+ end
54
+ end
55
+
56
+ # @internal
57
+ def failed_tests_count
58
+ synchronize do
59
+ @test_suite_stats[Ext::Test::Status::FAIL]
60
+ end
61
+ end
62
+
63
+ private
20
64
 
21
- CI.deactivate_test_suite(name)
65
+ def set_status_from_stats!
66
+ if failed_tests_count > 0
67
+ failed!
68
+ elsif passed_tests_count == 0
69
+ skipped!
70
+ else
71
+ passed!
72
+ end
22
73
  end
23
74
  end
24
75
  end
@@ -11,7 +11,7 @@ module Datadog
11
11
  self.active_test = nil
12
12
  end
13
13
 
14
- def activate_test!(test)
14
+ def activate_test(test)
15
15
  raise "Nested tests are not supported. Currently active test: #{active_test}" unless active_test.nil?
16
16
 
17
17
  if block_given?
@@ -26,14 +26,8 @@ module Datadog
26
26
  end
27
27
  end
28
28
 
29
- def deactivate_test!(test)
30
- return if active_test.nil?
31
-
32
- if active_test == test
33
- self.active_test = nil
34
- else
35
- raise "Trying to deactivate test #{test}, but currently active test is #{active_test}"
36
- end
29
+ def deactivate_test
30
+ self.active_test = nil
37
31
  end
38
32
 
39
33
  def active_test
@@ -23,7 +23,7 @@ module Datadog
23
23
  skip_tracing(block)
24
24
  end
25
25
 
26
- def trace(span_type, span_name, tags: {}, &block)
26
+ def trace(type, span_name, tags: {}, &block)
27
27
  skip_tracing(block)
28
28
  end
29
29
 
@@ -42,30 +42,10 @@ module Datadog
42
42
  def active_test_suite(test_suite_name)
43
43
  end
44
44
 
45
- def deactivate_test(test)
46
- end
47
-
48
- def deactivate_test_session
49
- end
50
-
51
- def deactivate_test_module
52
- end
53
-
54
- def deactivate_test_suite(test_suite_name)
55
- end
56
-
57
45
  private
58
46
 
59
47
  def skip_tracing(block = nil)
60
- if block
61
- block.call(null_span)
62
- else
63
- null_span
64
- end
65
- end
66
-
67
- def null_span
68
- @null_span ||= NullSpan.new
48
+ block.call(nil) if block
69
49
  end
70
50
  end
71
51
  end