Studio:Using Scripts to Simulate Missing Software Units

From STRIDE Wiki
Revision as of 22:31, 6 March 2008 by Timd (talk | contribs)
Jump to navigation Jump to search

Perl

TestScript.pl

use strict;
use Win32::OLE;
Win32::OLE->Option(Warn => 3);

# Create a new test in the parent suite
my $test = $main::testSuite->Tests->Add("Leap Year Positive Test");

# Start the mock script running asynchronously
StartMockScript("LeapYearMock.pl");
# Wait for the mock script to register as the owner of the function
WaitForOwnerRegistration("IsLeapYear");

# Initialize the test input values
my @input = (1600, 1604, 1996, 2000);

# Get the function user interface
my $f = $main::ascript->Functions->Item("IsLeapYear")->User;

# Iterate through the memebers of @input
for my $i (0..$#input)
{
    # Set the parameter value
    $f->ParameterList->{uYear} = $input[$i];

    # Call the function synchronously; the STRIDE runtime will route the call to the
    # currently registered function owner
    $f->Call();

    # Test the return value;
    # note that the value is returned as the name of the enumerator
    if ($f->ReturnValue eq "YES") {
        $test->{Status} = "PASS";
    }
    else {
        $test->{Status} = "FAIL";
        last;
    } 
}

# Notify the mock script that it should end
NotifyStopMockScript("LeapYearMock.pl");

#---------------------------------
# Starts a script in the current workspace running asynchronously
# 
# Param:
#   scriptName - Name of the script to be run (name with extension only; no path)
# Notes: 
# - The specified script must be a member of the current Studio Workspace
# - The shutdown notification trigger file is deleted if it exists
#---------------------------------
sub StartMockScript()
{
    my $scriptName = shift;

    # make sure trigger file isn't there
    unlink GetTriggerFileName($scriptName);
    
    my $file = $main::studio->Workspace->Files->Item($scriptName);
    if (!$file->IsRunning) {
        $file->RunNonBlocking();
    }
}

#---------------------------------
# Notifies an asynchronously running script that it should end
# 
# Param:
#   scriptName - Name of the script to notify (name with extension only; no path)
# Notes:
# - The specified script must be a member of the current Studio Workspace<br>
# - No notification occurs if the specified script isn't currently running
#---------------------------------
sub NotifyStopMockScript()
{
    my $scriptName = shift;
    
    my $file = $main::studio->Workspace->Files->Item($scriptName);
    if ($file->IsRunning) {
        # create trigger file informing mock script to end
        my $triggerName = GetTriggerFileName($scriptName);
        open FILE, ">", $triggerName;
    }
}

#---------------------------------
# Waits for the Owner registration of the specified function
#
# Param:
#   functionName - Name of the function to be waited upon
# Notes:
# - Once the function Owner is registerd, it can be called through the STRIDE runtime
# - The function gives up waiting after 1000 ms
#---------------------------------
sub WaitForOwnerRegistration()
{
    my $functionName = shift;

    my $f = $main::ascript->Functions->Item($functionName)->Owner;

    # note: time is expressed in seconds in perl
    my $startTime = time;
    do {
        $main::ascript->Sleep(100);
    } while (!$f->IsRegistered && time - $startTime < 2);    
}

#---------------------------------
# Returns the full path of a shutdown trigger file used to communicate between
# scripts running on different threads.
#
# Param:
#   scriptName - Name of the script to be notified
# Notes:
# - By convention, we use the existence of a file to notify an asynchronously running
#   script that it should end. The file is written in the current workspace directory.
#---------------------------------
sub GetTriggerFileName()
{
    my $scriptName = shift;
    return $main::studio->Workspace->Path."\\__$scriptName.shutdown";
}

LeapYearMock.pl

# standard STRIDE stuff
use strict;
use Win32::OLE;
Win32::OLE->Option(Warn => 3);

# get the owner instance of the function
my $f = $main::ascript->Functions->Item("IsLeapYear")->Owner;

# Register the script as the owner of the IsLeapYear() function.
# Now all calls to IsLeaprYear() will be routed to this implementation by
# the STRIDE runtime.
$f->Register();

# This causes ascript.WaitForEvent() to time out every 500 ms.
# We do this so that in the message loop below, the while() test
# will be evaluated when no messages are arriving.
$main::ascript->{WaitTimeoutPeriod} = 500;

do {
    # Wait for the runtime to notify us that an event has been routed to this script
    my $e = $main::ascript->WaitForEvent();

    if ($e->Type eq "FunctionOwner" && $e->Name eq "IsLeapYear") {
        # Get the passed-in parameter
        my $uYear = $e->ParameterList->uYear;
        # Call the mock function; set the Owner function return value
        $e->{ReturnValue} = IsLeapYear($uYear);
        # Return from the function call.
        $e->Return();
    }
    # uncomment the following lines to see the timeout error generated when no
    # envent is received after 500 ms of waiting
    #else {
    #    $main::studio->Output->PrintMessage($e->Type." - ".$e->Name);
    #}
} while (!IsTimeToQuit());

# Here we retrieve the value of a symbol #defined in a .h file seen by the
# STRIDE compiler
my $FirstGregorianYear = $main::ascript->Constants->Item("FIRST_GREGORIAN_YEAR");

#---------------------------------
# This is our script implementation of the fucntion we want to simulate (mock)
#
# Param: 
#   uYear - The year to be tested
# Return:
#   String "YES" or "NO"
# Notes:
#  - Any year that is less than FIRST_GREGORIAN_YEAR is considered not a leap year
#---------------------------------
sub IsLeapYear()
{
    # get the passed-in argument
    my $uYear = shift;

    if ($uYear < $FirstGregorianYear) {
        return "NO";
    }

    if ($uYear % 4) {
        return "NO";
    }

    if ($uYear % 100) {
        return "YES";
    }

    if ($uYear % 400) {
        return "NO";
    }

    return "YES";    
}

#---------------------------------
# Returns a value indicating whether the script should end
#
# Return:
#   0 -> the script should not end; 1 -> the script should end
# Notes:
# - By convention, we use the existence of a specific file to notify an asynchronously running
#   script that it should end. The file is written in the current workspace directory.
# - If the trigger file exists it is deleted
#---------------------------------
sub IsTimeToQuit()
{
    # check for existence of trigger file
    my $scriptName = $main::ascript->ScriptName;
    my $triggerName = GetTriggerFileName($scriptName);

    # check for file existence
    if (-e $triggerName)
    {
        # delete the trigger file
        unlink $triggerName;
        return 1;
    }
    return 0;
}

#---------------------------------
# Returns the full path of a shutdown trigger file used to communicate between
# scripts running on different threads
#
# Param: 
#   scriptName - Name of the script to be notified
# Return:
#   String
#---------------------------------
sub GetTriggerFileName()
{
    my $scriptName = shift;
    return $main::studio->Workspace->Path."\\__$scriptName.shutdown";
}

JScript

TestScript.js

// create a new test in the parent suite
var test = testSuite.Tests.Add("Leap Year Positive Test");

// start the mock script running asynchronously
startMockScript("LeapYearMock.js");
// wait for the mock script to register as the owner of the function
waitForOwnerRegistration("IsLeapYear");

// initialize the test input values
var arInput = new Array(1600, 1604, 1996, 2000);

// get the function user instance
var f = ascript.Functions.Item("IsLeapYear").User;

// iterate through the members of arInput
for (var nIndex in arInput) {
    // set the parameter value
    f.ParameterList.uYear = arInput[nIndex];
    
    // call the function synchronously; the STRIDE runtime will route the call to the
    // currently registered function owner
    f.Call();
    
    // test the return value; 
    // note that the value is returned as the name of the enumerator
    if (f.ReturnValue == "YES") {
        test.Status = "PASS";
    }
    else {
        test.Status = "FAIL";
        break;
    }
}

// notify the mock script that it should end
notifyStopMockScript("LeapYearMock.js");


/**
 * Starts a script in the current workspace running asynchronously
 * 
 * @notes 
 * - The specified script must be a member of the current Studio Workspace<br>
 * - The shutdown notification trigger file is deleted if it exists
 * @param scriptName Name of the script to be run (name with extension only; no path)
 */
function startMockScript(scriptName)
{
    var fso = new ActiveXObject("Scripting.FileSystemObject");

    // make sure that the trigger file isn't there
    var triggerName = GetTriggerFileName(scriptName);
    if (fso.FileExists(triggerName)) {
        // delete the shutdown trigger file if it exists
        fso.DeleteFile(triggerName);
    }

    // get the stride file object from studio
    var file = studio.Workspace.Files.Item(scriptName);
    if (!file.IsRunning) {
        // start the script running in a separate thread
        file.RunNonBlocking();
    }
}

/**
 * Notifies an asynchronously running script that it should end
 * 
 * @notes 
 * - The specified script must be a member of the current Studio Workspace<br>
 * - No notification occurs if the specified script isn't currently running
 * @param scriptName Name of the script to notify (name with extension only; no path)
 */
function notifyStopMockScript(scriptName)
{
    var fso = new ActiveXObject("Scripting.FileSystemObject");

    // get the stride file object from studio
    var file = studio.Workspace.Files.Item(scriptName);
    if (file.IsRunning) {
        var triggerName = GetTriggerFileName(scriptName);
        // create the trigger file; used to notify the script running asynchronously
        fso.CreateTextFile(triggerName);
    }
}

/**
 * Waits for the Owner registration of the specified function
 * @notes
 * - Once the function Owner is registerd, it can be called through the STRIDE runtime<br>
 * - The function gives up waiting after 1000 ms
 * @param functionName
 *               Name of the function to be waited upon
 */
function waitForOwnerRegistration(functionName)
{
    var f = ascript.Functions.Item(functionName).Owner;

    var timeStart = new Date();
    do {
        ascript.Sleep(100);
        var timeNow = new Date();
    } while (!f.IsRegistered & timeNow - timeStart < 1000);
}


/**
 * Returns the full path of a shutdown trigger file used to communicate between
 * scripts running on different threads.
 * @notes
 * - By convention, we use the existence of a file to notify an asynchronously running
 * script that it should end. The file is written in the current workspace directory.
 * @param scriptName Name of the script to be notified
 */
function GetTriggerFileName(scriptName)
{
    return studio.Workspace.Path + "\\__" + scriptName + ".shutdown";
}

LeapYearMock.js

// get the owner instance of the function
var f = ascript.Functions.Item("IsLeapYear").Owner;

// register this script as the owner of the IsLeapYear() function
// now all calls to IsLeapYear() will be routed to this implementation by
// the STRIDE runtime
f.Register();

// This causes ascript.WaitForEvent() to timeout every 500 ms.
// We do this so that in the message loop below, the while() test
// will be evaluated when no messages are arriving
ascript.WaitTimeoutPeriod = 500;

do {
    // wait for runtime to notify us that an event has been routed to this script
    var e = ascript.WaitForEvent();

    switch (e.Type) {
    case "FunctionOwner":
        if (e.Name == "IsLeapYear") {
            // get the passed-in parameter
            var uYear = e.ParameterList.uYear;
            // call the mock function; set the Owner function return value
            e.ReturnValue = IsLeapYear(uYear);
            // return from the function call
            e.Return();
        }
        break;

    /*  uncomment these lines to see the timeout errors generated when no
        event is received after 500 ms of waiting
    default:
        studio.Output.PrintMessage(e.Type+" - "+e.Name);
    */
    }
} while (!IsTimeToQuit());


// Here we retrieve the value of a symbol #defined in a .h file seen by the
// STRIDE compiler
var firstGregorianYear = ascript.Constants.Item("FIRST_GREGORIAN_YEAR").Value;

/**
 * This is our script implementation of the function we want to simulate (mock)
 * 
 * @param uYear  The year to be tested
 * @return
 * String "YES" or "NO"
 * @notes 
 * - Any year less than FIRST_GREGORIAN_YEAR is considered not a leap year
 */
function IsLeapYear(uYear)
{
    if (uYear < firstGregorianYear) {
        return "NO";
    }

    if (uYear % 4) {
        return "NO";
    }

    if (uYear % 100) {
        return "YES";
    }

    if (uYear % 400) {
        return "NO";
    }

    return "YES";
}

/**
 * Returns true or false, indicating whether this script should end
 * 
 * @notes 
 * - By convention, we use the existence of a specific file to notify an asynchronously running
 * script that it should end. The file is written in the current workspace directory.<br>
 * - If the trigger file exists it is deleted
 */
function IsTimeToQuit()
{
    var fso = new ActiveXObject("Scripting.FileSystemObject");

    // check for existence of trigger file
    var scriptName = ascript.ScriptName;
    if (fso.FileExists(GetTriggerFileName(scriptName))) {
        // delete the trigger file
        fso.DeleteFile(GetTriggerFileName(scriptName));
        return true;
    }
    return false;
}

/**
 * Returns the full path of a shutdown trigger file used to communicate between
 * scripts running on different threads.
 * @notes
 * - By convention, we use the existence of a file to notify an asynchronously running
 * script that it should end. The file is written in the current workspace directory.
 * @param scriptName Name of the script to be notified
 */
function GetTriggerFileName(scriptName)
{
    return studio.Workspace.Path + "\\__" + scriptName + ".shutdown";
}