diff --git a/lib/datadog/ci.rb b/lib/datadog/ci.rb index 55c14c74..2d56b52d 100644 --- a/lib/datadog/ci.rb +++ b/lib/datadog/ci.rb @@ -10,33 +10,179 @@ module Datadog # @public_api module CI class << self - # Trace a test run + # Return a {Datadog::CI::Test ci_test} that will trace a test called `test_name`. + # + # You could trace your test using a do-block like: + # + # ``` + # Datadog::CI.trace_test( + # "test_add_two_numbers", + # service_name: "my-web-site-tests", + # operation_name: "test", + # tags: { Datadog::CI::Ext::Test::TAG_FRAMEWORK => "my-test-framework" } + # ) do |ci_test| + # result = run_test + # + # if result.ok? + # ci_test.passed! + # else + # ci_test.failed!(exception: result.exception) + # end + # end + # ``` + # + # The {#trace_test} method can also be used without a block in this way: + # ``` + # ci_test = Datadog::CI.trace_test( + # "test_add_two_numbers', + # service: "my-web-site-tests", + # operation_name: "test", + # tags: { Datadog::CI::Ext::Test::TAG_FRAMEWORK => "my-test-framework" } + # ) + # run_test + # ci_test.finish + # ``` + # + # Remember that in this case, calling {Datadog::CI::Test#finish} is mandatory. + # + # @param [String] test_name {Datadog::CI::Test} name (example: "test_add_two_numbers"). + # @param [String] operation_name defines label for a test span in trace view ("test" if it's missing) + # @param [String] service_name the service name for this test + # @param [Hash] tags extra tags which should be added to the test. + # @return [Object] If a block is provided, returns the result of the block execution. + # @return [Datadog::CI::Test] If no block is provided, returns the active, + # unfinished {Datadog::CI::Test}. + # @yield Optional block where new newly created {Datadog::CI::Test} captures the execution. + # @yieldparam [Datadog::CI::Test] ci_test the newly created and active [Datadog::CI::Test] + # # @public_api def trace_test(test_name, service_name: nil, operation_name: "test", tags: {}, &block) recorder.trace_test(test_name, service_name: service_name, operation_name: operation_name, tags: tags, &block) end - # Start a test run trace. + # Same as {#trace_test} but it does not accept a block. + # + # Usage: + # + # ``` + # ci_test = Datadog::CI.start_test( + # "test_add_two_numbers', + # service: "my-web-site-tests", + # operation_name: "test", + # tags: { Datadog::CI::Ext::Test::TAG_FRAMEWORK => "my-test-framework" } + # ) + # run_test + # ci_test.finish + # ``` + # + # @param [String] test_name {Datadog::CI::Test} name (example: "test_add_two_numbers"). + # @param [String] operation_name the resource this span refers, or `test` if it's missing + # @param [String] service_name the service name for this span. + # @param [Hash] tags extra tags which should be added to the test. + # @return [Datadog::CI::Test] Returns the active, unfinished {Datadog::CI::Test}. + # # @public_api def start_test(test_name, service_name: nil, operation_name: "test", tags: {}) recorder.trace_test(test_name, service_name: service_name, operation_name: operation_name, tags: tags) end - # Trace any custom span + # Trace any custom span inside a test. For example, you could trace: + # - cucumber step + # - database query + # - any custom operation you want to see in your trace view + # + # You can use thi method with a do-block like: + # + # ``` + # Datadog::CI.trace( + # "step", + # "Given I have 42 cucumbers", + # tags: {} + # ) do + # run_operation + # end + # ``` + # + # The {#trace} method can also be used without a block in this way: + # ``` + # ci_span = Datadog::CI.trace( + # "step", + # "Given I have 42 cucumbers", + # tags: {} + # ) + # run_test + # ci_span.finish + # ``` + # Remember that in this case, calling {Datadog::CI::Span#finish} is mandatory. + # + # @param [String] span_type custom, user-defined span type (for example "step" or "query"). + # @param [String] span_name the resource this span refers, or `test` if it's missing + # @param [Hash] tags extra tags which should be added to the span. + # @return [Object] If a block is provided, returns the result of the block execution. + # @return [Datadog::CI::Span] If no block is provided, returns the active, + # unfinished {Datadog::CI::Span}. + # @yield Optional block where new newly created {Datadog::CI::Span} captures the execution. + # @yieldparam [Datadog::CI::Span] ci_span the newly created and active [Datadog::CI::Span] + # # @public_api def trace(span_type, span_name, tags: {}, &block) recorder.trace(span_type, span_name, tags: tags, &block) end + # The active, unfinished custom span if it matches given type. + # If no span is active, or if the active span is not a custom span with given type, returns nil. + # + # The active span belongs to an {.active_test}. + # + # Usage: + # + # ``` + # # start span + # Datadog::CI.trace( + # "step", + # "Given I have 42 cucumbers", + # tags: {} + # ) + # + # # somewhere else, access the active "step" span + # step_span = Datadog::CI.active_span("step") + # step_span.finish() + # ``` + # + # @param [String] span_type type of the span to retrieve (for example "step" or "query") that was provided to {.trace} + # @return [Datadog::CI::Span] the active span + # @return [nil] if no span is active, or if the active span is not a custom span with given type def active_span(span_type) span = recorder.active_span span if span && span.span_type == span_type end + # The active, unfinished test span. + # + # Usage: + # + # ``` + # # start a test + # Datadog::CI.start_test( + # "test_add_two_numbers', + # service: "my-web-site-tests", + # operation_name: "test", + # tags: { Datadog::CI::Ext::Test::TAG_FRAMEWORK => "my-test-framework" } + # ) + # + # # somewhere else, access the active test + # test_span = Datadog::CI.active_test + # test_span.passed! + # test_span.finish + # ``` + # + # @return [Datadog::CI::Test] the active test + # @return [nil] if no test is active def active_test recorder.active_test end + # Internal only, to finish a test use Datadog::CI::Test#finish def deactivate_test(test) recorder.deactivate_test(test) end diff --git a/lib/datadog/ci/span.rb b/lib/datadog/ci/span.rb index 7dbfb4e0..19e45a85 100644 --- a/lib/datadog/ci/span.rb +++ b/lib/datadog/ci/span.rb @@ -3,7 +3,6 @@ require_relative "ext/test" module Datadog - # Public API for Datadog CI visibility module CI # Represents a single part of a test run. # Could be a session, suite, test, or any custom span. @@ -16,46 +15,73 @@ def initialize(tracer_span) @tracer_span = tracer_span end + # @return [String] the name of the span. def name tracer_span.name end + # @return [String] the type of the span (for example "test" or type that was provided to [Datadog::CI.trace]). + def span_type + tracer_span.type + end + + # Sets the status of the span to "pass". + # @return [void] def passed! tracer_span.set_tag(Ext::Test::TAG_STATUS, Ext::Test::Status::PASS) end + # Sets the status of the span to "fail". + # @param [Exception] exception the exception that caused the test to fail. + # @return [void] def failed!(exception: nil) tracer_span.status = 1 tracer_span.set_tag(Ext::Test::TAG_STATUS, Ext::Test::Status::FAIL) tracer_span.set_error(exception) unless exception.nil? end + # Sets the status of the span to "skip". + # @param [Exception] exception the exception that caused the test to fail. + # @param [String] reason the reason why the test was skipped. + # @return [void] def skipped!(exception: nil, reason: nil) tracer_span.set_tag(Ext::Test::TAG_STATUS, Ext::Test::Status::SKIP) tracer_span.set_error(exception) unless exception.nil? tracer_span.set_tag(Ext::Test::TAG_SKIP_REASON, reason) unless reason.nil? end + # Gets tag value by key. + # @param [String] key the key of the tag. + # @return [String] the value of the tag. def get_tag(key) tracer_span.get_tag(key) end + # Sets tag value by key. + # @param [String] key the key of the tag. + # @param [String] value the value of the tag. + # @return [void] def set_tag(key, value) tracer_span.set_tag(key, value) end + # Sets metric value by key. + # @param [String] key the key of the metric. + # @param [Numeric] value the value of the metric. + # @return [void] def set_metric(key, value) tracer_span.set_metric(key, value) end + # Finishes the span. + # @return [void] def finish tracer_span.finish end - def span_type - tracer_span.type - end - + # Sets multiple tags at once. + # @param [Hash[String, String]] tags the tags to set. + # @return [void] def set_tags(tags) tags.each do |key, value| tracer_span.set_tag(key, value) diff --git a/lib/datadog/ci/test.rb b/lib/datadog/ci/test.rb index 2ab77b62..6cf43de2 100644 --- a/lib/datadog/ci/test.rb +++ b/lib/datadog/ci/test.rb @@ -4,16 +4,23 @@ 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 + # @return [String] the name of the test. + def name + get_tag(Ext::Test::TAG_NAME) + end + + # Finishes the current test. + # @return [void] def finish super CI.deactivate_test(self) end - - def name - get_tag(Ext::Test::TAG_NAME) - end end end end