Thursday, December 29, 2011

Simple LoadRunner Method to Post XML to Web Service

Here is a very simple method for posting XML to a web service from loadrunner using http protocol. This method is somewhat simpler than using the web service protocol.

XML Request In-Line
In this scenario:
  • url is saved to a parameter
  • xml request is saved to a parameter
  • http headers are defined
  • response validation is defined
  • request is sent

Here is the script:

Action()
{
    char *request_xml;


    // save web service url to param
    char *URL = "http://www.webservicex.com/globalweather.asmx";
    lr_save_string(URL, "URL_Param");


// save xml request to param
request_xml=
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
 "<soap:Body>"
  "<GetWeather xmlns=\"http://www.webserviceX.NET\">"
"<CityName>Seattle</CityName>"
"<CountryName>United States</CountryName>"
  "</GetWeather>"
 "</soap:Body>"
"</soap:Envelope>";


lr_save_string(request_xml, "REQUEST_XML_PARAM");
  
// add http headers
web_add_header("Content-Type", "text/xml; charset=utf-8");
        web_add_header("Host", "www.webservicex.com");
web_add_header("SOAPAction", "http://www.webserviceX.NET/GetWeather");


        // validate response
web_reg_find("Text=SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States", LAST);


// send request
lr_start_transaction("post_xml");


web_custom_request("post_to_http_jms_provider",
 "URL={URL_Param}",
 "Method=POST",
 "TargetFrame=",
 "Resource=0",
 "Referer=",
 "Mode=HTTP",
 "Body={REQUEST_XML_PARAM}",
 LAST); 


lr_end_transaction("post_xml", LR_AUTO);
}


Sample Output
Here is the sample output:

Virtual User Script started
Starting action vuser_init.
Web Turbo Replay of LoadRunner 9.10.0 for WIN2003; WebReplay85 build 5896   [MsgId: MMSG-27143]
Run Mode: HTML   [MsgId: MMSG-26000]
Run-Time Settings file: "E:\bin\PerformanceCenter\scripts\POST_XML_TEST\\default.cfg"   [MsgId: MMSG-27141]
Ending action vuser_init.
Running Vuser...
Starting iteration 1.
Starting action Action.
Action.c(7): Notify: Saving Parameter "URL_Param = http://www.webservicex.com/globalweather.asmx"
Action.c(21): Notify: Saving Parameter "REQUEST_XML_PARAM = <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Seattle</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(24): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(24): web_add_header("Content-Type") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(25): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(25): web_add_header("Host") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(26): web_add_header("SOAPAction") was successful   [MsgId: MMSG-26392]
Action.c(29): Registering web_reg_find was successful   [MsgId: MMSG-26390]
Action.c(32): Notify: Transaction "post_xml" started.
Action.c(34): Notify: Parameter Substitution: parameter "URL_Param" =  "http://www.webservicex.com/globalweather.asmx"
Action.c(34): Notify: Parameter Substitution: parameter "REQUEST_XML_PARAM" =  "<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Seattle</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(34): t=244ms: 277-byte response headers for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(34):     HTTP/1.1 200 OK\r\n
Action.c(34):     Cache-Control: private, max-age=0\r\n
Action.c(34):     Content-Type: text/xml; charset=utf-8\r\n
Action.c(34):     Content-Encoding: gzip\r\n
Action.c(34):     Vary: Accept-Encoding\r\n
Action.c(34):     Server: Microsoft-IIS/7.0\r\n
Action.c(34):     X-AspNet-Version: 4.0.30319\r\n
Action.c(34):     X-Powered-By: ASP.NET\r\n
Action.c(34):     Date: Thu, 29 Dec 2011 22:34:56 GMT\r\n
Action.c(34):     Content-Length: 728\r\n
Action.c(34):     \r\n
Action.c(34): t=264ms: 728-byte ENCODED response body received for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(34): t=265ms: 1103-byte DECODED response body for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(34):     <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.or
Action.c(34):     g/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
Action.c(34):     www.w3.org/2001/XMLSchema"><soap:Body><GetWeatherResponse xmlns="http://www.webserviceX.NE
Action.c(34):     T"><GetWeatherResult>&lt;?xml version="1.0" encoding="utf-16"?&gt;\r\n
Action.c(34):     &lt;CurrentWeather&gt;\r\n
Action.c(34):       &lt;Location&gt;SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States (KSEA) 47-27N 
Action.c(34):     122-19W 136M&lt;/Location&gt;\r\n
Action.c(34):       &lt;Time&gt;Dec 29, 2011 - 04:53 PM EST / 2011.12.29 2153 UTC&lt;/Time&gt;\r\n
Action.c(34):       &lt;Wind&gt; from the SE (140 degrees) at 9 MPH (8 KT):0&lt;/Wind&gt;\r\n
Action.c(34):       &lt;Visibility&gt; 10 mile(s):0&lt;/Visibility&gt;\r\n
Action.c(34):       &lt;SkyConditions&gt; overcast&lt;/SkyConditions&gt;\r\n
Action.c(34):       &lt;Temperature&gt; 46.0 F (7.8 C)&lt;/Temperature&gt;\r\n
Action.c(34):       &lt;DewPoint&gt; 39.9 F (4.4 C)&lt;/DewPoint&gt;\r\n
Action.c(34):       &lt;RelativeHumidity&gt; 79%&lt;/RelativeHumidity&gt;\r\n
Action.c(34):       &lt;Pressure&gt; 29.93 in. Hg (1013 hPa)&lt;/Pressure&gt;\r\n
Action.c(34):       &lt;Status&gt;Success&lt;/Status&gt;\r\n
Action.c(34):     &lt;/CurrentWeather&gt;</GetWeatherResult></GetWeatherResponse></soap:Body></soap:Envelope
Action.c(34):     >
Action.c(34): Registered web_reg_find successful for "Text=SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States" (count=1)   [MsgId: MMSG-26364]
Action.c(34): web_custom_request("post_to_http_jms_provider") was successful, 728 body bytes, 277 header bytes   [MsgId: MMSG-26386]
Action.c(44): Notify: Transaction "post_xml" ended with "Pass" status (Duration: 0.1738 Wasted Time: 0.0000).
Ending action Action.
Ending iteration 1.
Ending Vuser...
Starting action vuser_end.
Ending action vuser_end.
Vuser Terminated.



