timecop 0.6.1 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,4 @@
1
1
  require 'singleton'
2
- require File.join(File.dirname(__FILE__), "time_extensions")
3
2
  require File.join(File.dirname(__FILE__), "time_stack_item")
4
3
 
5
4
  # Timecop
@@ -14,7 +13,7 @@ class Timecop
14
13
  include Singleton
15
14
 
16
15
  class << self
17
- attr_accessor :active_support
16
+ private :instance
18
17
 
19
18
  # Allows you to run a block of code and "fake" a time throughout the execution of that block.
20
19
  # This is particularly useful for writing test methods where the passage of time is critical to the business
@@ -33,6 +32,7 @@ class Timecop
33
32
  # 3. Timecop.freeze(date_inst)
34
33
  # 4. Timecop.freeze(offset_in_seconds)
35
34
  # 5. Timecop.freeze(year, month, day, hour=0, minute=0, second=0)
35
+ # 6. Timecop.freeze() # Defaults to Time.now
36
36
  #
37
37
  # When a block is also passed, Time.now, DateTime.now and Date.today are all reset to their
38
38
  # previous values after the block has finished executing. This allows us to nest multiple
@@ -76,11 +76,11 @@ class Timecop
76
76
  end
77
77
 
78
78
  def baseline
79
- instance.send(:baseline)
79
+ instance.baseline
80
80
  end
81
81
 
82
82
  def baseline=(baseline)
83
- instance.send(:baseline=, baseline)
83
+ instance.baseline = baseline
84
84
  end
85
85
 
86
86
  # Reverts back to system's Time.now, Date.today and DateTime.now (if it exists) permamently when
@@ -88,73 +88,153 @@ class Timecop
88
88
  # the given block.
89
89
  def return(&block)
90
90
  if block_given?
91
- instance.send(:return, &block)
91
+ instance.return(&block)
92
92
  else
93
- instance.send(:unmock!)
93
+ instance.unmock!
94
94
  nil
95
95
  end
96
96
  end
97
+ alias :unfreeze :return
97
98
 
98
99
  def return_to_baseline
99
- instance.send(:return_to_baseline)
100
+ instance.return_to_baseline
100
101
  Time.now
101
102
  end
102
103
 
103
104
  def top_stack_item #:nodoc:
104
- instance.instance_variable_get(:@_stack).last
105
+ instance.stack.last
106
+ end
107
+
108
+ def safe_mode=(safe)
109
+ @safe_mode = safe
110
+ end
111
+
112
+ def safe_mode?
113
+ @safe_mode ||= false
114
+ end
115
+
116
+ def thread_safe=(t)
117
+ instance.thread_safe = t
118
+ end
119
+
120
+ def thread_safe
121
+ instance.thread_safe
122
+ end
123
+
124
+ # Returns whether or not Timecop is currently frozen/travelled
125
+ def frozen?
126
+ !instance.stack.empty?
105
127
  end
106
128
 
107
129
  private
108
130
  def send_travel(mock_type, *args, &block)
109
- val = instance.send(:travel, mock_type, *args, &block)
131
+ val = instance.travel(mock_type, *args, &block)
110
132
  block_given? ? val : Time.now
111
133
  end
112
134
  end
113
135
 
114
- private
136
+ def baseline=(b)
137
+ set_baseline(b)
138
+ stack << TimeStackItem.new(:travel, b)
139
+ end
140
+
141
+ def baseline
142
+ if @thread_safe
143
+ Thread.current[:timecop_baseline]
144
+ else
145
+ @baseline
146
+ end
147
+ end
148
+
149
+ def set_baseline(b)
150
+ if @thread_safe
151
+ Thread.current[:timecop_baseline] = b
152
+ else
153
+ @baseline = b
154
+ end
155
+ end
156
+
157
+ def stack
158
+ if @thread_safe
159
+ Thread.current[:timecop_stack] ||= []
160
+ Thread.current[:timecop_stack]
161
+ else
162
+ @stack
163
+ end
164
+ end
115
165
 
116
- def baseline=(baseline)
117
- @baseline = baseline
118
- @_stack << TimeStackItem.new(:travel, baseline)
166
+ def set_stack(s)
167
+ if @thread_safe
168
+ Thread.current[:timecop_stack] = s
169
+ else
170
+ @stack = s
171
+ end
119
172
  end
120
173
 
