31 "Save": ("s",sikuli.Key.CTRL), |
32 "Save": ("s",sikuli.Key.CTRL), |
32 "New": ("n",sikuli.Key.CTRL), |
33 "New": ("n",sikuli.Key.CTRL), |
33 "Address": ("l",sikuli.Key.CTRL)} # to reach address bar in GTK's file selector |
34 "Address": ("l",sikuli.Key.CTRL)} # to reach address bar in GTK's file selector |
34 |
35 |
35 def __init__(self, app): |
36 def __init__(self, app): |
36 self.app = app.sikuliapp |
37 self.app = app |
37 |
38 |
38 def __getattr__(self, name): |
39 def __getattr__(self, name): |
39 fkey = self.fkeys[name] |
40 fkey = self.fkeys[name] |
40 if type(fkey) != tuple: |
41 if type(fkey) != tuple: |
41 fkey = (fkey,) |
42 fkey = (fkey,) |
42 app = self.app |
|
43 |
43 |
44 def PressShortCut(): |
44 def PressShortCut(): |
45 app.focus() |
45 self.app.sikuliapp.focus() |
46 sikuli.type(*fkey) |
46 sikuli.type(*fkey) |
|
47 self.app.ReportText("Sending " + name + " shortcut") |
47 |
48 |
48 return PressShortCut |
49 return PressShortCut |
49 |
50 |
50 |
51 |
51 class IDEIdleObserver: |
52 class IDEIdleObserver: |
124 Parameters: |
128 Parameters: |
125 timeout (int): how long to wait for change, in seconds |
129 timeout (int): how long to wait for change, in seconds |
126 """ |
130 """ |
127 start_time = timesec() |
131 start_time = timesec() |
128 |
132 |
129 if self.event.wait(timeout): |
133 wait_result = self.event.wait(timeout) |
|
134 |
|
135 self.ReportScreenShot("stdout changed" if wait_result else "stdout didn't change") |
|
136 |
|
137 if wait_result: |
130 self.event.clear() |
138 self.event.clear() |
131 else: |
139 else: |
132 raise Exception("Stdout didn't become active before timeout") |
140 raise Exception("Stdout didn't become active before timeout") |
133 |
141 |
134 self.waitIdleStdout(period, timeout - (timesec() - start_time)) |
142 self.waitIdleStdout(period, timeout - (timesec() - start_time)) |
146 if self.event.wait(period): |
154 if self.event.wait(period): |
147 # no timeout -> got event -> not idle -> loop again |
155 # no timeout -> got event -> not idle -> loop again |
148 self.event.clear() |
156 self.event.clear() |
149 else: |
157 else: |
150 # timeout -> no event -> idle -> exit |
158 # timeout -> no event -> idle -> exit |
|
159 self.ReportScreenShot("stdout is idle") |
151 return True |
160 return True |
|
161 |
|
162 self.ReportScreenShot("stdout did not idle") |
152 |
163 |
153 raise Exception("Stdout did not idle before timeout") |
164 raise Exception("Stdout did not idle before timeout") |
154 |
165 |
155 def waitPatternInStdout(self, pattern, timeout, count=1): |
166 def waitPatternInStdout(self, pattern, timeout, count=1): |
156 found = 0 |
167 found = 0 |
182 exemple (str): path relative to exemples directory |
194 exemple (str): path relative to exemples directory |
183 |
195 |
184 Returns: |
196 Returns: |
185 Sikuli App class instance |
197 Sikuli App class instance |
186 """ |
198 """ |
|
199 |
|
200 self.screenshotnum = 0 |
|
201 self.starttime = timesec() |
|
202 self.screen = sikuli.Screen() |
|
203 |
|
204 self.report = open("report.html", "w") |
|
205 self.report.write("""<!doctype html> |
|
206 <html> |
|
207 <head> |
|
208 <meta charset="utf-8"> |
|
209 <meta name="color-scheme" content="light dark"> |
|
210 <title>Test report</title> |
|
211 </head> |
|
212 <body> |
|
213 """) |
187 |
214 |
188 command = [python_bin, opj(beremiz_path,"Beremiz.py"), "--log=/dev/stdout"] |
215 command = [python_bin, opj(beremiz_path,"Beremiz.py"), "--log=/dev/stdout"] |
189 |
216 |
190 if exemple is not None: |
217 if exemple is not None: |
191 command.append(opj(beremiz_path,"exemples",exemple)) |
218 command.append(opj(beremiz_path,"exemples",exemple)) |
195 # App class is broken in Sikuli 2.0.5: can't start process with arguments. |
222 # App class is broken in Sikuli 2.0.5: can't start process with arguments. |
196 # |
223 # |
197 # Workaround : - use subprocess module to spawn IDE process, |
224 # Workaround : - use subprocess module to spawn IDE process, |
198 # - use wmctrl to find IDE window details and maximize it |
225 # - use wmctrl to find IDE window details and maximize it |
199 # - pass exact window title to App class constructor |
226 # - pass exact window title to App class constructor |
|
227 |
|
228 self.ReportText("Launching " + repr(command)) |
200 |
229 |
201 self.proc = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=0) |
230 self.proc = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=0) |
202 |
231 |
203 # Window are macthed against process' PID |
232 # Window are macthed against process' PID |
204 ppid = self.proc.pid |
233 ppid = self.proc.pid |
234 |
263 |
235 IDEIdleObserver.__init__(self) |
264 IDEIdleObserver.__init__(self) |
236 stdoutIdleObserver.__init__(self) |
265 stdoutIdleObserver.__init__(self) |
237 |
266 |
238 # stubs for common sikuli calls to allow adding hooks later |
267 # stubs for common sikuli calls to allow adding hooks later |
239 for n in ["click","doubleClick","type"]: |
268 for name in ["click","doubleClick","type"]: |
240 setattr(self, n, getattr(sikuli, n)) |
269 def makeMyMeth(n): |
|
270 def myMeth(*args, **kwargs): |
|
271 getattr(sikuli, n)(*args, **kwargs) |
|
272 self.ReportScreenShot(n + "(" + repr(args) + "," + repr(kwargs) + ")") |
|
273 return myMeth |
|
274 setattr(self, name, makeMyMeth(name)) |
241 |
275 |
242 def close(self): |
276 def close(self): |
243 self.sikuliapp.close() |
277 self.sikuliapp.close() |
244 self.sikuliapp = None |
278 self.sikuliapp = None |
|
279 self.report.write(""" |
|
280 </body> |
|
281 </html>""") |
|
282 self.report.close() |
245 |
283 |
246 def __del__(self): |
284 def __del__(self): |
247 if self.sikuliapp is not None: |
285 if self.sikuliapp is not None: |
248 self.sikuliapp.close() |
286 self.sikuliapp.close() |
249 IDEIdleObserver.__del__(self) |
287 IDEIdleObserver.__del__(self) |
250 stdoutIdleObserver.__del__(self) |
288 stdoutIdleObserver.__del__(self) |
251 |
289 |
|
290 def ReportScreenShot(self, msg): |
|
291 elapsed = "%.3fs: "%(timesec() - self.starttime) |
|
292 fname = "capture"+str(self.screenshotnum)+".png" |
|
293 cap = self.screen.capture(self.r) |
|
294 cap.save(".", fname) |
|
295 # self.report.write("ReportScreenShot " + msg + " " + fname + "\n") |
|
296 self.screenshotnum = self.screenshotnum + 1 |
|
297 self.report.write( "<p>" + elapsed + msg + "<img src=\""+ fname + "\">" + "</p>") |
|
298 |
|
299 def ReportText(self, text): |
|
300 elapsed = "%.3fs: "%(timesec() - self.starttime) |
|
301 self.report.write("<p>" + elapsed + text + "</p>") |
|
302 |
|
303 def ReportOutput(self, text): |
|
304 elapsed = "%.3fs: "%(timesec() - self.starttime) |
|
305 self.report.write("<pre>" + elapsed + text + "</pre>") |
|
306 |
|
307 |
|
308 def run_test(func, *args, **kwargs): |
|
309 app = BeremizApp(*args, **kwargs) |
|
310 try: |
|
311 success = func(app) |
|
312 except: |
|
313 # sadly, sys.excepthook is broken in sikuli/jython |
|
314 # purpose of this run_test function is to work around it. |
|
315 # and catch exception cleanly anyhow |
|
316 e_type, e_value, e_traceback = sys.exc_info() |
|
317 err_msg = "\n".join(traceback.format_exception(e_type, e_value, e_traceback)) |
|
318 sys.stdout.write(err_msg) |
|
319 app.ReportOutput(err_msg) |
|
320 success = False |
|
321 |
|
322 app.close() |
|
323 |
|
324 if success: |
|
325 sikuli.exit(0) |
|
326 else: |
|
327 sikuli.exit(1) |
|
328 |
|
329 |
|
330 |