-
Notifications
You must be signed in to change notification settings - Fork 226
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Process.clock_gettime
support
#419
Merged
joshuacronemeyer
merged 16 commits into
travisjeffery:master
from
alexcwatt:support-clock-gettime
Jun 1, 2024
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
350a4cd
Add Process.clock_gettime support
alexcwatt 27e92d7
Support nested freeze calls for monotonic clock
alexcwatt 9d1209c
Refactor to avoid parse_time side effect
alexcwatt 317c18b
Address feedback on tests
alexcwatt b443315
Smaller sleep
alexcwatt 231750a
Rename current to initial_time
alexcwatt de109f7
Use assert_operator in more places
alexcwatt b332e4c
Update README
alexcwatt c95628f
Sleep between consecutive times
alexcwatt a59afa0
Reuse TIME_EPSILON
alexcwatt e9489b6
Extract variable
alexcwatt 62f51e8
Move tests for Process.clock_gettime
alexcwatt e550a95
Add tests for various units
alexcwatt 94c5734
Add test for date freeze
alexcwatt 466fc75
Fix unintended change to TimecopTest
alexcwatt 5afef57
Revert all changes to test/timecop_test.rb
alexcwatt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
require_relative "test_helper" | ||
require 'timecop' | ||
|
||
class TestTimecopWithProcessClock < Minitest::Test | ||
TIME_EPSILON = 0.001 # seconds - represents enough time for Process.clock_gettime to have advanced if not frozen | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Open to other ideas for this constant name to better represent the meaning that I'm currently explaining with the comment. |
||
|
||
def teardown | ||
Timecop.return | ||
end | ||
|
||
if RUBY_VERSION >= '2.1.0' | ||
def test_process_clock_gettime_monotonic | ||
Timecop.freeze do | ||
assert_same(*consecutive_monotonic, "CLOCK_MONOTONIC is not frozen") | ||
end | ||
|
||
initial_time = monotonic | ||
Timecop.freeze(-0.5) do | ||
assert_operator(monotonic, :<, initial_time, "CLOCK_MONOTONIC is not traveling back in time") | ||
end | ||
end | ||
|
||
def test_process_clock_gettime_monotonic_with_date_freeze | ||
date = Date.new(2024, 6, 1) | ||
monotonic1 = Timecop.freeze(date) { monotonic } | ||
monotonic2 = Timecop.freeze(date) { monotonic } | ||
|
||
refute_equal(monotonic1, monotonic2, "CLOCK_MONOTONIC is not expected to freeze deterministically with a date") | ||
end | ||
|
||
def test_process_clock_gettime_realtime_with_date_freeze | ||
date = Date.new(2024, 6, 1) | ||
realtime_1 = Timecop.freeze(date) { realtime } | ||
realtime_2 = Timecop.freeze(date) { realtime } | ||
|
||
assert_equal(realtime_1, realtime_2, "CLOCK_REALTIME is expected to support freezing with a date") | ||
end | ||
|
||
def test_process_clock_gettime_units_integer | ||
Timecop.freeze do | ||
time_in_nanoseconds = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) | ||
time_in_microseconds = Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond) | ||
time_in_milliseconds = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) | ||
time_in_seconds = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) | ||
|
||
assert_equal(time_in_microseconds, (time_in_nanoseconds / 10**3).to_i) | ||
assert_equal(time_in_milliseconds, (time_in_nanoseconds / 10**6).to_i) | ||
assert_equal(time_in_seconds, (time_in_nanoseconds / 10**9).to_i) | ||
end | ||
end | ||
|
||
def test_process_clock_gettime_units_float | ||
Timecop.freeze do | ||
time_in_nanoseconds = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond).to_f | ||
|
||
float_microseconds = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond) | ||
float_milliseconds = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) | ||
float_seconds = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) | ||
|
||
delta = 0.000001 | ||
assert_in_delta(float_microseconds, time_in_nanoseconds / 10**3, delta) | ||
assert_in_delta(float_milliseconds, time_in_nanoseconds / 10**6, delta) | ||
assert_in_delta(float_seconds, time_in_nanoseconds / 10**9, delta) | ||
end | ||
end | ||
|
||
def test_process_clock_gettime_monotonic_nested | ||
Timecop.freeze do | ||
parent = monotonic | ||
|
||
sleep(TIME_EPSILON) | ||
|
||
delta = 0.5 | ||
Timecop.freeze(delta) do | ||
child = monotonic | ||
assert_equal(child, parent + delta, "Nested freeze not working for monotonic time") | ||
end | ||
end | ||
end | ||
|
||
def test_process_clock_gettime_monotonic_travel | ||
initial_time = monotonic | ||
Timecop.travel do | ||
refute_same(*consecutive_monotonic, "CLOCK_MONOTONIC is frozen") | ||
assert_operator(monotonic, :>, initial_time, "CLOCK_MONOTONIC is not moving forward") | ||
end | ||
|
||
Timecop.travel(-0.5) do | ||
refute_same(*consecutive_monotonic, "CLOCK_MONOTONIC is frozen") | ||
assert_operator(monotonic, :<, initial_time, "CLOCK_MONOTONIC is not traveling properly") | ||
end | ||
end | ||
|
||
def test_process_clock_gettime_monotonic_scale | ||
scale = 4 | ||
sleep_length = 0.25 | ||
Timecop.scale(scale) do | ||
initial_time = monotonic | ||
sleep(sleep_length) | ||
expected_time = initial_time + (scale * sleep_length) | ||
assert_times_effectively_equal expected_time, monotonic, 0.1, "CLOCK_MONOTONIC is not scaling" | ||
end | ||
end | ||
|
||
def test_process_clock_gettime_realtime | ||
Timecop.freeze do | ||
assert_same(*consecutive_realtime, "CLOCK_REALTIME is not frozen") | ||
end | ||
|
||
initial_time = realtime | ||
Timecop.freeze(-20) do | ||
assert_operator(realtime, :<, initial_time, "CLOCK_REALTIME is not traveling back in time") | ||
end | ||
end | ||
|
||
def test_process_clock_gettime_realtime_travel | ||
initial_time = realtime | ||
Timecop.travel do | ||
refute_equal consecutive_realtime, "CLOCK_REALTIME is frozen" | ||
assert_operator(realtime, :>, initial_time, "CLOCK_REALTIME is not moving forward") | ||
end | ||
|
||
delta = 0.1 | ||
Timecop.travel(Time.now - delta) do | ||
refute_equal consecutive_realtime, "CLOCK_REALTIME is frozen" | ||
assert_operator(realtime, :<, initial_time, "CLOCK_REALTIME is not traveling properly") | ||
sleep(delta) | ||
assert_operator(realtime, :>, initial_time, "CLOCK_REALTIME is not traveling properly") | ||
end | ||
end | ||
|
||
def test_process_clock_gettime_realtime_scale | ||
scale = 4 | ||
sleep_length = 0.25 | ||
Timecop.scale(scale) do | ||
initial_time = realtime | ||
sleep(sleep_length) | ||
assert_operator(initial_time + scale * sleep_length, :<, realtime, "CLOCK_REALTIME is not scaling") | ||
end | ||
end | ||
|
||
private | ||
|
||
def monotonic | ||
Process.clock_gettime(Process::CLOCK_MONOTONIC) | ||
end | ||
|
||
def realtime | ||
Process.clock_gettime(Process::CLOCK_REALTIME) | ||
end | ||
|
||
def consecutive_monotonic | ||
consecutive_times(:monotonic) | ||
end | ||
|
||
def consecutive_realtime | ||
consecutive_times(:realtime) | ||
end | ||
|
||
def consecutive_times(time_method) | ||
t1 = send(time_method) | ||
sleep(TIME_EPSILON) | ||
t2 = send(time_method) | ||
|
||
[t1, t2] | ||
end | ||
end | ||
end |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about
Process::CLOCK_THREAD_CPUTIME_ID
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I could see adding that one too. I will focus on the bug fix first and then see if I can include this scope in the PR; if not, it should be a simpler follow-up after this lands.