Vous êtes sur la page 1sur 94

LoadRunner

LoadRunner parameters:
Using parameters in Loadrunner VuGen script: Parameterizing is a powerful thing in LoadRunner! Now, I'm going to demonstrate some tips and uses of them. Well, imagine the following situation... Let's we have created VuGen script, which emulates the one user:

The problem: script runs correctly in VuGen for one user, but it fails for several concurrent users. This happens since we recorded the VuGen script for the specific user:

I marked "username" and "password" parameters and values with red lines. Application under test (AUT) does not allow several sessions of the same user. So, if second concurrent VuGen user logs in to application, he gets an error: The solution: to parameterize the VuGen script. Implementation: Right click on a value we want to parameterize (in our case this is a username - "load1") and select "Replace with a parameter" menuitem:

Name the new parameter as "UserName" and click OK btn:

Please, pay attention to "Parameter type" combobox. It contains interesting possible types, such as: "Date/Time", Iteration Number, Random number, even XML, and others. I will not describe them in the present post... I hope, I will have a chance to describe parameter types later. In any case, it depends on the opinion of the blog readers :) 1. Wow! the value "load1" will be changed with a UserName parameter:

We have just added the new parameter! It's wonderful, isn't it, dear friend? :) But where can we specify the real values for our parameter? How to configure it? Let's continue... 2. Right click on the just added parameter and select "Parameter properties":

By the way, this menu allows others operations with parameters. You can:

o o

"Restore original value (load1)" In other words, rollback your changes "Replace more occurrences" This menuitem will find for the initial string ("load1") in the script and will propose to replace it with the new parameter ({UserName}). Tips: Please, use this feature carefully. For example, let's there is "http://server/load1.htm" link in the script. Here, "load1.htm" is a name of page, but user. "Replace All" will produce incorrect link "http://server/{UserName}.

"Parameter properties" will be shown:

This dialog is a "heart" of any parameter. You can here:


o

specify the type of the parameter

o o o

create the pool of values for any parameter (add new values for any parameter) specify, how the parameter will be changed against interations and users and other different settings...

Tips: You can open dat-file (UserName.dat) and edit its content in the Notepad or Excel. MS Excel is useful if file contains values for several parameters. Tips: I recommend to read the LR Function Reference, topic "Data Assignment and Update Methods for File/Table Parameters". It contains the excellent table describing Update and Data Assignement methods ("Update value on" combobox and "Select next row" combobox correspondingly). There are explanations with examples which describes - what settings should be applied for your case. In the issue, the filled dialog will look like:

These settings specify:


o o

the list of values to be used ("load1", "load2", "John21", etc.) update method ("Once") and data assignment method ("Unique"). The combination means: "The unique value assigned in the first iteration is used for all subsequent iterations of the Vuser" So, that's exactly the same we need.

After that I perform the similar procedure (steps 1-6) for "Password" parameter:

Please, note:
o

You can store parameters in the one dat-file. As for me, I prefer this way. Separating test data from the code is a good idea. And using one data-file is very convenient. In this post I use two dat-file. Just for demonstration...

Ooh! Everything is completed I hope. Now, we have to create a scenario, add parameterized LoadRunner VuGen script and execute it :) Here you are:

All transactions passed. In other words, all concurrent user logged with their unique login/password combination. Each user had his own session. The summary: Parameterizing allows simplify load/performance testing: 1. Instead of writing a separate LoadRunner VuGen script for each virtual user, we added parameters for specific information (username & password) and used the one script for all users. 2. Parameterizing allows decrease maintenance cost. If application under test is changed, one test should be updated for 100 (e.g.) users, but 100 tests for each user 3. Supporting of tests is a real pleasure :) To add new user, a test has to update dat-files only. ****************************************************************************** ******************************************************************************

What are LoadRunner parameter and parameterization:


This is a simple question - what is LoadRunner parameter? I've reread LoadRunner VuGen User's Guide v9, trying to find the exact definition of LoadRunner parameter. That's strange, but LoadRunner Help does not contain the exact answer on this question. In this article, I will answer basic questions, connected to LoadRunner parameters. I think, LR beginners should study this article carefully :) So, I recommend to read it, if you want to know:

the definition of LoadRunner parameter LoadRunner parameter functions how to create parameter in LoadRunner VuGen script

which types of LoadRunner parameter are available how LoadRunner processes parameters - gets and sets their values other key concepts, connected to LR parameters

OK, let's start. I've recorded Web (HTTP/HTML) simple script on Web Tours demo web application. There are two first steps:

Please, notice that we use the following hard-coded username & password - jojo and jojo:
1. "Name=username", "Value=jojo", ENDITEM, 2. "Name=password", "Value=bean", ENDITEM,

They are hard-coded in the script! This is important.

What should we do, if we want to execute our script sequentially for different users, for example for jojo/bean, then for bob/lcdm, dm/psswrd, and so on? There are several solutions:

The simplest one is to record our script for each user. In this case, our pseudocode will look like:

1. Login as jojo/bean 2. Perform other actions under jojo/bean 3. 4. Login as bob/lcdm 5. Perform other actions under bob/lcdm 6. 7. Login as dm/psswrd 8. Perform other actions under dm/psswrd 9. 10. so on...

As you can see, our code is duplicated. Actually, this is not good idea (read this article to find out why it is).

We can remake our code:

1. Loop - perform next actions for each user name and password 2. Read user name and password into {UserName} and {Password} variables 3. Login as {UserName} and {Password} 4. Perform other actions under {UserName} and {Password}

So, what have I done? I do not use hard-coded values (jojo/bean, bob/lcdm, dm/psswrd, and so on). Instead, we use special variables - {UserName} and {Password}. They are parameters. The source code will look like:

So, now we can answer: Question: What is LoadRunner parameter? The simple answer is: LoadRunner parameter is a special variable. Note: We will investigate parameters and I will provide more exact definition later. Since LoadRunner parameter is a variable, then I compare it with standard stack-based variable from C language (for example - int i).

Declaration To declare stack variable you can just write:

1. int i;

