timecop 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,29 @@
1
+ === 0.2.0 / 2008-12-23
2
+
3
+ * API Changes
4
+
5
+ * Timecop#travel no longer freezes time. Rather, it computes the current offset between the new "now" and the real "now", and
6
+ returns times as if Time had continued to move forward
7
+
8
+ * Timecop#freeze now behaves exactly as the old Timecop#travel behaved. Unless you depended on the actual freezing of time
9
+ (which I think would be rare), you should be able to continue to use #travel without worry.
10
+
11
+ * Timecop#return is now exposed (previously Timecop#unset_all, but not well advertised). It will completely unmock time,
12
+ and will probably be rarely used outside of the actual implementation of this library.
13
+
14
+ * More Test Coverage
15
+
16
+ * Tests now explicitly cover the cases when the Date and DateTime objects are not loaded, and ensures proper functionality
17
+ in their absence and existence.
18
+
19
+ * Still haven't done regression testing against anything other than a few version of 1.8.6 (including REE). We should
20
+ probably try to get this tested on both 1.8.7 and 1.9.1.
21
+
22
+ * Documentation
23
+
24
+ * Fixed up a lot of the poorly-formatted rdoc syntax. The public API should now be properly published in the rdoc,
25
+ and the internals are omitted.
26
+
1
27
  === 0.1.0 / 2008-11-09
2
28
 
3
29
  * Initial Feature Set
@@ -3,8 +3,12 @@ Manifest.txt
3
3
  README.txt
4
4
  Rakefile
5
5
  lib/timecop.rb
6
+ lib/timecop/stack_item.rb
6
7
  lib/timecop/time_extensions.rb
7
8
  lib/timecop/timecop.rb
8
9
  lib/timecop/version.rb
10
+ test/run_tests.sh
9
11
  test/test_timecop.rb
12
+ test/test_timecop_internals.rb
13
+ test/test_timecop_without_date.rb
10
14
  timecop.gemspec
data/README.txt CHANGED
@@ -4,14 +4,18 @@
4
4
 
5
5
  == DESCRIPTION:
6
6
 
7
- A gem providing simple ways to (temporarily) override Time.now, Date.today, and DateTime.now. It provides "time travel" capabilities, making it dead simple to write test time-dependent code.
7
+ A gem providing simple ways to mock Time.now, Date.today, and DateTime.now. It provides "time travel" and "time freezing" capabilities, making it dead simple to write test time-dependent code.
8
8
 
9
- == FEATURES/PROBLEMS:
9
+ == FEATURES:
10
10
 
11
- * Temporarily (or permanently if you prefer) change the concept of Time.now, DateTime.now (if defined), and Date.today (if defined)
12
- * Timecop#travel api allows an argument to be passed in as one of: 1) Time instance, 2) DateTime instance, 3) Date instance,
11
+ * Temporarily (or permanently if you prefer) change the concept of Time.now, DateTime.now, and Date.today
12
+ * Timecop api allows an arguments to be passed into #freeze and #travel as one of: 1) Time instance, 2) DateTime instance, 3) Date instance,
13
13
  4) individual arguments (year, month, day, hour, minute, second)
14
- * Nested calls to Timecop#travel are supported -- each block will maintain it's interpretation of now.
14
+ * Nested calls to Timecop#travel and Timecop#freeze are supported -- each block will maintain it's interpretation of now.
15
+
16
+ == SHORTCOMINGS:
17
+
18
+ * Only fully tested on the 1.8.6 Ruby implementations. 1.8.7 and 1.9.1 should be tested, as well as other flavors (jruby, etc.)
15
19
 
16
20
  == SYNOPSIS:
17
21
 
@@ -21,7 +25,7 @@ A gem providing simple ways to (temporarily) override Time.now, Date.today, and
21
25
  joe.purchase_home()
22
26
  assert !joe.mortgage_due?
23
27
  # move ahead a month and assert that the mortgage is due
24
- Timecop.travel(Date.today + 30) do
28
+ Timecop.freeze(Date.today + 30) do
25
29
  assert joe.mortgage_due?
26
30
  end
27
31
  </code>
data/Rakefile CHANGED
@@ -31,3 +31,13 @@ end
31
31
 
32
32
  # vim: syntax=Ruby
33
33
 
34
+ # Override the test task and instruct them how to actually run the tests.
35
+ Rake.application.send(:eval, "@tasks.delete('test')")
36
+ desc "Does not execute tests. Manually run shell script ./run_tests.sh to execute tests."
37
+ task :test do
38
+ puts <<-MSG
39
+ In order to run the test suite, run: cd test && ./run_tests.sh
40
+ The tests need to be run with different libraries loaded, which rules out using Rake
41
+ to automate them.
42
+ MSG
43
+ end
@@ -0,0 +1,11 @@
1
+
2
+ # Simply a data class for carrying around "time movement" objects. Makes it easy to keep track of the time
3
+ # movements on a simple stack.
4
+ class StackItem
5
+
6
+ attr_reader :mock_type, :year, :month, :day, :hour, :minute, :second
7
+ def initialize(mock_type, year, month, day, hour, minute, second)
8
+ @mock_type, @year, @month, @day, @hour, @minute, @second = mock_type, year, month, day, hour, minute, second
9
+ end
10
+ end
11
+
@@ -7,11 +7,33 @@
7
7
  class Time
