155397e29e88471bb7d5dbbc49ddcebefbabf1a8
[tests.git] / system / Completer.py
1 #!/usr/bin/env python
2 import sys, time
3 from datetime import datetime, timedelta
4
5 import utils
6
7 ### more generic code for waiting for any number of things 
8 ### within a given timeframe i.e. given some timeout/silent/period
9 ### takes in argument a list of tasks that are instances 
10 ### of a CompleterTask subclass
11 class Completer:
12     def __init__ (self, tasks, verbose=True):
13         self.tasks=tasks
14         self.verbose=verbose
15     def run (self, timeout_timedelta, silent_timedelta, period=None):
16         timeout = datetime.now()+timeout_timedelta
17         timeout_minutes = timeout_timedelta.total_seconds()/60
18         graceout = datetime.now()+silent_timedelta
19         silent_minutes = silent_timedelta.total_seconds()/60
20         period_seconds=int(period.total_seconds())
21         if self.verbose:
22             utils.header("max timeout is %d minutes, silent for %d minutes (period is %s s)"%\
23                              (timeout_minutes,silent_minutes,period_seconds))
24         tasks=self.tasks
25         while tasks:
26             fine=[]
27             for task in tasks:
28                 success=task.run (silent=datetime.now() <= graceout)
29                 if success: fine.append(task)
30             for task in fine: tasks.remove(task)
31             if not tasks: return True
32             if datetime.now() > timeout:
33                 for task in tasks: 
34                     print task.failure_message()
35                     task.failure_epilogue()
36                 return False
37             if self.verbose:
38                 print '%ds..'%period_seconds,
39             time.sleep(period_seconds)
40         # in case we're empty 
41         return True
42
43
44 #################### CompleterTask
45 ### . run(silent)  (return True or False)
46 ###   silent is an input boolean indicating if we're within the silent period
47 ### . failure()    (print a message)
48
49 ########## expectations (+ first arg self)
50 # failure()     (to describe which went wrong once it's over)
51 # -- and --
52 # run (silent)  
53 # -- or -- 
54 # actual_run()
55 # message()
56
57 class CompleterTask:
58     def run (self, silent):
59         result=self.actual_run()
60         if silent:
61             print '+' if result else '.',
62             sys.stdout.flush()
63         else:
64             print self.message(),"->","OK" if result else "KO"
65         return result
66     def message (self): return "you-need-to-redefine-message"
67     def failure_message (self): return "you-need-to-redefine-failure_message"
68     def failure_epilogue (self): pass
69
70 # random result
71 class TaskTest (CompleterTask):
72     counter=1
73     def __init__ (self,max):
74         import random
75         self.counter=TaskTest.counter
76         TaskTest.counter+=1
77         self.delay=random.random()*max
78         self.fire=datetime.now()+timedelta(seconds=self.delay)
79     def actual_run(self):
80         return datetime.now()>=self.fire
81     def message (self):
82         return "Task %d - delay was %d s"%(self.counter,self.delay)
83
84     def failure_message (self): return "BOTTOM LINE: FAILURE with task (%s)"%self.counter
85
86 def main ():
87     import sys
88     if len(sys.argv)!=6:
89         print "Usage: <command> number_tasks max_random timeout_s silent_s period_s"
90         sys.exit(1)
91     [number,max,timeout,silent,period]= [ int(x) for x in sys.argv[1:]]
92     tasks = [ TaskTest(max) for i in range(number)]
93     success=Completer(tasks,verbose=True).run(timedelta(seconds=timeout),
94                                               timedelta(seconds=silent),
95                                               timedelta(seconds=period))
96     print "OVERALL",success
97
98 if __name__ == '__main__':
99     main()