XML Request From Parameter File
The xml request message can also be saved in a parameter file rather than being defined in-line.  In this scenario:
  • url is saved to a parameter
  • xml request is saved in a parameter file which is set to update row on "sequential", update value on "once".
  • http headers are defined
  • response validation is defined
  • request is sent

Here is the script:

Action()
{
    // save web service url to parameter
    char *URL = "http://www.webservicex.com/globalweather.asmx";
    lr_save_string(URL, "URL_Param");
  
    // add http headers
    web_add_header("Content-Type", "text/xml; charset=utf-8");
    web_add_header("Host", "www.webservicex.com");
    web_add_header("SOAPAction", "http://www.webserviceX.NET/GetWeather");


    // validate response
    web_reg_find("Text=SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States", LAST);


    // send request
    lr_start_transaction("post_xml");


    web_custom_request("post_xml",
 "URL={URL_Param}",
 "Method=POST",
 "TargetFrame=",
 "Resource=0",
 "Referer=",
 "Mode=HTTP",
 "Body={Request_From_File}",
 LAST); 


    lr_end_transaction("post_xml", LR_AUTO);
}



Parameter File
Here is the parameter file Requst.dat, with the xml defined on a single line, parameter properties of update row on "sequential", update value on "once":

Request
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Seattle</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>



Sample Output
Here is the sample output:

Virtual User Script started
Starting action vuser_init.
Web Turbo Replay of LoadRunner 9.10.0 for WIN2003; WebReplay85 build 5896   [MsgId: MMSG-27143]
Run Mode: HTML   [MsgId: MMSG-26000]
Run-Time Settings file: "E:\bin\PerformanceCenter\scripts\POST_XML_FROM_FILE_TEST\\default.cfg"   [MsgId: MMSG-27141]
Ending action vuser_init.
Running Vuser...
Starting iteration 1.
Starting action Action.
Action.c(6): Notify: Saving Parameter "URL_Param = http://www.webservicex.com/globalweather.asmx"
Action.c(9): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(9): web_add_header("Content-Type") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(10): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(10): web_add_header("Host") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(11): web_add_header("SOAPAction") was successful   [MsgId: MMSG-26392]
Action.c(14): Registering web_reg_find was successful   [MsgId: MMSG-26390]
Action.c(17): Notify: Transaction "post_xml" started.
Action.c(19): Notify: Parameter Substitution: parameter "URL_Param" =  "http://www.webservicex.com/globalweather.asmx"
Action.c(19): Notify: Parameter Substitution: parameter "Request_From_File" =  "<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Seattle</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(19): t=210ms: 277-byte response headers for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(19):     HTTP/1.1 200 OK\r\n
Action.c(19):     Cache-Control: private, max-age=0\r\n
Action.c(19):     Content-Type: text/xml; charset=utf-8\r\n
Action.c(19):     Content-Encoding: gzip\r\n
Action.c(19):     Vary: Accept-Encoding\r\n
Action.c(19):     Server: Microsoft-IIS/7.0\r\n
Action.c(19):     X-AspNet-Version: 4.0.30319\r\n
Action.c(19):     X-Powered-By: ASP.NET\r\n
Action.c(19):     Date: Thu, 29 Dec 2011 22:33:25 GMT\r\n
Action.c(19):     Content-Length: 728\r\n
Action.c(19):     \r\n
Action.c(19): t=246ms: 728-byte ENCODED response body received for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(19): t=247ms: 1103-byte DECODED response body for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(19):     <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.or
Action.c(19):     g/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
Action.c(19):     www.w3.org/2001/XMLSchema"><soap:Body><GetWeatherResponse xmlns="http://www.webserviceX.NE
Action.c(19):     T"><GetWeatherResult>&lt;?xml version="1.0" encoding="utf-16"?&gt;\r\n
Action.c(19):     &lt;CurrentWeather&gt;\r\n
Action.c(19):       &lt;Location&gt;SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States (KSEA) 47-27N 
Action.c(19):     122-19W 136M&lt;/Location&gt;\r\n
Action.c(19):       &lt;Time&gt;Dec 29, 2011 - 04:53 PM EST / 2011.12.29 2153 UTC&lt;/Time&gt;\r\n
Action.c(19):       &lt;Wind&gt; from the SE (140 degrees) at 9 MPH (8 KT):0&lt;/Wind&gt;\r\n
Action.c(19):       &lt;Visibility&gt; 10 mile(s):0&lt;/Visibility&gt;\r\n
Action.c(19):       &lt;SkyConditions&gt; overcast&lt;/SkyConditions&gt;\r\n
Action.c(19):       &lt;Temperature&gt; 46.0 F (7.8 C)&lt;/Temperature&gt;\r\n
Action.c(19):       &lt;DewPoint&gt; 39.9 F (4.4 C)&lt;/DewPoint&gt;\r\n
Action.c(19):       &lt;RelativeHumidity&gt; 79%&lt;/RelativeHumidity&gt;\r\n
Action.c(19):       &lt;Pressure&gt; 29.93 in. Hg (1013 hPa)&lt;/Pressure&gt;\r\n
Action.c(19):       &lt;Status&gt;Success&lt;/Status&gt;\r\n
Action.c(19):     &lt;/CurrentWeather&gt;</GetWeatherResult></GetWeatherResponse></soap:Body></soap:Envelope
Action.c(19):     >
Action.c(19): Registered web_reg_find successful for "Text=SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States" (count=1)   [MsgId: MMSG-26364]
Action.c(19): web_custom_request("post_xml") was successful, 728 body bytes, 277 header bytes   [MsgId: MMSG-26386]
Action.c(29): Notify: Transaction "post_xml" ended with "Pass" status (Duration: 0.1629 Wasted Time: 0.0000).
Ending action Action.
Ending iteration 1.
Ending Vuser...
Starting action vuser_end.
Ending action vuser_end.
Vuser Terminated.

Varying Parameters in the Request

It is also quite easy to vary the input parameters passed in the request message using the function lr_xml_set_values as follows.  In this case: 
  • the field "CityName" in the request xml is specified
  • a new value for CityName is specified
  • the resulting request xml is put in a parameter
  • the updated request is sent
 // vary parameters
lr_xml_set_values("XML=\{Request_From_File\}",
"Query=//CityName",
"Value=Spokane",
"ResultParam=XML_With_Substitution",
LAST);

// add http headers
web_add_header("Content-Type", "text/xml; charset=utf-8");
        web_add_header("Host", "www.webservicex.com");
web_add_header("SOAPAction", "http://www.webserviceX.NET/GetWeather");

// validate response
web_reg_find("Text=FELTS FIELD, WA, United States", LAST);

// send request
lr_start_transaction("post_xml");

