timecop 0.3.1 → 0.3.4

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.
@@ -1,3 +1,26 @@
1
+ === 0.3.4 / unreleased (RC2 released 2009-11-28)
2
+
3
+ * Maintenance
4
+ * Fix various timezone-related issues. Notably, when traveling to a DateTime
5
+ instance specified in a non-local timezone, convert provided DateTime
6
+ instance to a local instance and return that from DateTime.now.
7
+ Code contributed by Michaël Witrant [piglop]
8
+ * Fix bug that would not allow Timecop to be used when Ruby's 'date'
9
+ library had not been previously loaded.
10
+ Code contributed by Tuomas Kareinen [tuomas]
11
+ * Fix bug when traveling to a DateTime across a DST boundary that
12
+ resulted in DateTime's being off by an hour.
13
+ * Migrate argument parsing into Timecop::TimeStackItem to reduce the
14
+ responsibility of the Timecop class.
15
+
16
+ === 0.3.3 2009-10-30
17
+
18
+ * Revoked due to regression.
19
+
20
+ === 0.3.2 / 2009-10-24
21
+
22
+ * Revoked due to regression.
23
+
1
24
  === 0.3.1 / 2009-09-30
2
25
 
3
26
  * Maintenance
@@ -7,7 +30,7 @@
7
30
 
8
31
  * API
9
32
  * Completely remove Timecop#unset_all (deprecated by Timecop#return in 0.2.0)
10
- * Return Time.now from #freeze, #travel and #return -- code contributed by Keith Bennett (keithrbennett)
33
+ * Return Time.now from #freeze, #travel and #return -- code contributed by Keith Bennett [keithrbennett]
11
34
 
12
35
  * Maintenance
13
36
  * Fix bug that left Time#mock_time set in some instances
data/README.rdoc CHANGED
@@ -1,15 +1,21 @@
1
1
  = timecop
2
2
 
