timecop
timecop copied to clipboard
Nanoseconds drift when frozen in Rails 3.2.13 using 1.9.3-p448
This is very similar to issue #56. However it seems to be broken with the most recent Ruby 1.9.3 patch level 1.9.3 (1.9.3-p448), Timecop (0.6.1), and Rails 3.2.13.
# Run this in rails console
require 'timecop'
t1 = Time.now
Timecop.freeze t1
t2 = Time.now
t1 == t2
# => false
t1.nsec == t2.nsec
# => false
t1.nsec
# => 811884000
t2.nsec
# => 811883926
Time.now == t2
# => true
Time.now.nsec
# => 811883926
This issue does not happen in irb or pry, only the Rails environment.
FWIW I also tested with Ruby 1.9.3-p392 and it did not have this problem.
It looks like the current master branch fixes this issue. It was related to #70.
When is the next patch release planned?
Not sure, yet. I'd like to fix this https://github.com/travisjeffery/timecop/commit/5456bdfc6d7c6faa071a440c61991a9593ecf38f.
Maybe some issues with jRuby support as well.
Just bit by this in ci, ruby 2.0.0-p247, rails 3.2.12. Going to try installing from master.
Didn't help:
context 'user has not been soft deleted' do
it 'soft-deletes the user' do
Timecop.freeze do
visit rails_admin.delete_path :user, user
click_button "Yes, I'm sure"
user.reload.deleted_at.should == Time.zone.now
end
end
end
Using /home/travis/.rvm/gems/ruby-2.0.0-p247
Installing rails (3.2.12)
Using timecop (0.6.2.2) from git://github.com/travisjeffery/timecop.git (at master)
1) Managing Users destroying user has not been soft deleted soft-deletes the user
Failure/Error: user.reload.deleted_at.should == Time.zone.now
expected: Fri, 26 Jul 2013 11:56:45 MST -07:00
got: Fri, 26 Jul 2013 11:56:45 MST -07:00 (using ==)
Diff:
# ./spec/features/admin/users_spec.rb:193:in `block (5 levels) in <top (required)>'
# ./spec/features/admin/users_spec.rb:189:in `block (4 levels) in <top (required)>'
I just got bit by something similar and solved it by declaring a precision at which I need that equivalence. mock_redis/333733e84.
@BM5k do you know the differences in the rational seconds? As a work around, you can use the RSpec be_within matcher: user.reload.deleted_at.should be_within(1.second).of(Time.zone.now)
Taking a peak at master, it seems like it may work (here). Are you able to step through and see which branch of that if statement gets run? Also, what if you don't use the block form of freeze?
@BM5k one other thing to be aware of, is that some databases will strip the fractional seconds. This could just be a DB issue. So when the value is read back, nsec will be 0.
@cupakromer Thanks for the info, I will definitely be playing with this more today. I will update with my results.
@cupakromer
- I get to line 54 of that if statement every time for this example
- postgres does not appear to be stripping nsec values
The test runs as-is on my machine, but fails on the CI server (travis pro). The within(1.second).of trick works locally & on CI. Thanks again.
@BM5k would be interesting to add some puts for Time.zone.now.nsec and user.reload.deleted_at.nsec before the should line to see what CI says.
This was locally, it will take me a little while to get results from CI server.
Managing Users
destroying
user has not been soft deleted
From: /Users/bm5k/projects/devfu/iss/spec/features/admin/users_spec.rb @ line 192 :
187:
188: it 'soft-deletes the user', :focus do
189: Timecop.freeze do
190: visit rails_admin.delete_path :user, user
191: click_button "Yes, I'm sure"
=> 192: binding.pry
193: user.reload.deleted_at.should == Time.zone.now
194: end
195: end
196:
197: end
[1] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_4::Nested_1>)> Time.zone.now.nsec
=> 760641000
[2] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_4::Nested_1>)> user.reload.deleted_at.nsec
=> 760641000
[3] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_4::Nested_1>)>
In the binding, if you run the expression on line 193 what happens? You may need to start taking apart the different fields. :grimacing: Also, could you paste how the user object is being created?
I setup a new Rails 4.0 project with Ruby 2.0.0, RSpec 2.14, and Timecop 0.6.2.2 and the following test passes:
describe 'Testing some timecop issues' do
it do
Timecop.freeze do
user = User.create name: 'Fred', age: 20
user.reload.created_at.should == Time.zone.now
end
end
end
If you want to jump on the RSpec irc channel sometime, I'd be happy to help live debug.
Hi The same Error arise on ruby 2.0.0p353 and timecop-0.7.0. This code didn't work:
time = Time.now
Timecop.freeze(time) do
with:
Timecop.freeze(Time.local(2013,12,2, 0, 0, 0)) do
time = Time.now
Got it fixed.
I can't understand why it works. Freezing Time on a given variabel time or on Time.local, why does it work?
Sure, it is not clear what I mean. I used to test a controller methnod:
time = Time.now
Timecop.freeze(time) do
stub_relation.should_receive(:update_all).with(data_export_at: time)
get :data_export, format: :csv
end
This worked fine with ruby 1.9.3-p327. But raised an error with ruby 1.9.3-p484 and 2.0.0-p353 because of the disparity of the expected and the get time. A debug showes that the ineqaulity based on nanoseconds.
With:
Timecop.freeze(Time.local(2013,12,2, 0, 0, 0)) do
time = Time.now
stub_relation.should_receive(:update_all).with(data_export_at: time)
get :data_export, format: :csv
end
It works!!
I hope I described it clearly. thx
After upgrading my Rails 2.3 application from Ruby 1.8.7 to 1.9.3 I faced the same problem.
I've been having issues with nanoseconds - its worth noting that if you're seeing specs pass locally but fail on your CI server due to a nanosecond issue, and you're running OSX locally, that OSX doesn't support nanoseconds so Time will always have microsecond precision, just like the value coming out of your DB, so you won't see any issues. If you'r CI server is on Linux it will have nanosecond precision.
The trick is to freeze time to something with microsecond precision rather than using an arbitrary time like Time.now. It would be nice if you could configure Timecop to truncate nanoseconds automatically (unless that level of precision is important to you). You can do this in Rails using the #change method:
Time.zone.now.change(nsec: 0)
@lukeredpath This makes sense... Thank you. I will try it.
Please note that my above recommended fix only worked in Rails 3 by accident and no longer works in Rails 4 as Time.change doesn't support nsec. See https://github.com/rails/rails/issues/16392
FWIW, seeing this on Rails 4.2.1, Timecop 0.7.3
Seeing with Rails 4.2.3, Timecop 0.7.4 as well.