datadog-ci 0.3.0 → 0.5.1
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 +89 -1
 - data/README.md +57 -34
 - data/lib/datadog/ci/concurrent_span.rb +59 -0
 - data/lib/datadog/ci/configuration/components.rb +40 -1
 - data/lib/datadog/ci/configuration/extensions.rb +21 -0
 - data/lib/datadog/ci/configuration/settings.rb +7 -1
 - data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +12 -1
 - data/lib/datadog/ci/contrib/cucumber/formatter.rb +22 -29
 - data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +10 -1
 - data/lib/datadog/ci/contrib/minitest/hooks.rb +15 -25
 - data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +10 -1
 - data/lib/datadog/ci/contrib/rspec/example.rb +13 -18
 - data/lib/datadog/ci/contrib/settings.rb +2 -0
 - data/lib/datadog/ci/ext/app_types.rb +5 -0
 - data/lib/datadog/ci/ext/environment/extractor.rb +5 -10
 - data/lib/datadog/ci/ext/environment/providers/github_actions.rb +13 -4
 - data/lib/datadog/ci/ext/settings.rb +11 -0
 - data/lib/datadog/ci/ext/test.rb +12 -1
 - data/lib/datadog/ci/null_span.rb +63 -0
 - data/lib/datadog/ci/span.rb +117 -0
 - data/lib/datadog/ci/test.rb +25 -0
 - data/lib/datadog/ci/test_module.rb +23 -0
 - data/lib/datadog/ci/test_session.rb +36 -0
 - data/lib/datadog/ci/test_suite.rb +25 -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/recorder.rb +293 -0
 - data/lib/datadog/ci/test_visibility/serializers/base.rb +100 -12
 - data/lib/datadog/ci/test_visibility/serializers/factories/test_suite_level.rb +37 -0
 - data/lib/datadog/ci/test_visibility/serializers/span.rb +2 -16
 - data/lib/datadog/ci/test_visibility/serializers/test_module.rb +46 -0
 - data/lib/datadog/ci/test_visibility/serializers/test_session.rb +46 -0
 - data/lib/datadog/ci/test_visibility/serializers/test_suite.rb +46 -0
 - data/lib/datadog/ci/test_visibility/serializers/test_v1.rb +3 -17
 - data/lib/datadog/ci/test_visibility/serializers/test_v2.rb +38 -0
 - data/lib/datadog/ci/test_visibility/transport.rb +4 -4
 - data/lib/datadog/ci/utils/test_run.rb +15 -0
 - data/lib/datadog/ci/utils/url.rb +15 -0
 - data/lib/datadog/ci/version.rb +2 -2
 - data/lib/datadog/ci.rb +378 -7
 - data/sig/datadog/ci/concurrent_span.rbs +23 -0
 - data/sig/datadog/ci/configuration/components.rbs +6 -0
 - data/sig/datadog/ci/configuration/extensions.rbs +9 -0
 - data/sig/datadog/ci/ext/app_types.rbs +6 -1
 - data/sig/datadog/ci/ext/environment/extractor.rbs +0 -2
 - data/sig/datadog/ci/ext/environment/providers/github_actions.rbs +5 -0
 - data/sig/datadog/ci/ext/settings.rbs +3 -0
 - data/sig/datadog/ci/ext/test.rbs +15 -0
 - data/sig/datadog/ci/null_span.rbs +37 -0
 - data/sig/datadog/ci/span.rbs +39 -0
 - data/sig/datadog/ci/test.rbs +7 -0
 - data/sig/datadog/ci/test_module.rbs +6 -0
 - data/sig/datadog/ci/test_session.rbs +9 -0
 - data/sig/datadog/ci/test_suite.rbs +6 -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/recorder.rbs +85 -0
 - data/sig/datadog/ci/test_visibility/serializers/base.rbs +26 -5
 - data/sig/datadog/ci/test_visibility/serializers/factories/test_suite_level.rbs +13 -0
 - data/sig/datadog/ci/test_visibility/serializers/test_module.rbs +26 -0
 - data/sig/datadog/ci/test_visibility/serializers/test_session.rbs +26 -0
 - data/sig/datadog/ci/test_visibility/serializers/test_suite.rbs +26 -0
 - data/sig/datadog/ci/test_visibility/serializers/test_v1.rbs +1 -1
 - data/sig/datadog/ci/test_visibility/serializers/test_v2.rbs +25 -0
 - data/sig/datadog/ci/test_visibility/transport.rbs +3 -3
 - data/sig/datadog/ci/utils/test_run.rbs +11 -0
 - data/sig/datadog/ci/utils/url.rbs +9 -0
 - data/sig/datadog/ci.rbs +31 -3
 - metadata +38 -6
 - data/lib/datadog/ci/extensions.rb +0 -19
 - data/lib/datadog/ci/recorder.rb +0 -83
 - data/sig/datadog/ci/extensions.rbs +0 -7
 - data/sig/datadog/ci/recorder.rbs +0 -18
 
| 
         @@ -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,293 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "datadog/tracing"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "datadog/tracing/trace_digest"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require "rbconfig"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require_relative "context/global"
         
     | 
| 
      
 9 
     | 
    
         
            +
            require_relative "context/local"
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            require_relative "../ext/app_types"
         
     | 
| 
      
 12 
     | 
    
         
            +
            require_relative "../ext/test"
         
     | 
