Studio:How to synchronize scripts
If a test scenario requires multiple scripts, synchronization between the scripts is required. For example, if testing a component requires you to simulate a dependent component it is sometimes easier to split the task into multiple scripts. One script drives the test, which launches a separate script non-blocking which simulates the dependent component.
The following example shows how communication between two scripts can be achieved using a simple client-server example. Here the client (host) calls the server (target) to get the current date and time as a string.
The interface between the client and server is defined in the header file TargetFunctions.h. A second, independent header file--ScriptSync.h--is added to the workspace to create a broadcast message that we use to communicate between the scripts.
While there are other ways of providing inter-script communication, the technique of using a broadcast message is the best choice in almost every situation. The broadcast message is by far the most lightweight and simple-to-implement alternative.
- Note
- An alternative to this synchronization technique using files is shown in the article Using Scripts to Simulate Missing Software Units.
Header Files
TargetFunctions.h
/* TargetFunctions.h
This defines the interface(s) of the function(s) we will be implementing
in our target (server) script.
*/
#define MAX_TIME_STRING 64
int get_time(char* szTime);
#ifdef _SCL
#pragma scl_function(get_time)
#pragma scl_ptr(get_time.szTime,"OUT","PRIVATE")
#pragma scl_string(get_time.szTime, MAX_TIME_STRING)
#endif /* _SCL */
ScriptSync.h
/* ScriptSync.h
By adding this file to a STRIDE workspace, you define a broadcast message
that can be used to communicate with one or more asynchronously running scripts
*/
#ifdef _SCL
#include <sr.h>
/* this message is used to tell the target message loop to terminate */
#define _BRD_SCRIPTSYNC_EXITLOOP (55555 | srMT_BRD)
#pragma scl_msg(_BRD_SCRIPTSYNC_EXITLOOP)
#endif
Server Script
The server script registers as the owner of get_time() and subscribes to the _BRD_SCRIPTSYNC_EXITLOOP broadcast message.
The script will service calls to get_time() until the subscribed broadcast message is received, which will cause the script to exit.
Target.pl
=head
Target.pl
Implements the functionality of get_time() which is declared in TargetFunctions.h
Script is intended to run asynchronously; will terminate upon receiving a broadcast
message with the name _BRD_SCRIPTSYNC_EXITLOOP
=cut
use strict;
use Carp;
use Win32::OLE;
use Time::gmtime;
Win32::OLE->Option(Warn => 3);
## subscribe to receive sync broadcast message
my $syncMsg = $main::ascript->Messages->Item("_BRD_SCRIPTSYNC_EXITLOOP")->User;
$syncMsg->Subscribe();
## take ownership of required interfaces implemented in this script
my $get_time = $main::ascript->Functions->Item("get_time")->Owner;
$get_time->Register();
## initialize exit variable
my $end = 0;
## Wait for requests from clients until exit
while ($end == 0)
{
## Wait for an event
my $event = $main::ascript->WaitForEvent();
## if the event is a get time request, get the time
## and return
if ($event->Name eq $get_time->Name)
{
$get_time->OutPointers->{szTime} = gmctime();
$get_time->{ReturnValue} = 1;
$get_time->Return();
}
## if the event is exit loop message, set the variable to exit the loop
elsif ($event->Name eq $syncMsg->Name)
{
$main::studio->Output->PrintMessage("Target: exiting");
$end = 1;
}
}
Client Script
The client script launches the server script if required and waits until the server is ready to accept requests. Then get_time() is called repeatedly until the user ends the test, at which time the _BRD_SCRIPTSYNC_EXITLOOP message is broadcast to terminate the server script.
Host.pl
=head
Host.pl
This script uses Target.pl to mock the function get_time() which is declared in
TargetFunctions.h
If Target.pl is not running, it will be started asynchronously.
get_time() is called and its returned information is displayed repeatedly in a message box
until message box's Cancel button is pressed.
=cut
use strict;
use Carp;
use Win32::OLE;
Win32::OLE->Option(Warn => 3);
## Start the server script running asyncronously if
## it isn't already running
my $serverScript = $main::studio->Workspace->Files->Item("Target.pl");
if (!$serverScript->IsRunning)
{
$serverScript->RunNonBlocking();
}
## Wait for the server to be ready to accept our calls.
## We will poll for the server to have taken ownership
## of the supported function that is registered last
while (!($main::ascript->Functions->Item("get_time")->Owner->IsRegistered))
{
$main::ascript->Sleep(100);
}
## Get the function's user object
my $get_time = $main::ascript->Functions->Item("get_time")->User;
my $continue = 1;
while ($continue)
{
## Call the function (blocking request to the server)
$get_time->Call();
my $ret = $main::ascript->MessageBox($get_time->OutPointers->szTime.
"\rRetVal: ".$get_time->ReturnValue,
"get_time()",
"OKCANCEL");
if ($ret ne "OK")
{
$continue = 0;
}
}
## Shut down the server by broadcasting message
$main::ascript->Messages->Item("_BRD_SCRIPTSYNC_EXITLOOP")->Owner->Broadcast();