8
8
  class << self
9
9
  # Time we might be behaving as
10
- attr_reader :mock_time
10
+ #attr_reader :mock_time
11
+
12
+ @@mock_offset = nil
13
+ @@mock_time = nil
14
+
15
+ def mock_time
16
+ if !@@mock_offset.nil?
17
+ now_without_mock_time - @@mock_offset
18
+ else
19
+ @@mock_time
20
+ end
21
+ end
11
22
 
12
23
  # Set new time to pretend we are.
13
- def mock_time=(new_now)
14
- @mock_time = new_now
24
+ def freeze_time(new_now)
25
+ @@mock_time = new_now
26
+ @@mock_offset = nil
27
+ end
28
+
29
+ def move_time(new_now)
30
+ @@mock_offset = new_now.nil? ? nil : (now_without_mock_time - new_now)
31
+ @@mock_time = nil
32
+ end
33
+
34
+ # Restores Time to system clock
35
+ def unmock!
36
+ move_time(nil)
15
37
  end
16
38
 
17
39
  # Alias the original now
@@ -30,14 +52,12 @@ end
30
52
  if Object.const_defined?(:Date)
31
53
  class Date
32
54
  class << self
33
- # Date we might be behaving as
34
- attr_reader :mock_date
35
-
36
- # Set new date to pretend we are.
37
- def mock_date=(new_date)
38
- @mock_date = new_date
55
+ def mock_date
56
+ now = Time.mock_time
57
+ return nil if now.nil?
58
+ Date.new(now.year, now.month, now.day)
39
59
  end
40
-
60
+
41
61
  # Alias the original today
42
62
  alias_method :today_without_mock_date, :today
43
63
 
@@ -55,22 +75,20 @@ end
55
75
  if Object.const_defined?(:DateTime)
56
76
  class DateTime
57
77
  class << self
58
- # Time we might be behaving as
59
- attr_reader :mock_time
60
-
61
- # Set new time to pretend we are.
62
- def mock_time=(new_now)
63
- @mock_time = new_now
78
+ def mock_time
79
+ t_now = Time.mock_time
80
+ return nil if t_now.nil?
81
+ DateTime.new(t_now.year, t_now.month, t_now.day, t_now.hour, t_now.min, t_now.sec)
64
82
  end
65
-
83
+
66
84
  # Alias the original now
67
85
  alias_method :now_without_mock_time, :now
68
-
86
+
69
87
  # Define now_with_mock_time
70
88
  def now_with_mock_time
71
89
  mock_time || now_without_mock_time
72
90
  end
73
-
91
+
74
92
  # Alias now to now_with_mock_time
75
93
  alias_method :now, :now_with_mock_time
76
94
  end
@@ -1,39 +1,17 @@
1
+ require 'singleton'
1
2
  require File.join(File.dirname(__FILE__), 'time_extensions')
3
+ require File.join(File.dirname(__FILE__), 'stack_item')
2
4
 
3
- # 1. Wrapper class for manipulating the extensions to the Time, Date, and DateTime objects
4
- # 2. Allows us to "freeze" time in our Ruby applications.
5
- # 3. This is very useful when your app's functionality is dependent on time (e.g.
5
+ # Timecop
6
+ # * Wrapper class for manipulating the extensions to the Time, Date, and DateTime objects
7
+ # * Allows us to "freeze" time in our Ruby applications.
8
+ # * Optionally allows time travel to simulate a running clock, such time is not technically frozen.
9
+ #
10
+ # This is very useful when your app's functionality is dependent on time (e.g.
6
11
  # anything that might expire). This will allow us to alter the return value of
7
12
  # Date.today, Time.now, and DateTime.now, such that our application code _never_ has to change.
8
13
  class Timecop
9
-
10
- # Re-bases Time.now, Date.today and DateTime.now (if it exists) to use the time passed in.
11
- # When using this method directly, it is up to the developer to call unset_all to return us
12
- # to sanity.
13
- #
14
- # * If being consumed in a rails app, Time.zone.local will be used to instantiate the time.
15
- # Otherwise, Time.local will be used.
16
- def self.set_all(year, month, day, hour=0, minute=0, second=0)
17
- if Time.respond_to?(:zone) && !Time.zone.nil?
18
- # ActiveSupport loaded
19
- time = Time.zone.local(year, month, day, hour, minute, second)
20
- else
21
- # ActiveSupport not loaded
22
- time = Time.local(year, month, day, hour, minute, second)
23
- end
24
-
25
- Time.mock_time = time
26
- Date.mock_date = Date.new(time.year, time.month, time.day) if Object.const_defined?(:Date)
27
- DateTime.mock_time = DateTime.new(time.year, time.month, time.day, time.hour, time.min, time.sec) if Object.const_defined?(:DateTime)
28
- end
29
-
30
- # Reverts back to system's Time.now, Date.today and DateTime.now (if it exists). If set_all
31
- # was never called in the first place, this method will have no effect.
32
- def self.unset_all
33
- Date.mock_date = nil
34
- DateTime.mock_time = nil if Object.const_defined?(:Date)
35
- Time.mock_time = nil if Object.const_defined?(:DateTime)
36
- end
14
+ include Singleton
37
15
 