web_custom_request("post_xml",
 "URL={URL_Param}",
 "Method=POST",
 "TargetFrame=",
 "Resource=0",
 "Referer=",
 "Mode=HTTP",
 "Body={XML_With_Substitution}",
 LAST); 

lr_end_transaction("post_xml", LR_AUTO);

The request variables can also be pulled from a parameter file as follows:
        // vary parameters
lr_xml_set_values("XML=\{Request_From_File\}",
"Query=//CityName",
"Value=\{CityNameParameter\}",
"ResultParam=XML_With_Substitution",
LAST);

Sample Output


Action.c(32): Notify: Parameter Substitution: parameter "Request_From_File" =  "<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Seattle</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(32): Notify: Saving Parameter "XML_With_Substitution = <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Spokane</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(32): "lr_xml_set_values" succeeded, 1 match processed
Action.c(39): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(39): web_add_header("Content-Type") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(40): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(40): web_add_header("Host") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(41): web_add_header("SOAPAction") was successful   [MsgId: MMSG-26392]
Action.c(44): Notify: Transaction "post_xml" started.
Action.c(46): Notify: Parameter Substitution: parameter "URL_Param" =  "http://www.webservicex.com/globalweather.asmx"
Action.c(46): Notify: Parameter Substitution: parameter "XML_With_Substitution" =  "<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Spokane</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(46): t=556ms: 277-byte response headers for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=2)
Action.c(46):     HTTP/1.1 200 OK\r\n
Action.c(46):     Cache-Control: private, max-age=0\r\n
Action.c(46):     Content-Type: text/xml; charset=utf-8\r\n
Action.c(46):     Content-Encoding: gzip\r\n
Action.c(46):     Vary: Accept-Encoding\r\n
Action.c(46):     Server: Microsoft-IIS/7.0\r\n
Action.c(46):     X-AspNet-Version: 4.0.30319\r\n
Action.c(46):     X-Powered-By: ASP.NET\r\n
Action.c(46):     Date: Thu, 29 Dec 2011 22:53:27 GMT\r\n
Action.c(46):     Content-Length: 706\r\n
Action.c(46):     \r\n
Action.c(46): t=578ms: 706-byte ENCODED response body received for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=2)
Action.c(46): t=579ms: 1081-byte DECODED response body for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=2)
Action.c(46):     <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.or
Action.c(46):     g/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
Action.c(46):     www.w3.org/2001/XMLSchema"><soap:Body><GetWeatherResponse xmlns="http://www.webserviceX.NE
Action.c(46):     T"><GetWeatherResult>&lt;?xml version="1.0" encoding="utf-16"?&gt;\r\n
Action.c(46):     &lt;CurrentWeather&gt;\r\n
Action.c(46):       &lt;Location&gt;FELTS FIELD, WA, United States (KSFF) 47-41N 117-19W 609M&lt;/Location&g
Action.c(46):     t;\r\n
Action.c(46):       &lt;Time&gt;Dec 29, 2011 - 04:53 PM EST / 2011.12.29 2153 UTC&lt;/Time&gt;\r\n
Action.c(46):       &lt;Wind&gt; from the SSW (210 degrees) at 9 MPH (8 KT):0&lt;/Wind&gt;\r\n
Action.c(46):       &lt;Visibility&gt; 10 mile(s):0&lt;/Visibility&gt;\r\n
Action.c(46):       &lt;SkyConditions&gt; mostly clear&lt;/SkyConditions&gt;\r\n
Action.c(46):       &lt;Temperature&gt; 43.0 F (6.1 C)&lt;/Temperature&gt;\r\n
Action.c(46):       &lt;DewPoint&gt; 33.1 F (0.6 C)&lt;/DewPoint&gt;\r\n
Action.c(46):       &lt;RelativeHumidity&gt; 67%&lt;/RelativeHumidity&gt;\r\n
Action.c(46):       &lt;Pressure&gt; 29.99 in. Hg (1015 hPa)&lt;/Pressure&gt;\r\n
Action.c(46):       &lt;Status&gt;Success&lt;/Status&gt;\r\n
Action.c(46):     &lt;/CurrentWeather&gt;</GetWeatherResult></GetWeatherResponse></soap:Body></soap:Envelope
Action.c(46):     >
Action.c(46): web_custom_request("post_xml") was successful, 706 body bytes, 277 header bytes   [MsgId: MMSG-26386]
Action.c(56): Notify: Transaction "post_xml" ended with "Pass" status (Duration: 0.2966).
Ending action Action.
Ending iteration 1.
Ending Vuser...
Starting action vuser_end.
Ending action vuser_end.
Vuser Terminated.



