diff options
Diffstat (limited to 'activesupport')
-rw-r--r-- | activesupport/CHANGELOG.md | 33 | ||||
-rw-r--r-- | activesupport/lib/active_support/concurrency/latch.rb | 17 | ||||
-rw-r--r-- | activesupport/lib/active_support/concurrency/share_lock.rb | 50 | ||||
-rw-r--r-- | activesupport/lib/active_support/dependencies/interlock.rb | 4 | ||||
-rw-r--r-- | activesupport/lib/active_support/inflector/methods.rb | 8 | ||||
-rw-r--r-- | activesupport/lib/active_support/key_generator.rb | 8 | ||||
-rw-r--r-- | activesupport/lib/active_support/testing/time_helpers.rb | 51 | ||||
-rw-r--r-- | activesupport/test/caching_test.rb | 2 | ||||
-rw-r--r-- | activesupport/test/key_generator_test.rb | 17 | ||||
-rw-r--r-- | activesupport/test/message_encryptor_test.rb | 4 | ||||
-rw-r--r-- | activesupport/test/multibyte_conformance_test.rb | 20 | ||||
-rw-r--r-- | activesupport/test/multibyte_grapheme_break_conformance_test.rb | 32 | ||||
-rw-r--r-- | activesupport/test/multibyte_normalization_conformance_test.rb | 27 | ||||
-rw-r--r-- | activesupport/test/multibyte_test_helpers.rb | 22 | ||||
-rw-r--r-- | activesupport/test/testing/file_fixtures_test.rb | 6 | ||||
-rw-r--r-- | activesupport/test/time_travel_test.rb | 43 |
16 files changed, 245 insertions, 99 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 01ff1a858d..1c6f340fba 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,34 @@ -* Fix parsing JSON time in `YYYY-MM-DD hh:mm:ss` (without `Z`). - Before such time was considered in UTC timezone, now, to comply with standard, it uses local timezone. +* `travel/travel_to` travel time helpers, now raise on nested calls, + as this can lead to confusing time stubbing. + + Instead of: + + travel_to 2.days.from_now do + # 2 days from today + travel_to 3.days.from_now do + # 5 days from today + end + end + + preferred way to achieve above is: + + travel 2.days do + # 2 days from today + end + + travel 5.days do + # 5 days from today + end + + *Vipul A M* + + +* Support parsing JSON time in ISO8601 local time strings in + `ActiveSupport::JSON.decode` when `parse_json_times` is enabled. + Strings in the format of `YYYY-MM-DD hh:mm:ss` (without a `Z` at + the end) will be parsed in the local timezone (`Time.zone`). In + addition, date strings (`YYYY-MM-DD`) are now parsed into `Date` + objects. *Grzegorz Witek* diff --git a/activesupport/lib/active_support/concurrency/latch.rb b/activesupport/lib/active_support/concurrency/latch.rb index 4abe5ece6f..35074265b8 100644 --- a/activesupport/lib/active_support/concurrency/latch.rb +++ b/activesupport/lib/active_support/concurrency/latch.rb @@ -2,17 +2,24 @@ require 'concurrent/atomic/count_down_latch' module ActiveSupport module Concurrency - class Latch < Concurrent::CountDownLatch + class Latch def initialize(count = 1) - ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::CountDownLatch instead.") - super(count) + if count == 1 + ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::Event instead.") + else + ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::CountDownLatch instead.") + end + + @inner = Concurrent::CountDownLatch.new(count) end - alias_method :release, :count_down + def release + @inner.count_down + end def await - wait(nil) + @inner.wait(nil) end end end diff --git a/activesupport/lib/active_support/concurrency/share_lock.rb b/activesupport/lib/active_support/concurrency/share_lock.rb index 89e63aefd4..cf632ea7b0 100644 --- a/activesupport/lib/active_support/concurrency/share_lock.rb +++ b/activesupport/lib/active_support/concurrency/share_lock.rb @@ -14,6 +14,38 @@ module ActiveSupport # to upgrade share locks to exclusive. + def raw_state # :nodoc: + synchronize do + threads = @sleeping.keys | @sharing.keys | @waiting.keys + threads |= [@exclusive_thread] if @exclusive_thread + + data = {} + + threads.each do |thread| + purpose, compatible = @waiting[thread] + + data[thread] = { + thread: thread, + sharing: @sharing[thread], + exclusive: @exclusive_thread == thread, + purpose: purpose, + compatible: compatible, + waiting: !!@waiting[thread], + sleeper: @sleeping[thread], + } + end + + # NB: Yields while holding our *internal* synchronize lock, + # which is supposed to be used only for a few instructions at + # a time. This allows the caller to inspect additional state + # without things changing out from underneath, but would have + # disastrous effects upon normal operation. Fortunately, this + # method is only intended to be called when things have + # already gone wrong. + yield data + end + end + def initialize super() @@ -21,6 +53,7 @@ module ActiveSupport @sharing = Hash.new(0) @waiting = {} + @sleeping = {} @exclusive_thread = nil @exclusive_depth = 0 end @@ -46,7 +79,7 @@ module ActiveSupport return false if no_wait yield_shares(purpose: purpose, compatible: compatible, block_share: true) do - @cv.wait_while { busy_for_exclusive?(purpose) } + wait_for(:start_exclusive) { busy_for_exclusive?(purpose) } end end @exclusive_thread = Thread.current @@ -69,7 +102,7 @@ module ActiveSupport if eligible_waiters?(compatible) yield_shares(compatible: compatible, block_share: true) do - @cv.wait_while { @exclusive_thread || eligible_waiters?(compatible) } + wait_for(:stop_exclusive) { @exclusive_thread || eligible_waiters?(compatible) } end end @cv.broadcast @@ -84,11 +117,11 @@ module ActiveSupport elsif @waiting[Thread.current] # We're nested inside a +yield_shares+ call: we'll resume as # soon as there isn't an exclusive lock in our way - @cv.wait_while { @exclusive_thread } + wait_for(:start_sharing) { @exclusive_thread } else # This is an initial / outermost share call: any outstanding # requests for an exclusive lock get to go first - @cv.wait_while { busy_for_sharing?(false) } + wait_for(:start_sharing) { busy_for_sharing?(false) } end @sharing[Thread.current] += 1 end @@ -153,7 +186,7 @@ module ActiveSupport yield ensure synchronize do - @cv.wait_while { @exclusive_thread && @exclusive_thread != Thread.current } + wait_for(:yield_shares) { @exclusive_thread && @exclusive_thread != Thread.current } if previous_wait @waiting[Thread.current] = previous_wait @@ -181,6 +214,13 @@ module ActiveSupport def eligible_waiters?(compatible) @waiting.any? { |t, (p, _)| compatible.include?(p) && @waiting.all? { |t2, (_, c2)| t == t2 || c2.include?(p) } } end + + def wait_for(method) + @sleeping[Thread.current] = method + @cv.wait_while { yield } + ensure + @sleeping.delete Thread.current + end end end end diff --git a/activesupport/lib/active_support/dependencies/interlock.rb b/activesupport/lib/active_support/dependencies/interlock.rb index f1865ca2f8..75b769574c 100644 --- a/activesupport/lib/active_support/dependencies/interlock.rb +++ b/activesupport/lib/active_support/dependencies/interlock.rb @@ -46,6 +46,10 @@ module ActiveSupport #:nodoc: yield end end + + def raw_state(&block) # :nodoc: + @lock.raw_state(&block) + end end end end diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index f94e12e14f..f14b8b81b7 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -238,8 +238,8 @@ module ActiveSupport # Tries to find a constant with the name specified in the argument string. # - # 'Module'.constantize # => Module - # 'Foo::Bar'.constantize # => Foo::Bar + # constantize('Module') # => Module + # constantize('Foo::Bar') # => Foo::Bar # # The name is assumed to be the one of a top-level constant, no matter # whether it starts with "::" or not. No lexical context is taken into @@ -248,8 +248,8 @@ module ActiveSupport # C = 'outside' # module M # C = 'inside' - # C # => 'inside' - # 'C'.constantize # => 'outside', same as ::C + # C # => 'inside' + # constantize('C') # => 'outside', same as ::C # end # # NameError is raised when the name is not in CamelCase or the constant is diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 7f73f9ddfc..0f0e931c06 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -31,11 +31,9 @@ module ActiveSupport @cache_keys = Concurrent::Map.new end - # Returns a derived key suitable for use. The default key_size is chosen - # to be compatible with the default settings of ActiveSupport::MessageVerifier. - # i.e. OpenSSL::Digest::SHA1#block_length - def generate_key(salt, key_size=64) - @cache_keys["#{salt}#{key_size}"] ||= @key_generator.generate_key(salt, key_size) + # Returns a derived key suitable for use. + def generate_key(*args) + @cache_keys[args.join] ||= @key_generator.generate_key(*args) end end diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb index ef27ce8eb5..41dff281ef 100644 --- a/activesupport/lib/active_support/testing/time_helpers.rb +++ b/activesupport/lib/active_support/testing/time_helpers.rb @@ -1,32 +1,39 @@ +require 'active_support/core_ext/string/strip' # for strip_heredoc +require 'concurrent/map' + module ActiveSupport module Testing class SimpleStubs # :nodoc: Stub = Struct.new(:object, :method_name, :original_method) def initialize - @stubs = {} + @stubs = Concurrent::Map.new { |h, k| h[k] = {} } end def stub_object(object, method_name, return_value) - key = [object.object_id, method_name] - - if stub = @stubs[key] + if stub = stubbing(object, method_name) unstub_object(stub) end new_name = "__simple_stub__#{method_name}" - @stubs[key] = Stub.new(object, method_name, new_name) + @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name) object.singleton_class.send :alias_method, new_name, method_name object.define_singleton_method(method_name) { return_value } end def unstub_all! - @stubs.each_value do |stub| - unstub_object(stub) + @stubs.each_value do |object_stubs| + object_stubs.each_value do |stub| + unstub_object(stub) + end end - @stubs = {} + @stubs.clear + end + + def stubbing(object, method_name) + @stubs[object.object_id][method_name] end private @@ -93,6 +100,34 @@ module ActiveSupport # end # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 def travel_to(date_or_time) + if block_given? && simple_stubs.stubbing(Time, :now) + travel_to_nested_block_call = <<-MSG.strip_heredoc + + Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing. + + Instead of: + + travel_to 2.days.from_now do + # 2 days from today + travel_to 3.days.from_now do + # 5 days from today + end + end + + preferred way to achieve above is: + + travel 2.days do + # 2 days from today + end + + travel 5.days do + # 5 days from today + end + + MSG + raise travel_to_nested_block_call + end + if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime) now = date_or_time.midnight.to_time else diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index ec7d028d7e..8002c14539 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -3,6 +3,8 @@ require 'abstract_unit' require 'active_support/cache' require 'dependencies_test_helpers' +require 'pathname' + module ActiveSupport module Cache module Strategy diff --git a/activesupport/test/key_generator_test.rb b/activesupport/test/key_generator_test.rb index f7e8e9a795..b60077460e 100644 --- a/activesupport/test/key_generator_test.rb +++ b/activesupport/test/key_generator_test.rb @@ -19,7 +19,7 @@ class KeyGeneratorTest < ActiveSupport::TestCase test "Generating a key of the default length" do derived_key = @generator.generate_key("some_salt") assert_kind_of String, derived_key - assert_equal OpenSSL::Digest::SHA1.new.block_length, derived_key.length, "Should have generated a key of the default size" + assert_equal 64, derived_key.length, "Should have generated a key of the default size" end test "Generating a key of an alternative length" do @@ -27,6 +27,21 @@ class KeyGeneratorTest < ActiveSupport::TestCase assert_kind_of String, derived_key assert_equal 32, derived_key.length, "Should have generated a key of the right size" end + + test "Expected results" do + # For any given set of inputs, this method must continue to return + # the same output: if it changes, any existing values relying on a + # key would break. + + expected = "b129376f68f1ecae788d7433310249d65ceec090ecacd4c872a3a9e9ec78e055739be5cc6956345d5ae38e7e1daa66f1de587dc8da2bf9e8b965af4b3918a122" + assert_equal expected, ActiveSupport::KeyGenerator.new("0" * 64).generate_key("some_salt").unpack('H*').first + + expected = "b129376f68f1ecae788d7433310249d65ceec090ecacd4c872a3a9e9ec78e055" + assert_equal expected, ActiveSupport::KeyGenerator.new("0" * 64).generate_key("some_salt", 32).unpack('H*').first + + expected = "cbea7f7f47df705967dc508f4e446fd99e7797b1d70011c6899cd39bbe62907b8508337d678505a7dc8184e037f1003ba3d19fc5d829454668e91d2518692eae" + assert_equal expected, ActiveSupport::KeyGenerator.new("0" * 64, iterations: 2).generate_key("some_salt").unpack('H*').first + end end class CachingKeyGeneratorTest < ActiveSupport::TestCase diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index eb71369397..a1ff4c1d3e 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -15,7 +15,7 @@ class MessageEncryptorTest < ActiveSupport::TestCase end def setup - @secret = SecureRandom.hex(64) + @secret = SecureRandom.random_bytes(32) @verifier = ActiveSupport::MessageVerifier.new(@secret, :serializer => ActiveSupport::MessageEncryptor::NullSerializer) @encryptor = ActiveSupport::MessageEncryptor.new(@secret) @data = { :some => "data", :now => Time.local(2010) } @@ -51,7 +51,7 @@ class MessageEncryptorTest < ActiveSupport::TestCase def test_alternative_serialization_method prev = ActiveSupport.use_standard_json_time_format ActiveSupport.use_standard_json_time_format = true - encryptor = ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64), SecureRandom.hex(64), :serializer => JSONSerializer.new) + encryptor = ActiveSupport::MessageEncryptor.new(SecureRandom.random_bytes(32), SecureRandom.random_bytes(128), :serializer => JSONSerializer.new) message = encryptor.encrypt_and_sign({ :foo => 123, 'bar' => Time.utc(2010) }) exp = { "foo" => 123, "bar" => "2010-01-01T00:00:00.000Z" } assert_equal exp, encryptor.decrypt_and_verify(message) diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb index 9fca47a985..c10133a7a6 100644 --- a/activesupport/test/multibyte_conformance_test.rb +++ b/activesupport/test/multibyte_conformance_test.rb @@ -6,29 +6,9 @@ require 'open-uri' require 'tmpdir' class MultibyteConformanceTest < ActiveSupport::TestCase - class Downloader - def self.download(from, to) - unless File.exist?(to) - unless File.exist?(File.dirname(to)) - system "mkdir -p #{File.dirname(to)}" - end - open(from) do |source| - File.open(to, 'w') do |target| - source.each_line do |l| - target.write l - end - end - end - end - true - end - end - include MultibyteTestHelpers - UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd" UNIDATA_FILE = '/NormalizationTest.txt' - CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance" FileUtils.mkdir_p(CACHE_DIR) RUN_P = begin Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE) diff --git a/activesupport/test/multibyte_grapheme_break_conformance_test.rb b/activesupport/test/multibyte_grapheme_break_conformance_test.rb index 6e2f02abed..61943b1ab3 100644 --- a/activesupport/test/multibyte_grapheme_break_conformance_test.rb +++ b/activesupport/test/multibyte_grapheme_break_conformance_test.rb @@ -1,37 +1,23 @@ # encoding: utf-8 require 'abstract_unit' +require 'multibyte_test_helpers' require 'fileutils' require 'open-uri' require 'tmpdir' class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase - class Downloader - def self.download(from, to) - unless File.exist?(to) - $stderr.puts "Downloading #{from} to #{to}" - unless File.exist?(File.dirname(to)) - system "mkdir -p #{File.dirname(to)}" - end - open(from) do |source| - File.open(to, 'w') do |target| - source.each_line do |l| - target.write l - end - end - end - end - end - end + include MultibyteTestHelpers - TEST_DATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd/auxiliary" - TEST_DATA_FILE = '/GraphemeBreakTest.txt' - CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance" + UNIDATA_FILE = '/auxiliary/GraphemeBreakTest.txt' + RUN_P = begin + Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE) + rescue + end def setup - FileUtils.mkdir_p(CACHE_DIR) - Downloader.download(TEST_DATA_URL + TEST_DATA_FILE, CACHE_DIR + TEST_DATA_FILE) + skip "Unable to download test data" unless RUN_P end def test_breaks @@ -46,7 +32,7 @@ class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase def each_line_of_break_tests(&block) lines = 0 max_test_lines = 0 # Don't limit below 21, because that's the header of the testfile - File.open(File.join(CACHE_DIR, TEST_DATA_FILE), 'r') do | f | + File.open(File.join(CACHE_DIR, UNIDATA_FILE), 'r') do | f | until f.eof? || (max_test_lines > 21 and lines > max_test_lines) lines += 1 line = f.gets.chomp! diff --git a/activesupport/test/multibyte_normalization_conformance_test.rb b/activesupport/test/multibyte_normalization_conformance_test.rb index 0d31c9520f..77ed0ce6de 100644 --- a/activesupport/test/multibyte_normalization_conformance_test.rb +++ b/activesupport/test/multibyte_normalization_conformance_test.rb @@ -8,34 +8,17 @@ require 'open-uri' require 'tmpdir' class MultibyteNormalizationConformanceTest < ActiveSupport::TestCase - class Downloader - def self.download(from, to) - unless File.exist?(to) - $stderr.puts "Downloading #{from} to #{to}" - unless File.exist?(File.dirname(to)) - system "mkdir -p #{File.dirname(to)}" - end - open(from) do |source| - File.open(to, 'w') do |target| - source.each_line do |l| - target.write l - end - end - end - end - end - end - include MultibyteTestHelpers - UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd" UNIDATA_FILE = '/NormalizationTest.txt' - CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance" + RUN_P = begin + Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE) + rescue + end def setup - FileUtils.mkdir_p(CACHE_DIR) - Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE) @proxy = ActiveSupport::Multibyte::Chars + skip "Unable to download test data" unless RUN_P end def test_normalizations_C diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb index 58cf5488cd..0ada8bc032 100644 --- a/activesupport/test/multibyte_test_helpers.rb +++ b/activesupport/test/multibyte_test_helpers.rb @@ -1,4 +1,26 @@ module MultibyteTestHelpers + class Downloader + def self.download(from, to) + unless File.exist?(to) + unless File.exist?(File.dirname(to)) + system "mkdir -p #{File.dirname(to)}" + end + open(from) do |source| + File.open(to, 'w') do |target| + source.each_line do |l| + target.write l + end + end + end + end + true + end + end + + UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd" + CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance" + FileUtils.mkdir_p(CACHE_DIR) + UNICODE_STRING = 'こにちわ'.freeze ASCII_STRING = 'ohayo'.freeze BYTE_STRING = "\270\236\010\210\245".force_encoding("ASCII-8BIT").freeze diff --git a/activesupport/test/testing/file_fixtures_test.rb b/activesupport/test/testing/file_fixtures_test.rb index 91b8a9071c..d98987fb8d 100644 --- a/activesupport/test/testing/file_fixtures_test.rb +++ b/activesupport/test/testing/file_fixtures_test.rb @@ -1,12 +1,14 @@ require 'abstract_unit' +require 'pathname' + class FileFixturesTest < ActiveSupport::TestCase self.file_fixture_path = File.expand_path("../../file_fixtures", __FILE__) test "#file_fixture returns Pathname to file fixture" do path = file_fixture("sample.txt") assert_kind_of Pathname, path - assert_match %r{activesupport/test/file_fixtures/sample.txt$}, path.to_s + assert_match %r{.*/test/file_fixtures/sample.txt$}, path.to_s end test "raises an exception when the fixture file does not exist" do @@ -23,6 +25,6 @@ class FileFixturesPathnameDirectoryTest < ActiveSupport::TestCase test "#file_fixture_path returns Pathname to file fixture" do path = file_fixture("sample.txt") assert_kind_of Pathname, path - assert_match %r{activesupport/test/file_fixtures/sample.txt$}, path.to_s + assert_match %r{.*/test/file_fixtures/sample.txt$}, path.to_s end end diff --git a/activesupport/test/time_travel_test.rb b/activesupport/test/time_travel_test.rb index 59c3e52c2f..e9f87bdf82 100644 --- a/activesupport/test/time_travel_test.rb +++ b/activesupport/test/time_travel_test.rb @@ -77,6 +77,49 @@ class TimeTravelTest < ActiveSupport::TestCase end end + def test_time_helper_travel_to_with_nested_calls_with_blocks + Time.stub(:now, Time.now) do + outer_expected_time = Time.new(2004, 11, 24, 01, 04, 44) + inner_expected_time = Time.new(2004, 10, 24, 01, 04, 44) + travel_to outer_expected_time do + assert_raises(RuntimeError, /Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing./) do + travel_to(inner_expected_time) do + #noop + end + end + end + end + end + + def test_time_helper_travel_to_with_nested_calls + Time.stub(:now, Time.now) do + outer_expected_time = Time.new(2004, 11, 24, 01, 04, 44) + inner_expected_time = Time.new(2004, 10, 24, 01, 04, 44) + travel_to outer_expected_time do + assert_nothing_raised do + travel_to(inner_expected_time) + + assert_equal inner_expected_time, Time.now + end + end + end + end + + def test_time_helper_travel_to_with_subsequent_calls + Time.stub(:now, Time.now) do + initial_expected_time = Time.new(2004, 11, 24, 01, 04, 44) + subsequent_expected_time = Time.new(2004, 10, 24, 01, 04, 44) + assert_nothing_raised do + travel_to initial_expected_time + travel_to subsequent_expected_time + + assert_equal subsequent_expected_time, Time.now + + travel_back + end + end + end + def test_travel_to_will_reset_the_usec_to_avoid_mysql_rouding Time.stub(:now, Time.now) do travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do |