38
16
  # Allows you to run a block of code and "fake" a time throughout the execution of that block.
39
17
  # This is particularly useful for writing test methods where the passage of time is critical to the business
@@ -43,49 +21,145 @@ class Timecop
43
21
  # joe = User.find(1)
44
22
  # joe.purchase_home()
45
23
  # assert !joe.mortgage_due?
46
- # Timecop.travel(2008, 10, 5) do
24
+ # Timecop.freeze(2008, 10, 5) do
47
25
  # assert joe.mortgage_due?
48
26
  # end
49
27
  # </code>
50
28
  #
51
- # travel will respond to several different arguments:
52
- # 1. Timecop.travel(time_inst)
53
- # 2. Timecop.travel(datetime_inst)
54
- # 3. Timecop.travel(date_inst)
55
- # 4. Timecop.travel(year, month, day, hour=0, minute=0, second=0)
29
+ # freeze and travel will respond to several different arguments:
30
+ # 1. Timecop.freeze(time_inst)
31
+ # 2. Timecop.freeze(datetime_inst)
32
+ # 3. Timecop.freeze(date_inst)
33
+ # 4. Timecop.freeze(year, month, day, hour=0, minute=0, second=0)
56
34
  #
57
- # When a block is also passed, the Time.now, DateTime.now and Date.today are all reset to their
35
+ # When a block is also passed, Time.now, DateTime.now and Date.today are all reset to their
58
36
  # previous values. This allows us to nest multiple calls to Timecop.travel and have each block
59
37
  # maintain it's concept of "now."
60
- #def self.travel(year, month, day, hour=0, minute=0, second=0, &block)
38
+ #
39
+ # * Note: Timecop.freeze will actually freeze time. This can cause unanticipated problems if
40
+ # benchmark or other timing calls are executed, which implicitly expect Time to actually move
41
+ # forward.
42
+ #
43
+ # * Rails Users: Be especially careful when setting this in your development environment in a
44
+ # rails project. Generators will load your environment, including the migration generator,
45
+ # which will lead to files being generated with the timestamp set by the Timecop.freeze call
46
+ # in your dev environment
47
+ def self.freeze(*args, &block)
48
+ instance().send(:travel, :freeze, *args, &block)
49
+ end
50
+
51
+ # Allows you to run a block of code and "fake" a time throughout the execution of that block.
52
+ # See Timecop#freeze for a sample of how to use (same exact usage syntax)
53
+ #
54
+ # * Note: Timecop.travel will not freeze time (as opposed to Timecop.freeze). This is a particularly
55
+ # good candidate for use in environment files in rails projects.
61
56
  def self.travel(*args, &block)
62
- year, month, day, hour, minute, second = parse_travel_args(*args)
57
+ instance().send(:travel, :move, *args, &block)
58
+ end
59
+
60
+ # Reverts back to system's Time.now, Date.today and DateTime.now (if it exists). If freeze_all or rebase_all
61
+ # was never called in the first place, this method will have no effect.
62
+ def self.return
63
+ instance().send(:unmock!)
64
+ end
63
65
 
64
- old_time = Time.mock_time
65
- old_date = Date.mock_date if Object.const_defined?(:Date)
66
- old_datetime = DateTime.mock_time if Object.const_defined?(:DateTime)
66
+ # [Deprecated]: See Timecop#return instead.
67
+ def self.unset_all
68
+ $stderr.puts "Timecop#unset_all is deprecated. Please use Timecop#return instead."
69
+ $stderr.flush
70
+ self.return
71
+ end
72
+
73
+ protected
74
+
75
+ def initialize
76
+ @_stack = []
77
+ end
78
+
79
+ def travel(mock_type, *args, &block)
80
+ # parse the arguments, build our base time units
81
+ year, month, day, hour, minute, second = parse_travel_args(*args)
67
82
 
68
- set_all(year, month, day, hour, minute, second)
83
+ # perform our action
84
+ if mock_type == :freeze
85
+ freeze_all(year, month, day, hour, minute, second)
86
+ else
87
+ move_all(year, month, day, hour, minute, second)
88
+ end
89
+ # store this time traveling on our stack...
90
+ @_stack << StackItem.new(mock_type, year, month, day, hour, minute, second)
69
91
 
70
- if block_given?
71
- begin
72
- yield
73
- ensure
74
- Time.mock_time = old_time
75
- Date.mock_date = old_date if Object.const_defined?(:Date)
76
- DateTime.mock_time = old_datetime if Object.const_defined?(:DateTime)
92
+ if block_given?
93
+ begin
94
+ yield
95
+ ensure
96
+ # pull it off the stack...
97
+ stack_item = @_stack.pop
98
+ if @_stack.size == 0
99
+ # completely unmock if there's nothing to revert back to
100
+ unmock!
101
+ else
102
+ # or reinstantiate the new the top of the stack (could be a :freeze or a :move)
103
+ new_top = @_stack.last
104
+ if new_top.mock_type == :freeze
105
+ freeze_all(new_top.year, new_top.month, new_top.day, new_top.hour, new_top.minute, new_top.second)
106
+ else
107
+ move_all(new_top.year, new_top.month, new_top.day, new_top.hour, new_top.minute, new_top.second)
108
+ end
109
+ end
110
+ end
77
111
  end
