Squish's C++ API

This section covers the C++ API that Squish provides to make it possible to achieve even tighter integration with the AUT, and to solve some specific problems that occasionally arise.

Recording Hints to Influence and Control the Event Recorder

Recording hints allow an application to influence Squish's event recorder while a test engineer records a test script. Using a recording hint an application can insert comments or function calls into the test script at particular points.

Recording hints are made possible by the RecordHint class. This class is supplied with Squish and is defined in the file recordhint.h in Squish's include directory. The public API is implemented inline in this file, so the application only needs to include the file itself—there is no need to link against an additional library.

To see how the RecordHint class is used in practice, we will review an example.

Let's assume that we have an application which defines a function called myfunc which we have also wrapped so that a test script can access it. After the user clicks a particular button in the application we want the test script to call myfunc. To do this, we add the following C++ code at the point where the button click is handled:

Squish::RecordHint myfunc_comment(Squish::RecordHint::Comment,
        "Call myfunc");
myfunc_comment.send();
Squish::RecordHint myfunc_caller(Squish::RecordHint::Function,
        "myfunc");
myfunc_caller.send();

Now when recording a script and clicking on the button, two extra lines in the test script will be generated, as the code snippet below illustrates:

def main():
    ...
    clickButton("....")
    # Call myfunc
    myfunc()
function main()
{
    ...
    clickButton("....");
    // Call myfunc
    myfunc();
}
sub main
{
    ...
    clickButton("....");
    # Call myfunc
    myfunc();
}
# encoding: UTF-8
require 'squish'
include Squish

def main
    # ...
    clickButton("....")
    # Call myfunc
    myfunc()
    # ...
end
proc main {} {
    ...
    invoke clickButton "...."
    # Call myfunc
    invoke myfunc
}

This small example shows when and how to use record hints. The complete API is in the recordhint.h file; look for the RecordHint class inside the Squish namespace.

Using the Qt Built-in Hook

In most cases, Squish hooks into the AUT without requiring any special preparation. However, in some cases this is not possible due to technical limitations of the operating system that prevent Squish from using a preloader. This applies to AIX, Android, and iOS as well as many embedded systems. Squish's built-in hook solves this problem.

The built-in hook needs to be added to the AUT's build steps. The necessary steps differ slightly depending on the build system used by the AUT. In the end, exactly one of the following functions must be called from application code. The configuration options for cmake and qmake will be explained in terms of these functions.

  • Squish::allowAttaching(<tcp_port_number>) - The AUT needs to be launched outside of Squish. It will listen for connections on the given port. This is the port number to use when registering the attachable AUT in Squish. This method is used on Android and iOS, for example.
  • Squish::installBuiltinHook() - Enables the AUT to be launched normally through Squish when using statically linked Qt, e.g. where dynamic preloading does not work.

On desktop operating systems an additional setting is required to enable the built-in hook, as instructed in Enforcing the Built-in Hook. Without this, Squish might try to hook the AUT twice, once via the built-in hook and once via dynamic hooking. Only one hooking method can be used at a time.

Using the Qt Built-in Hook with cmake

For Qt 6 or newer, Squish ships with a cmake module named SquishQtBuiltinHook that integrates the Qt Built-in Hook into a Qt application build.

Add the following statements to CMakeLists.txt in your Qt project to turn it into an Attachable AUT for Squish:

# Look for Squish, optional and allowed to fail
find_package(SquishQtBuiltinHook)

# other cmake rules

# Add Squish to application target if found
if(SquishQtBuiltinHook_FOUND)
    squish_qt_add_builtin_hook(<cmake_app_target> ATTACH_PORT <tcp_port_number>)
endif()

Afterwards execute cmake (or qt-cmake, a cmake helper shipped with Qt) as usual but with CMAKE_PREFIX_PATH extended to the Squish installation prefix whose Qt Built-in Hook should be included in the application:

/usr/local/Qt-6.2.5-static/bin/qt-cmake --fresh /path/to/application/sources -DCMAKE_PREFIX_PATH=/path/to/squish-for-qt-6.2.5-static

SquishQtBuiltinHook cmake functions

There is one cmake function that can be called:

squish_qt_add_builtin_hook(<target> [ATTACH_PORT <tcp_port_number>|BUILTIN|MANUAL] [EXTENSIONS <extname1> <extname2> ...])

The first (mandatory) argument is the build target to add the Qt Built-in Hook to.

There are three different modes of adding the Qt Built-in Hook and exactly one of them needs to be passed as an argument:

  • ATTACH_PORT <tcp_port_number> - Equivalent to calling Squish::allowAttaching(<tcp_port_number>) during application startup.
  • BUILTIN - Equivalent to calling Squish::installBuiltinHook() during application startup.
  • MANUAL - Adds the Squish include directory to the include path and defines HAVE_SQUISH. The application code has to call either Squish::installBuiltinHook() or Squish::allowAttaching(<tcp_port_number>) during startup.

For static Qt builds including Squish extensions can be controlled via the optional EXTENSIONS keyword, followed by a list of Squish extension names to bundle with the application target. If not set, the list of extensions that will get bundled are based on the list of Qt dependencies of the cmake application target. For example Squish's QtQuick support will be chosen automatically if the target depends on Qt6::Quick.

Using the Qt Built-in Hook with qmake

For Qt 4 or newer, Squish ships with a qmake include called qtbuiltinhook.pri that integrates the Qt Built-in Hook into a Qt application build.

