Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CIVIS-2844] Add test session public API #72

Merged
merged 16 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions .standard_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
# Remove from this list as you refactor files.
---
ignore:
- lib/datadog/ci/contrib/minitest/integration.rb:
- Style/SafeNavigation
- lib/datadog/ci/contrib/cucumber/integration.rb:
- Style/SafeNavigation
- lib/datadog/ci/contrib/rspec/integration.rb:
- Style/SafeNavigation
- lib/datadog/ci/ext/environment.rb:
- Style/SafeNavigation
- spec/support/log_helpers.rb:
- Performance/UnfreezeString
- Appraisals:
- Style/Alias
- lib/datadog/ci/test_visibility/serializers/base.rb:
- Style/HashExcept
- lib/datadog/ci/contrib/minitest/integration.rb:
- Style/SafeNavigation
- lib/datadog/ci/contrib/cucumber/integration.rb:
- Style/SafeNavigation
- lib/datadog/ci/contrib/rspec/integration.rb:
- Style/SafeNavigation
- lib/datadog/ci/ext/environment.rb:
- Style/SafeNavigation
- spec/support/log_helpers.rb:
- Performance/UnfreezeString
- Appraisals:
- Style/Alias
56 changes: 56 additions & 0 deletions lib/datadog/ci.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,59 @@ module Datadog
# @public_api
module CI
class << self
# Return a {Datadog::CI::TestSesstion ci_test_session} that represents the whole test session run.
# Raises an error if a session is already active.
#
# The {#start_test_session} method is used to mark the start of the test session:
# ```
# Datadog::CI.start_test_session(
# service: "my-web-site-tests",
# tags: { Datadog::CI::Ext::Test::TAG_FRAMEWORK => "my-test-framework" }
# )
#
# # Somewhere else after test run has ended
# Datadog::CI.active_test_session.finish
# ```
#
# Remember that calling {Datadog::CI::TestSession#finish} is mandatory.
#
# @param [String] service_name the service name for this session
# @param [Hash<String,String>] tags extra tags which should be added to the test.
# @return [Datadog::CI::TestSession] returns the active, running {Datadog::CI::TestSession}.
# @return [nil] if test suite level visibility is disabled (old Datadog agent detected)
#
# @public_api
def start_test_session(service_name: nil, tags: {})
recorder.start_test_session(service_name: service_name, tags: tags)
end

# The active, unfinished test session span.
#
# Usage:
#
# ```
# # start a test session
# Datadog::CI.start_test_session(
# service: "my-web-site-tests",
# tags: { Datadog::CI::Ext::Test::TAG_FRAMEWORK => "my-test-framework" }
# )
#
# # somewhere else, access the session
# test_session = Datadog::CI.active_test_session
# test_session.finish
# ```
#
# @return [Datadog::CI::TestSession] the active test session
# @return [nil] if no test session is active
def active_test_session
recorder.active_test_session
end

# Return a {Datadog::CI::Test ci_test} that will trace a test called `test_name`.
# Raises an error if a test is already active.
# If there is an active test session, the new test will be connected to the session.
# The test will inherit service name and tags from the running test session if not provided
# in parameters.
#
# You could trace your test using a <tt>do-block</tt> like:
#
Expand Down Expand Up @@ -189,6 +240,11 @@ def deactivate_test(test)
recorder.deactivate_test(test)
end

# Internal only, to finish a test session use Datadog::CI::TestSession#finish
def deactivate_test_session
recorder.deactivate_test_session
end

private

def components
Expand Down
59 changes: 59 additions & 0 deletions lib/datadog/ci/concurrent_span.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# frozen_string_literal: true

require_relative "span"

module Datadog
module CI
# Represents a single part of a test run that can be safely shared between threads.
# Examples of shared objects are: TestSession, TestModule, TestSpan.
#
# @public_api
class ConcurrentSpan < Span
def initialize(tracer_span)
super

@mutex = Mutex.new
end

# Gets tag value by key. This method is thread-safe.
# @param [String] key the key of the tag.
# @return [String] the value of the tag.
def get_tag(key)
synchronize { super }
end

# Sets tag value by key. This method is thread-safe.
# @param [String] key the key of the tag.
# @param [String] value the value of the tag.
# @return [void]
def set_tag(key, value)
synchronize { super }
end

# Sets metric value by key. This method is thread-safe.
# @param [String] key the key of the metric.
# @param [Numeric] value the value of the metric.
# @return [void]
def set_metric(key, value)
synchronize { super }
end

# Finishes the span. This method is thread-safe.
# @return [void]
def finish
synchronize { super }
end

# Sets multiple tags at once. This method is thread-safe.
# @param [Hash[String, String]] tags the tags to set.
# @return [void]
def set_tags(tags)
synchronize { super }
end

