Skip to content

Commit 8d8cead

Browse files
authored
Merge pull request #72 from DataDog/anmarchenko/test_session_tracing
[CIVIS-2844] Add test session public API
2 parents cf121f3 + d656b42 commit 8d8cead

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1452
-107
lines changed

.standard_todo.yml

+14-12
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
# Remove from this list as you refactor files.
33
---
44
ignore:
5-
- lib/datadog/ci/contrib/minitest/integration.rb:
6-
- Style/SafeNavigation
7-
- lib/datadog/ci/contrib/cucumber/integration.rb:
8-
- Style/SafeNavigation
9-
- lib/datadog/ci/contrib/rspec/integration.rb:
10-
- Style/SafeNavigation
11-
- lib/datadog/ci/ext/environment.rb:
12-
- Style/SafeNavigation
13-
- spec/support/log_helpers.rb:
14-
- Performance/UnfreezeString
15-
- Appraisals:
16-
- Style/Alias
5+
- lib/datadog/ci/test_visibility/serializers/base.rb:
6+
- Style/HashExcept
7+
- lib/datadog/ci/contrib/minitest/integration.rb:
8+
- Style/SafeNavigation
9+
- lib/datadog/ci/contrib/cucumber/integration.rb:
10+
- Style/SafeNavigation
11+
- lib/datadog/ci/contrib/rspec/integration.rb:
12+
- Style/SafeNavigation
13+
- lib/datadog/ci/ext/environment.rb:
14+
- Style/SafeNavigation
15+
- spec/support/log_helpers.rb:
16+
- Performance/UnfreezeString
17+
- Appraisals:
18+
- Style/Alias

lib/datadog/ci.rb

+56
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,59 @@ module Datadog
1010
# @public_api
1111
module CI
1212
class << self
13+
# Return a {Datadog::CI::TestSesstion ci_test_session} that represents the whole test session run.
14+
# Raises an error if a session is already active.
15+
#
16+
# The {#start_test_session} method is used to mark the start of the test session:
17+
# ```
18+
# Datadog::CI.start_test_session(
19+
# service: "my-web-site-tests",
20+
# tags: { Datadog::CI::Ext::Test::TAG_FRAMEWORK => "my-test-framework" }
21+
# )
22+
#
23+
# # Somewhere else after test run has ended
24+
# Datadog::CI.active_test_session.finish
25+
# ```
26+
#
27+
# Remember that calling {Datadog::CI::TestSession#finish} is mandatory.
28+
#
29+
# @param [String] service_name the service name for this session
30+
# @param [Hash<String,String>] tags extra tags which should be added to the test.
31+
# @return [Datadog::CI::TestSession] returns the active, running {Datadog::CI::TestSession}.
32+
# @return [nil] if test suite level visibility is disabled (old Datadog agent detected)
33+
#
34+
# @public_api
35+
def start_test_session(service_name: nil, tags: {})
36+
recorder.start_test_session(service_name: service_name, tags: tags)
37+
end
38+
39+
# The active, unfinished test session span.
40+
#
41+
# Usage:
42+
#
43+
# ```
44+
# # start a test session
45+
# Datadog::CI.start_test_session(
46+
# service: "my-web-site-tests",
47+
# tags: { Datadog::CI::Ext::Test::TAG_FRAMEWORK => "my-test-framework" }
48+
# )
49+
#
50+
# # somewhere else, access the session
51+
# test_session = Datadog::CI.active_test_session
52+
# test_session.finish
53+
# ```
54+
#
55+
# @return [Datadog::CI::TestSession] the active test session
56+
# @return [nil] if no test session is active
57+
def active_test_session
58+
recorder.active_test_session
59+
end
60+
1361
# Return a {Datadog::CI::Test ci_test} that will trace a test called `test_name`.
1462
# Raises an error if a test is already active.
63+
# If there is an active test session, the new test will be connected to the session.
64+
# The test will inherit service name and tags from the running test session if not provided
65+
# in parameters.
1566
#
1667
# You could trace your test using a <tt>do-block</tt> like:
1768
#
@@ -189,6 +240,11 @@ def deactivate_test(test)
189240
recorder.deactivate_test(test)
190241
end
191242

243+
# Internal only, to finish a test session use Datadog::CI::TestSession#finish
244+
def deactivate_test_session
245+
recorder.deactivate_test_session
246+
end
247+
192248
private
193249

194250
def components

lib/datadog/ci/concurrent_span.rb

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "span"
4+
5+
module Datadog
6+
module CI
7+
# Represents a single part of a test run that can be safely shared between threads.
8+
# Examples of shared objects are: TestSession, TestModule, TestSpan.
9+
#
10+
# @public_api
11+
class ConcurrentSpan < Span
12+
def initialize(tracer_span)
13+
super
14+
15+
@mutex = Mutex.new
16+
end
17+
18+
# Gets tag value by key. This method is thread-safe.
19+
# @param [String] key the key of the tag.
20+
# @return [String] the value of the tag.
21+
def get_tag(key)
22+
synchronize { super }
23+
end
24+
25+
# Sets tag value by key. This method is thread-safe.
26+
# @param [String] key the key of the tag.
27+
# @param [String] value the value of the tag.
28+
# @return [void]
29+
def set_tag(key, value)
30+
synchronize { super }
31+
end
32+
33+
# Sets metric value by key. This method is thread-safe.
34+
# @param [String] key the key of the metric.
35+
# @param [Numeric] value the value of the metric.
36+
# @return [void]
37+
def set_metric(key, value)
38+
synchronize { super }
39+
end
40+
41+
# Finishes the span. This method is thread-safe.
42+
# @return [void]
43+
def finish
44+
synchronize { super }
45+
end
46+
47+
# Sets multiple tags at once. This method is thread-safe.
48+
# @param [Hash[String, String]] tags the tags to set.
49+
# @return [void]
50+
def set_tags(tags)
51+
synchronize { super }
52+
end
53+
54+
def synchronize
55+
@mutex.synchronize { yield }
56+
end
57+
end
58+
end
59+
end

