timecop 0.9.6 → 0.9.10

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
  SHA256:
3
- metadata.gz: c86a01ea95f5019cb15a6a66c5668e68e7f1e293ca14b910a10cbe1cdd70d668
4
- data.tar.gz: e5c6987d3a452fa9ead841021f93bf4ea6bac713c1f6cea38cfc1c1d49baf6ee
3
+ metadata.gz: 79ad5f132158245aadf9f2afbfbfaa2e0ed8298e69037270f51cb801f9794b88
4
+ data.tar.gz: ec1d3177b1ca8f8f03c2bef0316f97953a4fe021d250903ea9159cdca8079550
5
5
  SHA512:
6
- metadata.gz: 4abbcd2d37b9fdf6dfb1ed1f63f945a882cd62a47ae2ae237da003ccac0bc660572be0800933ea484fb1632707bdc2de457b96f9aad8a62b1dfd96d766220692
7
- data.tar.gz: a19cc9026478a29161a832454edebf76105d56d3841575af71e9dbaa305a40a68af285d17b7473369ae913e463cadd7b67ab2e099d13d3162b713d2c3cd87d05
6
+ metadata.gz: 82b29e41644233b3aa80205a40e1f72e8616fc45b0c4e10fb12cbe5abf1e13dba36423667ba8ab4ec0e592633056d30d6f760a462137333ed629cffaf4272729
7
+ data.tar.gz: 81f1c484e1254c6b72bd55bcac4dda7aa5b73d2af04125fdbb433ef69baaf796efe022b3384555ef9241261a799af495bbf49fac88d1ca90b3a497908a9f4df6
data/README.markdown CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  ## DESCRIPTION
7
7
 
8
- A gem providing "time travel" and "time freezing" capabilities, making it dead simple to test time-dependent code. It provides a unified method to mock Time.now, Date.today, and DateTime.now in a single call.
8
+ A gem providing "time travel" and "time freezing" capabilities, making it dead simple to test time-dependent code. It provides a unified method to mock `Time.now`, `Date.today`, `DateTime.now`, and `Process.clock_gettime` in a single call.
9
9
 
10
10
  ## INSTALL
11
11
 
@@ -17,13 +17,13 @@ A gem providing "time travel" and "time freezing" capabilities, making it dead s
17
17
  - Travel back to a specific point in time, but allow time to continue moving forward from there.
18
18
  - Scale time by a given scaling factor that will cause time to move at an accelerated pace.
19
19
  - No dependencies, can be used with _any_ ruby project
20
- - Timecop api allows arguments to be passed into #freeze and #travel as one of the following:
20
+ - Timecop api allows arguments to be passed into `#freeze` and `#travel` as one of the following:
21
21
  - Time instance
22
22
  - DateTime instance
23
23
  - Date instance
24
24
  - individual arguments (year, month, day, hour, minute, second)
25
- - a single integer argument that is interpreted as an offset in seconds from Time.now
26
- - Nested calls to Timecop#travel and Timecop#freeze are supported -- each block will maintain its interpretation of now.
25
+ - a single integer argument that is interpreted as an offset in seconds from `Time.now`
26
+ - Nested calls to `Timecop#travel` and `Timecop#freeze` are supported -- each block will maintain its interpretation of now.
27
27
  - Works with regular Ruby projects, and Ruby on Rails projects
28
28
 
29
29
  ## USAGE
@@ -62,7 +62,7 @@ helpful if your whole application is time-sensitive. It allows you to build
62
62
  your test data at a single point in time, and to move in/out of that time as
63
63
  appropriate (within your tests)
64
64
 
65
- in config/environments/test.rb
65
+ in `config/environments/test.rb`
66
66
 
67
67
  ```ruby
68
68
  config.after_initialize do
@@ -74,10 +74,10 @@ end
74
74
 
75
75
  ### The difference between Timecop.freeze and Timecop.travel
76
76
 
77
- freeze is used to statically mock the concept of now. As your program executes,
78
- Time.now will not change unless you make subsequent calls into the Timecop API.
79
- travel, on the other hand, computes an offset between what we currently think
80
- Time.now is (recall that we support nested traveling) and the time passed in.
77
+ `freeze` is used to statically mock the concept of now. As your program executes,
78
+ `Time.now` will not change unless you make subsequent calls into the Timecop API.
79
+ `travel`, on the other hand, computes an offset between what we currently think
80
+ `Time.now` is (recall that we support nested traveling) and the time passed in.
81
81
  It uses this offset to simulate the passage of time. To demonstrate, consider
82
82
  the following code snippets:
83
83
 
@@ -129,6 +129,15 @@ Timecop.freeze
129
129
  # => Timecop::SafeModeException: Safe mode is enabled, only calls passing a block are allowed.
130
130
  ```
