diff --git a/lib/datadog/ci/configuration/components.rb b/lib/datadog/ci/configuration/components.rb index 8947464a..4f62b51d 100644 --- a/lib/datadog/ci/configuration/components.rb +++ b/lib/datadog/ci/configuration/components.rb @@ -80,16 +80,8 @@ def activate_ci!(settings) # startup logs are useless for test visibility and create noise settings.diagnostics.startup_logs.enabled = false - # When timecop is present, Time.now is mocked and .now_without_mock_time is added on Time to - # get the current time without the mock. - if timecop? - settings.time_now_provider = -> do - Time.now_without_mock_time - rescue NoMethodError - # fallback to normal Time.now if Time.now_without_mock_time is not defined for any reason - Time.now - end - end + # timecop configuration + configure_time_providers(settings) # Configure Datadog::Tracing module @@ -182,7 +174,6 @@ def build_test_visibility_api(settings) # Tests are running without CI visibility enabled settings.ci.enabled = false end - else Datadog.logger.debug("CI visibility configured to use agent transport via EVP proxy") @@ -299,6 +290,29 @@ def configure_telemetry(settings) end end + # When timecop is present: + # - Time.now is mocked and .now_without_mock_time is added on Time to get the current time without the mock. + # - Process.clock_gettime is mocked and .clock_gettime_without_mock is added on Process to get the monotonic time without the mock. + def configure_time_providers(settings) + return unless timecop? + + settings.time_now_provider = -> do + Time.now_without_mock_time + rescue NoMethodError + # fallback to normal Time.now if Time.now_without_mock_time is not defined for any reason + Time.now + end + + if defined?(Process.clock_gettime_without_mock) + settings.get_time_provider = ->(unit = :float_second) do + ::Process.clock_gettime_without_mock(::Process::CLOCK_MONOTONIC, unit) + rescue NoMethodError + # fallback to normal Process.clock_gettime if Process.clock_gettime_without_mock is not defined for any reason + Process.clock_gettime(::Process::CLOCK_MONOTONIC, unit) + end + end + end + def timecop? Gem.loaded_specs.key?("timecop") || !!defined?(Timecop) end diff --git a/sig/datadog/ci/configuration/components.rbs b/sig/datadog/ci/configuration/components.rbs index 835d5edf..c87d7aea 100644 --- a/sig/datadog/ci/configuration/components.rbs +++ b/sig/datadog/ci/configuration/components.rbs @@ -40,6 +40,8 @@ module Datadog def configure_telemetry: (untyped settings) -> void + def configure_time_providers: (untyped settings) -> void + def timecop?: () -> bool end end diff --git a/spec/datadog/ci/contrib/timecop/instrumentation_spec.rb b/spec/datadog/ci/contrib/timecop/instrumentation_spec.rb index 7a525477..bbcbf55b 100644 --- a/spec/datadog/ci/contrib/timecop/instrumentation_spec.rb +++ b/spec/datadog/ci/contrib/timecop/instrumentation_spec.rb @@ -13,6 +13,7 @@ Minitest::Runnable.reset Timecop.freeze(time_1990) + Timecop.mock_process_clock = true class SomeTest < Minitest::Test def test_pass @@ -25,6 +26,9 @@ def test_pass it "does not set frozen time when setting start time for traces" do expect(first_test_span.start_time).not_to eq(time_1990) + expect(first_test_span.duration).not_to eq(0) + expect(test_session_span.start_time).not_to eq(time_1990) + expect(test_session_span.duration).not_to eq(0) end end diff --git a/vendor/rbs/timecop/0/timecop.rbs b/vendor/rbs/timecop/0/timecop.rbs index 1b723f19..77b0190d 100644 --- a/vendor/rbs/timecop/0/timecop.rbs +++ b/vendor/rbs/timecop/0/timecop.rbs @@ -2,5 +2,9 @@ class Time def self.now_without_mock_time: () -> Time end +module Process + def self.clock_gettime_without_mock: (untyped clock, untyped unit) -> Numeric +end + class Timecop end