121
174
  def initialize #:nodoc:
122
- @_stack = []
175
+ @stack = []
176
+ @safe = nil
177
+ @thread_safe = false
178
+ end
179
+
180
+ def thread_safe=(t)
181
+ initialize
182
+ @thread_safe = t
183
+ end
184
+
185
+ def thread_safe
186
+ @thread_safe
123
187
  end
124
188
 
125
189
  def travel(mock_type, *args, &block) #:nodoc:
190
+ raise SafeModeException if Timecop.safe_mode? && !block_given? && !@safe
191
+
126
192
  stack_item = TimeStackItem.new(mock_type, *args)
127
193
 
128
- @_stack << stack_item
194
+ stack_backup = stack.dup
195
+ stack << stack_item
129
196
 
130
197
  if block_given?
198
+ safe_backup = @safe
199
+ @safe = true
131
200
  begin
132
201
  yield stack_item.time
133
202
  ensure
134
- @_stack.pop
203
+ stack.replace stack_backup
204
+ @safe = safe_backup
135
205
  end
136
206
  end
137
207
  end
138
208
 
139
209
  def return(&block)
140
- current_stack = @_stack
141
- current_baseline = @baseline
210
+ current_stack = stack
211
+ current_baseline = baseline
142
212
  unmock!
143
213
  yield
144
- @_stack = current_stack
145
- @baseline = current_baseline
214
+ ensure
215
+ set_stack current_stack
216
+ set_baseline current_baseline
146
217
  end
147
218
 
148
219
  def unmock! #:nodoc:
149
- @baseline = nil
150
- @_stack = []
220
+ set_baseline nil
221
+ set_stack []
151
222
  end
152
223
 
153
224
  def return_to_baseline
154
- if @baseline
155
- @_stack = [@_stack.shift]
225
+ if baseline
226
+ set_stack [stack.shift]
156
227
  else
157
228
  unmock!
158
229
  end
159
230
  end
231
+
232
+ class SafeModeException < StandardError
233
+ def initialize
234
+ super "Safe mode is enabled, only calls passing a block are allowed."
235
+ end
236
+ end
160
237
  end
238
+
239
+ # This must be done after TimeCop is available
240
+ require File.join(File.dirname(__FILE__), "time_extensions")
@@ -1,3 +1,3 @@
1
1
  class Timecop
2
- VERSION = "0.6.1"
2
+ VERSION = "0.9.6"
3
3
  end
data/test/test_helper.rb CHANGED
@@ -1,38 +1,39 @@
1
- require 'rubygems'
2
1
  require 'bundler/setup'
3
- require 'test/unit'
4
- begin
5
- require 'mocha/setup'
6
- rescue LoadError
7
- require 'mocha'
8
- end
2
+ require 'minitest/autorun'
3
+ require 'minitest/rg'
4
+ require 'pry'
5
+
6
+ $VERBOSE = true # enable ruby warnings
9
7
 
10
- class Test::Unit::TestCase
8
+ require 'mocha/minitest'
11
9
 
10
+ class Minitest::Test
12
11
  private
13
12
  # Tests to see that two times are within the given distance,
14
13
  # in seconds, from each other.
15
14
  def times_effectively_equal(time1, time2, seconds_interval = 1)
16
15
  (time1 - time2).abs <= seconds_interval
17
16
  end
18
-
17
+
19
18
  def assert_times_effectively_equal(time1, time2, seconds_interval = 1, msg = nil)
20
19
  assert times_effectively_equal(time1, time2, seconds_interval), "#{msg}: time1 = #{time1.to_s}, time2 = #{time2.to_s}"
21
20
  end
22
-
21
+
23
22
  def assert_times_effectively_not_equal(time1, time2, seconds_interval = 1, msg = nil)
24
23
  assert !times_effectively_equal(time1, time2, seconds_interval), "#{msg}: time1 = #{time1.to_s}, time2 = #{time2.to_s}"
25
24
  end
26
-
27
- def local_offset
28
- DateTime.now_without_mock_time.offset
25
+
26
+ # Gets the local offset (supplied by ENV['TZ'] or your computer's clock)
27
+ # At the given timestamp, or Time.now if not time is given.
28
+ def local_offset(time = Time.now)
29
+ Time.at(time.to_i).to_datetime.offset
29
30
  end
