datadog-ci 0.5.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -1
  3. data/README.md +1 -1
  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 +15 -11
  9. data/lib/datadog/ci/configuration/settings.rb +15 -0
  10. data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +4 -1
  11. data/lib/datadog/ci/contrib/cucumber/ext.rb +7 -5
  12. data/lib/datadog/ci/contrib/cucumber/formatter.rb +138 -27
  13. data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +2 -1
  14. data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +4 -1
  15. data/lib/datadog/ci/contrib/minitest/ext.rb +6 -4
  16. data/lib/datadog/ci/contrib/minitest/helpers.rb +23 -0
  17. data/lib/datadog/ci/contrib/minitest/hooks.rb +34 -19
  18. data/lib/datadog/ci/contrib/minitest/patcher.rb +10 -0
  19. data/lib/datadog/ci/contrib/minitest/reporter.rb +50 -0
  20. data/lib/datadog/ci/contrib/minitest/runnable.rb +42 -0
  21. data/lib/datadog/ci/contrib/minitest/runner.rb +41 -0
  22. data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +4 -1
  23. data/lib/datadog/ci/contrib/rspec/example.rb +55 -14
  24. data/lib/datadog/ci/contrib/rspec/example_group.rb +51 -0
  25. data/lib/datadog/ci/contrib/rspec/ext.rb +5 -4
  26. data/lib/datadog/ci/contrib/rspec/integration.rb +1 -1
  27. data/lib/datadog/ci/contrib/rspec/patcher.rb +14 -0
  28. data/lib/datadog/ci/contrib/rspec/runner.rb +57 -0
  29. data/lib/datadog/ci/contrib/settings.rb +1 -1
  30. data/lib/datadog/ci/ext/app_types.rb +2 -0
  31. data/lib/datadog/ci/ext/environment/providers/local_git.rb +8 -29
  32. data/lib/datadog/ci/ext/settings.rb +1 -0
  33. data/lib/datadog/ci/ext/test.rb +19 -6
  34. data/lib/datadog/ci/span.rb +33 -2
  35. data/lib/datadog/ci/test.rb +96 -1
  36. data/lib/datadog/ci/test_module.rb +1 -1
  37. data/lib/datadog/ci/test_session.rb +10 -2
  38. data/lib/datadog/ci/test_suite.rb +53 -2
  39. data/lib/datadog/ci/test_visibility/context/local.rb +3 -9
  40. data/lib/datadog/ci/test_visibility/null_recorder.rb +53 -0
  41. data/lib/datadog/ci/test_visibility/recorder.rb +52 -25
  42. data/lib/datadog/ci/test_visibility/serializers/base.rb +6 -5
  43. data/lib/datadog/ci/test_visibility/serializers/span.rb +1 -1
  44. data/lib/datadog/ci/test_visibility/serializers/test_module.rb +1 -1
  45. data/lib/datadog/ci/test_visibility/serializers/test_session.rb +1 -1
  46. data/lib/datadog/ci/test_visibility/serializers/test_suite.rb +1 -1
  47. data/lib/datadog/ci/test_visibility/serializers/test_v1.rb +1 -1
  48. data/lib/datadog/ci/utils/configuration.rb +15 -0
  49. data/lib/datadog/ci/utils/git.rb +70 -0
  50. data/lib/datadog/ci/version.rb +2 -2
  51. data/lib/datadog/ci.rb +40 -56
  52. metadata +14 -82
  53. data/lib/datadog/ci/null_span.rb +0 -63
  54. data/sig/datadog/ci/concurrent_span.rbs +0 -23
  55. data/sig/datadog/ci/configuration/components.rbs +0 -21
  56. data/sig/datadog/ci/configuration/extensions.rbs +0 -9
  57. data/sig/datadog/ci/configuration/settings.rbs +0 -16
  58. data/sig/datadog/ci/contrib/cucumber/configuration/settings.rbs +0 -12
  59. data/sig/datadog/ci/contrib/cucumber/ext.rbs +0 -25
  60. data/sig/datadog/ci/contrib/cucumber/formatter.rbs +0 -35
  61. data/sig/datadog/ci/contrib/cucumber/instrumentation.rbs +0 -16
  62. data/sig/datadog/ci/contrib/cucumber/integration.rbs +0 -26
  63. data/sig/datadog/ci/contrib/cucumber/patcher.rbs +0 -15
  64. data/sig/datadog/ci/contrib/integration.rbs +0 -44
  65. data/sig/datadog/ci/contrib/minitest/configuration/settings.rbs +0 -12
  66. data/sig/datadog/ci/contrib/minitest/ext.rbs +0 -23
  67. data/sig/datadog/ci/contrib/minitest/hooks.rbs +0 -19
  68. data/sig/datadog/ci/contrib/minitest/integration.rbs +0 -26
  69. data/sig/datadog/ci/contrib/minitest/patcher.rbs +0 -15
  70. data/sig/datadog/ci/contrib/rspec/configuration/settings.rbs +0 -12
  71. data/sig/datadog/ci/contrib/rspec/example.rbs +0 -20
  72. data/sig/datadog/ci/contrib/rspec/ext.rbs +0 -23
  73. data/sig/datadog/ci/contrib/rspec/integration.rbs +0 -26
  74. data/sig/datadog/ci/contrib/rspec/patcher.rbs +0 -15
  75. data/sig/datadog/ci/contrib/settings.rbs +0 -25
  76. data/sig/datadog/ci/ext/app_types.rbs +0 -14
  77. data/sig/datadog/ci/ext/environment/extractor.rbs +0 -25
  78. data/sig/datadog/ci/ext/environment/providers/appveyor.rbs +0 -48
  79. data/sig/datadog/ci/ext/environment/providers/aws_code_pipeline.rbs +0 -19
  80. data/sig/datadog/ci/ext/environment/providers/azure.rbs +0 -56
  81. data/sig/datadog/ci/ext/environment/providers/base.rbs +0 -71
  82. data/sig/datadog/ci/ext/environment/providers/bitbucket.rbs +0 -37
  83. data/sig/datadog/ci/ext/environment/providers/bitrise.rbs +0 -41
  84. data/sig/datadog/ci/ext/environment/providers/buddy.rbs +0 -37
  85. data/sig/datadog/ci/ext/environment/providers/buildkite.rbs +0 -45
  86. data/sig/datadog/ci/ext/environment/providers/circleci.rbs +0 -41
  87. data/sig/datadog/ci/ext/environment/providers/codefresh.rbs +0 -25
  88. data/sig/datadog/ci/ext/environment/providers/github_actions.rbs +0 -42
  89. data/sig/datadog/ci/ext/environment/providers/gitlab.rbs +0 -57
  90. data/sig/datadog/ci/ext/environment/providers/jenkins.rbs +0 -35
  91. data/sig/datadog/ci/ext/environment/providers/local_git.rbs +0 -66
  92. data/sig/datadog/ci/ext/environment/providers/teamcity.rbs +0 -17
  93. data/sig/datadog/ci/ext/environment/providers/travis.rbs +0 -35
  94. data/sig/datadog/ci/ext/environment/providers/user_defined_tags.rbs +0 -33
  95. data/sig/datadog/ci/ext/environment/providers.rbs +0 -13
  96. data/sig/datadog/ci/ext/environment.rbs +0 -44
  97. data/sig/datadog/ci/ext/git.rbs +0 -53
  98. data/sig/datadog/ci/ext/settings.rbs +0 -14
  99. data/sig/datadog/ci/ext/test.rbs +0 -58
  100. data/sig/datadog/ci/ext/transport.rbs +0 -29
  101. data/sig/datadog/ci/null_span.rbs +0 -37
  102. data/sig/datadog/ci/span.rbs +0 -39
  103. data/sig/datadog/ci/test.rbs +0 -7
  104. data/sig/datadog/ci/test_module.rbs +0 -6
  105. data/sig/datadog/ci/test_session.rbs +0 -9
  106. data/sig/datadog/ci/test_suite.rbs +0 -6
  107. data/sig/datadog/ci/test_visibility/context/global.rbs +0 -39
  108. data/sig/datadog/ci/test_visibility/context/local.rbs +0 -23
  109. data/sig/datadog/ci/test_visibility/flush.rbs +0 -17
  110. data/sig/datadog/ci/test_visibility/recorder.rbs +0 -85
  111. data/sig/datadog/ci/test_visibility/serializers/base.rbs +0 -94
  112. data/sig/datadog/ci/test_visibility/serializers/factories/test_level.rbs +0 -13
  113. data/sig/datadog/ci/test_visibility/serializers/factories/test_suite_level.rbs +0 -13
  114. data/sig/datadog/ci/test_visibility/serializers/span.rbs +0 -18
  115. data/sig/datadog/ci/test_visibility/serializers/test_module.rbs +0 -26
  116. data/sig/datadog/ci/test_visibility/serializers/test_session.rbs +0 -26
  117. data/sig/datadog/ci/test_visibility/serializers/test_suite.rbs +0 -26
  118. data/sig/datadog/ci/test_visibility/serializers/test_v1.rbs +0 -23
  119. data/sig/datadog/ci/test_visibility/serializers/test_v2.rbs +0 -25
  120. data/sig/datadog/ci/test_visibility/transport.rbs +0 -35
  121. data/sig/datadog/ci/transport/api/base.rbs +0 -21
  122. data/sig/datadog/ci/transport/api/builder.rbs +0 -12
  123. data/sig/datadog/ci/transport/api/ci_test_cycle.rbs +0 -21
  124. data/sig/datadog/ci/transport/api/evp_proxy.rbs +0 -19
  125. data/sig/datadog/ci/transport/gzip.rbs +0 -9
  126. data/sig/datadog/ci/transport/http.rbs +0 -36
  127. data/sig/datadog/ci/utils/git.rbs +0 -11
  128. data/sig/datadog/ci/utils/test_run.rbs +0 -11
  129. data/sig/datadog/ci/utils/url.rbs +0 -9
  130. data/sig/datadog/ci/version.rbs +0 -16
  131. data/sig/datadog/ci.rbs +0 -35