| 
      
 13 
     | 
    
         
            +
            require_relative "../ext/environment"
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            require_relative "../span"
         
     | 
| 
      
 16 
     | 
    
         
            +
            require_relative "../null_span"
         
     | 
| 
      
 17 
     | 
    
         
            +
            require_relative "../test"
         
     | 
| 
      
 18 
     | 
    
         
            +
            require_relative "../test_session"
         
     | 
| 
      
 19 
     | 
    
         
            +
            require_relative "../test_module"
         
     | 
| 
      
 20 
     | 
    
         
            +
            require_relative "../test_suite"
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            module Datadog
         
     | 
| 
      
 23 
     | 
    
         
            +
              module CI
         
     | 
| 
      
 24 
     | 
    
         
            +
                module TestVisibility
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # Common behavior for CI tests
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # Note: this class has too many responsibilities and should be split into multiple classes
         
     | 
| 
      
 27 
     | 
    
         
            +
                  class Recorder
         
     | 
| 
      
 28 
     | 
    
         
            +
                    attr_reader :environment_tags, :test_suite_level_visibility_enabled, :enabled
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    def initialize(enabled: true, test_suite_level_visibility_enabled: false)
         
     | 
| 
      
 31 
     | 
    
         
            +
                      @enabled = enabled
         
     | 
| 
      
 32 
     | 
    
         
            +
                      @test_suite_level_visibility_enabled = enabled && test_suite_level_visibility_enabled
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                      @environment_tags = @enabled ? Ext::Environment.tags(ENV).freeze : {}
         
     | 
| 
      
 35 
     | 
    
         
            +
                      @local_context = Context::Local.new
         
     | 
| 
      
 36 
     | 
    
         
            +
                      @global_context = Context::Global.new
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    def start_test_session(service: nil, tags: {})
         
     | 
| 
      
 40 
     | 
    
         
            +
                      return skip_tracing unless test_suite_level_visibility_enabled
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                      @global_context.fetch_or_activate_test_session do
         
     | 
| 
      
 43 
     | 
    
         
            +
                        tracer_span = start_datadog_tracer_span(
         
     | 
| 
      
 44 
     | 
    
         
            +
                          "test.session", build_span_options(service, Ext::AppTypes::TYPE_TEST_SESSION)
         
     | 
| 
      
 45 
     | 
    
         
            +
                        )
         
     | 
| 
      
 46 
     | 
    
         
            +
                        set_session_context(tags, tracer_span)
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                        build_test_session(tracer_span, tags)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    def start_test_module(test_module_name, service: nil, tags: {})
         
     | 
| 
      
 53 
     | 
    
         
            +
                      return skip_tracing unless test_suite_level_visibility_enabled
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                      @global_context.fetch_or_activate_test_module do
         
     | 
| 
      
 56 
     | 
    
         
            +
                        set_inherited_globals(tags)
         
     | 
| 
      
 57 
     | 
    
         
            +
                        set_session_context(tags)
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                        tracer_span = start_datadog_tracer_span(
         
     | 
| 
      
 60 
     | 
    
         
            +
                          test_module_name, build_span_options(service, Ext::AppTypes::TYPE_TEST_MODULE)
         
     | 
| 
      
 61 
     | 
    
         
            +
                        )
         
     | 
| 
      
 62 
     | 
    
         
            +
                        set_module_context(tags, tracer_span)
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                        build_test_module(tracer_span, tags)
         
     | 
| 
      
 65 
     | 
    
         
            +
                      end
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    def start_test_suite(test_suite_name, service: nil, tags: {})
         
     | 
| 
      
 69 
     | 
    
         
            +
                      return skip_tracing unless test_suite_level_visibility_enabled
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                      @global_context.fetch_or_activate_test_suite(test_suite_name) do
         
     | 
| 
      
 72 
     | 
    
         
            +
                        set_inherited_globals(tags)
         
     | 
| 
      
 73 
     | 
    
         
            +
                        set_session_context(tags)
         
     | 
| 
      
 74 
     | 
    
         
            +
                        set_module_context(tags)
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                        tracer_span = start_datadog_tracer_span(
         
     | 
| 
      
 77 
     | 
    
         
            +
                          test_suite_name, build_span_options(service, Ext::AppTypes::TYPE_TEST_SUITE)
         
     | 
| 
      
 78 
     | 
    
         
            +
                        )
         
     | 
| 
      
 79 
     | 
    
         
            +
                        set_suite_context(tags, span: tracer_span)
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                        build_test_suite(tracer_span, tags)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      end
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    def trace_test(test_name, test_suite_name, service: nil, tags: {}, &block)
         
     | 
| 
      
 86 
     | 
    
         
            +
                      return skip_tracing(block) unless enabled
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                      set_inherited_globals(tags)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      set_session_context(tags)
         
     | 
| 
      
 90 
     | 
    
         
            +
                      set_module_context(tags)
         
     | 
| 
      
 91 
     | 
    
         
            +
                      set_suite_context(tags, name: test_suite_name)
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                      tags[Ext::Test::TAG_NAME] = test_name
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                      span_options = build_span_options(
         
     | 
| 
      
 96 
     | 
    
         
            +
                        service,
         
     | 
| 
      
 97 
     | 
    
         
            +
                        Ext::AppTypes::TYPE_TEST,
         
     | 
| 
      
 98 
     | 
    
         
            +
                        # :resource is needed for the agent APM protocol to work correctly (for older agent versions)
         
     | 
| 
      
 99 
     | 
    
         
            +
                        # :continue_from is required to start a new trace for each test
         
     | 
| 
      
 100 
     | 
    
         
            +
                        {resource: test_name, continue_from: Datadog::Tracing::TraceDigest.new}
         
     | 
| 
      
 101 
     | 
    
         
            +
                      )
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                      if block
         
     | 
