tests/ide_tests/sikuliberemiz.py
branchwxPython4
changeset 3447 65c5f66e9298
parent 3446 de8cc85b688a
child 3448 523f6fcc7a28
equal deleted inserted replaced
3446:de8cc85b688a 3447:65c5f66e9298
     1 "Commons definitions for sikuli based beremiz IDE GUI tests"
     1 "Commons definitions for sikuli based beremiz IDE GUI tests"
     2 
     2 
     3 import os
     3 import os
     4 import sys
     4 import sys
     5 import subprocess
     5 import subprocess
       
     6 import traceback
     6 from threading import Thread, Event, Lock
     7 from threading import Thread, Event, Lock
     7 from time import time as timesec
     8 from time import time as timesec
     8 
     9 
     9 import sikuli
    10 import sikuli
    10 
    11 
    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:
    84             sikuli.wait(period)
    85             sikuli.wait(period)
    85             if not self.idechanged:
    86             if not self.idechanged:
    86                 break
    87                 break
    87             c = c - 1
    88             c = c - 1
    88 
    89 
       
    90         self.ReportScreenShot("UI is idle" if c != 0 else "UI is not idle")
       
    91 
    89         if c == 0:
    92         if c == 0:
    90             raise Exception("Window did not idle before timeout")
    93             raise Exception("Window did not idle before timeout")
    91 
    94 
    92  
    95  
    93 class stdoutIdleObserver:
    96 class stdoutIdleObserver:
   111         while True:
   114         while True:
   112             a = self.proc.stdout.readline()
   115             a = self.proc.stdout.readline()
   113             if len(a) == 0 or a is None: 
   116             if len(a) == 0 or a is None: 
   114                 break
   117                 break
   115             sys.stdout.write(a)
   118             sys.stdout.write(a)
       
   119             self.ReportOutput(a)
   116             self.event.set()
   120             self.event.set()
   117             if self.pattern is not None and a.find(self.pattern) >= 0:
   121             if self.pattern is not None and a.find(self.pattern) >= 0:
   118                 sys.stdout.write("found pattern in '" + a +"'")
   122                 sys.stdout.write("found pattern in '" + a +"'")
   119                 self.success_event.set()
   123                 self.success_event.set()
   120 
   124 
   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
   168                 self.success_event.clear()
   179                 self.success_event.clear()
   169                 found = found + 1
   180                 found = found + 1
   170                 if found >= count:
   181                 if found >= count:
   171                     break
   182                     break
   172         self.pattern = None
   183         self.pattern = None
       
   184         self.ReportScreenShot("found pattern" if res else "pattern not found")
   173         return res
   185         return res
   174 
   186 
   175 class BeremizApp(IDEIdleObserver, stdoutIdleObserver):
   187 class BeremizApp(IDEIdleObserver, stdoutIdleObserver):
   176     def __init__(self, projectpath=None, exemple=None):
   188     def __init__(self, projectpath=None, exemple=None):
   177         """
   189         """
   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