Skip to content

Commit

Permalink
add test session public API
Browse files Browse the repository at this point in the history
  • Loading branch information
anmarchenko committed Nov 22, 2023
1 parent a1d2e4b commit 750b30a
Show file tree
Hide file tree
Showing 17 changed files with 385 additions and 42 deletions.
53 changes: 53 additions & 0 deletions lib/datadog/ci.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,54 @@ 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 :
# ```
# 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}.
#
# @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.
#
Expand Down Expand Up @@ -189,6 +237,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
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
1 change: 1 addition & 0 deletions lib/datadog/ci/ext/app_types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module CI
module Ext
module AppTypes
TYPE_TEST = "test"
TYPE_TEST_SESSION = "test_session_end"
end
end
end
Expand Down
61 changes: 47 additions & 14 deletions lib/datadog/ci/recorder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
require_relative "ext/test"
require_relative "ext/environment"

require_relative "context/global"
require_relative "context/local"

require_relative "span"
require_relative "test"
require_relative "test_session"

module Datadog
module CI
Expand All @@ -22,6 +24,24 @@ class Recorder
def initialize
@environment_tags = Ext::Environment.tags(ENV).freeze
@local_context = Context::Local.new
@global_context = Context::Global.new
end

def start_test_session(service_name: nil, tags: {})
span_options = {
service: service_name,
span_type: Ext::AppTypes::TYPE_TEST_SESSION
}

tracer_span = Datadog::Tracing.trace("test.session", **span_options)
trace = Datadog::Tracing.active_trace

set_trace_origin(trace)

test_session = build_test_session(tracer_span, tags)
@global_context.activate_test_session!(test_session)

test_session
end

# Creates a new span for a CI test
Expand Down Expand Up @@ -73,17 +93,26 @@ def trace(span_type, span_name, tags: {}, &block)
end
end

def active_span
tracer_span = Datadog::Tracing.active_span
Span.new(tracer_span) if tracer_span
end

def active_test
@local_context.active_test
end

def active_test_session
@global_context.active_test_session
end

# TODO: does it make sense to have a paramter here?
def deactivate_test(test)
@local_context.deactivate_test!(test)
end

def active_span
tracer_span = Datadog::Tracing.active_span
Span.new(tracer_span) if tracer_span
def deactivate_test_session
@global_context.deactivate_test_session!
end

private
Expand All @@ -93,26 +122,30 @@ def set_trace_origin(trace)
trace.origin = Ext::Test::CONTEXT_ORIGIN if trace
end

def build_test_session(tracer_span, tags)
test_session = TestSession.new(tracer_span)
set_initial_tags(test_session, tags)
test_session
end

def build_test(tracer_span, tags)
test = Test.new(tracer_span)

test.set_default_tags
test.set_environment_runtime_tags

test.set_tags(tags)
test.set_tags(environment_tags)

set_initial_tags(test, tags)
test
end

def build_span(tracer_span, tags)
span = Span.new(tracer_span)
set_initial_tags(span, tags)
span
end

span.set_default_tags
span.set_environment_runtime_tags
span.set_tags(tags)
def set_initial_tags(ci_span, tags)
ci_span.set_default_tags
ci_span.set_environment_runtime_tags

span
ci_span.set_tags(tags)
ci_span.set_tags(environment_tags)
end
end
end
Expand Down
1 change: 0 additions & 1 deletion lib/datadog/ci/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
module Datadog
module CI
# Represents a single part of a test run.
# Could be a session, suite, test, or any custom span.
#
# @public_api
class Test < Span
Expand Down
27 changes: 27 additions & 0 deletions lib/datadog/ci/test_session.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

require_relative "span"

module Datadog
module CI
# Represents the whole test session process.
# This object can be shared between multiple threads.
#
# @public_api
class TestSession < Span
def initialize(tracer_span)
super

@mutex = Mutex.new
end

# Finishes the current test session.
# @return [void]
def finish
super

CI.deactivate_test_session
end
end
end
end
2 changes: 1 addition & 1 deletion lib/datadog/ci/test_visibility/serializers/test_v1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def content_map_size
end

def type
"test"
Ext::AppTypes::TYPE_TEST
end