131
131
 
132
+ ### Configuring Mocking Process.clock_gettime
133
+
134
+ By default Timecop does not mock Process.clock_gettime. You must enable it like this:
135
+
136
+ ``` ruby
137
+ # turn on
138
+ Timecop.mock_process_clock = true
139
+ ```
140
+
132
141
  ### Rails v Ruby Date/Time libraries
133
142
 
134
143
  Sometimes [Rails Date/Time methods don't play nicely with Ruby Date/Time methods.](https://rails.lighthouseapp.com/projects/8994/tickets/6410-dateyesterday-datetoday)
@@ -50,21 +50,28 @@ class Date #:nodoc:
50
50
 
51
51
  d = Date._strptime(str, fmt)
52
52
  now = Time.now.to_date
53
- year = d[:year] || now.year
53
+ year = d[:year] || d[:cwyear] || now.year
54
54
  mon = d[:mon] || now.mon
55
55
  if d.keys == [:year]
56
56
  Date.new(year, 1, 1, start)
57
57
  elsif d[:mday]
58
58
  Date.new(year, mon, d[:mday], start)
59
- elsif d[:wday]
60
- Date.new(year, mon, now.mday, start) + (d[:wday] - now.wday)
61
59
  elsif d[:yday]
62
60
  Date.new(year, 1, 1, start).next_day(d[:yday] - 1)
63
- elsif d[:cwyear] && d[:cweek]
64
- if d[:cwday]
65
- Date.commercial(d[:cwyear], d[:cweek], d[:cwday], start)
66
- else
67
- Date.commercial(d[:cwyear], d[:cweek], 1, start)
61
+ elsif d[:cwyear] || d[:cweek] || d[:wnum0] || d[:wnum1] || d[:wday] || d[:cwday]
62
+ week = d[:cweek] || d[:wnum1] || d[:wnum0] || now.strftime('%W').to_i
63
+ if d[:wnum0] #Week of year where week starts on sunday
64
+ if d[:cwday] #monday based day of week
65
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:cwday]}", '%Y %U %u', start)
66
+ else
67
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:wday] || 0}", '%Y %U %w', start)
68
+ end
69
+ else #Week of year where week starts on monday
70
+ if d[:wday] #sunday based day of week
71
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:wday]}", '%Y %W %w', start)
72
+ else
73
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:cwday] || 1}", '%Y %W %u', start)
74
+ end
68
75
  end
69
76
  elsif d[:seconds]
70
77
  Time.at(d[:seconds]).to_date
@@ -125,7 +132,6 @@ class DateTime #:nodoc:
125
132
  alias_method :now, :now_with_mock_time
126
133
 
127
134
  def parse_with_mock_date(*args)
128
- date_hash = Date._parse(*args)
129
135
  parsed_date = parse_without_mock_date(*args)
130
136
  return parsed_date unless mocked_time_stack_item
131
137
  date_hash = DateTime._parse(*args)
@@ -137,8 +143,17 @@ class DateTime #:nodoc:
137
143
  DateTime.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
138
144
  when date_hash[:mday]
139
145
  DateTime.new(mocked_time_stack_item.year, mocked_time_stack_item.month, date_hash[:mday])
146
+ when date_hash[:wday] && date_hash[:hour] && date_hash[:min]
147
+ closest_date = Date.closest_wday(date_hash[:wday]).to_datetime
148
+
149
+ DateTime.new(
150
+ closest_date.year, closest_date.month, closest_date.day,
151
+ date_hash[:hour], date_hash[:min]
152
+ )
140
153
  when date_hash[:wday]
141
154
  Date.closest_wday(date_hash[:wday]).to_datetime
155
+ when date_hash[:hour] && date_hash[:min] && date_hash[:sec]
156
+ DateTime.new(mocked_time_stack_item.year, mocked_time_stack_item.month, mocked_time_stack_item.day, date_hash[:hour], date_hash[:min], date_hash[:sec])
142
157
  else
143
158
  parsed_date + mocked_time_stack_item.travel_offset_days
144
159
  end
@@ -152,3 +167,59 @@ class DateTime #:nodoc:
152
167
  end
153
168
  end
154
169
  end
