TestUnit TestHarness.lsl
<lsl> /////////////////////////////////////////////////////////////////////////////////// /////// /////// /////// /////// TestUnit_TestHarness /////// /////// /////// /////// This is the interface script that talks to the Coordinator. It should be included /////// in each TestUnit. It communicates with the TestScripts via linked message. /////// /////// //////////////////////////////////////////////////////////////////////////////////////
//TestUnit_TestHarness .1 -> initial framework 6.23.2007 //TestUnit_TestHarness .2 -> testing and minor bugfixes 7.2.2007
//////////////////////////////////////////////////////////////////////////////////////
// General Specification for TestUnit from https://wiki.secondlife.com/wiki/LSLTest
//////////////////////////////////////////////////////////////////////////////////////
//Each test unit will have a small amount of boiler plate code to talk to the controller and understand the communication enough to change to another channel, start tests, and report test results. //[edit] Requirements
// * All test units reset to a default test broadcast channel of 0. // * All test units will be in a test group and the unit knows the group name. // * All test units have a name. // * Register to a coordinator on announcement of registration. // * Change communication channel on request of the coordinator // * Run its tests on the request of the coordinator // * Report test results with multiple levels of output // o verbose: everything // o normal: failures and summary information // o quiet: summary information only
//////////////////////////////////////////////////////////////////////////////////////
//
// Command Protocol
//
//////////////////////////////////////////////////////////////////////////////////////
//
// All commands, input,output,chat, or linked message will be :: separated
// lists in string form.
//
//////////////////////////////////////////////
// CHAT commands
//////////////////////////////////////////////
//
// Chat commands will be on the specified controlChannel
//
//////// INPUT ///////////
//
// The first element of a chat command can be only one of three things.
//
// ALL - A general indicator for all testUnits to process the command
// unitName - The specific name of this unit
// groupName - The specific name of the group this unit belongs to
//
// This first element of the chat input is parsed, and the remainder of the command
// is sent to the parseCommand function. The follow are the current input commands
//
////
// Reset - sends link message toAllChannel calling for reset, and then resets this script
// format example -> ALL::Reset
//
// RegisterUnit - sends out chat on controlChannel unit registration information
// format example -> ALL::RegisterUnit
//
// RunTest - clears passFail and sends out linked message to TestScript
// format example -> ALL::RunTest
//
// Report - sends out linked message to TestScript with broadcastChannel information
// format example -> ALL::Report
//
// SetReportType - sends out linked message to TestScript with Report Level change
// format example -> ALL::SetReportType::NORMAL
//
// UpdateUnitStatus - sends out unit status information on chat controlChannel
// format example -> ALL::UpdateUnitStatus
//
// SetControlChannel - changes controlChannel to value provided
// format example -> ALL::SetControlChannel::-1234
//
//
//////// OUTPUT ///////////
//
// Registration - Response to RegisterUnit command, send registration information to Coordinator
// format example -> Registration::unitKey::00000-0000-0000-00000::unitName::TestUnit1::groupName::Group1
//
// UpdateUnitStatus - Response to request to send out unit status information
// format example -> UpdateUnitStatus::unitKey::00000-0000-0000-00000::unitStatus::PASS
//
//
//
//////////////////////////////////////////////
// LINK MESSAGE commands
//////////////////////////////////////////////
//
// link message commands will be sent out on the toAllChannel, and recieved on the passFailChannel
//
//////// INPUT ///////////
//
// passFail - status of test sent by TestScript
// format example -> PASS
//
//////// OUTPUT ///////////
//
// RunTest - activation command to start test
// format example -> RunTest
//
// Reset - calls for script resets on toAllChannel
// format example -> Reset
//
// Report - sends channel and report type to TestScript
// format example -> Report::controlChannel::0::reportType::NORMAL
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Global Variables
integer toAllChannel = -255; // general channel - linked message integer passFailChannel = -355; // test scripts channel for communicating pass/fail - linked message
integer controlChannel = 1234; // command communication channel - chat
integer controlChannelListen; // handler for the listener event
integer debug = 0; // level of debug message integer debugChannel = DEBUG_CHANNEL; // output channel for debug messages
integer notecardLines; // key notecardRequestKey; // notecard globals key notecardLineRequest; // notecard stores UnitName and GroupName integer currentNoteLine; //
string groupName = "UNKNOWN"; // name of TestUnit group, used by Coordinator
// to select multiple units simultaneously
string unitName = "UNKNOWN"; // name of this TestUnit, used by Coordinator
// to select this unit specifically
string reportType = "NORMAL"; // determines length and content of report type
// NORMAL - failures and summary information // QUITE - summary information only // VERBOSE - everything
string passFail = "FAIL"; // current status of test script
// FAIL - default until successful completion of test // PASS - successful test completed
////////////////////////////////////////////////////////////////////////////////////////////////// ////////// ////////// Function: ParseCommand ////////// ////////// Input: string message - command to be parsed ////////// ////////// Output: no return value ////////// ////////// Purpose: This function calls various other functions or sets globals ////////// depending on message string. Allows external command calls ////////// from chat controlChannel ////////// ////////// Issues: no known issues ////////// ////////// ///////////////////////////////////////////////////////////////////////////////////////////////// ParseCommand(string message) {
if(debug > 1)llSay(debugChannel, llGetScriptName()+ "->ParseCommand: " + message); //reset all scripts if(message == "reset") { //broadcast to other scripts reset command llMessageLinked(LINK_SET, toAllChannel, "reset", NULL_KEY); //reset this script as well llResetScript(); } //RegisterUnit() else if(message == "RegisterUnit") { //Example output on controlChannel -> Registration::unitKey::00000-0000-0000-00000::unitName::TestUnit1::groupName::Group1 llSay(controlChannel, "Registration::unitKey::" + (string)llGetKey() + "::unitName::" + unitName + "::groupName::" + groupName); llMessageLinked(LINK_SET, toAllChannel, "Registered", NULL_KEY); }
//RunTest() else if(message == "RunTest") { //send link message on general channel initiating Test Scripts llMessageLinked(LINK_SET, toAllChannel, "RunTest", NULL_KEY); }
//Report() else if(message == "Report") { //send link message on general channel initiating report function from Test Scripts //Example output on toAllChannel channel -> Report::controlChannel::0::reportType::NORMAL llMessageLinked(LINK_SET, toAllChannel, "Report::controlChannel::" + (string)controlChannel + "::reportType::" + reportType, NULL_KEY); } else if(llSubStringIndex(message, "SetReportType::") != -1) { //parse value from string by deleting message upto index of :: //and set global reportType variable reportType = llDeleteSubString( message, 0, llSubStringIndex(message, "::") + 1); } // UpdateUnitStatus() else if(message == "UpdateUnitStatus") { //send chat message on control channel updating Coordinator with unit test status //Example output on control channel -> UpdateUnitStatus::unitKey::00000-0000-0000-00000::unitStatus::PASS llSay(controlChannel, "UpdateUnitStatus::unitKey::" + (string)llGetKey() + "::unitStatus::" + passFail ); } //SetControlChannel() //example SetControlChannel::1 else if(llSubStringIndex(message, "SetControlChannel::") != -1) { //parse value from string by deleting message upto index of :: //and set global controlChannel variable controlChannel = (integer)llDeleteSubString( message, 0, llSubStringIndex(message, "::") + 1); ActivateUnit(); }
} //end ParseCommand
////////////////////////////////////////////////////////////////////////////////////////////////// ////////// ////////// Function: Initialize ////////// ////////// Input: no input parameters ////////// ////////// Output: no return value ////////// ////////// Purpose: This function initializes any variables or functions necessary ////////// to get us started ////////// ////////// Issues: no known issues ////////// ////////// ///////////////////////////////////////////////////////////////////////////////////////////////// Initialize() {
//initiate data server call to begin reading notecard notecardRequestKey = llGetNumberOfNotecardLines("TestUnit_nc");
}
////////////////////////////////////////////////////////////////////////////////////////////////// ////////// ////////// Function: ActivateUnit ////////// ////////// Input: no input parameters ////////// ////////// Output: no return value ////////// ////////// Purpose: This function activates any variables or functions necessary to get ////////// us running ////////// ////////// Issues: no known issues ////////// ////////// ///////////////////////////////////////////////////////////////////////////////////////////////// ActivateUnit() {
//remove possible listeners llListenRemove(controlChannel); //create new listeners controlChannelListen = llListen(controlChannel,"",NULL_KEY,"");
}
/////////////////////////////////////////////////////////////////////////////////////// //STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE STATE// /////////////////////////////////////////////////////////////////////////////////////// // // // // // DEFAULT STATE // // // // // /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// default { /////////////////////////////////////////////////////// // State Entry of default state // ///////////////////////////////////////////////////////
state_entry() { Initialize(); }
//////////////////////////////////////////////////////// // On Rez of default state // ////////////////////////////////////////////////////////
on_rez(integer start_param) { Initialize(); }
/////////////////////////////////////////////////////// // Listen of default state // ///////////////////////////////////////////////////////
listen(integer channel, string name, key id, string message) { if(debug > 0){llSay(debugChannel, llGetScriptName()+ ":listen:" + message);} if(debug > 0){llSay(debugChannel, llGetScriptName()+ ":channel:" + (string)channel);} //from Coordinator if(channel == controlChannel) { //parse :: separated list list commandCheck = llParseString2List( message, ["::"], [""] ); //expect ALL, groupName, or unitName before first :: //Example ALL::RegisterUnit if( ( llList2String( commandCheck, 0 ) == "ALL" ) || ( llList2String( commandCheck, 0 ) == groupName ) || ( llList2String( commandCheck, 0 ) == unitName ) ) { //send message to ParseCommand //in string format // leave out variable before first :: //and send rest of message to last list element // put it back together with a :: separators ParseCommand(llDumpList2String( llList2List(commandCheck, 1, llGetListLength(commandCheck) - 1 ) , "::" ) ); //end parseCommand } // end || if } //end channel if
} //end of listen
/////////////////////////////////////////////////////// // Link Message of default state // ///////////////////////////////////////////////////////
link_message(integer sender_number, integer number, string message, key id) { //if link message is on the correct channel if(number == passFailChannel) { //update the passFail status with incoming message passFail = message; //notify Coordinator of new status for this unit ParseCommand("UpdateUnitStatus"); } } //end of link message
/////////////////////////////////////////////////////// // data server of default // ///////////////////////////////////////////////////////
dataserver(key queryid, string data) { if(queryid == notecardRequestKey) // line number request { notecardLines = (integer)data; currentNoteLine = 0; notecardLineRequest = llGetNotecardLine("TestUnit_nc", currentNoteLine); } //end line number request if(queryid == notecardLineRequest) //reading a line from the notecard { // if the string "UnitName:" exists in the data string, parse the unitName from between the [] if(llSubStringIndex(data,"UnitName:") > -1) { unitName = llGetSubString(data, llSubStringIndex(data,"[") + 1,llSubStringIndex(data,"]") - 1); } // if the string "GroupName:" exists in the data string, parse the groupName from between the [] else if(llSubStringIndex(data,"GroupName:") > -1) { groupName = llGetSubString(data, llSubStringIndex(data,"[") + 1,llSubStringIndex(data,"]") - 1); } //if additional lines on the notecard if(currentNoteLine < notecardLines - 1) { currentNoteLine += 1; //initiate another line request from the dataserver notecardLineRequest = llGetNotecardLine("TestUnit_nc", currentNoteLine); }else { // Done setting up, turn on listener ActivateUnit(); } } //end line request } //end data server
} // end default </lsl>