Studio:How to synchronize scripts: Difference between revisions
| No edit summary | m (Text replace - 'Category:Script Writing' to 'Category:Studio:Script Writing') | ||
| (11 intermediate revisions by 5 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.   | |||
| 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 | ## 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) | ||
| { | { | ||
|    ## 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(); | |||
| } | } | ||
| ## Get  | ## Wait for the server to be ready to accept our calls. | ||
| my $ | ## 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) | |||
| { | { | ||
|      $main::ascript->MessageBox($ |      ## 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: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();