lib/datadog/ci/configuration/components.rb

+16-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
require_relative "../ext/transport"
77
require_relative "../test_visibility/flush"
88
require_relative "../test_visibility/transport"
9+
require_relative "../test_visibility/serializers/factories/test_level"
10+
require_relative "../test_visibility/serializers/factories/test_suite_level"
911
require_relative "../transport/api/builder"
1012
require_relative "../recorder"
1113

@@ -48,7 +50,7 @@ def activate_ci!(settings)
4850
settings.tracing.test_mode.enabled = true
4951

5052
# Choose user defined TraceFlush or default to CI TraceFlush
51-
settings.tracing.test_mode.trace_flush = settings.ci.trace_flush || CI::TestVisibility::Flush::Finished.new
53+
settings.tracing.test_mode.trace_flush = settings.ci.trace_flush || CI::TestVisibility::Flush::Partial.new
5254

5355
writer_options = settings.ci.writer_options
5456
if test_visibility_transport
@@ -60,7 +62,9 @@ def activate_ci!(settings)
6062

6163
settings.tracing.test_mode.writer_options = writer_options
6264

63-
@ci_recorder = Recorder.new
65+
@ci_recorder = Recorder.new(
66+
test_suite_level_visibility_enabled: settings.ci.experimental_test_suite_level_visibility_enabled
67+
)
6468
end
6569

6670
def can_use_evp_proxy?(settings, agent_settings)
@@ -89,6 +93,7 @@ def build_agentless_transport(settings)
8993

9094
Datadog::CI::TestVisibility::Transport.new(
9195
api: Transport::Api::Builder.build_ci_test_cycle_api(settings),
96+
serializers_factory: serializers_factory(settings),
9297
dd_env: settings.env
9398
)
9499
end
@@ -99,9 +104,18 @@ def build_evp_proxy_transport(settings, agent_settings)
99104

100105
Datadog::CI::TestVisibility::Transport.new(
101106
api: Transport::Api::Builder.build_evp_proxy_api(agent_settings),
107+
serializers_factory: serializers_factory(settings),
102108
dd_env: settings.env
103109
)
104110
end
111+
112+
def serializers_factory(settings)
113+
if settings.ci.experimental_test_suite_level_visibility_enabled
114+
Datadog::CI::TestVisibility::Serializers::Factories::TestSuiteLevel
115+
else
116+
Datadog::CI::TestVisibility::Serializers::Factories::TestLevel
117+
end
118+
end
105119
end
106120
end
107121
end

lib/datadog/ci/configuration/settings.rb

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ def self.add_settings!(base)
3434
o.env CI::Ext::Settings::ENV_AGENTLESS_URL
3535
end
3636

37+
option :experimental_test_suite_level_visibility_enabled do |o|
38+
o.type :bool
39+
o.env CI::Ext::Settings::ENV_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED
40+
o.default false
41+
end
42+
3743
define_method(:instrument) do |integration_name, options = {}, &block|
3844
return unless enabled
3945

lib/datadog/ci/context/global.rb

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# frozen_string_literal: true
2+
3+
module Datadog
4+
module CI
5+
module Context
6+
# This context is shared between threads and represents the current test session.
7+
class Global
8+
def initialize
9+
@mutex = Mutex.new
10+
@test_session = nil
11+
end
12+
13+
def active_test_session
14+
@test_session
15+
end
16+
17+
def activate_test_session!(test_session)
18+
@mutex.synchronize do
19+
raise "Nested test sessions are not supported. Currently active test session: #{@test_session}" unless @test_session.nil?
20+
21+
@test_session = test_session
22+
end
23+
end
24+
25+
def deactivate_test_session!
26+
@mutex.synchronize { @test_session = nil }
27+
end
28+
end
29+
end
30+
end
31+
end

lib/datadog/ci/ext/app_types.rb

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ module CI
55
module Ext
66
module AppTypes
77
TYPE_TEST = "test"
8+
TYPE_TEST_SESSION = "test_session_end"
9+
10+
CI_SPAN_TYPES = [TYPE_TEST, TYPE_TEST_SESSION].freeze
811
end
912
end
1013
end

lib/datadog/ci/ext/settings.rb

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module Settings
88
ENV_MODE_ENABLED = "DD_TRACE_CI_ENABLED"
99
ENV_AGENTLESS_MODE_ENABLED = "DD_CIVISIBILITY_AGENTLESS_ENABLED"
1010
ENV_AGENTLESS_URL = "DD_CIVISIBILITY_AGENTLESS_URL"
11+
ENV_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED = "DD_CIVISIBILITY_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED"
1112
end
1213
end
1314
end

lib/datadog/ci/ext/test.rb

+8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ module Test
1616
TAG_SUITE = "test.suite"
1717
TAG_TRAITS = "test.traits"
1818
TAG_TYPE = "test.type"
19+
TAG_COMMAND = "test.command"
20+
21+
# those tags are special and they are used to conrrelate tests with the test sessions, suites, and modules
22+
TAG_TEST_SESSION_ID = "_test.session_id"
23+
SPECIAL_TAGS = [TAG_TEST_SESSION_ID].freeze
24+
25+
# tags that can be inherited from the test session
26+
INHERITABLE_TAGS = [TAG_FRAMEWORK, TAG_FRAMEWORK_VERSION, TAG_TYPE].freeze
1927

2028
# Environment runtime tags
2129
TAG_OS_ARCHITECTURE = "os.architecture"

0 commit comments

Comments
 (0)