This means, that name of variable is 'i', and its type is int. It's easy, isn't it? To declare LoadRunner parameter you have to: 2. select string, to be replaced with a parameter 3. right-click on the selected text 4. 'Replace with a parameter'

'Select or Create Parameter' dialog opens. 5. Here, you can enter name of your parameter:

Note, that you can see an original value as well ('jojo' in our case).

6. Specify type of parameter Parameter type is determined by the source of parameter data:

Once you specified name and type of parameter, you declared LoadRunner parameter. The above declaration of parameter uses GUI ('Select or Create Parameter' dialog). This is not the only approach. We can declare parameter and assign value with lr_save_ functions (lr_save_string, lr_save_int, lr_save_searched_string, lr_save_var, and lr_save_datetime). See these examples: 7. lr_save_string function

1. lr_save_string("abCDEfgID", "prmProductID"); 2. lr_output_message("ID is: %s", lr_eval_string("{prmProductID}"));

Here, I define new parameter ("prmProductID") and assign a value ("abCDEfgID") to it. So, the parameter contains this value. To make sure that the parameter contains correct value, please see the result of execution:

Please, note that we can write analogous code using a stack variable:

3. char *pszProductID = "abCDEfgID"; 4. lr_output_message("ID is: %s", pszProductID);

The result will be the same:

So, as you see, LR parameter is similar to standard variables. 8. lr_save_int function

1. lr_save_int(144, "prmCounter"); 2. lr_output_message("Counter: %s", lr_eval_string("{prmCounter}")); 3. 4. lr_save_int(-18, "prmCounter");

5. lr_output_message("Counter: %s", lr_eval_string("{prmCounter}"));

Result is:

Again, we can write analogous code using stack variables:

6. int i; 7. char pszCounter[12]; 8. 9. i = 144; 10. itoa(i, pszCounter, 10); 11. lr_output_message("Counter: %s", pszCounter); 12. 13. i = -18; 14. itoa(i, pszCounter, 10); 15. lr_output_message("Counter: %s", pszCounter);

The result will be the same too:

I think, you've received evidence, that LR parameter is similar to a variable. So, So, now you can ask: Question: What's the difference between LR parameter and stack-based variables? // That is the question (C) W. Shakespeare :) OK, I will answer - the LoadRunner parameters simplify:

Working with memory That is, you don't have to know, how many bytes allocate for a current value of parameter. You just use lr_save_string (for example) function, and LR allocates required number automatically. Working with data sources For example, you can indicate, that values of parameters will be stored in a file:

In this case, you don't have to write additional code for file processing. LoadRunner takes all routine procedures upon himself. For example - when and how update values from file, what is the current delimiter, and so on. Actually, these features (working with memory and data sources) are very comfortable from user's side. That's why LoadRunner parameter is used so extensively. ****************************************************************************** ******************************************************************************

How to perform basic operations on LoadRunner parameters?


LoadRunner functions and scripts, containing in the present article, will be interested for LoadRunner beginners in the first place. I will describe - how to perform basic mathematical operations on LoadRunner parameters:

How to convert LoadRunner parameter's value to integer number?

How to save integer number to LoadRunner parameter? How to perform mathematical operations on LoadRunner parameter?

You can use this article as a LoadRunner tutorial on LoadRunner parameter operations. Well... May I start? :) 1. How to convert LoadRunner parameter's value to integer number? Actually, this is not a difficult task. We have to use 'atoi' function. 'atoi' function converts a string to an integer value. Please, see the following code:

1. lr_save_string("543210", "prmCounter"); 2. lr_output_message("String value of prmCounter: %s", lr_eval_string("{prmCounter}")); 3. 4. i = atoi(lr_eval_string("{prmCounter}")); 5. 6. lr_output_message("Integer value of prmCounter: %d", i);

The result is:

The key line of code is:

7. i = atoi(lr_eval_string("{prmCounter}"));

We get parameter's string value using lr_eval_string function and after that atoi function converts it to int value. Apparently, it is easy :)

2. How to save integer number to LoadRunner parameter? There are several way how to convert integer and save it to parameter. 1. lr_save_int function. It saves an integer to a parameter. Code is very simple:

1. 2. 3. 4. 5. 6.

int i; i = 433; lr_save_int(i, "prmCounter"); lr_output_message("String value of prmCounter: %s", lr_eval_string("{prmCounter}"));

The result is:

2. sprintf function. It writes formatted output to a string. Then we use lr_save_string function to save the string to parameter:

1. 2. 3. 4. 5. 6. 7. 8.

int i; char szBuf[12]; i = 118; sprintf(szBuf, "%d", i); lr_save_string(szBuf, "prmCounter"); lr_output_message("String value of prmCounter: %s", lr_eval_string("{prmCounter}"));

The result is:

3. itoa function. It converts an integer to a string. And then we use lr_save_string function to save the string to parameter:

1. 2. 3. 4. 5. 6. 7.

int i; char szBuf[12]; i = 27; itoa(i, szBuf, 10); lr_save_string(szBuf, "prmCounter");

8. lr_output_message("String value of prmCounter: %s", lr_eval_string("{prmCounter}"));

The result is:

3. How to perform mathematical operations on LoadRunner parameter? Let's a parameter contains integer value and we have to double it (multiply by two). In this case, the algorithm is: 1. Convert and save LoadRunner parameter to integer number We discussed this operation above. 2. Multiply integer number by two H'm... Do you have guesses how to do it? :) 3. Save new integer number to LoadRunner parameter? This operation was discussed above too.

So, the final code is:

4. int i; 5. lr_save_string("11", "prmCounter"); 6. lr_output_message("String value of prmCounter: %s", lr_eval_string("{prmCounter}")); 7. 8. i = atoi(lr_eval_string("{prmCounter}"));

9. i *= 2; 10. 11. lr_save_int(i, "prmCounter"); 12. lr_output_message("String value of prmCounter: %s", lr_eval_string("{prmCounter}"));

And its result is:

So, as you can see, working with LoadRunner parameters is not difficult. ****************************************************************************** ******************************************************************************

