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