timecop 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bfc685225a22109f897b68e3174608a4b6a16def
4
- data.tar.gz: 66aee6ccc9dfd424502c3f3f149c175af3fa3fbc
3
+ metadata.gz: 580525a47a6026f3d83689807d96949dd5269fa5
4
+ data.tar.gz: ffb9cc8cf58489f231f3348656f99597b3c79989
5
5
  SHA512:
6
- metadata.gz: ac084a1428c7b528faa70aa321b1cb9db1f9f3f8eb45e4a94777f15b915662fc60ba9208091837903348348a1c902c109649aa7a471819cbec7ea1e0d8c1a377
7
- data.tar.gz: 14082a5cba36b7f5238455d884025c5f9ce01ca0b81073f9b9860220286d13db10cce76b3da38465fb585976fd29782a1e0d81913d699814e4505790446e03b7
6
+ metadata.gz: dc3c024856bf2bbaab09a0734aa1d87712e26c8b1e47774edf2d65153ceb22ae6bbe3b04499a4f12c68acf5f9352fb26e614c8c4996c80e14353f75049899598
7
+ data.tar.gz: c759c9cc704c61d2bd1cf13b4d44a6ad8a1b36a03fc398b4796f1dd1bc0d055a6ddda86aa0a31addc753eb6accc57049648d3fd5334477ccb0cc8cbc594076b7
@@ -1,6 +1,6 @@
1
1
  # timecop
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/travisjeffery/timecop.png)](http://travis-ci.org/travisjeffery/timecop)
3
+ [![Build Status](https://secure.travis-ci.org/travisjeffery/timecop.svg)](http://travis-ci.org/travisjeffery/timecop)
4
4
 
5
5
  ## DESCRIPTION
6
6
 
@@ -104,7 +104,7 @@ being able to simulate activity via subsequent calls to your application.
104
104
  Timecop.scale(3600)
105
105
  Time.now
106
106
  # => 2012-09-20 21:23:25 -0500
107
- # seconds later, hours have past it's gone from 9pm at night to 6am in the morning
107
+ # seconds later, hours have passed and it's gone from 9pm at night to 6am in the morning
108
108
  Time.now
109
109
  # => 2012-09-21 06:22:59 -0500
110
110
  ```
@@ -128,6 +128,12 @@ Timecop.freeze
128
128
  # => Timecop::SafeModeException: Safe mode is enabled, only calls passing a block are allowed.
129
129
  ```
130
130
 
131
+ ### Rails v Ruby Date/Time libraries
132
+
133
+ Sometimes [Rails Date/Time methods don't play nicely with Ruby Date/Time methods.](https://rails.lighthouseapp.com/projects/8994/tickets/6410-dateyesterday-datetoday)
134
+
135
+ Be careful mixing Ruby `Date.today` with Rails `Date.tomorrow` / `Date.yesterday` as things might break.
136
+
131
137
  ## Contribute
132
138
 
133
139
  timecop is maintained by [travisjeffery](http://github.com/travisjeffery), and
@@ -27,19 +27,8 @@ class Time #:nodoc:
27
27
  end
28
28
 
29
29
  class Date #:nodoc:
30
- WEEKDAYS = {
31
- "sunday" => 0,
32
- "monday" => 1,
33
- "tuesday" => 2,
34
- "wednesday" => 3,
35
- "thursday" => 4,
36
- "friday" => 5,
37
- "saturday" => 6
38
- }
39
-
40
30
  class << self
41
31
  def mock_date
42
- mocked_time_stack_item = Timecop.top_stack_item
43
32
  mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.date(self)
44
33
  end
45
34
 
@@ -65,26 +54,41 @@ class Date #:nodoc:
65
54
  alias_method :strptime, :strptime_with_mock_date
66
55
 
67
56
  def parse_with_mock_date(*args)
68
- str = args.first
69
- if str && WEEKDAYS.keys.include?(str.downcase)
70
- offset = WEEKDAYS[str.downcase] - Date.today.wday
71
-
72
- Date.today + offset
57
+ parsed_date = parse_without_mock_date(*args)
58
+ return parsed_date unless mocked_time_stack_item
59
+ date_hash = Date._parse(*args)
60
+
61
+ case
62
+ when date_hash[:year] && date_hash[:mon] && date_hash[:mday]
63
+ parsed_date
64
+ when date_hash[:mon] && date_hash[:mday]
65
+ Date.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
66
+ when date_hash[:wday]
67
+ closest_wday(date_hash[:wday])
73
68
  else
74
- parse_without_mock_date(*args)
69
+ parsed_date + mocked_time_stack_item.travel_offset_days
75
70
  end
76
71
  end
77
72
 
78
73
  alias_method :parse_without_mock_date, :parse
79
74
  alias_method :parse, :parse_with_mock_date
80
75
 
76
+ def mocked_time_stack_item
77
+ Timecop.top_stack_item
78
+ end
79
+
80
+ def closest_wday(wday)
81
+ today = Date.today
82
+ result = today - today.wday
83
+ result += 1 until wday == result.wday
84
+ result
85
+ end
81
86
  end
82
87
  end
83
88
 
84
89
  class DateTime #:nodoc:
85
90
  class << self
86
91
  def mock_time
87
- mocked_time_stack_item = Timecop.top_stack_item
88
92
  mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.datetime(self)
89
93
  end
90
94
 
@@ -97,19 +101,28 @@ class DateTime #:nodoc:
97
101
  alias_method :now, :now_with_mock_time
98
102
 
99
103
  def parse_with_mock_date(*args)
100
- str = args.first
101
- if str && Date::WEEKDAYS.keys.include?(str.downcase)
102
- offset = Date::WEEKDAYS[str.downcase] - DateTime.now.wday
103
-
104
- parsed_weekday =(DateTime.now + offset)
105
-
106
- DateTime.new(parsed_weekday.year, parsed_weekday.month, parsed_weekday.day, 0, 0, 0, 0)
104
+ date_hash = Date._parse(*args)
105
+ parsed_date = parse_without_mock_date(*args)
106
+ return parsed_date unless mocked_time_stack_item
107
+ date_hash = DateTime._parse(*args)
108
+
109
+ case
110
+ when date_hash[:year] && date_hash[:mon] && date_hash[:mday]
111
+ parsed_date
112
+ when date_hash[:mon] && date_hash[:mday]
113
+ DateTime.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
114
+ when date_hash[:wday]
115
+ Date.closest_wday(date_hash[:wday]).to_datetime
107
116
  else
108
- parse_without_mock_date(*args)
117
+ parsed_date + mocked_time_stack_item.travel_offset_days
109
118
  end
110
119
  end
111
120
 
112
121
  alias_method :parse_without_mock_date, :parse
113
122
  alias_method :parse, :parse_with_mock_date
123
+
124
+ def mocked_time_stack_item
125
+ Timecop.top_stack_item
126
+ end
114
127
  end
115
128
  end
@@ -43,7 +43,11 @@ class Timecop
43
43
  end
44
44
 
45
45
  def travel_offset
46
- @travel_offset
46
+ @travel_offset unless mock_type == :freeze
47
+ end
48
+
49
+ def travel_offset_days
50
+ (@travel_offset / 60 / 60 / 24).round
47
51
  end
48
52
 
49
53
  def scaling_factor
@@ -52,7 +56,7 @@ class Timecop
52
56
 
53
57
  def time(time_klass = Time) #:nodoc:
54
58
  if @time.respond_to?(:in_time_zone)
55
- time = time_klass.at(@time.dup.utc.to_r)
59
+ time = time_klass.at(@time.dup.localtime)
56
60
  else
57
61
  time = time_klass.at(@time)
58
62
  end
@@ -60,7 +64,7 @@ class Timecop
60
64
  if travel_offset.nil?
61
65
  time
62
66
  elsif scaling_factor.nil?
63
- time_klass.at((Time.now_without_mock_time + travel_offset).to_f)
67
+ time_klass.at(Time.now_without_mock_time + travel_offset)
64
68
  else
65
69
  time_klass.at(scaled_time)
66
70
  end
@@ -102,12 +106,12 @@ class Timecop
102
106
  elsif Object.const_defined?(:Date) && arg.is_a?(Date)
103
107
  time_klass.local(arg.year, arg.month, arg.day, 0, 0, 0)
104
108
  elsif args.empty? && (arg.kind_of?(Integer) || arg.kind_of?(Float))
105
- Time.now + arg
109
+ time_klass.now + arg
106
110
  elsif arg.nil?
107
- Time.now
111
+ time_klass.now
108
112
  else
109
113
  if arg.is_a?(String) && Time.respond_to?(:parse)
110
- Time.parse(arg)
114
+ time_klass.parse(arg)
111
115
  else
112
116
  # we'll just assume it's a list of y/m/d/h/m/s
113
117
  year = arg || 2000
@@ -122,7 +126,6 @@ class Timecop
122
126
  end
123
127
 
124
128
  def compute_travel_offset
125
- return nil if mock_type == :freeze
126
129
  time - Time.now_without_mock_time
127
130
  end
128
131
 
@@ -100,7 +100,7 @@ class Timecop
100
100
  end
101
101
 
102
102
  def top_stack_item #:nodoc:
103
- instance.instance_variable_get(:@_stack).last
103
+ instance.send(:stack).last
104
104
  end
105
105
 
106
106
  def safe_mode=(safe)
@@ -111,9 +111,17 @@ class Timecop
111
111
  @safe_mode ||= false
112
112
  end
113
113
 
114
+ def thread_safe=(t)
115
+ instance.send(:thread_safe=, t)
116
+ end
117
+
118
+ def thread_safe
119
+ instance.send(:thread_safe)
120
+ end
121
+
114
122
  # Returns whether or not Timecop is currently frozen/travelled
115
123
  def frozen?
116
- !instance.instance_variable_get(:@_stack).empty?
124
+ !instance.send(:stack).empty?
117
125
  end
118
126
 
119
127
  private
@@ -125,50 +133,97 @@ class Timecop
125
133
 
126
134
  private
127
135
 
128
- def baseline=(baseline)
129
- @baseline = baseline
130
- @_stack << TimeStackItem.new(:travel, baseline)
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
165
+
166
+ def set_stack(s)
167
+ if @thread_safe
168
+ Thread.current[:timecop_stack] = s
169
+ else
170
+ @stack = s
171
+ end
131
172
  end
132
173
 
133
174
  def initialize #:nodoc:
134
- @_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
135
187
  end
136
188
 
137
189
  def travel(mock_type, *args, &block) #:nodoc:
138
- raise SafeModeException if Timecop.safe_mode? && !block_given?
190
+ raise SafeModeException if Timecop.safe_mode? && !block_given? && !@safe
139
191
 
140
192
  stack_item = TimeStackItem.new(mock_type, *args)
141
193
 
142
- stack_backup = @_stack.dup
143
- @_stack << stack_item
194
+ stack_backup = stack.dup
195
+ stack << stack_item
144
196
 
145
197
  if block_given?
198
+ safe_backup = @safe
199
+ @safe = true
146
200
  begin
147
201
  yield stack_item.time
148
202
  ensure
149
- @_stack.replace stack_backup
203
+ @stack.replace stack_backup
204
+ @safe = safe_backup
150
205
  end
151
206
  end
152
207
  end
153
208
 
154
209
  def return(&block)
155
- current_stack = @_stack
156
- current_baseline = @baseline
210
+ current_stack = stack
211
+ current_baseline = baseline
157
212
  unmock!
158
213
  yield
159
214
  ensure
160
- @_stack = current_stack
161
- @baseline = current_baseline
215
+ set_stack current_stack
216
+ set_baseline current_baseline
162
217
  end
163
218
 
164
219
  def unmock! #:nodoc:
165
- @baseline = nil
166
- @_stack = []
220
+ set_baseline nil
221
+ set_stack []
167
222
  end
168
223
 
169
224
  def return_to_baseline
170
- if @baseline
171
- @_stack = [@_stack.shift]
225
+ if baseline
226
+ set_stack [stack.shift]
172
227
  else
173
228
  unmock!
174
229
  end
@@ -1,3 +1,3 @@
1
1
  class Timecop
2
- VERSION = "0.8.1"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -51,4 +51,8 @@ class Minitest::Test
51
51
  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
52
  end
53
53
 
54
+ def jruby?
55
+ RUBY_PLATFORM == "java"
56
+ end
57
+
54
58
  end
@@ -1,6 +1,7 @@
1
1
  require 'date'
2
2
  require_relative "test_helper"
3
3
  require 'timecop'
4
+
4
5
  require 'active_support/all'
5
6
 
6
7
  class TestTimeStackItem < Minitest::Test
@@ -191,6 +192,15 @@ class TestTimeStackItem < Minitest::Test
191
192
  end
192
193
  end
193
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
+
194
204
  def test_timezones_apply_dates
195
205
  Time.zone = "Central Time (US & Canada)"
196
206
  time = Time.zone.local(2013,1,3)
@@ -251,7 +251,7 @@ class TestTimecop < Minitest::Test
251
251
  new_now = Time.now
252
252
  assert_times_effectively_equal(new_now, t, 1, "Looks like we failed to actually travel time")
253
253
  sleep(0.25)
254
- assert_times_effectively_not_equal new_now, Time.now, 0.25, "Looks like time is not moving"
254
+ assert_times_effectively_not_equal new_now, Time.now, 0.24, "Looks like time is not moving"
255
255
  end
256
256
  end
257
257
 
@@ -495,6 +495,18 @@ class TestTimecop < Minitest::Test
495
495
  end
496
496
  end
497
497
 
498
+ def test_raises_when_safe_mode_and_no_block_though_previously_block_given
499
+ Timecop.freeze do
500
+ Timecop.freeze
501
+ end
502
+
503
+ with_safe_mode do
504
+ assert_raises Timecop::SafeModeException do
505
+ Timecop.freeze
506
+ end
507
+ end
508
+ end
509
+
498
510
  def test_no_raise_when_safe_mode_and_block_used
499
511
  with_safe_mode do
500
512
  Timecop.freeze {}
@@ -507,6 +519,14 @@ class TestTimecop < Minitest::Test
507
519
  end
508
520
  end
509
521
 
522
+ def test_no_raise_when_safe_mode_and_no_block_and_in_block_context
523
+ with_safe_mode do
524
+ Timecop.freeze do
525
+ Timecop.freeze
526
+ end
527
+ end
528
+ end
529
+
510
530
  def test_date_strptime_without_year
511
531
  Timecop.freeze(Time.new(1984,2,28)) do
512
532
  assert_equal Date.strptime('04-14', '%m-%d'), Date.new(1984, 4, 14)
@@ -536,6 +556,23 @@ class TestTimecop < Minitest::Test
536
556
  assert !Timecop.frozen?
537
557
  end
538
558
 
559
+ def test_thread_safe_timecop
560
+ Timecop.thread_safe = true
561
+ date = Time.local(2011, 01, 02)
562
+ thread = Thread.new do
563
+ Timecop.freeze(date) do
564
+ sleep 1 #give main thread time to run
565
+ assert_equal date, Time.now
566
+ end
567
+ end
568
+
569
+ sleep 0.25
570
+ assert Time.now != date
571
+ thread.join
572
+ ensure
573
+ Timecop.thread_safe = false
574
+ end
575
+
539
576
  private
540
577
 
541
578
  def with_safe_mode(enabled=true)
@@ -70,7 +70,7 @@ class TestTimecopWithoutDate < Minitest::Test
70
70
  new_now = Time.now
71
71
  assert_times_effectively_equal new_now, t, 1, "Looks like we failed to actually travel time" # 0.1 seconds
72
72
  sleep(0.25)
73
- assert_times_effectively_not_equal new_now, Time.now, 0.25, "Looks like time is not moving"
73
+ assert_times_effectively_not_equal new_now, Time.now, 0.24, "Looks like time is not moving"
74
74
  end
75
75
  end
76
76
 
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.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Travis Jeffery
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-04-01 00:00:00.000000000 Z
12
+ date: 2017-06-22 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: A gem providing "time travel" and "time freezing" capabilities, making
15
15
  it dead simple to test time-dependent code. It provides a unified method to mock