Sikuli is
now named SikuliX:
SikuliX automates anything you see on the
screen of your desktop computer running Windows, Mac or some Linux/Unix. It
uses image recognition powered by OpenCV to identify and control GUI
components. This is handy in cases when there is no easy access to a GUI's
internals or the source code of the application or web page you want to act on.
SikuliX supports below scripting languages
- Python language level 2.7 (supported by Jython)
- Ruby language level 1.9 and 2.0 (supported by JRuby)
… and you can use it in Java programming and
programming/scripting with any Java aware programming/scripting language
(Jython, JRuby, Scala, Clojure, …).
Though SikuliX is currently not available on
any mobile device, it can be used with the respective emulators on a desktop
computer or based on VNC solutions.
Besides locating images on a screen SikuliX
can run the mouse and the keyboard to interact with the identified GUI
elements. This is available for multi monitor environments and even for remote
systems with some restrictions.
SikuliX comes with basic text recognition
(OCR) and can be used to search text in images. This feature is powered by
Tesseract.
SikuliX is a Java application, that works on
Windows XP+, Mac 10.6+ and most Linux/Unix systems. For Windows, Mac and Ubuntu
12.04+ it is complete and should normally work out of the box. For other
Linux/Unix systems there usually are a few prerequisites to be setup.
SikuliX is the follow up of Sikuli Script with it's last version from 2011 as Sikuli-X-1.0rc3.
Though it is no longer supported: here you find valuable information for the installation.
Though it is no longer supported: here you find valuable information for the installation.
RESTful
SikuliX:
The idea of using SikuliX in a remote context
appeared due to automation architecture chosen for the project I'm currently
working on. As we scale our tests between number of environments, there was a
necessity in resolving some complicated tasks, that couldn't be done with
common libraries like Selenium, on remote VMs. SikuliX had everything we
needed, except remote platform we could use in a context of existing
architecture.
RESTful has a built a client-server platform, that included latest SikuliX API and several useful
utility classes for interacting with remote file system. After some period of
time I've realized that this approach could be useful for others, so now I'm
happy to anounce new experimental features, that are already pushed into
SikuliX repository - RemoteServer andRESTClient.
These modules are not fully tested yet, but
you can give it a try and report all issues you'll find or push appropriate
pull requests. There're some tests written for Windows / Linux OS, that could
be a start point for you.
And now let's take a look at provided
functionality.
RemoteServer module is based on Grizzly Http
Sever and contains the following endpoints:
- http://ip:port/sikuli/cmd/execute - uses apache commons exec library for running command line on a remote VM.
- http://ip:port/sikuli/file/upload - uploads a list of provided files to the given remote path.
- http://ip:port/sikuli/file/download - downloads a single file from remote path (multiple file download feature is not implemented yet).
- http://ip:port/sikuli/file/delete - removes file or directory by a given remote path (quick note: file system operations were implemented using apache commons iolibrary).
- http://ip:port/sikuli/file/exists - checks a list of inputs, if appropriate files or directories exist.
- http://ip:port/sikuli/file/createFolder - creates directory by a given remote path.
- http://ip:port/sikuli/file/cleanFolder- removes content of a given remote directory.
- http://ip:port/sikuli/file/copyFolder- copies 1 folder's content to another.
- http://ip:port/sikuli/image/click - uses SikuliX API for clicking provided image with a given wait timeout on a remote VM.
- http://ip:port/sikuli/image/setText - uses SikuliX API to print some text into appropriate control with a given wait timeout on a remote VM.
- http://ip:port/sikuli/image/exists - uses SikuliX API for checking if image is present on a screen or not on a remote VM.
- http://ip:port/sikuli/image/dragAndDrop - uses SikuliX API for dragging and dropping objects on remote VM.
If you take a look at code, you'll find it
pretty straightforward, e.g. here's a delete processor:
@POST
@Path("/delete")
public Response delete(@QueryParam("path") final String path)
{
return Response.status(FileUtility.delete(path) ?
Response.Status.OK : Response.Status.NOT_FOUND)
.build();
}
As you may saw above, common SikuliX APIs
provides us a way of setting wait timeout. This functionality is implemented
via observers mechanism, that uses onAppear event to process requested actions:
private RemoteDesktop onAppear(final Pattern element,
final SikuliAction action, final String text) {
desktop.onAppear(element, new ObserverCallBack() {
public void appeared(ObserveEvent e) {
switch (action) {
case CLICK:
e.getMatch().click();
break;
case TYPE:
e.getMatch().click();
e.getMatch().type(text);
break;
}
desktop.stopObserver();
}
});
return this;
}
Before moving to client's part, let's take a
look at one more interesting block, related to remote command line execution.
As I've mentioned above, we use apache commons exec library for this purpose.
And I must say, it provides a fantastic feature, that I struggled with for a
while - delayed exit from main thread by timeout. You may know that common java
command line executor will stuck forever, if started process waits for user
input or it's just a simple server application. Let's look what commons exec can
provide for this particular case:
public static int executeCommandLine(final Command command) {
if (command.getProcess() == null) {
CMD_LOGGER.severe("There's nothing to execute.");
return -1;
}
CMD_LOGGER.info("Processing the following command: " +
command.getProcess() +
(command.getArgs() != null ?
" " +
command.getArgs() : ""));
final long timeout = (command.getTimeout() > 0 ?
command.getTimeout() : 0) *
1000;
final CommandLine commandLine = new CommandLine(
separatorsToSystem(quoteArgument(
command.getProcess())));
if (command.getArgs() != null) {
for (String arg : command.getArgs()) {
commandLine.addArgument(quoteArgument(arg));
}
}
final ExecutionResultsHandler resultHandler =
new ExecutionResultsHandler();
final PumpStreamHandler streamHandler =
new PumpStreamHandler(
new
ExecutionLogger(CMD_LOGGER, Level.INFO),
new
ExecutionLogger(CMD_LOGGER, Level.SEVERE));
final DefaultExecutor executor = new DefaultExecutor();
executor.setStreamHandler(streamHandler);
executor.setProcessDestroyer(
new
ShutdownHookProcessDestroyer());
try {
executor.execute(commandLine, resultHandler);
resultHandler.waitFor(timeout);
} catch (InterruptedException | IOException e) {
CMD_LOGGER.severe("Command execution failed: "
+ e.getMessage());
return -1;
}
return resultHandler.getExitValue();
}
ExecutionResultsHandler will let process be
released by a given timeout.
That's almost pretty much related to server
side. To build remote server, use the following command:
mvn clean install
It will create a jar with all necessary
dependencies in your target folder.
To start server, use the following command:
java -jar
sikulixremoteserver-1.1.0-jar-with-dependencies.jar port
Port is optional. You can skip it, if you
want to use default one - 4041.
Now it's time to look at client side, that is
located inside RESTClient module.
There's nothing specific. Code is pretty
straightforward, as it only takes care about sending necessary objects to
listed above endpoints. Client implements SikuliX interface. Besides that, you
may find some other interfaces used as an input methods' arguments. We decided
to leave them in project to allow users overriding client's methods and common
sending containers. It was done for number of reasons. One of them is
incompatible Jersey 1.x and 2.x versions. If your project uses Jersey 1.x
dependencies, you won't be able to use new SikuliX REST client, as it's based
on Jersey 2.x. In such case you will need to implement your own client using
SikuliX remote interfaces.
As a simple example of REST call
implementation, let's take a look at multiple files upload API:
public void uploadFile(final List filesPath,
final String saveToPath) {
final MultiPart multiPart =
new MultiPart(MediaType.MULTIPART_FORM_DATA_TYPE);
for (String path : filesPath) {
multiPart.bodyPart(new FileDataBodyPart("file",
new
File(separatorsToSystem(path)),
MediaType.APPLICATION_OCTET_STREAM_TYPE));
}
final Response response = service.path("file")
.path("upload")
.queryParam("saveTo",
separatorsToSystem(saveToPath))
.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(multiPart,
multiPart.getMediaType()));
if (response.getStatus() ==
Response.Status.OK.getStatusCode()) {
CLIENT_LOGGER.info("File(-s) " + filesPath +
" has been saved to "
+
separatorsToSystem(saveToPath)
+ " on " + ip);
} else {
CLIENT_LOGGER.severe("Unable to save file(-s) " +
filesPath + " to " +
separatorsToSystem(saveToPath) +
" on " + ip);
}
response.close();
}
As you see, we can pass a list of files'
paths for uploading. It's pretty useful when we need to copy expected images we
want to allow SikuliX interact with on a remote VM.
Provided tests were created for Windows OS
and haven't been tested on Unix or Mac yet. If you're going to give it a try,
you'll need to install and start remote server first. By default all the tests
are disabled to avoid build failures, as such verifications are very platform
and configuration specific. To enable them, just change the following option in
a pom.xml:
skipTests=false
To choose classes to be included into test
run, you need to modify suites.xml located in resources folder. Actually, you
should carefully explore resources before execution. Batches' extensions should
be renamed to .bat. And you may also need to provide your own images, as they are
very OS specific.
When you finish with resources, you'll need
to update BaseTest configuration:
SIKULIX_SERVER_IP field must refer your newly
raised remote server IP address.
WAIT_TIMEOUT will tell SikuliX to wait until
expected image is appeared on a screen.
SIMILARITY level will be used while images
comparison.
As we've already mentioned file upload
scenario, let's take a look at appropriate test:
@Test
public void uploadFilesToServer() {
getClient().uploadFile(Arrays.asList(
BATCH_RUNNER_SCRIPT.getPath(),
BATCH_SAMPLE_SCRIPT.getPath()),
SERVER_PATH.getPath());
assertTrue(getClient().exists(Arrays.asList(
SERVER_PATH.getPath() +
"/" +
BATCH_RUNNER_SCRIPT.getName(),
SERVER_PATH.getPath() +
"/" +
BATCH_SAMPLE_SCRIPT.getName())));
}
To perform common SikuliX actions, you can
use the following example:
@Test
public void callCommandLineFromStartMenu() {
getClient().click(new ImageBox(
getResource(RESOURCE_BUTTON_START_IMAGE).getPath(),
SIMILARITY), WAIT_TIMEOUT);
getClient().setText(new ImageBox(
getResource(RESOURCE_INPUT_FIND_FILES_IMAGE)
.getPath(), SIMILARITY),
"cmd" + Key.ENTER,
WAIT_TIMEOUT);
assertTrue(getClient().exists(new ImageBox(
getResource(RESOURCE_INPUT_CMD_IMAGE).getPath(),
SIMILARITY), WAIT_TIMEOUT));
}
You can find more examples in official
SikuliX 2014 GitHub repository.
0 Comments