Skip to content

Commit

Permalink
Merge pull request #242 from DataDog/anmarchenko/timecop_mock_process…
Browse files Browse the repository at this point in the history
…_clock_issue

[SDTEST-482] Use correct monotonic clock time if Timecop.mock_process_clock is set
  • Loading branch information
anmarchenko authored Oct 17, 2024
2 parents fc40f77 + 1ef2028 commit 9ec713d
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 11 deletions.
36 changes: 25 additions & 11 deletions lib/datadog/ci/configuration/components.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions sig/datadog/ci/configuration/components.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ module Datadog

def configure_telemetry: (untyped settings) -> void

def configure_time_providers: (untyped settings) -> void

def timecop?: () -> bool
end
end
Expand Down
4 changes: 4 additions & 0 deletions spec/datadog/ci/contrib/timecop/instrumentation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
Minitest::Runnable.reset

Timecop.freeze(time_1990)
Timecop.mock_process_clock = true

class SomeTest < Minitest::Test
def test_pass
Expand All @@ -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
4 changes: 4 additions & 0 deletions vendor/rbs/timecop/0/timecop.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 9ec713d

Please sign in to comment.