tests/ide_tests/sikuliberemiz.py
branchwxPython4
changeset 3424 7db96e011fe7
child 3430 c2171d87b587
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/sikuliberemiz.py	Sun Feb 13 20:59:42 2022 +0100
@@ -0,0 +1,218 @@
+"Commons definitions for sikuli based beremiz IDE GUI tests"
+
+import os
+import sys
+import subprocess
+from threading import Thread, Event
+from sikuli import *
+
+home = os.environ["HOME"]
+beremiz_path = os.environ["BEREMIZPATH"]
+
+opj = os.path.join
+
+
+def StartBeremizApp(projectpath=None, exemple=None):
+    """
+    Starts Beremiz IDE, waits for main window to appear, maximize it.
+
+        Parameters: 
+            projectpath (str): path to project to open
+            exemple (str): path relative to exemples directory
+
+        Returns:
+            Sikuli App class instance
+    """
+
+    command = ["%s/beremizenv/bin/python"%home, opj(beremiz_path,"Beremiz.py"), "--log=/dev/stdout"]
+
+    if exemple is not None:
+        command.append(opj(beremiz_path,"exemples",exemple))
+    elif projectpath is not None:
+        command.append(projectpath)
+
+    # App class is broken in Sikuli 2.0.5: can't start process with arguments.
+    # 
+    # Workaround : - use subprocess module to spawn IDE process,
+    #              - use wmctrl to find IDE window details and maximize it
+    #              - pass exact window title to App class constructor
+
+    proc = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=0)
+
+    # Window are macthed against process' PID
+    ppid = proc.pid
+
+    # Timeout 5s
+    c = 50
+    while c > 0:
+        # equiv to "wmctrl -l -p | grep $pid"
+        try:
+            wlist = filter(lambda l:(len(l)>2 and l[2]==str(ppid)), map(lambda s:s.split(None,4), subprocess.check_output(["wmctrl", "-l", "-p"]).splitlines()))
+        except subprocess.CalledProcessError:
+            wlist = []
+
+        # window with no title only has 4 fields do describe it
+        # beremiz splashcreen has no title
+        # we wity until main window is visible
+        if len(wlist) == 1 and len(wlist[0]) == 5:
+            windowID,_zero,wpid,_XID,wtitle = wlist[0] 
+            break
+
+        wait(0.1)
+        c = c - 1
+
+    if c == 0:
+        raise Exception("Couldn't find Beremiz window")
+
+    # Maximize window x and y
+    subprocess.check_call(["wmctrl", "-i", "-r", windowID, "-b", "add,maximized_vert,maximized_horz"])
+
+    # switchApp creates an App object by finding window by title, is not supposed to spawn a process
+    return proc, switchApp(wtitle)
+
+class KBDShortcut:
+    """Send shortut to app by calling corresponding methods:
+          Stop   
+          Run    
+          Transfer
+          Connect
+          Clean  
+          Build  
+
+    example:
+        k = KBDShortcut(app)
+        k.Clean()
+    """
+
+    fkeys = {"Stop":     Key.F4,
+             "Run":      Key.F5,
+             "Transfer": Key.F6,
+             "Connect":  Key.F7,
+             "Clean":    Key.F9,
+             "Build":    Key.F11}
+
+    def __init__(self, app):
+        self.app = app
+    
+    def __getattr__(self, name):
+        fkey = self.fkeys[name]
+        app = self.app
+
+        def PressShortCut():
+            app.focus()
+            type(fkey)
+
+        return PressShortCut
+
+
+class IDEIdleObserver:
+    "Detects when IDE is idle. This is particularly handy when staring an operation and witing for the en of it."
+
+    def __init__(self, app):
+        """
+        Parameters: 
+            app (class App): Sikuli app given by StartBeremizApp
+        """
+        self.r = Region(app.window())
+
+        self.idechanged = False
+        
+        # 200 was selected because default 50 was still catching cursor blinking in console
+        # FIXME : remove blinking cursor in console
+        self.r.onChange(200,self._OnIDEWindowChange)
+        self.r.observeInBackground()
+
+    def __del__(self):
+        self.r.stopObserver()
+
+    def _OnIDEWindowChange(self, event):
+        print event
+        self.idechanged = True
+
+    def Wait(self, period, timeout):
+        """
+        Wait for IDE to stop changing
+        Parameters: 
+            period (int): how many seconds with no change to consider idle
+            timeout (int): how long to wait for idle, in seconds
+        """
+        c = timeout/period
+        while c > 0:
+            self.idechanged = False
+            wait(period)
+            if not self.idechanged:
+                break
+            c = c - 1
+
+        if c == 0:
+            raise Exception("Window did not idle before timeout")
+
+ 
+class stdoutIdleObserver:
+    "Detects when IDE's stdout is idle. Can be more reliable than pixel based version (false changes ?)"
+
+    def __init__(self, proc):
+        """
+        Parameters: 
+            proc (subprocess.Popen): Beremiz process, given by StartBeremizApp
+        """
+        self.proc = proc
+        self.stdoutchanged = False
+
+        self.thread = Thread(target = self._waitStdoutProc).start()
+
+    def _waitStdoutProc():
+        while True:
+            a = self.proc.stdout.read(1)
+            if len(a) == 0 or a is None: 
+                break
+            sys.stdout.write(a)
+            self.idechanged = True
+
+    def Wait(self, period, timeout):
+        """
+        Wait for IDE'stdout to stop changing
+        Parameters: 
+            period (int): how many seconds with no change to consider idle
+            timeout (int): how long to wait for idle, in seconds
+        """
+        c = timeout/period
+        while c > 0:
+            self.idechanged = False
+            wait(period)
+            if not self.idechanged:
+                break
+            c = c - 1
+
+        if c == 0:
+            raise Exception("Stdout did not idle before timeout")
+
+
+def waitPatternInStdout(proc, pattern, timeout, count=1):
+    
+    success_event = Event()
+
+    def waitPatternInStdoutProc():
+        found = 0
+        while True:
+            a = proc.stdout.readline()
+            if len(a) == 0 or a is None: 
+                raise Exception("App finished before producing expected stdout pattern")
+            sys.stdout.write(a)
+            if a.find(pattern) >= 0:
+                found = found + 1
+                if found >= count:
+                    success_event.set()
+                    break
+
+
+    Thread(target = waitPatternInStdoutProc).start()
+
+    if not success_event.wait(timeout):
+        # test timed out
+        return False
+    else:
+        return True
+
+
+