78
112
  end
79
- end
113
+
114
+ def unmock!
115
+ Time.unmock!
116
+ end
80
117
 
81
118
  private
82
- def self.parse_travel_args(*args)
119
+
120
+ # Re-bases Time.now, Date.today and DateTime.now (if it exists) to use the time passed in.
121
+ # When using this method directly, it is up to the developer to call unset_all to return us
122
+ # to sanity.
123
+ #
124
+ # * If being consumed in a rails app, Time.zone.local will be used to instantiate the time.
125
+ # Otherwise, Time.local will be used.
126
+ def freeze_all(year, month, day, hour=0, minute=0, second=0)
127
+ if Time.respond_to?(:zone) && !Time.zone.nil?
128
+ # ActiveSupport loaded
129
+ time = Time.zone.local(year, month, day, hour, minute, second)
130
+ else
131
+ # ActiveSupport not loaded
132
+ time = Time.local(year, month, day, hour, minute, second)
133
+ end
134
+
135
+ Time.freeze_time(time)
136
+ end
137
+
138
+ # Re-bases Time.now, Date.today and DateTime.now to use the time passed in and to continue moving time
139
+ # forward. When using this method directly, it is up to the developer to call return to return us to
140
+ # sanity.
141
+ #
142
+ # * If being consumed in a rails app, Time.zone.local will be used to instantiate the time.
143
+ # Otherwise, Time.local will be used.
144
+ def move_all(year, month, day, hour=0, minute=0, second=0)
145
+ if Time.respond_to?(:zone) && !Time.zone.nil?
146
+ # ActiveSupport loaded
147
+ time = Time.zone.local(year, month, day, hour, minute, second)
148
+ else
149
+ # ActiveSupport not loaded
150
+ time = Time.local(year, month, day, hour, minute, second)
151
+ end
152
+
153
+ Time.move_time(time)
154
+ end
155
+
156
+ def parse_travel_args(*args)
83
157
  arg = args.shift
84
158
  if arg.is_a?(Time) || (Object.const_defined?(:DateTime) && arg.is_a?(DateTime))
85
159
  year, month, day, hour, minute, second = arg.year, arg.month, arg.day, arg.hour, arg.min, arg.sec
86
160
  elsif Object.const_defined?(:Date) && arg.is_a?(Date)
87
161
  year, month, day, hour, minute, second = arg.year, arg.month, arg.day, 0, 0, 0
88
- puts "#{year}-#{month}-#{day} #{hour}:#{minute}:#{second}"
162
+ #puts "#{year}-#{month}-#{day} #{hour}:#{minute}:#{second}"
89
163
  else # we'll just assume it's a list of y/m/h/d/m/s
90
164
  year = arg || 0
91
165
  month = args.shift || 1
@@ -11,7 +11,7 @@ module Timecop
11
11
  end
12
12
 
13
13
  MAJOR = 0
14
- MINOR = 1
14
+ MINOR = 2
15
15
  TINY = 0
16
16
 
17
17
  STRING = [MAJOR, MINOR, TINY].join(".")
@@ -0,0 +1,10 @@
1
+ #!/bin/sh
2
+
3
+ echo "\033[1;81m Running test_timecop_internals...\033[0m"
4
+ ruby test_timecop_internals.rb || (echo "FAILED!!!!!!!!!!!!")
5
+
6
+ echo "\033[1;81m Running test_timecop_without_date...\033[0m"
7
+ ruby test_timecop_without_date.rb || (echo "FAILED!!!!!!!!!!!!")
8
+
9
+ echo "\033[1;81m Running test_timecop...\033[0m"
10
+ ruby test_timecop.rb || (echo "FAILED!!!!!!!!!!!!")
@@ -11,37 +11,37 @@ class TestTimecop < Test::Unit::TestCase
11
11
 
12
12
  # just in case...let's really make sure that Timecop is disabled between tests...
13
13
  def teardown
14
- Timecop.unset_all
14
+ Timecop.return
15
15
  end
16
16
 
17
- def test_travel_changes_and_resets_time
17
+ def test_freeze_changes_and_resets_time
18
18
  # depending on how we're invoked (individually or via the rake test suite)
19
19
  assert !Time.respond_to?(:zone) || Time.zone.nil?
20
20
 
21
21
  t = Time.local(2008, 10, 10, 10, 10, 10)
22
22
  assert_not_equal t, Time.now
23
- Timecop.travel(2008, 10, 10, 10, 10, 10) do
23
+ Timecop.freeze(2008, 10, 10, 10, 10, 10) do
24
24
  assert_equal t, Time.now
25
25
  end
26
26
  assert_not_equal t, Time.now
27
27
  end
