timecop 0.9.8 → 0.9.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad1963ee820d4d3519b4e078879aaa3914b2e9802460480fd9ca3c4a239677e4
4
- data.tar.gz: 49a827fc02c8580d33d6861f51974dd046996e7281442c33f89c527d4d4c5af0
3
+ metadata.gz: 79ad5f132158245aadf9f2afbfbfaa2e0ed8298e69037270f51cb801f9794b88
4
+ data.tar.gz: ec1d3177b1ca8f8f03c2bef0316f97953a4fe021d250903ea9159cdca8079550
5
5
  SHA512:
6
- metadata.gz: d18895efbc69f3768fcf68fa372759e727c30869e5ff1a77c8b7a2901e4a89dd49fe61df38c0a5c57dd9604250e0c49733c990c32757ac9a374a56fb3962d441
7
- data.tar.gz: 6938b5dfc45c545d8c0e83b757f1b99d9b69b2cfe23d5e1d0f9b4fc4ff56282af201a982bcf52084f05b0de88ea36256ecbe54e2988fc944693940f5179dcb97
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
 
@@ -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)
@@ -143,8 +143,17 @@ class DateTime #:nodoc:
143
143
  DateTime.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
144
144
  when date_hash[:mday]
145
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
+ )
146
153
  when date_hash[:wday]
147
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])
148
157
  else
149
158
  parsed_date + mocked_time_stack_item.travel_offset_days
150
159
  end
@@ -158,3 +167,59 @@ class DateTime #:nodoc:
158
167
  end
159
168
  end
160
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.
@@ -126,6 +132,24 @@ class Timecop
126
132
  !instance.stack.empty? && instance.stack.last.mock_type == :freeze
127
133
  end
128
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
151
+ end
152
+
129
153
  private
130
154
  def send_travel(mock_type, *args, &block)
131
155
  val = instance.travel(mock_type, *args, &block)
@@ -1,3 +1,3 @@
1
1
  class Timecop
2
- VERSION = "0.9.8"
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.8
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: 2023-08-14 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
@@ -49,7 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
49
  - !ruby/object:Gem::Version
50
50
  version: '0'
51
51
  requirements: []
52
- rubygems_version: 3.2.15
52
+ rubygems_version: 3.2.22
53
53
  signing_key:
54
54
  specification_version: 4
55
55
  summary: A gem providing "time travel" and "time freezing" capabilities, making it