How to save parameter value to other parameter in LoadRunner?


One my reader asked me - "How to save parameter value to other parameter in LoadRunner?". OK, there is a simple solution. To save parameter value to other parameter you have to: 1. Evaluate value of initial parameter Use lr_eval_string LoadRunner function.

2. Save the evaluated string to a second parameter Use lr_save_string LoadRunner function. Please, check the following code: // save string value to initial parameter lr_save_string("some string value", "prmStr1"); lr_output_message(lr_eval_string("Value of prmStr1: {prmStr1}")); // save the evaluated value to second parameter lr_save_string(lr_eval_string("{prmStr1}"), "prmStr2"); lr_output_message(lr_eval_string("Value of prmStr2: {prmStr2}")); Let's execute this code and check the result:

(click the image to enlarge it)

As you can see, both parameters (prmStr1 & prmStr2) have the same values - "some string value". So, we assigned the parameter value to other parameter successfully.

****************************************************************************** ******************************************************************************

How to get LoadRunner iteration number - using LoadRunner parameter?


In some cases, you have to know the current iteration number, which is being executed. It would be great to have something like lr_iteration_number() function. Unfortunately, LoadRunner does not provide that function... In the present article, I will describe how to get a current iteration nunber, which is being executed in LoadRunner VuGen or Controller. Actually, I know two ways how to get current LoadRunner iteration number: 1. LoadRunner parameter of 'Iteration Number' type 2. Global variable, which should be incremented each iteration Today, I will describe and explain first approach (parameter of 'Iteration Number' type). The second one (global variable) I will describe in the next article.

Well, we are starting: 1. LoadRunner parameter of 'Iteration Number' type To insert new parameter into LoadRunner VuGen script, select 'Vuser/Parameter List...' menu item:

'Parameter List' dialog will be opened. Then:


o o o

Click 'New' btn to create new LoadRunner parameter Set parameter name (I named it as 'prmIterationNumber') and Select its type - 'Iteration Number'

Please, see the screen shot on these steps:

'Iteration Number' type means that the parameter will be replaced with the current iteration number. This is logical. The default settings for just created parameter of 'Iteration Number' type will be:

Here, you can change text format for the new LoadRunner parameter. In my case, I will use default settings. So, Click 'Close' btn in 'Parameter List' dialog. We've just created the new parameter.

All we need is to use it. For that, insert the following code into your LoadRunner VuGen script:

4. lr_output_message("Current iteration #: %s", lr_eval_string("{prmIterationNumber}"));

Great! Now we are ready to execute our LoadRunner script. Since, we plan to test LoadRunner 'Iteration Number' parameter, edit LoadRunner Run-time Settings ans set 'Number of iteration' to 3 (or any other value you wish):

Execute the script in LoadRunner VuGen and see results:

As you can see, our parameter (prmIterationNumber) changes its value according to a number of current iteration. So, we can use it get current LoadRunner iteration number. To check the correctness, we have to execute our script in LoadRunner Controller and see results. I've create Controller scenario (5 users, 3 iterations) and executed it.

Please, see the results for one user (they are analogous for others us

Again, we can get current LoadRunner iteration number. So, this solution works correctly both for LoadRunner VuGen and LoadRunner Controller.

************************************************************************ ************************************************************************

How to organize user-defined functions into LoadRunner library?


Assume, you work and work in LoadRunner, create and create scripts. Eventually, you face the situation when your code is duplicated in many scripts. This is a wrong way! Take my word for it :) I will show - why it is and how to struggle against it. Duplicated code is an evil, because:

If you fix/modify a duplicated code, you have to edit each occurrence of this code. It needs add debug messages into duplicated code, you have to add debug functions into each occurrence of this code. If you decide to change the algorithm, implemented in a duplicated code, you are reluctant to find and edit it in each occurrence of this code.

If you have tens or hundreds VuGen tests, containing diplicated code, the support can become a nightmare. Rework will cost too much!!! You must avoid it from the beginning.

Well, what to do? You have create a user-defined library. It needs place your code into separate h-file. Let's assume, we will use the following function: sum(1, 4, -3) which calculates a sum of arguments. It's easy :) In LoadRunner VuGen, create new file and write this code:
1. 2. 3. 4. 5. 6. 7. 8. 9. #ifndef _AR_H_ #define _AR_H_ int sum(int nArg1, int nArg2, int nArg3) { return nArg1 + nArg2 + nArg3; } #endif // _AR_H_

Save it as "ar.h" into "Mercury\LoadRunner\include" folder. Actually, the name of file can be any. Out library will perform arithmetic operations (sum :) ), that's why I named the file as "ar.h". Your library file is created! To include it, use #include preprocessing directive in Action: #include "ar.h" Hint: If your file is not located in a default include folder ("Mercury\LoadRunner\include"), you have to specify full path, for example: #include "c:\\work\\projects\\include\\ar.h" Hint: Since you deal with C language, you have to use double backslash inside of path. That's all! Run your Action and enjoy:

As you see, our function Sum works correctly.

Now, I will answer ans show the following question: How to process global variables in LoadRunner VuGen script? Modify our "ar.h" from "Mercury\LoadRunner\include" folder:
1. #ifndef _AR_H_ 2. #define _AR_H_ 3. 4. int nGlobalValue = 0; 5. 6. int sum(int nArg1, int nArg2, int nArg3) 7. { 8. return nArg1 + nArg2 + nArg3; 9. } 10. 11. #endif // _AR_H_

Then, modify Action like this:

And add a second action ("Action2") to our script:

Note, that we added #include too. Hint: By the way, the script will work correctly even if you didn't add #include for second action. Nevertheless, I recommend to add this preprocessing. Execute our updated VuGen script, containing two actions:

As you can see, variable, located in h-file, is shared between actions. It saves his values between actions. In other words, variable, located in h-file, is a global variable. It can be used for LoadRunner actions communicating.

I hope, library of user-defined functions will simplify your scripts and decrease efforts for their maintenance. As a result, it will save your time and increase your performance :) ****************************************************************************** ******************************************************************************