28
28
 
29
- def test_recursive_travel
29
+ def test_recursive_freeze
30
30
  t = Time.local(2008, 10, 10, 10, 10, 10)
31
- Timecop.travel(2008, 10, 10, 10, 10, 10) do
31
+ Timecop.freeze(2008, 10, 10, 10, 10, 10) do
32
32
  assert_equal t, Time.now
33
33
  t2 = Time.local(2008, 9, 9, 9, 9, 9)
34
- Timecop.travel(2008, 9, 9, 9, 9, 9) do
34
+ Timecop.freeze(2008, 9, 9, 9, 9, 9) do
35
35
  assert_equal t2, Time.now
36
36
  end
37
37
  assert_equal t, Time.now
38
38
  end
39
- assert_nil Time.send(:mock_time)
39
+ assert_not_equal t, Time.now
40
40
  end
41
41
 
42
- def test_travel_with_time_instance_works_as_expected
42
+ def test_freeze_with_time_instance_works_as_expected
43
43
  t = Time.local(2008, 10, 10, 10, 10, 10)
44
- Timecop.travel(t) do
44
+ Timecop.freeze(t) do
45
45
  assert_equal t, Time.now
46
46
  assert_equal DateTime.new(2008, 10, 10, 10, 10, 10), DateTime.now
47
47
  assert_equal Date.new(2008, 10, 10), Date.today
@@ -51,9 +51,9 @@ class TestTimecop < Test::Unit::TestCase
51
51
  assert_not_equal Date.new(2008, 10, 10), Date.today
52
52
  end
53
53
 
54
- def test_travel_with_datetime_instance_works_as_expected
54
+ def test_freeze_with_datetime_instance_works_as_expected
55
55
  t = DateTime.new(2008, 10, 10, 10, 10, 10)
56
- Timecop.travel(t) do
56
+ Timecop.freeze(t) do
57
57
  assert_equal t, DateTime.now
58
58
  assert_equal Time.local(2008, 10, 10, 10, 10, 10), Time.now
59
59
  assert_equal Date.new(2008, 10, 10), Date.today
@@ -63,9 +63,9 @@ class TestTimecop < Test::Unit::TestCase
63
63
  assert_not_equal Date.new(2008, 10, 10), Date.today
64
64
  end
65
65
 
66
- def test_travel_with_date_instance_works_as_expected
66
+ def test_freeze_with_date_instance_works_as_expected
67
67
  d = Date.new(2008, 10, 10)
68
- Timecop.travel(d) do
68
+ Timecop.freeze(d) do
69
69
  assert_equal d, Date.today
70
70
  assert_equal Time.local(2008, 10, 10, 0, 0, 0), Time.now
71
71
  assert_equal DateTime.new(2008, 10, 10, 0, 0, 0), DateTime.now
@@ -75,10 +75,10 @@ class TestTimecop < Test::Unit::TestCase
75
75
  assert_not_equal DateTime.new(2008, 10, 10, 0, 0, 0), DateTime.now
76
76
  end
77
77
 
78
- def test_exception_thrown_in_travel_block_properly_resets_time
78
+ def test_exception_thrown_in_freeze_block_properly_resets_time
79
79
  t = Time.local(2008, 10, 10, 10, 10, 10)
80
80
  begin
81
- Timecop.travel(t) do
81
+ Timecop.freeze(t) do
82
82
  assert_equal t, Time.now
83
83
  raise "blah exception"
84
84
  end
@@ -88,51 +88,71 @@ class TestTimecop < Test::Unit::TestCase
88
88
  end
89
89
  end
90
90
 
91
+ def test_freeze_freezes_time
92
+ t = Time.local(2008, 10, 10, 10, 10, 10)
93
+ now = Time.now
94
+ Timecop.freeze(t) do
95
+ #assert Time.now < now, "If we had failed to freeze, time would have proceeded, which is what appears to have happened."
96
+ new_t, new_d, new_dt = Time.now, Date.today, DateTime.now
97
+ assert_equal t, new_t, "Failed to freeze time." # 2 seconds
98
+ #sleep(10)
99
+ assert_equal new_t, Time.now
100
+ assert_equal new_d, Date.today
101
+ assert_equal new_dt, DateTime.now
102
+ end
103
+ end
91
104
 
92
- def test_parse_travel_args_with_time
93
- t = Time.now
94
- y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
95
- ty, tm, td, th, tmin, ts = Timecop.send(:parse_travel_args, t)
96
- assert_equal y, ty
97
- assert_equal m, tm
98
- assert_equal d, td
99
- assert_equal h, th
100
- assert_equal min, tmin
101
- assert_equal s, ts
105
+ def test_travel_keeps_time_moving
106
+ t = Time.local(2008, 10, 10, 10, 10, 10)
107
+ now = Time.now
108
+ Timecop.travel(t) do
109
+ #assert Time.now < now, "If we had failed to freeze, time would have proceeded, which is what appears to have happened."
110
+ assert Time.now - t < 2000, "Looks like we failed to actually travel time" # 2 seconds
111
+ new_t = Time.now
112
+ #sleep(10)
113
+ assert_not_equal new_t, Time.now
114
+ end
102
115
  end
