This section discusses an advanced and very powerful feature offered by Squish—how to create wrappers that provide application bindings.
![]() | Automatic Dynamic Bindings |
|---|---|
From Squish 4.0, Squish's bindings support has been greatly enhanced. For Java and Qt-based applications, Squish now dynamically creates wrappers that automatically provide bindings for all the standard library objects that the AUT uses, and also for the AUT's own custom classes. And in Java's case, even classes that are not automatically dynamically bound can often be accessed using Java's introspection facilities. This means that in most cases there is no need to create custom bindings.
For Qt AUT's Squish will automatically bind only
slots and properties. Slots
are member functions declared in a class's |
Squish already comes with complete script bindings for the GUI toolkits it supports, such as Qt and Java AWT/Swing and Java SWT. This enables test engineers to access all the widgets, functions, and properties that these toolkits provide. Using these script bindings it is then possible to query and set object properties, and to call object methods, for any of the standard objects (such as widgets), used by the AUT. This make it possible to implement sophisticated and robust test scripts that can verify the application's state and behavior using verification points (see also How to Create and Use Verification Points (Section 13.20) in the User Guide (Chapter 13)).
In most cases, the script bindings provided with Squish are sufficient. But in some cases it might be necessary to access objects that are defined in the AUT or in a component used by the AUT that isn't one of the GUI toolkit's standard classes. This isn't a problem with Squish 4.0, since from that version bindings for AUT classes are automatically created by Squish. But for earlier versions this must be done manually.
Why would we want to access custom AUT components rather than simply those components in the GUI toolkit that the AUT is built with? Because sometimes we want to access the additional custom properties or to call the custom methods that custom components provide. For example, we might want to test a CAD application for which its custom Canvas widget is a key component. To provide comprehensive tests we would want to be able to query the custom Canvas to verify that objects that the test has drawn (by interacting with the application's user interface), have been drawn correctly.
For this purpose Squish provides a very powerful mechanism which makes it possible to instrument C++ code and create script bindings for it. With the bindings in place it then becomes possible to call functions and access properties which are declared in the application without changing the application itself.
In this section we will present an example that shows how to create bindings to make your AUT's API accessible to your test scripts. We will take you step-by-step through the process, so that you can see exactly how to instrument your application.
For the example, we will use the canvas
example that comes with Qt, and which is located in
examples/qt3/canvas for Qt 3 and in
examples/qt4/canvas for Qt 4.
This application is a very simple vector drawing application. By clicking on the canvas view, a new rectangle is inserted. Rectangles can be moved by dragging (clicking, and moving with the left mouse button held down).
To verify that the canvas inserts and moves the rectangles correctly, we need to access its API, so that we can to query their sizes and positions.
To create the bindings we must instrument the AUT or additional components and produce a bindings library (also called a wrapper library).
First we will instrument the application. To do this we will use the squishidl (Section 15.4.3) tool—this parses C++ header files and generates binding code that exposes the application's API to the script languages. The tool provides a number of command line options which are all explained in the squishidl (Section 15.4.3) section in the Tools Reference Manual (Chapter 15). Instead of running squishidl manually for every file, we will use froglogic's build tool (called “build”), which considerably simplifies the task. build uses a JavaScript-like language to describe build rules and targets and is a portable alternative to make.
![]() | Building Wrappers |
|---|---|
Even if you don't use build for your applications, we recommend using it for generating bindings libraries. |
To use build to create the application bindings, create a file
called Buildroot in your application's source
directory. This file must have a single line containing the absolute
path of the Squish installation directory. For example, if Squish is
installed in /usr/local/squish, the
Buildroot file would contain this single line:
/usr/local/squish
This will ensure that build can locate all the files necessary to create a bindings library.
![]() | Source Build Required |
|---|---|
Even if you use a binary version of Squish you will need Squish in
source form to create bindings—in particular the
|
When build is run, in addition to looking for the
Buildroot file that tells it where Squish is, it
also looks for a file called Buildsub. This file is
the build tool's equivalent to a Makefile for the
make tool—it describes the build targets and
should be in the same directory as the AUT's source code along with the
Buildroot file.
To generate an application bindings library for the canvas example
(whether for Qt 3 or Qt 4), we must put the following code in
the Buildsub file:
var canvasWrapperHeaders = ["canvas.h"];
var canvasWrapperLib = wrapperLib("canvas",
canvasWrapperHeaders, stdIncs, ".");
First we define an array which lists all the header files that we want
squishidl to create wrappers for. In this case we want to wrap the API
defined in the canvas.h file.
To create the wrapper library, the build tool will call the
wrapperLib function. The first argument to this function
must be the name which we want to use for the wrapper library. Usually
this is the application's or component's name without a suffix (i.e. no
.exe or .dll on Windows). The
second argument is an array that lists the files that we want wrapped:
We pass the array that we created in the previous line for this purpose.
The third parameter specifies the include paths which should be used,
and for this we use the build tool's predefined stdIncs
array which contains all the standard include paths. The last parameter
specifies the directory where we want the wrapper library to be built.
Here we have just said to build it in the current directory.
If the files you want to wrap include files from other directories, you must add the directories where those files can be found to the include paths. For example:
var all_includes = stdIncs.concat(["/my/include/path"]);
And instead of passing the stdIncs array as the third
parameter, pass your own array (here, all_includes),
as the third argument to the wrapperLib function.
Now that the Buildsub file has all the information
it needs, we can run the build tool in the directory that contains the
Buildsub file. This will result in the bindings
library being generated.
There is one potential problem. At runtime the wrapper library will need to access the application's symbols (classes, functions, etc.), but they will only be accessible if they are exported.
How symbols are exported varies depending on the compiler. For example,
on Solaris and IRIX, the symbols are automatically exported by default.
But gcc on Linux doesn't do this, and
requires the addition of the -rdynamic option to be given
to the linker.
How you add the -rdynamic option depends on the build
system you use. If you are using Qt Development Frameworks's
qmake as your application's build tool, you
could add the following line to your project file:
linux-g++*:LIBS += -rdynamic
This will add the -rdynamic option to the link line on both
32- and 64-bit Linux when gcc is used. The
.pro file can also be safely used on other platforms
because on them this line will be ignored.
For Windows, exporting the symbols is a little more involved.
Essentially we have to do the same to the application as we would do
when we create a DLL. We must mark each class that we want to be
exported with the __declspec(dllexport) attribute.
This will generate an import library with the application's exported
symbols which we must link into the wrapper library (this last part is
automatically handled by the build tool).
The first part is to modify the application's build system to export the symbols and to create the import library.
If you are using qmake to build your
application, these lines must be added to the application's project
(.pro) file:
win32:DEFINES += TEST_EXPORT=__declspec(dllexport) unix:DEFINES += TEST_EXPORT=
If you don't use qmake, but use
nmake directly to compile your application,
add the following to the NMakefile file's
CXXFLAGS variable:
/DTEST_EXPORT=__declspec(dllexport)
If you also use make on Unix you can add a
corresponding line to the Unix Makefile's
CXXFLAGS variable:
-DTEST_EXPORT=
Now the export macro is defined correctly. But one more step is required so that the Windows linker will create the desired import library with the exported symbols: You must use the export macro in the code which should be exported.
In our canvas example we have the following class definitions defined in
canvas.h which we want to create bindings
for:
class CanvasInterface : public Qt
{
//...
};
class CanvasItem : public CanvasInterface
{
//...
};
class CanvasRect : public CanvasItem
{
//...
};
class CanvasModel : public QObject, public CanvasInterface
{
//...
};
class CanvasView : public QScrollView
{
//...
};
To let the Windows linker know that we want to export the symbols of
these classes, we must mark them with the export macro we have just
defined. So we modify the file by adding the TEST_EXPORT to
the class definitions as shown below:
class TEST_EXPORT CanvasInterface : public Qt
{
//...
};
class TEST_EXPORT CanvasItem : public CanvasInterface
{
//...
};
class TEST_EXPORT CanvasRect : public CanvasItem
{
//...
};
class TEST_EXPORT CanvasModel : public QObject, public CanvasInterface
{
//...
};
class TEST_EXPORT CanvasView : public QScrollView
{
//...
};
![]() | Qt Designer .ui files |
|---|---|
If the class is defined in a Qt |
![]() | Windows-specific |
|---|---|
On Windows, the squishidl tool creates introspection and automation
code only for classes, global functions, and variables which are
exported using the |
If you now rebuild your application, an import library with the exported
symbols called
will be
created.
<appname>.lib
The next step is to extend the wrapper library's
Buildsub file to import the application's exported
symbols. To do this we must add the following code to the
Buildsub file:
var importMacro;
if (windows)
importMacro = "TEST_EXPORT=__declspec(dllimport)";
else
importMacro = "TEST_EXPORT=";
setDefines(canvasWrapperLib, [importMacro]);
This defines a macro called TEST_EXPORT to import exported
symbols. Also, it adds it as a define to the canvasWrapper
library's complier line.
Now when using build to create the wrapper library, the bindings library will be correctly generated.
Depending on your application's architecture there might be extra libraries involved that the application or that the wrapped library depends on. Such dependencies will be inherited by the wrapper library. Since some platforms require that all undefined symbols are resolved at link time we will sometimes have to explicitly specify the extra libraries when linking the wrapper.
Such dependencies become apparent if you get "unresolved external symbol" (or similar) errors at link time when building the wrapper library.
Here's a build script snippet—i.e., an extract from a
Buildsub file—that demonstrates how to use
the extras property that is reserved for the
purpose of specifying additional link flags. It is based on above
example with the additional requirement that the MyUtils Windows DLL
defines some symbols required by the wrapped object:
// create an array property if it doesn't exist
if (!canvasWrapperLib.extras)
canvasWrapperLib.extras = [];
// push a new element onto the end of the array
canvasWrapperLib.extras.push("MyUtils.lib");
On a Unix-like system you would pass a switch like
-lMyUtils and possibly -L flags that tell the
linker which paths to search when looking for the library.
We have seen that is it sometimes necessary to change some build settings to make the application's API testable. If you want to test exactly the same build as you will ship, you can leave these settings on all the time since exporting the application's symbols is harmless.
If you want to make the API of a dynamic library testable, the library's symbols will be exported anyway (otherwise it wouldn't be possible to use the library). So for dynamic libraries, no extra work is necessary to export symbols.
If the wrapper library containing the application bindings follows the
naming convention
libAUT_NAMEwrapper.so (Unix),
libAUT_NAMEwrapper.dylib
(Mac OS X) or
AUT_NAMEwrapper.dll (Windows),
and it is located in an AUT path or a path listed in the
SQUISH_WRAPPER_PATH environment variable, the wrapper
library is automatically loaded when starting the AUT. This is
convenient when just wrapping the AUT's API. But if we also want to wrap
the APIs of additional components, it is usually more convenient to
create multiple wrapper libraries (one for each component).
For Squish to load multiple wrapper libraries, they must all be
located in an AUT path or a path listed in the
SQUISH_WRAPPER_PATH environment variable. To tell
Squish to load specific wrapper libraries, simply specify their names
in the test suite's suite.conf, as the value of the
WRAPPERS key. The name of the wrapper is what you specified
as first argument to the wrapperLib function in the
wrapper's Buildsub file.
Alternatively, you can specify which wrapper libraries to load in the Squish IDE via the Test Suite's Settings view (Section 16.2.14). All the available wrappers are listed in the Wrappers section—simply check the checkboxes of those you want to be loaded.