Correlation:
LoadRunner Correlation - How to capture an array of dynamic data with web_reg_save_param function
Imagine, that server returns the following response:

(since blogspot.com doesn't show tags correctly, I'm reluctant to show server response as image) . And we have to capture dynamic values of all IDs (underlined with green lines). These values can be used later - say, for LoadRunner script correlation. As usual, several solutions exist :) Let's see them: 1. Insert five web_reg_save_param functions using "Ord=1" (2, 3, ...) attribute:

1. web_reg_save_param "Ord=1", LAST); 2. web_reg_save_param "Ord=2", LAST); 3. web_reg_save_param "Ord=3", LAST); 4. web_reg_save_param "Ord=4", LAST); 5. web_reg_save_param "Ord=5", LAST);

("ID1Value", "LB= value=\"", "RB=\"", ("ID2Value", "LB= value=\"", "RB=\"", ("ID3Value", "LB= value=\"", "RB=\"", ("ID4Value", "LB= value=\"", "RB=\"", ("ID5Value", "LB= value=\"", "RB=\"",

Tips: Please, note that web_reg_save_param function does not perform correlation. web_reg_save_param function just registers a request for correlation from the next server response. That's why web_reg_save_param function should be placed before action functions, such as: web_url, web_submit_form, and others. Tips: Enable extended logging for LoadRunner parameters substitutions. It will be helpful for script debugging:

OK, let's execute our script and see results - whether values will be captured or not:

As you can see, web_reg_save_param functions executed correctly and parameters contain values. I would remind you that, we used "Ord" attribute. It indicates the ordinal position or instance of the match. Read LoadRunner Help on web_reg_save_param function for detailed info. Then, you can perform any operations with saved parameters (for instance, read article How to perform basic operations on LoadRunner parameters?)

Another solution is to not use "Ord" attribute. Instead of it, we will extend boundaries to capture values from a server response.

2. Insert five web_reg_save_param functions with extended boundaries:

1. web_reg_save_param ("ID1Value", "LB=ID1\" value=\"", "RB=\"", LAST); 2. web_reg_save_param ("ID2Value", "LB=ID2\" value=\"", "RB=\"", LAST); 3. web_reg_save_param ("ID3Value", "LB=ID3\" value=\"", "RB=\"", LAST);

4. web_reg_save_param ("ID4Value", "LB=ID4\" value=\"", "RB=\"", LAST); 5. web_reg_save_param ("ID5Value", "LB=ID5\" value=\"", "RB=\"", LAST);

I extended left boundaries and included ID1, ID2, etc with inverted quote. Since new boundaries will define values explicitly, we don't need use "Ord" attribute. That's why I deleted "Ord" attribute from web_reg_save_param function. Let's execute new code and see results:

Values captured successfully! :) Ain't it fun? :)

I think, now is a time to introduce third way how to capture an array of dynamic data. For that, we will use "Ord=All" attribute of web_reg_save_param function:

3. Insert web_reg_save_param function using "Ord=All" attribute:

1. web_reg_save_param ("IDValues", "LB= value=\"", "RB=\"", "Ord=All", LAST);

It looks very simple, doesn't it? :) Let's execute it and see results:

Values captured. Great! There are several important items and I would like to pay your attention:
o o

We specified one web_reg_save_param function only with "Ord=All" attribute, and it captured all values. Captured values were saved into parameters with automatically generated names. Please, note that I specified initial parameter name - "IDValues". Values were saved into parameters "IDValues_1", "IDValues_2", "IDValues_3", etc. So, an array of values were created. Additional parameter was created automatically - "IDValues_count". It contains number of matches saved to the parameter array.

Please, see this example - how to use "_count" LoadRunner parameter to iterate all items in a array of captured values:

5. web_reg_save_param ("IDValues", "LB= value=\"", "RB=\"", "Ord=All", LAST);

6. 7. // get number of matches 8. nCount = atoi(lr_eval_string("{IDValue_count}")); 9. 10. for (i = 1; i <= nCount; i++) { 11. // create full name of a current parameter 12. sprintf(szParamName, "{IDValue_%d}", i); 13. 14. // output a value of current parameter 15. lr_output_message("Value of %s: %s",szParamName, lr_eval_string(szParamName)); 16. }

And the result of execution is:

(click to enlarge image). Using above example, you can simplify LoadRunner script correlation. If you have to capture an

array of values from server response, do not forget about web_reg_save_param function only with "Ord=All" attribute.

Summary:

I provided several solutions how to perform correlation and capture an array of dynamic values. Last solution (web_reg_save_param function using "Ord=All" attribute) is more simple and convenient. One call of web_reg_save_param function can capture five, ten or hundred values and place them into an array of parameters. It is convenient - LoadRunner correlation becomes easier.

****************************************************************************** ******************************************************************************

How to select correct boundaries for web_reg_save_param LoadRunner function?


I will explain and show - how to select correct boundaries for web_reg_save_param LoadRunner function. web_reg_save_param is used for LoadRunner script correlation. That's why every load tester should study this function thoroughly, if you wish your scripts work correctly. Imagine that the server returns the following part of HTML page:

Note: I will refer to this example later.

1. What boundaries should be used in web_reg_save_param LoadRunner function to capture value of "ID1" (abcde)? The correct left boundary for web_reg_save_param function should be: o ID1" value="

And right boundary for web_reg_save_param is:


o

"

See the solution:

Do you know why it is? I will explain in details. To find out the boundaries, we have to see texts located on the left and on the right. What is the text located on the left? There are several variants:
o

" (inverted comma) Actually, inverted comma cannot be used as left boundary, because it is located in several places in server's responce. So, we have to extend the left boundary: =" (equal sign and inverted comma) These two characters also unusable, because it is located in several places too. For example, type="text" or value="abcde". So, we have to extend the left boundary again. So, I recommend to include unique part - ID1:

ID1" value=" These string occurs one time in server's response. So, it can identify uniquely the value of "ID1" (abcde).

Right boundary can be found easily. Since abcde is followed by inverted comma, we can assign inverted comma to right boundary. So, right boundary is ". We have found left and right boundaries, so the final web_reg_save_param function is:

6. web_reg_save_param("pID1Value", "LB=ID1\" value=\"", "RB=\"", LAST);

Hint: Since left and right boundaries contains inverted commas, we have to insert backslash before. Backslash followed by special characters are named Escape Sequences. Let's execute our code. For that, enable extended logging from Run-time Settings:

The result of LoadRunner script execution is the following:

As you can see, we specified correct boundaries for web_reg_save_param LoadRunner function. Value of ID1 has been captured correctly. Hint: Use lr_eval_string function to get captured value (value of LoadRunner parameter).

An example:

2. What boundaries should be used in web_reg_save_param LoadRunner function to user name (John Smith)? Using of web_reg_save_param LoadRunner function looks similar to previous example with a small difference - we have to extract user name from several lines:

0.

User name: 1. John Smith 2. bla-bla-bla

How to process this case? Piece of cake :) Left boundary for user name is "User name:" text followed by a new line. In terms of C language, a new line is "\r\n". So, the left boundary is "User name:\r\n". Right boundary is a new line only - "\r\n".

So, the final code, which can be used for LoadRunner script correlation, is:

3. web_reg_save_param("pUserName", "LB=User name:\r\n", "RB=\r\n", LAST);

The result of its execution is:

web_reg_save_param is a kind of "must know" LoadRunner function. The principle is simple - it tries parse server's response and find a text, located between left and right boundaries. In next articles, I will show different variants how to use web_reg_save_param LoadRunner function for script correlation. So, keep tracking carefully :)

Recording:
How to record LoadRunner script on FireFox?

By default, LoadRunner records Web applications on Internet Explorer browser. LoadRunner recording options look like on this image:

How to record LoadRunner script on FireFox? Answer: Use the following Recording options:

Use these settings with any LoadRunner Web protocol, such as:

Web (HTTP/HTML) AJAX (Click and Script) Web (Click and Script)

This way works correctly for all FireFox versions - 1.5, 2.0, 3.0.

************************************************************************************* *************************************************************************************

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

In my previous article, "How to record a file saving, performed by user from browser page?", I shown that user's activities are not recorded by LoadRunner. This is a rule! 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 :) So, Let's start. You can download file from a server with the web_url function. See an example: Image downloading: 1. 2. 3. 4. 5. 6. 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: 1. Capture image from server's response 2. 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 summurize - "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: 1. 2. 3. 4. 5. 6. 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. ************************************************************************************* *************************************************************************************

Content verification:

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: 1. Using LoadRunner content verification with web_reg_find function 2. 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 of web_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? 1. 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: o open the script in Tree View (with menu 'View/Tree View') o select initial step ('Url: WebTours') o select the text I would like to check on the page and o 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:

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

Please, 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. 2. Description of web_reg_find function attributes The simplest web_reg_find function can look like:

0.

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:


o o o

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':

4. 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:


o o o o

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):

9. int nFound; 10. 11. web_reg_find("Text=A Coach class ticket for :", "SaveCount=TextPresent_Count", LAST);

12. 13. // open Itinerary page 14. web_image("Itinerary Button", "Alt=Itinerary Button", LAST); 15. 16. nFound = atoi(lr_eval_string("{TextPresent_Count}")); 17. if (nFound == 11) 18. lr_output_message("Correct number of 'Coach class ticket' text: %d", nFound); 19. else 20. { 21. lr_error_message("Incorrect number of 'Coach class ticket' text: %d", nFound); 22. return 0; 23. } 24.

If you have additional info on 8th line:

25.

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 does not containa specific text, say 'Exception occurred'? For that we can use 'Fail=' attribute. Possibles values:
o o

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

28. 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. 3. '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. 4. Other important info on web_reg_find function I understand, that the present article is not comprehensive :) So, what to read next? o Function Reference on web_reg_find function I didn't explain several features - text flags (light LoadRunner regular expressions), TextPfx/TextSfx attributes and so on. o Function Reference on web_find function o HP Knowledge base

************************************************************************************* *************************************************************************************

Broken links detection - LoadRunner tutorial


Today, I will describe how to use LoadRunner for broken links detection.

'Broken' link is 'not valid' link. This link usually returns 404 Error - "Page not found". Another side of broken links is that images other resources are not displayed.

Using LoadRunner for broken links detection can be helpful, when you perform load testing of a site and you have to be sure, that all pages, images, applets, and other resources are available during the high server loading.

LoadRunner allows broken links detection during: 1. Script recording 2. Script execution 1. Broken links detection during LoadRunner script recording You have to set appropriate recording options. For that, click 'Options...' button from 'Star Recording' dialog:

'Recording Options' dialog opens. Here, set 'Add comment to script for HTTP errors while recording' option:

Please, note that you can see a description of the 'Add comment to script for HTTP errors while recording' option. Now, you are ready to record your script. Click 'OK' button on 'Recording Options' dialog and start recording. If LoadRunner find any HTTP errors, it will include comment into VuGen script. My application had some problems - there were several broken links for images there. So, LoadRunner generated the following script (click the image to view enlarged):

As you see, my application had several broken links to images and LoadRunner detected it

successfully. Also, you can find the point, where these broken links were used. This is previous function - web_url. LoadRunner inserted comments, which describe all occurred HTTP errors. Comments contain URL and server response. Since you have server responses, you can determine reasons of HTTP errors. Tips: For detailed info on HTTP status codes read this article from wikipedia and this one from w3.org. Tips: Read about the most 'popular' HTTP status code - 404.

2. Broken links detection during LoadRunner script execution LoadRunner can check broken links during the script execution too. You have to turn off one LoadRunner run-time option - 'Non-critical resource errors as warnings':

3.

Uncheck the 'Non-critical resource errors as warnings' option and execute previous script again. The result is:

Please, note that LoadRunner's Replay Log contains errors about images we mentioned before. Their links were broken, that's why LoadRunner generated errors. I hope, this LoadRunner tutorial was helpful. As you can see, the initial task - broken links detection with LoadRunner - can be resolved in two ways. You have to set appropriate option only. LoadRunner will do the rest :) ************************************************************************************* *************************************************************************************

Monitoring:
How to detect memory leaks with LoadRunner - visual tutorial
Today, I plan to share my experience on the memory leaks detecting. This article is a step-by-step instruction on how to use HP/Mercury LoadRunner to perform load testing for the purpose of memory leaks discovering.

The task: It needs perform testing of Web server to discover memory leaks. The solution:

1. Create LoadRunner VuGen script for your application (Web server in my script) Let's suppose, we have done it:

Actually, I will explain different tricks and features of VuGen scripts in the further posts. 2. Create LoadRunner Controller scenario for your application using the VuGen scripts. Here it is:

3. The next step is to add measurement monitors which are quantitative indicators of resources being monitored (for example, memory usage, CPU usage, handle and thread count, and so on). For that, on "Run" tab in the LoadRunner Controller, drag "Windows Resources" item from the "Available Graphs" tree and drop it to graphs area. See the screenshot:

Then right-click on the just added graph ("Windows Resources") and select "Add Measurements" from the pop-up menu. "Windows Resources" dialog will be shown:

The following actions are easy. I will add the name of server where the application (Web server in my case) will be run. This server can be a remote one. In this case, you have to make sure that LoadRunner will have an access to get info from the remote computer. Usually, I provide administrator rights for user which executes LR scripts. After that I select counters I want to measure during the load testing:

So, "Windows Resources" dialog looks like:

I selected the following counters: o % User Time for two processes (RService & tomcat5) and the whole system (_Total) o Available bytes in the system o Handle counts o Thread counts o Working Sets Also you may wish to add "Private Bytes" counter. Description for each counter is available on "Windows Resources" dialog. Obviously that the set of counters depends on the requirements and software to be tested.

Pay attention... If you select the "Windows Resources" graph, the list of counters will be shown in the lower part of the LoadRunner Controller:

4. Our mission is almost complete :) We have to run the scenario against a number of concurrent users (I user 30 users) and observe the graph displaying info on memory, CPU, handles and so on... I will show the result graph after 4 hrs of execution:

Pay attention to the yellow dotted line (memory usage of "RService" process) and the brown dotted line (habdle count counsumed by "RService" process) trends. They grow constantly during ~ 4.5 hours. I marked them out with red lines. So, this graph provides proofs that the application, I tested, contains memory leaks in the "RService" process. Several words about useful tricks and hints, connected to measurements ans graphs:

It is possible to export graphs to HTML from the LR. For that, right-click on the graph and select "Export to HTML...". It will generate HTML report containing the graph and a legenda for it. Very useful feature :) I recommend to set the range of time to be shown on the graph, to "Relative to scenario start". To perform this, right-click on the graph and select "Configure...". That allow to display statistics for the whole execution, but the latest N minutes. Sometimes, it is convenient to show more detailed graph. For that, double click on the graph - one big graph will be shown. To revert, double-click again. Also, you can change the number of graphs displayed - open menu View/View Graphs and set the required number.

5. So, now Dev team has to fix the problem in the "RService" process.. The last graph shows the results of load testing perfomed on the fixed application:

No any memory leaks found during 10 hrs. Memory leaks fixed! Isn't it a progress? :)

********************************************************** ********************************************************** LoadRunner coding/scripting:


Automated software testing and different ways on how to do more and quicker at a shorter time

Examples on LoadRunner Regular Expressions