103
116
 
104
- def test_parse_travel_args_with_datetime
105
- t = DateTime.now
106
- y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
107
- ty, tm, td, th, tmin, ts = Timecop.send(:parse_travel_args, t)
108
- assert_equal y, ty
109
- assert_equal m, tm
110
- assert_equal d, td
111
- assert_equal h, th
112
- assert_equal min, tmin
113
- assert_equal s, ts
117
+ def test_recursive_rebasing_maintains_each_context
118
+ t = Time.local(2008, 10, 10, 10, 10, 10)
119
+ Timecop.travel(2008, 10, 10, 10, 10, 10) do
120
+ assert((t - Time.now).abs < 50, "Failed to travel time.")
121
+ t2 = Time.local(2008, 9, 9, 9, 9, 9)
122
+ Timecop.travel(2008, 9, 9, 9, 9, 9) do
123
+ assert((t2 - Time.now) < 50, "Failed to travel time.")
124
+ assert((t - Time.now) > 1000, "Failed to travel time.")
125
+ end
126
+ assert((t - Time.now).abs < 2000, "Failed to restore previously-traveled time.")
127
+ end
128
+ assert_nil Time.send(:mock_time)
114
129
  end
115
130
 
116
- def test_parse_travel_args_with_date
117
- date = Date.today
118
- y, m, d, h, min, s = date.year, date.month, date.day, 0, 0, 0
119
- ty, tm, td, th, tmin, ts = Timecop.send(:parse_travel_args, date)
120
- assert_equal y, ty
121
- assert_equal m, tm
122
- assert_equal d, td
123
- assert_equal h, th
124
- assert_equal min, tmin
125
- assert_equal s, ts
131
+ def test_recursive_travel_then_freeze
132
+ t = Time.local(2008, 10, 10, 10, 10, 10)
133
+ Timecop.travel(2008, 10, 10, 10, 10, 10) do
134
+ assert((t - Time.now).abs < 50, "Failed to travel time.")
135
+ t2 = Time.local(2008, 9, 9, 9, 9, 9)
136
+ Timecop.freeze(2008, 9, 9, 9, 9, 9) do
137
+ assert_equal t2, Time.now
138
+ end
139
+ assert((t - Time.now).abs < 2000, "Failed to restore previously-traveled time.")
140
+ end
141
+ assert_nil Time.send(:mock_time)
126
142
  end
127
143
 
128
- def test_parse_travel_args_with_individual_arguments
129
- y, m, d, h, min, s = 2008, 10, 10, 10, 10, 10
130
- ty, tm, td, th, tmin, ts = Timecop.send(:parse_travel_args, y, m, d, h, min, s)
131
- assert_equal y, ty
132
- assert_equal m, tm
133
- assert_equal d, td
134
- assert_equal h, th
135
- assert_equal min, tmin
136
- assert_equal s, ts
144
+ def test_recursive_freeze_then_travel
145
+ t = Time.local(2008, 10, 10, 10, 10, 10)
146
+ Timecop.freeze(t) do
147
+ assert_equal t, Time.now
148
+ t2 = Time.local(2008, 9, 9, 9, 9, 9)
149
+ Timecop.travel(t2) do
150
+ assert((t2 - Time.now) < 50, "Failed to travel time.")
151
+ assert((t - Time.now) > 1000, "Failed to travel time.")
152
+ end
153
+ assert_equal t, Time.now
154
+ end
155
+ assert_nil Time.send(:mock_time)
137
156
  end
157
+
138
158
  end
@@ -0,0 +1,54 @@
1
+
2
+ require 'date'
3
+ require 'test/unit'
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
5
+
6
+ class TestTimecopInternals < Test::Unit::TestCase
7
+
8
+ def test_parse_travel_args_with_time
9
+ t = Time.now
10
+ y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
11
+ ty, tm, td, th, tmin, ts = Timecop.instance().send(:parse_travel_args, t)
12
+ assert_equal y, ty
13
+ assert_equal m, tm
14
+ assert_equal d, td
15
+ assert_equal h, th
16
+ assert_equal min, tmin
17
+ assert_equal s, ts
18
+ end
19
+
20
+ def test_parse_travel_args_with_datetime
21
+ t = DateTime.now
22
+ y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
23
+ ty, tm, td, th, tmin, ts = Timecop.instance().send(:parse_travel_args, t)
24
+ assert_equal y, ty
25
+ assert_equal m, tm
26
+ assert_equal d, td
27
+ assert_equal h, th
28
+ assert_equal min, tmin
29
+ assert_equal s, ts
30
+ end
31
+
32
+ def test_parse_travel_args_with_date
33
+ date = Date.today
34
+ y, m, d, h, min, s = date.year, date.month, date.day, 0, 0, 0
35
+ ty, tm, td, th, tmin, ts = Timecop.instance().send(:parse_travel_args, date)
36
+ assert_equal y, ty
37
+ assert_equal m, tm
38
+ assert_equal d, td
39
+ assert_equal h, th
40
+ assert_equal min, tmin
41
+ assert_equal s, ts
42
+ end
43
+
44
+ def test_parse_travel_args_with_individual_arguments
45
+ y, m, d, h, min, s = 2008, 10, 10, 10, 10, 10
46
+ ty, tm, td, th, tmin, ts = Timecop.instance().send(:parse_travel_args, y, m, d, h, min, s)
47
+ assert_equal y, ty
48
+ assert_equal m, tm
49
+ assert_equal d, td
50
+ assert_equal h, th
51
+ assert_equal min, tmin
52
+ assert_equal s, ts
53
+ end
54
+ end
@@ -2,20 +2,119 @@
2
2
  require 'test/unit'
