datadog-ci 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -1
- data/README.md +26 -3
- data/lib/datadog/ci/concurrent_span.rb +59 -0
- data/lib/datadog/ci/configuration/components.rb +37 -3
- data/lib/datadog/ci/configuration/settings.rb +7 -1
- data/lib/datadog/ci/context/global.rb +80 -0
- data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +12 -1
- data/lib/datadog/ci/contrib/cucumber/formatter.rb +3 -4
- data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +10 -1
- data/lib/datadog/ci/contrib/minitest/hooks.rb +3 -4
- data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +10 -1
- data/lib/datadog/ci/contrib/rspec/example.rb +3 -4
- data/lib/datadog/ci/contrib/settings.rb +2 -0
- data/lib/datadog/ci/ext/app_types.rb +5 -0
- data/lib/datadog/ci/ext/settings.rb +11 -0
- data/lib/datadog/ci/ext/test.rb +12 -1
- data/lib/datadog/ci/null_span.rb +63 -0
- data/lib/datadog/ci/recorder.rb +207 -35
- data/lib/datadog/ci/span.rb +13 -3
- data/lib/datadog/ci/test.rb +0 -1
- data/lib/datadog/ci/test_module.rb +23 -0
- data/lib/datadog/ci/test_session.rb +36 -0
- data/lib/datadog/ci/test_suite.rb +25 -0
- data/lib/datadog/ci/test_visibility/serializers/base.rb +100 -12
- data/lib/datadog/ci/test_visibility/serializers/factories/test_suite_level.rb +37 -0
- data/lib/datadog/ci/test_visibility/serializers/span.rb +2 -16
- data/lib/datadog/ci/test_visibility/serializers/test_module.rb +46 -0
- data/lib/datadog/ci/test_visibility/serializers/test_session.rb +46 -0
- data/lib/datadog/ci/test_visibility/serializers/test_suite.rb +46 -0
- data/lib/datadog/ci/test_visibility/serializers/test_v1.rb +3 -17
- data/lib/datadog/ci/test_visibility/serializers/test_v2.rb +38 -0
- data/lib/datadog/ci/test_visibility/transport.rb +4 -4
- data/lib/datadog/ci/utils/test_run.rb +15 -0
- data/lib/datadog/ci/version.rb +1 -1
- data/lib/datadog/ci.rb +214 -32
- data/sig/datadog/ci/concurrent_span.rbs +23 -0
- data/sig/datadog/ci/configuration/components.rbs +2 -0
- data/sig/datadog/ci/context/global.rbs +37 -0
- data/sig/datadog/ci/ext/app_types.rbs +6 -1
- data/sig/datadog/ci/ext/settings.rbs +3 -0
- data/sig/datadog/ci/ext/test.rbs +15 -0
- data/sig/datadog/ci/null_span.rbs +37 -0
- data/sig/datadog/ci/recorder.rbs +54 -1
- data/sig/datadog/ci/span.rbs +4 -0
- data/sig/datadog/ci/test_module.rbs +6 -0
- data/sig/datadog/ci/test_session.rbs +9 -0
- data/sig/datadog/ci/test_suite.rbs +6 -0
- data/sig/datadog/ci/test_visibility/serializers/base.rbs +26 -5
- data/sig/datadog/ci/test_visibility/serializers/factories/test_suite_level.rbs +13 -0
- data/sig/datadog/ci/test_visibility/serializers/test_module.rbs +26 -0
- data/sig/datadog/ci/test_visibility/serializers/test_session.rbs +26 -0
- data/sig/datadog/ci/test_visibility/serializers/test_suite.rbs +26 -0
- data/sig/datadog/ci/test_visibility/serializers/test_v1.rbs +1 -1
- data/sig/datadog/ci/test_visibility/serializers/test_v2.rbs +25 -0
- data/sig/datadog/ci/test_visibility/transport.rbs +3 -3
- data/sig/datadog/ci/utils/test_run.rbs +11 -0
- data/sig/datadog/ci.rbs +18 -2
- metadata +26 -2
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "datadog/tracing/span_operation"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module CI
|
7
|
+
# Represents an ignored span when CI visibility is disabled.
|
8
|
+
# Replaces all methods with no-op.
|
9
|
+
#
|
10
|
+
# @public_api
|
11
|
+
class NullSpan < Span
|
12
|
+
def initialize
|
13
|
+
super(Datadog::Tracing::SpanOperation.new("null.span"))
|
14
|
+
end
|
15
|
+
|
16
|
+
def id
|
17
|
+
end
|
18
|
+
|
19
|
+
def name
|
20
|
+
end
|
21
|
+
|
22
|
+
def service
|
23
|
+
end
|
24
|
+
|
25
|
+
def span_type
|
26
|
+
end
|
27
|
+
|
28
|
+
def passed!
|
29
|
+
end
|
30
|
+
|
31
|
+
def failed!(exception: nil)
|
32
|
+
end
|
33
|
+
|
34
|
+
def skipped!(exception: nil, reason: nil)
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_tag(key)
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_tag(key, value)
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_metric(key, value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def finish
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_tags(tags)
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_environment_runtime_tags
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_default_tags
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
self.class.to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/datadog/ci/recorder.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "datadog/tracing"
|
4
|
+
require "datadog/tracing/trace_digest"
|
4
5
|
|
5
6
|
require "rbconfig"
|
6
7
|
|
@@ -8,36 +9,98 @@ require_relative "ext/app_types"
|
|
8
9
|
require_relative "ext/test"
|
9
10
|
require_relative "ext/environment"
|
10
11
|
|
12
|
+
require_relative "context/global"
|
11
13
|
require_relative "context/local"
|
12
14
|
|
13
15
|
require_relative "span"
|
16
|
+
require_relative "null_span"
|
14
17
|
require_relative "test"
|
18
|
+
require_relative "test_session"
|
19
|
+
require_relative "test_module"
|
20
|
+
require_relative "test_suite"
|
15
21
|
|
16
22
|
module Datadog
|
17
23
|
module CI
|
18
24
|
# Common behavior for CI tests
|
25
|
+
# Note: this class has too many responsibilities and should be split into multiple classes
|
19
26
|
class Recorder
|
20
|
-
attr_reader :environment_tags
|
27
|
+
attr_reader :environment_tags, :test_suite_level_visibility_enabled, :enabled
|
28
|
+
|
29
|
+
def initialize(enabled: true, test_suite_level_visibility_enabled: false)
|
30
|
+
@enabled = enabled
|
31
|
+
@test_suite_level_visibility_enabled = enabled && test_suite_level_visibility_enabled
|
21
32
|
|
22
|
-
def initialize
|
23
33
|
@environment_tags = Ext::Environment.tags(ENV).freeze
|
24
34
|
@local_context = Context::Local.new
|
35
|
+
@global_context = Context::Global.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def start_test_session(service: nil, tags: {})
|
39
|
+
return skip_tracing unless test_suite_level_visibility_enabled
|
40
|
+
|
41
|
+
@global_context.fetch_or_activate_test_session do
|
42
|
+
tracer_span = start_datadog_tracer_span(
|
43
|
+
"test.session", build_span_options(service, Ext::AppTypes::TYPE_TEST_SESSION)
|
44
|
+
)
|
45
|
+
set_session_context(tags, tracer_span)
|
46
|
+
|
47
|
+
build_test_session(tracer_span, tags)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def start_test_module(test_module_name, service: nil, tags: {})
|
52
|
+
return skip_tracing unless test_suite_level_visibility_enabled
|
53
|
+
|
54
|
+
@global_context.fetch_or_activate_test_module do
|
55
|
+
set_inherited_globals(tags)
|
56
|
+
set_session_context(tags)
|
57
|
+
|
58
|
+
tracer_span = start_datadog_tracer_span(
|
59
|
+
test_module_name, build_span_options(service, Ext::AppTypes::TYPE_TEST_MODULE)
|
60
|
+
)
|
61
|
+
set_module_context(tags, tracer_span)
|
62
|
+
|
63
|
+
build_test_module(tracer_span, tags)
|
64
|
+
end
|
25
65
|
end
|
26
66
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
67
|
+
def start_test_suite(test_suite_name, service: nil, tags: {})
|
68
|
+
return skip_tracing unless test_suite_level_visibility_enabled
|
69
|
+
|
70
|
+
@global_context.fetch_or_activate_test_suite(test_suite_name) do
|
71
|
+
set_inherited_globals(tags)
|
72
|
+
set_session_context(tags)
|
73
|
+
set_module_context(tags)
|
74
|
+
|
75
|
+
tracer_span = start_datadog_tracer_span(
|
76
|
+
test_suite_name, build_span_options(service, Ext::AppTypes::TYPE_TEST_SUITE)
|
77
|
+
)
|
78
|
+
set_suite_context(tags, span: tracer_span)
|
79
|
+
|
80
|
+
build_test_suite(tracer_span, tags)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def trace_test(test_name, test_suite_name, service: nil, tags: {}, &block)
|
85
|
+
return skip_tracing(block) unless enabled
|
86
|
+
|
87
|
+
set_inherited_globals(tags)
|
88
|
+
set_session_context(tags)
|
89
|
+
set_module_context(tags)
|
90
|
+
set_suite_context(tags, name: test_suite_name)
|
34
91
|
|
35
92
|
tags[Ext::Test::TAG_NAME] = test_name
|
36
93
|
|
37
|
-
|
38
|
-
|
39
|
-
|
94
|
+
span_options = build_span_options(
|
95
|
+
service,
|
96
|
+
Ext::AppTypes::TYPE_TEST,
|
97
|
+
# :resource is needed for the agent APM protocol to work correctly (for older agent versions)
|
98
|
+
# :continue_from is required to start a new trace for each test
|
99
|
+
{resource: test_name, continue_from: Datadog::Tracing::TraceDigest.new}
|
100
|
+
)
|
40
101
|
|
102
|
+
if block
|
103
|
+
start_datadog_tracer_span(test_name, span_options) do |tracer_span|
|
41
104
|
test = build_test(tracer_span, tags)
|
42
105
|
|
43
106
|
@local_context.activate_test!(test) do
|
@@ -45,10 +108,7 @@ module Datadog
|
|
45
108
|
end
|
46
109
|
end
|
47
110
|
else
|
48
|
-
tracer_span =
|
49
|
-
trace = Datadog::Tracing.active_trace
|
50
|
-
|
51
|
-
set_trace_origin(trace)
|
111
|
+
tracer_span = start_datadog_tracer_span(test_name, span_options)
|
52
112
|
|
53
113
|
test = build_test(tracer_span, tags)
|
54
114
|
@local_context.activate_test!(test)
|
@@ -57,62 +117,174 @@ module Datadog
|
|
57
117
|
end
|
58
118
|
|
59
119
|
def trace(span_type, span_name, tags: {}, &block)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
120
|
+
return skip_tracing(block) unless enabled
|
121
|
+
|
122
|
+
span_options = build_span_options(
|
123
|
+
nil, # service name is completely optional for custom spans
|
124
|
+
span_type,
|
125
|
+
{resource: span_name}
|
126
|
+
)
|
64
127
|
|
65
128
|
if block
|
66
|
-
|
129
|
+
start_datadog_tracer_span(span_name, span_options) do |tracer_span|
|
67
130
|
block.call(build_span(tracer_span, tags))
|
68
131
|
end
|
69
132
|
else
|
70
|
-
tracer_span =
|
133
|
+
tracer_span = start_datadog_tracer_span(span_name, span_options)
|
71
134
|
|
72
135
|
build_span(tracer_span, tags)
|
73
136
|
end
|
74
137
|
end
|
75
138
|
|
139
|
+
def active_span
|
140
|
+
tracer_span = Datadog::Tracing.active_span
|
141
|
+
Span.new(tracer_span) if tracer_span
|
142
|
+
end
|
143
|
+
|
76
144
|
def active_test
|
77
145
|
@local_context.active_test
|
78
146
|
end
|
79
147
|
|
148
|
+
def active_test_session
|
149
|
+
@global_context.active_test_session
|
150
|
+
end
|
151
|
+
|
152
|
+
def active_test_module
|
153
|
+
@global_context.active_test_module
|
154
|
+
end
|
155
|
+
|
156
|
+
def active_test_suite(test_suite_name)
|
157
|
+
@global_context.active_test_suite(test_suite_name)
|
158
|
+
end
|
159
|
+
|
80
160
|
def deactivate_test(test)
|
81
161
|
@local_context.deactivate_test!(test)
|
82
162
|
end
|
83
163
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
164
|
+
def deactivate_test_session
|
165
|
+
@global_context.deactivate_test_session!
|
166
|
+
end
|
167
|
+
|
168
|
+
def deactivate_test_module
|
169
|
+
@global_context.deactivate_test_module!
|
170
|
+
end
|
171
|
+
|
172
|
+
def deactivate_test_suite(test_suite_name)
|
173
|
+
@global_context.deactivate_test_suite!(test_suite_name)
|
87
174
|
end
|
88
175
|
|
89
176
|
private
|
90
177
|
|
178
|
+
def skip_tracing(block = nil)
|
179
|
+
if block
|
180
|
+
block.call(null_span)
|
181
|
+
else
|
182
|
+
null_span
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
91
186
|
# Sets trace's origin to ciapp-test
|
92
187
|
def set_trace_origin(trace)
|
93
188
|
trace.origin = Ext::Test::CONTEXT_ORIGIN if trace
|
94
189
|
end
|
95
190
|
|
96
|
-
def
|
97
|
-
|
191
|
+
def build_test_session(tracer_span, tags)
|
192
|
+
test_session = TestSession.new(tracer_span)
|
193
|
+
set_initial_tags(test_session, tags)
|
194
|
+
test_session
|
195
|
+
end
|
98
196
|
|
99
|
-
|
100
|
-
|
197
|
+
def build_test_module(tracer_span, tags)
|
198
|
+
test_module = TestModule.new(tracer_span)
|
199
|
+
set_initial_tags(test_module, tags)
|
200
|
+
test_module
|
201
|
+
end
|
101
202
|
|
102
|
-
|
103
|
-
|
203
|
+
def build_test_suite(tracer_span, tags)
|
204
|
+
test_suite = TestSuite.new(tracer_span)
|
205
|
+
set_initial_tags(test_suite, tags)
|
206
|
+
test_suite
|
207
|
+
end
|
104
208
|
|
209
|
+
def build_test(tracer_span, tags)
|
210
|
+
test = Test.new(tracer_span)
|
211
|
+
set_initial_tags(test, tags)
|
105
212
|
test
|
106
213
|
end
|
107
214
|
|
108
215
|
def build_span(tracer_span, tags)
|
109
216
|
span = Span.new(tracer_span)
|
217
|
+
set_initial_tags(span, tags)
|
218
|
+
span
|
219
|
+
end
|
110
220
|
|
111
|
-
|
112
|
-
|
113
|
-
|
221
|
+
def build_span_options(service, span_type, other_options = {})
|
222
|
+
other_options[:service] = service || @global_context.service
|
223
|
+
other_options[:span_type] = span_type
|
114
224
|
|
115
|
-
|
225
|
+
other_options
|
226
|
+
end
|
227
|
+
|
228
|
+
def set_inherited_globals(tags)
|
229
|
+
# this code achieves the same as @global_context.inheritable_session_tags.merge(tags)
|
230
|
+
# but without allocating a new hash
|
231
|
+
@global_context.inheritable_session_tags.each do |key, value|
|
232
|
+
tags[key] = value unless tags.key?(key)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def set_initial_tags(ci_span, tags)
|
237
|
+
ci_span.set_default_tags
|
238
|
+
ci_span.set_environment_runtime_tags
|
239
|
+
|
240
|
+
ci_span.set_tags(tags)
|
241
|
+
ci_span.set_tags(environment_tags)
|
242
|
+
end
|
243
|
+
|
244
|
+
def set_session_context(tags, test_session = nil)
|
245
|
+
test_session ||= active_test_session
|
246
|
+
tags[Ext::Test::TAG_TEST_SESSION_ID] = test_session.id.to_s if test_session
|
247
|
+
end
|
248
|
+
|
249
|
+
def set_module_context(tags, test_module = nil)
|
250
|
+
test_module ||= active_test_module
|
251
|
+
if test_module
|
252
|
+
tags[Ext::Test::TAG_TEST_MODULE_ID] = test_module.id.to_s
|
253
|
+
tags[Ext::Test::TAG_MODULE] = test_module.name
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def set_suite_context(tags, span: nil, name: nil)
|
258
|
+
return if span.nil? && name.nil?
|
259
|
+
|
260
|
+
test_suite = span || active_test_suite(name)
|
261
|
+
|
262
|
+
if test_suite
|
263
|
+
tags[Ext::Test::TAG_TEST_SUITE_ID] = test_suite.id.to_s
|
264
|
+
tags[Ext::Test::TAG_SUITE] = test_suite.name
|
265
|
+
else
|
266
|
+
tags[Ext::Test::TAG_SUITE] = name
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def start_datadog_tracer_span(span_name, span_options, &block)
|
271
|
+
if block
|
272
|
+
Datadog::Tracing.trace(span_name, **span_options) do |tracer_span, trace|
|
273
|
+
set_trace_origin(trace)
|
274
|
+
|
275
|
+
yield tracer_span
|
276
|
+
end
|
277
|
+
else
|
278
|
+
tracer_span = Datadog::Tracing.trace(span_name, **span_options)
|
279
|
+
trace = Datadog::Tracing.active_trace
|
280
|
+
set_trace_origin(trace)
|
281
|
+
|
282
|
+
tracer_span
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def null_span
|
287
|
+
@null_span ||= NullSpan.new
|
116
288
|
end
|
117
289
|
end
|
118
290
|
end
|
data/lib/datadog/ci/span.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "ext/test"
|
4
|
+
require_relative "utils/test_run"
|
4
5
|
|
5
6
|
module Datadog
|
6
7
|
module CI
|
@@ -15,11 +16,21 @@ module Datadog
|
|
15
16
|
@tracer_span = tracer_span
|
16
17
|
end
|
17
18
|
|
19
|
+
# @return [Integer] the ID of the span.
|
20
|
+
def id
|
21
|
+
tracer_span.id
|
22
|
+
end
|
23
|
+
|
18
24
|
# @return [String] the name of the span.
|
19
25
|
def name
|
20
26
|
tracer_span.name
|
21
27
|
end
|
22
28
|
|
29
|
+
# @return [String] the service name of the span.
|
30
|
+
def service
|
31
|
+
tracer_span.service
|
32
|
+
end
|
33
|
+
|
23
34
|
# @return [String] the type of the span (for example "test" or type that was provided to [Datadog::CI.trace]).
|
24
35
|
def span_type
|
25
36
|
tracer_span.type
|
@@ -83,9 +94,7 @@ module Datadog
|
|
83
94
|
# @param [Hash[String, String]] tags the tags to set.
|
84
95
|
# @return [void]
|
85
96
|
def set_tags(tags)
|
86
|
-
tags
|
87
|
-
tracer_span.set_tag(key, value)
|
88
|
-
end
|
97
|
+
tracer_span.set_tags(tags)
|
89
98
|
end
|
90
99
|
|
91
100
|
def set_environment_runtime_tags
|
@@ -93,6 +102,7 @@ module Datadog
|
|
93
102
|
tracer_span.set_tag(Ext::Test::TAG_OS_PLATFORM, ::RbConfig::CONFIG["host_os"])
|
94
103
|
tracer_span.set_tag(Ext::Test::TAG_RUNTIME_NAME, Core::Environment::Ext::LANG_ENGINE)
|
95
104
|
tracer_span.set_tag(Ext::Test::TAG_RUNTIME_VERSION, Core::Environment::Ext::ENGINE_VERSION)
|
105
|
+
tracer_span.set_tag(Ext::Test::TAG_COMMAND, Utils::TestRun.command)
|
96
106
|
end
|
97
107
|
|
98
108
|
def set_default_tags
|
data/lib/datadog/ci/test.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "concurrent_span"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module CI
|
7
|
+
# Represents a single test module.
|
8
|
+
# Read here on what test module could mean:
|
9
|
+
# https://docs.datadoghq.com/continuous_integration/explorer/?tab=testruns#module
|
10
|
+
# This object can be shared between multiple threads.
|
11
|
+
#
|
12
|
+
# @public_api
|
13
|
+
class TestModule < ConcurrentSpan
|
14
|
+
# Finishes this test module.
|
15
|
+
# @return [void]
|
16
|
+
def finish
|
17
|
+
super
|
18
|
+
|
19
|
+
CI.deactivate_test_module
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "concurrent_span"
|
4
|
+
require_relative "ext/test"
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module CI
|
8
|
+
# Represents the whole test session process.
|
9
|
+
# Documentation on test sessions is here:
|
10
|
+
# https://docs.datadoghq.com/continuous_integration/explorer/?tab=testruns#sessions
|
11
|
+
# This object can be shared between multiple threads.
|
12
|
+
#
|
13
|
+
# @public_api
|
14
|
+
class TestSession < ConcurrentSpan
|
15
|
+
# Finishes the current test session.
|
16
|
+
# @return [void]
|
17
|
+
def finish
|
18
|
+
super
|
19
|
+
|
20
|
+
CI.deactivate_test_session
|
21
|
+
end
|
22
|
+
|
23
|
+
def inheritable_tags
|
24
|
+
return @inheritable_tags if defined?(@inheritable_tags)
|
25
|
+
|
26
|
+
# 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
|
28
|
+
res = {}
|
29
|
+
Ext::Test::INHERITABLE_TAGS.each do |tag|
|
30
|
+
res[tag] = get_tag(tag)
|
31
|
+
end
|
32
|
+
@inheritable_tags = res.freeze
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "concurrent_span"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module CI
|
7
|
+
# Represents a single test suite.
|
8
|
+
#
|
9
|
+
# Read here on what test suite means:
|
10
|
+
# https://docs.datadoghq.com/continuous_integration/explorer/?tab=testruns#suite
|
11
|
+
#
|
12
|
+
# This object can be shared between multiple threads.
|
13
|
+
#
|
14
|
+
# @public_api
|
15
|
+
class TestSuite < ConcurrentSpan
|
16
|
+
# Finishes this test suite.
|
17
|
+
# @return [void]
|
18
|
+
def finish
|
19
|
+
super
|
20
|
+
|
21
|
+
CI.deactivate_test_suite(name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
require_relative "../../ext/test"
|
6
|
+
|
3
7
|
module Datadog
|
4
8
|
module CI
|
5
9
|
module TestVisibility
|
@@ -9,11 +13,31 @@ module Datadog
|
|
9
13
|
MINIMUM_DURATION_NANO = 0
|
10
14
|
MAXIMUM_DURATION_NANO = 9223372036854775807
|
11
15
|
|
12
|
-
|
16
|
+
CONTENT_FIELDS = [
|
17
|
+
"name", "resource", "service",
|
18
|
+
"error", "start", "duration",
|
19
|
+
"meta", "metrics",
|
20
|
+
"type" => "span_type"
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
REQUIRED_FIELDS = [
|
24
|
+
"error",
|
25
|
+
"name",
|
26
|
+
"resource",
|
27
|
+
"start",
|
28
|
+
"duration"
|
29
|
+
].freeze
|
30
|
+
|
31
|
+
attr_reader :trace, :span, :meta
|
13
32
|
|
14
33
|
def initialize(trace, span)
|
15
34
|
@trace = trace
|
16
35
|
@span = span
|
36
|
+
|
37
|
+
@meta = @span.meta.reject { |key, _| Ext::Test::SPECIAL_TAGS.include?(key) }
|
38
|
+
|
39
|
+
@errors = {}
|
40
|
+
@validated = false
|
17
41
|
end
|
18
42
|
|
19
43
|
def to_msgpack(packer = nil)
|
@@ -40,7 +64,23 @@ module Datadog
|
|
40
64
|
|
41
65
|
# validates according to citestcycle json schema
|
42
66
|
def valid?
|
43
|
-
|
67
|
+
validate! unless @validated
|
68
|
+
|
69
|
+
@errors.empty?
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate!
|
73
|
+
@errors.clear
|
74
|
+
|
75
|
+
validate_required_fields!
|
76
|
+
validate_start_time!
|
77
|
+
validate_duration!
|
78
|
+
|
79
|
+
@validated = true
|
80
|
+
end
|
81
|
+
|
82
|
+
def validation_errors
|
83
|
+
@errors
|
44
84
|
end
|
45
85
|
|
46
86
|
def content_fields
|
@@ -67,6 +107,18 @@ module Datadog
|
|
67
107
|
@span.parent_id
|
68
108
|
end
|
69
109
|
|
110
|
+
def test_session_id
|
111
|
+
to_integer(@span.get_tag(Ext::Test::TAG_TEST_SESSION_ID))
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_module_id
|
115
|
+
to_integer(@span.get_tag(Ext::Test::TAG_TEST_MODULE_ID))
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_suite_id
|
119
|
+
to_integer(@span.get_tag(Ext::Test::TAG_TEST_SUITE_ID))
|
120
|
+
end
|
121
|
+
|
70
122
|
def type
|
71
123
|
end
|
72
124
|
|
@@ -98,10 +150,6 @@ module Datadog
|
|
98
150
|
@duration ||= duration_nano(@span.duration)
|
99
151
|
end
|
100
152
|
|
101
|
-
def meta
|
102
|
-
@span.meta
|
103
|
-
end
|
104
|
-
|
105
153
|
def metrics
|
106
154
|
@span.metrics
|
107
155
|
end
|
@@ -122,16 +170,48 @@ module Datadog
|
|
122
170
|
|
123
171
|
private
|
124
172
|
|
125
|
-
def
|
126
|
-
!start
|
173
|
+
def validate_start_time!
|
174
|
+
validate_required!("start")
|
175
|
+
validate_greater_than_or_equal!("start", MINIMUM_TIMESTAMP_NANO)
|
176
|
+
end
|
177
|
+
|
178
|
+
def validate_duration!
|
179
|
+
validate_required!("duration")
|
180
|
+
validate_greater_than_or_equal!("duration", MINIMUM_DURATION_NANO)
|
181
|
+
validate_less_than_or_equal!("duration", MAXIMUM_DURATION_NANO)
|
182
|
+
end
|
183
|
+
|
184
|
+
def validate_required_fields!
|
185
|
+
required_fields.each do |field|
|
186
|
+
validate_required!(field)
|
187
|
+
end
|
127
188
|
end
|
128
189
|
|
129
|
-
def
|
130
|
-
|
190
|
+
def validate_required!(field)
|
191
|
+
if send(field).nil?
|
192
|
+
add_error(field, "is required")
|
193
|
+
end
|
131
194
|
end
|
132
195
|
|
133
|
-
def
|
134
|
-
|
196
|
+
def validate_greater_than_or_equal!(field, value)
|
197
|
+
return if send(field).nil?
|
198
|
+
|
199
|
+
if send(field) < value
|
200
|
+
add_error(field, "must be greater than or equal to #{value}")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def validate_less_than_or_equal!(field, value)
|
205
|
+
return if send(field).nil?
|
206
|
+
|
207
|
+
if send(field) > value
|
208
|
+
add_error(field, "must be less than or equal to #{value}")
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def add_error(field, message)
|
213
|
+
@errors[field] ||= Set.new
|
214
|
+
@errors[field] << message
|
135
215
|
end
|
136
216
|
|
137
217
|
def required_fields
|
@@ -154,6 +234,14 @@ module Datadog
|
|
154
234
|
def duration_nano(duration)
|
155
235
|
(duration * 1e9).to_i
|
156
236
|
end
|
237
|
+
|
238
|
+
def to_s
|
239
|
+
"#{self.class.name}(id:#{span_id},name:#{name})"
|
240
|
+
end
|
241
|
+
|
242
|
+
def to_integer(value)
|
243
|
+
value.to_i if value
|
244
|
+
end
|
157
245
|
end
|
158
246
|
end
|
159
247
|
end
|