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