cyclical 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+