Saving Parameters From XML Response
The XML response can be saved in a parameter "Response" as follows:

    // save response
    web_reg_save_param("Response", "LB=", "RB=", "Search=Body", LAST);


With the response, fields from the xml can be extracted as follows to later be used in the next xml request.  In this case field "FieldInResponseXml" is saved to parameter "ResponseFieldParam":
   lr_xml_get_values("XML=\{Response\}",
     "ValueParam=ResponseFieldParam",
     "Query=//FieldInResponseXml",
     LAST);

The parameter {ResponseFieldParam} can then be used to construct the next xml request.




Thursday, December 15, 2011

Loadrunner Technique for Handling Early Script Exits

The following shows a technique for handling the following type of scenario within a single script:
  • Percent users complete step 1: 100
  • Percent users complete step 2: 60
  • Percent users complete step 3: 10
  • etc.
In this case, all users begin the script, some of them continue on to the next part, some of them continue on to the third step, etc.  It is convenient to have this scenario handled within a single script.  Load can then be varied merely by varying the vuser count.
The user behavior controlling when they exit is handled by creating a parameter table with columns as follows
  • stepTwoPercentComplete (set to 60, e.g.)
  • stepThreePercentComplete (set to 10, e.g.)
  • etc.
The script will read the parameterized percentages, then generate a random number each iteration to determine how many steps to complete before exiting.

This technique allows a fairly complex scenario to be handled by a single simple script.

Sample Script
#include "as_web.h"
#include "lrw_custom_body.h"


char url[] = "URL=http://server001";


Action()
{   
int stepOnePercentComplete = 100; // 100% do step 1
int stepTwoPercentComplete = atoi(lr_eval_string("{stepTwoPercentComplete}")); // get param, e.g., 60% complete step 2
int stepThreePercentComplete = atoi(lr_eval_string("{stepThreePercentComplete}")); // get param, e.g., 10% complete step 3
   
  
    // Get a random percent to use in deciding whether and when to exit early in the script
    int randomPercent = (rand() % 100)+1;  // Get a random percent


lr_log_message( "--- Random Percent = %d", randomPercent );
    lr_log_message( "--- Percent users completing step 1 = %d", stepOnePercentComplete );
    lr_log_message( "--- Percent users completing step 2 = %d", stepTwoPercentComplete );
    lr_log_message( "--- Percent users completing step 3 = %d", stepThreePercentComplete );

   /****************************** STEP 1 *************************************/
   web_reg_save_param( "transactionResponse",
                       "LB=<title ID=titletext>",
                       "RB=</title>",
                       "Search=Body",
                       "NOTFOUND=Warning",
                        LAST );


   lr_start_transaction("step1");


   web_custom_request( "step 1",
                       url,
                       "Method=GET",
                       "Body=",
                       LAST );


   if( 0 != strstr( lr_eval_string( "{transactionResponse}" ), "Error" ) )  {
  lr_end_transaction( "step1", LR_FAIL );
  lr_error_message( "--- ERROR (exception found in response): %s", lr_eval_string( "{transactionResponse}" ) );
  lr_exit(LR_EXIT_ITERATION_AND_CONTINUE,LR_FAIL);
  }
   else {
  lr_end_transaction("step1", LR_PASS);
  lr_log_message( "--- Verify Response = %s", lr_eval_string( "{transactionResponse}" ));
   }
   /****************************** END STEP 1 *************************************/



   /****************************** STEP 2 *************************************/
    // DECIDE WHETHER USER EXITS BEFORE STEP 2
   if (randomPercent > stepTwoPercentComplete) {
  lr_log_message( "--- Exiting after step 1 because randomPercent > stepTwoPercentComplete (%d > %d)", randomPercent, stepTwoPercentComplete);
       lr_exit(LR_EXIT_ITERATION_AND_CONTINUE,LR_PASS);;
   }
   lr_log_message( "--- Continuing to step 2 because stepTwoPercentComplete >= randomPercent (%d >= %d)", stepTwoPercentComplete, randomPercent);


   web_reg_save_param( "transactionResponse",
                       "LB=<title ID=titletext>",
                       "RB=</title>",
                       "Search=Body",
                       "NOTFOUND=Warning",
                        LAST );


   lr_start_transaction("step2");


   web_custom_request( "step 2",
                       url,
                       "Method=GET",
                       "Body=",
                       LAST );


   if( 0 != strstr( lr_eval_string( "{transactionResponse}" ), "Error" ) )  {
  lr_end_transaction( "step2", LR_FAIL );
  lr_error_message( "--- ERROR (exception found in response): %s", lr_eval_string( "{transactionResponse}" ) );
  lr_exit(LR_EXIT_ITERATION_AND_CONTINUE,LR_FAIL);
  }
   else {
  lr_end_transaction("step2", LR_PASS);
  lr_log_message( "--- Verify Response = %s", lr_eval_string( "{transactionResponse}" ));
   }
   /****************************** END STEP 2 *************************************/




   /****************************** STEP 3 *************************************/


   // DECIDE WHETHER USER EXITS BEFORE STEP 3
   if (randomPercent > stepThreePercentComplete) {
  lr_log_message( "--- Exiting after step 2 because randomPercent > stepThreePercentComplete (%d > %d)", randomPercent, stepThreePercentComplete);
  lr_exit(LR_EXIT_ITERATION_AND_CONTINUE,LR_PASS);;
   }
   lr_log_message( "--- Continuing to step 3 because stepThreePercentComplete >= randomPercent (%d >= %d)", stepThreePercentComplete, randomPercent);


   web_reg_save_param( "transactionResponse",
                       "LB=<title ID=titletext>",
                       "RB=</title>",
                       "Search=Body",
                       "NOTFOUND=Warning",
                        LAST );


   lr_start_transaction("step3");


   web_custom_request( "step 3",
                       url,
                       "Method=GET",
                       "Body=",
                       LAST );


   if( 0 != strstr( lr_eval_string( "{transactionResponse}" ), "Error" ) )  {
  lr_end_transaction( "step3", LR_FAIL );
  lr_error_message( "--- ERROR (exception found in response): %s", lr_eval_string( "{transactionResponse}" ) );
  lr_exit(LR_EXIT_ITERATION_AND_CONTINUE,LR_FAIL);
  }
   else {
  lr_end_transaction("step3", LR_PASS);
  lr_log_message( "--- Verify Response = %s", lr_eval_string( "{transactionResponse}" ));
   }
   /****************************** END STEP 3 *************************************/
}



