LoadRunner VuGen scripting - How to automatically download file from server and save it to local disk?

LoadRunner records file transferring from server and does not record file saving.

What to do, if you have to save transferred file to local disk?
Continue reading, and you will get the solution :)

You can download file from a server with the web_url function.
See an example:

Image downloading:
web_url("logo.gif",
"URL=http://www.google.com/intl/en_ALL/images/logo.gif",
"Resource=1",
"RecContentType=image/gif",
"Snapshot=t1.inf",
LAST);

This code downloads Google's logo image:

To save this image as file to local disk, we have to perform these steps:
Capture image from server's response
Save captured data to local disk

How to capture image from server's response?

Use web_reg_save_param function with the following boundaries - "LB=\r\n\r\n", "RB=". These boundaries allows to capture the whole data from a body of server's response. Function will look like:
web_reg_save_param("prmLogoImage", "LB=\r\n\r\n", "RB=", LAST);

This function should be placed before web_url function. After execution, prmLogoImage parameter will contain GIF-file.

I will clarify briefly the meaning of boundaries - "LB=\r\n\r\n" and "RB=".
Please, read the basic concepts of HTTP protocol, read Request message section:

HTTP response consists of the following:
Headers, such as HTTP/1.1 200 OK or Content-Length: 3473
An empty line
A message body, containg text of requested page or file

So, Header and Message body should be separated by empty line.
First CRLF (that is, a carriage return (CR = "\r") followed by a line feed (LF = "\n")) ends last header, and second CRLF ( = "\r\n") creates empty line. All data, followed by second CRLF, are treated as message body.
To summarize - "LB=\r\n\r\n" says "start capturing from the beginning of message body", empty right boundary "RB=" says "capture data till the end of message".

Open LoadRunner and enable logging of data, returned by server:


Then execute script containing initial web_url function, and open Replay log:


As you see, Replay log contains "\r\n\r\n" at the end of server's response.
Also, pay attention, that server returns the length of file to be downloaded (Content-Length: 8558).

Save captured data to local disk
Saving captured binary data (in our case, GIF file) requires additional step - we have to determine the length (= size) of captured data.

Tips: The simplest way - strlen function - is not correct. Imagine, that that captured data contains embedded NULL characters ('\0'):
"123\0qwe"
The real size of captured data = 7 bytes ('1', '2', '3', '\0', 'q', 'w', 'e').
But strlen function will return value 3, because it counts bytes before the first NULL character ('\0').

To calculate size of captured data use lr_eval_string_ext function:
lr_eval_string_ext ("{prmLogoImage}", strlen("{prmLogoImage}") /* = 14*/, &szBuf, &nLength, 0, 0,-1);

lr_eval_string_ext function copies captured data into szBuf array and places a size of captured data into nLength variable. That's easy, I hope :) If not, Help will help :)

Tips: There is another way to get known the size of file to be downloaded. Remember, that server returned the length of file (Content-Length: 8558). So, you can extract this value using Correlation.

And the last action is to save binary data from szBuf array into a local file. I used standard fwrite function from C programming language:
fwrite(szBuf, len, 1, hFile);

Well, please see the whole source code:

Click the block to expand the source code:
int WriteDataToFile(char *szFileName, const char *szBuf, int len)
{
int hFile;

hFile = fopen(szFileName,"wb");
.....

Execute the source code, and you will see, that new file will be created and saved automatically - "C:\LogoImage.gif". And this is what we needed - Google's logo image.

How to record a file saving, performed by user from browser page?

The problem:
This is a frequently asked question - why file downloading from a browser page (right mouse click, then "Save Target As...") was not recorded in LoadRunner VuGen script?

The difference between functional and load testing:Actually, LoadRunner records a file transferring only, not downloading!
This is the principal difference between functional and load testing! I will describe it.
Functional testing is done from a user's viewpoint.


What does a user see? It sees windows, dialogs, UI cotrols.
For example, user can see downloaded html-page in a browser, or as plain text in a telnet session.
In this case, data transferring is the same (HTTP protocol), while data representation varies (browser or telnet session).
LoadRunner performs Load testing, i.e. it records and emulates data transferring between client and server. Data transferring only, not data displaying for user!
For the above example, html-page can be downloaded directly from web-server. Another variant - page can be downloaded from FTP-server (even using Secured FTP protocol - FTPS), and then data will be passed to browser. User will see the same page.
In this case, data representation is the same (browser page), while data transferring varies (HTTP or FTPS protocols).

So, remember the simple rule:

LoadRunner does not record client side activities!

Tips: That's why LoadRunner does not show browser windows during load testing.
Note: Please, do not tell me, that VuGen shows Run-Time Viewer during replay :)
The run-time viewer was developed specifically for script debugging. There is no dependency between data representations in Run-Time Viewer and in a real browser (for detailed info read Mercury Support Knowledge Base (http://kb-web.mercury.com, requires login), topic 14094 "What are the limitations of the Run-Time Viewer").

LoadRunner - how to convert a plain text to URL format

The task - How to convert a plain text string to URL-format in LoadRunner?

Several days ago I faced with a simple task - it was needed to convert a passed parameter (which was in a plain text form) to URL-format. In other words, I had to convert:
string "ab" to the same string "ab"
string "a b" to the string "a%20b"
string "a b_c%" to "a%20b%5Fc%25"
and so on ...

The cause - Why it was needed?
LoadRunner script contained parameters, which should be passed to URL directly. Some my parameters contained special characters like spaces (" "), quotes ("), percentage ("%"), asterisks ("*") and others. To be processed correctly by a browser and a web server, these characters should be translated and passed as their hex-codes.
For example:
a space character (" ") has hex-code 0x20, so it should be passed as "%20"
underline character (" ") has hex-code 0x5F, so it should be passed as "%5F"
The ways on how to perform the task.
To perform this task I decided to use LoadRunner's web_convert_param function. I didn't know at that time that this function does not satisfy my requirements. So, I was reluctant to write my own function EncodePlainToURL. Well, let's see both solutions.
web_convert_param function.

As Help says, web_convert_param function converts HTML to a URL or plain text.
So, I wrote this code:
char sIn[] = "t es%d$ + eprst_"; That means that space (" ") was converted to a plus ("+"). I expected to see "%20" instead of a plus character.

Actually, it seems that "+" and "%20" are twins. For example Google uses a plus to encode a space within a search query. For example, if the query is "some text", then the URL will be: http://www.google.com/search?hl=en&q=some+text&btnG=Google+Search

I was tried to encode spaces (and others special characters) to their hex-codes.
That's why the following function was written:


EncodePlainToURL function.

The logic is simple enough:
If the current character is a digits or an alphabetic letter, then pass it "as is"
Otherwise the current character is a special one. So, hex-code should be written in this case This is a code of EncodePlainToURL function:/* * EncodePlainToURL converts a plain text string to an URL-form string. * * Parameters: sIn - input string to be encoded to URL format * sOut - output buffer * Note: the size of "sOut" parameter should be at least equal to triple size * of "sIn" parameter plus one character(for end-terminator '\0') 

 Examples:
 "a" -> "a" * "a b" -> "a%20b" * "a b_cc:\/c%" -> "a%20b%5Fcc%3A%2Fc%25" */ 

char *EncodePlainToURL(const char *sIn, char *sOut) 
{
 int i; 
 char cCurChar; 
 char sCurStr[4] = {0}; 
 sOut[0] = '\0'; for (i = 0; cCurChar = sIn[i]; i++) 
 {
 // if this is a digit or an alphabetic letter

 if (isdigit(cCurChar) || isalpha(cCurChar)) 
 {
 // then write the current character "as is" 
 sprintf(sCurStr, "%c", cCurChar); } 
 else
 { 
 // else convert it to hex-form. "_" -> "%5F" 
 sprintf(sCurStr, "%%%X", cCurChar);
 }
 // append current item to the output string 
 strcat(sOut, sCurStr);
 } 
 return sOut; 
}
 The example of usage is: 
char sIn[] = "t es%d$ + eprst_"; char sOut[100];
 lr_output_message("%s", EncodePlainToURL(sIn, sOut));
 Execute the code, and see the result: 
The input string "t es%d$ + eprst_" was converted to "t%20es%25d%24%20%2B%20eprst%5F".

CHECK POINTS--LoadRunner web_reg_find function - How to verify web page content?

When you perform load testing, you have to be fully confident, that your application works correctly. It may be very usefull to check UI of application - is it shown correctly or not.

This verification can be performed with 2 ways: 
Using LoadRunner content verification with web_reg_find function  Running LoadRunner GUI Vusers (QTP or WR scripts) 
The present LoadRunner tutorial describes content verifications with web_reg_find function. I will explain and show a usage ofweb_reg_find function, its attributes and result.
To get additional information about the seconds approach (running LoadRunner GUI Vusers), please read the article How to execute QTP script from LoadRunner?
How to add LoadRunner content verification?Let's start with LoadRunner demo Web application - Web Tours.
I will show how to check that a greeting 'Welcome to the Web Tours site' is shown on a start page:

I've recorded a simple script on Web Tour application - it just opens a start page, logs in, and logs out:

After that: 
open the script in Tree View (with menu 'View/Tree View') 
select initial step ('Url: WebTours') 
select the text I would like to check on the page and 
right mouse click: Use default seetings in 'Find Text' dialog:

Then open your LoadRunner script in Script View (menu 'View/Script View') and you will see that web_reg_find function has been just added before the first function:

web_reg_find("Text=Welcome to the Web Tours site", "Search=Body", LAST); 
web_url("WebTours", "URL=...", ...

Note:web_reg_find function has been added before the page opening function (web_url)!
This is because LoadRunner web_reg_find function does not search for text on a page, it just registers a search request for a text string on an HTML page.

This is very important and I would like to pay your attention - web_reg_find function should be placed before the function, which loads a page. 
Description of web_reg_find function attributes
The simplest web_reg_find function can look like:

web_reg_find("Text=Welcome to the Web Tours site", LAST); 
This form means 'register a request to search for a "Welcome to the Web Tours site" text on a next Web page retrieved from a server.

Attribute 'Text=' contains a text, that should be searched.
If the check fails, the error is reported after the next action function executes:

Attribute 'Search=' defines the scope of the search: 
Body (default value) means to search in a body of server's response and its resources 
Headers means to search within a pages headers 
Noresource means to search in a body of HTML page retrived from server and do not searhc in its resources 
Example:
Please, see a server response:
The following web_reg_find function checks that server's name is 'Xitami':

web_reg_find("Text=Server: Xitami", "Search=Headers", LAST);

The next important attribute of web_reg_find function is 'SaveCount='. Use it to save a number of matches that were found.

Let me show an example on this attribute and you will understand it.
Imagine, that we have to get a number of 'A Coach class ticket for :' text on Itinerary page:
The following code: 
uses web_reg_find function with "SaveCount=" attribute (3rd line) before the Itinerary page loads 
then loads Itinerary page (6th line) 
extracts number of matches (8th line) and 
compares it to an expected value (9th line): 

int nFound; 

web_reg_find("Text=A Coach class ticket for :", "SaveCount=TextPresent_Count", LAST); 


// open Itinerary page 
web_image("Itinerary Button", "Alt=Itinerary Button", LAST); 


nFound = atoi(lr_eval_string("{TextPresent_Count}")); 
if (nFound == 11) 
lr_output_message("Correct number of 'Coach class ticket' text: %d", nFound); 
else 
{ 
lr_error_message("Incorrect number of 'Coach class ticket' text: %d", nFound); 
return 0; 


If you have additional info on 8th line:

nFound = atoi(lr_eval_string("{TextPresent_Count}")); please, read my article How to perform basic operations on LoadRunner parameters?

All previous examples generated errors when text was not present on a page. What about the case, when should check that a web page doesnot containa specific text, say 'Exception occurred'? For that we can use 'Fail=' attribute.

Possibles values: 
NotFound (default value) means to generate error if the text is not found on a page 
Found means to generate error if the text is found on a page 
For example, the following web_reg_find function 

web_reg_find("Text=Error occurred","Search=Body", "Fail=Found", LAST); will fail only if a web page contains "Error occurred" text.
If this text is not shown on a page, then function finishes successfully.

Tip: use this approach to verify that your application work correctly under heavy load. 
'Find Text' dialog for web_reg_find function
You can generate all content verifications manually or with 'Find Text' dialog.
I would recommend using of 'Find Text' dialog for LoadRunner beginners.

For example, there is an analogous 'Find Text' dialog options for previous web_reg_find function example (with 'Error occurred'):

As for me, I prefer writing web_reg_find function and its attributes manually. 
Other important info on web_reg_find function
I understand, that the present article is not comprehensive :)

How to get LoadRunner iteration number - using global variable?

How to determine the current iteration number?
The first way is to use LoadRunner parameter od 'Iteration Number' type.
The second way is to use a LoadRunner global variable.
I will explain this approach now. Also, you will see how to define and use global variables.

Define a global variable
You can add a new variable definition into vuser_init section of LoadRunner script:

Then we will add a variable incrementation and usage into Action section of LoadRunner script. For example, like this:
Please, note that LoadRunner does not require using of "extern" keyword.

And last step is to open LoadRunner Run-time Settings and to set the desired total iteration count. In my example I set it to 3:

That's all! Let's start our script and see Log-file:

As you can see, nIterationNumber variable changes its values with every new iteration.

Tip: do not forget to increment its value, for example with ++ operator.

Both proposed approaches (with LoadRunner parameter and with a global variable) are simple enough. Use any you like and track your LoadRunner iterations thoroughly :)

    1 comment:

    Nikolai Potapov said...
    This "global" counter is really a local to a context of that particular vuser.
    What if you wanted something shared and modified by all vusers, really a global counter? As an example comes to mind "Total passed transactions" counter.

    How to run LoadRunner Controller from command line?

    Today I will explain how to open and run LoadRunner Controller scripts from command line.
    This feature can decrease tester's manual efforts and thereby to increase your effectiveness and performance.
    You can open LoadRunner Controller scenario with:
    LoadRunner\bin\Wlrun.exe -Run -TestPath scenario.lrs -ResultName res_folder


    For example:
    (note: click the image to enlarge it)
    As a result of above command, LoadRunner Controller:
    starts
    opens Controller scenario
    executes it
    saves results to folder 'C:\Temp\LR_Res\result_0'

    After that, Controller finishes working and closes.
    Please, see files from created 'C:\Temp\LR_Res\result_0' folder:

    Tips: As you see, there is LoadRunner Result file (result_0.lrr). You can pass it automatically to LoadRunner Analysis for further processing.Refer for detailed information: Custom HTML report in LoadRunner Analysis from command line.

    Actually, Wlrun.exe can have several settings. There are their descriptions from HP LoadRunner Controller User's Guide (© HP/Mercury Interactive):

    TestPath 
    Path to the scenario.
    For example, C:\LoadRunner\scenario\Scenario.lrs
    If the path includes blank spaces, use quotation marks.

    Run 

    Runs the scenario, dumps all output messages into res_dir\output.txt and closes Controller

    InvokeAnalysis 

    Instructs LoadRunner to invoke Analysis upon scenario termination. If this argument is not specified, LoadRunner uses the scenario default setting.

    ResultName 
    Full results path. For example, "C:\Temp\Res_01"

    ResultCleanName 

    Results name. For example, "Res_01"

    ResultLocation 

    Results directory. For example, "C:\Temp"

    Note: ResultName (full path) = ResultLocation (directory) + ResultCleanName (name)

    Well, let's see different variants of how to open and run LoadRunner Controller script:

    Open LoadRunner Controller:

    Wlrun.exe

    Open LoadRunner Controller scenario 'scenario.lrs' and do not execute it:

    Wlrun.exe -TestPath scenario.lrs

    Open LoadRunner Controller scenario 'scenario.lrs', execute it, and save results to default folder ('C:\Documents and Settings\user_login\Local Settings\Temp\res'):

    Wlrun.exe -Run -TestPath scenario.lrs

    Open LoadRunner Controller scenario 'scenario.lrs', execute it, and save results to 'res_folder' folder:

    Wlrun.exe -Run -TestPath scenario.lrs -ResultName res_folder

    Open LoadRunner Controller scenario 'scenario.lrs', execute it, save results to 'res_folder' folder, and after that start Analysis on created results with default template:
    Wlrun.exe -Run -TestPath scenario.lrs -ResultName res_folder -InvokeAnalysis

    And please see more complex example for desert :)

    Open LoadRunner Controller scenario 'scenario.lrs', execute it, save results to 'res_folder' folder, and after that start Analysis on created results with default template. Repeat all these steps 10 times:

    set LR_HOME=C:\Program Files\Mercury\LoadRunner
    for /L %%i in (1,1,10) do "%LR_HOME%\bin\Wlrun.exe" -Run -TestPath "%LR_HOME%\scenario\memory_leak_crash.lrs" -ResultName C:\Temp\LR_Res\result%%i

    Result is:


    Well, where to use execution from a command line?
    It can be very useful in some cases, when you plain to:
    run LoadRunner Controller and pass its results to LoadRunner Analysis
    run LoadRunner scripts by schedule

    Examples on LoadRunner Regular Expressions

    How to use Regular Expressions in LoadRunner.

    Introduction:
    The present article is a summarizing of the LoadRunner Regular Expressions challenge and its results. Also, I added code for RegExp patterns/subpatterns matching.
    All LoadRunner Regular Expressions functions are shown with examples.

    Outline:

    1. How to check - whether RegExp pattern matches against a text or not
    2. How to get a matched strings (RegExp patterns and subpatterns)

    How to check - Whether RegExp pattern matches against a text or not


    I thanks Charlie Weiblen and Tim Koopmans for the solution. I modified it slightly.
    So, here it is:

    1. Download and unpack Binaries and Developer files for PCRE (Perl Compatible Regular Expressions).
      These and others files are available on Pcre for Windows page.
    2. Unzip downloaded archives into c:\pcreC:\pcre folder
    3. Сomment out the include for stdlib.h file in:
      • C:\pcre\include\pcre.h
      • C:\pcre\include\pcreposix.h
      Commented stdlib.h file
    4. In your LoadRunner script, add to globals.h:
      • #include "c:\\pcre\\include\\pcre.h"
      • #include "c:\\pcre\\include\\pcreposix.h"
      Edited globals.h file
    5. Add the match() function to vuser_init section:

      //////////////////////////////////////////////////////////////////////////
      /// 'match' function matches a 'pattern' against a given 'subject'
      /// It returns 1 for a match, or 0 for a non-match / error
      int match(const char *subject, const char *pattern)
      {
      int rc; // Returned code
      regex_t re; // Compiled regexp pattern

      lr_load_dll("c:\\pcre\\bin\\pcre3.dll");

      if (regcomp(&re, pattern, 0) != 0)
      return 0; // Report error

      rc = regexec(&re, subject, 0, NULL, 0);
      regfree(&re);

      if (rc != 0)
      return 0; // Report error
      else
      return 1;
      }

    6. Let's run sample LoadRunner script and check the result:
      As you can see, match() function works correctly. Using match() function, you can check - whether RegExp pattern matches against a text or not.

      It can be helpful, when you verify in LoadRunner that the text (RegExp pattern) matches the text on a downloaded page.

      I tested the match() function with different patterns and subject strings:
      #Subject stringPatternsResult of
      match()
      Is correct
      result?
      1abcdefb(c(.*))e1Yes
      2abcdefb(z(.*))e0Yes
      32008\\d{2,5}1Yes
      42008\\d{5}0Yes
      5abc 1st of May 2008xyz\\d.*\\d1Yes
      Note: Since LoadRunner uses ANSI C language, please do not forget to double backslashes (\\). For example, to match any digit character (0-9), use pattern "\\d".

      match() function is simple enough. But it searches only and it cannot extract matched subpatterns from the text. For example, we have to extract the name of month from these strings:
      • "abc 1st of May 2008xyz"
      • "abc 25th of February 2031"
      • etc
      We can use the following pattern:
      • \d.+([A-Z]\w+)\s+\d{4}

      The name of month will be matches by subpattern ([A-Z]\w+). How to extract the found text? You can use matchex() function for that. Let's discuss it in details...
    How to get a matched strings (RegExp patterns and subpatterns)

    To get a matched (found) strings, we have to update our match() function.
    That's why I created matchex() ('match' EXtended) function.

    1. Add the matchex() function to vuser_init section
      //////////////////////////////////////////////////////////////////////////
      /// 'matchex' (EXtended) function matches a 'pattern' against a given 'subject'
      /// It returns number of matches:
      /// 0 - for a non-match or error
      /// 1 and more - for successful matches
      int matchex(const char *subject, const char *pattern, int nmatch, regmatch_t *pmatch)
      {
      int rc; // Returned code
      regex_t re; // Compiled regexp pattern

      lr_load_dll("c:\\pcre\\bin\\pcre3.dll");

      if (regcomp(&re, pattern, 0) != 0)
      return 0; // Report error

      rc = regexec(&re, subject, nmatch, pmatch, 0);
      pcre_free(&re); // Release memory used for the compiled pattern

      if (rc < 0)
      return 0; // Report error

      // Get total number of matched patterns and subpatterns
      for (rc = 0; rc < nmatch; rc++)
      if (pmatch[rc].rm_so == -1)
      break;

      return rc;
      }

    2. Let's run sample LoadRunner script and check the result:
      matchex() function returns a number of matched patterns/subpatterns and fill an array in with information about each matched substring.


      What is an information about each matched substring?


      This info contains the offset (rm_so) to the first character of each substring and the offset (rm_eo) to the first character after the end of each substring, respectively.

      Note1: 
      The 0th element of the array relates to the entire portion of string that was matched.
      Note2: Subsequent elements of the array relate to the capturing subpatterns of the regular expression.
      Note3: Unused entries in the array have both structure members set to -1.

      Let's investigate it with the example. This is our subject string:
      ExampleThe replay log shows offsets for matched substrings:
      • Action.c(7): Matched 3 patterns
      • Action.c(10): Start offset: 1, End offset: 6
      • Action.c(10): Start offset: 2, End offset: 5
      • Action.c(10): Start offset: 3, End offset: 5

      Start offset: 1 and End offset: 6 match substring "bcdef".
      Note4: End offset is the first character after the end the current substring. That's why character "g" (with index 6) is not a part of matched string.

      As I've written in Note1, "bcdef" is the entire portion of string that was matched.
      Others items from an array relate to matched subpatterns.


      What is a subpattern in Regular Expression?
      It is a part of the RegExp pattern surrounded with parenthesis - "(" and ")".

      It's easy to get out the order of subpatterns. Just look through your pattern from left to right. When you find an open parenthes, this is a start of the current subpattern.
      Subpattern can be embedded.

      So, others captured subpatterns are:
      • Start offset: 2, End offset: 5 matches substring "cde".
        Note: current subpattern is "([acqz](.*))".
      • Start offset: 3, End offset: 5 match substring "de".
        Note: current subpattern is "(.*)".

      As you can see - this is not so difficult. :)
      Regular Expressions can be very powerful and useful in LoadRunner.
    Another example:

    Let's practise with an example I mentioned early:
    For example, we have to extract the name of month from these strings:

    • "abc 1st of May 2008xyz"
    • "abc 25th of February 2031"
    • etc
    We can use the following pattern:
    • \d.+([A-Z]\w+)\s+\d{4}
    The name of month will be matches by subpattern ([A-Z]\w+).

    Please, see LoadRunner script, which captures and prints name of months:




    Note: Pay attention that I use   arr[1] to get info about substring.

    As you remember, arr[0] contains info about the entire matched pattern, arr[1], arr[2], and so on contain info about matched subpattern.