timecop
timecop copied to clipboard
Added opt-in feature to scale sleep operations.
A lot of my time-dependent code I work with uses sleep operations with specific timings for scheduling events. These time-dependent snippets are also coupled with the Time-related operations that Timecop already stubs, so the two types of operations always need to be stubbed together for me. I'm currently using some localized stubbing but I'm now finding it necessary to duplicate this code in several test, so I thought I'd submit a pull request to stub sleep when scale is called.
To be specific, it's usually ConditionVariable#wait and not Kernel#sleep that I use, but this pull request mocks both, because it is nearly the same operation.
This is a purely opt-in feature and shouldn't change the way Timecop.scale works for people already using Timecop in the workflows. You can opt-in to this expanded stubbing with Timecop.scale_sleep = true.
I've tried to match the existing style as much as possible in the implementation, the tests, and the explanation I added to the README (which is in a separate commit, so you can decide if you want it or not).
Let me know if you'd prefer some other way of enabling the feature instead of Timecop.scale_sleep = true, and I'd bet we can make it happen.
Thanks for your consideration, and great work so far with Timecop!
Can you give me some real world examples for using this? Still not sold on the usefulness of it
We were using this for integration-testing of components of distributed systems (that were written in Ruby, and relied on sleep and ConditionVariable#wait to only consume resources when they needed to). Specifically, we were checking that timeouts were working properly with time sped up so that our each test didn't take tens of seconds to complete. We wanted to use Timecop to do this, because Timecop is the go-to gem for speeding up the passing of time, but we found it ignored this aspect of time, so we wrote a patch.
@jemc what we do is simply stub sleep in our specs.
e.g.
# given
class Sprocket
def process
# do something
sleep 10
# do something else
end
end
# an an rspec of
RSpec.describe Sprocket do
it 'should sleep for 10 seconds' do
expect(subject).to receive(:sleep).with(10)
subject.process
end
it 'should do something' do
allow(subject).to receive(:sleep)
expect(subject.process).to eq('something')
end
end