https://github.com/socketry/timers/issues/82 https://github.com/socketry/timers/commit/039bbd2750d5e50721789ef5d3404b18c36517bc From 039bbd2750d5e50721789ef5d3404b18c36517bc Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Wed, 12 Apr 2023 17:25:59 +1200 Subject: [PATCH] Modernize gem (#83) * Improve robustness of `Timer#inspect`. * 100% test coverage. --- a/fixtures/timer_quantum.rb +++ b/fixtures/timer_quantum.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Released under the MIT License. # Copyright, 2022, by Samuel Williams. --- a/lib/timers/group.rb +++ b/lib/timers/group.rb @@ -92,8 +92,9 @@ def wait # - 0: timers ready to fire # - +ve: timers waiting to fire def wait_interval(offset = current_offset) - handle = @events.first - handle.time - Float(offset) if handle + if handle = @events.first + handle.time - Float(offset) + end end # Fire all timers that are ready. --- a/lib/timers/priority_heap.rb +++ b/lib/timers/priority_heap.rb @@ -84,9 +84,10 @@ def valid? private - def swap(i, j) - @contents[i], @contents[j] = @contents[j], @contents[i] - end + # Left here for reference, but unused. + # def swap(i, j) + # @contents[i], @contents[j] = @contents[j], @contents[i] + # end def bubble_up(index) parent_index = (index - 1) / 2 # watch out, integer division! --- a/lib/timers/timer.rb +++ b/lib/timers/timer.rb @@ -23,12 +23,11 @@ def initialize(group, interval, recurring = false, offset = nil, &block) @interval = interval @recurring = recurring @block = block - @offset = offset - + @offset = nil @handle = nil # If a start offset was supplied, use that, otherwise use the current timers offset. - reset(@offset || @group.current_offset) + reset(offset || @group.current_offset) end def paused? @@ -73,7 +72,7 @@ def cancel @handle = nil # This timer is no longer valid: - @group.timers.delete self if @group + @group.timers.delete(self) if @group end # Reset this timer. Do not call while paused. @@ -117,18 +116,18 @@ def fires_in # Inspect a timer def inspect - buffer = "#{to_s[0..-2]} ".dup + buffer = to_s[0..-2] if @offset - if fires_in >= 0 - buffer << "fires in #{fires_in} seconds" + delta_offset = @offset - @group.current_offset + + if delta_offset > 0 + buffer << " fires in #{delta_offset} seconds" else - buffer << "fired #{fires_in.abs} seconds ago" + buffer << " fired #{delta_offset.abs} seconds ago" end buffer << ", recurs every #{interval}" if recurring - else - buffer << "dead" end buffer << ">" --- a/lib/timers/wait.rb +++ b/lib/timers/wait.rb @@ -17,6 +17,7 @@ def self.for(duration, &block) timeout.while_time_remaining(&block) else + # If there is no "duration" to wait for, we wait forever. loop do yield(nil) end --- a/license.md +++ b/license.md @@ -28,6 +28,7 @@ Copyright, 2017-2020, by Olle Jonsson. Copyright, 2020, by Tim Smith. Copyright, 2021, by Wander Hillen. Copyright, 2022, by Yoshiki Takagi. +Copyright, 2023, by Peter Goldstein. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal --- a/test/timers/group.rb +++ b/test/timers/group.rb @@ -36,18 +36,31 @@ expect(called).to be == true expect(fired).to be == true end + + it "repeatedly calls the wait block if it sleeps less than the interval" do + called = 0 + fired = false + + group.after(0.1) { fired = true } + + group.wait do |interval| + called += 1 + sleep(0.01) + end + + expect(called).to be > 1 + expect(fired).to be == true + end end it "sleeps until the next timer" do interval = 0.1 - started_at = Time.now fired = false group.after(interval) {fired = true} group.wait expect(fired).to be == true - expect(Time.now - started_at).to be_within(TIMER_QUANTUM).of(interval) end it "fires instantly when next timer is in the past" do @@ -88,6 +101,26 @@ end.to raise_exception(TypeError) end + with "#now_and_after" do + it "fires the timer immediately" do + result = [] + + group.now_and_after(TIMER_QUANTUM * 2) { result << :foo } + + expect(result).to be == [:foo] + end + + it "fires the timer at the correct time" do + result = [] + + group.now_and_after(TIMER_QUANTUM * 2) { result << :foo } + + group.wait + + expect(result).to be == [:foo, :foo] + end + end + with "recurring timers" do it "continues to fire the timers at each interval" do result = [] --- a/test/timers/group/cancel.rb +++ b/test/timers/group/cancel.rb @@ -10,6 +10,17 @@ describe Timers::Group do let(:group) {subject.new} + it "can cancel a timer" do + fired = false + + timer = group.after(0.1) { fired = true } + timer.cancel + + group.wait + + expect(fired).to be == false + end + it "should be able to cancel twice" do fired = false @@ -51,4 +62,18 @@ expect(group.timers).to be(:empty?) expect(x).to be == 0 end + + with "#cancel" do + it "should cancel all timers" do + timers = 3.times.map do + group.every(0.1) {} + end + + expect(group.timers).not.to be(:empty?) + + group.cancel + + expect(group.timers).to be(:empty?) + end + end end --- a/test/timers/wait.rb +++ b/test/timers/wait.rb @@ -14,9 +14,14 @@ it "repeats until timeout expired" do timeout = Timers::Wait.new(interval*repeats) count = 0 + previous_remaining = nil timeout.while_time_remaining do |remaining| - expect(remaining).to be_within(TIMER_QUANTUM).of(timeout.duration - (count * interval)) + if previous_remaining + expect(remaining).to be_within(TIMER_QUANTUM).of(previous_remaining - interval) + end + + previous_remaining = remaining count += 1 sleep(interval) @@ -34,4 +39,18 @@ expect(result).to be == :done end + + with "#for" do + with "no duration" do + it "waits forever" do + count = 0 + Timers::Wait.for(nil) do + count += 1 + break if count > 10 + end + + expect(count).to be > 10 + end + end + end end