def synchronize
@mutex.synchronize { yield }
end
end
end
end
18 changes: 16 additions & 2 deletions lib/datadog/ci/configuration/components.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
require_relative "../ext/transport"
require_relative "../test_visibility/flush"
require_relative "../test_visibility/transport"
require_relative "../test_visibility/serializers/factories/test_level"
require_relative "../test_visibility/serializers/factories/test_suite_level"
require_relative "../transport/api/builder"
require_relative "../recorder"

Expand Down Expand Up @@ -48,7 +50,7 @@ def activate_ci!(settings)
settings.tracing.test_mode.enabled = true

# Choose user defined TraceFlush or default to CI TraceFlush
settings.tracing.test_mode.trace_flush = settings.ci.trace_flush || CI::TestVisibility::Flush::Finished.new
settings.tracing.test_mode.trace_flush = settings.ci.trace_flush || CI::TestVisibility::Flush::Partial.new

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

settings.tracing.test_mode.writer_options = writer_options

@ci_recorder = Recorder.new
@ci_recorder = Recorder.new(
test_suite_level_visibility_enabled: settings.ci.experimental_test_suite_level_visibility_enabled
)
end

def can_use_evp_proxy?(settings, agent_settings)
Expand Down Expand Up @@ -89,6 +93,7 @@ def build_agentless_transport(settings)

Datadog::CI::TestVisibility::Transport.new(
api: Transport::Api::Builder.build_ci_test_cycle_api(settings),
serializers_factory: serializers_factory(settings),
dd_env: settings.env
)
end
Expand All @@ -99,9 +104,18 @@ def build_evp_proxy_transport(settings, agent_settings)

Datadog::CI::TestVisibility::Transport.new(
api: Transport::Api::Builder.build_evp_proxy_api(agent_settings),
serializers_factory: serializers_factory(settings),
dd_env: settings.env
)
end

def serializers_factory(settings)
if settings.ci.experimental_test_suite_level_visibility_enabled
Datadog::CI::TestVisibility::Serializers::Factories::TestSuiteLevel
else
Datadog::CI::TestVisibility::Serializers::Factories::TestLevel
end
end
end
end
end
Expand Down
6 changes: 6 additions & 0 deletions lib/datadog/ci/configuration/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ def self.add_settings!(base)
o.env CI::Ext::Settings::ENV_AGENTLESS_URL
end

option :experimental_test_suite_level_visibility_enabled do |o|
o.type :bool
o.env CI::Ext::Settings::ENV_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED
o.default false
end

define_method(:instrument) do |integration_name, options = {}, &block|
return unless enabled

Expand Down
31 changes: 31 additions & 0 deletions lib/datadog/ci/context/global.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module Datadog
module CI
module Context
# This context is shared between threads and represents the current test session.
class Global
def initialize
@mutex = Mutex.new
@test_session = nil
end

def active_test_session
@test_session
end

def activate_test_session!(test_session)
@mutex.synchronize do
raise "Nested test sessions are not supported. Currently active test session: #{@test_session}" unless @test_session.nil?

@test_session = test_session
end
end

def deactivate_test_session!
@mutex.synchronize { @test_session = nil }
end
end
end
end
end
3 changes: 3 additions & 0 deletions lib/datadog/ci/ext/app_types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ module CI
module Ext
module AppTypes
TYPE_TEST = "test"
TYPE_TEST_SESSION = "test_session_end"

CI_SPAN_TYPES = [TYPE_TEST, TYPE_TEST_SESSION].freeze
end
end
end
Expand Down
1 change: 1 addition & 0 deletions lib/datadog/ci/ext/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module Settings
ENV_MODE_ENABLED = "DD_TRACE_CI_ENABLED"
ENV_AGENTLESS_MODE_ENABLED = "DD_CIVISIBILITY_AGENTLESS_ENABLED"
ENV_AGENTLESS_URL = "DD_CIVISIBILITY_AGENTLESS_URL"
ENV_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED = "DD_CIVISIBILITY_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED"
end
end
end
Expand Down
8 changes: 8 additions & 0 deletions lib/datadog/ci/ext/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ module Test
TAG_SUITE = "test.suite"
TAG_TRAITS = "test.traits"
TAG_TYPE = "test.type"
TAG_COMMAND = "test.command"

# those tags are special and they are used to conrrelate tests with the test sessions, suites, and modules
TAG_TEST_SESSION_ID = "_test.session_id"
SPECIAL_TAGS = [TAG_TEST_SESSION_ID].freeze

# tags that can be inherited from the test session
INHERITABLE_TAGS = [TAG_FRAMEWORK, TAG_FRAMEWORK_VERSION, TAG_TYPE].freeze

# Environment runtime tags
TAG_OS_ARCHITECTURE = "os.architecture"
Expand Down
Loading