Contact: fumanchu@aminus.org

Log in as guest/misc to create tickets

root/test_recur.py

Revision 105 (checked in by dowski, 5 years ago)

A couple changes to make Workers and Schedulers more robust.

1. Worker.advance() now correctly handles self.recurrence == None.
2. Scheduler.stop() now correctly handles self.curthread == None

  • Property svn:eol-style set to native
Line 
1 """Test recur.py"""
2
3 import os
4 import recur
5 import time
6 import unittest
7
8 import datetime
9 dtd = datetime.date
10 dtdt = datetime.datetime
11 dtt = datetime.time
12
13
14 class RecurTest(unittest.TestCase):
15    
16     def test_0_sane_date(self):
17         # Sane dates
18         self.assertEqual(recur.sane_date(1999, 12, 31), dtd(1999, 12, 31))
19         self.assertEqual(recur.sane_date(2000, 1, 1), dtd(2000, 1, 1))
20         self.assertEqual(recur.sane_date(2004, 5, 4), dtd(2004, 5, 4))
21         # Insane dates
22         self.assertEqual(recur.sane_date(1999, 12, 0), dtd(1999, 11, 30))
23         self.assertEqual(recur.sane_date(2000, 1, -1), dtd(1999, 12, 30))
24         self.assertEqual(recur.sane_date(2004, 5, -4), dtd(2004, 4, 26))
25         self.assertEqual(recur.sane_date(1999, 12, 40), dtd(2000, 1, 9))
26         self.assertEqual(recur.sane_date(2000, 1, 81), dtd(2000, 3, 21))
27         self.assertEqual(recur.sane_date(2004, 5, 365), dtd(2005, 4, 30))
28         self.assertEqual(recur.sane_date(1999, 14, 28, True), dtd(2000, 2, 28))
29         self.assertEqual(recur.sane_date(1999, -10, 28, True), dtd(1998, 2, 28))
30    
31     def test_1_sane_time(self):
32         # Sane times
33         self.assertEqual(recur.sane_time(0, 6, 0, 0), (0, dtt(6, 0, 0)))
34         self.assertEqual(recur.sane_time(1, 3, 59, 5), (1, dtt(3, 59, 5)))
35         self.assertEqual(recur.sane_time(5, 16, 0, 10), (5, dtt(16, 0, 10)))
36         # Insane times
37         self.assertEqual(recur.sane_time(0, 26, 0, 0), (1, dtt(2, 0, 0)))
38         self.assertEqual(recur.sane_time(0, 6, 80, 10), (0, dtt(7, 20, 10)))
39         self.assertEqual(recur.sane_time(0, 0, 0, -1), (-1, dtt(23, 59, 59)))
40         self.assertEqual(recur.sane_time(0, 23, 59, 64), (1, dtt(0, 0, 4)))
41         self.assertEqual(recur.sane_time(-4, -2, 0, 0), (-5, dtt(22, 0, 0)))
42    
43     def test_seconds(self):
44         dates = recur.seconds(dtdt(1999, 12, 28, 23, 59), 10, dtdt(1999, 12, 29, 0))
45         self.assertEqual([x for x in dates],
46                          [dtdt(1999, 12, 28, 23, 59, 0),
47                           dtdt(1999, 12, 28, 23, 59, 10),
48                           dtdt(1999, 12, 28, 23, 59, 20),
49                           dtdt(1999, 12, 28, 23, 59, 30),
50                           dtdt(1999, 12, 28, 23, 59, 40),
51                           dtdt(1999, 12, 28, 23, 59, 50),
52                           dtdt(1999, 12, 29, 0, 0, 0),
53                           ]
54                          )
55    
56     def test_eachminute(self):
57         dates = recur.eachminute(dtdt(1999, 1, 1, 1, 55), 15,
58                                  dtdt(1999, 1, 1, 2, 2))
59         self.assertEqual([x for x in dates],
60                          [dtdt(1999, 1, 1, 1, 55, 15),
61                           dtdt(1999, 1, 1, 1, 56, 15),
62                           dtdt(1999, 1, 1, 1, 57, 15),
63                           dtdt(1999, 1, 1, 1, 58, 15),
64                           dtdt(1999, 1, 1, 1, 59, 15),
65                           dtdt(1999, 1, 1, 2, 0, 15),
66                           dtdt(1999, 1, 1, 2, 1, 15),
67                           ]
68                          )
69    
70     def test_minutes(self):
71         dates = recur.minutes(dtdt(1999, 12, 28, 23), 15, dtdt(1999, 12, 29, 1))
72         self.assertEqual([x for x in dates],
73                          [dtdt(1999, 12, 28, 23),
74                           dtdt(1999, 12, 28, 23, 15),
75                           dtdt(1999, 12, 28, 23, 30),
76                           dtdt(1999, 12, 28, 23, 45),
77                           dtdt(1999, 12, 29, 0, 0),
78                           dtdt(1999, 12, 29, 0, 15),
79                           dtdt(1999, 12, 29, 0, 30),
80                           dtdt(1999, 12, 29, 0, 45),
81                           dtdt(1999, 12, 29, 1, 0),
82                           ]
83                          )
84    
85     def test_eachhour(self):
86         dates = recur.eachhour(dtdt(1999, 12, 29, 22), 59, 59,
87                                dtdt(1999, 12, 30, 5))
88         self.assertEqual([x for x in dates],
89                          [dtdt(1999, 12, 29, 22, 59, 59),
90                           dtdt(1999, 12, 29, 23, 59, 59),
91                           dtdt(1999, 12, 30, 0, 59, 59),
92                           dtdt(1999, 12, 30, 1, 59, 59),
93                           dtdt(1999, 12, 30, 2, 59, 59),
94                           dtdt(1999, 12, 30, 3, 59, 59),
95                           dtdt(1999, 12, 30, 4, 59, 59),
96                           ]
97                          )
98    
99     def test_hours(self):
100         dates = recur.hours(dtd(1999, 12, 28), 4, dtd(1999, 12, 29))
101         self.assertEqual([x for x in dates],
102                          [dtdt(1999, 12, 28, 0),
103                           dtdt(1999, 12, 28, 4),
104                           dtdt(1999, 12, 28, 8),
105                           dtdt(1999, 12, 28, 12),
106                           dtdt(1999, 12, 28, 16),
107                           dtdt(1999, 12, 28, 20),
108                           dtdt(1999, 12, 29, 0),
109                           dtdt(1999, 12, 29, 4),
110                           dtdt(1999, 12, 29, 8),
111                           dtdt(1999, 12, 29, 12),
112                           dtdt(1999, 12, 29, 16),
113                           dtdt(1999, 12, 29, 20),
114                           ]
115                          )
116         # Test a startDate and endDate with a time component.
117         dates = recur.hours(dtdt(1999, 12, 28, 9), 4, dtdt(1999, 12, 29, 7))
118         self.assertEqual([x for x in dates],
119                          [dtdt(1999, 12, 28, 9),
120                           dtdt(1999, 12, 28, 13),
121                           dtdt(1999, 12, 28, 17),
122                           dtdt(1999, 12, 28, 21),
123                           dtdt(1999, 12, 29, 1),
124                           dtdt(1999, 12, 29, 5),
125                           ]
126                          )
127    
128     def test_time_from_str(self):
129         # Valid values
130         self.assertEqual(recur.time_from_str("4:03:18"), dtt(4, 3, 18))
131         self.assertEqual(recur.time_from_str("14:5:48"), dtt(14, 5, 48))
132         self.assertEqual(recur.time_from_str("0"), dtt(0))
133         self.assertEqual(recur.time_from_str("0:0:0"), dtt(0))
134         self.assertEqual(recur.time_from_str("23:59:59"), dtt(23, 59, 59))
135        
136         # Invalid values
137         self.assertRaises(ValueError, recur.time_from_str, "28:59:59")
138         self.assertRaises(ValueError, recur.time_from_str, "no:time:to:lose")
139    
140     def test_eachday(self):
141         dates = recur.eachday(dtd(1999, 12, 28), dtt(6, 43, 21),
142                               dtd(2000, 1, 3))
143         self.assertEqual([x for x in dates],
144                          [dtdt(1999, 12, 28, 6, 43, 21),
145                           dtdt(1999, 12, 29, 6, 43, 21),
146                           dtdt(1999, 12, 30, 6, 43, 21),
147                           dtdt(1999, 12, 31, 6, 43, 21),
148                           dtdt(2000, 1, 1, 6, 43, 21),
149                           dtdt(2000, 1, 2, 6, 43, 21),
150                           dtdt(2000, 1, 3, 6, 43, 21),
151                           ]
152                          )
153         # Test start and end dates that have time components.
154         # If 'inside' the given time, those extrema should
155         # not be included.
156         dates = recur.eachday(dtdt(1999, 12, 28, 8), dtt(6, 43, 21),
157                               dtdt(2000, 1, 3, 2))
158         self.assertEqual([x for x in dates],
159                          [dtdt(1999, 12, 29, 6, 43, 21),
160                           dtdt(1999, 12, 30, 6, 43, 21),
161                           dtdt(1999, 12, 31, 6, 43, 21),
162                           dtdt(2000, 1, 1, 6, 43, 21),
163                           dtdt(2000, 1, 2, 6, 43, 21),
164                           ]
165                          )
166    
167     def test_days(self):
168         dates = recur.days(dtd(1999, 12, 28), 1, dtd(2000, 1, 3))
169         self.assertEqual([x for x in dates],
170                          [dtd(1999, 12, 28),
171                           dtd(1999, 12, 29),
172                           dtd(1999, 12, 30),
173                           dtd(1999, 12, 31),
174                           dtd(2000, 1, 1),
175                           dtd(2000, 1, 2),
176                           dtd(2000, 1, 3),
177                           ]
178                          )
179         dates = recur.days(dtd(1999, 12, 28), 3, dtd(2000, 1, 3))
180         self.assertEqual([x for x in dates],
181                          [dtd(1999, 12, 28),
182                           dtd(1999, 12, 31),
183                           dtd(2000, 1, 3),
184                           ]
185                          )
186    
187     def test_eachweekday(self):
188         dates = recur.eachweekday(dtd(1999, 12, 28), 0, dtt(6, 43, 21),
189                                   dtd(2000, 1, 30))
190         self.assertEqual([x for x in dates],
191                          [dtdt(2000, 1, 3, 6, 43, 21),
192                           dtdt(2000, 1, 10, 6, 43, 21),
193                           dtdt(2000, 1, 17, 6, 43, 21),
194                           dtdt(2000, 1, 24, 6, 43, 21),
195                           ]
196                          )
197         # Test start and end dates that have time components.
198         # If 'inside' the given time, those extrema should
199         # not be included.
200         dates = recur.eachweekday(dtdt(2000, 1, 3, 8), 0, dtt(6, 43, 21),
201                                   dtdt(2000, 1, 31, 2))
202         self.assertEqual([x for x in dates],
203                          [dtdt(2000, 1, 10, 6, 43, 21),
204                           dtdt(2000, 1, 17, 6, 43, 21),
205                           dtdt(2000, 1, 24, 6, 43, 21),
206                           ]
207                          )
208    
209     def test_eachweek(self):
210         knowndates = [dtd(1999, 12, 31),
211                       dtd(2000, 1, 7),
212                       dtd(2000, 1, 14),
213                       dtd(2000, 1, 21),
214                       dtd(2000, 1, 28),
215                       dtd(2000, 2, 4),
216                       dtd(2000, 2, 11),
217                       ]
218        
219         dates = recur.eachweek(dtd(1999, 12, 28), 4, dtd(2000, 2, 13))
220         self.assertEqual([x for x in dates], knowndates)
221        
222         # Try a weekday that is out of bounds.
223         dates = recur.eachweek(dtd(1999, 12, 28), 11, dtd(2000, 2, 13))
224         self.assertEqual([x for x in dates], knowndates)
225    
226     def test_weeks(self):
227         dates = recur.weeks(dtd(1999, 12, 28), 1, dtd(2000, 2, 13))
228         self.assertEqual([x for x in dates],
229                          [dtd(1999, 12, 28),
230                           dtd(2000, 1, 4),
231                           dtd(2000, 1, 11),
232                           dtd(2000, 1, 18),
233                           dtd(2000, 1, 25),
234                           dtd(2000, 2, 1),
235                           dtd(2000, 2, 8),
236                           ]
237                          )
238         dates = recur.weeks(dtd(1999, 12, 28), 3, dtd(2000, 2, 13))
239         self.assertEqual([x for x in dates],
240                          [dtd(1999, 12, 28),
241                           dtd(2000, 1, 18),
242                           dtd(2000, 2, 8),
243                           ]
244                          )
245    
246     def test_eachmonth(self):
247         knowndates = [dtd(1999, 12, 20),
248                       dtd(2000, 1, 20),
249                       dtd(2000, 2, 20),
250                       dtd(2000, 3, 20),
251                       dtd(2000, 4, 20),
252                       dtd(2000, 5, 20),
253                       dtd(2000, 6, 20),
254                       ]
255         dates = recur.eachmonth(dtd(1999, 11, 28), 20, dtd(2000, 7, 13))
256         self.assertEqual([x for x in dates], knowndates)
257        
258         # Try a day that is out of bounds.
259         knowndates = [datetime.date(1999, 12, 10),
260                       datetime.date(2000, 2, 9),
261                       datetime.date(2000, 4, 9),
262                       datetime.date(2000, 6, 9)]
263         dates = recur.eachmonth(dtd(1999, 11, 28), 40, dtd(2000, 7, 13))
264         self.assertEqual([x for x in dates], knowndates)
265    
266     def test_months(self):
267         dates = recur.months(dtd(1999, 12, 31), 1, dtd(2000, 7, 13))
268         self.assertEqual([x for x in dates],
269                          [dtd(1999, 12, 31),
270                           dtd(2000, 1, 31),
271                           dtd(2000, 3, 2),
272                           dtd(2000, 3, 31),
273                           dtd(2000, 5, 1),
274                           dtd(2000, 5, 31),
275                           dtd(2000, 7, 1),
276                           ]
277                          )
278         dates = recur.months(dtd(1999, 12, 31), 3, dtd(2000, 7, 13))
279         self.assertEqual([x for x in dates],
280                          [dtd(1999, 12, 31),
281                           dtd(2000, 3, 31),
282                           dtd(2000, 7, 1),
283                           ]
284                          )
285    
286     def test_eachyear(self):
287         knowndates = [dtd(1999, 3, 1),
288                       dtd(2000, 2, 29),
289                       dtd(2001, 3, 1),
290                       dtd(2002, 3, 1),
291                       dtd(2003, 3, 1),
292                       dtd(2004, 2, 29),
293                       dtd(2005, 3, 1),
294                       ]
295         dates = recur.eachyear(dtd(1998, 11, 28), 2, 29, dtd(2005, 7, 13))
296         self.assertEqual([x for x in dates], knowndates)
297        
298         # Try a day that is out of bounds.
299         dates = recur.eachyear(dtd(1998, 11, 28), 1, 60, dtd(2005, 7, 13))
300         self.assertEqual([x for x in dates], knowndates)
301        
302         # Try a negative month.
303         dates = recur.eachyear(dtd(1998, 11, 28), -10, 29, dtd(2005, 7, 13))
304         for x, y in enumerate(dates):
305             self.assertEqual(y, knowndates[x])
306    
307     def test_years(self):
308         dates = recur.years(dtd(1999, 12, 31), 1, dtd(2006, 7, 13))
309         self.assertEqual([x for x in dates],
310                          [dtd(1999, 12, 31),
311                           dtd(2000, 12, 31),
312                           dtd(2001, 12, 31),
313                           dtd(2002, 12, 31),
314                           dtd(2003, 12, 31),
315                           dtd(2004, 12, 31),
316                           dtd(2005, 12, 31),
317                           ]
318                          )
319         dates = recur.years(dtd(2000, 2, 29), 3, dtd(2012, 7, 13))
320         self.assertEqual([x for x in dates],
321                          [dtd(2000, 2, 29),
322                           dtd(2003, 3, 1),
323                           dtd(2006, 3, 1),
324                           dtd(2009, 3, 1),
325                           dtd(2012, 2, 29),
326                           ]
327                          )
328    
329     def test_Recurrence(self):
330         # Test seconds
331         n = datetime.datetime.now()
332         dates = list(recur.Recurrence(n, "1 second",
333                                       n + datetime.timedelta(seconds=5)))
334         knowndates = [n + datetime.timedelta(seconds=x) for x in xrange(6)]
335         self.assertEqual(dates, knowndates)
336        
337         # Test eachday
338         knowndates = [dtdt(600, 1, 1, 6, 35),
339                       dtdt(600, 1, 2, 6, 35),
340                       dtdt(600, 1, 3, 6, 35),
341                       dtdt(600, 1, 4, 6, 35),
342                       dtdt(600, 1, 5, 6, 35),
343                       ]
344         dates = recur.Recurrence(dtd(600, 1, 1), "6:35 every day", dtd(600, 1, 5))
345         self.assertEqual([x for x in dates], knowndates)
346        
347         # Test eachweekday
348         knowndates = [dtdt(1999, 12, 28, 6, 35),
349                       dtdt(2000, 1, 4, 6, 35),
350                       dtdt(2000, 1, 11, 6, 35),
351                       dtdt(2000, 1, 18, 6, 35),
352                       dtdt(2000, 1, 25, 6, 35),
353                      ]
354         dates = recur.Recurrence(dtd(1999, 12, 28), "6:35 every tuesday", dtd(2000, 1, 30))
355         self.assertEqual([x for x in dates], knowndates)
356        
357         # Test eachweek
358         knowndates = [dtd(1999, 12, 31),
359                       dtd(2000, 1, 7),
360                       dtd(2000, 1, 14),
361                       dtd(2000, 1, 21),
362                       dtd(2000, 1, 28),
363                       dtd(2000, 2, 4),
364                       dtd(2000, 2, 11),
365                       ]
366         dates = recur.Recurrence(dtd(1999, 12, 28), "fri", dtd(2000, 2, 13))
367         self.assertEqual([x for x in dates], knowndates)
368         dates = recur.Recurrence(dtd(1999, 12, 28), "friday", dtd(2000, 2, 13))
369         self.assertEqual([x for x in dates], knowndates)
370        
371         # Test eachmonth
372         knowndates = [dtd(1999, 12, 20),
373                       dtd(2000, 1, 20),
374                       dtd(2000, 2, 20),
375                       dtd(2000, 3, 20),
376                       dtd(2000, 4, 20),
377                       dtd(2000, 5, 20),
378                       dtd(2000, 6, 20),
379                       ]
380         dates = recur.Recurrence(dtd(1999, 11, 28), "20 each month", dtd(2000, 7, 13))
381         self.assertEqual([x for x in dates], knowndates)
382        
383         # Test negative eachmonth
384         knowndates = [dtd(1999, 12, 26),
385                       dtd(2000, 1, 26),
386                       dtd(2000, 2, 24),
387                       dtd(2000, 3, 26),
388                       dtd(2000, 4, 25),
389                       dtd(2000, 5, 26),
390                       dtd(2000, 6, 25),
391                       ]
392         dates = recur.Recurrence(dtd(1999, 11, 28), "-5 each month", dtd(2000, 7, 13))
393         self.assertEqual([x for x in dates], knowndates)
394        
395         # Test eachyear
396         knowndates = [dtd(1999, 3, 1),
397                       dtd(2000, 2, 29),
398                       dtd(2001, 3, 1),
399                       dtd(2002, 3, 1),
400                       dtd(2003, 3, 1),
401                       dtd(2004, 2, 29),
402                       dtd(2005, 3, 1),
403                       ]
404         dates = recur.Recurrence(dtd(1998, 11, 28), "Feb 29", dtd(2005, 7, 13))
405         self.assertEqual([x for x in dates], knowndates)
406        
407         # Make sure Recurrence objects are unique.
408         dates2 = recur.Recurrence(dtd(1998, 11, 28), "Feb 29", dtd(2005, 7, 13))
409         self.assertNotEqual(dates, dates2)
410         self.assertNotEqual(dates.generator, dates2.generator)
411        
412         # Try a "years" test
413         dates = recur.Recurrence(dtd(1999, 12, 31), "1 year", dtd(2006, 7, 13))
414         self.assertEqual([x for x in dates],
415                          [dtd(1999, 12, 31),
416                           dtd(2000, 12, 31),
417                           dtd(2001, 12, 31),
418                           dtd(2002, 12, 31),
419                           dtd(2003, 12, 31),
420                           dtd(2004, 12, 31),
421                           dtd(2005, 12, 31),
422                           ]
423                          )
424        
425         # Try a non-matching recurrence.
426         self.assertRaises(ValueError, recur.Recurrence,
427                           dtd(1998, 11, 28), "Umptember 29",
428                           dtd(2005, 7, 13))
429    
430     def test_Workers(self):
431         count = [0]
432         class Counter(recur.Worker):
433             def work(self):
434                 count[0] += 1
435                 import time
436        
437         another = [0]
438         class AnotherCounter(recur.Worker):
439             def work(self):
440                 another[0] += 1
441        
442         paused = [0]
443         class PausedWorker(recur.Worker):
444             """A worker that starts paused (active==False)."""
445             def __init__(self, *args, **params):
446                 recur.Worker.__init__(self, *args, **params)
447                 self.active = False
448            
449             def work(self):
450                 # This worker starts paused (inactive) and shouldn't do
451                 # any work
452                 paused[0] += 1
453        
454         # the aptly named...
455         class WorkerNotAppearingInThisFilm(recur.Worker):
456             def work(self):
457                 pass
458        
459         s = recur.Scheduler({'mycounter': Counter("1 second")})
460         s.start()
461         time.sleep(2.5)
462         s.stop()
463         # count == 3 because the first recurrence value == now.
464         self.assertEqual(count[0], 3)
465        
466         s = recur.Scheduler({'c1': Counter("1 second"),
467                              'c2': AnotherCounter("2 seconds"),
468                              # A worker that starts paused (inactive).
469                              'c3': PausedWorker("1 second"),
470                              # A worker with an empty recurrence string.
471                              # This is allowed in __init__, and sets
472                              # self.recurrence to None.
473                              'c4': WorkerNotAppearingInThisFilm("")})
474         s.start()
475         try:
476             # Here's how this should work:
477             #   elapsed time     count    another
478             #       0.0            +         +
479             #       0.5           =4        =1
480             #       1.0            +
481             #       1.5           =5        =1  pause
482             #       2.0            X         X
483             #       2.5           =5        =1  resume
484             #       3.0            +
485             #       3.5           =6        =1
486             #       4.0            +         +
487             #       4.5           =7        =2
488             time.sleep(0.5)
489             self.assertEqual(count[0], 4)
490             self.assertEqual(another[0], 1)
491             time.sleep(1)
492             self.assertEqual(count[0], 5)
493             self.assertEqual(another[0], 1)
494            
495             s.paused = True
496             time.sleep(1)
497             self.assertEqual(count[0], 5)
498             self.assertEqual(another[0], 1)
499             s.paused = False
500            
501             time.sleep(1)
502             self.assertEqual(count[0], 6)
503             # We skipped "another" when we paused, above.
504             self.assertEqual(another[0], 1)
505            
506             time.sleep(1)
507             self.assertEqual(count[0], 7)
508             self.assertEqual(another[0], 2)
509            
510             # Make sure the PausedWorker hasn't done anything
511             self.assertEqual(paused[0], 0)
512            
513         finally:
514             s.stop()
515        
516         # Test a Scheduler with a single Worker with an empty recurrence string
517         s = recur.Scheduler({'w': WorkerNotAppearingInThisFilm("")})
518         s.start()
519         time.sleep(0.5)
520         s.stop()
521
522 if __name__ == "__main__":
523     unittest.main()
Note: See TracBrowser for help on using the browser.