I'm going to show and explain 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:\pcre

3. omment out the include for stdlib.h file in: o C:\pcre\include\pcre.h o C:\pcre\include\pcreposix.h

4. In your LoadRunner script, add to globals.h: o #include "c:\\pcre\\include\\pcre.h" o #include "c:\\pcre\\include\\pcreposix.h"

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: Result of match() 1 0 1 Is correct result? Yes Yes Yes

# 1 abcdef 2 abcdef 3 2008

Subject string

Patterns b(c(.*))e b(z(.*))e \\d{2,5}

4 2008 5 abc 1st of May 2008xyz

\\d{5} \\d.*\\d

0 1

Yes Yes

7. 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: o "abc 1st of May 2008xyz" o "abc 25th of February 2031" o etc We can use the following pattern:
o

\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:

The replay log shows offsets for matched substrings: o Action.c(7): Matched 3 patterns o Action.c(10): Start offset: 1, End offset: 6 o Action.c(10): Start offset: 2, End offset: 5 o 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:

o o

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. ************************************************************************************* *************************************************************************************

How to organize user-defined functions into LoadRunner library?


Assume, you work and work in LoadRunner, create and create scripts. Eventually, you face the situation when your code is duplicated in many scripts. This is a wrong way! Take my word for it :) I will show - why it is and how to struggle against it. Duplicated code is an evil, because:

If you fix/modify a duplicated code, you have to edit each occurrence of this code. It needs add debug messages into duplicated code, you have to add debug functions into each occurrence of this code.

If you decide to change the algorithm, implemented in a duplicated code, you are reluctant to find and edit it in each occurrence of this code.

If you have tens or hundreds VuGen tests, containing diplicated code, the support can become a nightmare. Rework will cost too much!!! You must avoid it from the beginning.

Well, what to do? You have create a user-defined library. It needs place your code into separate h-file. Let's assume, we will use the following function: sum(1, 4, -3) which calculates a sum of arguments. It's easy :) In LoadRunner VuGen, create new file and write this code: 1. 2. 3. 4. 5. 6. 7. 8. 9. #ifndef _AR_H_ #define _AR_H_ int sum(int nArg1, int nArg2, int nArg3) { return nArg1 + nArg2 + nArg3; } #endif // _AR_H_

Save it as "ar.h" into "Mercury\LoadRunner\include" folder. Actually, the name of file can be any. Out library will perform arithmetic operations (sum :) ), that's why I named the file as "ar.h". Your library file is created! To include it, use #include preprocessing directive in Action: #include "ar.h" Hint: If your file is not located in a default include folder ("Mercury\LoadRunner\include"), you have to specify full path, for example: #include "c:\\work\\projects\\include\\ar.h" Hint: Since you deal with C language, you have to use double backslash inside of path. That's all! Run your Action and enjoy:

As you see, our function Sum works correctly.

Now, I will answer ans show the following question: How to process global variables in LoadRunner VuGen script? Modify our "ar.h" from "Mercury\LoadRunner\include" folder: 1. #ifndef _AR_H_ 2. #define _AR_H_ 3. 4. int nGlobalValue = 0; 5. 6. int sum(int nArg1, int nArg2, int nArg3) 7. { 8. return nArg1 + nArg2 + nArg3; 9. } 10. 11. #endif // _AR_H_

Then, modify Action like this:

And add a second action ("Action2") to our script:

Note, that we added #include too. Hint: By the way, the script will work correctly even if you didn't add #include for second action. Nevertheless, I recommend to add this preprocessing. Execute our updated VuGen script, containing two actions:

As you can see, variable, located in h-file, is shared between actions. It saves his values between actions. In other words, variable, located in h-file, is a global variable. It can be used for LoadRunner actions

communicating. I hope, library of user-defined functions will simplify your scripts and decrease efforts for their maintenance. As a result, it will save your time and increase your performance :) ************************************************************************************* *************************************************************************************

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.

1. 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_"; lr_save_string(sIn, "InputParam"); web_convert_param("InputParam", "SourceEncoding=PLAIN", "TargetEncoding=URL", LAST); lr_output_message("%s", lr_eval_string("{InputParam}"));

Press F5 to execute the code, and.... H'm.... The input string "t es%d$ + eprst_" was converted to "t+es%25d%24+%2B+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:

2. EncodePlainToURL function. The logic is simple enough: o If the current character is a digits or an alphabetic letter, then pass it "as is" o 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') * * Author: Dmitry Motevich

* * 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". Yeah! :) All special characters (including spaces) were converted to hex-code, i.e. to URL-format!

************************************************************************************* ************************************************************************************* *

LoadRunner unique file names:


How to get unique file name in LoadRunner?
In my previous LoadRunner VIDEO post, I shown and explained how to record PDF file saving in LoadRunner. The provided code works correctly in LoadRunner VuGen or in LoadRunner Controller for one concurrent user. Here it is: //Truncate to zero length or create file for writing. fp = fopen("c:\\temp\\_file.pdf","wb"); //Process the data ... //Write the data to an output file. fwrite(bufData, nSize, 1, fp); If you try to run this code with several concurrent users, all of them will try to create the same file and write to it. As a result, this will led to the error. How to resolve this problem? Each user has to get unique file name and create a file with the unique name .

Now, I'm going to show - how to generate unique file name in LoadRunner. The first step is to get a timestamp as a part of file name. The timestamp consists of:

date (month, day, year) time (hours, minutes, seconds, milliseconds)

You can use the following simple code to get a full timestamp without milliseconds: lr_save_datetime("%m%d%Y_%H%M%S", DATE_NOW, "prmTimeStamp"); lr_output_message(lr_eval_string("Timestamp is: {prmTimeStamp}")); The result of this code is:

(click the image to enlarge it) Do you know what does the string "%m%d%Y_%H%M%S" mean? These are Datetime Format Codes. Please, refer to a table for their descriptions: # 1 %m 2 %d 3 %Y 4 %H 5 %M 6 %S Datetime Format Code month number (01-12) day of month (01-31) year, including century (for example, 1988) hour (00-23) minute (00-59) seconds (00-59) Description

The above code is not ideal. If you application (and LoadRunner script) saves more than one file per second, then file names will be dublicated.

That's why I can propose the code, which get a timestamp with milliseconds: ent timestamp: typedef long time_t; struct _timeb { time_t time; unsigned short millitm; short timezone; short dstflag; }; struct tm { int tm_sec; // seconds after the minute - [0,59] int tm_min; // minutes after the hour - [0,59] int tm_hour; // hours since midnight - [0,23] int tm_mday; // day of the month - [1,31] int tm_mon; // months since January - [0,11] int tm_year; // years since 1900 int tm_wday; // days since Sunday - [0,6] int tm_yday; // days since January 1 - [0,365] int tm_isdst; // daylight savings time flag #ifdef LINUX int tm_gmtoff; const char * tm_zone; #endif }; struct _timeb tb; struct tm * now; char szFileName[256]; _tzset(); // Sets variables used by localtime _ftime(&tb); // Gets the current timestamp // Convert to time structure now = (struct tm *)localtime(&tb.time); sprintf(szFileName, "%02d%02d%04d_%02d%02d%02d_%03u", now->tm_mon + 1, now->tm_mday, now->tm_year + 1900,

now->tm_hour, now->tm_min, now->tm_sec, tb.millitm); lr_output_message("Timestamp is: %s", szFileName); Let's execute this code and check the result:

(click the image to enlarge it) That's much better :) We can generate timestamp with millisecond. Unfortunately, the chances that concurrent users will generate the same file name at the same time still exist. To resolve this issue, we can get unique ids per virtual users. This can be done with lr_whoami LoadRunner function: int vuserid, scid; char *groupid; lr_whoami(&vuserid, &groupid, &scid); lr_message("Group: %s, vuser id: %d, scenario id %d", groupid, vuserid, scid); lr_whoami LoadRunner function returns information about the Vuser executing the script.

When you run this code in LoadRunner Vuser Generator, the result will be:

When you run this code in LoadRunner Controller, the result will be:

So, the last step is to combine timestamp with a vuser execution's ids and use them as a file name. This will guarantee, that each LoadRunner virtual user will have unique file name.