170
+
171
+ if RUBY_VERSION >= '2.1.0'
172
+ module Process #:nodoc:
173
+ class << self
174
+ alias_method :clock_gettime_without_mock, :clock_gettime
175
+
176
+ def clock_gettime_mock_time(clock_id, unit = :float_second)
177
+ mock_time = case clock_id
178
+ when Process::CLOCK_MONOTONIC
179
+ mock_time_monotonic
180
+ when Process::CLOCK_REALTIME
181
+ mock_time_realtime
182
+ end
183
+
184
+ return clock_gettime_without_mock(clock_id, unit) unless Timecop.mock_process_clock? && mock_time
185
+
186
+ divisor = case unit
187
+ when :float_second
188
+ 1_000_000_000.0
189
+ when :second
190
+ 1_000_000_000
191
+ when :float_millisecond
192
+ 1_000_000.0
193
+ when :millisecond
194
+ 1_000_000
195
+ when :float_microsecond
196
+ 1000.0
197
+ when :microsecond
198
+ 1000
199
+ when :nanosecond
200
+ 1
201
+ end
202
+
203
+ (mock_time / divisor)
204
+ end
205
+
206
+ alias_method :clock_gettime, :clock_gettime_mock_time
207
+
208
+ private
209
+
210
+ def mock_time_monotonic
211
+ mocked_time_stack_item = Timecop.top_stack_item
212
+ mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.monotonic
213
+ end
214
+
215
+ def mock_time_realtime
216
+ mocked_time_stack_item = Timecop.top_stack_item
217
+
218
+ return nil if mocked_time_stack_item.nil?
219
+
220
+ t = mocked_time_stack_item.time
221
+ t.to_i * 1_000_000_000 + t.nsec
222
+ end
223
+ end
224
+ end
225
+ end
@@ -9,6 +9,7 @@ class Timecop
9
9
  @travel_offset = @scaling_factor = nil
10
10
  @scaling_factor = args.shift if mock_type == :scale
11
11
  @mock_type = mock_type
12
+ @monotonic = parse_monotonic_time(*args) if RUBY_VERSION >= '2.1.0'
12
13
  @time = parse_time(*args)
13
14
  @time_was = Time.now_without_mock_time
14
15
  @travel_offset = compute_travel_offset
@@ -54,6 +55,26 @@ class Timecop
54
55
  @scaling_factor
55
56
  end
56
57
 
58
+ if RUBY_VERSION >= '2.1.0'
59
+ def monotonic
60
+ if travel_offset.nil?
61
+ @monotonic
62
+ elsif scaling_factor.nil?
63
+ current_monotonic + travel_offset * (10 ** 9)
64
+ else
65
+ (@monotonic + (current_monotonic - @monotonic) * scaling_factor).to_i
66
+ end
67
+ end
68
+
69
+ def current_monotonic
70
+ Process.clock_gettime_without_mock(Process::CLOCK_MONOTONIC, :nanosecond)
71
+ end
72
+
73
+ def current_monotonic_with_mock
74
+ Process.clock_gettime_mock_time(Process::CLOCK_MONOTONIC, :nanosecond)
75
+ end
76
+ end
77
+
57
78
  def time(time_klass = Time) #:nodoc:
58
79
  if @time.respond_to?(:in_time_zone)
59
80
  time = time_klass.at(@time.dup.localtime)
@@ -97,6 +118,16 @@ class Timecop
97
118
  Rational(utc_offset, 24 * 60 * 60)
98
119
  end
99
120
 
121
+ def parse_monotonic_time(*args)
122
+ arg = args.shift
123
+ offset_in_nanoseconds = if args.empty? && (arg.kind_of?(Integer) || arg.kind_of?(Float))
124
+ arg * 1_000_000_000
125
+ else
126
+ 0
127
+ end
128
+ current_monotonic_with_mock + offset_in_nanoseconds
129
+ end
130
+
100
131
  def parse_time(*args)
101
132
  arg = args.shift
102
133
  if arg.is_a?(Time)
@@ -38,6 +38,12 @@ class Timecop
38
38
  # previous values after the block has finished executing. This allows us to nest multiple
39
39
  # calls to Timecop.travel and have each block maintain it's concept of "now."
40
40
  #
41
+ # The Process.clock_gettime call mocks both CLOCK::MONOTIC and CLOCK::REALTIME
42
+ #
43
+ # CLOCK::MONOTONIC works slightly differently than other clocks. This clock cannot move to a
44
+ # particular date/time. So the only option that changes this clock is #4 which will move the
45
+ # clock the requested offset. Otherwise the clock is frozen to the current tick.
46
+ #
41
47
  # * Note: Timecop.freeze will actually freeze time. This can cause unanticipated problems if
42
48
  # benchmark or other timing calls are executed, which implicitly expect Time to actually move
43
49
  # forward.
@@ -121,9 +127,27 @@ class Timecop
121
127
  instance.thread_safe