Thursday, December 1, 2011

How To Send Compressed XML Messages Using Loadrunner

This post describes one method to gzip XML requests prior to sending them to a web service from LoadRunner using the http protocol.  This method uses the following basic procedure:
  1. Load the zlib.dll which is included with LoadRunner.
  2. Use the gzwrite method in zlib.dll to write your xml request to a file
  3. Read the gzipped bytes into a char array
  4. Save the char array with the gzipped bytes to a parameter
  5. Set the request headers specifying compression
  6. Post the compressed request to the web service
The following script shows one example using this method:
int requestPrepared = 0;
Action()
{
typedef void *gzFile;
gzFile file;
long infile;
char *buffer;
        char *filename = "test.gz";
int count;
int fileLen;


// URL
        char *URL = (char *)"http://SERVER:8080/MyService";
        lr_save_string(URL, "URL_Param");


// .............. gzip request ..............
if ( !requestPrepared ) {
lr_load_dll("zlib.dll"); 
file = (void *)gzopen(filename, "wb");
count = strlen(lr_eval_string("{Request}"));
fileLen = gzwrite(file, lr_eval_string("{Request}"), count);
lr_log_message("Gzipped %9d bytes: ", fileLen);
gzclose(file);

// open the gzip file
infile = fopen(filename, "rb");
if (!infile) {
lr_error_message("Unable to open file %s", filename);
return;
}

// get file length
fseek(infile, 0, SEEK_END);
fileLen=ftell(infile);
fseek(infile, 0, SEEK_SET);

// Allocate memory for buffer
buffer=(char *)malloc(fileLen+1);
if (!buffer) {
  lr_error_message("Could not malloc %10d bytes", fileLen+1);
  fclose(infile);
  return;
}

// Read file contents into buffer
count = fread(buffer, 1, fileLen, infile);
lr_log_message("Read %9d bytes from gzipped file: ", count);
fclose(infile);

// Save the buffer to a loadrunner parameter
lr_save_var( buffer, count, 0, "GZippedRequest");
free(buffer);
requestPrepared = 1;
}
// .............. end gzip request ..............


// Request Headers
web_add_header("Content-Type", "application/xml; charset=UTF-8");
web_add_header("Accept-Encoding", "gzip;q=1.0, identity; q=0.5, *;q=0");
web_add_header("Content-Encoding", "gzip");
        web_add_header("Cache-Control", "no-cache");
        web_add_header("Connection", "keep-alive");


// Validate response
web_reg_find("Text=<MyNode>MyValue</MyNode>", LAST);


lr_start_transaction("Post_request");


web_custom_request("Post_Request",
 "URL={URL_Param}",
 "Method=POST",
 "TargetFrame=",
 "Resource=0",
 "Referer=",
 "Mode=HTTP",
 "Body={GZippedRequest}",
 LAST); 


lr_end_transaction("Post_request", LR_AUTO);
}

