--- 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()
+app.waitIdleStdout(5,30)
-stdoutIdle.Wait(5,15)
+app.k.Connect()
-k.Connect()
+app.waitIdleStdout()
-stdoutIdle.Wait(2,15)
+app.k.Transfer()
-k.Transfer()
+app.waitIdleStdout()
-stdoutIdle.Wait(2,15)
-
-del stdoutIdle
-
-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/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):
- """
- Parameters:
- app (class App): Sikuli app given by StartBeremizApp
- """
- self.r = Region(app.window())
+ def __init__(self):
+ """
+ Parameters:
+ app (class BeremizApp)
+ """
+ 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,19 +93,18 @@
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
+ def __init__(self):
+ """
+ Parameters:
+ app (class BeremizApp)
+ """
self.stdoutchanged = False
- self.changes = 0
- self.last_change_count = 0
-
self.event = Event()
+ self.pattern = None
+ self.success_event = Event()
+
self.thread = Thread(target = self._waitStdoutProc).start()
def _waitStdoutProc(self):
@@ -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()
-
- def WaitForChangeAndIdle(self, period, timeout):
+ if self.pattern is not None and a.find(self.pattern) >= 0:
+ sys.stdout.write("found pattern in '" + a +"'")
+ self.success_event.set()
+
+ 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))
-
- def Wait(self, period, timeout):
+
+ if self.event.wait(timeout):
+ self.event.clear()
+ else:
+ raise Exception("Stdout didn't become active before 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)
- while c > 0:
- changes = self.changes
- wait(period)
- if self.changes == changes:
- self.last_change_count = self.changes
- 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():
+ 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:
- 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 +"'")
+ 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:
- success_event.set()
break
-
-
- Thread(target = waitPatternInStdoutProc).start()
-
- if not success_event.wait(timeout):
- # test timed out
- return False
- else:
- return True
-
-
-
+ 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:
+ # 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("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
+ self.sikuliapp = sikuli.switchApp(wtitle)
+ self.k = KBDShortcut(self)
+
+ IDEIdleObserver.__init__(self)
+ stdoutIdleObserver.__init__(self)
+
+ # stubs for common sikuli calls to allow adding hooks later
+ for n in ["click","doubleClick","type"]:
+ setattr(self, n, getattr(sikuli, n))
+
+ def close(self):
+ self.sikuliapp.close()
+ self.sikuliapp = None
+
+ def __del__(self):
+ if self.sikuliapp is not None:
+ self.sikuliapp.close()
+ IDEIdleObserver.__del__(self)
+ stdoutIdleObserver.__del__(self)
+