943e9c58833f148109b7ad7f80c1dfa2e9635a61
[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, message=None):
13         self.tasks=tasks
14         self.verbose=verbose
15         self.message="({})".format(message) if message else ""
16     def run (self, timeout_timedelta, silent_timedelta, period):
17         begin = datetime.now()
18         timeout = begin+timeout_timedelta
19         timeout_seconds = timeout_timedelta.total_seconds()
20         timeout_minutes = timeout_seconds/60
21         graceout = datetime.now()+silent_timedelta
22         silent_seconds = silent_timedelta.total_seconds()
23         silent_minutes = silent_seconds/60
24         period_seconds=int(period.total_seconds())
25         if self.verbose:
26             if timeout_seconds >= 120:
27                 utils.header("Completer [%d tasks]: max timeout is %d minutes, "
28                              "silent for %d minutes (period is %s s)"%\
29                              (len(self.tasks), timeout_minutes,
30                               silent_minutes, period_seconds))
31             else:
32                 utils.header("Completer [%d tasks]: max timeout is %d seconds, "
33                              "silent for %d seconds (period is %s s)"%\
34                              (len(self.tasks), timeout_seconds,
35                               silent_seconds, period_seconds))
36         tasks=self.tasks
37         while tasks:
38             fine=[]
39             for task in tasks:
40                 success=task.run (silent=datetime.now() <= graceout)
41                 if success: fine.append(task)
42             for task in fine: tasks.remove(task)
43             if not tasks:
44                 if self.verbose:
45                     duration = datetime.now()-begin
46                     print "total completer {} {}s".format(self.message,
47                                                           int(duration.total_seconds()))
48                 return True
49             if datetime.now() > timeout:
50                 for task in tasks: 
51                     task.failure_epilogue()
52                 return False
53             if self.verbose:
54                 print '%ds..'%period_seconds,
55             time.sleep(period_seconds)
56         # in case we're empty 
57         return True
58
59
60 #################### CompleterTask
61 ### . run(silent)  (return True or False)
62 ###   silent is an input boolean indicating if we're within the silent period
63 ### . failure()    (print a message)
64
65 ########## expectations (+ first arg self)
66 # failure()     (to describe which went wrong once it's over)
67 # -- and --
68 # run (silent)  
69 # -- or -- 
70 # actual_run()
71 # message()
72
73 class CompleterTask:
74     def run (self, silent):
75         result=self.actual_run()
76         if silent:
77             print '+' if result else '.',
78             sys.stdout.flush()
79         else:
80             print self.message(),"->","OK" if result else "KO"
81         return result
82     def message (self): return "you-need-to-redefine-message"
83     def failure_epilogue (self): print "you-need-to-redefine-failure_epilogue"
84
85 # random result
86 class TaskTest (CompleterTask):
87     counter=1
88     def __init__ (self,max):
89         import random
90         self.counter=TaskTest.counter
91         TaskTest.counter+=1
92         self.delay=random.random()*max
93         self.fire=datetime.now()+timedelta(seconds=self.delay)
94     def actual_run(self):
95         return datetime.now()>=self.fire
96     def message (self):
97         return "Task %d - delay was %d s"%(self.counter,self.delay)
98
99     def failure_epilogue (self): print "BOTTOM LINE: FAILURE with task (%s)"%self.counter
100
101 def main ():
102     import sys
103     if len(sys.argv)!=6:
104         print "Usage: <command> number_tasks max_random timeout_s silent_s period_s"
105         sys.exit(1)
106     [number,max,timeout,silent,period]= [ int(x) for x in sys.argv[1:]]
107     tasks = [ TaskTest(max) for i in range(number)]
108     success=Completer(tasks,verbose=True).run(timedelta(seconds=timeout),
109                                               timedelta(seconds=silent),
110                                               timedelta(seconds=period))
111     print "OVERALL",success
112
113 if __name__ == '__main__':
114     main()