def name
Expand Down
6 changes: 6 additions & 0 deletions sig/datadog/ci.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ module Datadog

def self.start_test: (String span_name, ?service_name: String?, ?operation_name: String, ?tags: Hash[untyped, untyped]) -> Datadog::CI::Test

def self.start_test_session: (?service_name: String?, ?tags: Hash[untyped, untyped]) -> Datadog::CI::TestSession

def self.trace: (String span_type, String span_name, ?tags: Hash[untyped, untyped]) ?{ (Datadog::CI::Span span) -> untyped } -> untyped

def self.active_test_session: () -> Datadog::CI::TestSession?

def self.active_test: () -> Datadog::CI::Test?

def self.active_span: (String span_type) -> Datadog::CI::Span?

def self.deactivate_test: (Datadog::CI::Test test) -> void

def self.deactivate_test_session: () -> void

def self.components: () -> Datadog::CI::Configuration::Components

def self.recorder: () -> Datadog::CI::Recorder
Expand Down
19 changes: 19 additions & 0 deletions sig/datadog/ci/context/global.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Datadog
module CI
module Context
class Global
@mutex: Thread::Mutex

@test_session: Datadog::CI::TestSession?

def initialize: () -> void

def active_test_session: () -> Datadog::CI::TestSession?

def activate_test_session!: (Datadog::CI::TestSession test_session) -> void

def deactivate_test_session!: () -> void
end
end
end
end
3 changes: 2 additions & 1 deletion sig/datadog/ci/ext/app_types.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ module Datadog
module CI
module Ext
module AppTypes
TYPE_TEST: String
TYPE_TEST: "test"
TYPE_TEST_SESSION: "test_session_end"
end
end
end
Expand Down
11 changes: 11 additions & 0 deletions sig/datadog/ci/recorder.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,26 @@ module Datadog
class Recorder
@environment_tags: Hash[String, String]
@local_context: Datadog::CI::Context::Local
@global_context: Datadog::CI::Context::Global

attr_reader environment_tags: Hash[String, String]

def trace_test: (String span_name, ?service_name: String?, ?operation_name: String, ?tags: Hash[untyped, untyped]) ?{ (Datadog::CI::Test span) -> untyped } -> untyped

def trace: (String span_type, String span_name, ?tags: Hash[untyped, untyped]) ?{ (Datadog::CI::Span span) -> untyped } -> untyped

def start_test_session: (?service_name: String?, ?tags: Hash[untyped, untyped]) -> Datadog::CI::TestSession

def active_test_session: () -> Datadog::CI::TestSession?

def active_test: () -> Datadog::CI::Test?

def active_span: () -> Datadog::CI::Span?

def deactivate_test: (Datadog::CI::Test test) -> void

def deactivate_test_session: () -> void

def create_datadog_span: (String span_name, ?span_options: Hash[untyped, untyped], ?tags: Hash[untyped, untyped]) ?{ (Datadog::CI::Span span) -> untyped } -> untyped

def set_trace_origin: (Datadog::Tracing::TraceOperation trace) -> untyped
Expand All @@ -24,7 +31,11 @@ module Datadog

def build_test: (Datadog::Tracing::SpanOperation tracer_span, Hash[untyped, untyped] tags) -> Datadog::CI::Test

def build_test_session: (Datadog::Tracing::SpanOperation tracer_span, Hash[untyped, untyped] tags) -> Datadog::CI::TestSession

def build_span: (Datadog::Tracing::SpanOperation tracer_span, Hash[untyped, untyped] tags) -> Datadog::CI::Span

def set_initial_tags: (Datadog::CI::Span ci_span, Hash[untyped, untyped] tags) -> void
end
end
end
7 changes: 7 additions & 0 deletions sig/datadog/ci/test_session.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Datadog
module CI
class TestSession < Span
@mutex: Thread::Mutex
end
end
end
2 changes: 1 addition & 1 deletion sig/datadog/ci/test_visibility/serializers/test_v1.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Datadog
def content_fields: () -> Array[String | Hash[String, String]]
def content_map_size: () -> Integer

def type: () -> "test"
def type: () -> ::String

def name: () -> ::String

Expand Down
Loading

0 comments on commit 750b30a

Please sign in to comment.