From 274e0ff53de74daa3bdf52dab76838b5973bbcc6 Mon Sep 17 00:00:00 2001 From: Nick Elser Date: Mon, 1 Jun 2015 20:00:45 -0700 Subject: [PATCH] add some tests for time parsing, fix some time parsing bugs --- CHANGELOG.md | 5 ++ lib/zhong/at.rb | 37 ++++++++--- lib/zhong/every.rb | 2 +- lib/zhong/version.rb | 2 +- test/minitest_helper.rb | 4 -- test/test_helper.rb | 11 +++ test/zhong_test.rb | 144 +++++++++++++++++++++++++++++++++++++++- zhong.gemspec | 2 +- 8 files changed, 189 insertions(+), 18 deletions(-) delete mode 100644 test/minitest_helper.rb create mode 100644 test/test_helper.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 94ceead..e1452e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.4 + +- Fix several bugs related to time parsing. +- In a totally unrelated change, add some tests around time parsing. + ## 0.1.3 - Fix several memory leaks. diff --git a/lib/zhong/at.rb b/lib/zhong/at.rb index b11eebe..94112df 100644 --- a/lib/zhong/at.rb +++ b/lib/zhong/at.rb @@ -17,22 +17,35 @@ class FailedToParse < StandardError; end attr_accessor :minute, :hour, :wday - def initialize(minute: nil, hour: nil, wday: nil, grace: 0.minutes) + def initialize(minute: nil, hour: nil, wday: nil, grace: 0.seconds) @minute = minute @hour = hour @wday = wday @grace = grace + + fail ArgumentError unless valid? end def next_at(time = Time.now) at_time = @wday.nil? ? time.dup : (time + (@wday - time.wday).days) - at_time = at_time.change(min: @minute) - at_time = at_time.change(hour: @hour) if @hour + at_time = if !@minute.nil? && !@hour.nil? + at_time.change(hour: @hour, min: @minute) + elsif !@minute.nil? + at_time.change(min: @minute) + elsif !@hour.nil? && @hour != time.hour + at_time.change(hour: @hour) + else + at_time.change(sec: 0) + end - if at_time < @grace.ago + if at_time < (time.change(sec: 0) - @grace) if @wday.nil? - at_time += 1.day + if @hour.nil? + at_time += 1.hour + else + at_time += 1.day + end else at_time += 1.week end @@ -41,7 +54,13 @@ def next_at(time = Time.now) end end - def self.parse(at, grace: 0) + private def valid? + (@minute.nil? || (0..59).cover?(@minute)) && + (@hour.nil? || (0..23).cover?(@hour)) && + (@wday.nil? || (0..6).cover?(@wday)) + end + + def self.parse(at, grace: 0.seconds) return unless at # TODO: refactor this mess @@ -51,7 +70,7 @@ def self.parse(at, grace: 0) case at when /\A([[:alpha:]]+)\s+(.*)\z/ - wday = WDAYS[$1] + wday = WDAYS[$1.downcase] if wday parsed_time = parse($2, grace: grace) @@ -65,12 +84,12 @@ def self.parse(at, grace: 0) when /\A\*{1,2}:(\d\d)\z/ new(minute: $1.to_i, grace: grace) when /\A(\d{1,2}):\*{1,2}\z/ - new(hour: $1, grace: grace) + new(hour: $1.to_i, grace: grace) else fail FailedToParse, at end rescue ArgumentError - throw FailedToParse, at + fail FailedToParse, at end end diff --git a/lib/zhong/every.rb b/lib/zhong/every.rb index f05ced7..6d9c835 100644 --- a/lib/zhong/every.rb +++ b/lib/zhong/every.rb @@ -8,7 +8,7 @@ class FailedToParse < StandardError; end month: 1.month, semiannual: 6.months, # enterprise! year: 1.year, - decade: 10.year + decade: 10.years }.freeze def initialize(period) diff --git a/lib/zhong/version.rb b/lib/zhong/version.rb index 0c30104..e9d6f7a 100644 --- a/lib/zhong/version.rb +++ b/lib/zhong/version.rb @@ -1,3 +1,3 @@ module Zhong - VERSION = "0.1.3" + VERSION = "0.1.4" end diff --git a/test/minitest_helper.rb b/test/minitest_helper.rb deleted file mode 100644 index 15b0972..0000000 --- a/test/minitest_helper.rb +++ /dev/null @@ -1,4 +0,0 @@ -$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) -require 'zhong' - -require 'minitest/autorun' diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..3e8be10 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,11 @@ +$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) + +if ENV["CODECLIMATE_REPO_TOKEN"] + require "codeclimate-test-reporter" + CodeClimate::TestReporter.start +end + +require "zhong" +require "minitest/autorun" + +ENV["ZHONG_TEST"] = "true" diff --git a/test/zhong_test.rb b/test/zhong_test.rb index 95b8d00..5a879e3 100644 --- a/test/zhong_test.rb +++ b/test/zhong_test.rb @@ -1,6 +1,146 @@ -require 'minitest_helper' +require "test_helper" -class TestZhong < Minitest::Test +# Many At tests lifted from Clockwork (thanks Clockwork!) +class TestAt < Minitest::Test + def time_in_day(hour, minute, day = 0, sec = 0) + Time.new.beginning_of_week(:monday).change(hour: hour, min: minute, sec: sec) + day.days + end + + def test_16_20 + at = Zhong::At.parse("16:20", grace: 0) + + assert_equal time_in_day(16, 20), at.next_at(time_in_day(16, 15)) + assert_equal time_in_day(16, 20), at.next_at(time_in_day(16, 20)) + assert_equal time_in_day(16, 20), at.next_at(time_in_day(16, 20, 0, 10)) + assert_equal time_in_day(16, 20), at.next_at(time_in_day(16, 20, 0, 59)) + assert_equal time_in_day(16, 20, 1), at.next_at(time_in_day(16, 21)) + assert_equal time_in_day(16, 20, 1), at.next_at(time_in_day(16, 21, 0, 01)) + assert_equal time_in_day(16, 20, 2), at.next_at(time_in_day(16, 21, 1, 30)) + end + + def test_16_20_with_grace + at = Zhong::At.parse("16:20", grace: 5.minutes) + + assert_equal time_in_day(16, 20), at.next_at(time_in_day(16, 21)) + assert_equal time_in_day(16, 20), at.next_at(time_in_day(16, 25)) + assert_equal time_in_day(16, 20, 1), at.next_at(time_in_day(16, 26)) + end + + def test_8_20_before + at = Zhong::At.parse("8:20") + + assert_equal time_in_day(8, 20), at.next_at(time_in_day(8, 15)) + assert_equal time_in_day(8, 20, 1), at.next_at(time_in_day(8, 21)) + end + + def test_two_star_20 + at = Zhong::At.parse("**:20") + + assert_equal time_in_day(8, 20), at.next_at(time_in_day(8, 15)) + assert_equal time_in_day(9, 20), at.next_at(time_in_day(9, 15)) + assert_equal time_in_day(10, 20), at.next_at(time_in_day(9, 45)) + end + + def test_one_star_20 + at = Zhong::At.parse("*:45") + + assert_equal time_in_day(8, 45), at.next_at(time_in_day(8, 35)) + assert_equal time_in_day(9, 45), at.next_at(time_in_day(9, 35)) + assert_equal time_in_day(10, 45), at.next_at(time_in_day(9, 50)) + end + + def test_one_star_20_with_grace + at = Zhong::At.parse("*:45", grace: 5.minutes) + + assert_equal time_in_day(8, 45), at.next_at(time_in_day(8, 35)) + assert_equal time_in_day(8, 45), at.next_at(time_in_day(8, 50)) + assert_equal time_in_day(9, 45), at.next_at(time_in_day(8, 51)) + assert_equal time_in_day(9, 45), at.next_at(time_in_day(9, 35)) + assert_equal time_in_day(10, 45), at.next_at(time_in_day(9, 55)) + end + + def test_16_two_stars + at = Zhong::At.parse("16:**") + + assert_equal time_in_day(16, 00), at.next_at(time_in_day(8, 45)) + assert_equal time_in_day(16, 00), at.next_at(time_in_day(10, 00)) + assert_equal time_in_day(16, 00), at.next_at(time_in_day(16, 00)) + assert_equal time_in_day(16, 01), at.next_at(time_in_day(16, 01)) + assert_equal time_in_day(16, 30), at.next_at(time_in_day(16, 30)) + assert_equal time_in_day(16, 59), at.next_at(time_in_day(16, 59)) + assert_equal time_in_day(16, 00, 1), at.next_at(time_in_day(17, 00)) + assert_equal time_in_day(16, 00, 1), at.next_at(time_in_day(23, 59)) + end + + def test_8_two_stars + at = Zhong::At.parse("8:**") + + assert_equal time_in_day(8, 00), at.next_at(time_in_day(3, 45)) + assert_equal time_in_day(8, 00), at.next_at(time_in_day(5, 00)) + assert_equal time_in_day(8, 00), at.next_at(time_in_day(8, 00)) + assert_equal time_in_day(8, 01), at.next_at(time_in_day(8, 01)) + assert_equal time_in_day(8, 30), at.next_at(time_in_day(8, 30)) + assert_equal time_in_day(8, 59), at.next_at(time_in_day(8, 59)) + assert_equal time_in_day(8, 00, 1), at.next_at(time_in_day(9, 00)) + assert_equal time_in_day(8, 00, 1), at.next_at(time_in_day(12, 59)) + end + + def test_saturday_12 + at = Zhong::At.parse("Saturday 12:00") + + assert_equal time_in_day(12, 00, 5), at.next_at(time_in_day(12, 00)) + assert_equal time_in_day(12, 00, 5), at.next_at(time_in_day(13, 00, 1)) + assert_equal time_in_day(12, 00, 5), at.next_at(time_in_day(23, 59, 3)) + assert_equal time_in_day(12, 00, 5), at.next_at(time_in_day(12, 00, 5)) + assert_equal time_in_day(12, 00, 12), at.next_at(time_in_day(12, 01, 5)) + assert_equal time_in_day(12, 00, 12), at.next_at(time_in_day(13, 01, 6, 01)) + end + + def test_sat_12 + at = Zhong::At.parse("sat 12:00") + + assert_equal time_in_day(12, 00, 5), at.next_at(time_in_day(12, 00)) + assert_equal time_in_day(12, 00, 5), at.next_at(time_in_day(13, 00, 1)) + assert_equal time_in_day(12, 00, 5), at.next_at(time_in_day(23, 59, 3)) + assert_equal time_in_day(12, 00, 5), at.next_at(time_in_day(12, 00, 5)) + assert_equal time_in_day(12, 00, 12), at.next_at(time_in_day(12, 01, 5)) + assert_equal time_in_day(12, 00, 12), at.next_at(time_in_day(13, 01, 6, 01)) + end + + def test_tue_12_59 + at = Zhong::At.parse("tue 12:59") + + assert_equal time_in_day(12, 59, 1), at.next_at(time_in_day(7, 00)) + assert_equal time_in_day(12, 59, 1), at.next_at(time_in_day(12, 00, 1)) + assert_equal time_in_day(12, 59, 1), at.next_at(time_in_day(12, 59, 1)) + assert_equal time_in_day(12, 59, 8), at.next_at(time_in_day(13, 00, 1)) + assert_equal time_in_day(12, 59, 8), at.next_at(time_in_day(13, 01, 6, 01)) + end + + def test_thr_12_59 + at = Zhong::At.parse("thr 12:59") + + assert_equal time_in_day(12, 59, 3), at.next_at(time_in_day(7, 00)) + assert_equal time_in_day(12, 59, 3), at.next_at(time_in_day(12, 00, 3)) + assert_equal time_in_day(12, 59, 3), at.next_at(time_in_day(12, 59, 3)) + assert_equal time_in_day(12, 59, 10), at.next_at(time_in_day(13, 00, 3)) + assert_equal time_in_day(12, 59, 10), at.next_at(time_in_day(13, 01, 6, 01)) + end + + def test_invalid_time_32 + assert_raises Zhong::At::FailedToParse do + Zhong::At.parse("32:00") + end + end + + def test_invalid_multi_line_time_sat_12 + assert_raises Zhong::At::FailedToParse do + Zhong::At.parse("sat 12:00\nreally invalid time") + end + end +end + +class TestLibrary < Minitest::Test def test_that_it_has_a_version_number refute_nil ::Zhong::VERSION end diff --git a/zhong.gemspec b/zhong.gemspec index 21e59fa..8251c20 100644 --- a/zhong.gemspec +++ b/zhong.gemspec @@ -31,5 +31,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rubocop", "~> 0.30.0" spec.add_development_dependency "minitest", "~> 5.5.0" - # spec.add_development_dependency "codeclimate-test-reporter", "~> 0.4.7" + spec.add_development_dependency "codeclimate-test-reporter", "~> 0.4.7" end