--- a/tests/ide_tests/edit_project.sikuli/edit_project.py Tue Mar 29 08:30:03 2022 +0200
+++ b/tests/ide_tests/edit_project.sikuli/edit_project.py Tue Mar 29 08:50:01 2022 +0200
@@ -12,65 +12,54 @@
from sikuliberemiz import *
-proc,app = StartBeremizApp(exemple="python")
+app = BeremizApp(exemple="python") -# To detect when actions did finish because IDE content isn't changing
-idle = IDEIdleObserver(app)
+app.doubleClick("1646062660770.png") -doubleClick("1646062660770.png")
+app.type(Key.DOWN * 10, Key.CTRL) -type(Key.DOWN * 10, Key.CTRL)
-doubleClick("1646066996620.png")
+app.doubleClick("1646066996620.png")
-type(Key.TAB*3) # select text content
-type("'sys.stdout.write(\"EDIT TEST OK\\n\")'")
+app.type(Key.TAB*3) # select text content
+app.type("'sys.stdout.write(\"EDIT TEST OK\\n\")'")
-stdoutIdle = stdoutIdleObserver(proc)
+app.waitForChangeAndIdleStdout() -stdoutIdle.WaitForChangeAndIdle(2,15)
+app.waitForChangeAndIdleStdout() -stdoutIdle.WaitForChangeAndIdle(2,15)
-stdoutIdle.WaitForChangeAndIdle(2,15)
+app.waitForChangeAndIdleStdout()
-stdoutIdle.WaitForChangeAndIdle(2,15)
+app.waitForChangeAndIdleStdout()
-# wait 10 seconds for 10 Grumpfs
-found = waitPatternInStdout(proc, "EDIT TEST OK", 10)
+# wait 10 seconds for 10 patterns +found = app.waitPatternInStdout("EDIT TEST OK", 10) --- a/tests/ide_tests/new_project.sikuli/new_project.py Tue Mar 29 08:30:03 2022 +0200
+++ b/tests/ide_tests/new_project.sikuli/new_project.py Tue Mar 29 08:50:01 2022 +0200
@@ -12,133 +12,122 @@
from sikuliberemiz import *
# Start the app without any project given
-proc,app = StartBeremizApp()
new_project_path = os.path.join(os.path.abspath(os.path.curdir), "new_test_project")
# New project path must exist (usually created in directory selection dialog)
os.mkdir(new_project_path)
-# To detect when actions did finish because IDE content isn't changing
-idle = IDEIdleObserver(app)
-# To send keyboard shortuts
# Create new project (opens new project directory selection dialog)
# Move to "Home" section of file selecor, otherwise address is
# "file ignored" at first run
# Enter directory by name
-type(new_project_path + Key.ENTER)
+app.type(new_project_path + Key.ENTER)
# When prompted for creating first program select type ST
-type(Key.TAB*4) # go to lang dropdown
-type(Key.DOWN*2) # change selected language
-type(Key.ENTER) # validate
+app.type(Key.TAB*4) # go to lang dropdown +app.type(Key.DOWN*2) # change selected language +app.type(Key.ENTER) # validate
+app.type("Test program")
{printf("Test OK\\n");fflush(stdout);}
# Focus project tree and select root item
# FIXME: Select other field to ensure URI is validated
# Close project config editor
# Focus seems undefined at that time (FIXME)
# Force focussing on "something" so that next shortcut is taken
-stdoutIdle = stdoutIdleObserver(proc)
+app.waitIdleStdout(5,30)
-found = waitPatternInStdout(proc, "Test OK", 10)
+found = app.waitPatternInStdout("Test OK", 10) --- a/tests/ide_tests/run_python_exemple.sikuli/run_python_exemple.py Tue Mar 29 08:30:03 2022 +0200
+++ b/tests/ide_tests/run_python_exemple.sikuli/run_python_exemple.py Tue Mar 29 08:50:01 2022 +0200
@@ -12,41 +12,28 @@
from sikuliberemiz import *
-proc,app = StartBeremizApp(exemple="python")
+app = BeremizApp(exemple="python") -# To detect when actions did finish because IDE content isn't changing
-# idle = IDEIdleObserver(app)
-# screencap based idle detection was making many false positive. Test is more stable with stdout based idle detection
-stdoutIdle = stdoutIdleObserver(proc)
+app.waitForChangeAndIdleStdout() -# To send keyboard shortuts
+app.waitForChangeAndIdleStdout()
+app.waitForChangeAndIdleStdout()
+app.waitForChangeAndIdleStdout()
# wait 10 seconds for 10 Grumpfs
-found = waitPatternInStdout(proc, "Grumpf", 10, 10)
+found = app.waitPatternInStdout("Grumpf", 10, 10) --- a/tests/ide_tests/sikuliberemiz.py Tue Mar 29 08:30:03 2022 +0200
+++ b/tests/ide_tests/sikuliberemiz.py Tue Mar 29 08:50:01 2022 +0200
@@ -3,112 +3,47 @@
-from threading import Thread, Event
+from threading import Thread, Event, Lock from time import time as timesec
beremiz_path = os.environ["BEREMIZPATH"]
python_bin = os.environ.get("BEREMIZPYTHONPATH", "/usr/bin/python")
-def StartBeremizApp(projectpath=None, exemple=None):
- Starts Beremiz IDE, waits for main window to appear, maximize it.
- projectpath (str): path to project to open
- exemple (str): path relative to exemples directory
- Sikuli App class instance
- command = [python_bin, 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
- # equiv to "wmctrl -l -p | grep $pid"
- 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:
- # window with no title only has 4 fields do describe it
- # beremiz splashcreen has no title
- # wait until main window is visible
- if len(wlist) == 1 and len(wlist[0]) == 5:
- windowID,_zero,wpid,_XID,wtitle = wlist[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)
- """Send shortut to app by calling corresponding methods:
+ """Send shortut to app by calling corresponding methods.
- fkeys = {"Stop": Key.F4,
- "Save": ("s",Key.CTRL),
- "Address": ("l",Key.CTRL)} # to reach address bar in GTK's file selector
+ fkeys = {"Stop": sikuli.Key.F4, + "Transfer": sikuli.Key.F6, + "Connect": sikuli.Key.F7, + "Clean": sikuli.Key.F9, + "Build": sikuli.Key.F11, + "Save": ("s",sikuli.Key.CTRL), + "New": ("n",sikuli.Key.CTRL), + "Address": ("l",sikuli.Key.CTRL)} # to reach address bar in GTK's file selector
+ self.app = app.sikuliapp def __getattr__(self, name):
- if typeof(fkey) != tuple:
+ if type(fkey) != tuple:
@@ -116,12 +51,12 @@
"Detects when IDE is idle. This is particularly handy when staring an operation and witing for the en of it."
- def __init__(self, app):
- app (class App): Sikuli app given by StartBeremizApp
- self.r = Region(app.window())
+ self.r = sikuli.Region(self.sikuliapp.window()) @@ -136,7 +71,7 @@
def _OnIDEWindowChange(self, event):
- def Wait(self, period, timeout):
+ def WaitIdleUI(self, period=1, timeout=15): Wait for IDE to stop changing
@@ -146,7 +81,7 @@
c = max(timeout/period,1)
@@ -158,18 +93,17 @@
class stdoutIdleObserver:
"Detects when IDE's stdout is idle. Can be more reliable than pixel based version (false changes ?)"
- def __init__(self, proc):
- proc (subprocess.Popen): Beremiz process, given by StartBeremizApp
self.stdoutchanged = False
- self.last_change_count = 0
+ self.success_event = Event() self.thread = Thread(target = self._waitStdoutProc).start()
@@ -178,72 +112,140 @@
a = self.proc.stdout.readline()
if len(a) == 0 or a is None:
- self.changes = self.changes + 1
+ if self.pattern is not None and a.find(self.pattern) >= 0: + sys.stdout.write("found pattern in '" + a +"'") + self.success_event.set() - def WaitForChangeAndIdle(self, period, timeout):
+ def waitForChangeAndIdleStdout(self, period=2, timeout=15): Wait for IDE'stdout to start changing
timeout (int): how long to wait for change, in seconds
- if self.changes == self.last_change_count:
- if self.event.wait(timeout):
- self.last_change_count = self.changes
- raise Exception("Stdout didn't become active before timeout")
- self.Wait(period, timeout - (timesec() - start_time))
+ if self.event.wait(timeout): + raise Exception("Stdout didn't become active before timeout") - def Wait(self, period, timeout):
+ self.waitIdleStdout(period, timeout - (timesec() - start_time)) + def waitIdleStdout(self, period=2, timeout=15): Wait for IDE'stdout to stop changing
period (int): how many seconds with no change to consider idle
timeout (int): how long to wait for idle, in seconds
- c = max(timeout/period, 1)
+ end_time = timesec() + timeout + while timesec() < end_time: + if self.event.wait(period): + # no timeout -> got event -> not idle -> loop again + # timeout -> no event -> idle -> exit + raise Exception("Stdout did not idle before timeout") + def waitPatternInStdout(self, pattern, timeout, count=1): + end_time = timesec() + timeout + remain = end_time - timesec() + res = self.success_event.wait(remain) + self.success_event.clear() +class BeremizApp(IDEIdleObserver, stdoutIdleObserver): + def __init__(self, projectpath=None, exemple=None): + Starts Beremiz IDE, waits for main window to appear, maximize it. + projectpath (str): path to project to open + exemple (str): path relative to exemples directory + Sikuli App class instance + command = [python_bin, 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 + self.proc = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=0) + # Window are macthed against process' PID
- if self.changes == changes:
- self.last_change_count = self.changes
+ # equiv to "wmctrl -l -p | grep $pid" + 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: + # window with no title only has 4 fields do describe it + # beremiz splashcreen has no title + # wait until main window is visible + if len(wlist) == 1 and len(wlist[0]) == 5: + windowID,_zero,wpid,_XID,wtitle = wlist[0] - raise Exception("Stdout did not idle before timeout")
+ 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"]) -def waitPatternInStdout(proc, pattern, timeout, count=1):
- success_event = Event()
+ # switchApp creates an App object by finding window by title, is not supposed to spawn a process + self.sikuliapp = sikuli.switchApp(wtitle) + self.k = KBDShortcut(self) + IDEIdleObserver.__init__(self) + stdoutIdleObserver.__init__(self) - def waitPatternInStdoutProc():
- a = proc.stdout.readline()
- if len(a) == 0 or a is None:
- raise Exception("App finished before producing expected stdout pattern")
- if a.find(pattern) >= 0:
- sys.stdout.write("found pattern in '" + a +"'")
+ # stubs for common sikuli calls to allow adding hooks later + for n in ["click","doubleClick","type"]: + setattr(self, n, getattr(sikuli, n))
- Thread(target = waitPatternInStdoutProc).start()
- if not success_event.wait(timeout):
+ if self.sikuliapp is not None: + IDEIdleObserver.__del__(self) + stdoutIdleObserver.__del__(self)