| 
      
 104 
     | 
    
         
            +
                        start_datadog_tracer_span(test_name, span_options) do |tracer_span|
         
     | 
| 
      
 105 
     | 
    
         
            +
                          test = build_test(tracer_span, tags)
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                          @local_context.activate_test!(test) do
         
     | 
| 
      
 108 
     | 
    
         
            +
                            block.call(test)
         
     | 
| 
      
 109 
     | 
    
         
            +
                          end
         
     | 
| 
      
 110 
     | 
    
         
            +
                        end
         
     | 
| 
      
 111 
     | 
    
         
            +
                      else
         
     | 
| 
      
 112 
     | 
    
         
            +
                        tracer_span = start_datadog_tracer_span(test_name, span_options)
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                        test = build_test(tracer_span, tags)
         
     | 
| 
      
 115 
     | 
    
         
            +
                        @local_context.activate_test!(test)
         
     | 
| 
      
 116 
     | 
    
         
            +
                        test
         
     | 
| 
      
 117 
     | 
    
         
            +
                      end
         
     | 
| 
      
 118 
     | 
    
         
            +
                    end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                    def trace(span_type, span_name, tags: {}, &block)
         
     | 
| 
      
 121 
     | 
    
         
            +
                      return skip_tracing(block) unless enabled
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                      span_options = build_span_options(
         
     | 
| 
      
 124 
     | 
    
         
            +
                        nil, # service name is completely optional for custom spans
         
     | 
| 
      
 125 
     | 
    
         
            +
                        span_type,
         
     | 
| 
      
 126 
     | 
    
         
            +
                        {resource: span_name}
         
     | 
| 
      
 127 
     | 
    
         
            +
                      )
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                      if block
         
     | 
| 
      
 130 
     | 
    
         
            +
                        start_datadog_tracer_span(span_name, span_options) do |tracer_span|
         
     | 
| 
      
 131 
     | 
    
         
            +
                          block.call(build_span(tracer_span, tags))
         
     | 
| 
      
 132 
     | 
    
         
            +
                        end
         
     | 
| 
      
 133 
     | 
    
         
            +
                      else
         
     | 
| 
      
 134 
     | 
    
         
            +
                        tracer_span = start_datadog_tracer_span(span_name, span_options)
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                        build_span(tracer_span, tags)
         
     | 
| 
      
 137 
     | 
    
         
            +
                      end
         
     | 
| 
      
 138 
     | 
    
         
            +
                    end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                    def active_span
         
     | 
| 
      
 141 
     | 
    
         
            +
                      tracer_span = Datadog::Tracing.active_span
         
     | 
| 
      
 142 
     | 
    
         
            +
                      Span.new(tracer_span) if tracer_span
         
     | 
| 
      
 143 
     | 
    
         
            +
                    end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                    def active_test
         
     | 
| 
      
 146 
     | 
    
         
            +
                      @local_context.active_test
         
     | 
| 
      
 147 
     | 
    
         
            +
                    end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                    def active_test_session
         
     | 
| 
      
 150 
     | 
    
         
            +
                      @global_context.active_test_session
         
     | 
| 
      
 151 
     | 
    
         
            +
                    end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                    def active_test_module
         
     | 
| 
      
 154 
     | 
    
         
            +
                      @global_context.active_test_module
         
     | 
| 
      
 155 
     | 
    
         
            +
                    end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                    def active_test_suite(test_suite_name)
         
     | 
| 
      
 158 
     | 
    
         
            +
                      @global_context.active_test_suite(test_suite_name)
         
     | 
| 
      
 159 
     | 
    
         
            +
                    end
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                    def deactivate_test(test)
         
     | 
| 
      
 162 
     | 
    
         
            +
                      @local_context.deactivate_test!(test)
         
     | 
| 
      
 163 
     | 
    
         
            +
                    end
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                    def deactivate_test_session
         
     | 
| 
      
 166 
     | 
    
         
            +
                      @global_context.deactivate_test_session!
         
     | 
| 
      
 167 
     | 
    
         
            +
                    end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                    def deactivate_test_module
         
     | 
| 
      
 170 
     | 
    
         
            +
                      @global_context.deactivate_test_module!
         
     | 
| 
      
 171 
     | 
    
         
            +
                    end
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                    def deactivate_test_suite(test_suite_name)
         
     | 
| 
      
 174 
     | 
    
         
            +
                      @global_context.deactivate_test_suite!(test_suite_name)
         
     | 
| 
      
 175 
     | 
    
         
            +
                    end
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                    private
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                    def skip_tracing(block = nil)
         
     | 
| 
      
 180 
     | 
    
         
            +
                      if block
         
     | 
| 
      
 181 
     | 
    
         
            +
                        block.call(null_span)
         
     | 
| 
      
 182 
     | 
    
         
            +
                      else
         
     | 
| 
      
 183 
     | 
    
         
            +
                        null_span
         
     | 
| 
      
 184 
     | 
    
         
            +
                      end
         
     | 