As the screenshot shows, the default wrappers—i.e., those for the relevant GUI toolkit—are not listed since they are always loaded automatically anyway.
Here's an example test script which inserts three rectangles into the
canvas. To verify that the rectangles were inserted correctly, we use
the functions, CanvasView::canvasModel,
CanvasModel::numItems, CanvasModel::item
(taking an integer index) and CanvasItem::rect. All of these
functions are available through the bindings library we created.
Here is the example script's code. The complete example test suite
can be found in examples/qt4/suite_canvas for
Qt 4. (A very similar example for Qt 3, that only has a Python
test script, is in examples/qt3/suite_canvas).
def checkRectangle(item, x, y, width, height):
test.compare(item.rect().x, x)
test.compare(item.rect().y, y)
test.compare(item.rect().width, width)
test.compare(item.rect().height, height)
def main():
snooze(1.5)
sendEvent("QMoveEvent", ":Canvas_QMainWindow", 572, 471, 538, 402)
# insert 3 rectangular items
mouseClick(":Canvas.CanvasView_CanvasView", 90, 56, 1, Qt.LeftButton)
mouseClick(":Canvas.CanvasView_CanvasView", 170, 173, 1, Qt.LeftButton)
mouseClick(":Canvas.CanvasView_CanvasView", 271, 96, 1, Qt.LeftButton)
snooze(0.5)
# retrieve reference to the canvas model
canvas = waitForObject(":Canvas.CanvasView_CanvasView")
model = canvas.canvasModel()
# check that it contains 3 items
test.compare(model.numItems(), 3)
# for each item, check that it has the correct geometry
checkRectangle(model.item(0), 90, 56, 50, 70)
checkRectangle(model.item(1), 170, 173, 50, 70)
checkRectangle(model.item(2), 271, 96, 50, 70)
# close and exit
sendEvent("QCloseEvent", ":Canvas_QMainWindow")
function checkRectangle(item, x, y, width, height)
{
test.compare(item.rect().x, x);
test.compare(item.rect().y, y);
test.compare(item.rect().width, width);
test.compare(item.rect().height, height);
}
function main()
{
snooze(1.5);
sendEvent("QMoveEvent", ":Canvas_QMainWindow", 572, 471, 538, 402);
// insert 3 rectangular items
mouseClick(":Canvas.CanvasView_CanvasView", 90, 56, 1, Qt.LeftButton);
mouseClick(":Canvas.CanvasView_CanvasView", 170, 173, 1, Qt.LeftButton);
mouseClick(":Canvas.CanvasView_CanvasView", 271, 96, 1, Qt.LeftButton);
snooze(0.5);
// retrieve reference to the canvas model
var canvas = waitForObject(":Canvas.CanvasView_CanvasView");
var model = canvas.canvasModel();
// check that it contains 3 items
test.compare(model.numItems(), 3);
// for each item, check that it has the correct geometry
checkRectangle(model.item(0), 90, 56, 50, 70);
checkRectangle(model.item(1), 170, 173, 50, 70);
checkRectangle(model.item(2), 271, 96, 50, 70);
// close and exit
sendEvent("QCloseEvent", ":Canvas_QMainWindow");
}
sub checkRectangle
{
my ($item, $x, $y, $width, $height) = @_;
test::compare($item->rect()->x, $x);
test::compare($item->rect()->y, $y);
test::compare($item->rect()->width, $width);
test::compare($item->rect()->height, $height);
}
sub main
{
snooze(1.5);
sendEvent("QMoveEvent", ":Canvas_QMainWindow", 572, 471, 538, 402);
# insert 3 rectangular items
mouseClick(":Canvas.CanvasView_CanvasView", 90, 56, 1, Qt::LeftButton);
mouseClick(":Canvas.CanvasView_CanvasView", 170, 173, 1, Qt::LeftButton);
mouseClick(":Canvas.CanvasView_CanvasView", 271, 96, 1, Qt::LeftButton);
snooze(0.5);
# retrieve reference to the canvas model
my $canvas = waitForObject(":Canvas.CanvasView_CanvasView");
my $model = $canvas->canvasModel();
# check that it contains 3 items
test::compare($model->numItems(), 3);
# for each item, check that it has the correct geometry
checkRectangle($model->item(0), 90, 56, 50, 70);
checkRectangle($model->item(1), 170, 173, 50, 70);
checkRectangle($model->item(2), 271, 96, 50, 70);
# close and exit
sendEvent("QCloseEvent", ":Canvas_QMainWindow");
}
proc checkRectangle {item x y width height} {
test compare [property get [invoke $item rect] x] $x
test compare [property get [invoke $item rect] y] $y
test compare [property get [invoke $item rect] width] $width
test compare [property get [invoke $item rect] height] $height
}
proc main {} {
snooze 1.5
sendEvent "QMoveEvent" ":Canvas_QMainWindow" 572 471 538 402
# insert 3 rectangular items
invoke mouseClick ":Canvas.CanvasView_CanvasView" 90 56 1 [enum Qt LeftButton]
invoke mouseClick ":Canvas.CanvasView_CanvasView" 170 173 1 [enum Qt LeftButton]
invoke mouseClick ":Canvas.CanvasView_CanvasView" 271 96 1 [enum Qt LeftButton]
snooze 0.5
# retrieve reference to the canvas model
set canvas [waitForObject ":Canvas.CanvasView_CanvasView"]
set model [invoke $canvas canvasModel]
# check that it contains 3 items
test compare [invoke $model numItems] 3
# for each item, check that it has the correct geometry
checkRectangle [invoke $model item 0] 90 56 50 70
checkRectangle [invoke $model item 1] 170 173 50 70
checkRectangle [invoke $model item 2] 271 96 50 70
# close and exit
sendEvent "QCloseEvent" ":Canvas_QMainWindow"
}
This wraps up the coverage of creating application bindings and accessing them from test scripts. Using this functionality, it is possible to implement very powerful, robust, and sophisticated tests.
Depending on your Squish edition, you can explore all the bindings (wrappers) that are available for Squish. Squish comes with its own set of wrapper libraries, but if you have created your own bindings for an AUT or for a library an AUT depends on, as described above, then you can explore those too.
Squish provides a simple stand-alone GUI application for exploring
wrappers. The application is called
wrapperexpl and is in Squish's
bin directory. Run this application from the
command line—or click or double-click it from Windows Explorer or
choose it in the Mac OS X Finder, as appropriate for your operating
system.
As soon as the wrapperexpl starts up it
presents its Open Wrapper Libraries dialog. This dialog lists the
built-in wrappers supplied with Squish (e.g.,
“squishqtwrapper”). Check the checkbox for any of these
that you want to explore. If you want to add your own wrapper libraries,
for each one, simply click the button and
choose the relevant .so,
.dylib or .dll file (depending
on platform) from the file chooser dialog, and make sure that the
library's checkbox is checked after it has been added.

