timecop 0.5.1 → 0.6.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.
data/README.markdown CHANGED
@@ -1,6 +1,8 @@
1
1
  # timecop
2
2
 
3
- - Source[http://github.com/jtrupiano/timecop]
3
+ [![Build Status](https://secure.travis-ci.org/travisjeffery/timecop.png)](http://travis-ci.org/travisjeffery/timecop)
4
+
5
+ - Source[http://github.com/travisjeffery/timecop]
4
6
  - Documentation[http://johntrupiano.rubyforge.org/timecop]
5
7
 
6
8
  ## DESCRIPTION
@@ -15,6 +17,7 @@ A gem providing "time travel" and "time freezing" capabilities, making it dead s
15
17
 
16
18
  - Freeze time to a specific point.
17
19
  - Travel back to a specific point in time, but allow time to continue moving forward from there.
20
+ - Scale time by a given scaling factor that will cause time to move at an accelerated pace.
18
21
  - No dependencies, can be used with _any_ ruby project
19
22
  - Timecop api allows arguments to be passed into #freeze and #travel as one of the following:
20
23
  - Time instance
@@ -39,6 +42,22 @@ Timecop.freeze(Date.today + 30) do
39
42
  end
40
43
  ```
41
44
 
45
+ You can mock the time for a set of tests easily via setup/teardown methods
46
+
47
+ ```ruby
48
+ describe "some set of tests to mock" do
49
+ before do
50
+ Timecop.freeze(Time.local(1990))
51
+ end
52
+
53
+ after do
54
+ Timecop.return
55
+ end
56
+
57
+ it "should do blah blah blah" {}
58
+ end
59
+ ```
60
+
42
61
  Set the time for the test environment of a rails app -- this is particularly
43
62
  helpful if your whole application is time-sensitive. It allows you to build
44
63
  your test data at a single point in time, and to move in/out of that time as
@@ -75,6 +94,25 @@ sleep(10)
75
94
  new_time == Time.now # ==> false
76
95
  ```
77
96
 
97
+ ### Timecop.scale
98
+
99
+ Let's say you want to test a "live" integration wherein entire days could pass by
100
+ in minutes while you're able to simulate "real" activity. For example, one such use case
101
+ is being able to test reports and invoices that run in 30 day cycles in very little time, while also
102
+ being able to simulate activity via subsequent calls to your application.
103
+
104
+ ```ruby
105
+ # seconds will now seem like hours
106
+ Timecop.scale(3600)
107
+ Time.now
108
+ # => 2012-09-20 21:23:25 -0500
109
+ # seconds later, hours have past it's gone from 9pm at night to 6am in the morning
110
+ Time.now
111
+ # => 2012-09-21 06:22:59 -0500
112
+ ```
113
+
114
+ See #42 for more information, thanks to Ken Mayer, David Holcomb, and Pivotal Labs.
115
+
78
116
  ## REFERENCES
79
117
 
80
118
  * {0.3.4 release}[http://blog.smartlogicsolutions.com/2009/12/07/timecop-0-3-4-released/]
@@ -84,8 +122,8 @@ new_time == Time.now # ==> false
84
122
 
85
123
  ## Contribute
86
124
 
87
- timecop is maintained by [travisjeffery](http://github.com/travisjeffery), who
88
- you can also hit up on [Twitter](http://twitter.com/travisjeffery).
125
+ timecop is maintained by [travisjeffery](http://github.com/travisjeffery), and
126
+ was created by [jtrupiano](https://github.com/jtrupiano).
89
127
 
90
128
  Here's the most direct way to get your work merged into the project.
91
129
 
@@ -1,29 +1,25 @@
1
1
 
2
2
  class Time #:nodoc:
3
3
  class << self
4
- # Time we are behaving as
5
4
  def mock_time
6
5
  mocked_time_stack_item = Timecop.top_stack_item
7
6
  mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.time(self)
8
7
  end
9
8
 
10
- # Alias the original now
11
9
  alias_method :now_without_mock_time, :now
12
10
 
13
- # Define now_with_mock_time
14
11
  def now_with_mock_time
15
12
  mock_time || now_without_mock_time
16
13
  end
17
14
 
18
- # Alias now to now_with_mock_time
19
15
  alias_method :now, :now_with_mock_time
20
16
 
21
17
  alias_method :new_without_mock_time, :new
22
18
 
23
- def new_with_mock_time *args
19
+ def new_with_mock_time(*args)
24
20
  begin
25
21
  raise ArgumentError.new if args.size <= 0
26
- new_without_mock_time *args
22
+ new_without_mock_time(*args)
27
23
  rescue ArgumentError
28
24
  now
29
25
  end
@@ -36,21 +32,17 @@ end
36
32
  if Object.const_defined?(:Date) && Date.respond_to?(:today)
37
33
  class Date #:nodoc:
38
34
  class << self
39
- # Date we are behaving as
40
35
  def mock_date
41
36
  mocked_time_stack_item = Timecop.top_stack_item
42
37
  mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.date(self)
43
38
  end
44
39
 
45
- # Alias the original today
46
40
  alias_method :today_without_mock_date, :today
47
41
 
48
- # Define today_with_mock_date
49
42
  def today_with_mock_date
50
43
  mock_date || today_without_mock_date
51
44
  end
52
45
 
53
- # Alias today to today_with_mock_date
54
46
  alias_method :today, :today_with_mock_date
55
47
  end
56
48
  end
@@ -59,27 +51,31 @@ end
59
51
  if Object.const_defined?(:DateTime) && DateTime.respond_to?(:now)
60
52
  class DateTime #:nodoc:
61
53
  class << self
62
- # Time we are behaving as
63
54
  def mock_time
64
55
  mocked_time_stack_item = Timecop.top_stack_item
65
56
  mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.datetime(self)
66
57
  end
67
58
 
68
- # Fake alias :now_without_mock_time :now
69
- # It appears that the DateTime library itself references Time.now
70
- # for it's interpretation of now which caused
71
- # DateTime.now_without_mock_time to incorrectly return the frozen time.
72
59
  def now_without_mock_time
73
- Time.now_without_mock_time.send :to_datetime
60
+ Time.now_without_mock_time.to_datetime
74
61
  end
75
62
 
76
- # Define now_with_mock_time
77
63
  def now_with_mock_time
78
64
  mock_time || now_without_mock_time
79
65
  end
80
66
 
81
- # Alias now to now_with_mock_time
82
67
  alias_method :now, :now_with_mock_time
83
68
  end
84
69
  end
70
+
71
+ # for ruby1.8
72
+ unless Time::public_instance_methods.include? :to_datetime
73
+ class DateTime
74
+ class << self
75
+ def now_without_mock_time
76
+ Time.now_without_mock_time.send(:to_datetime)
77
+ end
78
+ end
79
+ end
80
+ end
85
81
  end
@@ -1,92 +1,110 @@
1
-
2
1
  class Timecop
3
- # A data class for carrying around "time movement" objects. Makes it easy to keep track of the time
4
- # movements on a simple stack.
5
- class TimeStackItem #:nodoc:
6
-
7
- attr_reader :mock_type
8
- def initialize(mock_type, *args)
9
- raise "Unknown mock_type #{mock_type}" unless [:freeze, :travel].include?(mock_type)
10
- @mock_type = mock_type
11
- @time = parse_time(*args)
12
- @travel_offset = compute_travel_offset
13
- @dst_adjustment = compute_dst_adjustment(@time)
14
- end
15
-
16
- def year
17
- time.year
18
- end
19
-
20
- def month
21
- time.month
22
- end
23
-
24
- def day
25
- time.day
26
- end
27
-
28
- def hour
29
- time.hour
30
- end
31
-
32
- def min
33
- time.min
34
- end
35
-
36
- def sec
37
- time.sec
38
- end
39
-
40
- def utc_offset
41
- time.utc_offset
42
- end
43
-
44
- def travel_offset
45
- @travel_offset
46
- end
47
-
48
- def time(time_klass=Time) #:nodoc:
49
- if travel_offset.nil?
50
- time_klass.at( @time.to_f )
51
- else
52
- time_klass.at( ( Time.now_without_mock_time + travel_offset ).to_f )
2
+ # A data class for carrying around "time movement" objects. Makes it easy to keep track of the time
3
+ # movements on a simple stack.
4
+ class TimeStackItem #:nodoc:
5
+ attr_reader :mock_type
6
+
7
+ def initialize(mock_type, *args)
8
+ raise "Unknown mock_type #{mock_type}" unless [:freeze, :travel, :scale].include?(mock_type)
9
+ @scaling_factor = args.shift if mock_type == :scale
10
+ @mock_type = mock_type
11
+ @time = parse_time(*args)
12
+ @time_was = Time.now_without_mock_time
13
+ @travel_offset = compute_travel_offset
14
+ @dst_adjustment = compute_dst_adjustment(@time)
53
15
  end
54
- end
55
-
56
- def date(date_klass=Date)
57
- date_klass.jd(time.__send__(:to_date).jd)
58
- end
59
-
60
- def datetime(datetime_klass=DateTime)
61
- # DateTime doesn't know about DST, so let's remove its presence
62
- our_offset = utc_offset + dst_adjustment
63
- if Float.method_defined?(:to_r)
64
- fractions_of_a_second = time.to_f % 1
65
- datetime_klass.new(year, month, day, hour, min, sec + fractions_of_a_second,
66
- utc_offset_to_rational(our_offset))
67
- else
16
+
17
+ def year
18
+ time.year
19
+ end
20
+
21
+ def month
22
+ time.month
23
+ end
24
+
25
+ def day
26
+ time.day
27
+ end
28
+
29
+ def hour
30
+ time.hour
31
+ end
32
+
33
+ def min
34
+ time.min
35
+ end
36
+
37
+ def sec
38
+ time.sec
39
+ end
40
+
41
+ def utc_offset
42
+ time.utc_offset
43
+ end
44
+
45
+ def travel_offset
46
+ @travel_offset
47
+ end
48
+
49
+ def scaling_factor
50
+ @scaling_factor
51
+ end
52
+
53
+ def time(klass = time_klass) #:nodoc:
54
+ begin
55
+ actual_time = klass.at(@time)
56
+ calculated_time = klass.at(@time.to_f)
57
+ time = times_are_equal_within_epsilon(actual_time, calculated_time, 1) ? actual_time : calculated_time
58
+ rescue
59
+ time = klass.at(@time.to_f)
60
+ end
61
+
62
+ if travel_offset.nil?
63
+ time
64
+ elsif scaling_factor.nil?
65
+ klass.at(Time.now_without_mock_time + travel_offset)
66
+ else
67
+ klass.at(scaled_time)
68
+ end
69
+ end
70
+
71
+ def scaled_time
72
+ (@time + (Time.now_without_mock_time - @time_was) * scaling_factor).to_f
73
+ end
74
+
75
+ def date(date_klass = Date)
76
+ date_klass.jd(time.__send__(:to_date).jd)
77
+ end
78
+
79
+ def datetime(datetime_klass = DateTime)
68
80
  our_offset = utc_offset + dst_adjustment
69
- datetime_klass.new(year, month, day, hour, min, sec, utc_offset_to_rational(our_offset))
81
+
82
+ if Float.method_defined?(:to_r)
83
+ fractions_of_a_second = time.to_f % 1
84
+ datetime_klass.new(year, month, day, hour, min, sec + fractions_of_a_second, utc_offset_to_rational(our_offset))
85
+ else
86
+ our_offset = utc_offset + dst_adjustment
87
+ datetime_klass.new(year, month, day, hour, min, sec, utc_offset_to_rational(our_offset))
88
+ end
70
89
  end
71
- end
72
-
73
- def dst_adjustment
74
- @dst_adjustment
75
- end
76
-
77
- private
90
+
91
+ def dst_adjustment
92
+ @dst_adjustment
93
+ end
94
+
95
+ private
96
+
78
97
  def rational_to_utc_offset(rational)
79
98
  ((24.0 / rational.denominator) * rational.numerator) * (60 * 60)
80
99
  end
81
-
100
+
82
101
  def utc_offset_to_rational(utc_offset)
83
102
  Rational(utc_offset, 24 * 60 * 60)
84
103
  end
85
-
104
+
86
105
  def parse_time(*args)
87
- time_klass = Time.respond_to?(:zone) && Time.zone ? Time.zone : Time
88
106
  arg = args.shift
89
- if arg.is_a?(Time)
107
+ if arg.is_a?(Time)
90
108
  if Timecop.active_support != false && arg.respond_to?(:in_time_zone)
91
109
  arg.in_time_zone
92
110
  else
@@ -117,17 +135,24 @@ class Timecop
117
135
  end
118
136
  end
119
137
  end
120
-
138
+
121
139
  def compute_dst_adjustment(time)
122
140
  return 0 if !(time.dst? ^ Time.now.dst?)
123
141
  return -1 * 60 * 60 if time.dst?
124
142
  return 60 * 60
125
143
  end
126
-
144
+
127
145
  def compute_travel_offset
128
146
  return nil if mock_type == :freeze
129
147
  time - Time.now_without_mock_time
130
148
  end
131
-
149
+
150
+ def times_are_equal_within_epsilon t1, t2, epsilon_in_seconds
151
+ (t1 - t2).abs < epsilon_in_seconds
152
+ end
153
+
154
+ def time_klass
155
+ Time.respond_to?(:zone) ? Time.zone : Time
156
+ end
157
+ end
132
158
  end
133
- end
@@ -1,6 +1,6 @@
1
1
  require 'singleton'
2
- require 'timecop/time_extensions'
3
- require 'timecop/time_stack_item'
2
+ require File.join(File.dirname(__FILE__), "time_extensions")
3
+ require File.join(File.dirname(__FILE__), "time_stack_item")
4
4
 
5
5
  # Timecop
6
6
  # * Wrapper class for manipulating the extensions to the Time, Date, and DateTime objects
@@ -47,11 +47,9 @@ class Timecop
47
47
  # which will lead to files being generated with the timestamp set by the Timecop.freeze call
48
48
  # in your dev environment
49
49
  #
50
- # Returns the value of the block or nil.
50
+ # Returns the value of the block if one is given, or the mocked time.
51
51
  def freeze(*args, &block)
52
- val = instance().send(:travel, :freeze, *args, &block)
53
-
54
- block_given? ? val : nil
52
+ send_travel(:freeze, *args, &block)
55
53
  end
56
54
 
57
55
  # Allows you to run a block of code and "fake" a time throughout the execution of that block.
@@ -60,74 +58,103 @@ class Timecop
60
58
  # * Note: Timecop.travel will not freeze time (as opposed to Timecop.freeze). This is a particularly
61
59
  # good candidate for use in environment files in rails projects.
62
60
  #
63
- # Returns the value of the block or nil.
61
+ # Returns the value of the block if one is given, or the mocked time.
64
62
  def travel(*args, &block)
65
- val = instance().send(:travel, :travel, *args, &block)
63
+ send_travel(:travel, *args, &block)
64
+ end
66
65
 
67
- block_given? ? val : nil
66
+ # Allows you to run a block of code and "scale" a time throughout the execution of that block.
67
+ # The first argument is a scaling factor, for example:
68
+ # Timecop.scale(2) do
69
+ # ... time will 'go' twice as fast here
70
+ # end
71
+ # See Timecop#freeze for exact usage of the other arguments
72
+ #
73
+ # Returns the value of the block if one is given, or the mocked time.
74
+ def scale(*args, &block)
75
+ send_travel(:scale, *args, &block)
68
76
  end
69
77
 
70
78
  def baseline
71
- instance().send(:baseline)
79
+ instance.send(:baseline)
72
80
  end
73
81
 
74
82
  def baseline=(baseline)
75
- instance().send(:baseline=, baseline)
83
+ instance.send(:baseline=, baseline)
76
84
  end
77
85
 
78
- # Reverts back to system's Time.now, Date.today and DateTime.now (if it exists).
79
- def return
80
- instance().send(:unmock!)
81
- nil
86
+ # Reverts back to system's Time.now, Date.today and DateTime.now (if it exists) permamently when
87
+ # no block argument is given, or temporarily reverts back to the system's time temporarily for
88
+ # the given block.
89
+ def return(&block)
90
+ if block_given?
91
+ instance.send(:return, &block)
92
+ else
93
+ instance.send(:unmock!)
94
+ nil
95
+ end
82
96
  end
83
97
 
84
98
  def return_to_baseline
85
- instance().send(:return_to_baseline)
99
+ instance.send(:return_to_baseline)
86
100
  Time.now
87
101
  end
88
102
 
89
103
  def top_stack_item #:nodoc:
90
- instance().instance_variable_get(:@_stack).last
104
+ instance.instance_variable_get(:@_stack).last
105
+ end
106
+
107
+ private
108
+ def send_travel(mock_type, *args, &block)
109
+ val = instance.send(:travel, mock_type, *args, &block)
110
+ block_given? ? val : Time.now
91
111
  end
92
112
  end
93
113
 
94
- protected
114
+ private
95
115
 
96
- def baseline=(baseline)
97
- @baseline = baseline
98
- @_stack << TimeStackItem.new(:travel, baseline)
99
- end
116
+ def baseline=(baseline)
117
+ @baseline = baseline
118
+ @_stack << TimeStackItem.new(:travel, baseline)
119
+ end
100
120
 
101
- def initialize #:nodoc:
102
- @_stack = []
103
- end
121
+ def initialize #:nodoc:
122
+ @_stack = []
123
+ end
104
124
 
105
- def travel(mock_type, *args, &block) #:nodoc:
106
- stack_item = TimeStackItem.new(mock_type, *args)
125
+ def travel(mock_type, *args, &block) #:nodoc:
126
+ stack_item = TimeStackItem.new(mock_type, *args)
107
127
 
108
- # store this time traveling on our stack...
109
- @_stack << stack_item
128
+ @_stack << stack_item
110
129
 
111
- if block_given?
112
- begin
113
- yield stack_item.time
114
- ensure
115
- # pull it off the stack...
116
- @_stack.pop
117
- end
130
+ if block_given?
131
+ begin
132
+ yield stack_item.time
133
+ ensure
134
+ @_stack.pop
118
135
  end
119
136
  end
137
+ end
120
138
 
121
- def unmock! #:nodoc:
122
- @baseline = nil
123
- @_stack = []
124
- end
139
+ def return(&block)
140
+ current_stack = @_stack
141
+ current_baseline = @baseline
142
+ unmock!
143
+ yield
144
+ @_stack = current_stack
145
+ @baseline = current_baseline
146
+ end
125
147
 
126
- def return_to_baseline
127
- if @baseline
128
- @_stack = [@_stack.shift]
129
- else
130
- unmock!
131
- end
148
+ def unmock! #:nodoc:
149
+ @baseline = nil
150
+ @_stack = []
151
+ end
152
+
153
+ def return_to_baseline
154
+ if @baseline
155
+ @_stack = [@_stack.shift]
156
+ else
157
+ unmock!
132
158
  end
159
+ end
133
160
  end
@@ -1,3 +1,3 @@
1
1
  class Timecop
2
- VERSION = "0.5.1"
3
- end
2
+ VERSION = "0.6.0"
3
+ end
data/lib/timecop.rb CHANGED
@@ -1,2 +1,2 @@
1
- require 'timecop/timecop'
2
- require 'timecop/version'
1
+ require File.join(File.dirname(__FILE__), "timecop", "timecop")
2
+ require File.join(File.dirname(__FILE__), "timecop", "version")
data/test/test_helper.rb CHANGED
@@ -1,7 +1,11 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
3
  require 'test/unit'
4
- require 'mocha'
4
+ begin
5
+ require 'mocha/setup'
6
+ rescue LoadError
7
+ require 'mocha'
8
+ end
5
9
 
6
10
  class Test::Unit::TestCase
7
11
 
@@ -1,18 +1,18 @@
1
1
  require 'date'
2
- require 'test_helper'
2
+ require File.join(File.dirname(__FILE__), "test_helper")
3
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
4
4
 
5
5
  class TestTimeStackItem < Test::Unit::TestCase
6
-
7
6
  def teardown
8
7
  Timecop.active_support = nil
9
8
  Timecop.return
10
9
  end
11
-
10
+
12
11
  def test_new_with_time
13
12
  t = Time.now
14
13
  y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
15
14
  stack_item = Timecop::TimeStackItem.new(:freeze, t)
15
+
16
16
  assert_equal y, stack_item.year
17
17
  assert_equal m, stack_item.month
18
18
  assert_equal d, stack_item.day
@@ -25,6 +25,7 @@ class TestTimeStackItem < Test::Unit::TestCase
25
25
  t = Time.new(2012, 7, 28, 20, 0)
26
26
  y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
27
27
  stack_item = Timecop::TimeStackItem.new(:freeze, t)
28
+
28
29
  assert_equal y, stack_item.year
29
30
  assert_equal m, stack_item.month
30
31
  assert_equal d, stack_item.day
@@ -32,11 +33,12 @@ class TestTimeStackItem < Test::Unit::TestCase
32
33
  assert_equal min, stack_item.min
33
34
  assert_equal s, stack_item.sec
34
35
  end
35
-
36
+
36
37
  def test_new_with_datetime_now
37
38
  t = DateTime.now
38
39
  y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
39
40
  stack_item = Timecop::TimeStackItem.new(:freeze, t)
41
+
40
42
  assert_equal y, stack_item.year
41
43
  assert_equal m, stack_item.month
42
44
  assert_equal d, stack_item.day
@@ -44,19 +46,21 @@ class TestTimeStackItem < Test::Unit::TestCase
44
46
  assert_equal min, stack_item.min
45
47
  assert_equal s, stack_item.sec
46
48
  end
47
-
49
+
48
50
  def test_new_with_datetime_in_different_timezone
49
51
  each_timezone do
50
52
  t = DateTime.parse("2009-10-11 00:38:00 +0200")
51
53
  stack_item = Timecop::TimeStackItem.new(:freeze, t)
54
+
52
55
  assert_date_times_equal(t, stack_item.datetime)
53
56
  end
54
57
  end
55
-
58
+
56
59
  def test_new_with_date
57
60
  date = Date.today
58
61
  y, m, d, h, min, s = date.year, date.month, date.day, 0, 0, 0
59
62
  stack_item = Timecop::TimeStackItem.new(:freeze, date)
63
+
60
64
  assert_equal y, stack_item.year
61
65
  assert_equal m, stack_item.month
62
66
  assert_equal d, stack_item.day
@@ -64,7 +68,7 @@ class TestTimeStackItem < Test::Unit::TestCase
64
68
  assert_equal min, stack_item.min
65
69
  assert_equal s, stack_item.sec
66
70
  end
67
-
71
+
68
72
  # Due to the nature of this test (calling Time.now once in this test and
69
73
  # once in #new), this test may fail when two subsequent calls
70
74
  # to Time.now return a different second.
@@ -72,6 +76,7 @@ class TestTimeStackItem < Test::Unit::TestCase
72
76
  t = Time.now
73
77
  y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
74
78
  stack_item = Timecop::TimeStackItem.new(:freeze, 0)
79
+
75
80
  assert_equal y, stack_item.year
76
81
  assert_equal m, stack_item.month
77
82
  assert_equal d, stack_item.day
@@ -79,10 +84,11 @@ class TestTimeStackItem < Test::Unit::TestCase
79
84
  assert_equal min, stack_item.min
80
85
  assert_equal s, stack_item.sec
81
86
  end
82
-
87
+
83
88
  def test_new_with_individual_arguments
84
89
  y, m, d, h, min, s = 2008, 10, 10, 10, 10, 10
85
90
  stack_item = Timecop::TimeStackItem.new(:freeze, y, m, d, h, min, s)
91
+
86
92
  assert_equal y, stack_item.year
87
93
  assert_equal m, stack_item.month
88
94
  assert_equal d, stack_item.day
@@ -90,88 +96,122 @@ class TestTimeStackItem < Test::Unit::TestCase
90
96
  assert_equal min, stack_item.min
91
97
  assert_equal s, stack_item.sec
92
98
  end
93
-
99
+
94
100
  def test_rational_to_utc_offset
95
101
  assert_equal -14400, a_time_stack_item.send(:rational_to_utc_offset, Rational(-1, 6))
96
102
  assert_equal -18000, a_time_stack_item.send(:rational_to_utc_offset, Rational(-5, 24))
97
103
  assert_equal 0, a_time_stack_item.send(:rational_to_utc_offset, Rational(0, 1))
98
104
  assert_equal 3600, a_time_stack_item.send(:rational_to_utc_offset, Rational(1, 24))
99
105
  end
100
-
106
+
101
107
  def test_utc_offset_to_rational
102
108
  assert_equal Rational(-1, 6), a_time_stack_item.send(:utc_offset_to_rational, -14400)
103
109
  assert_equal Rational(-5, 24), a_time_stack_item.send(:utc_offset_to_rational, -18000)
104
110
  assert_equal Rational(0, 1), a_time_stack_item.send(:utc_offset_to_rational, 0)
105
111
  assert_equal Rational(1, 24), a_time_stack_item.send(:utc_offset_to_rational, 3600)
106
112
  end
107
-
108
- # Ensure DST adjustment is calculated properly for DateTime's
113
+
109
114
  def test_compute_dst_adjustment_for_dst_to_dst
110
115
  Timecop.freeze(DateTime.parse("2009-10-1 00:38:00 -0400"))
111
116
  t = DateTime.parse("2009-10-11 00:00:00 -0400")
112
117
  tsi = Timecop::TimeStackItem.new(:freeze, t)
113
118
  return if !(Time.now.dst? && tsi.time.dst?)
119
+
114
120
  assert_equal 0, tsi.send(:dst_adjustment)
115
121
  end
116
-
122
+
117
123
  def test_compute_dst_adjustment_for_non_dst_to_non_dst
118
124
  Timecop.freeze(DateTime.parse("2009-12-1 00:38:00 -0400"))
119
125
  t = DateTime.parse("2009-12-11 00:00:00 -0400")
120
126
  tsi = Timecop::TimeStackItem.new(:freeze, t)
121
127
  return if Time.now.dst? || tsi.time.dst?
122
- assert_equal 0, tsi.send(:dst_adjustment)
128
+
129
+ assert_equal 0, tsi.send(:dst_adjustment)
123
130
  end
124
-
131
+
125
132
  def test_compute_dst_adjustment_for_dst_to_non_dst
126
133
  Timecop.freeze(DateTime.parse("2009-10-1 00:38:00 -0400"))
127
134
  t = DateTime.parse("2009-12-11 00:00:00 -0400")
128
135
  tsi = Timecop::TimeStackItem.new(:freeze, t)
129
136
  return if !Time.now.dst? || tsi.time.dst?
137
+
130
138
  assert_equal 60 * 60, tsi.send(:dst_adjustment)
131
139
  end
132
-
140
+
133
141
  def test_compute_dst_adjustment_for_non_dst_to_dst
134
142
  Timecop.freeze(DateTime.parse("2009-12-1 00:38:00 -0400"))
135
143
  t = DateTime.parse("2009-10-11 00:00:00 -0400")
136
144
  tsi = Timecop::TimeStackItem.new(:freeze, t)
137
145
  return if Time.now.dst? || !tsi.time.dst?
146
+
138
147
  assert_equal -1 * 60 * 60, tsi.send(:dst_adjustment)
139
148
  end
140
-
149
+
141
150
  # Ensure DateTimes handle changing DST properly
142
151
  def test_datetime_for_dst_to_non_dst
143
152
  Timecop.freeze(DateTime.parse("2009-12-1 00:38:00 -0500"))
144
153
  t = DateTime.parse("2009-10-11 00:00:00 -0400")
145
154
  tsi = Timecop::TimeStackItem.new(:freeze, t)
155
+
146
156
  assert_date_times_equal t, tsi.datetime
147
157
  end
148
-
158
+
149
159
  def test_datetime_for_non_dst_to_dst
150
160
  Timecop.freeze(DateTime.parse("2009-10-11 00:00:00 -0400"))
151
161
  t = DateTime.parse("2009-11-30 23:38:00 -0500")
152
162
  tsi = Timecop::TimeStackItem.new(:freeze, t)
153
163
  return if !tsi.time.dst?
164
+
154
165
  assert_date_times_equal t, tsi.datetime
155
166
  assert_equal Date.new(2009, 12, 1), tsi.date
156
167
  end
157
-
158
- # Ensure @travel_offset is set properly
168
+
159
169
  def test_set_travel_offset_for_travel
160
- # Timecop.freeze(2009, 10, 1, 0, 0, 0)
161
170
  t_now = Time.now
162
171
  t = Time.local(2009, 10, 1, 0, 0, 30)
163
172
  expected_offset = t - t_now
164
173
  tsi = Timecop::TimeStackItem.new(:travel, t)
174
+
165
175
  assert_times_effectively_equal expected_offset, tsi.send(:travel_offset), 1, "Offset not calculated correctly"
166
176
  end
167
-
177
+
168
178
  def test_set_travel_offset_for_freeze
169
179
  Timecop.freeze(2009, 10, 1, 0, 0, 0)
170
180
  t = Time.local(2009, 10, 1, 0, 0, 30)
171
181
  tsi = Timecop::TimeStackItem.new(:freeze, t)
182
+
172
183
  assert_equal nil, tsi.send(:travel_offset)
173
184
  end
174
185
 
186
+ def test_timezones
187
+ require 'active_support/all'
188
+ Time.zone = "Europe/Zurich"
189
+ time = Time.zone.parse("2012-12-27T12:12:12+08:00")
190
+ Timecop.freeze(time) do |frozen_time|
191
+ assert_equal time, frozen_time
192
+ end
193
+ end
194
+
195
+ def test_timezones_apply_dates
196
+ require 'active_support/all'
197
+ Time.zone = "Marshall Is."
198
+ time = Time.zone.local(2013,1,3)
199
+
200
+ Timecop.freeze(time) do
201
+ assert_equal time.to_date, Date.today
202
+ end
203
+ end
204
+
205
+ def test_set_scaling_factor_for_scale
206
+ t_now = Time.now
207
+ t = Time.local(2009, 10, 1, 0, 0, 30)
208
+ expected_offset = t - t_now
209
+ tsi = Timecop::TimeStackItem.new(:scale, 4, t)
210
+
211
+ assert_times_effectively_equal expected_offset, tsi.send(:travel_offset), 1, "Offset not calculated correctly"
212
+ assert_equal tsi.send(:scaling_factor), 4, "Scaling factor not set"
213
+ end
214
+
175
215
  def test_parse_string_date_with_active_support
176
216
  date = '2012-01-02'
177
217
  Time.expects(:parse).with(date).returns(Time.local(2012, 01, 02))
@@ -207,4 +247,21 @@ class TestTimeStackItem < Test::Unit::TestCase
207
247
  Timecop.freeze(Date.new(2012, 6, 9))
208
248
  end
209
249
  end
250
+
251
+ def test_nsecs_are_set
252
+ time = Time.now
253
+ Timecop.freeze time
254
+ assert_equal time, Time.now
255
+ assert_equal time.nsec, Time.now.nsec if (Time.now.respond_to?(:nsec))
256
+ end
257
+
258
+ def test_time_with_different_timezone
259
+ require 'active_support/all'
260
+
261
+ Time.zone = "Tokyo"
262
+ t = Time.now
263
+ Timecop.freeze(t) do
264
+ assert_times_effectively_equal t, Time.now
265
+ end
266
+ end
210
267
  end
data/test/timecop_test.rb CHANGED
@@ -1,27 +1,22 @@
1
1
  require 'date'
2
- require 'test_helper'
2
+ require File.join(File.dirname(__FILE__), "test_helper")
3
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
4
4
 
5
5
  class TestTimecop < Test::Unit::TestCase
6
-
7
- def setup
8
-
9
- end
10
-
11
- # just in case...let's really make sure that Timecop is disabled between tests...
12
6
  def teardown
13
7
  Timecop.return
14
8
  end
15
9
 
16
10
  def test_freeze_changes_and_resets_time
17
- # depending on how we're invoked (individually or via the rake test suite)
18
11
  assert !Time.respond_to?(:zone) || Time.zone.nil?
19
12
 
20
13
  t = Time.local(2008, 10, 10, 10, 10, 10)
21
14
  assert_not_equal t, Time.now
15
+
22
16
  Timecop.freeze(2008, 10, 10, 10, 10, 10) do
23
17
  assert_equal t, Time.now
24
18
  end
19
+
25
20
  assert_not_equal t, Time.now
26
21
  end
27
22
 
@@ -68,6 +63,7 @@ class TestTimecop < Test::Unit::TestCase
68
63
  custom_timeklass = Class.new(Time) do
69
64
  def custom_format_method() strftime('%F') end
70
65
  end
66
+
71
67
  Timecop.freeze(2008, 10, 10, 10, 10, 10) do
72
68
  assert custom_timeklass.now.is_a? custom_timeklass
73
69
  assert Time.now.eql? custom_timeklass.now
@@ -80,6 +76,7 @@ class TestTimecop < Test::Unit::TestCase
80
76
  custom_dateklass = Class.new(Date) do
81
77
  def custom_format_method() strftime('%F') end
82
78
  end
79
+
83
80
  Timecop.freeze(2008, 10, 10, 10, 10, 10) do
84
81
  assert custom_dateklass.today.is_a? custom_dateklass
85
82
  assert Date.today.eql? custom_dateklass.today
@@ -92,6 +89,7 @@ class TestTimecop < Test::Unit::TestCase
92
89
  custom_datetimeklass = Class.new(DateTime) do
93
90
  def custom_format_method() strftime('%F') end
94
91
  end
92
+
95
93
  Timecop.freeze(2008, 10, 10, 10, 10, 10) do
96
94
  assert custom_datetimeklass.now.is_a? custom_datetimeklass
97
95
  assert DateTime.now.eql? custom_datetimeklass.now
@@ -119,6 +117,7 @@ class TestTimecop < Test::Unit::TestCase
119
117
  assert_date_times_equal DateTime.new(2008, 10, 10, 10, 10, 10, local_offset), DateTime.now
120
118
  assert_equal Date.new(2008, 10, 10), Date.today
121
119
  end
120
+
122
121
  assert_not_equal t, Time.now
123
122
  assert_not_equal DateTime.new(2008, 10, 10, 10, 10, 10, local_offset), DateTime.now
124
123
  assert_not_equal Date.new(2008, 10, 10), Date.today
@@ -133,7 +132,6 @@ class TestTimecop < Test::Unit::TestCase
133
132
  Timecop.freeze(t) do
134
133
  assert_date_times_equal t, DateTime.now
135
134
  end
136
- # Undo the original freeze
137
135
  Timecop.return
138
136
  end
139
137
  end
@@ -250,6 +248,21 @@ class TestTimecop < Test::Unit::TestCase
250
248
  end
251
249
  end
252
250
 
251
+ def test_scaling_keeps_time_moving_at_an_accelerated_rate
252
+ t = Time.local(2008, 10, 10, 10, 10, 10)
253
+ Timecop.scale(4, t) do
254
+ start = Time.now
255
+ assert_times_effectively_equal start, t, 1, "Looks like we failed to actually travel time"
256
+ sleep(0.25)
257
+ assert_times_effectively_equal Time.at((start + 4*0.25).to_f), Time.now, 0.25, "Looks like time is not moving at 4x"
258
+ end
259
+ end
260
+
261
+ def test_scaling_returns_now_if_no_block_given
262
+ t = Time.local(2008, 10, 10, 10, 10, 10)
263
+ assert_times_effectively_equal t, Timecop.scale(4, t)
264
+ end
265
+
253
266
  def test_freeze_with_utc_time
254
267
  each_timezone do
255
268
  t = Time.utc(2008, 10, 10, 10, 10, 10)
@@ -324,9 +337,22 @@ class TestTimecop < Test::Unit::TestCase
324
337
  assert_nil Time.send(:mock_time)
325
338
  end
326
339
 
327
- def test_travel_time_returns_nil
340
+ def test_travel_time_returns_now_if_no_block_given
328
341
  t_future = Time.local(2030, 10, 10, 10, 10, 10)
329
- assert_nil Timecop.travel(t_future)
342
+ assert_times_effectively_equal t_future, Timecop.travel(t_future)
343
+ end
344
+
345
+ def test_return_temporarily_returns_to_current_time_in_given_block
346
+ time_after_travel = Time.local(1990, 7, 16)
347
+ now = Time.now
348
+
349
+ Timecop.travel(time_after_travel)
350
+
351
+ assert_times_effectively_equal(time_after_travel, Time.now)
352
+ Timecop.return do
353
+ assert_times_effectively_equal(now, Time.now)
354
+ end
355
+ assert_times_effectively_equal(time_after_travel, Time.now)
330
356
  end
331
357
 
332
358
  def test_travel_time_with_block_returns_the_value_of_the_block
@@ -337,9 +363,9 @@ class TestTimecop < Test::Unit::TestCase
337
363
  assert_equal expected, actual
338
364
  end
339
365
 
340
- def test_freeze_time_returns_nil
366
+ def test_freeze_time_returns_now_if_no_block_given
341
367
  t_future = Time.local(2030, 10, 10, 10, 10, 10)
342
- assert_nil Timecop.freeze(t_future)
368
+ assert_times_effectively_equal t_future, Timecop.freeze(t_future)
343
369
  end
344
370
 
345
371
  def test_freeze_time_with_block_returns_the_value_of_the_block
@@ -403,4 +429,10 @@ class TestTimecop < Test::Unit::TestCase
403
429
  assert_equal date, Time.now
404
430
  assert_equal date, Time.new
405
431
  end
432
+
433
+ def test_not_callable_send_travel
434
+ assert_raise NoMethodError do
435
+ Timecop.send_travel(:travel, Time.now - 100)
436
+ end
437
+ end
406
438
  end
@@ -1,5 +1,5 @@
1
1
 
2
- require 'test_helper'
2
+ require File.join(File.dirname(__FILE__), "test_helper")
3
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
4
4
 
5
5
  class TestTimecopWithoutDate < Test::Unit::TestCase
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.5.1
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-09-05 00:00:00.000000000 Z
13
+ date: 2013-03-08 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: A gem providing "time travel" and "time freezing" capabilities, making
16
16
  it dead simple to test time-dependent code. It provides a unified method to mock
@@ -22,7 +22,6 @@ extra_rdoc_files:
22
22
  - LICENSE
23
23
  - README.markdown
24
24
  files:
25
- - History.rdoc
26
25
  - LICENSE
27
26
  - README.markdown
28
27
  - Rakefile
@@ -37,8 +36,9 @@ files:
37
36
  - test/timecop_test.rb
38
37
  - test/timecop_without_date_test.rb
39
38
  - test/timecop_without_date_but_with_time_test.rb
40
- homepage: http://github.com/jtrupiano/timecop
41
- licenses: []
39
+ homepage: https://github.com/travisjeffery/timecop
40
+ licenses:
41
+ - MIT
42
42
  post_install_message:
43
43
  rdoc_options:
44
44
  - --charset=UTF-8
@@ -57,8 +57,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
57
  - !ruby/object:Gem::Version
58
58
  version: '0'
59
59
  requirements: []
60
- rubyforge_project: johntrupiano
61
- rubygems_version: 1.8.10
60
+ rubyforge_project: timecop
61
+ rubygems_version: 1.8.23
62
62
  signing_key:
63
63
  specification_version: 3
64
64
  summary: A gem providing "time travel" and "time freezing" capabilities, making it
data/History.rdoc DELETED
@@ -1,132 +0,0 @@
1
- === 0.5.0 / 2012-09-12
2
-
3
- * Maintenance
4
- * Timecop#travel, Timecop#freeze blocks return nil or block value
5
-
6
- === 0.4.6 / 2012-09-05
7
-
8
- * Maintenance
9
- * Fix rounding support for Ruby < 1.9
10
-
11
- === 0.4.5 / 2012-08-09
12
-
13
- * Maintenance
14
- * Subclasses of Time/Date/DateTime now return proper instances
15
-
16
- === 0.4.4 / 2012-07-29
17
-
18
- * Maintenance
19
- * Can now configure Timecop to not use ActiveSupport by setting Timecop.active_support = false
20
-
21
- === 0.4.3 / 2012-07-27
22
-
23
- * Maintenance
24
- * Fix alias of Time#new/Time#now when Time#new given with args
25
-
26
- === 0.4.2 / 2012-07-27
27
-
28
- * Maintenance
29
- * Mock Time#new the same as Time#now since they're synonyms
30
- * Set default arg in parsing to 2000
31
- * Time.freeze defaults to Time#now
32
- * Use Time#parse is available to parse Time's given as strings
33
- * Add ActiveSupport Time.zone support
34
- * Stop DateTime from losing precision after traveling
35
-
36
- === 0.3.5 / 2010-06-07
37
-
38
- * Maintenance
39
- * Return a cloned time instance in order to support the destructive methods #gmt, #utc and #localtime
40
- * Don't use full path requires where unnecessary
41
-
42
- === 0.3.4 / 2009-12-07
43
-
44
- * Maintenance
45
- * Fix various timezone-related issues. Notably, when traveling to a DateTime
46
- instance specified in a non-local timezone, convert provided DateTime
47
- instance to a local instance and return that from DateTime.now.
48
- Code contributed by Michaël Witrant [piglop]
49
- * Fix bug that would not allow Timecop to be used when Ruby's 'date'
50
- library had not been previously loaded.
51
- Code contributed by Tuomas Kareinen [tuomas]
52
- * Fix bug when traveling to a DateTime across a DST boundary that
53
- resulted in DateTime's being off by an hour.
54
- * Migrate argument parsing into Timecop::TimeStackItem to reduce the
55
- responsibility of the Timecop class.
56
-
57
- === 0.3.3 2009-10-30
58
-
59
- * Revoked due to regression.
60
-
61
- === 0.3.2 / 2009-10-24
62
-
63
- * Revoked due to regression.
64
-
65
- === 0.3.1 / 2009-09-30
66
-
67
- * Maintenance
68
- * DRY up the Timecop class internals.
69
-
70
- === 0.3.0 / 2009-09-20
71
-
72
- * API
73
- * Completely remove Timecop#unset_all (deprecated by Timecop#return in 0.2.0)
74
- * Return Time.now from #freeze, #travel and #return -- code contributed by Keith Bennett [keithrbennett]
75
-
76
- * Maintenance
77
- * Fix bug that left Time#mock_time set in some instances
78
- * Upped build dependency to jeweler ~> 1.2.1
79
- * Don't pollute top-level namespace with classes/constants
80
-
81
- * Documentation
82
- * Clearer examples in the README, better description in the gemspec
83
- * Improve RDoc
84
-
85
- === 0.2.1 / 2009-03-06
86
- * API Changes
87
-
88
- * Introduced a 5th style of arguments to be passed into #travel and #freeze. Now, if you pass in a single integer value,
89
- it will be interpreted as a relative offset in seconds from the current Time.now. Previously this was interpreted as
90
- only the year, similar to calling Time.local(2008) --> Jan. 1, 2008. This is no longer the case.
91
-
92
- * Documentation
93
-
94
- * Moved to Textile for the README.
95
-
96
- * Added documentation for the new feature, and fixed a few typos.
97
-
98
- === 0.2.0 / 2008-12-23
99
-
100
- * API Changes
101
-
102
- * Timecop#travel no longer freezes time. Rather, it computes the current offset between the new "now" and the real "now", and
103
- returns times as if Time had continued to move forward
104
-
105
- * Timecop#freeze now behaves exactly as the old Timecop#travel behaved. Unless you depended on the actual freezing of time
106
- (which I think would be rare), you should be able to continue to use #travel without worry.
107
-
108
- * Timecop#return is now exposed (previously Timecop#unset_all, but not well advertised). It will completely unmock time,
109
- and will probably be rarely used outside of the actual implementation of this library.
110
-
111
- * More Test Coverage
112
-
113
- * Tests now explicitly cover the cases when the Date and DateTime objects are not loaded, and ensures proper functionality
114
- in their absence and existence.
115
-
116
- * Still haven't done regression testing against anything other than a few version of 1.8.6 (including REE). We should
117
- probably try to get this tested on both 1.8.7 and 1.9.1.
118
-
119
- * Documentation
120
-
121
- * Fixed up a lot of the poorly-formatted rdoc syntax. The public API should now be properly published in the rdoc,
122
- and the internals are omitted.
123
-
124
- === 0.1.0 / 2008-11-09
125
-
126
- * Initial Feature Set
127
-
128
- * Temporarily (or permanently if you prefer) change the concept of Time.now, DateTime.now (if defined), and Date.today (if defined)
129
- * Timecop#travel api allows an argument to be passed in as one of: 1) Time instance, 2) DateTime instance, 3) Date instance,
130
- 4) individual arguments (year, month, day, hour, minute, second)
131
- * Nested calls to Timecop#travel are supported -- each block will maintain it's interpretation of now.
132
-