| 
      
 185 
     | 
    
         
            +
                    end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                    # Sets trace's origin to ciapp-test
         
     | 
| 
      
 188 
     | 
    
         
            +
                    def set_trace_origin(trace)
         
     | 
| 
      
 189 
     | 
    
         
            +
                      trace.origin = Ext::Test::CONTEXT_ORIGIN if trace
         
     | 
| 
      
 190 
     | 
    
         
            +
                    end
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                    def build_test_session(tracer_span, tags)
         
     | 
| 
      
 193 
     | 
    
         
            +
                      test_session = TestSession.new(tracer_span)
         
     | 
| 
      
 194 
     | 
    
         
            +
                      set_initial_tags(test_session, tags)
         
     | 
| 
      
 195 
     | 
    
         
            +
                      test_session
         
     | 
| 
      
 196 
     | 
    
         
            +
                    end
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                    def build_test_module(tracer_span, tags)
         
     | 
| 
      
 199 
     | 
    
         
            +
                      test_module = TestModule.new(tracer_span)
         
     | 
| 
      
 200 
     | 
    
         
            +
                      set_initial_tags(test_module, tags)
         
     | 
| 
      
 201 
     | 
    
         
            +
                      test_module
         
     | 
| 
      
 202 
     | 
    
         
            +
                    end
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
      
 204 
     | 
    
         
            +
                    def build_test_suite(tracer_span, tags)
         
     | 
| 
      
 205 
     | 
    
         
            +
                      test_suite = TestSuite.new(tracer_span)
         
     | 
| 
      
 206 
     | 
    
         
            +
                      set_initial_tags(test_suite, tags)
         
     | 
| 
      
 207 
     | 
    
         
            +
                      test_suite
         
     | 
| 
      
 208 
     | 
    
         
            +
                    end
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                    def build_test(tracer_span, tags)
         
     | 
| 
      
 211 
     | 
    
         
            +
                      test = Test.new(tracer_span)
         
     | 
| 
      
 212 
     | 
    
         
            +
                      set_initial_tags(test, tags)
         
     | 
| 
      
 213 
     | 
    
         
            +
                      test
         
     | 
| 
      
 214 
     | 
    
         
            +
                    end
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
                    def build_span(tracer_span, tags)
         
     | 
| 
      
 217 
     | 
    
         
            +
                      span = Span.new(tracer_span)
         
     | 
| 
      
 218 
     | 
    
         
            +
                      set_initial_tags(span, tags)
         
     | 
| 
      
 219 
     | 
    
         
            +
                      span
         
     | 
| 
      
 220 
     | 
    
         
            +
                    end
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
                    def build_span_options(service, span_type, other_options = {})
         
     | 
| 
      
 223 
     | 
    
         
            +
                      other_options[:service] = service || @global_context.service
         
     | 
| 
      
 224 
     | 
    
         
            +
                      other_options[:span_type] = span_type
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
      
 226 
     | 
    
         
            +
                      other_options
         
     | 
| 
      
 227 
     | 
    
         
            +
                    end
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                    def set_inherited_globals(tags)
         
     | 
| 
      
 230 
     | 
    
         
            +
                      # this code achieves the same as @global_context.inheritable_session_tags.merge(tags)
         
     | 
| 
      
 231 
     | 
    
         
            +
                      # but without allocating a new hash
         
     | 
| 
      
 232 
     | 
    
         
            +
                      @global_context.inheritable_session_tags.each do |key, value|
         
     | 
| 
      
 233 
     | 
    
         
            +
                        tags[key] = value unless tags.key?(key)
         
     | 
| 
      
 234 
     | 
    
         
            +
                      end
         
     | 
| 
      
 235 
     | 
    
         
            +
                    end
         
     | 
| 
      
 236 
     | 
    
         
            +
             
     | 
| 
      
 237 
     | 
    
         
            +
                    def set_initial_tags(ci_span, tags)
         
     | 
| 
      
 238 
     | 
    
         
            +
                      ci_span.set_default_tags
         
     | 
| 
      
 239 
     | 
    
         
            +
                      ci_span.set_environment_runtime_tags
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
                      ci_span.set_tags(tags)
         
     | 
| 
      
 242 
     | 
    
         
            +
                      ci_span.set_tags(environment_tags)
         
     | 
| 
      
 243 
     | 
    
         
            +
                    end
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
      
 245 
     | 
    
         
            +
                    def set_session_context(tags, test_session = nil)
         
     | 
| 
      
 246 
     | 
    
         
            +
                      test_session ||= active_test_session
         
     | 
| 
      
 247 
     | 
    
         
            +
                      tags[Ext::Test::TAG_TEST_SESSION_ID] = test_session.id.to_s if test_session
         
     | 
| 
      
 248 
     | 
    
         
            +
                    end
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
      
 250 
     | 
    
         
            +
                    def set_module_context(tags, test_module = nil)
         
     | 
| 
      
 251 
     | 
    
         
            +
                      test_module ||= active_test_module
         
     | 
| 
      
 252 
     | 
    
         
            +
                      if test_module
         
     | 
| 
      
 253 
     | 
    
         
            +
                        tags[Ext::Test::TAG_TEST_MODULE_ID] = test_module.id.to_s
         
     | 
| 
      
 254 
     | 
    
         
            +
                        tags[Ext::Test::TAG_MODULE] = test_module.name
         
     | 