Note that you must make sure that you only select wrapper libraries—they are easy to identify, since their filenames always contain the string “wrapper”.
After choosing Add..., the library will be added to the list in the Open Wrapper Libraries dialog.

The list of wrapper libraries that are shown are remembered for future use, but you can remove any you are not interested in, by clicking them and then clicking the button.
Click the button to load all the checked libraries into the Wrapper Explorer.
After selecting one or more libraries in the Open Wrapper Libraries dialog, a new window replaces the dialog—the Wrapper Explorer window. This window displays the classes from the wrappers, presenting them using a hierarchical tree view.

Expanding a class will show its parent class or classes, and any public enums, constructors, properties, and member functions that it has available.
For faster navigation, there is a Filter text field at the bottom of the window. When text is entered here, it will be used to filter the tree's entries. If all the text is in lowercase, the filter is applied case insensitively; but if the text has upper- and lowercase the filter is applied case sensitively. If Classes is selected in the bottom right combobox, the filter is only applied to class names—this is very useful for quickly finding a particular class. For fast navigation amongst class members, change the bottom right combobox to Members.
Selected items can be copied to the clipboard as text, with the indention that is used in the tree preserved. Items are selected by clicking them, and selections can be extended to include multiple items. For example, on Windows™, second and subsequent items can be selected by pressing the Ctrl key when clicking additional items. Alternatively, a range of items can be selected by clicking one (which selects it), and then pressing the Shift key while clicking another—this will result in the two clicked items being selected, along with all the items in between them. It is also possible to select all the items by clicking the | menu item.
![]() | Lazy Discovery |
|---|---|
For efficiency, child items are only discovered the first time an item is expanded. This means that selecting all items will produce different results depending on which items have been expanded. |