Studio:How to synchronize scripts: Difference between revisions
No edit summary |
m (Text replace - 'Category:Script Writing' to 'Category:Studio:Script Writing') |
||
(6 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 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. | 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"> | <source lang="c"> | ||
#define | /* 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); | int get_time(char* szTime); | ||
#ifdef _SCL | #ifdef _SCL | ||
#pragma scl_function(get_time) | #pragma scl_function(get_time) | ||
#pragma scl_ptr(get_time.szTime, | #pragma scl_ptr(get_time.szTime,"OUT","PRIVATE") | ||
#pragma scl_string(get_time.szTime, | #pragma scl_string(get_time.szTime, MAX_TIME_STRING) | ||
#pragma | #endif /* _SCL */ | ||
#endif | </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> | </source> | ||
==Server Script== | ==Server Script== | ||
The 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"> | <source lang="perl"> | ||
## | =head | ||
my $ | Target.pl | ||
my $ | |||
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 | ## initialize exit variable | ||
my $end = 0; | my $end = 0; | ||
## Wait for requests from clients until exit | ## Wait for requests from clients until exit | ||
while ($end == 0) | while ($end == 0) | ||
Line 42: | Line 79: | ||
## Wait for an event | ## Wait for an event | ||
my $event = $main::ascript->WaitForEvent(); | my $event = $main::ascript->WaitForEvent(); | ||
## if the event is a get time request, get the time | ## if the event is a get time request, get the time | ||
## and return | ## and return | ||
if ($event->Name eq $ | 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> | </source> | ||
==Client Script== | ==Client Script== | ||
The 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"> | <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-> | $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-> | $main::ascript->Messages->Item("_BRD_SCRIPTSYNC_EXITLOOP")->Owner->Broadcast(); | ||
</source> | </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();