122
128
  end
123
129
 
124
- # Returns whether or not Timecop is currently frozen/travelled
130
+ # Returns whether or not Timecop is currently frozen
125
131
  def frozen?
126
- !instance.stack.empty?
132
+ !instance.stack.empty? && instance.stack.last.mock_type == :freeze
133
+ end
134
+
135
+ # Returns whether or not Timecop is currently travelled
136
+ def travelled?
137
+ !instance.stack.empty? && instance.stack.last.mock_type == :travel
138
+ end
139
+
140
+ # Returns whether or not Timecop is currently scaled
141
+ def scaled?
142
+ !instance.stack.empty? && instance.stack.last.mock_type == :scale
143
+ end
144
+
145
+ def mock_process_clock=(mock)
146
+ @mock_process_clock = mock
147
+ end
148
+
149
+ def mock_process_clock?
150
+ @mock_process_clock ||= false
127
151
  end
128
152
 
129
153
  private
@@ -1,3 +1,3 @@
1
1
  class Timecop
2
- VERSION = "0.9.6"
2
+ VERSION = "0.9.10"
3
3
  end
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.9.6
4
+ version: 0.9.10
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: 2022-11-29 00:00:00.000000000 Z
12
+ date: 2024-06-14 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
@@ -29,11 +29,6 @@ files:
29
29
  - lib/timecop/time_stack_item.rb
30
30
  - lib/timecop/timecop.rb
31
31
  - lib/timecop/version.rb
32
- - test/test_helper.rb
33
- - test/time_stack_item_test.rb
34
- - test/timecop_test.rb
35
- - test/timecop_without_date_but_with_time_test.rb
36
- - test/timecop_without_date_test.rb
37
32
  homepage: https://github.com/travisjeffery/timecop
38
33
  licenses:
39
34
  - MIT
@@ -54,15 +49,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
49
  - !ruby/object:Gem::Version
55
50
  version: '0'
56
51
  requirements: []
57
- rubygems_version: 3.2.15
52
+ rubygems_version: 3.2.22
58
53
  signing_key:
59
- specification_version: 3
54
+ specification_version: 4
60
55
  summary: A gem providing "time travel" and "time freezing" capabilities, making it
61
56
  dead simple to test time-dependent code. It provides a unified method to mock Time.now,
62
57
  Date.today, and DateTime.now in a single call.
63
- test_files:
64
- - test/test_helper.rb
65
- - test/time_stack_item_test.rb
66
- - test/timecop_test.rb
67
- - test/timecop_without_date_test.rb
68
- - test/timecop_without_date_but_with_time_test.rb
58
+ test_files: []
data/test/test_helper.rb DELETED
@@ -1,59 +0,0 @@
1
- require 'bundler/setup'
2
- require 'minitest/autorun'
3
- require 'minitest/rg'
4
- require 'pry'
5
-
6
- $VERBOSE = true # enable ruby warnings
7
-
8
- require 'mocha/minitest'
9
-
10
- class Minitest::Test
11
- private
12
- # Tests to see that two times are within the given distance,
13
- # in seconds, from each other.
14
- def times_effectively_equal(time1, time2, seconds_interval = 1)
15
- (time1 - time2).abs <= seconds_interval
16
- end
17
-
18
- def assert_times_effectively_equal(time1, time2, seconds_interval = 1, msg = nil)
19
- assert times_effectively_equal(time1, time2, seconds_interval), "#{msg}: time1 = #{time1.to_s}, time2 = #{time2.to_s}"
20
- end
21
-
22
- def assert_times_effectively_not_equal(time1, time2, seconds_interval = 1, msg = nil)
23
- assert !times_effectively_equal(time1, time2, seconds_interval), "#{msg}: time1 = #{time1.to_s}, time2 = #{time2.to_s}"
24
- end
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
30
- end
31
-
32
- TIMEZONES = ["Pacific/Midway", "Europe/Paris", "UTC", "America/Chicago"]
33
-
34
- def each_timezone
35
- old_tz = ENV["TZ"]
36
-
37
- begin
38
- TIMEZONES.each do |timezone|
39
- ENV["TZ"] = timezone
40
- yield
41
- end
42
- ensure
43
- ENV["TZ"] = old_tz
44
- end
45
- end
46
-
47
- def a_time_stack_item
48
- Timecop::TimeStackItem.new(:freeze, 2008, 1, 1, 0, 0, 0)
49
- end
50
-
51
- def assert_date_times_equal(dt1, dt2)
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}"
53
- end
54
-
55
- def jruby?
56
- RUBY_PLATFORM == "java"
57
- end
58
-
59
- end