30
-
31
- TIMEZONES = ["Europe/Paris", "UTC", "EDT"]
32
-
31
+
32
+ TIMEZONES = ["Pacific/Midway", "Europe/Paris", "UTC", "America/Chicago"]
33
+
33
34
  def each_timezone
34
35
  old_tz = ENV["TZ"]
35
-
36
+
36
37
  begin
37
38
  TIMEZONES.each do |timezone|
38
39
  ENV["TZ"] = timezone
@@ -42,13 +43,17 @@ class Test::Unit::TestCase
42
43
  ENV["TZ"] = old_tz
43
44
  end
44
45
  end
45
-
46
+
46
47
  def a_time_stack_item
47
48
  Timecop::TimeStackItem.new(:freeze, 2008, 1, 1, 0, 0, 0)
48
49
  end
49
-
50
+
50
51
  def assert_date_times_equal(dt1, dt2)
51
- assert_equal dt1, dt2, "Failed for timezone: #{ENV['TZ']}: #{dt1.to_s} not equal to #{dt2.to_s}"
52
+ assert_in_delta dt1.to_time.to_f, dt2.to_time.to_f, 0.01, "Failed for timezone: #{ENV['TZ']}: #{dt1.to_s} not equal to #{dt2.to_s}"
52
53
  end
53
-
54
+
55
+ def jruby?
56
+ RUBY_PLATFORM == "java"
57
+ end
58
+
54
59
  end
@@ -1,11 +1,13 @@
1
1
  require 'date'
2
- require File.join(File.dirname(__FILE__), "test_helper")
3
- require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
2
+ require_relative "test_helper"
3
+ require 'timecop'
4
4
 
5
- class TestTimeStackItem < Test::Unit::TestCase
5
+ require 'active_support/all'
6
+
7
+ class TestTimeStackItem < Minitest::Test
6
8
  def teardown
7
- Timecop.active_support = nil
8
9
  Timecop.return
10
+ Time.zone = nil
9
11
  end
10
12
 
11
13
  def test_new_with_time
@@ -85,6 +87,19 @@ class TestTimeStackItem < Test::Unit::TestCase
85
87
  assert_equal s, stack_item.sec
86
88
  end
87
89
 
90
+ def test_new_with_float
91
+ t = Time.now
92
+ y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
93
+ stack_item = Timecop::TimeStackItem.new(:freeze, 0.0)
94
+
95
+ assert_equal y, stack_item.year
96
+ assert_equal m, stack_item.month
97
+ assert_equal d, stack_item.day
98
+ assert_equal h, stack_item.hour
99
+ assert_equal min, stack_item.min
100
+ assert_equal s, stack_item.sec
101
+ end
102
+
88
103
  def test_new_with_individual_arguments
89
104
  y, m, d, h, min, s = 2008, 10, 10, 10, 10, 10
90
105
  stack_item = Timecop::TimeStackItem.new(:freeze, y, m, d, h, min, s)
@@ -111,49 +126,35 @@ class TestTimeStackItem < Test::Unit::TestCase
111
126
  assert_equal Rational(1, 24), a_time_stack_item.send(:utc_offset_to_rational, 3600)
112
127
  end
113
128
 
114
- def test_compute_dst_adjustment_for_dst_to_dst
115
- Timecop.freeze(DateTime.parse("2009-10-1 00:38:00 -0400"))
116
- t = DateTime.parse("2009-10-11 00:00:00 -0400")
117
- tsi = Timecop::TimeStackItem.new(:freeze, t)
118
- return if !(Time.now.dst? && tsi.time.dst?)
119
-
120
- assert_equal 0, tsi.send(:dst_adjustment)
121
- end
122
-
123
- def test_compute_dst_adjustment_for_non_dst_to_non_dst
124
- Timecop.freeze(DateTime.parse("2009-12-1 00:38:00 -0400"))
125
- t = DateTime.parse("2009-12-11 00:00:00 -0400")
126
- tsi = Timecop::TimeStackItem.new(:freeze, t)
127
- return if Time.now.dst? || tsi.time.dst?
128
-
129
- assert_equal 0, tsi.send(:dst_adjustment)
130
- end
129
+ def test_datetime_in_presence_of_activesupport_timezone
130
+ skip('requires ActiveSupport') unless Time.respond_to? :zone
131
+ backed_up_zone, backed_up_tzvar = Time.zone, ENV['TZ']
131
132
 
