timecop 0.8.1 → 0.9.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.
- checksums.yaml +4 -4
- data/README.markdown +8 -2
- data/lib/timecop/time_extensions.rb +39 -26
- data/lib/timecop/time_stack_item.rb +10 -7
- data/lib/timecop/timecop.rb +73 -18
- data/lib/timecop/version.rb +1 -1
- data/test/test_helper.rb +4 -0
- data/test/time_stack_item_test.rb +10 -0
- data/test/timecop_test.rb +38 -1
- data/test/timecop_without_date_test.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 580525a47a6026f3d83689807d96949dd5269fa5
|
4
|
+
data.tar.gz: ffb9cc8cf58489f231f3348656f99597b3c79989
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc3c024856bf2bbaab09a0734aa1d87712e26c8b1e47774edf2d65153ceb22ae6bbe3b04499a4f12c68acf5f9352fb26e614c8c4996c80e14353f75049899598
|
7
|
+
data.tar.gz: c759c9cc704c61d2bd1cf13b4d44a6ad8a1b36a03fc398b4796f1dd1bc0d055a6ddda86aa0a31addc753eb6accc57049648d3fd5334477ccb0cc8cbc594076b7
|
data/README.markdown
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# timecop
|
2
2
|
|
3
|
-
[](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
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
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.
|
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(
|
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
|
-
|
109
|
+
time_klass.now + arg
|
106
110
|
elsif arg.nil?
|
107
|
-
|
111
|
+
time_klass.now
|
108
112
|
else
|
109
113
|
if arg.is_a?(String) && Time.respond_to?(:parse)
|
110
|
-
|
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
|
|
data/lib/timecop/timecop.rb
CHANGED
@@ -100,7 +100,7 @@ class Timecop
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def top_stack_item #:nodoc:
|
103
|
-
instance.
|
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.
|
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=(
|
129
|
-
|
130
|
-
|
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
|
-
@
|
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 =
|
143
|
-
|
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
|
-
@
|
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 =
|
156
|
-
current_baseline =
|
210
|
+
current_stack = stack
|
211
|
+
current_baseline = baseline
|
157
212
|
unmock!
|
158
213
|
yield
|
159
214
|
ensure
|
160
|
-
|
161
|
-
|
215
|
+
set_stack current_stack
|
216
|
+
set_baseline current_baseline
|
162
217
|
end
|
163
218
|
|
164
219
|
def unmock! #:nodoc:
|
165
|
-
|
166
|
-
|
220
|
+
set_baseline nil
|
221
|
+
set_stack []
|
167
222
|
end
|
168
223
|
|
169
224
|
def return_to_baseline
|
170
|
-
if
|
171
|
-
|
225
|
+
if baseline
|
226
|
+
set_stack [stack.shift]
|
172
227
|
else
|
173
228
|
unmock!
|
174
229
|
end
|
data/lib/timecop/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -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)
|
data/test/timecop_test.rb
CHANGED
@@ -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.
|
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.
|
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.
|
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:
|
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
|