timecop 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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