@@ -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,7 @@ 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"
12
13
 
13
14
  # Source: https://docs.datadoghq.com/getting_started/site/
14
15
  DD_SITE_ALLOWLIST = [
@@ -12,22 +12,26 @@ 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
- # those tags are special and they are used to correlate tests with the test sessions, suites, and modules
26
+ # those tags are special and used to correlate tests with the test sessions, suites, and modules
27
+ # they are transient and not sent to the backend
24
28
  TAG_TEST_SESSION_ID = "_test.session_id"
25
29
  TAG_TEST_MODULE_ID = "_test.module_id"
26
30
  TAG_TEST_SUITE_ID = "_test.suite_id"
27
- SPECIAL_TAGS = [TAG_TEST_SESSION_ID, TAG_TEST_MODULE_ID, TAG_TEST_SUITE_ID].freeze
31
+ TRANSIENT_TAGS = [TAG_TEST_SESSION_ID, TAG_TEST_MODULE_ID, TAG_TEST_SUITE_ID].freeze
28
32
 
29
- # tags that can be inherited from the test session
30
- INHERITABLE_TAGS = [TAG_FRAMEWORK, TAG_FRAMEWORK_VERSION, TAG_TYPE].freeze
33
+ # tags that are common for the whole session and can be inherited from the test session
34
+ INHERITABLE_TAGS = [TAG_FRAMEWORK, TAG_FRAMEWORK_VERSION].freeze
31
35
 
32
36
  # Environment runtime tags
33
37
  TAG_OS_ARCHITECTURE = "os.architecture"
@@ -35,13 +39,22 @@ module Datadog
35
39
  TAG_RUNTIME_NAME = "runtime.name"
36
40
  TAG_RUNTIME_VERSION = "runtime.version"
37
41
 
42
+ # internal APM tag to mark a span as a test span
38
43
  TAG_SPAN_KIND = "span.kind"
44
+ SPAN_KIND_TEST = "test"
39
45
 
46
+ # test status as recognized by Datadog
40
47
  module Status
41
48
  PASS = "pass"
42
49
  FAIL = "fail"
43
50
  SKIP = "skip"
44
51
  end
52
+
53
+ # test types (e.g. test, benchmark, browser)
54
+ module Type
55
+ TEST = "test"
56
+ BENCHMARK = "benchmark" # DEV: not used yet, will be used when benchmarks are supported
57
+ end
45
58
  end
46
59
  end
47
60
  end
@@ -32,10 +32,34 @@ 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
 
39
+ # Checks whether span status is not set yet.
40
+ # @return [bool] true if span does not have status
41
+ def undefined?
42
+ tracer_span.get_tag(Ext::Test::TAG_STATUS).nil?
43
+ end
44
+
45
+ # Checks whether span status is PASS.
46
+ # @return [bool] true if span status is PASS
47
+ def passed?
48
+ tracer_span.get_tag(Ext::Test::TAG_STATUS) == Ext::Test::Status::PASS
49
+ end
50
+
51
+ # Checks whether span status is FAIL.
52
+ # @return [bool] true if span status is FAIL
53
+ def failed?
54
+ tracer_span.get_tag(Ext::Test::TAG_STATUS) == Ext::Test::Status::FAIL
55
+ end
56
+
57
+ # Checks whether span status is SKIP.
58
+ # @return [bool] true if span status is SKIP
59
+ def skipped?
60
+ tracer_span.get_tag(Ext::Test::TAG_STATUS) == Ext::Test::Status::SKIP
61
+ end
62
+
39
63
  # Sets the status of the span to "pass".
40
64
  # @return [void]
41
65
  def passed!
@@ -106,12 +130,19 @@ module Datadog
106
130
  end
107
131
 
108
132
  def set_default_tags
109
- tracer_span.set_tag(Ext::Test::TAG_SPAN_KIND, Ext::AppTypes::TYPE_TEST)
133
+ tracer_span.set_tag(Ext::Test::TAG_SPAN_KIND, Ext::Test::SPAN_KIND_TEST)
110
134
  end
111
135
 
112
136
  def to_s
113
137
  "#{self.class}(name:#{name},tracer_span:#{@tracer_span})"
114
138
  end
139
+
140
+ private
141
+
142
+ # provides access to global CI recorder for CI models to deactivate themselves
143
+ def recorder
144
+ Datadog.send(:components).ci_recorder
145
+ end
115
146
  end
116
147
  end
117
148
  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,100 @@ module Datadog
18
20
  def finish
19
21
  super
20
22
 
21
- CI.deactivate_test(self)
23
+ recorder.deactivate_test
24
+ end
25
+
26
+ # Running test suite that this test is part of (if any).
27
+ # @return [Datadog::CI::TestSuite] the test suite this test belongs to
28
+ # @return [nil] if the test suite is not found
29
+ def test_suite
30
+ suite_name = test_suite_name
31
+ CI.active_test_suite(suite_name) if suite_name
32
+ end
33
+
34
+ # Span id of the running test suite this test belongs to.
35
+ # @return [String] the span id of the test suite.
36
+ def test_suite_id
37
+ get_tag(Ext::Test::TAG_TEST_SUITE_ID)
38
+ end
39
+
40
+ # Name of the running test suite this test belongs to.
41
+ # @return [String] the name of the test suite.
42
+ def test_suite_name
43
+ get_tag(Ext::Test::TAG_SUITE)
44
+ end
45
+
46
+ # Span id of the running test module this test belongs to.
47
+ # @return [String] the span id of the test module.
48
+ def test_module_id
49
+ get_tag(Ext::Test::TAG_TEST_MODULE_ID)
50
+ end
51
+
52
+ # Span id of the running test session this test belongs to.
53
+ # @return [String] the span id of the test session.
54
+ def test_session_id
55
+ get_tag(Ext::Test::TAG_TEST_SESSION_ID)
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
22
117
  end
23
118
  end
24
119
  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,14 +17,22 @@ 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
+ # Return the test session's name which is equal to test command used
24
+ # @return [String] the command for this test session.
25
+ def name
26
+ get_tag(Ext::Test::TAG_COMMAND)
27
+ end
28
+
29
+ # Return the test session tags that could be inherited by sub-spans
30
+ # @return [Hash] the tags to be inherited by sub-spans.
23
31
  def inheritable_tags
24
32
  return @inheritable_tags if defined?(@inheritable_tags)
25
33
 
26
34
  # this method is not synchronized because it does not iterate over the tags collection, but rather
27
- # uses synchronized method to get each tag value
35
+ # uses synchronized method #get_tag to get each tag value
28
36
  res = {}
29
37
  Ext::Test::INHERITABLE_TAGS.each do |tag|
30
38
  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
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "recorder"
4
+
5
+ module Datadog
6
+ module CI
7
+ module TestVisibility
8
+ # Special recorder that does not record anything
9
+ class NullRecorder
10
+ def start_test_session(service: nil, tags: {})
11
+ skip_tracing
12
+ end
13
+
14
+ def start_test_module(test_module_name, service: nil, tags: {})
15
+ skip_tracing
16
+ end
17
+
18
+ def start_test_suite(test_suite_name, service: nil, tags: {})
19
+ skip_tracing
20
+ end
21
+
22
+ def trace_test(test_name, test_suite_name, service: nil, tags: {}, &block)
23
+ skip_tracing(block)
24
+ end
25
+
26
+ def trace(type, span_name, tags: {}, &block)
27
+ skip_tracing(block)
28
+ end
29
+
30
+ def active_span
31
+ end
32
+
33
+ def active_test
34
+ end
35
+
36
+ def active_test_session
37
+ end
38
+
39
+ def active_test_module
40
+ end
41
+
42
+ def active_test_suite(test_suite_name)
43
+ end
44
+
45
+ private
46
+
47
+ def skip_tracing(block = nil)
48
+ block.call(nil) if block
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -8,12 +8,13 @@ require "rbconfig"
8
8
  require_relative "context/global"
9
9
  require_relative "context/local"
10
10
 
11
+ require_relative "../codeowners/parser"
11
12
  require_relative "../ext/app_types"
12
13
  require_relative "../ext/test"
13
14
  require_relative "../ext/environment"
15
+ require_relative "../utils/git"
14
16
 
15
17
  require_relative "../span"
16
- require_relative "../null_span"
17
18
  require_relative "../test"
18
19
  require_relative "../test_session"
19
20
  require_relative "../test_module"
@@ -25,15 +26,18 @@ module Datadog
25
26
  # Common behavior for CI tests
26
27
  # Note: this class has too many responsibilities and should be split into multiple classes
27
28
  class Recorder
28
- attr_reader :environment_tags, :test_suite_level_visibility_enabled, :enabled
29
+ attr_reader :environment_tags, :test_suite_level_visibility_enabled
29
30
 
30
- def initialize(enabled: true, test_suite_level_visibility_enabled: false)
31
- @enabled = enabled
32
- @test_suite_level_visibility_enabled = enabled && test_suite_level_visibility_enabled
31
+ def initialize(
32
+ test_suite_level_visibility_enabled: false,
33
+ codeowners: Codeowners::Parser.new(Utils::Git.root).parse
34
+ )
35
+ @test_suite_level_visibility_enabled = test_suite_level_visibility_enabled
33
36
 
34
- @environment_tags = @enabled ? Ext::Environment.tags(ENV).freeze : {}
37
+ @environment_tags = Ext::Environment.tags(ENV).freeze
35
38
  @local_context = Context::Local.new
36
39
  @global_context = Context::Global.new
40
+ @codeowners = codeowners
37
41
  end
38
42
 
39
43
  def start_test_session(service: nil, tags: {})
@@ -83,14 +87,13 @@ module Datadog
83
87
  end
84
88
 
85
89
  def trace_test(test_name, test_suite_name, service: nil, tags: {}, &block)
86
- return skip_tracing(block) unless enabled
87
-
88
90
  set_inherited_globals(tags)
89
91
  set_session_context(tags)
90
92
  set_module_context(tags)
91
93
  set_suite_context(tags, name: test_suite_name)
92
94
 
93
95
  tags[Ext::Test::TAG_NAME] = test_name
96
+ tags[Ext::Test::TAG_TYPE] ||= Ext::Test::Type::TEST
94
97
 
95
98
  span_options = build_span_options(
96
99
  service,
@@ -104,7 +107,7 @@ module Datadog
104
107
  start_datadog_tracer_span(test_name, span_options) do |tracer_span|
105
108
  test = build_test(tracer_span, tags)
106
109
 
107
- @local_context.activate_test!(test) do
110
+ @local_context.activate_test(test) do
108
111
  block.call(test)
109
112
  end
110
113
  end
@@ -112,17 +115,15 @@ module Datadog
112
115
  tracer_span = start_datadog_tracer_span(test_name, span_options)
113
116
 
114
117
  test = build_test(tracer_span, tags)
115
- @local_context.activate_test!(test)
118
+ @local_context.activate_test(test)
116
119
  test
117
120
  end
118
121
  end
119
122
 
120
- def trace(span_type, span_name, tags: {}, &block)
121
- return skip_tracing(block) unless enabled
122
-
123
+ def trace(span_name, type: "span", tags: {}, &block)
123
124
  span_options = build_span_options(
124
125
  nil, # service name is completely optional for custom spans
125
- span_type,
126
+ type,
126
127
  {resource: span_name}
127
128
  )
128
129
 
@@ -158,8 +159,8 @@ module Datadog
158
159
  @global_context.active_test_suite(test_suite_name)
159
160
  end
160
161
 
161
- def deactivate_test(test)
162
- @local_context.deactivate_test!(test)
162
+ def deactivate_test
163
+ @local_context.deactivate_test
163
164
  end
164
165
 
165
166
  def deactivate_test_session
@@ -177,11 +178,7 @@ module Datadog
177
178
  private
178
179
 
179
180
  def skip_tracing(block = nil)
180
- if block
181
- block.call(null_span)
182
- else
183
- null_span
184
- end
181
+ block.call(nil) if block
185
182
  end
186
183
 
187
184
  # Sets trace's origin to ciapp-test
@@ -210,6 +207,9 @@ module Datadog
210
207
  def build_test(tracer_span, tags)
211
208
  test = Test.new(tracer_span)
212
209
  set_initial_tags(test, tags)
210
+ validate_test_suite_level_visibility_correctness(test)
211
+ set_codeowners(test)
212
+
213
213
  test
214
214
  end
215
215
 
@@ -219,9 +219,9 @@ module Datadog
219
219
  span
220
220
  end
221
221
 
222
- def build_span_options(service, span_type, other_options = {})
222
+ def build_span_options(service, type, other_options = {})
223
223
  other_options[:service] = service || @global_context.service
224
- other_options[:span_type] = span_type
224
+ other_options[:type] = type
225
225
 
226
226
  other_options
227
227
  end
@@ -255,6 +255,12 @@ module Datadog
255
255
  end
256
256
  end
257
257
 
258
+ def set_codeowners(test)
259
+ source = test.source_file
260
+ owners = @codeowners.list_owners(source) if source
261
+ test.set_tag(Ext::Test::TAG_CODEOWNERS, owners) unless owners.nil?
262
+ end
263
+
258
264
  def set_suite_context(tags, span: nil, name: nil)
259
265
  return if span.nil? && name.nil?
260
266
 
@@ -284,8 +290,29 @@ module Datadog
284
290
  end
285
291
  end
286
292
 
287
- def null_span
288
- @null_span ||= NullSpan.new
293
+ def validate_test_suite_level_visibility_correctness(test)
294
+ return unless test_suite_level_visibility_enabled
295
+
296
+ if test.test_suite_id.nil?
297
+ Datadog.logger.debug do
298
+ "Test [#{test.name}] does not have a test suite associated with it. " \
299
+ "Expected test suite [#{test.test_suite_name}] to be running."
300
+ end
301
+ end
302
+
303
+ if test.test_module_id.nil?
304
+ Datadog.logger.debug do
305
+ "Test [#{test.name}] does not have a test module associated with it. " \
306
+ "Make sure that there is a test module running within a session."
307
+ end
308
+ end
309
+
310
+ if test.test_session_id.nil?
311
+ Datadog.logger.debug do
312
+ "Test [#{test.name}] does not have a test session associated with it. " \
313
+ "Make sure that there is a test session running."
314
+ end
315
+ end
289
316
  end
290
317
  end
291
318
  end