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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +60 -1
- data/README.md +1 -1
- data/lib/datadog/ci/codeowners/matcher.rb +102 -0
- data/lib/datadog/ci/codeowners/parser.rb +42 -0
- data/lib/datadog/ci/codeowners/rule.rb +33 -0
- data/lib/datadog/ci/concurrent_span.rb +2 -1
- data/lib/datadog/ci/configuration/components.rb +15 -11
- data/lib/datadog/ci/configuration/settings.rb +15 -0
- data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +4 -1
- data/lib/datadog/ci/contrib/cucumber/ext.rb +7 -5
- data/lib/datadog/ci/contrib/cucumber/formatter.rb +138 -27
- data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +2 -1
- data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +4 -1
- data/lib/datadog/ci/contrib/minitest/ext.rb +6 -4
- data/lib/datadog/ci/contrib/minitest/helpers.rb +23 -0
- data/lib/datadog/ci/contrib/minitest/hooks.rb +34 -19
- data/lib/datadog/ci/contrib/minitest/patcher.rb +10 -0
- data/lib/datadog/ci/contrib/minitest/reporter.rb +50 -0
- data/lib/datadog/ci/contrib/minitest/runnable.rb +42 -0
- data/lib/datadog/ci/contrib/minitest/runner.rb +41 -0
- data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +4 -1
- data/lib/datadog/ci/contrib/rspec/example.rb +55 -14
- data/lib/datadog/ci/contrib/rspec/example_group.rb +51 -0
- data/lib/datadog/ci/contrib/rspec/ext.rb +5 -4
- data/lib/datadog/ci/contrib/rspec/integration.rb +1 -1
- data/lib/datadog/ci/contrib/rspec/patcher.rb +14 -0
- data/lib/datadog/ci/contrib/rspec/runner.rb +57 -0
- data/lib/datadog/ci/contrib/settings.rb +1 -1
- data/lib/datadog/ci/ext/app_types.rb +2 -0
- data/lib/datadog/ci/ext/environment/providers/local_git.rb +8 -29
- data/lib/datadog/ci/ext/settings.rb +1 -0
- data/lib/datadog/ci/ext/test.rb +19 -6
- data/lib/datadog/ci/span.rb +33 -2
- data/lib/datadog/ci/test.rb +96 -1
- data/lib/datadog/ci/test_module.rb +1 -1
- data/lib/datadog/ci/test_session.rb +10 -2
- data/lib/datadog/ci/test_suite.rb +53 -2
- data/lib/datadog/ci/test_visibility/context/local.rb +3 -9
- data/lib/datadog/ci/test_visibility/null_recorder.rb +53 -0
- data/lib/datadog/ci/test_visibility/recorder.rb +52 -25
- data/lib/datadog/ci/test_visibility/serializers/base.rb +6 -5
- data/lib/datadog/ci/test_visibility/serializers/span.rb +1 -1
- data/lib/datadog/ci/test_visibility/serializers/test_module.rb +1 -1
- data/lib/datadog/ci/test_visibility/serializers/test_session.rb +1 -1
- data/lib/datadog/ci/test_visibility/serializers/test_suite.rb +1 -1
- data/lib/datadog/ci/test_visibility/serializers/test_v1.rb +1 -1
- data/lib/datadog/ci/utils/configuration.rb +15 -0
- data/lib/datadog/ci/utils/git.rb +70 -0
- data/lib/datadog/ci/version.rb +2 -2
- data/lib/datadog/ci.rb +40 -56
- metadata +14 -82
- data/lib/datadog/ci/null_span.rb +0 -63
- data/sig/datadog/ci/concurrent_span.rbs +0 -23
- data/sig/datadog/ci/configuration/components.rbs +0 -21
- data/sig/datadog/ci/configuration/extensions.rbs +0 -9
- data/sig/datadog/ci/configuration/settings.rbs +0 -16
- data/sig/datadog/ci/contrib/cucumber/configuration/settings.rbs +0 -12
- data/sig/datadog/ci/contrib/cucumber/ext.rbs +0 -25
- data/sig/datadog/ci/contrib/cucumber/formatter.rbs +0 -35
- data/sig/datadog/ci/contrib/cucumber/instrumentation.rbs +0 -16
- data/sig/datadog/ci/contrib/cucumber/integration.rbs +0 -26
- data/sig/datadog/ci/contrib/cucumber/patcher.rbs +0 -15
- data/sig/datadog/ci/contrib/integration.rbs +0 -44
- data/sig/datadog/ci/contrib/minitest/configuration/settings.rbs +0 -12
- data/sig/datadog/ci/contrib/minitest/ext.rbs +0 -23
- data/sig/datadog/ci/contrib/minitest/hooks.rbs +0 -19
- data/sig/datadog/ci/contrib/minitest/integration.rbs +0 -26
- data/sig/datadog/ci/contrib/minitest/patcher.rbs +0 -15
- data/sig/datadog/ci/contrib/rspec/configuration/settings.rbs +0 -12
- data/sig/datadog/ci/contrib/rspec/example.rbs +0 -20
- data/sig/datadog/ci/contrib/rspec/ext.rbs +0 -23
- data/sig/datadog/ci/contrib/rspec/integration.rbs +0 -26
- data/sig/datadog/ci/contrib/rspec/patcher.rbs +0 -15
- data/sig/datadog/ci/contrib/settings.rbs +0 -25
- data/sig/datadog/ci/ext/app_types.rbs +0 -14
- data/sig/datadog/ci/ext/environment/extractor.rbs +0 -25
- data/sig/datadog/ci/ext/environment/providers/appveyor.rbs +0 -48
- data/sig/datadog/ci/ext/environment/providers/aws_code_pipeline.rbs +0 -19
- data/sig/datadog/ci/ext/environment/providers/azure.rbs +0 -56
- data/sig/datadog/ci/ext/environment/providers/base.rbs +0 -71
- data/sig/datadog/ci/ext/environment/providers/bitbucket.rbs +0 -37
- data/sig/datadog/ci/ext/environment/providers/bitrise.rbs +0 -41
- data/sig/datadog/ci/ext/environment/providers/buddy.rbs +0 -37
- data/sig/datadog/ci/ext/environment/providers/buildkite.rbs +0 -45
- data/sig/datadog/ci/ext/environment/providers/circleci.rbs +0 -41
- data/sig/datadog/ci/ext/environment/providers/codefresh.rbs +0 -25
- data/sig/datadog/ci/ext/environment/providers/github_actions.rbs +0 -42
- data/sig/datadog/ci/ext/environment/providers/gitlab.rbs +0 -57
- data/sig/datadog/ci/ext/environment/providers/jenkins.rbs +0 -35
- data/sig/datadog/ci/ext/environment/providers/local_git.rbs +0 -66
- data/sig/datadog/ci/ext/environment/providers/teamcity.rbs +0 -17
- data/sig/datadog/ci/ext/environment/providers/travis.rbs +0 -35
- data/sig/datadog/ci/ext/environment/providers/user_defined_tags.rbs +0 -33
- data/sig/datadog/ci/ext/environment/providers.rbs +0 -13
- data/sig/datadog/ci/ext/environment.rbs +0 -44
- data/sig/datadog/ci/ext/git.rbs +0 -53
- data/sig/datadog/ci/ext/settings.rbs +0 -14
- data/sig/datadog/ci/ext/test.rbs +0 -58
- data/sig/datadog/ci/ext/transport.rbs +0 -29
- data/sig/datadog/ci/null_span.rbs +0 -37
- data/sig/datadog/ci/span.rbs +0 -39
- data/sig/datadog/ci/test.rbs +0 -7
- data/sig/datadog/ci/test_module.rbs +0 -6
- data/sig/datadog/ci/test_session.rbs +0 -9
- data/sig/datadog/ci/test_suite.rbs +0 -6
- data/sig/datadog/ci/test_visibility/context/global.rbs +0 -39
- data/sig/datadog/ci/test_visibility/context/local.rbs +0 -23
- data/sig/datadog/ci/test_visibility/flush.rbs +0 -17
- data/sig/datadog/ci/test_visibility/recorder.rbs +0 -85
- data/sig/datadog/ci/test_visibility/serializers/base.rbs +0 -94
- data/sig/datadog/ci/test_visibility/serializers/factories/test_level.rbs +0 -13
- data/sig/datadog/ci/test_visibility/serializers/factories/test_suite_level.rbs +0 -13
- data/sig/datadog/ci/test_visibility/serializers/span.rbs +0 -18
- data/sig/datadog/ci/test_visibility/serializers/test_module.rbs +0 -26
- data/sig/datadog/ci/test_visibility/serializers/test_session.rbs +0 -26
- data/sig/datadog/ci/test_visibility/serializers/test_suite.rbs +0 -26
- data/sig/datadog/ci/test_visibility/serializers/test_v1.rbs +0 -23
- data/sig/datadog/ci/test_visibility/serializers/test_v2.rbs +0 -25
- data/sig/datadog/ci/test_visibility/transport.rbs +0 -35
- data/sig/datadog/ci/transport/api/base.rbs +0 -21
- data/sig/datadog/ci/transport/api/builder.rbs +0 -12
- data/sig/datadog/ci/transport/api/ci_test_cycle.rbs +0 -21
- data/sig/datadog/ci/transport/api/evp_proxy.rbs +0 -19
- data/sig/datadog/ci/transport/gzip.rbs +0 -9
- data/sig/datadog/ci/transport/http.rbs +0 -36
- data/sig/datadog/ci/utils/git.rbs +0 -11
- data/sig/datadog/ci/utils/test_run.rbs +0 -11
- data/sig/datadog/ci/utils/url.rbs +0 -9
- data/sig/datadog/ci/version.rbs +0 -16
- 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 "
|
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 = [
|
data/lib/datadog/ci/ext/test.rb
CHANGED
@@ -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"
|
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
|
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
|
-
|
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
|
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
|
data/lib/datadog/ci/span.rb
CHANGED
@@ -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
|
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::
|
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
|
data/lib/datadog/ci/test.rb
CHANGED
@@ -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
|
-
|
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
|
@@ -17,14 +17,22 @@ module Datadog
|
|
17
17
|
def finish
|
18
18
|
super
|
19
19
|
|
20
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
30
|
-
|
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
|
29
|
+
attr_reader :environment_tags, :test_suite_level_visibility_enabled
|
29
30
|
|
30
|
-
def initialize(
|
31
|
-
|
32
|
-
|
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 =
|
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
|
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
|
118
|
+
@local_context.activate_test(test)
|
116
119
|
test
|
117
120
|
end
|
118
121
|
end
|
119
122
|
|
120
|
-
def trace(
|
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
|
-
|
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
|
162
|
-
@local_context.deactivate_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,
|
222
|
+
def build_span_options(service, type, other_options = {})
|
223
223
|
other_options[:service] = service || @global_context.service
|
224
|
-
other_options[:
|
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
|
288
|
-
|
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
|