| 
      
 255 
     | 
    
         
            +
                      end
         
     | 
| 
      
 256 
     | 
    
         
            +
                    end
         
     | 
| 
      
 257 
     | 
    
         
            +
             
     | 
| 
      
 258 
     | 
    
         
            +
                    def set_suite_context(tags, span: nil, name: nil)
         
     | 
| 
      
 259 
     | 
    
         
            +
                      return if span.nil? && name.nil?
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
      
 261 
     | 
    
         
            +
                      test_suite = span || active_test_suite(name)
         
     | 
| 
      
 262 
     | 
    
         
            +
             
     | 
| 
      
 263 
     | 
    
         
            +
                      if test_suite
         
     | 
| 
      
 264 
     | 
    
         
            +
                        tags[Ext::Test::TAG_TEST_SUITE_ID] = test_suite.id.to_s
         
     | 
| 
      
 265 
     | 
    
         
            +
                        tags[Ext::Test::TAG_SUITE] = test_suite.name
         
     | 
| 
      
 266 
     | 
    
         
            +
                      else
         
     | 
| 
      
 267 
     | 
    
         
            +
                        tags[Ext::Test::TAG_SUITE] = name
         
     | 
| 
      
 268 
     | 
    
         
            +
                      end
         
     | 
| 
      
 269 
     | 
    
         
            +
                    end
         
     | 
| 
      
 270 
     | 
    
         
            +
             
     | 
| 
      
 271 
     | 
    
         
            +
                    def start_datadog_tracer_span(span_name, span_options, &block)
         
     | 
| 
      
 272 
     | 
    
         
            +
                      if block
         
     | 
| 
      
 273 
     | 
    
         
            +
                        Datadog::Tracing.trace(span_name, **span_options) do |tracer_span, trace|
         
     | 
| 
      
 274 
     | 
    
         
            +
                          set_trace_origin(trace)
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
      
 276 
     | 
    
         
            +
                          yield tracer_span
         
     | 
| 
      
 277 
     | 
    
         
            +
                        end
         
     | 
| 
      
 278 
     | 
    
         
            +
                      else
         
     | 
| 
      
 279 
     | 
    
         
            +
                        tracer_span = Datadog::Tracing.trace(span_name, **span_options)
         
     | 
| 
      
 280 
     | 
    
         
            +
                        trace = Datadog::Tracing.active_trace
         
     | 
| 
      
 281 
     | 
    
         
            +
                        set_trace_origin(trace)
         
     | 
| 
      
 282 
     | 
    
         
            +
             
     | 
| 
      
 283 
     | 
    
         
            +
                        tracer_span
         
     | 
| 
      
 284 
     | 
    
         
            +
                      end
         
     | 
| 
      
 285 
     | 
    
         
            +
                    end
         
     | 
| 
      
 286 
     | 
    
         
            +
             
     | 
| 
      
 287 
     | 
    
         
            +
                    def null_span
         
     | 
| 
      
 288 
     | 
    
         
            +
                      @null_span ||= NullSpan.new
         
     | 
| 
      
 289 
     | 
    
         
            +
                    end
         
     | 
| 
      
 290 
     | 
    
         
            +
                  end
         
     | 
| 
      
 291 
     | 
    
         
            +
                end
         
     | 
| 
      
 292 
     | 
    
         
            +
              end
         
     | 
| 
      
 293 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,5 +1,9 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            require "set"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative "../../ext/test"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       3 
7 
     | 
    
         
             
            module Datadog
         
     | 
| 
       4 
8 
     | 
    
         
             
              module CI
         
     | 
| 
       5 
9 
     | 
    
         
             
                module TestVisibility
         
     | 
| 
         @@ -9,11 +13,31 @@ module Datadog 
     | 
|
| 
       9 
13 
     | 
    
         
             
                      MINIMUM_DURATION_NANO = 0
         
     | 
| 
       10 
14 
     | 
    
         
             
                      MAXIMUM_DURATION_NANO = 9223372036854775807
         
     | 
| 
       11 
15 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                       
     | 
| 
      
 16 
     | 
    
         
            +
                      CONTENT_FIELDS = [
         
     | 
| 
      
 17 
     | 
    
         
            +
                        "name", "resource", "service",
         
     | 
| 
      
 18 
     | 
    
         
            +
                        "error", "start", "duration",
         
     | 
| 
      
 19 
     | 
    
         
            +
                        "meta", "metrics",
         
     | 
| 
      
 20 
     | 
    
         
            +
                        "type" => "span_type"
         
     | 
| 
      
 21 
     | 
    
         
            +
                      ].freeze
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      REQUIRED_FIELDS = [
         
     | 
| 
      
 24 
     | 
    
         
            +
                        "error",
         
     | 
| 
      
 25 
     | 
    
         
            +
                        "name",
         
     | 
| 
      
 26 
     | 
    
         
            +
                        "resource",
         
     | 
| 
      
 27 
     | 
    
         
            +
                        "start",
         
     | 
| 
      
 28 
     | 
    
         
            +
                        "duration"
         
     | 
| 
      
 29 
     | 
    
         
            +
                      ].freeze
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                      attr_reader :trace, :span, :meta
         
     | 
| 
       13 
32 
     | 
    
         | 
| 
       14 
33 
     | 
    
         
             
                      def initialize(trace, span)
         
     | 
| 
       15 