There are three different modes of adding the Qt Built-in Hook, controlled by two qmake variables that need to be set before including qtbuiltinhook.pri:

  • SQUISH_ATTACH_PORT = <tcp_port_number> - Equivalent to calling Squish::allowAttaching(<tcp_port_number>) during application startup. This mode requires Qt >= 5.1.
  • SQUISH_BUILTINHOOK = 1 - Equivalent to calling Squish::installBuiltinHook() during application startup. This mode requires Qt >= 5.1.
  • Neither variable set - Adds the Squish include directory to the include path and defines HAVE_SQUISH. The application code has to call either Squish::installBuiltinHook() or Squish::allowAttaching(<tcp_port_number>) during startup.

Add the following statements to the qmake project file that builds the main application target. Doing so will turn the Qt application into an Attachable AUT for Squish:

SQUISH_ATTACH_PORT = tcp_port_number
include(/path-to-squish/qtbuiltinhook.pri)

All Squish hook configuration variables must be set before including qtbuiltinhook.pri.

If the application should be started by squishserver (only available on platforms that can run squishserver) instead, add the following statements:

SQUISH_BUILTINHOOK = 1
include(/path-to-squish/qtbuiltinhook.pri)

Afterwards execute qmake as usual to reconfigure the project build to include the Qt Built-In Hook of Squish, then build and deploy the project.

For static Qt builds, including Squish extensions needs to be configured explicitly by setting the SQUISH_WRAPPER_EXTENSIONS qmake variable to list all Squish extensions that should be included.

Example for a QtWidgets-based application:

SQUISH_ATTACH_PORT = 4711
SQUISH_WRAPPER_EXTENSIONS = squishqgraphicsview squishqtabwidget
include(/home/user/squish-qt-6.2-static/qtbuiltinhook.pri)

Example for a QtQuick-based application:

SQUISH_ATTACH_PORT = 4711
SQUISH_WRAPPER_EXTENSIONS = squishqtquick squishqtquicktypes
include(/home/user/squish-qt-6.2-static/qtbuiltinhook.pri)

Generic Qt Built-in Hook Instructions

The built-in hook can be added to arbitrary build systems. This works the same way as using the cmake module in MANUAL mode. Two changes to the AUT are required:

  1. Include the qtbuiltinhook.h header file, which can be found in Squish's include directory, in the application's code where the main function is defined or where the QApplication object is created.
  2. Call the Squish::installBuiltinHook or Squish::allowAttaching function as soon as you have created the QApplication object.

Example:

#include <QApplication>
#include "qtbuiltinhook.h"

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    Squish::installBuiltinHook();
    // ...
    return app.exec();
}

This is the only preparation needed to make your program testable on most platforms that don't support the preloading mechanism. It does not matter if you leave in this code on other platforms, since the function is smart enough to do nothing if it isn't needed.

The Squish::installBuiltinHook function is very lightweight and won't make any difference to the program's performance. Nonetheless, we recommend removing it for publicly released versions of the program. This can easily be done using an #ifdef that includes the header and the function call for testing builds and excludes them for release builds.

Details

The Squish::installBuiltinHook function performs the following actions:

  • If the environment variable SQUISH_PREFIX is not set, it does nothing and returns immediately.
  • Otherwise it tries to load the Qt toolkit support library squishqtwrapper and its dependencies from the lib (or bin) subdirectory in the directory specified by SQUISH_PREFIX, and tries to resolve and call an initialization function in that library. If it fails to find the library or finds it but fails to resolve the initialization function, it does nothing and returns.

The Squish::installBuiltinHook function returns true if the hooking succeeded, that is, the application is executed by Squish; otherwise it returns false.

Enforcing the Qt Built-in Hook

The built-in hook is meant as a fallback mechanism on platforms where the normal hooking doesn't work. So if you want to use the built-in hook on platforms where Squish supports non-intrusive hooking, Squish will still use the non-intrusive hooking mechanism by default, although the built-in hook is included in the AUT.

Nonetheless, it is possible to force the squishserver to use the built-in hook rather than Squish's non-intrusive hooking mechanism. This can be done by setting a squishserver configuration option (see Configuring squishserver):

squishserver --config setUsesBuiltinHook <aut> on

Clearing the AUT Hook Setting

To disable use of the built-in hook for a registered AUT again, call: squishserver --config setUsesBuiltinHook <aut> off

Attaching to a Running Application with the Built-in Hook

It is also possible to use the built-in hook mechanism to attach to a running application (see Attaching to Running Applications for more details on attaching to a running application).

To make an application attachable with the built-in hook, you must call the Squish::allowAttaching function after the QApplication has been created. The argument to this function is a port number that the application should listen on for a squishserver to connect to. The function is declared in qtbuiltinhook.h.

Here is the standard pattern for making an application attachable:

#include <QApplication>
#include "qtbuiltinhook.h"

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    Squish::allowAttaching(11233);
    //...
    return app.exec();
}
  • Include the file qtbuiltinhook.h that is in Squish's include directory.
  • Make the application listen on port 11233.

Rebuild the application with these changes to make it possible for Squish to attach to it. Now start the AUT using the start*aut program supplied with Squish (in the Squish tool's bin directory):

startaut --uses-builtin-hook aut

This starts the AUT running and listening on the specified port, so you can now attach to it from within a test script. The next step is to register the AUT as an attachable AUT as described in Register the Attachable AUT. See Attaching from a Script for details on how to attach to the application from a test script.

However, you might have to create or adjust up to four environment variables: SQUISH_PREFIX, LD_LIBRARY_PATH, DYLD_LIBRARY_PATH, and PATH to make the AUT attachable.

You cannot use startaut if an application is downloaded and started via a web browser for example. In such cases, activate the built-in hook by setting the SQUISH_PREFIX environment variable to point to a Squish for Qt directory.