3
- * http://github.com/jtrupiano/timecop
3
+ * Source[http://github.com/jtrupiano/timecop]
4
+ * Documentation[http://johntrupiano.rubyforge.org/timecop]
4
5
 
5
6
  == DESCRIPTION
6
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
9
 
10
+ == INSTALL
11
+
12
+ gem install timecop
13
+
9
14
  == FEATURES
10
15
 
11
16
  * Freeze time to a specific point.
12
17
  * Travel back to a specific point in time, but allow time to continue moving forward from there.
18
+ * No dependencies, can be used with _any_ ruby project
13
19
  * Timecop api allows arguments to be passed into #freeze and #travel as one of the following:
14
20
  * Time instance
15
21
  * DateTime instance
@@ -54,16 +60,9 @@ in config/environments/test.rb
54
60
  sleep(10)
55
61
  new_time == Time.now # ==> false
56
62
 
57
- == DEPENDENCIES
58
-
59
- * None
60
-
61
- == INSTALL
62
-
63
- * sudo gem install timecop (latest stable version from rubyforge)
64
- * sudo gem install jtrupiano-timecop (HEAD of the repo from github)
65
-
66
63
  == REFERENCES
67
64
 
68
- * "0.2.0 release":http://blog.smartlogicsolutions.com/2008/12/24/timecop-2-released-freeze-and-rebase-time-ruby/
69
- * "0.1.0 release":http://blog.smartlogicsolutions.com/2008/11/19/timecop-freeze-time-in-ruby-for-better-testing/
65
+ * {0.3.4 release}[http://blog.smartlogicsolutions.com/2009/12/07/timecop-0-3-4-released/]
66
+ * {0.3.0 release}[http://blog.smartlogicsolutions.com/2009/09/20/timecop-0-3-0-released/]
67
+ * {0.2.0 release}[http://blog.smartlogicsolutions.com/2008/12/24/timecop-2-released-freeze-and-rebase-time-ruby/]
68
+ * {0.1.0 release}[http://blog.smartlogicsolutions.com/2008/11/19/timecop-freeze-time-in-ruby-for-better-testing/]
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'rake/rdoctask'
4
4
 
5
- gem 'jeweler', '~> 1.2.1'
5
+ gem 'jeweler', '~> 1.3.0'
6
6
 
7
7
  begin
8
8
  require 'jeweler'
@@ -16,6 +16,7 @@ begin
16
16
  s.authors = ["John Trupiano"]
17
17
  s.files = FileList["[A-Z]*", "{bin,lib,test}/**/*"]
18
18
  end
19
+ Jeweler::GemcutterTasks.new
19
20
  Jeweler::RubyforgeTasks.new do |rubyforge|
20
21
  rubyforge.doc_task = "rdoc"
21
22
  rubyforge.remote_doc_path = "timecop"
@@ -47,7 +48,7 @@ Rake::RDocTask.new do |rdoc|
47
48
  rdoc.options << '--line-numbers' << '--inline-source'
48
49
  rdoc.title = "timecop #{version}"
49
50
  rdoc.rdoc_files.include('README*')
50
- rdoc.rdoc_files.include('History.txt')
51
+ rdoc.rdoc_files.include('History.rdoc')
51
52
  rdoc.rdoc_files.include('lib/**/*.rb')
52
53
  end
53
54
 
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :minor: 3
3
- :patch: 1
2
+ :patch: 4
4
3
  :major: 0
4
+ :minor: 3
@@ -1,40 +1,10 @@
1
- #--
2
- # 1. Extensions to the Time, Date, and DateTime objects
3
- # 2. Allows us to "freeze" time in our Ruby applications.
4
- # 3. This is very useful when your app's functionality is dependent on time (e.g.
5
- # anything that might expire). This will allow us to alter the return value of
6
- # Date.today, Time.now, and DateTime.now, such that our application code _never_ has to change.
7
1
 
8
2
  class Time #:nodoc:
9
3
  class << self
10
- # Time we might be behaving as
11
- #attr_reader :mock_time
12
-
13
- @@mock_offset = nil
14
- @@mock_time = nil
15
-
4
+ # Time we are behaving as
16
5
  def mock_time
17
- if !@@mock_offset.nil?
18
- now_without_mock_time - @@mock_offset
19
- else
20
- @@mock_time
21
- end
22
- end
23
-
24
- # Set new time to pretend we are.
25
- def freeze_time(new_now)
26
- @@mock_time = new_now
27
- @@mock_offset = nil
28
- end
29
-
30
- def move_time(new_now)
31
- @@mock_offset = new_now.nil? ? nil : (now_without_mock_time - new_now)
32
- @@mock_time = nil
33
- end
34
-
35
- # Restores Time to system clock
36
- def unmock!
37
- move_time(nil)
6
+ mocked_time_stack_item = Timecop.top_stack_item
7
+ mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.time
38
8
  end
39
9
 
40
10
  # Alias the original now
@@ -50,15 +20,15 @@ class Time #:nodoc:
50
20
  end
51
21
  end
52
22
 
53
- if Object.const_defined?(:Date)
23
+ if Object.const_defined?(:Date) && Date.respond_to?(:today)
54
24
  class Date #:nodoc:
55
25
  class << self
26
+ # Date we are behaving as
56
27
  def mock_date
57
- now = Time.mock_time
58
- return nil if now.nil?
59
- Date.new(now.year, now.month, now.day)
28
+ mocked_time_stack_item = Timecop.top_stack_item
29
+ mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.date
60
30
  end
61
-
31
+
62
32
  # Alias the original today
63
33
  alias_method :today_without_mock_date, :today
64
34
 
@@ -73,18 +43,23 @@ if Object.const_defined?(:Date)
73
43
  end
74
44
  end
75
45
 
76
- if Object.const_defined?(:DateTime)
46
+ if Object.const_defined?(:DateTime) && DateTime.respond_to?(:now)
77
47
  class DateTime #:nodoc:
78
48
  class << self
49
+ # Time we are behaving as
79
50
  def mock_time
80
- t_now = Time.mock_time
81
- return nil if t_now.nil?
82
- DateTime.new(t_now.year, t_now.month, t_now.day, t_now.hour, t_now.min, t_now.sec)
51
+ mocked_time_stack_item = Timecop.top_stack_item
52
+ mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.datetime
83
53
  end
84
-
85
- # Alias the original now
86
- alias_method :now_without_mock_time, :now
87
-
54
+
55
+ # Fake alias :now_without_mock_time :now
56
+ # It appears that the DateTime library itself references Time.now
57
+ # for it's interpretation of now which caused
58
+ # DateTime.now_without_mock_time to incorrectly return the frozen time.
59
+ def now_without_mock_time
60
+ Time.now_without_mock_time.send :to_datetime
61
+ end
62
+
88
63
  # Define now_with_mock_time
89
64
  def now_with_mock_time
90
65
  mock_time || now_without_mock_time
@@ -0,0 +1,113 @@
1
+
2
+ 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
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 #:nodoc:
49
+ if travel_offset.nil?
50
+ @time
51
+ else
52
+ Time.now_without_mock_time + travel_offset
53
+ end
54
+ end
55
+
56
+ def date
57
+ time.send(:to_date)
58
+ end
59
+
60
+ def datetime
61
+ # DateTime doesn't know about DST, so let's remove its presence
62
+ our_offset = utc_offset + dst_adjustment
63
+ DateTime.new(year, month, day, hour, min, sec, utc_offset_to_rational(our_offset))
64
+ end
65
+
66
+ def dst_adjustment
67
+ @dst_adjustment
68
+ end
69
+
70
+ private
71
+ def rational_to_utc_offset(rational)
72
+ ((24.0 / rational.denominator) * rational.numerator) * (60 * 60)
73
+ end
74
+
75
+ def utc_offset_to_rational(utc_offset)
76
+ Rational(utc_offset, 24 * 60 * 60)
77
+ end
78
+
79
+ def parse_time(*args)
80
+ arg = args.shift
81
+ if arg.is_a?(Time)
82
+ arg.getlocal
83
+ elsif Object.const_defined?(:DateTime) && arg.is_a?(DateTime)
84
+ offset_difference = Time.now.utc_offset - rational_to_utc_offset(arg.offset)
85
+ Time.local(arg.year, arg.month, arg.day, arg.hour, arg.min, arg.sec) + offset_difference
86
+ elsif Object.const_defined?(:Date) && arg.is_a?(Date)
87
+ Time.local(arg.year, arg.month, arg.day, 0, 0, 0)
88
+ elsif args.empty? && arg.kind_of?(Integer)
89
+ Time.now + arg
90
+ else # we'll just assume it's a list of y/m/d/h/m/s
91
+ year = arg || 0
92
+ month = args.shift || 1
93
+ day = args.shift || 1
94
+ hour = args.shift || 0
95
+ minute = args.shift || 0
96
+ second = args.shift || 0
97
+ Time.local(year, month, day, hour, minute, second)
98
+ end
99
+ end
100
+
101
+ def compute_dst_adjustment
102
+ return 0 if !(time.dst? ^ Time.now.dst?)
103
+ return -1 * 60 * 60 if time.dst?
104
+ return 60 * 60
105
+ end
106
+
107
+ def compute_travel_offset
108
+ return nil if mock_type == :freeze
109
+ @time - Time.now_without_mock_time
110
+ end
111
+
112
+ end
113
+ end
@@ -1,6 +1,6 @@
1
1
  require 'singleton'
2
2
  require File.join(File.dirname(__FILE__), 'time_extensions')
3
- require File.join(File.dirname(__FILE__), 'stack_item')
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
@@ -58,7 +58,7 @@ class Timecop
58
58
  #
59
59
  # Returns the 'new' current Time.
60
60
  def self.travel(*args, &block)
61
- instance().send(:travel, :move, *args, &block)
61
+ instance().send(:travel, :travel, *args, &block)
62
62
  Time.now
63
63
  end
64
64
 
@@ -69,6 +69,10 @@ class Timecop
69
69
  instance().send(:unmock!)
70
70
  Time.now
71
71
  end
72
+
73
+ def self.top_stack_item #:nodoc:
74
+ instance().instance_variable_get(:@_stack).last
75
+ end
72
76
 
73
77
  protected
74
78
 
@@ -77,12 +81,7 @@ class Timecop
77
81
  end
78
82
 
79
83
  def travel(mock_type, *args, &block) #:nodoc:
80
- # parse the arguments, build our base time units
81
- year, month, day, hour, minute, second = parse_travel_args(*args)
82
-
83
- stack_item = StackItem.new(mock_type, year, month, day, hour, minute, second)
84
- # perform our action
85
- freeze_or_move(stack_item)
84
+ stack_item = TimeStackItem.new(mock_type, *args)
86
85
 
87
86
  # store this time traveling on our stack...
88
87
  @_stack << stack_item
@@ -93,61 +92,12 @@ class Timecop
93
92
  ensure
94
93
  # pull it off the stack...
95
94
  @_stack.pop
96
- if @_stack.size == 0
97
- # completely unmock if there's nothing to revert back to
98
- unmock!
99
- else
100
- # or reinstantiate the new the top of the stack (could be a :freeze or a :move)
101
- new_top = @_stack.last
102
- freeze_or_move(new_top)
103
- end
104
95
  end
105
96
  end
106
97
  end
107
98
 
108
99
  def unmock! #:nodoc:
109
100
  @_stack = []
110
- Time.unmock!
111
101
  end
112
102
 
113
- private
114
-
115
- def freeze_or_move(stack_item) #:nodoc:
116
- if stack_item.mock_type == :freeze
117
- Time.freeze_time(time_for_stack_item(stack_item))
118
- else
119
- Time.move_time(time_for_stack_item(stack_item))
120
- end
121
- end
122
-
123
- def time_for_stack_item(stack_item) #:nodoc:
124
- if Time.respond_to?(:zone) && !Time.zone.nil?
125
- # ActiveSupport loaded
126
- time = Time.zone.local(stack_item.year, stack_item.month, stack_item.day, stack_item.hour, stack_item.minute, stack_item.second)
127
- else
128
- # ActiveSupport not loaded
129
- time = Time.local(stack_item.year, stack_item.month, stack_item.day, stack_item.hour, stack_item.minute, stack_item.second)
130
- end
131
- end
132
-
133
- def parse_travel_args(*args) #:nodoc:
134
- arg = args.shift
135
- if arg.is_a?(Time) || (Object.const_defined?(:DateTime) && arg.is_a?(DateTime))
136
- year, month, day, hour, minute, second = arg.year, arg.month, arg.day, arg.hour, arg.min, arg.sec
137
- elsif Object.const_defined?(:Date) && arg.is_a?(Date)
138
- year, month, day, hour, minute, second = arg.year, arg.month, arg.day, 0, 0, 0
139
- #puts "#{year}-#{month}-#{day} #{hour}:#{minute}:#{second}"
140
- elsif args.empty? && arg.kind_of?(Integer)
141
- t = Time.now + arg
142
- year, month, day, hour, minute, second = t.year, t.month, t.day, t.hour, t.min, t.sec
143
- else # we'll just assume it's a list of y/m/h/d/m/s
144
- year = arg || 0
145
- month = args.shift || 1
146
- day = args.shift || 1
147
- hour = args.shift || 0
148
- minute = args.shift || 0
149
- second = args.shift || 0
150
- end
151
- return year, month, day, hour, minute, second
152
- end
153
103
  end
data/test/run_tests.sh CHANGED
@@ -1,10 +1,13 @@
1
1
  #!/bin/sh
2
2
 
3
- echo "\033[1;81m Running test_timecop_internals...\033[0m"
4
- ruby test_timecop_internals.rb || (echo "FAILED!!!!!!!!!!!!")
3
+ echo "\033[1;81m Running test_time_stack_item...\033[0m"
4
+ ruby test_time_stack_item.rb || (echo "FAILED!!!!!!!!!!!!")
5
5
 
6
6
  echo "\033[1;81m Running test_timecop_without_date...\033[0m"
7
7
  ruby test_timecop_without_date.rb || (echo "FAILED!!!!!!!!!!!!")
8
8
 
9
+ echo "\033[1;81m Running test_timecop_without_date_but_with_time...\033[0m"
10
+ ruby test_timecop_without_date_but_with_time.rb || (echo "FAILED!!!!!!!!!!!!")
11
+
9
12
  echo "\033[1;81m Running test_timecop...\033[0m"
10
13
  ruby test_timecop.rb || (echo "FAILED!!!!!!!!!!!!")
@@ -0,0 +1,46 @@
1
+ require 'test/unit'
2
+
3
+ class Test::Unit::TestCase
4
+ private
5
+ # Tests to see that two times are within the given distance,
6
+ # in seconds, from each other.
7
+ def times_effectively_equal(time1, time2, seconds_interval = 1)
8
+ (time1 - time2).abs <= seconds_interval
9
+ end
10
+
11
+ def assert_times_effectively_equal(time1, time2, seconds_interval = 1, msg = nil)
12
+ assert times_effectively_equal(time1, time2, seconds_interval), "#{msg}: time1 = #{time1.to_s}, time2 = #{time2.to_s}"
13
+ end
14
+
15
+ def assert_times_effectively_not_equal(time1, time2, seconds_interval = 1, msg = nil)
16
+ assert !times_effectively_equal(time1, time2, seconds_interval), "#{msg}: time1 = #{time1.to_s}, time2 = #{time2.to_s}"
17
+ end
18
+
19
+ def local_offset
20
+ DateTime.now_without_mock_time.offset
21
+ end
22
+
23
+ TIMEZONES = ["Europe/Paris", "UTC", "EDT"]
24
+
25
+ def each_timezone
26
+ old_tz = ENV["TZ"]
27
+
28
+ begin
29
+ TIMEZONES.each do |timezone|
30
+ ENV["TZ"] = timezone
31
+ yield
32
+ end
33
+ ensure
34
+ ENV["TZ"] = old_tz
35
+ end
36
+ end
37
+
38
+ def a_time_stack_item
39
+ Timecop::TimeStackItem.new(:freeze, 2008, 1, 1, 0, 0, 0)
40
+ end
41
+
42
+ def assert_date_times_equal(dt1, dt2)
43
+ assert_equal dt1, dt2, "Failed for timezone: #{ENV['TZ']}: #{dt1.to_s} not equal to #{dt2.to_s}"
44
+ end
45
+
46
+ end
@@ -0,0 +1,167 @@
1
+ require 'date'
2
+ require 'test_helper'
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
4
+
5
+ class TestTimeStackItem < Test::Unit::TestCase
6
+
7
+ def teardown
8
+ Timecop.return
9
+ end
10
+
11
+ def test_new_with_time
12
+ t = Time.now
13
+ y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
14
+ stack_item = Timecop::TimeStackItem.new(:freeze, t)
15
+ assert_equal y, stack_item.year
16
+ assert_equal m, stack_item.month
17
+ assert_equal d, stack_item.day
18
+ assert_equal h, stack_item.hour
19
+ assert_equal min, stack_item.min
20
+ assert_equal s, stack_item.sec
21
+ end
22
+
23
+ def test_new_with_datetime_now
24
+ t = DateTime.now
25
+ y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
26
+ stack_item = Timecop::TimeStackItem.new(:freeze, t)
27
+ assert_equal y, stack_item.year
28
+ assert_equal m, stack_item.month
29
+ assert_equal d, stack_item.day
30
+ assert_equal h, stack_item.hour
31
+ assert_equal min, stack_item.min
32
+ assert_equal s, stack_item.sec
33
+ end
34
+
35
+ def test_new_with_datetime_in_different_timezone
36
+ each_timezone do
37
+ t = DateTime.parse("2009-10-11 00:38:00 +0200")
38
+ stack_item = Timecop::TimeStackItem.new(:freeze, t)
39
+ assert_date_times_equal(t, stack_item.datetime)
40
+ end
41
+ end
42
+
43
+ def test_new_with_date
44
+ date = Date.today
45
+ y, m, d, h, min, s = date.year, date.month, date.day, 0, 0, 0
46
+ stack_item = Timecop::TimeStackItem.new(:freeze, date)
47
+ assert_equal y, stack_item.year
48
+ assert_equal m, stack_item.month
49
+ assert_equal d, stack_item.day
50
+ assert_equal h, stack_item.hour
51
+ assert_equal min, stack_item.min
52
+ assert_equal s, stack_item.sec
53
+ end
54
+
55
+ # Due to the nature of this test (calling Time.now once in this test and
56
+ # once in #new), this test may fail when two subsequent calls
57
+ # to Time.now return a different second.
58
+ def test_new_with_integer
59
+ t = Time.now
60
+ y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
61
+ stack_item = Timecop::TimeStackItem.new(:freeze, 0)
62
+ assert_equal y, stack_item.year
63
+ assert_equal m, stack_item.month
64
+ assert_equal d, stack_item.day
65
+ assert_equal h, stack_item.hour
66
+ assert_equal min, stack_item.min
67
+ assert_equal s, stack_item.sec
68
+ end
69
+
70
+ def test_new_with_individual_arguments
71
+ y, m, d, h, min, s = 2008, 10, 10, 10, 10, 10
72
+ stack_item = Timecop::TimeStackItem.new(:freeze, y, m, d, h, min, s)
73
+ assert_equal y, stack_item.year
74
+ assert_equal m, stack_item.month
75
+ assert_equal d, stack_item.day
76
+ assert_equal h, stack_item.hour
77
+ assert_equal min, stack_item.min
78
+ assert_equal s, stack_item.sec
79
+ end
80
+
81
+ def test_rational_to_utc_offset
82
+ assert_equal -14400, a_time_stack_item.send(:rational_to_utc_offset, Rational(-1, 6))
83
+ assert_equal -18000, a_time_stack_item.send(:rational_to_utc_offset, Rational(-5, 24))
84
+ assert_equal 0, a_time_stack_item.send(:rational_to_utc_offset, Rational(0, 1))
85
+ assert_equal 3600, a_time_stack_item.send(:rational_to_utc_offset, Rational(1, 24))
86
+ end
87
+
88
+ def test_utc_offset_to_rational
89
+ assert_equal Rational(-1, 6), a_time_stack_item.send(:utc_offset_to_rational, -14400)
90
+ assert_equal Rational(-5, 24), a_time_stack_item.send(:utc_offset_to_rational, -18000)
91
+ assert_equal Rational(0, 1), a_time_stack_item.send(:utc_offset_to_rational, 0)
92
+ assert_equal Rational(1, 24), a_time_stack_item.send(:utc_offset_to_rational, 3600)
93
+ end
94
+
95
+ # Ensure DST adjustment is calculated properly for DateTime's
96
+ def test_compute_dst_adjustment_for_dst_to_dst
97
+ Timecop.freeze(DateTime.parse("2009-10-1 00:38:00 -0400"))
98
+ t = DateTime.parse("2009-10-11 00:00:00 -0400")
99
+ tsi = Timecop::TimeStackItem.new(:freeze, t)
100
+ assert Time.now.dst?, "precondition"
101
+ assert tsi.time.dst?, "precondition"
102
+ assert_equal 0, tsi.send(:dst_adjustment)
103
+ end
104
+
105
+ def test_compute_dst_adjustment_for_non_dst_to_non_dst
106
+ Timecop.freeze(DateTime.parse("2009-12-1 00:38:00 -0400"))
107
+ t = DateTime.parse("2009-12-11 00:00:00 -0400")
108
+ tsi = Timecop::TimeStackItem.new(:freeze, t)
109
+ assert !Time.now.dst?, "precondition"
110
+ assert !tsi.time.dst?, "precondition"
111
+ assert_equal 0, tsi.send(:dst_adjustment)
112
+ end
113
+
114
+ def test_compute_dst_adjustment_for_dst_to_non_dst
115
+ Timecop.freeze(DateTime.parse("2009-10-1 00:38:00 -0400"))
116
+ t = DateTime.parse("2009-12-11 00:00:00 -0400")
117
+ tsi = Timecop::TimeStackItem.new(:freeze, t)
118
+ assert Time.now.dst?, "precondition"
119
+ assert !tsi.time.dst?, "precondition"
120
+ assert_equal 60 * 60, tsi.send(:dst_adjustment)
121
+ end
122
+
123
+ def test_compute_dst_adjustment_for_non_dst_to_dst
124
+ Timecop.freeze(DateTime.parse("2009-12-1 00:38:00 -0400"))
125
+ t = DateTime.parse("2009-10-11 00:00:00 -0400")
126
+ tsi = Timecop::TimeStackItem.new(:freeze, t)
127
+ assert !Time.now.dst?, "precondition"
128
+ assert tsi.time.dst?, "precondition"
129
+ assert_equal -1 * 60 * 60, tsi.send(:dst_adjustment)
130
+ end
131
+
132
+ # Ensure DateTime's handle changing DST properly
133
+ def test_datetime_for_dst_to_non_dst
134
+ Timecop.freeze(DateTime.parse("2009-12-1 00:38:00 -0500"))
135
+ t = DateTime.parse("2009-10-11 00:00:00 -0400")
136
+ tsi = Timecop::TimeStackItem.new(:freeze, t)
137
+ assert_date_times_equal t, tsi.datetime
138
+ # verify Date also 'moves backward' an hour to change the day
139
+ assert_equal Date.new(2009, 10, 10), tsi.date
140
+ end
141
+
142
+ def test_datetime_for_non_dst_to_dst
143
+ Timecop.freeze(DateTime.parse("2009-10-11 00:00:00 -0400"))
144
+ t = DateTime.parse("2009-11-30 23:38:00 -0500")
145
+ tsi = Timecop::TimeStackItem.new(:freeze, t)
146
+ assert_date_times_equal t, tsi.datetime
147
+ # verify Date also 'moves forward' an hour to change the day
148
+ assert_equal Date.new(2009, 12, 1), tsi.date
149
+ end
150
+
151
+ # Ensure @travel_offset is set properly
152
+ def test_set_travel_offset_for_travel
153
+ # Timecop.freeze(2009, 10, 1, 0, 0, 0)
154
+ t_now = Time.now
155
+ t = Time.local(2009, 10, 1, 0, 0, 30)
156
+ expected_offset = t - t_now
157
+ tsi = Timecop::TimeStackItem.new(:travel, t)
158
+ assert_times_effectively_equal expected_offset, tsi.send(:travel_offset), 1, "Offset not calculated correctly"
159
+ end
160
+
161
+ def test_set_travel_offset_for_freeze
162
+ Timecop.freeze(2009, 10, 1, 0, 0, 0)
163
+ t = Time.local(2009, 10, 1, 0, 0, 30)
164
+ tsi = Timecop::TimeStackItem.new(:freeze, t)
165
+ assert_equal nil, tsi.send(:travel_offset)
166
+ end
167
+ end
data/test/test_timecop.rb CHANGED
@@ -1,6 +1,5 @@
1
-
2
1
  require 'date'
3
- require 'test/unit'
2
+ require 'test_helper'
4
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
5
4
 
6
5
  class TestTimecop < Test::Unit::TestCase
@@ -67,24 +66,62 @@ class TestTimecop < Test::Unit::TestCase
67
66
  t = Time.local(2008, 10, 10, 10, 10, 10)
68
67
  Timecop.freeze(t) do
69
68
  assert_equal t, Time.now
70
- assert_equal DateTime.new(2008, 10, 10, 10, 10, 10), DateTime.now
69
+ assert_date_times_equal DateTime.new(2008, 10, 10, 10, 10, 10, local_offset), DateTime.now
71
70
  assert_equal Date.new(2008, 10, 10), Date.today
72
71
  end
73
72
  assert_not_equal t, Time.now
74
- assert_not_equal DateTime.new(2008, 10, 10, 10, 10, 10), DateTime.now
73
+ assert_not_equal DateTime.new(2008, 10, 10, 10, 10, 10, local_offset), DateTime.now
75
74
  assert_not_equal Date.new(2008, 10, 10), Date.today
76
75
  end
77
76
 
78
- def test_freeze_with_datetime_instance_works_as_expected
79
- t = DateTime.new(2008, 10, 10, 10, 10, 10)
80
- Timecop.freeze(t) do
81
- assert_equal t, DateTime.now
82
- assert_equal Time.local(2008, 10, 10, 10, 10, 10), Time.now
83
- assert_equal Date.new(2008, 10, 10), Date.today
77
+ def test_freeze_with_datetime_on_specific_timezone_during_dst
78
+ each_timezone do
79
+ # Start from a time that is subject to DST
80
+ Timecop.freeze(2009, 9, 1)
81
+ # Travel to a DateTime that is also in DST
82
+ t = DateTime.parse("2009-10-11 00:38:00 +0200")
83
+ Timecop.freeze(t) do
84
+ assert_date_times_equal t, DateTime.now
85
+ end
86
+ # Undo the original freeze
87
+ Timecop.return
84
88
  end
85
- assert_not_equal t, DateTime.now
86
- assert_not_equal Time.local(2008, 10, 10, 10, 10, 10), Time.now
87
- assert_not_equal Date.new(2008, 10, 10), Date.today
89
+ end
90
+
91
+ def test_freeze_with_datetime_on_specific_timezone_not_during_dst
92
+ each_timezone do
93
+ # Start from a time that is not subject to DST
94
+ Timecop.freeze(2009, 12, 1)
95
+ # Travel to a time that is also not in DST
96
+ t = DateTime.parse("2009-12-11 00:38:00 +0100")
97
+ Timecop.freeze(t) do
98
+ assert_date_times_equal t, DateTime.now
99
+ end
100
+ end
101
+ end
102
+
103
+ def test_freeze_with_datetime_from_a_non_dst_time_to_a_dst_time
104
+ each_timezone do
105
+ # Start from a time that is not subject to DST
106
+ Timecop.freeze(DateTime.parse("2009-12-1 00:00:00 +0100"))
107
+ # Travel back to a time in DST
108
+ t = DateTime.parse("2009-10-11 00:38:00 +0200")
109
+ Timecop.freeze(t) do
110
+ assert_date_times_equal t, DateTime.now
111
+ end
112
+ end
113
+ end
114
+
115
+ def test_freeze_with_datetime_from_a_dst_time_to_a_non_dst_time
116
+ each_timezone do
117
+ # Start from a time that is not subject to DST
118
+ Timecop.freeze(DateTime.parse("2009-10-11 00:00:00 +0200"))
119
+ # Travel back to a time in DST
120
+ t = DateTime.parse("2009-12-1 00:38:00 +0100")
121
+ Timecop.freeze(t) do
122
+ assert_date_times_equal t, DateTime.now
123
+ end
124
+ end
88
125
  end
89
126
 
90
127
  def test_freeze_with_date_instance_works_as_expected
@@ -92,18 +129,18 @@ class TestTimecop < Test::Unit::TestCase
92
129
  Timecop.freeze(d) do
93
130
  assert_equal d, Date.today
94
131
  assert_equal Time.local(2008, 10, 10, 0, 0, 0), Time.now
95
- assert_equal DateTime.new(2008, 10, 10, 0, 0, 0), DateTime.now
132
+ assert_date_times_equal DateTime.new(2008, 10, 10, 0, 0, 0, local_offset), DateTime.now
96
133
  end
97
134
  assert_not_equal d, Date.today
98
135
  assert_not_equal Time.local(2008, 10, 10, 0, 0, 0), Time.now
99
- assert_not_equal DateTime.new(2008, 10, 10, 0, 0, 0), DateTime.now
136
+ assert_not_equal DateTime.new(2008, 10, 10, 0, 0, 0, local_offset), DateTime.now
100
137
  end
101
138
 
102
139
  def test_freeze_with_integer_instance_works_as_expected
103
140
  t = Time.local(2008, 10, 10, 10, 10, 10)
104
141
  Timecop.freeze(t) do
105
142
  assert_equal t, Time.now
106
- assert_equal DateTime.new(2008, 10, 10, 10, 10, 10), DateTime.now
143
+ assert_date_times_equal DateTime.new(2008, 10, 10, 10, 10, 10, local_offset), DateTime.now
107
144
  assert_equal Date.new(2008, 10, 10), Date.today
108
145
  Timecop.freeze(10) do
109
146
  assert_equal t + 10, Time.now
@@ -147,25 +184,44 @@ class TestTimecop < Test::Unit::TestCase
147
184
  t = Time.local(2008, 10, 10, 10, 10, 10)
148
185
  now = Time.now
149
186
  Timecop.travel(t) do
150
- #assert Time.now < now, "If we had failed to freeze, time would have proceeded, which is what appears to have happened."
151
- assert Time.now - t < 2000, "Looks like we failed to actually travel time" # 2 seconds
152
- new_t = Time.now
153
- #sleep(10)
154
- assert_not_equal new_t, Time.now
187
+ new_now = Time.now
188
+ assert_times_effectively_equal(new_now, t, 1, "Looks like we failed to actually travel time")
189
+ sleep(0.25)
190
+ assert_times_effectively_not_equal new_now, Time.now, 0.25, "Looks like time is not moving"
191
+ end
192
+ end
193
+
194
+ def test_mocked_date_time_now_is_local
195
+ each_timezone do
196
+ t = DateTime.parse("2009-10-11 00:38:00 +0200")
197
+ Timecop.freeze(t) do
198
+ assert_equal local_offset, DateTime.now.offset, "Failed for timezone: #{ENV['TZ']}"
199
+ end
155
200
  end
156
201
  end
157
202
 
158
- def test_recursive_rebasing_maintains_each_context
203
+ def test_freeze_with_utc_time
204
+ each_timezone do
205
+ t = Time.utc(2008, 10, 10, 10, 10, 10)
206
+ local = t.getlocal
207
+ Timecop.freeze(t) do
208
+ assert_equal local, Time.now, "Failed for timezone: #{ENV['TZ']}"
209
+ end
210
+ end
211
+ end
212
+
213
+ def test_recursive_travel_maintains_each_context
159
214
  t = Time.local(2008, 10, 10, 10, 10, 10)
160
215
  Timecop.travel(2008, 10, 10, 10, 10, 10) do
161
216
  assert((t - Time.now).abs < 50, "Failed to travel time.")
162
217
  t2 = Time.local(2008, 9, 9, 9, 9, 9)
163
218
  Timecop.travel(2008, 9, 9, 9, 9, 9) do
164
- assert((t2 - Time.now) < 50, "Failed to travel time.")
165
- assert((t - Time.now) > 1000, "Failed to travel time.")
219
+ assert_times_effectively_equal(t2, Time.now, 1, "Failed to travel time.")
220
+ assert_times_effectively_not_equal(t, Time.now, 1000, "Failed to travel time.")
166
221
  end
167
- assert((t - Time.now).abs < 2000, "Failed to restore previously-traveled time.")
222
+ assert_times_effectively_equal(t, Time.now, 2, "Failed to restore previously-traveled time.")
168
223
  end
224
+ assert_nil Time.send(:mock_time)
169
225
  end
170
226
 
171
227
  def test_recursive_travel_then_freeze
@@ -176,8 +232,9 @@ class TestTimecop < Test::Unit::TestCase
176
232
  Timecop.freeze(2008, 9, 9, 9, 9, 9) do
177
233
  assert_equal t2, Time.now
178
234
  end
179
- assert((t - Time.now).abs < 2000, "Failed to restore previously-traveled time.")
235
+ assert_times_effectively_equal(t, Time.now, 2, "Failed to restore previously-traveled time.")
180
236
  end
237
+ assert_nil Time.send(:mock_time)
181
238
  end
182
239
 
183
240
  def test_recursive_freeze_then_travel
@@ -186,11 +243,12 @@ class TestTimecop < Test::Unit::TestCase
186
243
  assert_equal t, Time.now
187
244
  t2 = Time.local(2008, 9, 9, 9, 9, 9)
188
245
  Timecop.travel(t2) do
189
- assert((t2 - Time.now) < 50, "Failed to travel time.")
190
- assert((t - Time.now) > 1000, "Failed to travel time.")
246
+ assert_times_effectively_equal(t2, Time.now, 1, "Failed to travel time.")
247
+ assert_times_effectively_not_equal(t, Time.now, 1000, "Failed to travel time.")
191
248
  end
192
249
  assert_equal t, Time.now
193
250
  end
251
+ assert_nil Time.send(:mock_time)
194
252
  end
195
253
 
196
254
  def test_return_values_are_Time_instances
@@ -218,13 +276,4 @@ class TestTimecop < Test::Unit::TestCase
218
276
  assert times_effectively_equal(t_real, t_return)
219
277
  end
220
278
 
221
-
222
- private
223
-
224
- # Tests to see that two times are within the given distance,
225
- # in seconds, from each other.
226
- def times_effectively_equal(time1, time2, seconds_interval = 1)
227
- (time1.to_i - time2.to_i).abs <= seconds_interval
228
- end
229
-
230
279
  end
@@ -1,8 +1,8 @@
1
1
 
2
- require 'test/unit'
2
+ require 'test_helper'
3
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
4
4
 
5
- class TestTimecopWithouDate < Test::Unit::TestCase
5
+ class TestTimecopWithoutDate < Test::Unit::TestCase
6
6
 
7
7
  def setup
8
8
  assert !Object.const_defined?(:Date)
@@ -56,10 +56,10 @@ class TestTimecopWithouDate < Test::Unit::TestCase
56
56
  t = Time.local(2008, 10, 10, 10, 10, 10)
57
57
  now = Time.now
58
58
  Timecop.freeze(t) do
59
- #assert Time.now < now, "If we had failed to freeze, time would have proceeded, which is what appears to have happened."
59
+ sleep(0.25)
60
+ assert Time.now < now, "If we had failed to freeze, time would have proceeded, which is what appears to have happened."
60
61
  new_t = Time.now
61
62
  assert_equal t, new_t, "Failed to change move time." # 2 seconds
62
- #sleep(10)
63
63
  assert_equal new_t, Time.now
64
64
  end
65
65
  end
@@ -68,24 +68,23 @@ class TestTimecopWithouDate < Test::Unit::TestCase
68
68
  t = Time.local(2008, 10, 10, 10, 10, 10)
69
69
  now = Time.now
70
70
  Timecop.travel(t) do
71
- #assert Time.now < now, "If we had failed to freeze, time would have proceeded, which is what appears to have happened."
72
- assert Time.now - t < 2000, "Looks like we failed to actually travel time" # 2 seconds
73
- new_t = Time.now
74
- #sleep(10)
75
- assert_not_equal new_t, Time.now
71
+ new_now = Time.now
72
+ assert_times_effectively_equal new_now, t, 1, "Looks like we failed to actually travel time" # 0.1 seconds
73
+ sleep(0.25)
74
+ assert_times_effectively_not_equal new_now, Time.now, 0.25, "Looks like time is not moving"
76
75
  end
77
76
  end
78
77
 
79
- def test_recursive_rebasing_maintains_each_context
78
+ def test_recursive_travel_maintains_each_context
80
79
  t = Time.local(2008, 10, 10, 10, 10, 10)
81
80
  Timecop.travel(2008, 10, 10, 10, 10, 10) do
82
81
  assert((t - Time.now).abs < 50, "Failed to travel time.")
83
82
  t2 = Time.local(2008, 9, 9, 9, 9, 9)
84
83
  Timecop.travel(2008, 9, 9, 9, 9, 9) do
85
- assert((t2 - Time.now) < 50, "Failed to travel time.")
86
- assert((t - Time.now) > 1000, "Failed to travel time.")
84
+ assert_times_effectively_equal(t2, Time.now, 1, "Failed to travel time.")
85
+ assert_times_effectively_not_equal(t, Time.now, 1000, "Failed to travel time.")
87
86
  end
88
- assert((t - Time.now).abs < 2000, "Failed to restore previously-traveled time.")
87
+ assert_times_effectively_equal(t, Time.now, 2, "Failed to restore previously-traveled time.")
89
88
  end
90
89
  assert_nil Time.send(:mock_time)
91
90
  end
@@ -98,7 +97,7 @@ class TestTimecopWithouDate < Test::Unit::TestCase
98
97
  Timecop.freeze(2008, 9, 9, 9, 9, 9) do
99
98
  assert_equal t2, Time.now
100
99
  end
101
- assert((t - Time.now).abs < 2000, "Failed to restore previously-traveled time.")
100
+ assert_times_effectively_equal(t, Time.now, 2, "Failed to restore previously-traveled time.")
102
101
  end
103
102
  assert_nil Time.send(:mock_time)
104
103
  end
@@ -109,8 +108,8 @@ class TestTimecopWithouDate < Test::Unit::TestCase
109
108
  assert_equal t, Time.now
110
109
  t2 = Time.local(2008, 9, 9, 9, 9, 9)
111
110
  Timecop.travel(t2) do
112
- assert((t2 - Time.now) < 50, "Failed to travel time.")
113
- assert((t - Time.now) > 1000, "Failed to travel time.")
111
+ assert_times_effectively_equal(t2, Time.now, 1, "Failed to travel time.")
112
+ assert_times_effectively_not_equal(t, Time.now, 1000, "Failed to travel time.")
114
113
  end
115
114
  assert_equal t, Time.now
116
115
  end
@@ -0,0 +1,12 @@
1
+ require "test/unit"
2
+
3
+ class TestTimecopWithoutDateButWithTime < Test::Unit::TestCase
4
+ TIMECOP_LIB = File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
5
+
6
+ def test_loads_properly_when_time_is_required_instead_of_date
7
+ assert_nothing_raised do
8
+ require "time"
9
+ require TIMECOP_LIB
10
+ end
11
+ end
12
+ 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.3.1
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Trupiano
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-09-30 00:00:00 -04:00
12
+ date: 2009-12-07 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -23,19 +23,21 @@ extra_rdoc_files:
23
23
  - LICENSE
24
24
  - README.rdoc
25
25
  files:
26
- - History.txt
26
+ - History.rdoc
27
27
  - LICENSE
28
28
  - README.rdoc
29
29
  - Rakefile
30
30
  - VERSION.yml
31
31
  - lib/timecop.rb
32
- - lib/timecop/stack_item.rb
33
32
  - lib/timecop/time_extensions.rb
33
+ - lib/timecop/time_stack_item.rb
34
34
  - lib/timecop/timecop.rb
35
35
  - test/run_tests.sh
36
+ - test/test_helper.rb
37
+ - test/test_time_stack_item.rb
36
38
  - test/test_timecop.rb
37
- - test/test_timecop_internals.rb
38
39
  - test/test_timecop_without_date.rb
40
+ - test/test_timecop_without_date_but_with_time.rb
39
41
  has_rdoc: true
40
42
  homepage: http://github.com/jtrupiano/timecop
41
43
  licenses: []
@@ -65,6 +67,8 @@ signing_key:
65
67
  specification_version: 3
66
68
  summary: 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.
67
69
  test_files:
70
+ - test/test_helper.rb
71
+ - test/test_time_stack_item.rb
68
72
  - test/test_timecop.rb
69
- - test/test_timecop_internals.rb
70
73
  - test/test_timecop_without_date.rb
74
+ - test/test_timecop_without_date_but_with_time.rb
@@ -1,12 +0,0 @@
1
-
2
- class Timecop
3
- # Simply 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 StackItem #:nodoc:
6
-
7
- attr_reader :mock_type, :year, :month, :day, :hour, :minute, :second
8
- def initialize(mock_type, year, month, day, hour, minute, second)
9
- @mock_type, @year, @month, @day, @hour, @minute, @second = mock_type, year, month, day, hour, minute, second
10
- end
11
- end
12
- end
@@ -1,69 +0,0 @@
1
-
2
- require 'date'
3
- require 'test/unit'
4
- require File.join(File.dirname(__FILE__), '..', 'lib', 'timecop')
5
-
6
- class TestTimecopInternals < Test::Unit::TestCase
7
-
8
- def test_parse_travel_args_with_time
9
- t = Time.now
10
- y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
11
- ty, tm, td, th, tmin, ts = Timecop.instance().send(:parse_travel_args, t)
12
- assert_equal y, ty
13
- assert_equal m, tm
14
- assert_equal d, td
15
- assert_equal h, th
16
- assert_equal min, tmin
17
- assert_equal s, ts
18
- end
19
-
20
- def test_parse_travel_args_with_datetime
21
- t = DateTime.now
22
- y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
23
- ty, tm, td, th, tmin, ts = Timecop.instance().send(:parse_travel_args, t)
24
- assert_equal y, ty
25
- assert_equal m, tm
26
- assert_equal d, td
27
- assert_equal h, th
28
- assert_equal min, tmin
29
- assert_equal s, ts
30
- end
31
-
32
- def test_parse_travel_args_with_date
33
- date = Date.today
34
- y, m, d, h, min, s = date.year, date.month, date.day, 0, 0, 0
35
- ty, tm, td, th, tmin, ts = Timecop.instance().send(:parse_travel_args, date)
36
- assert_equal y, ty
37
- assert_equal m, tm
38
- assert_equal d, td
39
- assert_equal h, th
40
- assert_equal min, tmin
41
- assert_equal s, ts
42
- end
43
-
44
- # Due to the nature of this test (calling Time.now once in this test and
45
- # once in #parse_travel_args), this test may fail when two subsequent calls
46
- # to Time.now return a different second.
47
- def test_parse_travel_args_with_integer
48
- t = Time.now
49
- y, m, d, h, min, s = t.year, t.month, t.day, t.hour, t.min, t.sec
50
- ty, tm, td, th, tmin, ts = Timecop.instance().send(:parse_travel_args, 0)
51
- assert_equal y, ty
52
- assert_equal m, tm
53
- assert_equal d, td
54
- assert_equal h, th
55
- assert_equal min, tmin
56
- assert_equal s, ts
57
- end
58
-
59
- def test_parse_travel_args_with_individual_arguments
60
- y, m, d, h, min, s = 2008, 10, 10, 10, 10, 10
61
- ty, tm, td, th, tmin, ts = Timecop.instance().send(:parse_travel_args, y, m, d, h, min, s)
62
- assert_equal y, ty
63
- assert_equal m, tm
64
- assert_equal d, td
65
- assert_equal h, th
66
- assert_equal min, tmin
67
- assert_equal s, ts
68
- end
69
- end