34 
     | 
    
         
             
                        @trace = trace
         
     | 
| 
       16 
35 
     | 
    
         
             
                        @span = span
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                        @meta = @span.meta.reject { |key, _| Ext::Test::SPECIAL_TAGS.include?(key) }
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                        @errors = {}
         
     | 
| 
      
 40 
     | 
    
         
            +
                        @validated = false
         
     | 
| 
       17 
41 
     | 
    
         
             
                      end
         
     | 
| 
       18 
42 
     | 
    
         | 
| 
       19 
43 
     | 
    
         
             
                      def to_msgpack(packer = nil)
         
     | 
| 
         @@ -40,7 +64,23 @@ module Datadog 
     | 
|
| 
       40 
64 
     | 
    
         | 
| 
       41 
65 
     | 
    
         
             
                      # validates according to citestcycle json schema
         
     | 
| 
       42 
66 
     | 
    
         
             
                      def valid?
         
     | 
| 
       43 
     | 
    
         
            -
                         
     | 
| 
      
 67 
     | 
    
         
            +
                        validate! unless @validated
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                        @errors.empty?
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                      def validate!
         
     | 
| 
      
 73 
     | 
    
         
            +
                        @errors.clear
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                        validate_required_fields!
         
     | 
| 
      
 76 
     | 
    
         
            +
                        validate_start_time!
         
     | 
| 
      
 77 
     | 
    
         
            +
                        validate_duration!
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                        @validated = true
         
     | 
| 
      
 80 
     | 
    
         
            +
                      end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                      def validation_errors
         
     | 
| 
      
 83 
     | 
    
         
            +
                        @errors
         
     | 
| 
       44 
84 
     | 
    
         
             
                      end
         
     | 
| 
       45 
85 
     | 
    
         | 
| 
       46 
86 
     | 
    
         
             
                      def content_fields
         
     | 
| 
         @@ -67,6 +107,18 @@ module Datadog 
     | 
|
| 
       67 
107 
     | 
    
         
             
                        @span.parent_id
         
     | 
| 
       68 
108 
     | 
    
         
             
                      end
         
     | 
| 
       69 
109 
     | 
    
         | 
| 
      
 110 
     | 
    
         
            +
                      def test_session_id
         
     | 
| 
      
 111 
     | 
    
         
            +
                        to_integer(@span.get_tag(Ext::Test::TAG_TEST_SESSION_ID))
         
     | 
| 
      
 112 
     | 
    
         
            +
                      end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                      def test_module_id
         
     | 
| 
      
 115 
     | 
    
         
            +
                        to_integer(@span.get_tag(Ext::Test::TAG_TEST_MODULE_ID))
         
     | 
| 
      
 116 
     | 
    
         
            +
                      end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                      def test_suite_id
         
     | 
| 
      
 119 
     | 
    
         
            +
                        to_integer(@span.get_tag(Ext::Test::TAG_TEST_SUITE_ID))
         
     | 
| 
      
 120 
     | 
    
         
            +
                      end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
       70 
122 
     | 
    
         
             
                      def type
         
     | 
| 
       71 
123 
     | 
    
         
             
                      end
         
     | 
| 
       72 
124 
     | 
    
         | 
| 
         @@ -98,10 +150,6 @@ module Datadog 
     | 
|
| 
       98 
150 
     | 
    
         
             
                        @duration ||= duration_nano(@span.duration)
         
     | 
| 
       99 
151 
     | 
    
         
             
                      end
         
     | 
| 
       100 
152 
     | 
    
         | 
| 
       101 
     | 
    
         
            -
                      def meta
         
     | 
| 
       102 
     | 
    
         
            -
                        @span.meta
         
     | 
| 
       103 
     | 
    
         
            -
                      end
         
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
153 
     | 
    
         
             
                      def metrics
         
     | 
| 
       106 
154 
     | 
    
         
             
                        @span.metrics
         
     | 
| 
       107 
155 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -122,16 +170,48 @@ module Datadog 
     | 
|
| 
       122 
170 
     | 
    
         | 
| 
       123 
171 
     | 
    
         
             
                      private
         
     | 
| 
       124 
172 
     | 
    
         | 
| 
       125 
     | 
    
         
            -
                      def  
     | 
| 
       126 
     | 
    
         
            -
                        !start 
     | 
| 
      
 173 
     | 
    
         
            +
                      def validate_start_time!
         
     | 
| 
      
 174 
     | 
    
         
            +
                        validate_required!("start")
         
     | 
| 
      
 175 
     | 
    
         
            +
                        validate_greater_than_or_equal!("start", MINIMUM_TIMESTAMP_NANO)
         
     | 
| 
      
 176 
     | 
    
         
            +
                      end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                      def validate_duration!
         
     | 
| 
      
 179 
     | 
    
         
            +
                        validate_required!("duration")
         
     | 
| 
      
 180 
     | 
    
         
            +
                        validate_greater_than_or_equal!("duration", MINIMUM_DURATION_NANO)
         
     | 
| 
      
 181 
     | 
    
         
            +
                        validate_less_than_or_equal!("duration", MAXIMUM_DURATION_NANO)
         
     | 
| 
      
 182 
     | 
    
         
            +
                      end
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                      def validate_required_fields!
         
     | 
| 
      
 185 
     | 
    
         
            +
                        required_fields.each do |field|
         
     | 