So, the final code is: typedef long time_t; struct _timeb { time_t time; unsigned short millitm; short timezone; short dstflag; }; struct tm { int tm_sec; // seconds after the minute - [0,59] int tm_min; // minutes after the hour - [0,59] int tm_hour; // hours since midnight - [0,23] int tm_mday; // day of the month - [1,31] int tm_mon; // months since January - [0,11] int tm_year; // years since 1900 int tm_wday; // days since Sunday - [0,6] int tm_yday; // days since January 1 - [0,365] int tm_isdst; // daylight savings time flag #ifdef LINUX int tm_gmtoff; const char * tm_zone; #endif }; struct _timeb tb; struct tm * now; char szFileName[256]; int vuserid, scid; char *vusergroup; _tzset(); // Sets variables used by localtime _ftime(&tb); // Gets the current timestamp // Convert to time structure now = (struct tm *)localtime(&tb.time); sprintf(szFileName, "%02d%02d%04d_%02d%02d%02d_%03u_%d_%d_%s",

now->tm_mon + 1, now->tm_mday, now->tm_year + 1900, now->tm_hour, now->tm_min, now->tm_sec, tb.millitm, vuserid, scid, vusergroup); lr_output_message("File name is: %s", szFileName); Note: You can download this source code from here. The result of above code is:

Using the generated name (05312008_221557_116_7_0_group1) you can save the file into "05312008_221557_116_7_0_group1.pdf" or "05312008_221557_116_7_0_group1.txt" depending on the file type.

************************************************************************************* ************************************************************************************* *

Generating unique file name using LoadRunner parameter


I'm sure that my readers are the best readers in the world! They are careful and they are great professionals. I constantly learn from them. Indeed! I want to thank Tim for his comment on getting unique file name in LoadRunner. Sure, there is very simple way to generate unique file name in LoadRunner using LoadRunner parameters. How to do that? We have to create three different types LoadRunner parameters:

Vuser ID Iteration Number Date/Time

1. Open "Parameter List" dialog from "Vuser/Parameter list...":

2. Add new LoadRunner parameter of "Vuser ID" type:

3. Then add second LoadRunner parameter of "Iteration" type:

4. And add third LoadRunner parameter of "Date/Time" type:

(Click the image to enlarge it)

Please, note that I added new Date/Time format - "%Y%m%d_%H%M%S.000". I described the meanings of %Y, %m, etc in this post. Point and three zeros (".000") means using of milliseconds. So, this Date/Time format produce a string like: 20080616_231514.953 5. That's all. Now I add the following line of code: lr_output_message(lr_eval_string("{VuserID}_{Iteration}_{DateTime}"));

The result of this line is:

As you can see, we generated unique string for the current LoadRunner virtual user. You can use it to save the current file, for example as "1_1_20080616_232322.940.pdf".

Summary: Using of LoadRunner parameters is easy enough. LoadRunner provides several useful types of parameters. We used three of them:

Vuser ID Iteration Number Date/Time

Also, this way doesn't require strong programming knowledge. The code is small and compact (in our script it consists of 1 line). Simple and useful! ************************************************************************************* *************************************************************************************

LoadRunner unique file name with web_save_timestamp_param function


Earlier, I shown two ways how to create unique file names in LoadRunner:

How to get unique file name in LoadRunner Generating unique file name using LoadRunner parameter

Today I'm going to show the simplest way. And I would like to thank Charlie for his comment. He suggested to use web_save_timestamp_param function. web_save_timestamp_param function saves the current timestamp to LoadRunner parameter. Timestamp is the number of milliseconds since midnight January 1st, 1970 (also known as Unix Epoch). This is how web_save_timestamp_param works: web_save_timestamp_param("TimeStamp", LAST); lr_output_message("Timestamp: %s", lr_eval_string("{TimeStamp}")); And the result is:

As I explained in this loadRunner tutorial about unique file names in LoadRunner, we have to get unique ids per virtual users with lr_whoami LoadRunner function. So, the final LoadRunner script is: char szFileName[256]; int vuserid, scid; char *groupid; lr_whoami(&vuserid, &groupid, &scid);web_save_timestamp_param("TimeStamp", LAST);

sprintf(szFileName, "%s_%d_%d_%s", lr_eval_string("{TimeStamp}"), vuserid, scid, groupid); lr_output_message("File name: %s", szFileName); And its result is from LoadRunner Controller:

So, you can add a required file extension (txt, pdf, etc) and get a unique file name. It will work for any number of concurrent virtual users in LoadRunner Controller.

Summary:

Shown the way how to generate unique file names in LoadRunner It uses web_save_timestamp_param function

************************************************************************************* *************************************************************************************

Bugs in LoadRunner:
Watch for my logic please:

Each program contains errors (bugs). LoadRunner is a program too. So, LoadRunner should contain bugs. Even more! LoadRunner contains bugs.

Here is a list of bugs in LoadRunner 9.10 that I noticed. And I would like to ask you to share your experience on LoadRunner too. I'm sure that you faced with such errors.

Why do I do that?

I know, that some members of LoadRunner development team read this blog. So dear readers, you can report errors and make your favourite (I hope :)) LoadRunner better. Also, there is another reason to post LoadRunner bugs. Since you are QA specialists, I think it will be interesting to find bugs in a released commercial product (= LoadRunner). Prove, that you are qualified bugs hunters! :) Description

1 1. LoadRunner Controller 2. Open Design tab 3. Double-click any action from 'Global Schedule' section -> 'Edit Action' dlg is opened. OK 4. Click 'Help' -> Nothing happens

2 1. Open LR Controller Help pdf-file, page 77. 2. It contains the phrase: The following additional right-click options are available: Reset IDs. Resets the IDs of the Vusers in the group. Actually, menu item is named as "Renumber":

3 1. Open LoadRunner VuGen 2. Record and replay script 3. Open 'Tree view' 4. Select 'Response' item from tree view -> 'Create Parameter' menu item is disabled, see:

5. Select 'Body' item from tree view -> 'Create Parameter' menu item is enabled, see:

I think, 'Create Parameter' menu item should be enabled in both cases. 4 1. Open LoadRunner VuGen 2. Create new LR parameter 3. Open 'Parameter Simulation' dlg -> There are not 'short keys' (underlined letters for quick access with keyboard):

5 Could you send yours, dear reader?..

I think, the list of LoadRunner bugs is an interesting challenge for LoadRunner users community. And definitely - it will be usefull for all of us.

That's why I ask you to send your notes about LoadRunner bugs. Send detailed info and do not forget to mention your name. The world should know best testers :)