3
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
4
4
 
5
- class TestTimecopWithoutDate < Test::Unit::TestCase
5
+ class TestTimecopWithouDate < Test::Unit::TestCase
6
6
 
7
- def test_travel_changes_and_resets_time
8
- # depending on how we're invoked (individually or via the rake test suite)
9
- assert Object.const_defined?(:Time)
10
- #assert !Object.const_defined?(:Date)
7
+ def setup
8
+ assert !Object.const_defined?(:Date)
11
9
  assert !Object.const_defined?(:DateTime)
10
+ end
11
+
12
+ # just in case...let's really make sure that Timecop is disabled between tests...
13
+ def teardown
14
+ Timecop.return
15
+ end
16
+
17
+ def test_freeze_changes_and_resets_time
18
+ # depending on how we're invoked (individually or via the rake test suite)
19
+ assert !Time.respond_to?(:zone) || Time.zone.nil?
12
20
 
13
21
  t = Time.local(2008, 10, 10, 10, 10, 10)
14
22
  assert_not_equal t, Time.now
15
- Timecop.travel(2008, 10, 10, 10, 10, 10) do
23
+ Timecop.freeze(2008, 10, 10, 10, 10, 10) do
16
24
  assert_equal t, Time.now
17
25
  end
18
26
  assert_not_equal t, Time.now
19
27
  end
20
28
 
29
+ def test_recursive_freeze
30
+ t = Time.local(2008, 10, 10, 10, 10, 10)
31
+ Timecop.freeze(2008, 10, 10, 10, 10, 10) do
32
+ assert_equal t, Time.now
33
+ t2 = Time.local(2008, 9, 9, 9, 9, 9)
34
+ Timecop.freeze(2008, 9, 9, 9, 9, 9) do
35
+ assert_equal t2, Time.now
36
+ end
37
+ assert_equal t, Time.now
38
+ end
39
+ assert_nil Time.send(:mock_time)
40
+ end
41
+
42
+ def test_exception_thrown_in_freeze_block_properly_resets_time
43
+ t = Time.local(2008, 10, 10, 10, 10, 10)
44
+ begin
45
+ Timecop.freeze(t) do
46
+ assert_equal t, Time.now
47
+ raise "blah exception"
48
+ end
49
+ rescue
50
+ assert_not_equal t, Time.now
51
+ assert_nil Time.send(:mock_time)
52
+ end
53
+ end
54
+
55
+ def test_freeze_freezes_time
56
+ t = Time.local(2008, 10, 10, 10, 10, 10)
57
+ now = Time.now
58
+ Timecop.freeze(t) do
59
+ #assert Time.now < now, "If we had failed to freeze, time would have proceeded, which is what appears to have happened."
60
+ new_t = Time.now
61
+ assert_equal t, new_t, "Failed to change move time." # 2 seconds
62
+ #sleep(10)
63
+ assert_equal new_t, Time.now
64
+ end
65
+ end
66
+
67
+ def test_travel_keeps_time_moving
68
+ t = Time.local(2008, 10, 10, 10, 10, 10)
69
+ now = Time.now
70
+ Timecop.travel(t) do
71
+ #assert Time.now < now, "If we had failed to freeze, time would have proceeded, which is what appears to have happened."
72
+ assert Time.now - t < 2000, "Looks like we failed to actually travel time" # 2 seconds
73
+ new_t = Time.now
74
+ #sleep(10)
75
+ assert_not_equal new_t, Time.now
76
+ end
77
+ end
78
+
79
+ def test_recursive_rebasing_maintains_each_context
80
+ t = Time.local(2008, 10, 10, 10, 10, 10)
81
+ Timecop.travel(2008, 10, 10, 10, 10, 10) do
82
+ assert((t - Time.now).abs < 50, "Failed to travel time.")
83
+ t2 = Time.local(2008, 9, 9, 9, 9, 9)
84
+ Timecop.travel(2008, 9, 9, 9, 9, 9) do
85
+ assert((t2 - Time.now) < 50, "Failed to travel time.")
86
+ assert((t - Time.now) > 1000, "Failed to travel time.")
87
+ end
88
+ assert((t - Time.now).abs < 2000, "Failed to restore previously-traveled time.")
89
+ end
90
+ assert_nil Time.send(:mock_time)
91
+ end
92
+
93
+ def test_recursive_travel_then_freeze
94
+ t = Time.local(2008, 10, 10, 10, 10, 10)
95
+ Timecop.travel(2008, 10, 10, 10, 10, 10) do
96
+ assert((t - Time.now).abs < 50, "Failed to travel time.")
97
+ t2 = Time.local(2008, 9, 9, 9, 9, 9)
98
+ Timecop.freeze(2008, 9, 9, 9, 9, 9) do
99
+ assert_equal t2, Time.now
100
+ end
101
+ assert((t - Time.now).abs < 2000, "Failed to restore previously-traveled time.")
102
+ end
103
+ assert_nil Time.send(:mock_time)
104
+ end
105
+
106
+ def test_recursive_freeze_then_travel
107
+ t = Time.local(2008, 10, 10, 10, 10, 10)
108
+ Timecop.freeze(t) do
109
+ assert_equal t, Time.now
110
+ t2 = Time.local(2008, 9, 9, 9, 9, 9)
111
+ Timecop.travel(t2) do
112
+ assert((t2 - Time.now) < 50, "Failed to travel time.")
113
+ assert((t - Time.now) > 1000, "Failed to travel time.")
114
+ end
115
+ assert_equal t, Time.now
116
+ end
117
+ assert_nil Time.send(:mock_time)
118
+ end
119
+
21
120
  end
