beremiz

Tests: refactored sikuli based test
wxPython4
2022-03-29, Edouard Tisserant
de8cc85b688a
Parents 83545348403e
Children 65c5f66e9298
Tests: refactored sikuli based test
--- 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 *
# Start the app
-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")
+
+app.WaitIdleUI()
-doubleClick("1646062660770.png")
-
-idle.Wait(1,15)
+app.click("example")
-click("example")
+app.WaitIdleUI()
-idle.Wait(1,15)
+app.type(Key.DOWN * 10, Key.CTRL)
-type(Key.DOWN * 10, Key.CTRL)
-
-idle.Wait(1,15)
+app.WaitIdleUI()
-doubleClick("1646066996620.png")
+app.doubleClick("1646066996620.png")
-idle.Wait(1,15)
+app.WaitIdleUI()
-type(Key.TAB*3) # select text content
-
-type("'sys.stdout.write(\"EDIT TEST OK\\n\")'")
+app.type(Key.TAB*3) # select text content
-type(Key.ENTER)
+app.type("'sys.stdout.write(\"EDIT TEST OK\\n\")'")
-idle.Wait(1,15)
+app.type(Key.ENTER)
-k = KBDShortcut(app)
+app.WaitIdleUI()
-k.Save()
+app.k.Save()
-del idle
+app.k.Clean()
-stdoutIdle = stdoutIdleObserver(proc)
-
-k.Clean()
+app.waitForChangeAndIdleStdout()
-stdoutIdle.WaitForChangeAndIdle(2,15)
+app.k.Build()
-k.Build()
+app.waitForChangeAndIdleStdout()
-stdoutIdle.WaitForChangeAndIdle(2,15)
-
-k.Connect()
+app.k.Connect()
-stdoutIdle.WaitForChangeAndIdle(2,15)
+app.waitForChangeAndIdleStdout()
-k.Transfer()
+app.k.Transfer()
-stdoutIdle.WaitForChangeAndIdle(2,15)
-
-del stdoutIdle
+app.waitForChangeAndIdleStdout()
-k.Run()
+app.k.Run()
-# 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)
app.close()
--- 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()
+app = BeremizApp()
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
-k = KBDShortcut(app)
-
-idle.Wait(1,15)
+app.WaitIdleUI()
# Create new project (opens new project directory selection dialog)
-k.New()
+app.k.New()
-idle.Wait(1,15)
+app.WaitIdleUI()
# Move to "Home" section of file selecor, otherwise address is
# "file ignored" at first run
-type("f", Key.CTRL)
-type(Key.ESC)
-type(Key.TAB)
+app.type("f", Key.CTRL)
+app.type(Key.ESC)
+app.type(Key.TAB)
# Enter directory by name
-k.Address()
+app.k.Address()
# Fill address bar
-type(new_project_path + Key.ENTER)
+app.type(new_project_path + Key.ENTER)
-idle.Wait(1,15)
+app.WaitIdleUI()
# 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
-idle.Wait(1,15)
+app.WaitIdleUI()
# Name created program
-type("Test program")
+app.type("Test program")
-idle.Wait(1,15)
+app.WaitIdleUI()
# Focus on Variable grid
-type(Key.TAB*4)
+app.type(Key.TAB*4)
# Add 2 variables
-type(Key.ADD*2)
+app.type(Key.ADD*2)
# Focus on ST text
-idle.Wait(1,15)
+app.WaitIdleUI()
-type(Key.TAB*8)
+app.type(Key.TAB*8)
-type("""\
+app.type("""\
LocalVar0 := LocalVar1;
{printf("Test OK\\n");fflush(stdout);}
""")
-k.Save()
+app.k.Save()
# Close ST POU
-type("w", Key.CTRL)
+app.type("w", Key.CTRL)
-idle.Wait(1,15)
+app.WaitIdleUI()
# Focus project tree and select root item
-type(Key.TAB)
+app.type(Key.TAB)
-type(Key.LEFT)
+app.type(Key.LEFT)
-type(Key.UP)
+app.type(Key.UP)
# Edit root item
-type(Key.ENTER)
+app.type(Key.ENTER)
-idle.Wait(1,15)
+app.WaitIdleUI()
# Switch to config tab
-type(Key.RIGHT*2)
+app.type(Key.RIGHT*2)
# Focus on URI
-type(Key.TAB)
+app.type(Key.TAB)
# Set URI
-type("LOCAL://")
+app.type("LOCAL://")
# FIXME: Select other field to ensure URI is validated
-type(Key.TAB)
+app.type(Key.TAB)
-k.Save()
+app.k.Save()
# Close project config editor
-type("w", Key.CTRL)
+app.type("w", Key.CTRL)
-idle.Wait(1,15)
+app.WaitIdleUI()
# Focus seems undefined at that time (FIXME)
# Force focussing on "something" so that next shortcut is taken
-type(Key.TAB)
+app.type(Key.TAB)
-del idle
+app.waitIdleStdout()
-stdoutIdle = stdoutIdleObserver(proc)
-stdoutIdle.Wait(2,15)
+app.k.Build()
-k.Build()
-
-stdoutIdle.Wait(5,15)
+app.waitIdleStdout(5,30)
-k.Connect()
+app.k.Connect()
-stdoutIdle.Wait(2,15)
-
-k.Transfer()
+app.waitIdleStdout()
-stdoutIdle.Wait(2,15)
+app.k.Transfer()
-del stdoutIdle
+app.waitIdleStdout()
-k.Run()
+app.k.Run()
# wait 10 seconds
-found = waitPatternInStdout(proc, "Test OK", 10)
+found = app.waitPatternInStdout("Test OK", 10)
app.close()
--- 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 *
# Start the app
-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
+app.k.Clean()
-stdoutIdle = stdoutIdleObserver(proc)
+app.waitForChangeAndIdleStdout()
-# To send keyboard shortuts
-k = KBDShortcut(app)
+app.k.Build()
-k.Clean()
-
-stdoutIdle.Wait(2,15)
+app.waitForChangeAndIdleStdout()
-k.Build()
-
-stdoutIdle.Wait(2,15)
+app.k.Connect()
-k.Connect()
-
-stdoutIdle.Wait(2,15)
+app.waitForChangeAndIdleStdout()
-k.Transfer()
-
-stdoutIdle.Wait(2,15)
+app.k.Transfer()
-#del idle
+app.waitForChangeAndIdleStdout()
-del stdoutIdle
-
-k.Run()
+app.k.Run()
# wait 10 seconds for 10 Grumpfs
-found = waitPatternInStdout(proc, "Grumpf", 10, 10)
+found = app.waitPatternInStdout("Grumpf", 10, 10)
app.close()
--- 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 @@
import os
import sys
import subprocess
-from threading import Thread, Event
+from threading import Thread, Event, Lock
from time import time as timesec
-typeof=type
-
-from sikuli import *
+import sikuli
beremiz_path = os.environ["BEREMIZPATH"]
python_bin = os.environ.get("BEREMIZPYTHONPATH", "/usr/bin/python")
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 = [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
- 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
- # wait 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
+ """Send shortut to app by calling corresponding methods.
example:
- k = KBDShortcut(app)
+ k = KBDShortcut()
k.Clean()
"""
- fkeys = {"Stop": Key.F4,
- "Run": Key.F5,
- "Transfer": Key.F6,
- "Connect": Key.F7,
- "Clean": Key.F9,
- "Build": Key.F11,
- "Save": ("s",Key.CTRL),
- "New": ("n",Key.CTRL),
- "Address": ("l",Key.CTRL)} # to reach address bar in GTK's file selector
+ fkeys = {"Stop": sikuli.Key.F4,
+ "Run": sikuli.Key.F5,
+ "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
def __init__(self, app):
- self.app = app
+ self.app = app.sikuliapp
def __getattr__(self, name):
fkey = self.fkeys[name]
- if typeof(fkey) != tuple:
+ if type(fkey) != tuple:
fkey = (fkey,)
app = self.app
def PressShortCut():
app.focus()
- type(*fkey)
+ sikuli.type(*fkey)
return PressShortCut
@@ -116,12 +51,12 @@
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):
+ def __init__(self):
"""
Parameters:
- app (class App): Sikuli app given by StartBeremizApp
+ app (class BeremizApp)
"""
- self.r = Region(app.window())
+ self.r = sikuli.Region(self.sikuliapp.window())
self.idechanged = False
@@ -136,7 +71,7 @@
def _OnIDEWindowChange(self, event):
self.idechanged = True
- def Wait(self, period, timeout):
+ def WaitIdleUI(self, period=1, timeout=15):
"""
Wait for IDE to stop changing
Parameters:
@@ -146,7 +81,7 @@
c = max(timeout/period,1)
while c > 0:
self.idechanged = False
- wait(period)
+ sikuli.wait(period)
if not self.idechanged:
break
c = c - 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):
+ def __init__(self):
"""
Parameters:
- proc (subprocess.Popen): Beremiz process, given by StartBeremizApp
+ app (class BeremizApp)
"""
- self.proc = proc
self.stdoutchanged = False
- self.changes = 0
- self.last_change_count = 0
+ self.event = Event()
- self.event = Event()
+ self.pattern = None
+ 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:
break
- # sys.stdout.write(a)
- self.changes = self.changes + 1
+ sys.stdout.write(a)
self.event.set()
+ 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
Parameters:
timeout (int): how long to wait for change, in seconds
"""
start_time = timesec()
- if self.changes == self.last_change_count:
- if self.event.wait(timeout):
- self.event.clear()
- self.last_change_count = self.changes
- else:
- raise Exception("Stdout didn't become active before timeout")
- self.Wait(period, timeout - (timesec() - start_time))
+ if self.event.wait(timeout):
+ self.event.clear()
+ else:
+ 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
Parameters:
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
+ self.event.clear()
+ while timesec() < end_time:
+ if self.event.wait(period):
+ # no timeout -> got event -> not idle -> loop again
+ self.event.clear()
+ else:
+ # timeout -> no event -> idle -> exit
+ return True
+
+ raise Exception("Stdout did not idle before timeout")
+
+ def waitPatternInStdout(self, pattern, timeout, count=1):
+ found = 0
+ self.pattern = pattern
+ end_time = timesec() + timeout
+ self.event.clear()
+ while True:
+ remain = end_time - timesec()
+ if remain <= 0 :
+ res = False
+ break
+
+ res = self.success_event.wait(remain)
+ if res:
+ self.success_event.clear()
+ found = found + 1
+ if found >= count:
+ break
+ self.pattern = None
+ return res
+
+class BeremizApp(IDEIdleObserver, stdoutIdleObserver):
+ def __init__(self, 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 = [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
+ ppid = self.proc.pid
+
+ # Timeout 5s
+ c = 50
while c > 0:
- changes = self.changes
- wait(period)
- if self.changes == changes:
- self.last_change_count = self.changes
+ # 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
+ # wait until main window is visible
+ if len(wlist) == 1 and len(wlist[0]) == 5:
+ windowID,_zero,wpid,_XID,wtitle = wlist[0]
break
+
+ sikuli.wait(0.1)
c = c - 1
if c == 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():
- 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:
- sys.stdout.write("found pattern in '" + a +"'")
- found = found + 1
- if found >= count:
- success_event.set()
- break
+ # 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()
+ def close(self):
+ self.sikuliapp.close()
+ self.sikuliapp = None
- if not success_event.wait(timeout):
- # test timed out
- return False
- else:
- return True
+ def __del__(self):
+ if self.sikuliapp is not None:
+ self.sikuliapp.close()
+ IDEIdleObserver.__del__(self)
+ stdoutIdleObserver.__del__(self)
-
-