Here is step-by-step walk-through:


  • Load the zlib.dll which is included with LoadRunner.  



  • lr_load_dll("zlib.dll"); 


  • Use the gzwrite method in zlib.dll to write your xml request to a file



  • file = (void *)gzopen(filename, "wb");
    count = strlen(lr_eval_string("{Request}"));
    fileLen = gzwrite(file, lr_eval_string("{Request}"), count);


  • Read the gzipped bytes into a char array



  • infile = fopen(filename, "rb"); 
    fileLen=ftell(infile); 
    buffer=(char *)malloc(fileLen+1); 
    count = fread(buffer, 1, fileLen, infile);


  • Save the char array with the gzipped bytes to a parameter



  • lr_save_var( buffer, count, 0, "GZippedRequest");


  • Set the request headers specifying compression



  • web_add_header("Accept-Encoding", "gzip;q=1.0, identity; q=0.5, *;q=0");
    web_add_header("Content-Encoding", "gzip");


  • Post the compressed request to the web service



  • web_custom_request("Post_Request", 
      "URL={URL_Param}",
      "Method=POST", 
      "TargetFrame=",
      "Resource=0",
      "Referer=",
      "Mode=HTTP",
      "Body={GZippedRequest}",
      LAST); 

    This technique could be varied in a number of ways.  One variation would be to parameterize the gzipped file's name per vuser, so that each vuser could send a different request as follows:
         // create parameter of type "Vuser ID" using format "file-vuser%03s.gz"
        char *filename = lr_eval_string("{filename}");

    Another variation would be to create a list of different parameterized requests up front in the init() section which could then be randomly sent in the Action() section.  With this method, you would not want to go to the file system on every iteration to vary request parameters unless the expected transaction rate is low, so you would want to create the gzipped requests once up front.


    Friday, November 11, 2011

    VisualCSV - A Free CSV Analyzer - CSV File to Chart, CSV File to Stats, CSV File to Histograms, CSV File Correlation Charts

    VisualCSV         Download Now

    VisualCSV is a free CSV analyzer that is:
    • A Java application 
    • Converts CSV File to Charts
    • Converts CSV File to Statistics
    • Converts CSV File to Histograms
    • Converts CSV File to Correlation Charts
    Performance testing, system monitoring, etc. often generate CSV files that must be analyzed, and typically you need to chart columns, calculate stats on columns, correlate columns, etc.

    VisualCSV is a java application that, given a csv file input, will generate most of the data you need automatically, including:
    • statistics on each column in the CSV file 
      • min
      • avg
      • max
      • standard deviation
      • variance, median
      • percentiles (such as 95th and 98th percentiles, or any list of percentiles you specify)
    • trend charts for each column in jpeg format
    • histogram charts for each column
    • correlation metrics between any pairs of columns
    • correlation charts showing correlations between any pairs of columns
    • transposed correlation charts reversing x and y axis.
    • charts created using jfreechart
    • stats created using apache commons math
    The following sections shos sample output generated from processing a csv file from windows performance monitor (perfmon) having the following columns:
    (PDH-CSV 4.0) (Pacific Standard Time)(480)
    \\CHELSTRPL006\Memory\Available Bytes
    \\CHELSTRPL006\Network Interface(HP NC382i DP Multifunction Gigabit Server Adapter _4)\Bytes Total/sec
    \\CHELSTRPL006\Network Interface(HP NC382i DP Multifunction Gigabit Server Adapter _4)\Output Queue Length
    \\CHELSTRPL006\PhysicalDisk(_Total)\% Disk Time
    \\CHELSTRPL006\PhysicalDisk(_Total)\Avg. Disk sec/Read
    \\CHELSTRPL006\PhysicalDisk(_Total)\Avg. Disk sec/Write
    \\CHELSTRPL006\Processor(_Total)\% Privileged Time
    \\CHELSTRPL006\Processor(_Total)\% Processor Time

    Summary stats
    The following csv file showing summary stats for each column is generated.


    Trend Charts
    For each column, a chart is generated such as this:

    Histograms
    For each column, a frequency histogram showing number of occurrences by bucket is generated such as this:
    The number of buckets is configurable.

    For each chart, a relative frequency histogram  is generated showing relative frequency by bucket is generated such as this:

    Correlations Between Columns
    For each pair of columns, a csv file showing correlations between pairs of columns is generated such as this:

    Correlation Charts
    For each pair of columns, a correlation chart and a transposed correlation chart (x and y swapped) is generated such as this:

    Download
    VisualCSV can be downloaded here:

    Usage
    • VisualCSV requires java in the system path.  It has been tested with java 1.6 and probably runs fine with 1.5 and 1.7 as well.
    • Download and unzip the files.
    • Analyze the sample CSV file from command line by running:
      • run.bat TestPerfmonFile.csv
    • This will analyze the test file and generate output in directory .\out.  The output should 

    Configuration

    Client configuration is found in the file client.config.

    Columns can be specifically included or excluded by putting them in specified include or exclude files which by default (as specified in client.config) are called 
    Column-Include-Names.properties
    Column-Exclude-Names.properties

    The include file can be empty or missing in which case all columns are analyzed.  Otherwise, the columns analyzed are all columns in the include file minus those in the exclude file.

    Likewise columns can be specifically included or excluded from being correlated with each other by putting them in specified include or exclude files which by default (as specified in client.config)  are called 
    Correlation-Include-Names.properties
    Correlation-Exclude-Names.properties

    Again, the include file can be empty or missing in which case all columns are correlated. Otherwise, the columns in the exclude file are subtracted from the includes.

    The client.config file includes the following values by default:


    # --- Functionality
    createTrendCharts=true
    computeCorrelations=true
    createCorrelationCharts=true


    # --- whether to include or exclude columns from analysis
    # --- put columns in this file to include them in analysis.  empty file or no file means analyze all columns.
    columnIncludeFile=Column-Include-Names.properties


    # --- put columns in this file to exclude them from analysis.
    columnExcludeFile=Column-Exclude-Names.properties


    # --- whether to include or exclude columns from correlation with other columns
    correlationIncludeFile=Correlation-Include-Names.properties
    correlationExcludeFile=Correlation-Exclude-Names.properties




    # --- Timestamp column index (0 = first column)
    hasTimestampColumn=true
    timestampColumnIndex=0


    # --- Java SimpleDateFormat date format for parsing timestamps
    timestampFormat=MM/dd/yyyy HH:mm:ss.S

    # Letter Date or Time Component Presentation Examples
    # G Era designator Text AD
    # y Year Year 1996; 99
    # M Month in year Month July; Jul; 07
    # w Week in year Number 27
    # W Week in month Number 2
    # D Day in year Number 189
    # d Day in month Number 10
    # F Day of week in month Number 2
    # E Day in week Text Tuesday; Tue
    # a Am/pm marker Text PM
    # H Hour in day (0-23) Number 0
    # k Hour in day (1-24) Number 24
    # K Hour in am/pm (0-11) Number 0
    # h Hour in am/pm (1-12) Number 12
    # m Minute in hour Number 30
    # s Second in minute Number 55
    # S Millisecond Number 978
    # z Time zone General time zone Pacific Standard Time; PST; GMT-08:00
    # Z Time zone RFC 822 time zone -0800


    # --- chart settings
    chartEveryNthDataPoint=1
    histogramLogScale=false


    # --- statistics settings
    createChartCorrelationGreaterThan=0.0
    createChartCorrelationLessThan=-0.0
    numHistogramBuckets=10
    percentiles=80,90,95,98,99


    # --- Output file locations
    outDir=out
    deleteStatsDirs=true
    outTrendDir=out/Trend
    outCorrelationDir=out/Correlation
    outHistogramDir=out/Histogram
    outputStatsFile=SummaryStats.csv