@@ -2,15 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{timecop}
5
- s.version = "0.1.0"
5
+ s.version = "0.2.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["John Trupiano"]
9
- s.date = %q{2008-11-09}
9
+ s.date = %q{2008-12-23}
10
10
  s.description = %q{A gem providing simple ways to temporarily override Time.now, Date.today, and DateTime.now. It provides "time travel" capabilities, making it dead simple to write test time-dependent code.}
11
11
  s.email = %q{jtrupiano@gmail.com}
12
12
  s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
13
- s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "lib/timecop.rb", "lib/timecop/time_extensions.rb", "lib/timecop/timecop.rb", "lib/timecop/version.rb", "test/test_timecop.rb", "timecop.gemspec", "test/test_timecop_with_rails.rb", "test/test_timecop_without_date.rb"]
13
+ s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "lib/timecop.rb", "lib/timecop/stack_item.rb", "lib/timecop/time_extensions.rb", "lib/timecop/timecop.rb", "lib/timecop/version.rb", "test/run_tests.sh", "test/test_timecop.rb", "test/test_timecop_internals.rb", "test/test_timecop_without_date.rb", "timecop.gemspec"]
14
14
  s.has_rdoc = true
15
15
  s.homepage = %q{http://github.com/jtrupiano/timecop}
16
16
  s.rdoc_options = ["--main", "README.txt"]
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.rubyforge_project = %q{johntrupiano}
19
19
  s.rubygems_version = %q{1.3.1}
20
20
  s.summary = %q{A gem providing simple ways to temporarily override Time.now, Date.today, and DateTime.now. It provides "time travel" capabilities, making it dead simple to write test time-dependent code.}
21
- s.test_files = ["test/test_timecop.rb", "test/test_timecop_with_rails.rb", "test/test_timecop_without_date.rb"]
21
+ s.test_files = ["test/test_timecop.rb", "test/test_timecop_internals.rb", "test/test_timecop_without_date.rb"]
22
22
 
23
23
  if s.respond_to? :specification_version then
24
24
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timecop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Trupiano
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-11-09 00:00:00 -05:00
12
+ date: 2008-12-24 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -38,10 +38,14 @@ files:
38
38
  - README.txt
39
39
  - Rakefile
40
40
  - lib/timecop.rb
41
+ - lib/timecop/stack_item.rb
41
42
  - lib/timecop/time_extensions.rb
42
43
  - lib/timecop/timecop.rb
43
44
  - lib/timecop/version.rb
45
+ - test/run_tests.sh
44
46
  - test/test_timecop.rb
47
+ - test/test_timecop_internals.rb
48
+ - test/test_timecop_without_date.rb
45
49
  - timecop.gemspec
46
50
  has_rdoc: true
47
51
  homepage: http://github.com/jtrupiano/timecop
@@ -72,5 +76,5 @@ specification_version: 2
72
76
  summary: A gem providing simple ways to temporarily override Time.now, Date.today, and DateTime.now. It provides "time travel" capabilities, making it dead simple to write test time-dependent code.
73
77
  test_files:
74
78
  - test/test_timecop.rb
75
- - test/test_timecop_with_rails.rb
79
+ - test/test_timecop_internals.rb
76
80
  - test/test_timecop_without_date.rb
@@ -1,22 +0,0 @@
1
-
2
- require 'test/unit'
3
-
4
- require 'rubygems'
5
- gem "activesupport", ">= 2.1.0"
6
- require 'activesupport'
7
-
8
- require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
9
-
10
- class TestTimecopWithRails < Test::Unit::TestCase
11
-
12
- def test_travel_changes_and_resets_time
13
- Time.zone = 'Eastern Time (US & Canada)'
14
- t = Time.zone.local(2008, 10, 10, 10, 10, 10)
15
- assert_not_equal t, Time.now
16
- Timecop.travel(2008, 10, 10, 10, 10, 10) do
17
- assert_equal t, Time.now
18
- end
19
- assert_not_equal t, Time.now
20
- end
21
-
22
- end