Difference between revisions of "Studio:How to synchronize scripts"
m (Text replace - 'Category:Script Writing' to 'Category:Studio:Script Writing') |
|||
(7 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
− | If a test scenario requires multiple scripts synchronization between the scripts is required. For example if testing a component requires to simulate a | + | 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. | + | 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 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=== | ||
+ | <source lang="c"> | ||
+ | /* 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 */ | ||
+ | </source> | ||
+ | ===ScriptSync.h=== | ||
+ | <source lang="c"> | ||
+ | /* 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 | ||
− | + | </source> | |
− | + | ||
− | + | ==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=== |
− | + | <source lang="perl"> | |
− | + | =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; | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | ==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=== | ||
+ | <source lang="perl"> | ||
+ | =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(); | |
− | + | </source> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | [[Category: | + | [[Category:Studio:Script Writing]] |
Latest revision as of 16:31, 20 August 2009
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.
Contents
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();