datadog-ci 0.5.0 → 0.6.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 +33 -2
- data/lib/datadog/ci/configuration/components.rb +13 -9
- data/lib/datadog/ci/configuration/extensions.rb +21 -0
- data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +1 -1
- data/lib/datadog/ci/contrib/cucumber/ext.rb +7 -5
- data/lib/datadog/ci/contrib/cucumber/formatter.rb +99 -19
- data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +1 -1
- data/lib/datadog/ci/contrib/minitest/ext.rb +6 -4
- data/lib/datadog/ci/contrib/minitest/helpers.rb +22 -0
- data/lib/datadog/ci/contrib/minitest/hooks.rb +45 -17
- data/lib/datadog/ci/contrib/minitest/patcher.rb +7 -0
- data/lib/datadog/ci/contrib/minitest/plugin.rb +73 -0
- data/lib/datadog/ci/contrib/minitest/runnable.rb +42 -0
- data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +1 -1
- data/lib/datadog/ci/contrib/rspec/example.rb +2 -2
- data/lib/datadog/ci/contrib/rspec/example_group.rb +46 -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 +5 -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/test.rb +2 -0
- data/lib/datadog/ci/span.rb +24 -0
- data/lib/datadog/ci/test.rb +30 -0
- data/lib/datadog/ci/test_session.rb +8 -0
- data/lib/datadog/ci/test_visibility/context/global.rb +82 -0
- data/lib/datadog/ci/test_visibility/context/local.rb +52 -0
- data/lib/datadog/ci/test_visibility/null_recorder.rb +73 -0
- data/lib/datadog/ci/test_visibility/recorder.rb +314 -0
- data/lib/datadog/ci/version.rb +1 -1
- data/lib/datadog/ci.rb +3 -3
- data/sig/datadog/ci/configuration/components.rbs +2 -2
- data/sig/datadog/ci/configuration/extensions.rbs +9 -0
- data/sig/datadog/ci/contrib/cucumber/ext.rbs +1 -5
- data/sig/datadog/ci/contrib/cucumber/formatter.rbs +17 -4
- data/sig/datadog/ci/contrib/minitest/ext.rbs +1 -5
- data/sig/datadog/ci/contrib/minitest/helpers.rbs +13 -0
- data/sig/datadog/ci/contrib/minitest/hooks.rbs +9 -1
- data/sig/datadog/ci/contrib/minitest/plugin.rbs +31 -0
- data/sig/datadog/ci/contrib/minitest/runnable.rbs +24 -0
- data/sig/datadog/ci/contrib/rspec/example_group.rbs +21 -0
- data/sig/datadog/ci/contrib/rspec/ext.rbs +2 -8
- data/sig/datadog/ci/contrib/rspec/runner.rbs +21 -0
- data/sig/datadog/ci/ext/test.rbs +2 -0
- data/sig/datadog/ci/span.rbs +8 -0
- data/sig/datadog/ci/test.rbs +5 -0
- data/sig/datadog/ci/test_visibility/context/global.rbs +39 -0
- data/sig/datadog/ci/test_visibility/context/local.rbs +23 -0
- data/sig/datadog/ci/test_visibility/null_recorder.rbs +45 -0
- data/sig/datadog/ci/test_visibility/recorder.rbs +85 -0
- data/sig/datadog/ci.rbs +3 -1
- metadata +23 -11
- data/lib/datadog/ci/context/global.rb +0 -80
- data/lib/datadog/ci/context/local.rb +0 -50
- data/lib/datadog/ci/extensions.rb +0 -19
- data/lib/datadog/ci/recorder.rb +0 -291
- data/sig/datadog/ci/context/global.rbs +0 -37
- data/sig/datadog/ci/context/local.rbs +0 -21
- data/sig/datadog/ci/extensions.rbs +0 -7
- data/sig/datadog/ci/recorder.rbs +0 -83
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../ext/test"
|
4
|
+
require_relative "ext"
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module CI
|
8
|
+
module Contrib
|
9
|
+
module RSpec
|
10
|
+
# Instrument RSpec::Core::Example
|
11
|
+
module ExampleGroup
|
12
|
+
def self.included(base)
|
13
|
+
base.singleton_class.prepend(ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Instance methods for configuration
|
17
|
+
module ClassMethods
|
18
|
+
def run(reporter = ::RSpec::Core::NullReporter)
|
19
|
+
return super unless configuration[:enabled]
|
20
|
+
return super unless top_level?
|
21
|
+
|
22
|
+
test_suite = Datadog::CI.start_test_suite(file_path)
|
23
|
+
|
24
|
+
result = super
|
25
|
+
|
26
|
+
if result
|
27
|
+
test_suite.passed!
|
28
|
+
else
|
29
|
+
test_suite.failed!
|
30
|
+
end
|
31
|
+
test_suite.finish
|
32
|
+
|
33
|
+
result
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def configuration
|
39
|
+
Datadog.configuration.ci[:rspec]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -7,13 +7,14 @@ module Datadog
|
|
7
7
|
# RSpec integration constants
|
8
8
|
# TODO: mark as `@public_api` when GA, to protect from resource and tag name changes.
|
9
9
|
module Ext
|
10
|
-
|
10
|
+
FRAMEWORK = "rspec"
|
11
|
+
DEFAULT_SERVICE_NAME = "rspec"
|
12
|
+
|
11
13
|
ENV_ENABLED = "DD_TRACE_RSPEC_ENABLED"
|
14
|
+
|
15
|
+
# TODO: remove in 1.0
|
12
16
|
ENV_OPERATION_NAME = "DD_TRACE_RSPEC_OPERATION_NAME"
|
13
|
-
FRAMEWORK = "rspec"
|
14
17
|
OPERATION_NAME = "rspec.example"
|
15
|
-
SERVICE_NAME = "rspec"
|
16
|
-
TEST_TYPE = "test"
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "datadog/tracing/contrib/patcher"
|
4
|
+
|
4
5
|
require_relative "example"
|
6
|
+
require_relative "example_group"
|
7
|
+
require_relative "runner"
|
5
8
|
|
6
9
|
module Datadog
|
7
10
|
module CI
|
@@ -19,6 +22,8 @@ module Datadog
|
|
19
22
|
|
20
23
|
def patch
|
21
24
|
::RSpec::Core::Example.include(Example)
|
25
|
+
::RSpec::Core::Runner.include(Runner)
|
26
|
+
::RSpec::Core::ExampleGroup.include(ExampleGroup)
|
22
27
|
end
|
23
28
|
end
|
24
29
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../ext/test"
|
4
|
+
require_relative "ext"
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module CI
|
8
|
+
module Contrib
|
9
|
+
module RSpec
|
10
|
+
# Instrument RSpec::Core::Runner
|
11
|
+
module Runner
|
12
|
+
def self.included(base)
|
13
|
+
base.prepend(InstanceMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def run_specs(example_groups)
|
18
|
+
return super unless configuration[:enabled]
|
19
|
+
|
20
|
+
test_session = CI.start_test_session(
|
21
|
+
tags: {
|
22
|
+
CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
|
23
|
+
CI::Ext::Test::TAG_FRAMEWORK_VERSION => CI::Contrib::RSpec::Integration.version.to_s,
|
24
|
+
CI::Ext::Test::TAG_TYPE => CI::Ext::Test::TEST_TYPE
|
25
|
+
},
|
26
|
+
service: configuration[:service_name]
|
27
|
+
)
|
28
|
+
|
29
|
+
test_module = CI.start_test_module(test_session.name)
|
30
|
+
|
31
|
+
result = super
|
32
|
+
|
33
|
+
if result != 0
|
34
|
+
# TODO: repeating this twice feels clunky, we need to remove test_module API before GA
|
35
|
+
test_module.failed!
|
36
|
+
test_session.failed!
|
37
|
+
else
|
38
|
+
test_module.passed!
|
39
|
+
test_session.passed!
|
40
|
+
end
|
41
|
+
test_module.finish
|
42
|
+
test_session.finish
|
43
|
+
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def configuration
|
50
|
+
Datadog.configuration.ci[:rspec]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/datadog/ci/ext/test.rb
CHANGED
@@ -20,6 +20,8 @@ module Datadog
|
|
20
20
|
TAG_TYPE = "test.type"
|
21
21
|
TAG_COMMAND = "test.command"
|
22
22
|
|
23
|
+
TEST_TYPE = "test"
|
24
|
+
|
23
25
|
# those tags are special and they are used to correlate tests with the test sessions, suites, and modules
|
24
26
|
TAG_TEST_SESSION_ID = "_test.session_id"
|
25
27
|
TAG_TEST_MODULE_ID = "_test.module_id"
|
data/lib/datadog/ci/span.rb
CHANGED
@@ -36,6 +36,30 @@ module Datadog
|
|
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!
|
data/lib/datadog/ci/test.rb
CHANGED
@@ -20,6 +20,36 @@ module Datadog
|
|
20
20
|
|
21
21
|
CI.deactivate_test(self)
|
22
22
|
end
|
23
|
+
|
24
|
+
# Running test suite that this test is part of (if any).
|
25
|
+
# @return [Datadog::CI::TestSuite] the test suite this test belongs to
|
26
|
+
# @return [nil] if the test suite is not found
|
27
|
+
def test_suite
|
28
|
+
suite_name = test_suite_name
|
29
|
+
CI.active_test_suite(suite_name) if suite_name
|
30
|
+
end
|
31
|
+
|
32
|
+
# Span id of the running test suite this test belongs to.
|
33
|
+
# @return [String] the span id of the test suite.
|
34
|
+
def test_suite_id
|
35
|
+
get_tag(Ext::Test::TAG_TEST_SUITE_ID)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_suite_name
|
39
|
+
get_tag(Ext::Test::TAG_SUITE)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Span id of the running test module this test belongs to.
|
43
|
+
# @return [String] the span id of the test module.
|
44
|
+
def test_module_id
|
45
|
+
get_tag(Ext::Test::TAG_TEST_MODULE_ID)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Span id of the running test module this test belongs to.
|
49
|
+
# @return [String] the span id of the test session.
|
50
|
+
def test_session_id
|
51
|
+
get_tag(Ext::Test::TAG_TEST_SESSION_ID)
|
52
|
+
end
|
23
53
|
end
|
24
54
|
end
|
25
55
|
end
|
@@ -20,6 +20,14 @@ module Datadog
|
|
20
20
|
CI.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
|
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module CI
|
5
|
+
module TestVisibility
|
6
|
+
module Context
|
7
|
+
# This context is shared between threads and represents the current test session and test module.
|
8
|
+
class Global
|
9
|
+
def initialize
|
10
|
+
# we are using Monitor instead of Mutex because it is reentrant
|
11
|
+
@mutex = Monitor.new
|
12
|
+
|
13
|
+
@test_session = nil
|
14
|
+
@test_module = nil
|
15
|
+
@test_suites = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch_or_activate_test_suite(test_suite_name, &block)
|
19
|
+
@mutex.synchronize do
|
20
|
+
@test_suites[test_suite_name] ||= block.call
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch_or_activate_test_module(&block)
|
25
|
+
@mutex.synchronize do
|
26
|
+
@test_module ||= block.call
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def fetch_or_activate_test_session(&block)
|
31
|
+
@mutex.synchronize do
|
32
|
+
@test_session ||= block.call
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def active_test_module
|
37
|
+
@test_module
|
38
|
+
end
|
39
|
+
|
40
|
+
def active_test_session
|
41
|
+
@test_session
|
42
|
+
end
|
43
|
+
|
44
|
+
def active_test_suite(test_suite_name)
|
45
|
+
@mutex.synchronize { @test_suites[test_suite_name] }
|
46
|
+
end
|
47
|
+
|
48
|
+
def service
|
49
|
+
@mutex.synchronize do
|
50
|
+
# thank you RBS for this weirdness
|
51
|
+
test_session = @test_session
|
52
|
+
test_session.service if test_session
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def inheritable_session_tags
|
57
|
+
@mutex.synchronize do
|
58
|
+
test_session = @test_session
|
59
|
+
if test_session
|
60
|
+
test_session.inheritable_tags
|
61
|
+
else
|
62
|
+
{}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def deactivate_test_session!
|
68
|
+
@mutex.synchronize { @test_session = nil }
|
69
|
+
end
|
70
|
+
|
71
|
+
def deactivate_test_module!
|
72
|
+
@mutex.synchronize { @test_module = nil }
|
73
|
+
end
|
74
|
+
|
75
|
+
def deactivate_test_suite!(test_suite_name)
|
76
|
+
@mutex.synchronize { @test_suites.delete(test_suite_name) }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module CI
|
5
|
+
module TestVisibility
|
6
|
+
module Context
|
7
|
+
class Local
|
8
|
+
def initialize
|
9
|
+
@key = :datadog_ci_active_test
|
10
|
+
|
11
|
+
self.active_test = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def activate_test!(test)
|
15
|
+
raise "Nested tests are not supported. Currently active test: #{active_test}" unless active_test.nil?
|
16
|
+
|
17
|
+
if block_given?
|
18
|
+
begin
|
19
|
+
self.active_test = test
|
20
|
+
yield
|
21
|
+
ensure
|
22
|
+
self.active_test = nil
|
23
|
+
end
|
24
|
+
else
|
25
|
+
self.active_test = test
|
26
|
+
end
|
27
|
+
end
|
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
|
37
|
+
end
|
38
|
+
|
39
|
+
def active_test
|
40
|
+
Thread.current[@key]
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def active_test=(test)
|
46
|
+
Thread.current[@key] = test
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,73 @@
|
|
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(span_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
|
+
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
|
+
private
|
58
|
+
|
59
|
+
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
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|