File: group_spec.rb

package info (click to toggle)
ruby-timers 4.1.1-2.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid, trixie
  • size: 196 kB
  • sloc: ruby: 668; makefile: 7
file content (254 lines) | stat: -rw-r--r-- 6,708 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

require 'spec_helper'

RSpec.describe Timers::Group do
  describe "#wait" do
    it "calls the wait block with nil" do
      called = false
      
      subject.wait do |interval|
        expect(interval).to be == nil
        called = true
      end
      
      expect(called).to be true
    end
  
    it "calls the wait block with an interval" do
      called = false
      fired = false

      subject.after(0.1) { fired = true }

      subject.wait do |interval|
        expect(interval).to be_within(TIMER_QUANTUM).of(0.1)
        called = true
        sleep 0.2
      end

      expect(called).to be true
      expect(fired).to be true
    end
  end
  
  it "sleeps until the next timer" do
    interval   = TIMER_QUANTUM * 2
    started_at = Time.now

    fired = false
    subject.after(interval) { fired = true }
    subject.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
    fired = false
    subject.after(TIMER_QUANTUM) { fired = true }
    sleep(TIMER_QUANTUM * 2)
    subject.wait

    expect(fired).to be true
  end

  it "calculates the interval until the next timer should fire" do
    interval = 0.1

    subject.after(interval)
    expect(subject.wait_interval).to be_within(TIMER_QUANTUM).of interval

    sleep(interval)
    expect(subject.wait_interval).to be <= 0
  end

  it "fires timers in the correct order" do
    result = []

    subject.after(TIMER_QUANTUM * 2) { result << :two }
    subject.after(TIMER_QUANTUM * 3) { result << :three }
    subject.after(TIMER_QUANTUM * 1) { result << :one }

    sleep TIMER_QUANTUM * 4
    subject.fire

    expect(result).to eq [:one, :two, :three]
  end

  it "raises TypeError if given an invalid time" do
    expect do
      subject.after(nil) { nil }
    end.to raise_exception(TypeError)
  end

  describe "recurring timers" do
    it "continues to fire the timers at each interval" do
      result = []

      subject.every(TIMER_QUANTUM * 2) { result << :foo }

      sleep TIMER_QUANTUM * 3
      subject.fire
      expect(result).to eq [:foo]

      sleep TIMER_QUANTUM * 5
      subject.fire
      expect(result).to eq [:foo, :foo]
    end
  end

  it "calculates the proper interval to wait until firing" do
    interval_ms = 25

    subject.after(interval_ms / 1000.0)

    expect(subject.wait_interval).to be_within(TIMER_QUANTUM).of(interval_ms / 1000.0)
  end

  describe "pause and continue timers" do
    before(:each) do
      @interval   = TIMER_QUANTUM * 2

      @fired = false
      @timer = subject.after(@interval) { @fired = true }
      @fired2 = false
      @timer2 = subject.after(@interval) { @fired2 = true }
    end

    it "does not fire when paused" do
      @timer.pause
      subject.wait
      expect(@fired).to be false
    end

    it "fires when continued after pause" do
      @timer.pause
      subject.wait
      @timer.resume
      
      sleep @timer.interval
      subject.wait
      
      expect(@fired).to be true
    end

    it "can pause all timers at once" do
      subject.pause
      subject.wait
      expect(@fired).to be false
      expect(@fired2).to be false
    end

    it "can continue all timers at once" do
      subject.pause
      subject.wait
      subject.resume
      
      # We need to wait until we are sure both timers will fire, otherwise highly accurate clocks (e.g. JVM) may only fire the first timer, but not the second, because they are actually schedueled at different times.
      sleep TIMER_QUANTUM * 2
      subject.wait
      
      expect(@fired).to be true
      expect(@fired2).to be true
    end

    it "can fire the timer directly" do
      fired = false
      timer = subject.after( TIMER_QUANTUM * 1 ) { fired = true }
      timer.pause
      subject.wait
      expect(fired).not_to be true
      timer.resume
      expect(fired).not_to be true
      timer.fire
      expect(fired).to be true
    end

  end

  describe "delay timer" do
    it "adds appropriate amount of time to timer" do
      timer = subject.after(10)
      timer.delay(5)
      expect(timer.offset - subject.current_offset).to be_within(TIMER_QUANTUM).of(15)
    end
  end

  describe "delay timer collection" do
    it "delay on set adds appropriate amount of time to all timers" do
      timer = subject.after(10)
      timer2 = subject.after(20)
      subject.delay(5)
      expect(timer.offset - subject.current_offset).to be_within(TIMER_QUANTUM).of(15)
      expect(timer2.offset - subject.current_offset).to be_within(TIMER_QUANTUM).of(25)
    end
  end

  describe "on delaying a timer" do
    it "fires timers in the correct order" do
      result = []

      subject.after(TIMER_QUANTUM * 2) { result << :two }
      subject.after(TIMER_QUANTUM * 3) { result << :three }
      first = subject.after(TIMER_QUANTUM * 1) { result << :one }
      first.delay(TIMER_QUANTUM * 3)

      sleep TIMER_QUANTUM * 5
      subject.fire

      expect(result).to eq [:two, :three, :one]
    end
  end

  describe "Timer inspection" do
    it "before firing" do
      fired = false
      timer = subject.after(TIMER_QUANTUM * 5) { fired = true }
      timer.pause
      expect(fired).not_to be true
      expect(timer.inspect).to match(/\A#<Timers::Timer:[\da-f]+ fires in [-\.\de]+ seconds>\Z/)
    end

    it "after firing" do
      fired = false
      timer = subject.after(TIMER_QUANTUM) { fired = true }

      subject.wait

      expect(fired).to be true
      expect(timer.inspect).to match(/\A#<Timers::Timer:[\da-f]+ fired [-\.\de]+ seconds ago>\Z/)
    end

    it "recurring firing" do
      result = []
      timer = subject.every(TIMER_QUANTUM) { result << :foo }

      subject.wait
      expect(result).not_to be_empty
      expect(timer.inspect).to match(/\A#<Timers::Timer:[\da-f]+ fires in [-\.\de]+ seconds, recurs every #{sprintf("%0.2f", TIMER_QUANTUM)}>\Z/)
    end
  end

  describe "fires_in" do
    let(:interval) { TIMER_QUANTUM * 2 }

    it "calculates the interval until the next fire if it's recurring" do
      timer = subject.every(interval) { true }
      expect(timer.fires_in).to be_within(TIMER_QUANTUM).of(interval)
    end

    context "when timer is not recurring" do
      let!(:timer) { subject.after(interval) { true } }

      it "calculates the interval until the next fire if it hasn't already fired" do
        expect(timer.fires_in).to be_within(TIMER_QUANTUM).of(interval)
      end

      it "calculates the interval since last fire if already fired" do
        subject.wait
        sleep(interval)
        expect(timer.fires_in).to be_within(TIMER_QUANTUM).of(0 - interval)
      end
    end
  end
end