Studio:How to synchronize scripts: Difference between revisions
No edit summary |
m (Text replace - 'Category:Script Writing' to 'Category:Studio:Script Writing') |
||
(8 intermediate revisions by 4 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 22: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.
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();