| 
      
 186 
     | 
    
         
            +
                          validate_required!(field)
         
     | 
| 
      
 187 
     | 
    
         
            +
                        end
         
     | 
| 
       127 
188 
     | 
    
         
             
                      end
         
     | 
| 
       128 
189 
     | 
    
         | 
| 
       129 
     | 
    
         
            -
                      def  
     | 
| 
       130 
     | 
    
         
            -
                         
     | 
| 
      
 190 
     | 
    
         
            +
                      def validate_required!(field)
         
     | 
| 
      
 191 
     | 
    
         
            +
                        if send(field).nil?
         
     | 
| 
      
 192 
     | 
    
         
            +
                          add_error(field, "is required")
         
     | 
| 
      
 193 
     | 
    
         
            +
                        end
         
     | 
| 
       131 
194 
     | 
    
         
             
                      end
         
     | 
| 
       132 
195 
     | 
    
         | 
| 
       133 
     | 
    
         
            -
                      def  
     | 
| 
       134 
     | 
    
         
            -
                         
     | 
| 
      
 196 
     | 
    
         
            +
                      def validate_greater_than_or_equal!(field, value)
         
     | 
| 
      
 197 
     | 
    
         
            +
                        return if send(field).nil?
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                        if send(field) < value
         
     | 
| 
      
 200 
     | 
    
         
            +
                          add_error(field, "must be greater than or equal to #{value}")
         
     | 
| 
      
 201 
     | 
    
         
            +
                        end
         
     | 
| 
      
 202 
     | 
    
         
            +
                      end
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
      
 204 
     | 
    
         
            +
                      def validate_less_than_or_equal!(field, value)
         
     | 
| 
      
 205 
     | 
    
         
            +
                        return if send(field).nil?
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
                        if send(field) > value
         
     | 
| 
      
 208 
     | 
    
         
            +
                          add_error(field, "must be less than or equal to #{value}")
         
     | 
| 
      
 209 
     | 
    
         
            +
                        end
         
     | 
| 
      
 210 
     | 
    
         
            +
                      end
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
                      def add_error(field, message)
         
     | 
| 
      
 213 
     | 
    
         
            +
                        @errors[field] ||= Set.new
         
     | 
| 
      
 214 
     | 
    
         
            +
                        @errors[field] << message
         
     | 
| 
       135 
215 
     | 
    
         
             
                      end
         
     | 
| 
       136 
216 
     | 
    
         | 
| 
       137 
217 
     | 
    
         
             
                      def required_fields
         
     | 
| 
         @@ -154,6 +234,14 @@ module Datadog 
     | 
|
| 
       154 
234 
     | 
    
         
             
                      def duration_nano(duration)
         
     | 
| 
       155 
235 
     | 
    
         
             
                        (duration * 1e9).to_i
         
     | 
| 
       156 
236 
     | 
    
         
             
                      end
         
     | 
| 
      
 237 
     | 
    
         
            +
             
     | 
| 
      
 238 
     | 
    
         
            +
                      def to_s
         
     | 
| 
      
 239 
     | 
    
         
            +
                        "#{self.class.name}(id:#{span_id},name:#{name})"
         
     | 
| 
      
 240 
     | 
    
         
            +
                      end
         
     | 
| 
      
 241 
     | 
    
         
            +
             
     | 
| 
      
 242 
     | 
    
         
            +
                      def to_integer(value)
         
     | 
| 
      
 243 
     | 
    
         
            +
                        value.to_i if value
         
     | 
| 
      
 244 
     | 
    
         
            +
                      end
         
     | 
| 
       157 
245 
     | 
    
         
             
                    end
         
     | 
| 
       158 
246 
     | 
    
         
             
                  end
         
     | 
| 
       159 
247 
     | 
    
         
             
                end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative "../test_v2"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative "../test_session"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative "../test_module"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require_relative "../test_suite"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require_relative "../span"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module Datadog
         
     | 
| 
      
 10 
     | 
    
         
            +
              module CI
         
     | 
| 
      
 11 
     | 
    
         
            +
                module TestVisibility
         
     | 
| 
      
 12 
     | 
    
         
            +
                  module Serializers
         
     | 
| 
      
 13 
     | 
    
         
            +
                    module Factories
         
     | 
| 
      
 14 
     | 
    
         
            +
                      # This factory takes care of creating citestcycle serializers when test-suite-level visibility is enabled
         
     | 
| 
      
 15 
     | 
    
         
            +
                      module TestSuiteLevel
         
     | 
| 
      
 16 
     | 
    
         
            +
                        module_function
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                        def serializer(trace, span)
         
     | 
| 
      
 19 
     | 
    
         
            +
                          case span.type
         
     | 
| 
      
 20 
     | 
    
         
            +
                          when Datadog::CI::Ext::AppTypes::TYPE_TEST
         
     | 
| 
      
 21 
     | 
    
         
            +
                            Serializers::TestV2.new(trace, span)
         
     | 
| 
      
 22 
     | 
    
         
            +
                          when Datadog::CI::Ext::AppTypes::TYPE_TEST_SESSION
         
     | 
| 
      
 23 
     | 
    
         
            +
                            Serializers::TestSession.new(trace, span)
         
     | 
| 
      
 24 
     | 
    
         
            +
                          when Datadog::CI::Ext::AppTypes::TYPE_TEST_MODULE
         
     | 
