timecop 0.6.1 → 0.9.6

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,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