132
- def test_compute_dst_adjustment_for_dst_to_non_dst
133
- Timecop.freeze(DateTime.parse("2009-10-1 00:38:00 -0400"))
134
- t = DateTime.parse("2009-12-11 00:00:00 -0400")
133
+ Time.zone = ENV['TZ'] = 'America/Los_Angeles'
134
+ t = DateTime.new(2001, 2, 28, 23, 59, 59.5)
135
135
  tsi = Timecop::TimeStackItem.new(:freeze, t)
136
- return if !Time.now.dst? || tsi.time.dst?
137
136
 
138
- assert_equal 60 * 60, tsi.send(:dst_adjustment)
137
+ assert_date_times_equal t, tsi.datetime
138
+ ensure
139
+ Time.zone, ENV['TZ'] = backed_up_zone, backed_up_tzvar
139
140
  end
140
141
 
141
- def test_compute_dst_adjustment_for_non_dst_to_dst
142
- Timecop.freeze(DateTime.parse("2009-12-1 00:38:00 -0400"))
142
+ # Ensure DateTimes handle changing DST properly
143
+ def test_datetime_for_dst_to_non_dst
144
+ Timecop.freeze(DateTime.parse("2009-12-1 00:38:00 -0500"))
143
145
  t = DateTime.parse("2009-10-11 00:00:00 -0400")
144
146
  tsi = Timecop::TimeStackItem.new(:freeze, t)
145
- return if Time.now.dst? || !tsi.time.dst?
146
147
 
147
- assert_equal -1 * 60 * 60, tsi.send(:dst_adjustment)
148
+ assert_date_times_equal t, tsi.datetime
148
149
  end
149
150
 
150
- # Ensure DateTimes handle changing DST properly
151
- def test_datetime_for_dst_to_non_dst
151
+ # Ensure DateTimes handle changing DST properly when changing from DateTime to Time
152
+ def test_datetime_for_dst_to_time_for_non_dst
152
153
  Timecop.freeze(DateTime.parse("2009-12-1 00:38:00 -0500"))
153
154
  t = DateTime.parse("2009-10-11 00:00:00 -0400")
154
155
  tsi = Timecop::TimeStackItem.new(:freeze, t)
155
156
 
156
- assert_date_times_equal t, tsi.datetime
157
+ assert_date_times_equal t.to_time, tsi.time
157
158
  end
158
159
 
159
160
  def test_datetime_for_non_dst_to_dst
@@ -180,11 +181,10 @@ class TestTimeStackItem < Test::Unit::TestCase
180
181
  t = Time.local(2009, 10, 1, 0, 0, 30)
181
182
  tsi = Timecop::TimeStackItem.new(:freeze, t)
182
183
 
183
- assert_equal nil, tsi.send(:travel_offset)
184
+ assert_nil tsi.send(:travel_offset)
184
185
  end
185
186
 
186
187
  def test_timezones
187
- require 'active_support/all'
188
188
  Time.zone = "Europe/Zurich"
189
189
  time = Time.zone.parse("2012-12-27T12:12:12+08:00")
190
190
  Timecop.freeze(time) do |frozen_time|
@@ -192,13 +192,21 @@ class TestTimeStackItem < Test::Unit::TestCase
192
192
  end
193
193
  end
194
194
 
195
+ def test_timezones_with_parsed_string
196
+ Time.zone = "Europe/Zurich"
197
+ time_string = "2012-12-27 12:12"
198
+ expected_time = Time.zone.parse(time_string)
199
+ Timecop.freeze(time_string) do |frozen_time|
200
+ assert_equal expected_time, frozen_time
201
+ end
202
+ end
203
+
195
204
  def test_timezones_apply_dates
196
- require 'active_support/all'
197
- Time.zone = "Marshall Is."
205
+ Time.zone = "Central Time (US & Canada)"
198
206
  time = Time.zone.local(2013,1,3)
199
207
 
200
208
  Timecop.freeze(time) do
201
- assert_equal time.to_date, Date.today
209
+ assert_equal time.to_date, Time.zone.now.to_date
202
210
  end
203
211
  end
204
212
 
@@ -212,48 +220,18 @@ class TestTimeStackItem < Test::Unit::TestCase
212
220
  assert_equal tsi.send(:scaling_factor), 4, "Scaling factor not set"
213
221
  end
214
222
 