| 
      
 25 
     | 
    
         
            +
                            Serializers::TestModule.new(trace, span)
         
     | 
| 
      
 26 
     | 
    
         
            +
                          when Datadog::CI::Ext::AppTypes::TYPE_TEST_SUITE
         
     | 
| 
      
 27 
     | 
    
         
            +
                            Serializers::TestSuite.new(trace, span)
         
     | 
| 
      
 28 
     | 
    
         
            +
                          else
         
     | 
| 
      
 29 
     | 
    
         
            +
                            Serializers::Span.new(trace, span)
         
     | 
| 
      
 30 
     | 
    
         
            +
                          end
         
     | 
| 
      
 31 
     | 
    
         
            +
                        end
         
     | 
| 
      
 32 
     | 
    
         
            +
                      end
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -7,25 +7,11 @@ module Datadog 
     | 
|
| 
       7 
7 
     | 
    
         
             
                module TestVisibility
         
     | 
| 
       8 
8 
     | 
    
         
             
                  module Serializers
         
     | 
| 
       9 
9 
     | 
    
         
             
                    class Span < Base
         
     | 
| 
       10 
     | 
    
         
            -
                      CONTENT_FIELDS = [
         
     | 
| 
       11 
     | 
    
         
            -
                        "trace_id", "span_id", "parent_id",
         
     | 
| 
       12 
     | 
    
         
            -
                        "name", "resource", "service",
         
     | 
| 
       13 
     | 
    
         
            -
                        "error", "start", "duration",
         
     | 
| 
       14 
     | 
    
         
            -
                        "meta", "metrics",
         
     | 
| 
       15 
     | 
    
         
            -
                        "type" => "span_type"
         
     | 
| 
       16 
     | 
    
         
            -
                      ].freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
                      CONTENT_FIELDS = (["trace_id", "span_id", "parent_id"] + Base::CONTENT_FIELDS).freeze
         
     | 
| 
       17 
11 
     | 
    
         | 
| 
       18 
12 
     | 
    
         
             
                      CONTENT_MAP_SIZE = calculate_content_map_size(CONTENT_FIELDS)
         
     | 
| 
       19 
13 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                      REQUIRED_FIELDS = [
         
     | 
| 
       21 
     | 
    
         
            -
                        "trace_id",
         
     | 
| 
       22 
     | 
    
         
            -
                        "span_id",
         
     | 
| 
       23 
     | 
    
         
            -
                        "error",
         
     | 
| 
       24 
     | 
    
         
            -
                        "name",
         
     | 
| 
       25 
     | 
    
         
            -
                        "resource",
         
     | 
| 
       26 
     | 
    
         
            -
                        "start",
         
     | 
| 
       27 
     | 
    
         
            -
                        "duration"
         
     | 
| 
       28 
     | 
    
         
            -
                      ].freeze
         
     | 
| 
      
 14 
     | 
    
         
            +
                      REQUIRED_FIELDS = (["trace_id", "span_id"] + Base::REQUIRED_FIELDS).freeze
         
     | 
| 
       29 
15 
     | 
    
         | 
| 
       30 
16 
     | 
    
         
             
                      def content_fields
         
     | 
| 
       31 
17 
     | 
    
         
             
                        CONTENT_FIELDS
         
     | 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative "base"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative "../../ext/test"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Datadog
         
     | 
| 
      
 7 
     | 
    
         
            +
              module CI
         
     | 
| 
      
 8 
     | 
    
         
            +
                module TestVisibility
         
     | 
| 
      
 9 
     | 
    
         
            +
                  module Serializers
         
     | 
| 
      
 10 
     | 
    
         
            +
                    class TestModule < Base
         
     | 
| 
      
 11 
     | 
    
         
            +
                      CONTENT_FIELDS = (["test_session_id", "test_module_id"] + Base::CONTENT_FIELDS).freeze
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                      CONTENT_MAP_SIZE = calculate_content_map_size(CONTENT_FIELDS)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                      REQUIRED_FIELDS = (["test_session_id", "test_module_id"] + Base::REQUIRED_FIELDS).freeze
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                      def content_fields
         
     | 
| 
      
 18 
     | 
    
         
            +
                        CONTENT_FIELDS
         
     | 
| 
      
 19 
     | 
    
         
            +
                      end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                      def content_map_size
         
     | 
| 
      
 22 
     | 
    
         
            +
                        CONTENT_MAP_SIZE
         
     | 
| 
      
 23 
     | 
    
         
            +
                      end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                      def type
         
     | 
| 
      
 26 
     | 
    
         
            +
                        Ext::AppTypes::TYPE_TEST_MODULE
         
     | 
| 
      
 27 
     | 
    
         
            +
                      end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                      def name
         
     | 
| 
      
 30 
     | 
    
         
            +
                        "#{@span.get_tag(Ext::Test::TAG_FRAMEWORK)}.test_module"
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                      def resource
         
     | 
| 
      
 34 
     | 
    
         
            +
                        "#{@span.get_tag(Ext::Test::TAG_FRAMEWORK)}.test_module.#{@span.get_tag(Ext::Test::TAG_MODULE)}"
         
     | 
| 
      
 35 
     | 
    
         
            +
                      end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                      private
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                      def required_fields
         
     | 
| 
      
 40 
     | 
    
         
            +
                        REQUIRED_FIELDS
         
     | 
| 
      
 41 
     | 
    
         
            +
                      end
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     |