cyclical 0.1.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.
@@ -0,0 +1,650 @@
1
+ require 'spec_helper'
2
+
3
+ describe Schedule do
4
+ it "should create instances with start date" do
5
+ t = Time.now
6
+ s = Schedule.new(t)
7
+
8
+ s.start_time.should == t
9
+ end
10
+
11
+ # basic API for listing occurences
12
+ describe "single date schedule" do
13
+ before do
14
+ @time = Time.local(2011, 11, 1, 0, 0, 0)
15
+ @schedule = Schedule.new(@time)
16
+ end
17
+
18
+ it "should accept end time and return suboccurrence" do
19
+ @schedule.end_time = Time.local(2011, 11, 1, 1)
20
+
21
+ @schedule.end_time.should == Time.local(2011, 11, 1, 1)
22
+
23
+ so = @schedule.suboccurrences_between(Time.local(2011, 11, 1, 0, 30), Time.local(2011, 11, 2))
24
+ so.first.start.should == Time.local(2011, 11, 1, 0, 30)
25
+ so.first.occurrence_start?.should_not be_true
26
+ so.first.end.should == @schedule.end_time
27
+ so.first.occurrence_end?.should be_true
28
+ end
29
+
30
+ it "should list the single occurrence" do
31
+ @schedule.first(1).should include(Time.local(2011, 11, 1))
32
+ end
33
+
34
+ it "should find next occurrence" do
35
+ t = Time.local(2011, 11, 1)
36
+
37
+ @schedule.next_occurrence(t - 1.second).should == t
38
+ @schedule.next_occurrence(t + 1.second).should be_nil
39
+
40
+ @schedule.next_occurrence(t).should == t
41
+ end
42
+
43
+ it "should find previous occurrence" do
44
+ t = Time.local(2011, 11, 1)
45
+
46
+ @schedule.previous_occurrence(t + 1.second).should == t
47
+ @schedule.previous_occurrence(t - 1.second).should be_nil
48
+
49
+ @schedule.previous_occurrence(t).should be_nil
50
+ end
51
+
52
+ it "should list occurrences between dates" do
53
+ t = Time.local(2011, 11, 1)
54
+
55
+ @schedule.occurrences_between(t - 10.minutes, t - 1.second).should be_empty
56
+ @schedule.occurrences_between(t + 1.second, t + 10.minutes).should be_empty
57
+
58
+ @schedule.occurrences_between(t - 1.second, t + 1.second).should include(t)
59
+
60
+ @schedule.occurrences_between(t - 10.minutes, t).should be_empty
61
+ @schedule.occurrences_between(t, t + 10.minutes).should include(t)
62
+ end
63
+
64
+ it "should list occurrences up to a date" do
65
+ t = Time.local(2011, 11, 1)
66
+
67
+ @schedule.occurrences(t - 1.second).should be_empty
68
+ @schedule.occurrences(t).should be_empty
69
+ @schedule.occurrences(t + 1.second).should include(t)
70
+ end
71
+
72
+ it "should list all occurences" do
73
+ @schedule.occurrences.should include(Time.local(2011, 11, 1))
74
+ end
75
+ end
76
+
77
+ describe "advanced case" do
78
+ before do
79
+ @time = Time.local(2011, 9, 1, 10)
80
+ @schedule = Schedule.new @time, Rule.monthly(2).weekdays(:mon => 2).count(5)
81
+ end
82
+
83
+ it "should list first 3 occurrences" do
84
+ expected = [Time.local(2011, 9, 12, 10), Time.local(2011, 11, 14, 10), Time.local(2012, 1, 9, 10)]
85
+
86
+ @schedule.first(3).should == expected
87
+ end
88
+
89
+ it "should find next occurrence" do
90
+ @schedule.next_occurrence(Time.local(2011, 10, 1)).should == Time.local(2011, 11, 14, 10)
91
+ @schedule.next_occurrence(Time.local(2011, 11, 14)).should == Time.local(2011, 11, 14, 10)
92
+ @schedule.next_occurrence(Time.local(2011, 11, 14, 10)).should == Time.local(2011, 11, 14, 10)
93
+ @schedule.next_occurrence(Time.local(2011, 11, 14, 10, 0, 1)).should == Time.local(2012, 1, 9, 10)
94
+ end
95
+
96
+ it "should find previous occurrence" do
97
+ @schedule.previous_occurrence(Time.local(2011, 12, 1)).should == Time.local(2011, 11, 14, 10)
98
+ @schedule.previous_occurrence(Time.local(2011, 11, 14, 10, 0, 1)).should == Time.local(2011, 11, 14, 10)
99
+ @schedule.previous_occurrence(Time.local(2011, 11, 14, 10)).should == Time.local(2011, 9, 12, 10)
100
+ end
101
+
102
+ it "should list occurrences between dates" do
103
+ @schedule.occurrences_between(Time.local(2011, 10, 1), Time.local(2011, 11, 14, 10, 0, 1)).should include(Time.local(2011, 11, 14, 10))
104
+ @schedule.occurrences_between(Time.local(2011, 11, 14, 10), Time.local(2011, 11, 14, 11)).should include(Time.local(2011, 11, 14, 10))
105
+
106
+ @schedule.occurrences_between(Time.local(2011, 11, 14, 10, 0, 1), Time.local(2011, 12, 1)).should be_empty
107
+ @schedule.occurrences_between(Time.local(2011, 11, 1), Time.local(2011, 11, 14, 10)).should be_empty
108
+ end
109
+
110
+ it "should list occurrences upto a date" do
111
+ expected = [Time.local(2011, 9, 12, 10), Time.local(2011, 11, 14, 10), Time.local(2012, 1, 9, 10)]
112
+
113
+ @schedule.occurrences(Time.local(2012, 1, 9, 10)).should == expected[0..1]
114
+ @schedule.occurrences(Time.local(2012, 1, 9, 10, 0, 1)).should == expected
115
+ end
116
+
117
+ it "should list all occurrences" do
118
+ expected = [Time.local(2011, 9, 12, 10), Time.local(2011, 11, 14, 10),
119
+ Time.local(2012, 1, 9, 10), Time.local(2012, 3, 12, 10), Time.local(2012, 5, 14, 10)]
120
+
121
+ @schedule.occurrences.should == expected
122
+ end
123
+ end
124
+
125
+ describe "multiple-day schedule" do
126
+ before do
127
+ @time = Time.local(2011, 11, 1, 15)
128
+ @schedule = Schedule.new @time, Rule.daily(4).count(5)
129
+ @schedule.end_time = Time.local(2011, 11, 3, 8) # two days later at 8 AM
130
+ end
131
+
132
+ it "should return single occurrence span between dates" do
133
+ so = @schedule.suboccurrences_between(Time.local(2011, 11, 1), Time.local(2011, 11, 2))
134
+ so.first.start.should == Time.local(2011, 11, 1, 15)
135
+ so.first.end.should == Time.local(2011, 11, 2)
136
+ so.first.occurrence_start?.should be_true
137
+ so.first.occurrence_end?.should_not be_true
138
+
139
+ so = @schedule.suboccurrences_between(Time.local(2011, 11, 1), Time.local(2011, 11, 3, 10))
140
+ so.first.start.should == Time.local(2011, 11, 1, 15)
141
+ so.first.end.should == Time.local(2011, 11, 3, 8)
142
+ so.first.occurrence_start?.should be_true
143
+ so.first.occurrence_end?.should be_true
144
+
145
+ so = @schedule.suboccurrences_between(Time.local(2011, 11, 2, 5), Time.local(2011, 11, 3, 10))
146
+ so.first.start.should == Time.local(2011, 11, 2, 5)
147
+ so.first.end.should == Time.local(2011, 11, 3, 8)
148
+ so.first.occurrence_start?.should_not be_true
149
+ so.first.occurrence_end?.should be_true
150
+
151
+ @schedule.suboccurrences_between(Time.local(2011, 11, 3, 9), Time.local(2011, 11, 4, 11)).should be_empty
152
+ end
153
+
154
+ it "should return multiple occurrence spans" do
155
+ so = @schedule.suboccurrences_between(Time.local(2011, 11, 1), Time.local(2011, 11, 8))
156
+
157
+ so.length.should == 2
158
+ so.first.start.should == Time.local(2011, 11, 1, 15)
159
+ so.first.end.should == Time.local(2011, 11, 3, 8)
160
+ so.first.occurrence_start?.should be_true
161
+ so.first.occurrence_end?.should be_true
162
+
163
+ so[1].start.should == Time.local(2011, 11, 5, 15)
164
+ so[1].end.should == Time.local(2011, 11, 7, 8)
165
+ so[1].occurrence_start?.should be_true
166
+ so[1].occurrence_end?.should be_true
167
+ end
168
+ end
169
+
170
+ describe "self overlapping schedule" do
171
+ before do
172
+ @time = Time.local(2011, 11, 1, 15)
173
+ @schedule = Schedule.new @time, Rule.daily.count(5)
174
+ @schedule.end_time = Time.local(2011, 11, 3, 8) # two days later at 8 AM
175
+ end
176
+
177
+ it "should find two occurrences in a single day" do
178
+ s = @schedule.suboccurrences_between(Time.local(2011, 11, 2), Time.local(2011, 11, 3))
179
+
180
+ s.length.should == 2
181
+
182
+ s.first.start.should == Time.local(2011, 11, 2)
183
+ s.first.occurrence_start?.should_not be_true
184
+ s.first.end.should == Time.local(2011, 11, 3)
185
+ s.first.occurrence_end?.should_not be_true
186
+
187
+ s.last.start.should == Time.local(2011, 11, 2, 15)
188
+ s.last.occurrence_start?.should be_true
189
+ s.last.end.should == Time.local(2011, 11, 3)
190
+ s.last.occurrence_end?.should_not be_true
191
+ end
192
+ end
193
+
194
+ # Examples from RFC 5545 except excluded and included dates
195
+ describe "rfc 5545 examples" do
196
+ before do
197
+ @time = Time.local(1997, 9, 2, 9, 0, 0)
198
+ end
199
+
200
+ describe "daily for 10 occurrences" do
201
+ before do
202
+ @schedule = Schedule.new @time, Rule.daily.count(10)
203
+ end
204
+
205
+ it "should list occurences" do
206
+ expected = (2..11).map { |i| Time.local(1997, 9, i, 9, 0, 0) }
207
+ @schedule.occurrences.should == expected
208
+ end
209
+ end
210
+
211
+ describe "daily until December 24, 1997" do
212
+ before do
213
+ @schedule = Schedule.new @time, Rule.daily.stop(Time.local(1997, 12, 24))
214
+ end
215
+
216
+ it "should list occurrences" do
217
+ expected = (2..30).map { |i| Time.local(1997, 9, i, 9, 0, 0) } +
218
+ (1..31).map { |i| Time.local(1997, 10, i, 9, 0, 0) } +
219
+ (1..30).map { |i| Time.local(1997, 11, i, 9, 0, 0) } +
220
+ (1..23).map { |i| Time.local(1997, 12, i, 9, 0, 0) }
221
+
222
+ @schedule.occurrences.should == expected
223
+ end
224
+ end
225
+
226
+ describe "every other day - forever" do
227
+ before do
228
+ @schedule = Schedule.new @time, Rule.daily(2)
229
+ end
230
+
231
+ it "should list first 45 occurrences" do
232
+ expected = (1..15).map { |i| Time.local(1997, 9, 2*i, 9, 0, 0)} +
233
+ (1..15).map { |i| Time.local(1997, 10, 2*i, 9, 0, 0)} +
234
+ (1..15).map { |i| Time.local(1997, 11, 2*i - 1, 9, 0, 0)}
235
+
236
+ @schedule.first(45).should == expected
237
+ @schedule.occurrences(Time.local(1997, 12, 1)).should == expected
238
+ end
239
+ end
240
+
241
+ describe "every 10 days, 5 occurrences" do
242
+ before do
243
+ @schedule = Schedule.new @time, Rule.daily(10).count(5)
244
+ end
245
+
246
+ it "should list occurrences" do
247
+ expected = [2, 12, 22].map { |i| Time.local(1997, 9, i, 9, 0, 0)} +
248
+ [2, 12].map { |i| Time.local(1997, 10, i, 9, 0, 0)}
249
+
250
+ @schedule.occurrences.should == expected
251
+ end
252
+ end
253
+
254
+ describe "every day in January, for 3 years" do
255
+ before do
256
+ @schedule_1 = Schedule.new Time.local(1998, 1, 1, 9), Rule.daily.month(:january).stop(Time.local(2000, 1, 31, 14))
257
+
258
+ rule = Rule.yearly.month(:january).weekdays(1, :tu, :we, :th, 5, 6, :su).stop(Time.local(2000, 1, 31, 14))
259
+ @schedule_2 = Schedule.new Time.local(1998, 1, 1, 9), rule
260
+ end
261
+
262
+ it "should list occurrences" do
263
+ expected = (1..31).map { |i| Time.local(1998, 1, i, 9) } +
264
+ (1..31).map { |i| Time.local(1999, 1, i, 9) } +
265
+ (1..31).map { |i| Time.local(2000, 1, i, 9) }
266
+
267
+ @schedule_1.occurrences.length.should == expected.length
268
+ @schedule_1.occurrences.should == expected
269
+
270
+ @schedule_2.occurrences.length.should == expected.length
271
+ @schedule_2.occurrences.should == expected
272
+ end
273
+ end
274
+
275
+ describe "weekly for 10 occurrences" do
276
+ before do
277
+ @schedule = Schedule.new @time, Rule.weekly.count(10)
278
+ end
279
+
280
+ it "should list occurrences" do
281
+ expected = [2, 9, 16, 23, 30].map { |i| Time.local(1997, 9, i, 9)} +
282
+ [7, 14, 21, 28].map { |i| Time.local(1997, 10, i, 9)} +
283
+ [4].map { |i| Time.local(1997, 11, i, 9)}
284
+
285
+ @schedule.occurrences.should == expected
286
+ end
287
+ end
288
+
289
+ describe "weekly until December 24, 1997" do
290
+ before do
291
+ @schedule = Schedule.new @time, Rule.weekly.stop(Time.local(1997, 12, 24))
292
+ end
293
+
294
+ it "should list occurrences" do
295
+ expected = [2, 9, 16, 23, 30].map { |i| Time.local(1997, 9, i, 9) } +
296
+ [7, 14, 21, 28].map { |i| Time.local(1997, 10, i, 9) } +
297
+ [4, 11, 18, 25].map { |i| Time.local(1997, 11, i, 9) } +
298
+ [2, 9, 16, 23].map { |i| Time.local(1997, 12, i, 9) }
299
+
300
+ @schedule.occurrences.should == expected
301
+ end
302
+ end
303
+
304
+ describe "every other week forever" do
305
+ before do
306
+ @schedule = Schedule.new @time, Rule.weekly(2)
307
+ end
308
+
309
+ it "should list first 14 occurrences" do
310
+ expected = [2, 16, 30].map { |i| Time.local(1997, 9, i, 9) } +
311
+ [14, 28].map { |i| Time.local(1997, 10, i, 9) } +
312
+ [11, 25].map { |i| Time.local(1997, 11, i, 9) } +
313
+ [9, 23].map { |i| Time.local(1997, 12, i, 9) } +
314
+ [6, 20].map { |i| Time.local(1998, 1, i, 9) } +
315
+ [3, 17].map { |i| Time.local(1998, 2, i, 9) }
316
+
317
+ @schedule.first(13).should == expected
318
+ end
319
+ end
320
+
321
+ describe "weekly on tuesday and thursday for five weeks" do
322
+ before do
323
+ @stop_schedule = Schedule.new @time, Rule.weekly.weekdays(:tue, :thu).stop(Time.local(1997, 10, 7))
324
+ @count_schedule = Schedule.new @time, Rule.weekly.weekdays(:tue, :thu).count(10)
325
+ end
326
+
327
+ it "should list occurrences" do
328
+ expected = [2, 4, 9, 11, 16, 18, 23, 25, 30].map { |i| Time.local(1997, 9, i, 9) } +
329
+ [2].map { |i| Time.local(1997, 10, i, 9) }
330
+
331
+ @stop_schedule.occurrences.should == expected
332
+ @count_schedule.occurrences.should == expected
333
+ end
334
+ end
335
+
336
+ describe "every other week on Monday, Wednesday, and Friday until December 24, 1997" do
337
+ # starting on Monday, September 1, 1997
338
+ before do
339
+ @time = Time.local(1997, 9, 1, 9)
340
+ @schedule = Schedule.new @time, Rule.weekly(2).weekdays(:mon, :wed, :fri).stop(Time.local(1997, 12, 24))
341
+ end
342
+
343
+ it "should list occurrences" do
344
+ expected = [1, 3, 5, 15, 17, 19, 29].map { |i| Time.local(1997, 9, i, 9) } +
345
+ [1, 3, 13, 15, 17, 27, 29, 31].map { |i| Time.local(1997, 10, i, 9) } +
346
+ [10, 12, 14, 24, 26, 28].map { |i| Time.local(1997, 11, i, 9) } +
347
+ [8, 10, 12, 22].map { |i| Time.local(1997, 12, i, 9) }
348
+
349
+ @schedule.occurrences.should == expected
350
+ end
351
+ end
352
+
353
+ describe "every other week on Tuesday and Thursday, for 8 occurrences" do
354
+ before do
355
+ @schedule = Schedule.new @time, Rule.weekly(2).weekdays(:tu, :th).count(8)
356
+ end
357
+
358
+ it "should list occurrences" do
359
+ expected = [2, 4, 16, 18, 30].map { |i| Time.local(1997, 9, i, 9) } +
360
+ [2, 14, 16].map { |i| Time.local(1997, 10, i, 9) }
361
+
362
+ @schedule.occurrences.should == expected
363
+ end
364
+ end
365
+
366
+ describe "monthly on the first friday for 10 occurrences" do
367
+ before do
368
+ @schedule = Schedule.new @time, Rule.monthly.weekdays(:friday => 1).count(10)
369
+ end
370
+
371
+ it "should list occurrences" do
372
+ expected = [[9, 5], [10, 3], [11, 7], [12, 5]].map {|d| Time.local(1997, d[0], d[1], 9) } +
373
+ [[1, 2], [2, 6], [3, 6], [4, 3], [5, 1], [6, 5]].map {|d| Time.local(1998, d[0], d[1], 9) }
374
+
375
+ @schedule.occurrences.should == expected
376
+ end
377
+ end
378
+
379
+ describe "monthly on the first friday until December 24, 1997" do
380
+ before do
381
+ @schedule = Schedule.new @time, Rule.monthly.weekdays(:friday => 1).stop(Time.local(1997, 12, 24))
382
+ end
383
+
384
+ it "should list occurrences" do
385
+ expected = [[9, 5], [10, 3], [11, 7], [12, 5]].map {|d| Time.local(1997, d[0], d[1], 9) }
386
+
387
+ @schedule.occurrences.should == expected
388
+ end
389
+ end
390
+
391
+ describe "every other month on the first and last Sunday of the month for 10 occurrences" do
392
+ before do
393
+ @schedule = Schedule.new @time, Rule.monthly(2).weekdays(:sunday => [1, -1]).count(10)
394
+ end
395
+
396
+ it "should list occurrences" do
397
+ expected = [[9, 7], [9, 28], [11, 2], [11, 30]].map {|d| Time.local(1997, d[0], d[1], 9) } +
398
+ [[1, 4], [1, 25], [3, 1], [3, 29], [5, 3], [5, 31]].map {|d| Time.local(1998, d[0], d[1], 9) }
399
+
400
+ @schedule.occurrences.should == expected
401
+ end
402
+ end
403
+
404
+ describe "monthly on the second-to-last monday of the month for 6 months" do
405
+ before do
406
+ @schedule = Schedule.new @time, Rule.monthly.weekdays(:monday => -2).count(6)
407
+ end
408
+
409
+ it "should list occurrences" do
410
+ expected = [[9, 22], [10, 20], [11, 17], [12, 22]].map {|d| Time.local(1997, d[0], d[1], 9) } +
411
+ [[1, 19], [2, 16]].map {|d| Time.local(1998, d[0], d[1], 9) }
412
+
413
+ @schedule.occurrences.should == expected
414
+ end
415
+ end
416
+
417
+ describe "monthly on the third-to-the-last day of the month, forever" do
418
+ before do
419
+ @schedule = Schedule.new @time, Rule.monthly.monthday(-3)
420
+ end
421
+
422
+ it "should list 6 occurrences" do
423
+ expected = [[9, 28], [10, 29], [11, 28], [12, 29]].map {|d| Time.local(1997, d[0], d[1], 9) } +
424
+ [[1, 29], [2, 26]].map {|d| Time.local(1998, d[0], d[1], 9) }
425
+
426
+ @schedule.first(6).should == expected
427
+ end
428
+ end
429
+
430
+ describe "monthly on the 2nd and 15th of the month for 10 occurrences" do
431
+ before do
432
+ @schedule = Schedule.new @time, Rule.monthly.monthdays(2, 15).count(10)
433
+ end
434
+
435
+ it "should list occurrences" do
436
+ expected = [[9, 2], [9, 15], [10, 2], [10, 15], [11, 2], [11, 15], [12, 2], [12, 15]].map {|d| Time.local(1997, d[0], d[1], 9) } +
437
+ [[1, 2], [1, 15]].map {|d| Time.local(1998, d[0], d[1], 9) }
438
+
439
+ @schedule.occurrences.should == expected
440
+ end
441
+ end
442
+
443
+ describe "monthly on the first and last day of the month for 10 occurrences" do
444
+ before do
445
+ @schedule = Schedule.new @time, Rule.monthly.monthdays(1, -1).count(10)
446
+ end
447
+
448
+ it "should list occurrences" do
449
+ expected = [[9, 30], [10, 1], [10, 31], [11, 1], [11, 30], [12, 1], [12, 31]].map {|d| Time.local(1997, d[0], d[1], 9) } +
450
+ [[1, 1], [1, 31], [2, 1]].map {|d| Time.local(1998, d[0], d[1], 9) }
451
+
452
+ @schedule.occurrences.should == expected
453
+ end
454
+ end
455
+
456
+ describe "every 18 months on the 10th thru 15th of the month for 10 occurrences" do
457
+ before do
458
+ @schedule = Schedule.new @time, Rule.monthly(18).monthdays(*(10..15).to_a).count(10)
459
+ end
460
+
461
+ it "should list occurrences" do
462
+ expected = (10..15).map {|d| Time.local(1997, 9, d, 9) } +
463
+ (10..13).map {|d| Time.local(1999, 3, d, 9) }
464
+
465
+ @schedule.occurrences.should == expected
466
+ end
467
+ end
468
+
469
+ describe "every Tuesday, every other month" do
470
+ before do
471
+ @schedule = Schedule.new @time, Rule.monthly(2).weekdays(:tue)
472
+ end
473
+
474
+ it "should list 18 occurrences" do
475
+ expected = [2, 9, 16, 23, 30].map {|d| Time.local(1997, 9, d, 9)} +
476
+ [4, 11, 18, 25].map {|d| Time.local(1997, 11, d, 9)} +
477
+ [6, 13, 20, 27].map {|d| Time.local(1998, 1, d, 9)} +
478
+ [3, 10, 17, 24, 31].map {|d| Time.local(1998, 3, d, 9)}
479
+
480
+ @schedule.first(18).should == expected
481
+ end
482
+ end
483
+
484
+ describe "yearly in June and July for 10 occurrences" do
485
+ before do
486
+ @schedule = Schedule.new Time.local(1997, 6, 10, 9), Rule.yearly.months(6, 7).count(10)
487
+ end
488
+
489
+ it "shoul lists occurrences" do
490
+ expected = [1997, 1998, 1999, 2000, 2001].map {|y| [Time.local(y, 6, 10, 9), Time.local(y, 7, 10, 9)] }.flatten
491
+
492
+ @schedule.occurrences.should == expected
493
+ end
494
+ end
495
+
496
+ describe "every other year on January, February, and March for 10 occurrences" do
497
+ before do
498
+ @schedule = Schedule.new Time.local(1997, 3, 10, 9), Rule.yearly(2).months(1, 2, 3).count(10)
499
+ end
500
+
501
+ it "should list occurrences" do
502
+ expected = [Time.local(1997, 3, 10, 9)] + [1999, 2001, 2003].map {|y| [1, 2, 3].map {|m| Time.local(y, m, 10, 9)}}.flatten
503
+
504
+ @schedule.occurrences.should == expected
505
+ end
506
+ end
507
+
508
+ describe "every third year on the 1st, 100th, and 200th day for 10 occurrences" do
509
+ before do
510
+ @schedule = Schedule.new Time.local(1997, 1, 1, 9), Rule.yearly(3).yeardays(1, 100, 200).count(10)
511
+ end
512
+
513
+ it "should list occurrences" do
514
+ expected = [[1997, 1, 1, 9], [1997, 4, 10, 9], [1997, 7, 19, 9], [2000, 1, 1, 9], [2000, 4, 9, 9],
515
+ [2000, 7, 18, 9], [2003, 1, 1, 9], [2003, 4, 10, 9], [2003, 7, 19, 9], [2006, 1, 1, 9]].map {|d| Time.local(d[0], d[1], d[2], d[3])}
516
+
517
+ @schedule.occurrences.should == expected
518
+ end
519
+ end
520
+
521
+ describe "every 20th Monday of the year, forever" do
522
+ before do
523
+ @schedule = Schedule.new Time.local(1997, 5, 19, 9), Rule.yearly.weekdays(:mon => 20)
524
+ end
525
+
526
+ it "should list 3 occurrences" do
527
+ expected = [Time.local(1997, 5, 19, 9), Time.local(1998, 5, 18, 9), Time.local(1999, 5, 17, 9)]
528
+
529
+ @schedule.first(3).should == expected
530
+ end
531
+ end
532
+
533
+ # skipped: Monday of week number 20 (where the default start of the week is Monday), forever
534
+
535
+ describe "every Thursday in March, forever" do
536
+ before do
537
+ @schedule = Schedule.new Time.local(1997, 3, 13, 9), Rule.yearly.month(:march).weekday(:thursday)
538
+ end
539
+
540
+ it "should list 11 occurrences" do
541
+ expected = [13, 20, 27].map {|d| Time.local(1997, 3, d, 9)} +
542
+ [5, 12, 19, 26].map {|d| Time.local(1998, 3, d, 9)} +
543
+ [4, 11, 18, 25].map {|d| Time.local(1999, 3, d, 9)}
544
+
545
+ @schedule.first(11).should == expected
546
+ end
547
+ end
548
+
549
+ describe "every Thursday, but only during June, July, and August, forever" do
550
+ before do
551
+ @schedule = Schedule.new Time.local(1997, 6, 5, 9), Rule.yearly.months(6, 7, 8).weekday(:thu)
552
+ end
553
+
554
+ it "should list first X occurrences" do
555
+ expected = [5, 12, 19, 26].map {|d| Time.local(1997, 6, d, 9)} +
556
+ [3, 10, 17, 24, 31].map {|d| Time.local(1997, 7, d, 9)} +
557
+ [7, 14, 21, 28].map {|d| Time.local(1997, 8, d, 9)} +
558
+ [4, 11, 18, 25].map {|d| Time.local(1998, 6, d, 9)} +
559
+ [2, 9, 16, 23, 30].map {|d| Time.local(1998, 7, d, 9)} +
560
+ [6, 13, 20, 27].map {|d| Time.local(1998, 8, d, 9)} +
561
+ [3, 10, 17, 24].map {|d| Time.local(1999, 6, d, 9)} +
562
+ [1, 8, 15, 22, 29].map {|d| Time.local(1999, 7, d, 9)} +
563
+ [5, 12, 19, 26].map {|d| Time.local(1999, 8, d, 9)}
564
+
565
+ @schedule.first(39).should == expected
566
+ end
567
+ end
568
+
569
+ describe "every Friday the 13th, forever" do
570
+ before do
571
+ @schedule = Schedule.new Time.local(1997, 9, 2, 9), Rule.monthly.weekday(:fri).monthday(13)
572
+ end
573
+
574
+ it "should list 5 occurrences" do
575
+ expected = [Time.local(1998, 2, 13, 9), Time.local(1998, 3, 13, 9), Time.local(1998, 11, 13, 9),
576
+ Time.local(1999, 8, 13, 9), Time.local(2000, 10, 13, 9)]
577
+
578
+ @schedule.first(5).should == expected
579
+ end
580
+ end
581
+
582
+ describe "the first Saturday that follows the first Sunday of the month, forever" do
583
+ before do
584
+ @schedule = Schedule.new Time.local(1997, 9, 13, 9), Rule.monthly.weekday(:sat).monthdays(*(7..13))
585
+ end
586
+
587
+ it "should list 10 occurrences" do
588
+ expected = [[9, 13], [10, 11], [11, 8], [12, 13]].map { |d| Time.local(1997, d[0], d[1], 9) } +
589
+ [[1, 10], [2, 7], [3, 7], [4, 11], [5, 9], [6, 13]].map { |d| Time.local(1998, d[0], d[1], 9) }
590
+
591
+ @schedule.first(10).should == expected
592
+ end
593
+ end
594
+
595
+ describe "every 4 years, the first Tuesday after a Monday in November, forever (U.S. Presidential Election day):" do
596
+ before do
597
+ @schedule = Schedule.new Time.local(1996, 11, 5, 9), Rule.yearly(4).month(11).weekday(:tue).monthdays(*(2..8))
598
+ end
599
+
600
+ it "should list first 3 election days" do
601
+ expected = [Time.local(1996, 11, 5, 9), Time.local(2000, 11, 7, 9), Time.local(2004, 11, 2, 9)]
602
+
603
+ @schedule.first(3).should == expected
604
+ end
605
+ end
606
+
607
+ # skipped most of the the rest - i.e. we're missing implementation of BYSETPOS and less-than-a-day recurrence rules
608
+
609
+ describe "ignoring an invalid date (i.e., February 30)" do
610
+ before do
611
+ @schedule = Schedule.new Time.local(2007, 1, 15, 9), Rule.monthly.monthdays(15, 30).count(5)
612
+ end
613
+
614
+ it "should list occurrences" do
615
+ expected = [[1, 15], [1, 30], [2, 15], [3, 15], [3, 30]].map {|d| Time.local(2007, d[0], d[1], 9) }
616
+
617
+ @schedule.occurrences.should == expected
618
+ end
619
+ end
620
+ end
621
+
622
+ describe "serialization" do
623
+ before do
624
+ @schedule = Schedule.new(Time.local(2000, 1, 1, 10), Rule.monthly.monthdays(1).count(10))
625
+ @schedule.end_time = (Time.local(2000, 1, 1, 10, 10))
626
+ end
627
+
628
+ it "should do a hash round trip" do
629
+ h = @schedule.to_hash
630
+ s = Schedule.from_hash(h)
631
+
632
+ s.start_time.should == @schedule.start_time
633
+ s.end_time.should == @schedule.end_time
634
+
635
+ s.rule.class.should == @schedule.rule.class
636
+ s.rule.step.should == @schedule.rule.step
637
+ end
638
+
639
+ it "should do a JSON round trip" do
640
+ j = @schedule.to_json
641
+ s = Schedule.from_json(j)
642
+
643
+ s.start_time.should == @schedule.start_time
644
+ s.end_time.should == @schedule.end_time
645
+
646
+ s.rule.class.should == @schedule.rule.class
647
+ s.rule.step.should == @schedule.rule.step
648
+ end
649
+ end
650
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'cyclical'
5
+ include Cyclical
6
+
7
+ RSpec.configure do |config|
8
+ config.mock_with :rspec
9
+ end
10
+