215
- def test_parse_string_date_with_active_support
216
- date = '2012-01-02'
217
- Time.expects(:parse).with(date).returns(Time.local(2012, 01, 02))
218
- Timecop.freeze(date)
219
- end
220
-
221
223
  def test_parse_only_string_with_active_support
222
224
  Time.expects(:parse).never
223
225
  Timecop.freeze(2011, 01, 02, hour=0, minute=0, second=0)
224
226
  end
225
227
 
226
- def test_parse_with_active_support_off
227
- date = '2012-01-02'
228
- Timecop.active_support = false
229
- Time.expects(:parse).never
230
- Timecop.freeze(date)
231
- end
232
-
233
- def test_uses_active_supports_in_time_zone
234
- time = Time.now
235
- Time.any_instance.expects(:in_time_zone).returns(time)
236
- Timecop::TimeStackItem.new(:freeze, time)
237
- end
238
-
239
- def test_configured_off_active_support_in_time_zone_xxx
240
- Timecop.active_support = false
241
- Time.any_instance.expects(:in_time_zone).never
242
- Timecop::TimeStackItem.new(:freeze, Time.now)
243
- end
244
-
245
228
  def test_parse_date
246
- assert_nothing_raised do
247
- Timecop.freeze(Date.new(2012, 6, 9))
248
- end
229
+ Timecop.freeze(Date.new(2012, 6, 9))
249
230
  end
250
231
 
251
232
  def test_time_zone_returns_nil
252
- c = class << Time; self end
253
- c.send(:define_method, :zone) { nil }
254
- assert_nothing_raised do
255
- Timecop.freeze
256
- end
233
+ Time.zone = nil
234
+ Timecop.freeze
257
235
  end
258
236
 
259
237
  def test_nsecs_are_set
@@ -263,13 +241,59 @@ class TestTimeStackItem < Test::Unit::TestCase
263
241
  assert_equal time.nsec, Time.now.nsec if (Time.now.respond_to?(:nsec))
264
242
  end
265
243
 
266
- def test_time_with_different_timezone
267
- require 'active_support/all'
244
+ def test_time_with_different_timezone_keeps_nsec
245
+ Time.zone = "Tokyo"
246
+ t = Time.now
247
+ Timecop.freeze(t) do
248
+ assert_equal t, Time.now
249
+ assert_equal t.nsec, Time.now.nsec if (Time.now.respond_to?(:nsec))
250
+ end
251
+ end
252
+
253
+ def test_time_now_always_returns_local_time
254
+ Time.zone = "Tokyo"
255
+ t = Time.utc(2000, 1, 1)
256
+ Timecop.freeze(t) do
257
+ assert_equal t.getlocal.zone, Time.now.zone
258
+ end
259
+ end
260
+
261
+ def test_time_zone_now_returns_time_in_that_zone
262
+ Time.zone = "Hawaii"
263
+ t = Time.utc(2000, 1, 1)
264
+ Timecop.freeze(t) do
265
+ assert_equal t, Time.zone.now
266
+ assert_equal 'HST', Time.zone.now.zone
267
+ end
268
+ end
268
269
 
270
+ def test_freezing_a_time_leaves_timezone_intact
269
271
  Time.zone = "Tokyo"
270
272
  t = Time.now
273
+ t_dup = t.dup
274
+ Timecop.freeze(t) {}
275
+ assert_equal t_dup.zone, t.zone
276
+ end
277
+
278
+ def test_freezing_a_time_with_zone_returns_proper_zones
279
+ Time.zone = "Hawaii"
280
+ t = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1), ActiveSupport::TimeZone['Tokyo'])
271
281
  Timecop.freeze(t) do
272
- assert_times_effectively_equal t, Time.now
282
+ local_now = Time.now
283
+ assert_equal t, local_now
284
+ assert_equal t.getlocal.zone, local_now.zone
285
+
286
+ zoned_now = Time.zone.now
287
+ assert_equal t, zoned_now
288
+ assert_equal 'HST', zoned_now.zone
289
+ end
290
+ end
291
+
292
+ def test_datetime_timezones
293
+ dt = DateTime.new(2011,1,3,15,25,0,"-6")
294
+ Timecop.freeze(dt) do
295
+ now = DateTime.now
296
+ assert_equal dt, now, "#{dt.to_f}, #{now.to_f}"
273
297
  end
274
298
  end
275
299
  end