Friday, November 12, 2010

My load test 7

How have you validated that IP spoofing is not working? Have you tried to get the Vuser IP and place it in an output message? For example:

char *vuser_ip;

vuser_ip = lr_get_vuser_ip();

if (vuser_ip)
lr_vuser_status_message("The Vuser is spoofed with the following ip : %s",vuser_ip);
else
lr_vuser_status_message("The Vuser is not spoofed");
LR - Extracting a dynamic value hidden in javascript code
What function is used to extract a dynamic value hidden in javascript code? The value is required in order to complete a web_submit_data POST method parameter set.

Here's the script:


"5091228" is the dynamic value to extract.

Sorry about the javascript no-wrap posting. Reformatted for readability:



Hidden or not, you use web_reg_save_param to grab this dynamic value. If it is returned, it can be correlated.
I'm trying to do just this, but it is not working. I have a web_custom_request("shared.js") statement embedded in a web_concurrent
/* -------------------------------------------------------------------------------
Script Title :
Script Description : This script is to read database

Recorder Version : 1435
------------------------------------------------------------------------------- */

vuser_init()
{
/* declare variables*/
int rowincrement=1,TextCheck, i, j;
unsigned long RowCount = 0;
unsigned long * const count = &RowCount;
char *ItemName[500];
char *LastStatus[500];
/*conenction*/
lrd_init(&InitInfo, DBTypeVersion);
lrd_open_context(&Ctx2, LRD_DBTYPE_ODBC, 0, 0, 0);
lrd_db_option(Ctx2, OT_ODBC_OV_ODBC3, 0, 0);
lrd_alloc_connection(&Con2, LRD_DBTYPE_ODBC, Ctx2, 0 /*Unused*/, 0);
lrd_db_option(Con2, OT_ODBC_LOGIN_TIMEOUT, (void FAR const *)15, 0);
lrd_db_option(Con2, OT_ODBC_SS_QUOTED_IDENT, "OFF", 0);
lrd_db_option(Con2, OT_ODBC_SQL_PACKET_SIZE, (void FAR const *)4096, 0);
lrd_db_option(Con2, OT_ODBC_SS_ANSI_NPW, "ON", 0);
lrd_open_connection(&Con2, LRD_DBTYPE_ODBC, "", lr_decrypt("4a27962de"), "", lr_decrypt("4a27962d886b524e097fae09a52c595022c32775d6ac53b7"
"26f4c6e52c4aa7fe8f4655f2adb12dbd4fc99008542f41c6eda3e1f7ffdd"
"4ca7fd2ae0febd73f541202ac2db1e21eb822ec0b977225a45f3e20ad309"
"fb1996549d468df8a0d89bf8f6efdd5a21e8dcc3304c7b"), Ctx2, 1, 0);
/*cursor*/
lrd_open_cursor(&Csr2, Con2, 0);
lrd_db_option(Csr2, OT_ODBC_CURSOR_CLOSE, 0, 0);
lrd_db_option(Csr2, OT_ODBC_CURSOR_UNBOUNDCOLS, 0, 0);
lrd_stmt(Csr2, "set showplan_all off\r\n", -1, 1 /*Direct exec*/, 0 /*None*/, 0);
lrd_cancel(0, Csr2, 0 /*Unused*/, 0);
lrd_stmt(Csr2, "use [CAR]", -1, 1 /*Direct exec*/, 0 /*None*/, 0);
lrd_result_set(Csr2, 0, 0, 0);
lrd_db_option(Csr2, OT_ODBC_CURSOR_CLOSE, 0, 0);
lrd_db_option(Csr2, OT_ODBC_CURSOR_UNBOUNDCOLS, 0, 0);
lr_think_time(10);
lrd_cancel(0, Csr2, 0 /*Unused*/, 0);
/*sql query*/
lrd_stmt(Csr2, "select * from item_status\r\n", -1, 1 /*Direct exec*/, 0 /*None*/, 0);
lrd_bind_cols(Csr2, BCInfo_D21, 0);

/* get the row count*/
lrd_fetch(Csr2, -8, 1, count, PrintRow2, 0);
lr_output_message("count= %d",*count);

/*lr fetch to verify if the data is coming in the frid*/
for(i=1; i<=("count= %d",*count); i++) { lr_output_message("%d",i); lrd_save_col(Csr2, 2, i, 0, ItemName[i]); lrd_save_col(Csr2, 3, i, 0, LastStatus[i]); lrd_fetch(Csr2, 1,1 , 0, PrintRow20, 0); lr_start_transaction(ItemName[i]); /*condition to check the status*/ if (LastStatus[i]=="Up") { lr_end_transaction(ItemName[i], LR_PASS); } else { lr_end_transaction(ItemName[i], LR_FAIL); } } lr_start_transaction("ItemFetch"); lrd_fetch(Csr2, -14, 1, 0, PrintRow20, 0); GRID(20); lr_end_transaction("ItemFetch", LR_AUTO); /* close all the connections and cursors*/ lrd_db_option(Csr2, OT_ODBC_CURSOR_UNBOUNDCOLS, 0, 0); lrd_result_set(Csr2, 0, 0, 0); lrd_cancel(0, Csr2, 0 /*Unused*/, 0); lrd_cancel(0, Csr2, 0 /*Unused*/, 0); lrd_cancel(0, Csr2, 0 /*Unused*/, 0); return 0; } Merge all CSV or TXT files in a folder in one worksheet Ron de Bruin (last update 5-July-2007) Go back to the Excel tips page 1: Non VBA example from Dave Peterson 2: VBA example I made based on Dave's example Example 1 Merge all data from the csv files into a text file Note: with a few small changes you can also use this for txt files. Replace *.csv for *.txt 1) Windows Start Button | Run 2) Type cmd and hit enter ("command" in Win 98) 3) Go to the folder with the CSV files (for help enter "help cd") 4) Type copy *.csv all.txt and hit enter to copy all data in the files into all.txt. 5) Type exit and hit enter to close the DOS window Now we must import the text file all.txt into Excel. 1) Open Excel 2) When you use File Open to open all.txt the Text Import Wizard will help you import the file 3) Choose Delimited 4) Next 5) Check Comma 6) Finish Example 2 This code will ask you to browse to the folder with the csv files and after you click OK in this dialog it merge all data into a txt file and then import and save it into a Excel file for you. Copy the code below into a normal module of a workbook : Alt-F11 Insert>Module
Paste the macro
Alt q to go back to Excel
Alt F8 to open your macro list
Select Merge_CSV_Files and press Run

There is no need to change anything in the code example for csv files to test it.
But read the Tips below the macro if you not get the result you want.
' Start Code

Declare Function OpenProcess Lib "kernel32" _
(ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long

Declare Function GetExitCodeProcess Lib "kernel32" _
(ByVal hProcess As Long, _
lpExitCode As Long) As Long

Public Const PROCESS_QUERY_INFORMATION = &H400
Public Const STILL_ACTIVE = &H103


Public Sub ShellAndWait(ByVal PathName As String, Optional WindowState)
Dim hProg As Long
Dim hProcess As Long, ExitCode As Long
'fill in the missing parameter and execute the program
If IsMissing(WindowState) Then WindowState = 1
hProg = Shell(PathName, WindowState)
'hProg is a "process ID under Win32. To get the process handle:
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, hProg)
Do
'populate Exitcode variable
GetExitCodeProcess hProcess, ExitCode
DoEvents
Loop While ExitCode = STILL_ACTIVE
End Sub


Sub Merge_CSV_Files()
Dim BatFileName As String
Dim TXTFileName As String
Dim XLSFileName As String
Dim FileExtStr As String
Dim FileFormatNum As Long
Dim DefPath As String
Dim Wb As Workbook
Dim oApp As Object
Dim oFolder
Dim foldername

'Create two temporary file names
BatFileName = Environ("Temp") & _
"\CollectCSVData" & Format(Now, "dd-mm-yy-h-mm-ss") & ".bat"
TXTFileName = Environ("Temp") & _
"\AllCSV" & Format(Now, "dd-mm-yy-h-mm-ss") & ".txt"

'Folder where you want to save the Excel file
DefPath = Application.DefaultFilePath
If Right(DefPath, 1) <> "\" Then
DefPath = DefPath & "\"
End If

'Set the extension and file format
If Val(Application.Version) < 12 Then 'You use Excel 97-2003 FileExtStr = ".xls": FileFormatNum = -4143 Else 'You use Excel 2007 FileExtStr = ".xlsx": FileFormatNum = 51 'If you want to save as xls(97-2003 format) in 2007 use 'FileExtStr = ".xls": FileFormatNum = 56 End If 'Name of the Excel file with a date/time stamp XLSFileName = DefPath & "MasterCSV " & _ Format(Now, "dd-mmm-yyyy h-mm-ss") & FileExtStr 'Browse to the folder with CSV files Set oApp = CreateObject("Shell.Application") Set oFolder = oApp.BrowseForFolder(0, "Select folder with CSV files", 512) If Not oFolder Is Nothing Then foldername = oFolder.Self.Path If Right(foldername, 1) <> "\" Then
foldername = foldername & "\"
End If

'Create the bat file
Open BatFileName For Output As #1
Print #1, "Copy " & Chr(34) & foldername & "*.csv" _
& Chr(34) & " " & TXTFileName
Close #1

'Run the Bat file to collect all data from the CSV files into a TXT file
ShellAndWait BatFileName, 0
If Dir(TXTFileName) = "" Then
MsgBox "There are no csv files in this folder"
Kill BatFileName
Exit Sub
End If

'Open the TXT file in Excel
Application.ScreenUpdating = False
Workbooks.OpenText Filename:=TXTFileName, Origin:=xlWindows, StartRow _
:=1, DataType:=xlDelimited, TextQualifier:=xlDoubleQuote, _
ConsecutiveDelimiter:=False, Tab:=False, Semicolon:=False, Comma:=True, _
Space:=False, Other:=False

'Save text file as a Excel file
Set Wb = ActiveWorkbook
Application.DisplayAlerts = False
Wb.SaveAs Filename:=XLSFileName, FileFormat:=FileFormatNum
Application.DisplayAlerts = True

Wb.Close savechanges:=False
MsgBox "You find the Excel file here: " & vbNewLine & XLSFileName

'Delete the bat and text file you temporary used
Kill BatFileName
Kill TXTFileName

Application.ScreenUpdating = True
End If
End Sub

' End code

My load test 6

Prashanth in my case.
-Controller installed in one machine (server)
-Vugen installed in 4different machines

this means we dnt have LGenerators right??

so can i install LGenerators in those 4machines where Vugen is installed already???

if so how can i install, where can i get Lgenerator install component.
Your friend is wrong and has given you misleading information. The LG agent will be installed if you select the full installation of LoadRunner on the machine which would install Vugen,Controller and Analysis along with the LG agent.

In your case, you have only Vugen installed so it doesn't mean that the machine can be used as LG. You need to do the installation of the LG agent. you'll find this option in the loadrunner setup.Start the LR setup file and click on the Loadgenerator option.This will install the LG agent in addition to your existing Vugen. Do this on all the machines which you intend to use as LG
thanks again for taking time to reply me.
your words are crystal clear.

currently we are using LR 8.1
i will speak to my Lead to upgrade it to LR 9.5

___________
one more query:
i got confuse between Performance center & LR

LR & Performance center are different tools.

-Perf center dosen't have Vugen, am i correct?

so intially we have to create a script in LR Vugen then login to Perf center under Project--Vuser scripts we have to browse the script then by selecting the host machine the script will run in the selected host machine.
You dont require 9.5 for installing your LG. If you have the setup file of your current version of LR then you can do it on your current version itself without necessity of upgrading.

Performance center is a centralized testing infrastructure which can be used for load testing across locations. It is web-based and hence can be accessed by different users from different locations to run load tests.

How to parse parameter values to the Transaction Names - This thread has been closed
I want to parse the parameter values to the transaction names, so for every iteration I can obtain the transaction time for specific paramater supplied?

I am trying this code, but no success:

char *qrytime, *newqry;

qrytime = lr_eval_string "{Run_Query}" );

lr_save_string(qrytime,"newqry");


lr_start_transaction("{newqry}");

////////////////////////

lr_end_transaction("{newqry}",LR_AUTO);

Text from doc:

Dynamic transactions names are names that change
depending upon a parameterized value. If there are
several reports that are run randomly, the team can
define the transaction name dynamically using the
code below:

Char rname[10];

sprintf( rname, �Run_Report_%s�, lr_eval_string(�{report}�) );

lr_output_message(rname);

lr_start_transaction(rname);

lr_end_transaction(rname,2);

Dynamic Transaction Names in LoadRunner
Say you want the transactions to be named dynamically depending on a parameter being used. You can use sprintf and lr_eval_string functions to dynamically create the trn names. Here is the sample code


Action()
{

char trnname[10];

sprintf (trnname, "Item_%s", lr_eval_string("{TrnType}"));
lr_start_transaction(trnname);
web_submit_data("Payment Done",...... ) ;
lr_end_transaction(trnname, LR_AUTO);

return 0;
}
Port Numbers using by LR9.0
Hi,
We are using LR9.0 on a secured clinet network. In order to enable LR to access client specific applications, we need to ask Network guys to enable Firewall ports for LR. Hence; I require the port (TCP/UDP) number that are using by LR. Please provide the details ASAP.
Hello Krupa,

Can you please elaborate the problem?

Do you want to know the Controller - Load Generator communication ports?

Thanks!

Krupasindhu
Jun 23, 2009 14:34:33 GMT N/A: Question Author
________________________________________
Yes, I am looking for the port numbers using by both LR Controller & Load Generator. Thanks for narrow down the issue.

Shivaram Patil
Jun 23, 2009 14:35:55 GMT 9 pts
________________________________________
Hi,
you can refer this in Recording Options...

Tools->Recording Options->Port Mapping

You can make a new entry and speficy the connect type....Add the client certificate to the list.
Ex: IP, Port number, password etc...

Controller and load generator: 443

I suggest you to refer help file....

if you are using a secured client network.....you need to consider firewall, MI listner and SSL settings as well.

If you have installed the tool, documentation can be accessed..LoadRunner->Documentation.

HP Loadrunner and Java Application hosted on a Linux Server - This thread has been closed
We have got a new client request for doing performance testing for a Web Based Java Application(using JBoss) hosted in a Linux Server,however, the Load Testing would be done from a Windows platform as always.
Will LR support this? Are there any differences in the way LR would recognise the controls or the the way it behaves?
It should not matter what platform hosts a Web based application. If it runs in a browser and you can record with the Web protocol it should work fine. I have tested Java apps hosted on Windows servers, AIX servers, Solaris servers, and Linux servers and all of them were pretty much the same.
Hi Alan,

Are there any latency factors involved when we do the performance testing from a windows platform with the application being in a Linux Server?
Why would there be any latency problems. Are your load generators on the same subnet as the applications servers? If so, it should be the same regardless of what platform is used to host an application.
Just think of the load generators executing your web transactions just like they are executed when you use the browser manually. The only difference is that the scripts communicate with the application under test at layer 4 of the OSI model. This eliminates the browser.
Hats off to u Alan.
Just think of the load generators executing your web transactions just like they are executed when you use the browser manually. The only difference is that the scripts communicate with the application under test at layer 4 of the OSI model. This eliminates the browser.

Action()
{
int myrandomnumber;
myrandomnumber = rand()%100;
[PUT ACTION CODE HERE]

if(myrandomnumber<20){ [PUT SAVE CODE HERE] } return 0; }You could use blocks like pra said but then you'd not only have duplicate code, which means more work for you, but you'd also have the fun of having to reset all your run time settings in the scenario when you make any changes. May 13, 2009 16:27:43 GMT Unassigned ________________________________________ Trust me. It works. Open a new script and run the code below. It'll show you how often the execution path goes into the 'save' section and how often not. Expect about an 80 / 20 split. Action() { int myrandomnumber; int i,save, dontsave; for(i=0;i<100;i++){ myrandomnumber = rand()%100; if(myrandomnumber<20){ save++; } else{ dontsave++; } } lr_output_message("save: %d", save); lr_output_message("dont_save: %d", dontsave); return 0; } Getting the current state of a checkbox in LoadRunner VUGen for Oracle NCA Protocol am having a hard time writing a conditional statement for checkbox using Oracle NCA protocol. I want to first to check whether a checkbox is checked or unchecked. If it is checked, then I will uncheck it. Otherwise, I will check it. Can anyone help me with this? Here's the code that I wrote. I observed that if the checkbox if checked, it unchecks it. But if the checkbox is uncheck, it doesn't check it. if ("B_OP_SEQS_COUNT_POINT_FLAG_0" != "0") { nca_button_set("B_OP_SEQS_COUNT_POINT_FLAG_0", 0); } else { nca_button_set("B_OP_SEQS_COUNT_POINT_FLAG_0", 1); } Use the function nca_obj_get_info in the if loop for creating the condition, like if(nca_obj_get_info("B_OP_SEQS_COUNT_POINT_FLAG_0",object_checked) == E_OK) Thanks for your help. It gave me an idea how to solve the problem. I put the following coeds in my script: nca_obj_get_info("B_OP_SEQS_COUNT_POINT_FLAG_0", "object_checked", Countpoint); if (strcmp(Countpoint, "0") != 0) { nca_button_set("B_OP_SEQS_COUNT_POINT_FLAG_0", 0); } else { nca_button_set("B_OP_SEQS_COUNT_POINT_FLAG_0", 1); } Thanks for introducing me to ncs_obj_get_info function. Thanks a lot!!! Recording button that appears on selecting a choice in the dropwdown - This thread has been closed In my application there is dropdown with 3 choices. If i choose option 1 a button appears (Specify Product button). if i choose option 2 a dropdown list appears,..likewise for option 3. LR records the Specify Product button click with respect to the co ordinates. But while replaying the script the button doesnot appear and LR throws error. Its a .Net Application HP guys sent a patch like for handlnig this issue in LR 9.10 and this issue got solved in LR version 9.5 by default. int comp; char *city1, *city2; city1 = lr_eval_string( "{Departure}" ); city2 = lr_eval_string( "{Arrival}" ); comp = strcmp( city1,city2 ); if (comp == 0) { lr_next_row( "Arrival.dat" ); city2 = lr_eval_string( "{Arrival}" ); lr_save_string(city2,"NewArrival"); } web_submit_form("reservations.pl", "Snapshot=t4.inf", ITEMDATA, "Name=depart", "Value={Departure}", ENDITEM, "Name=departDate", "Value=03/31/2009", ENDITEM, "Name=arrive", "Value={NewArrival}", ENDITEM, "Name=returnDate", "Value=04/01/2009", ENDITEM, : : : LAST); Load balancing issue Yes. They were involved. They said that it's a script issue. Another person from my team took over the load testing. He commented out web_add_cookie() from the script. I would like to find out what exactly happens if these functions are commented out from the script and how this will affect the load balancing. The reaason why I am asking this question is because the network person is saying that the load is getting balanced in the servers after the script change was made( meaning the load test was done woth the script with the web_add_cookie() function commented. Below is the change they made. { web_cache_cleanup(); web_cleanup_cookies(); /*web_add_cookie("INF_COOKIE=R2923242701; DOMAIN=���.."); web_add_cookie("WT_FPC=id=198.45.19.20-1166439968.29964767:lv=1225858501053:ss=1225856505274; DOMAIN=�.."); web_add_cookie("__utma=48171320.1212086417.1225828676.1225856562.1225857569.8; DOMAIN=����.."); web_add_cookie("__utmb=48171320; DOMAIN=���.."); */ How will this affect the load balancing? Also, I do not have the details as to what load balancing they are using. Some comments from Bill: 1. VUGEN will sometimes create web_add_cookie() functions that are not needed. I routinely comment out these functions throughout the script because LR script playback will dynamically create and update cookie correctly for each vuser. 2. It is typical for load balancing technology to create a cookies to cause future requests from this user to be steered to a particular web/app/db set of servers. Your recording appears to have cookies for this purpose and thus every vuser was steered down the same path. By removing the web_add_cookie from your script will allow for each iteration to playback in a natural or life-like manner. IP Spoofing PC9.1 Win 2003 server Hi Gurus, IP Spoofing does not seem to be working for us in Performance Center. It seems our data center does NOT use DHCP to obtain IP addresses. An example of one of our servers IP configs is as follows: IP Address. . . . . . . . . . . . : 172.18.96.114 Subnet Mask . . . . . . . . . . . : 255.255.255.0 IP Address. . . . . . . . . . . . : 172.18.96.113 Subnet Mask . . . . . . . . . . . : 255.255.255.0 IP Address. . . . . . . . . . . . : 172.18.96.112 Subnet Mask . . . . . . . . . . . : 255.255.255.0 IP Address. . . . . . . . . . . . : 172.18.96.111 Subnet Mask . . . . . . . . . . . : 255.255.255.0 IP Address. . . . . . . . . . . . : 172.18.96.110 Subnet Mask . . . . . . . . . . . : 255.255.255.0 IP Address. . . . . . . . . . . . : 172.18.96.43 Subnet Mask . . . . . . . . . . . : 255.255.255.0 All the documentation I read says that I have to enable IP Spoofer in the controller. This is the same as making the selection in in the test (General Tab --> Advanced Frame --> Enable IP Spoofer)?

my load test 5

It includes first buffer time. You are referring to Average response time, and the metrics in page breakdown graph is not an average. Also check the granularity you have mentioned
Thank you for the reply. I have seen the max response time for that transaction,it is not more than 3.87 secs for 74 users (i.e. 74 passed transactions). Now if the response time include first buffer , the times are not matching because First buffer is 13 secs and the maximun response time during the transaction is 3.87 secs. Could you please clarify. Thanks
Performance center installation

I have reached the Server Configuration page on my admin server and I need to set up the hosts,
I have populated my Database and File server (I used the admin server as my file server)
But I am not sure what do I need to enter for the:
Utility Server

Or:
SiteScope Configuration
SiteScope Server:
SiteScope Port:
Use HTTPS:
Use Account:
Account:


We have build
2 host machines
2 controllers
1 db server (SQL)
1 admin server

I am not sure where would the Utility Server fit in?

Any help is appreciated.

The sitescope servers are optional and is for monitoring PC. The utility server is mandatory and needs to be installed.

The performance center server installation includes web(user/admin), file and utility server installations. You can customize the installation and select the utility server option to install and add it to your PC installation
Thanks,
I know see the utility server in the install patch and had it all along on my admin server I just didn't know what server to put under the utility server during server config section :-)

LoadRunner 9.51 – unable to record with Citrix object detail recording - This thread has been closed
Hi,

I am new to recording Citrix protocol.

I am trying to use object detail recording in VUGen, i.e LR can see some of the objects of the application and can perform actions such as syncing on text.

So far I have setup the following
Installed Citrix agent for presentation server as per LR setup instructions.
Installed Citrix client v11 and installed the patch for client v10+ on LoadRunner server
Citrix input for code generation option is turned on

Details of my environment -
Citrix agent for PS version: 9.50
Citrix Presentation Server version: 4.5
Citrix client version: 11
LoadRunner version: 9.51, installed on Win2003 server SP2, DEP disabled.

When I record, I remote desktop (RDP) to the LoadRunner server , run VuGen and then record using Citrix_ICA protocol. I can record and replay no problem. I just can’t get LR to recognise any context sensitive stuff e.g obj_mouse_click.

Where have I gone wrong?

Did you select "use citrix agent input for code generation" option in recording options?
"use Citrix input for code generation" option is turned on

Citrix administrator did a silent install of the agent and did not install properly.

He reinstalled the agent manually; issue resolved.

Citrix agent was not installed properly using silent install. Server administrator re-run the installation and worked OK.

how to add "Insert text check" btn to floating toolbar?
I've seen Vugen 9.x demo where the user had a "insert text check" icon on the floating toolbar that appeared when he was recording a session.

Clicking that btn caused a "web_reg_find()" function to be inserted.

Unfortunately my Vugen client does NOT have that btn on the floating toolbar. Any ideas on how I can add it?

Thanks in advance...

What is the protocol you are using? For few protocols you may need to manually add the text check after the recording. Eg. Web (Click&Script)

Yes, I'm using Web(Click&Script). So, what's the trick for adding the "insert text check" btn when using that protocol?

There is no trick here. After recording the script, add the web_reg_find to the script manually. This is the only trick.
Hmm, does web_reg_find() really work with Web (Click & Script) protocol?

Ie, maybe they removed the shortcut from the floating toolbar for a reason...?
It should work. In C&S text automation is manual. Let me know if you face any issue.
Thanks for the quick reply. Actually I am facing a wierd issue. I've got a very simple Web (click & script) script that works until I add in the web_reg_find().

All it does is go to the ITRC and log in. that part works find.

But... when I add web_reg_find(), I then get the red error: "Error -27216: Invalid Argument number" n the replay log. This appears to be my web_reg_find(). I'm uploading a screen snapshot.

Any suggestions?

----
Also, do you happen to know how to make VuGen show line numbers in the "Script" mode editor?
Your syntax is wrong.

Eg. web_reg_find("Text=Welcome", LAST );

Right click in the script > Add Step > web_reg_find. It gives you an editor to generate the function with proper syntax.

I dont think we can display line number. my be an ER
Awesome, that new web_reg_find() syntax works,thanks!!

And that tip about right-clicking in the script body and doing "insert | new step | web_reg_find" to get a function editor is even better!! Now I see how to invert the sense of the test (ie, to have it fail if it finds the string "error").

----
One last question: is there any problem with having more than one web_reg_find() in the same spot? Ie, I want to have 2 web_reg_find() statements before I invoke web_browser() to fetch the page...

#1/2) The first web_reg_find() would do a positive test (look for a certain string), and fail if it's NOT found.

#2/2) The second web_reg_find() would do a negative test (look for an "error" string, and fail if it IS found.

Is that ok? Or should I be implementing that via a different techique?
I was going to say that the "View | Status bar" wasn't working on my VuGen client (version 9.51), but it turns out it actually is working... sort of...

Ie, it adds a tiny status bar at the bottom of the Vugen window, and on the far right it says "Line:28" (or whatever line I've selected in the script editor window).

So, I should've awarded more points - sorry about that...

But actually what I'm really looking for is something equivalent to doing "vi somefile" and then ":set number" (ie, displaying the line number beside EVERY line in the file).

Can Vugen be made to do THAT?
How to measure external System Time? - This thread has been closed
I have the following question to measure external system time? For example
there are two systems A & B, B is where our data gets saved and to get some information from backend to populate the values based selected option we get displayed with read-only values as this values get from b system.

Based on the condition can someone help me how to find the external system time?
You might be able to get some response times using HP Diagnostics, provided you can instrument the external system.
But this tool doesnt give for each senario i guess right, please do give me more details or any help document.
You need to have license for HP Diagnostics server and J2EE/.Net addin in controller.

Note: Depending on the amount of instrumentation this tool adds performance degradation to your system.

This is agent based tool and you can control what, when and how to monitor from diagnostics server.

I would suggest going through manual or somebody from this forum can help if they have any documents on this.

I would suggest get a trail version, instrument it and check if this is what you need.

LR recording of Outlook anyone? - This thread has been closed
Has anyone ever recorded Microsoft Outlook using LoadRunner for stress testing purposes?
What protocol did you use?
Microsoft Outlook can be scripted using the MAPI protocol. You will have to code the entire script manually using the MAPI commands in the function reference for sending mail, receiving mails etc. However, I would also like to tell you that MAPI supports older versions of Outlook and hence I had trouble making my script work with the latest outlook that I had. This is one thing to keep in mind.

I just tried with IMAP protocol just for knowledge but its not capturing anything while recording.

Is that the reason we have to code the entire script?
Yes. you will have to code everything, right from the user id/password to be used to login to the outlook to all the operations you would like to perform.

I've tried the MAPI protocol and it's working. I've managed to successfully send an email to myself using LR :-)

The only problem I now have is how to get rid of the 'a program is trying to access email addresses you have stored in outlook. do you want to allow this.' dialog, as it is introducing a manual step in my script and is affecting the response times that I'm capturing.
Any ideas would be most welcome.

To overcome the current problem, I would suggest you to create a new Outlook account just for your testing and use that for your scripts. The current problem you are encountering is because of using your current outlook that is configured to your mails. Also, do you have outlook open/active when you are replaying the script?

We had to make several customizations in order to successfully test MAPI. Here are a few notes:

Your main test account (authenticated when you start the outlook client) will need rights to access all of the mailboxes that you will be using in order to suppress the "allow" message that you are receiving.

Each generator needs the Outlook client installed

Each generator needs outlook profiles for all test users that will be authenticating through that generator

Each generator needs to be standing by with the agent process, not service, running.

Using Exchange, we were receiving security messages that were interrupting the test run. To suppress these we had to use the Exchange Admin pack to customize the Outlook security settings. This required an update to the Exchange server, as well as registry settings on the generators to direct them to check the settings.
I've tried using two different email accounts - one for sending from and one for sending to, but I still get the "allow" message.
Is this what you meant?

Laura,
Thanks also for the info.
I've asked our IT support about the mailbox access rights. I'll see what they say.

Thanks for your continued help on this.
Managed to find a solution to the security question problem. Used a little application called 'Click Yes'.

Proxy connection timeout value configuration - This thread has been closed
Is it possible to configure the connection timeout value for proxy connections in LoadRunner?

Apparently the default proxy connection timeout is about approximately 20sec. Below is a sample output from proxy connection timeout:

Time in seconds since 1/1/70: 1249388993
Action.c(17): web_set_secure_proxy was successful [MsgId: MMSG-26392]
Action.c(18): Continuing after Error -27796: Failed to connect to server "xx.xx.xx.xx:8080": [10060] Connection timed out [MsgId: MERR-27796]
Time in seconds since 1/1/70: 1249389014
think this is more to do with your request/response timeout than your proxy. Set the timeout value in your script > Run-time Settings > Preferences > Advanced > Http Request timeout/Http response timeout.
Thanks, but actually if the problem would be in the HTTP request connect timeout, the error code would be -27783. And in any case my HTTP request timeouts have been set to 120sec.

It seems that there is a separate value for proxy connection timeouts, but is it configurable? Or is there some other timeout value on the vugen TCP/IP-stack equivalent? Or does this timeout come somewhere from OS TCP/IP-stack?
You will have to configure this timeout at the proxy server as you cannot configure this at your Vugen. The obj.conf file might need to be modified at your proxy server end. Refer below link for the values to be changed: http://docs.sun.com/app/docs/doc/819-3708/6n5tldh57?a=view

Timeout Values
Timeouts have a significant impact on server performance. Setting the optimal timeout for the Proxy Server helps to conserve network resources.
There are two instance-specific SAFs (server application functions) and one global parameter that can be used to configure timeout values within the Proxy Server.
This section contains the following topics:
init-proxy SAF (obj.conf)
http-client-config SAF (obj.conf)
KeepAliveTimeout (magnus.conf)
init-proxy SAF (obj.conf)
The init-proxy function initializes the Proxy Server’s internal settings. This function is called during the initialization of the Proxy Server, but should also be specified in the obj.conf file to ensure that the values are initialized properly.
The syntax of this function is as follows:
Init fn=init-proxy timeout=seconds timeout-2=seconds
In the previous example, the following parameters have direct applicability to Proxy Server timeout settings for the init-proxy SAF:
timeout (proxy timeout)– The proxy timeout parameter tells the server how long to wait before aborting an idle connection. A high proxy timeout value commits a valuable proxy thread to a potentially down client for a long time. A low timeout value aborts CGI scripts that take a long time to produce results, such as a database query gateway.
To determine the best proxy timeout for the server, consider these issues:
Will the Proxy Server be handling many database queries or CGI scripts?
Will the Proxy Server be handling a small enough number of requests that a process can be spared at any given time?
If you answered yes to either of these questions, you may decide to set a high proxy timeout value. The highest proxy timeout value recommended is 1 hour. The default value is 300 seconds (5 minutes).
You can view or modify the proxy timeout value by accessing the Configure System Preferences page under the Preferences tab in the Server Manager. This parameter is referenced as Proxy Timeout.
timeout-2 (timeout after interrupt)– The timeout after interrupt value tells the Proxy Server how much time it must continue writing a cache file after a client has aborted the transaction. In other words, if the Proxy Server has almost finished caching a document and the client aborts the connection, the server can continue caching the document until it reaches the timeout after interrupt value.
The highest recommended timeout after interrupt value is 5 minutes. The default value is 15 seconds.
http-client-config SAF (obj.conf)
The http-client-config function configures the Proxy Server’s HTTP client.
The syntax of this function is as follows:
Init fn=http-client-config
keep-alive=(true|false)
keep-alive-timeout=seconds
always-use-keep-alive=(true|false)
protocol=HTTP Protocol
proxy-agent="Proxy-agent HTTP request header"
The settings are defined as follows:
keep-alive– (Optional) Boolean that indicates whether the HTTP client should attempt to use persistent connections. The default is true.
keep-alive-timeout– (Optional) The maximum number of seconds to keep a persistent connection open. The default is 29.
always-use-keep-alive– (Optional) Boolean that indicates whether the HTTP client can reuse existing persistent connections for all types of requests. The default is false, meaning persistent connections will not be reused for non-GET requests or for requests with a body.
protocol– (Optional) HTTP protocol version string. By default, the HTTP client uses either HTTP/1.0 or HTTP/1.1, based on the contents of the HTTP request. In general, do not use the protocol parameter unless you encounter specific protocol interoperability problems.
proxy-agent– (Optional) Value of the Proxy-agent HTTP request header. The default is a string that contains the Proxy Server product name and version.
KeepAliveTimeout (magnus.conf)
This parameter determines the maximum time (in seconds) that the server holds open HTTP keep-alive connections or persistent connections between the client and the Proxy Server. The default is 30 seconds. The connection times out if idle for more than 30 seconds. The maximum is 300 seconds (5 minutes).
Problem solved. The problem was that the default TcpMaxConnectRetransmissions in Windows TCP/IP stack was 3.

The intervals between connection retransmissions in Windows OS seem to be such that it takes always about 20sec to
timeout failing TCP connection attempts.

Increasing the TcpMaxConnectRetransmissions parameter in registry helped to solve this issue. So actually this problem had nothing to do with Loadrunner.

When playback a web application, i'm getting the following error.

"Warning: Extension LrXml.dll reports error -1 on call to function ExtPerThreadInitialize
Error: Vuser failed to initialize extension LrXml.dll.
Vuser Terminated."

Please help me to resolve this problem.

Reinstall load runner

Performance center 9.0License renewal - This thread has been closed
Our performance center 9.0 current licenses will be ending this month; we are planning to get/renew the license from hp, what is the correct way to apply the new license to the performance center?

Could someone help me, what is the procedure to update/apply new license. Do we need to delete all the hosts from the Performance center and apply the license or just directly update the license from PC Admin page, or anything needs to update from DB side.

Appreciate your suggestions.
You just log into the PC admin site and select License and enter your new codes. Consult the PC admin guide if you are not sure.

All other servers will pick up the new license from the database.
The Performance Center License will be automatically pushed down to the utility server after applying it.
The Host (LoadRunner) License will be pushed down to all the Hosts machines (controllers not Load Generators) and the utility server. This configuration part after applying the license is similar to a reset in the Admin Site (under server configuration).

If the hosts are not correctly updated for some reasons, you can either:
* Perform a reset from the Admin site to force their reconfiguration,
* Delete and add again the problematic host.

The process as said in the previous post is very straightforward. I'm just giving you some heads up in case you may experience any issues.
we have sucessfully updated the PC and Hostlicenses......

Load Generator error - This thread has been closed
Here i am facing an error while running the script with 3different Load generators.

I am running script in remote machine.

Machine NTB0742 (my machine connected to "Machine X" remotely)

LGenerator: CPU6766 (where Vugen is installed) --- Result failed

LGenerator: NTB0742 (nothing installed related to LR)--Result--failed to connect machine

LGenerator: Localhost-- Result PASSED
(File attached for reference)
_______________________________________________
Procedure i did:
I created script and selected option"Connect to Controller" (from TOOLS menu)
In Controller vuser window, i given the Load generator machine names under Load generator tab.

Hello Prasad,

I hope you have installed the Loadgenerator agent on the machines where you are getting the failure as I can read that you have not installed LR on one machine and have only Vugen on the other machine which means that these machines cannot be used as Loadgenerators.

First,ensure that you ahve installed the LG agent on these machines.Next, check if the process magentproc.exe is running in your windows task manager.

Once you are sure of the above two steps, go to the LG tab in your controller and click on each LG name and click on connect and see if the connection is successful.

If the connection is not successful, then check if there is a firewall between the LG and the controller machine and see if the ports 54345 and 443 are open.
thank you so much for kind reply.

I was informed by one of my friend, dat no need to install LG in any machines, if u select a specific machine as LG it will automatically install LG in it.

that is why i selected different machines.
so i hope im wrong.
_____

My load test 4

JDS Australia » Tech Tips » Testing Web Services With a Standard Web Vuser
Testing Web Services With a Standard Web Vuser
October 6th, 2008 Posted by Stuart Moncrieff 6 Comments »
It is possible to test web services using the standard Web (HTTP/HTML) virtual user type instead of the Web Services vuser type. The main disadvantage of this is that you cannot generate your SOAP body from the WSDL file using the VuGen wizard. But if you know what your XML request should look like, then you shouldn’t have any real problems.
Here are my tips:

• Send your SOAP payload using lr_custom_request().
• Add a SOAPAction HTTP header using web_add_header().
• Remove unnecessary HTTP headers (that are generated automatically by VuGen) with web_remove_auto_header().
• Don’t forget to verify that you get a valid response. Use web_reg_find() for a simple check. For better verification of the SOAP response use lr_xml_find().
• To extract values from the response body, use lr_xml_get_values(). Brush up on your XPath qeries beforehand though.
• It may be necessary to HTML-encode some characters in your XML/SOAP message (e.g. convert “&” to “&”). Unfortunately VuGen does not provide this functionality (but HP could easily add it to the web_convert_param function), so you will have to either write (or find) a function to do it, or convert all the entries in your data table before running the script.
As an example, here is a simple script that makes use of a web service that will look up the source of a Shakespeare quote for you. The WSDL is available from http://www.xmlme.com/WSShakespeare.asmx?wsdl.
Action()
{
// ContentCheck Rules for known error messages
web_global_verification("Text=Speech not found", "ID=SpeechNotFound", LAST);

lr_start_transaction ("Search For Shakespeare Quote");

// By default, VuGen sends a user-agent header.
// Let's remove this as an example of removing automatically generated headers.
web_remove_auto_header("User-Agent", "ImplicitGen=No", LAST);

// Add a SOAPAction HTTP header
web_add_header("SOAPAction", "http://xmlme.com/WebServices/GetSpeech");

// Save entire body from the HTTP response for later checking with lr_xml_find.
web_reg_save_param("ResponseBody",
"LB=",
"RB=",
"Search=Body",
"IgnoreRedirections=Yes",
LAST);

// Note that the text to search for would normally be replaced with a parameter,
// and so would the element of the below SOAP message.
web_reg_find("Text=TWELFTH NIGHT", LAST);

web_custom_request("Search Shakespeare",
"URL=http://www.xmlme.com/WSShakespeare.asmx",
"Method=POST",
"Resource=0",
"Referer=",
"Snapshot=t1.inf",
"Mode=URL",
"EncType=text/xml; charset=utf-8",
"Body=" // As it is SOAP, you are unlikely to have to use BodyBinary, unless your request has CDATA.
""
""
""
""
"Be not afraid of greatness"
"
"
"
"
"
",
LAST);

// The response from the web service looks like this:
/*





<SPEECH>
<PLAY>TWELFTH NIGHT</PLAY>
<SPEAKER>MALVOLIO</SPEAKER>
'Be not afraid of greatness:' 'twas well writ.</SPEECH>




*/

// An example of extracting the a value from a SOAP reponse.
// This saves the element into {OutputParameter}.
// The same syntax could be used with lr_xml_find to check the response.
lr_xml_extract("XML={ResponseBody}",
"XMLFragmentParam=OutputParameter",
"Query=/soap:Envelope/soap:Body/GetSpeechResponse/GetSpeechResult", LAST);
lr_output_message("Source of Shakespeare quote: %s", lr_eval_string("{OutputParameter}"));

lr_end_transaction ("Search For Shakespeare Quote", LR_AUTO);

return 0;
}
Writing to a file
/*
Writes a string to the end of a file.
Arguments:
- file_name: Include the full path in the file name, and escape any slashes. E.g. "C:\\TEMP\\output.txt". Note that file does not have to exist beforehand, but directory does.
- string: If attempting to write a single line, include a newline character at the end of the string.
Returns 0 on success. On failure, function will raise lr_error_message and return -1.
*/
int jds_append_to_file(char* file_name, char* string) {
int fp; // file pointer
int rc; // return code
int length = strlen(string);

// Check that file_name is not NULL.
if (file_name == NULL) {
lr_error_message("Error. File name is NULL");
return -1;
}

fp = fopen(file_name, "a"); // open file in "append" mode.
if (fp == NULL) {
lr_error_message("Error opening file: %s", file_name);
return -1;
}

rc = fprintf(fp, "%s", string);
if (rc != length) {
lr_error_message("Error writing to file: %s", file_name);
return -1;
}

rc = fclose(fp);
if (rc != 0) {
lr_error_message("Error closing file: %s", file_name);
return -1;
}

return 0;
}
Check if a file already exists
// Checks if a file already exists on the filesystem.
// Arguments:
// - file_name: Include the full path in the file name.
// Returns TRUE (1) if file exists and user has read access to the file, otherwise function returns FALSE (0).
int jds_file_exists(char* file_name) {
int fp; // file pointer

fp = fopen(file_name, "r+"); // open file in read mode. File must already exist.
if (fp == NULL) {
return FALSE;
} else {
fclose(fp);
return TRUE;
}
}
Saving a file to hard disk
// Saves a file to the hard disk.
// Arguments:
// - file_name: Include the full path in the file name. Note that file must not exist before function is called.
// - file_content: The data to save to the file. Can be binary or string data.
// - file_size: The size/length of the data to save to the file. If it is string data, you can find this using strlen(). If you are saving binary data from a web page, use web_get_int_property(HTTP_INFO_DOWNLOAD_SIZE).
// Returns 0 on success. On failure, function will raise lr_error_message and return -1.
int jds_save_file(char* file_name, void* file_content, unsigned int file_size) {
int rc; // function return code
int fp; // file pointer

// Check input values
if (file_name == NULL) {
lr_error_message("File name is NULL");
return -1;
} else if (file_content == NULL) {
lr_error_message("File content is NULL");
return -1;
} else if (file_size < 1) { lr_error_message("Invalid file size: %d", file_size); return -1; } // Does the file already exist? if (jds_file_exists(file_name) == TRUE) { lr_error_message("File %s already exists", file_name); return -1; } fp = fopen(file_name, "wb"); // open file in "write, binary" mode. if (fp == NULL) { lr_error_message("Error opening file: %s", file_name); return -1; } rc = fwrite(file_content, file_size, 1, fp); if (rc != 1) { lr_error_message("Error writing to file. Items written: %d", rc); return -1; } rc = fclose(fp); if (rc != 0) { lr_error_message("Error closing file: %s", file_name); return -1; } return 0; } Saving a binary file from a webpage (like a PDF or a GIF). Note that this is not a good way to veryify that your LoadRunner/BPM script is running successfully. Action() { int size; char* file = "C:\\TEMP\\test.zip"; // Make this big enough to hold the downloaded file. web_set_max_html_param_len("1048576"); // 1 MB // Save entire HTTP response body web_reg_save_param("FileContents", "LB/BIN=", "RB/BIN=", "Search=Body", LAST); // Note that it is best to use web_custom_request, as this guarantees that only one file is being downloaded by this step. web_custom_request("DownloadPlugin", "URL=http://www.example.com/files/test.zip", "Method=GET", "Resource=1", "RecContentType=text/css", "Referer=http://www.jds.net.au", "Snapshot=t1.inf", LAST); // returns the size of the previous HTTP response size = web_get_int_property(HTTP_INFO_DOWNLOAD_SIZE); jds_save_file(file, lr_eval_string("{FileContents}"), size); return 0; } Understanding Aggregate Variance within LoadRunner Analysis January 23rd, 2009 Posted by Nick Wilton No Comments » From time to time, you may notice variances within the Loadrunner Analysis tool. This is most apparent when reviewing the Transaction Response Time (Percentile) graph. If you look at the following Transaction Summary Report: And look at the following Transaction Response Time (Percentile) graph: As you can see there are some conflicting values: Summary Report Graph Legend Graph Mouseover (Tooltip) Average 50th Percentile Average Median 50th Percentile 7.718 5.728 7.942 5.830 5.728 Firstly, I’ll state that all these values are correct. The LoadRunner Analysis engine calculates all values correctly, and all variances can be easily explained by examining the data. Median vs Mean vs Average vs Percentile The first concept to understand and permanently implant into your brain, is the difference between these four aggregate types. An “Aggregate” is a single value that represents an underlying set of multiple values, commonly known aggregates are Average, Minimum and Maximum. Most people understand what an “Average” is…that is the sum of a value set divided by the count of the value set. “Average” is also known as “Mean”. “Median” however is a different concept, a median value is calculated by sorting a set of data values from smallest to largest value then dividing this sorted set by two. The “Median” is the middle value at the place you divided the set. Median is identical to the 50th Percentile. Percentiles are an extension on the median concept, but instead of dividing the sorted value set by two…we instead divide the value set by a percentage (starting with the smallest value). So the 15th Percentile, refers to the value 15% along the sorted set…the 75th Percentile refers to the value 75% along the sorted set. Also the 0th Percentile is undefinable value. Based on this information, we can safely separate the values into the following two groups. Average Median/ 95th Percentile Summary Report 7.718 5.728 Graph Legend 7.942 5.830 Graph Mouseover - 5.728 Clearly the median value for the Graph Mouseover and the Summary Report are identical, so for the rest of the article I’ll will no longer refer to the Graph Mouseover. Graph Values vs Raw Values When the Percentile graph is generated (or any other graph for that matter), the analysis tool generates a small sample set for the graph. The sample set size is configurable in some graphs by setting the graph’s granularity, however the Percentile graph is fixed at a sample set of 100 data values…with each corresponding to a separate percentile value. Therefore the values for Average and Median for the Graph Legend are calculated from the Graph data, not the Raw data. Keeping this in mind, the Graph Average value is simply the average of the 1th, 2nd, 3rd…100th Percentile. Whilst this is graph average is close, it is not as accurate as the Summary Report average (which is calculated from the complete raw data set). Okay, so up to this point we’ve explained all the value variations, except for the difference with the Median value. This problem is interesting, remember I mentioned that the median value is simply the sorted value set divided into two…and that the 0th Percentile is undefined. Well, the analysis tool equally divides the graph data result set into a set of values representing the 1th Percentile through to the 50th Percentile, and the 51st Percentile through to the 100th Percentile. As there is no clearly defined midpoint in this data set the analysis engine uses the next value…in this case the 51st Percentile. So in summary. the Graph Median value actually the value of the 51st Percentile…not the 50th Percentile. This may be considered a minor bug. In Summary In summary, the graph value data is adequate for producing a graph…however for aggregate values I recommend only using the Summary Report data as the Summary report is the only report in the analysis engine that is guaranteed to display accurate data based on the complete data set. // Code taken from HP whitepaper on LoadRunner by // Opral Wisham from Waste Management, Inc. // This example was used in a script that required clicking // the "refresh" button until the run status changed to // "success". This code provides an automatic refresh // until the batch job has been completed. The next step // requires the completion of the batch job. int x; // flag will be 0 or 9 char *temp, *temp2; // values to hold strings Action() { temp2="Success"; //compare string 2 //lr_message("temp2 = %s", temp2); // set x to 0. x is the success flag x=0; do { web_reg_save_param("RunStatus", "LB=\n",
"RB=\n",
"Ord=5",
"Search=body",
LAST);

web_submit_data("PROCESSMONITOR.PROCESSMONITOR.GBL",
"Action=http://crpu028a:8050/psc/fs84cpv/EMPLOYEE/ERP/c/PROCESSMONITOR.PROCESSMONITOR.GBL",
"Method=POST",
"RecContentType=text/html",
"Referer=http://crpu028a:8050/psc/fs84cpv/EMPLOYEE/ERP/c/PROCESSMONITOR.PROCESSMONITOR.GBL?Page=PMN_PRCSLIST&Action=U&",
"Snapshot=t17.inf",
"Mode=NORESOURCE",
ITEMDATA,
"Name=ICType", "Value=Panel", ENDITEM,
"Name=ICElementNum", "Value=0", ENDITEM,
"Name=ICStateNum", "Value={ICStateNum6}", ENDITEM,
"Name=ICAction", "Value=REFRESH_BTN", ENDITEM,
"Name=ICXPos", "Value=0", ENDITEM,
"Name=ICYPos", "Value=0", ENDITEM,
"Name=ICFocus", "Value=", ENDITEM,
"Name=ICChanged", "Value=-1", ENDITEM,
"Name=ICFind", "Value=", ENDITEM,
"Name=PMN_FILTER_WS_OPRID", "Value=CPVID", ENDITEM,
"Name=PMN_FILTER_PRCSTYPE", "Value=", ENDITEM,
"Name=PMN_FILTER_FILTERVALUE", "Value=1", ENDITEM,
"Name=PMN_FILTER_FILTERUNIT", "Value=1", ENDITEM,
"Name=PMN_FILTER_SERVERNAME", "Value=PSUNX", ENDITEM,
"Name=PMN_FILTER_PRCSNAME", "Value=", ENDITEM,
"Name=PMN_DERIVED_PRCSINSTANCE", "Value={Process_Instance}", ENDITEM,
"Name=PMN_DERIVED_TO_PRCSINSTANCE", "Value={Process_Instance}", ENDITEM,
"Name=PMN_FILTER_RUNSTATUS", "Value=", ENDITEM,
LAST);

// Compare correlation value with character string
temp = lr_eval_string(lr_eval_string("{RunStatus}"));
// correlation value to variable
//lr_message("temp = %s", temp);
//compare string 1
if(strcmp(temp,temp2)==0){
// string compare success with correlation value
x=9; // set flag to indicate success
}
} while (x == 0); // do while flag not set

return 0;
}

Running a batch file using load runner


Action()

char command[1024]; // declare a cahar val

sprintf(command, "C:\\test\\test.bat");// mention the path of the file with full path details

system(command); // it will execute the command

return 0;

/**************************************/

on test.bat file i kept folowing command.

/************************************/
ping localhost -n 10
/***********************************/

now this batch file will run 10 times

Get the body length on a HTML request - This thread has been closed
Hi all,

I'm sending a request to an HTTP server which should return a PDF file. The request is:
web_url("sefas", "URL=http://localhost/test",
"TargetFrame=",
"Resource=1",
"RecContentType=application/pdf",
"Referer=",
"Snapshot=t2.inf",
LAST);

The response header don't have a content length defined, but on the replay log at the end of the web_url() function, I'll have this message:
Action.c(6): web_url("sefas") was successful, 62402 body bytes, 184 header bytes, 69 chunking overhead bytes [MsgId: MMSG-26385]

It gives the body length of the request, how can we get this value ?

thanks everyone.
Finally I found the solution (I did not find it in the first help look-up :(( ).
use the web_get_int_property() function.
It accept parameter as input, and one is HTTP_INFO_DOWNLOAD_SIZE and retrieve the complete size of the request (body+header+chunked).

if it could help someone.

Does Response time Include First Buffer time - This thread has been closed
Hi All,

This may be basic but I want to understand if the response time includes First buffer time also. Why I am asking this questions is average response time for one transaction 3.38 secs and the Page breakdown graph displayed an average of 14.8 secs for the transaction , out of which the first buffer is 13 secs. Can any one give me an idea on this. For the data what i thought is that I should not relate Response times with Page breakdown data. Please let me know your thoughts.

My load test 3

JDS Australia » Tech Tips » Monitoring Tomcat with LoadRunner
Monitoring Tomcat with LoadRunner
December 10th, 2008 Posted by Stuart Moncrieff 3 Comments »
LoadRunner does not come with a monitor for Tomcat. Fortunately, you can easily create one in about 5 minutes…

Tomcat exposes metrics related to JVM memory and Servlet container threads (and some other useful information) on a Status page at /manager (ask your Tomcat admin to enable it).

Create a standard web vuser script which loads the Tomcat Status page, and capture all the metrics you want using web_reg_save_param. Then log these values using lr_user_data_point. The metrics will be visible in the LoadRunner Controller and also in LoadRunner Analysis on the User-Defined Data Points Graph.
lr_start_transaction("monitor tomcat");

/*

JVM

Free memory: 130.99 MB Total memory: 254.18 MB Max memory: 1016.12 MB

*/

web_reg_save_param("JVMFreeMemory",
"LB=Free memory: ",
"RB= MB",
"Ord=1",
LAST);

web_reg_save_param("JVMTotalMemory",
"LB=Total memory: ",
"RB= MB",
"Ord=1",
LAST);

web_reg_save_param("JVMMaxMemory",
"LB=Max memory: ",
"RB= MB",
"Ord=1",
LAST);

web_reg_find("Text=/manager",
LAST);

web_url("status",
"URL=http://{ServerName}/manager/status",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTTP",
LAST);

lr_end_transaction("monitor tomcat", LR_AUTO);

// Tomcat JVM metrics
lr_user_data_point("Tomcat JVM Free memory", atof(lr_eval_string("{JVMFreeMemory}")));
lr_user_data_point("Tomcat JVM Total memory", atof(lr_eval_string("{JVMTotalMemory}")));
lr_user_data_point("Tomcat JVM Max memory", atof(lr_eval_string("{JVMMaxMemory}")));
VuGen correlation for SAP Web Dynpro
July 27th, 2008 Posted by Stuart Moncrieff 3 Comments »
If you are trying to create a LoadRunner script for a SAP Web Dynpro application, and you are having problems correlating the SAPEVENTQUEUE in your POST request, then this Tech Tip is for you…

Here is what a typical request might look like:
web_submit_data("sap-ext-sid_2",
"Action=http://www.example.com:8000/sap/bc/webdynpro/SAP/ERC_A_WORKCENTER/;sap-ext-sid={SapExtSid2_120}",
"Method=POST",
"TargetFrame=",
"RecContentType=text/html",
"Referer=http://www.example.com:8000/sap/bc/webdynpro/SAP/ERC_A_WORKCENTER/;sap-ext-sid={SapExtSid2_98}",
"Snapshot=t18.inf",
"Mode=HTML",
ITEMDATA,
"Name=SAPEVENTQUEUE", "Value=Custom_ClientInfos~E002Id~E004WD01~E005WindowOpenerExists~E004false~E005ClientURL~E004http~003A~002F~002Fwww.example.com~003A8000~002Fsap~002Fbc~002Fwebdynpro~002FSAP~002FERC_A_WORKCENTER~002F~003Bsap-ext-sid~003DzuUt57Mx_3JozG7pOff~002AEg--U_0j6OHCaCQurUN1Pimp1Q--~E003~E002ClientAction~E004enqueue~E005ResponseData~E004delta~E003~E002~E003~E001TimeTrigger_Trigger~E002Id~E004WDE4~E003~E002ResponseData~E004delta~E005ClientAction~E004submit~E003~E002~E003", ENDITEM,
"Name=sap-charset", "Value=utf-8", ENDITEM,
"Name=_client_url_", "Value=", ENDITEM,
LAST);
Obviously the sap-ext-sid has already been correlated (this is easy to do with a Correlation Rule), but the SAPEVENTQUEUE also needs to be correlated. This is difficult, as it is constructed dynamically using JavaScript, so the value does not appear directly in any HTML response, and therefore cannot be correlated using a simple web_reg_save_param.
Examining the SAPEVENTQUEUE string, there are two repeated patterns; a series of 5 characters like “~E005?, and a series of 5 characters like “~003A” (without the “E”). Taking an educated guess, we can see that the string…
1 http~003A~002F~002Fwww.example.com~003A8000~002Fsap~002Fbc~002Fwebdynpro~002FSAP~002FERC_A_WORKCENTER~002F~003Bsap-ext-sid~003DzuUt57Mx_3J
…is an encoding of…
1 http://www.example.com:8000/sap/bc/webdynpro/SAP/ERC_A_WORKCENTER/;sap-ext-sid=zuUt57Mx_3J
…which means that…
• ~003A is :
• ~002F is /
• ~002F is /
• ~003D is =
• ~003B is ;
So it looks like SAP has invented their own way of URL Encoding values to be POSTed to a Web Dynpro server.
But what about the encoded values with an “E” at the start? Searching through the source code, we find that these are special “event separators”…
• ~E001 is EVENT
• ~E002 is SECTION_BEGIN
• ~E003 is SECTION_END
• ~E004 is KEYVALUE
• ~E005 is KEYVALUE_PAIR
• ~E006 is COLLECTION_ENTRY
As I am unlikely to want to change the separators, here is a simple function that will encode a string using SAP’s special version of URL encoding.
// This function replaces unreserved characters in a string with their encoded values.
// Encoding is in the style of SAP Web Dynpro. E.g. "abd*def" becomes "abc~002Adef".
// Reserved/unreserved characters are according to RFC3986 (http://tools.ietf.org/html/rfc3986)
// This function returns a pointer to the start of the encoded string (buf).
// Note that buf must be big enough to hold original string plus all converted entities.
char* dynpro_encode(char* plain_string, char* buf) {
int len = strlen(plain_string);
int i,j;
char hex_value[3];

if (plain_string == NULL) {
lr_error_message("Input string is empty.");
return NULL;
}

for (i=0, j=0; i= 'A' && plain_string[i] <= 'Z') || (plain_string[i] >= 'a' && plain_string[i] <= 'z') || (plain_string[i] >= '0' && plain_string[i] <= '9') || (plain_string[i] == '-') || (plain_string[i] == '_') || (plain_string[i] == '.') || (plain_string[i] == '~') ) { buf[j++] = plain_string[i]; } else if ( (plain_string[i] < 32 ) || (plain_string[i] > 126) ) {
lr_error_message("Input string contains non-printable or non-ASCII character %c at position: %d", plain_string[i], i);
return NULL;
} else {
// The unicode value for use in url encoding is the same as the hex value for the ASCII character
itoa(plain_string[i], hex_value, 16);
buf[j++] = '~';
buf[j++] = '0';
buf[j++] = '0';
buf[j++] = toupper(hex_value[0]);
buf[j++] = toupper(hex_value[1]);
}
}

buf[j] = NULL; // terminate the string
return buf;
}
And, just for completeness, here is a function that will decode a string.
char *strncpy ( char *dest, const char *source, size_t n ); // explicit declaration required

// This function replaces encoded characters from with their non-encoded value.
// Decoding is in the style of SAP Web Dynpro. E.g. "abc~002Adef" becomes "abd*def".
// Reserved characters are according to RFC3986 (http://tools.ietf.org/html/rfc3986)
// This function returns a pointer to the start of the decoded string (buf).
// Note that buf must be big enough to hold the decoded string (always equal to or shorter than the encoded string).
char* dynpro_decode(char* enc_string, char* buf) {
int len = strlen(enc_string);
int i, j;
char code[3]; // holds url encoded value e.g. "2F" (/)
int hex; // decimal value of hex code e.g. 47 (0x2F)
int rc; // return code

if (enc_string == NULL) {
lr_error_message("Input string is empty.");
return NULL;
}

for (i=0, j=0; i // Only convert entities that do not start with "~E". Do not run off the end of the string.
if ( (enc_string[i] == '~') &&
(enc_string[i+1] != 'E') &&
((i+4) < len) &&
(enc_string[i+1] == '0') &&
(enc_string[i+2] == '0') &&
(isalpha(enc_string[i+3]) || isdigit(enc_string[i+3])) &&
(isalpha(enc_string[i+4]) || isdigit(enc_string[i+4])) ) {
// Get the hex value from the input string
code[0] = enc_string[i+3];
code[1] = enc_string[i+4];
code[3] = NULL;
// Convert the hex value to the appropriate character, and add it to buf
rc = sscanf(code, "%2x", &hex);
if (rc != 1) {
lr_error_message("Invalid hex value: %s", code);
}
buf[j] = hex;
i+=4; // skip the rest of this encoded value in the input string
} else {
buf[j] = enc_string[i];
}
}

return buf;
}

My load test 2

Action()
{
merc_timer_handle_t timer;
double duration;
double pacing_time;

// Read desired pacing timer from a text file.
// The text file should contain the number of seconds to be used for pacing.
pacing_time = (double)jds_read_pacing_time("C:\\TEMP\\vugen_pacing.txt");

// Start the timer.
timer = lr_start_timer();

// Call Action function that you want to control pacing for.
// Note that if you do it this way (rather than putting the pacing code at the start and end of the
// Action), then you need to first go to the Run Logic area of the runtime settings, and right-click the
// Search Action and select "remove item" from the context menu.
Search();

// Stop the timer
duration = lr_end_timer(timer);

// Wait for the necessary number of seconds to elapse before starting the next iteration.
if (duration < pacing_time) { lr_think_time(pacing_time - duration); } else { lr_error_message("Pacing time exceeded. Target: %G seconds. Actual: %g seconds", pacing_time, duration); } return 0; } // Read pacing time from a file. Returns time in seconds (whole seconds only). // Note that file can be on a shared network drive. int jds_read_pacing_time(char* file_name) { long fs; // file stream int number_from_file; // Open the file for reading fs = fopen(file_name, "r+"); if (fs == NULL) { lr_error_message("Error opening file: %s", file_name); return -1; } // Read number from file. if (fscanf(fs, "%d", &number_from_file) != 1) { lr_error_message("Error reading number from file: %s", file_name); return -1; } fclose(fs); return number_from_file; } JDS Australia » Tech Tips » Querying a MySQL database with LoadRunner Querying a MySQL database with LoadRunner March 29th, 2009 Posted by Stuart Moncrieff 2 Comments » Let’s imagine that you want to execute arbitary SELECT, INSERT, UPDATE and DELETE queries against a MySQL database from a VuGen script. Obviously it is easiest to use the JDBC libraries from a Java-based script, but most people aren’t licensed for any of the Java-based vuser types. It is much more useful to be able to interact with MySQL from a C-based vuser script, such as the Web (HTTP/HTML) vuser type. JDS has already released code that allows you to use MySQL instead of the Virtual Table Server from a C-based script, but this code will allow you to run any query you like. Assuming that you have already installed MySQL and created a user, you must then create a database schema and table(s) to use. CREATE DATABASE `loadrunner` ; USE `loadrunner`; CREATE TABLE `test_data` ( `order_id` BIGINT UNSIGNED NOT NULL COMMENT 'Order numbers. Must be unique.', `status` BOOL NOT NULL DEFAULT '0' COMMENT 'Whether data has been used or not. A value of 0 means FALSE.', `date_used` DATETIME NULL COMMENT 'Date/time that the data was used.', UNIQUE ( `order_id` ) ) ENGINE = innodb COMMENT = 'LoadRunner test data'; Now you are free to talk to the database from your VuGen script. Here is the example code: // The MySQL 5.0 C API documentation is available from: // http://dev.mysql.com/doc/refman/5.0/en/c-api-functions.html // Note that this code may have problems with thread safety. // It is therefore best to run each vuser as a process rather than as a thread. Action() { int rc; // return code int db_connection; // Declaractions is a bit dodgy. Should really use MYSQL defined in mysql.h int query_result; // Declaractions is a bit dodgy. Should really use MYSQL_RES defined in mysql.h char** result_row; // Return data as array of strings. Declaractions is a bit dodgy. Should really use MYSQL_ROW defined in mysql.h char *server = "localhost"; char *user = "root"; char *password = ""; // very naughty to leave default root account with no password :) char *database = "loadrunner"; int port = 3306; // default MySQL port int unix_socket = NULL; // leave this as null int flags = 0; // no flags // You should be able to find the MySQL DLL somewhere in your MySQL install directory. rc = lr_load_dll("C:\\LoadRunner\\Lib\\libmysql.dll"); if (rc != 0) { lr_error_message("Could not load libmysql.dll"); lr_abort(); } // Allocate and initialise a new MySQL object db_connection = mysql_init(NULL); if (db_connection == NULL) { lr_error_message("Insufficient memory"); lr_abort(); } // Connect to the database rc = mysql_real_connect(db_connection, server, user, password, database, port, unix_socket, flags); if (rc == NULL) { lr_error_message("%s", mysql_error(db_connection)); mysql_close(db_connection); lr_abort(); } // INSERT a row into the database table lr_param_sprintf("paramInsertQuery", "INSERT INTO test_data (order_id) VALUE (%d)", time(NULL)); // use current time as order ID for this example rc = mysql_query(db_connection, lr_eval_string("{paramInsertQuery}")); if (rc != 0) { lr_error_message("%s", mysql_error(db_connection)); mysql_close(db_connection); lr_abort(); } // SELECT a single value from the database table, and print the result rc = mysql_query(db_connection, "SELECT order_id FROM test_data WHERE status IS FALSE LIMIT 1"); if (rc != 0) { lr_error_message("%s", mysql_error(db_connection)); mysql_close(db_connection); lr_abort(); } query_result = mysql_use_result(db_connection); if (query_result == NULL) { lr_error_message("%s", mysql_error(db_connection)); mysql_free_result(query_result); mysql_close(db_connection); lr_abort(); } result_row = (char **)mysql_fetch_row(query_result); // if the result set had multiple rows, we could keep calling mysql_fetch_row until it returned NULL to get all the rows. if (result_row == NULL) { lr_error_message("Did not expect the result set to be empty"); mysql_free_result(query_result); mysql_close(db_connection); lr_abort(); } lr_save_string(result_row[0], "paramOrderID"); // this parameter will be used when deleting the row. lr_output_message("Order ID is: %s", lr_eval_string("{paramOrderID}")); mysql_free_result(query_result); // SELECT and UPDATE a row in the same step (to avoid concurrency problems if more than 1 vuser is consuming this data). // Note that for transactions to work, your MySQL database must use the InnoDB engine. rc = mysql_query(db_connection, "BEGIN"); // begin the transaction if (rc != 0) { lr_error_message("%s", mysql_error(db_connection)); mysql_close(db_connection); lr_abort(); } rc = mysql_query(db_connection, "SELECT order_id FROM test_data WHERE status IS FALSE LIMIT 1 FOR UPDATE"); // note that "FOR UPDATE" locks the record for reading, so other vusers will not get this value. if (rc != 0) { lr_error_message("%s", mysql_error(db_connection)); mysql_close(db_connection); lr_abort(); } query_result = mysql_use_result(db_connection); if (query_result == NULL) { lr_error_message("%s", mysql_error(db_connection)); mysql_free_result(query_result); mysql_close(db_connection); lr_abort(); } result_row = (char **)mysql_fetch_row(query_result); // if the result set had multiple rows, we could keep calling mysql_fetch_row until it returned NULL to get all the rows. if (result_row == NULL) { lr_error_message("Did not expect the result set to be empty"); mysql_free_result(query_result); mysql_close(db_connection); lr_abort(); } lr_save_string(result_row[0], "paramOrderID"); // this parameter will be used when deleting the row. lr_output_message("Order ID is: %s", lr_eval_string("{paramOrderID}")); mysql_free_result(query_result); lr_param_sprintf("paramUpdateQuery", "UPDATE test_data SET status=TRUE, date_used=NOW() WHERE order_id='%s'", lr_eval_string("{paramOrderID}")); rc = mysql_query(db_connection, lr_eval_string("{paramUpdateQuery}")); // UPDATE row to indicate that data has been used if (rc != 0) { lr_error_message("%s", mysql_error(db_connection)); mysql_close(db_connection); lr_abort(); } rc = mysql_query(db_connection, "COMMIT"); // commit the transaction if (rc != 0) { lr_error_message("%s", mysql_error(db_connection)); mysql_close(db_connection); lr_abort(); } // SELECT a row from the table (this returns an empty record set if table was empty at the beginning) rc = mysql_query(db_connection, "SELECT order_id FROM test_data WHERE status IS FALSE LIMIT 1"); if (rc != 0) { lr_error_message("%s", mysql_error(db_connection)); mysql_close(db_connection); lr_abort(); } query_result = mysql_use_result(db_connection); if (query_result == NULL) { lr_error_message("%s", mysql_error(db_connection)); mysql_free_result(query_result); mysql_close(db_connection); lr_abort(); } result_row = (char **)mysql_fetch_row(query_result); // if the result set had multiple rows, we could keep calling mysql_fetch_row until it returned NULL to get all the rows. if (result_row == NULL) { lr_output_message("Result set is empty as expected"); mysql_free_result(query_result); } else { lr_error_message("Did not expect the result set to contain any rows"); mysql_free_result(query_result); mysql_close(db_connection); lr_abort(); } // DELETE a row from the table lr_param_sprintf("paramDeleteQuery", "DELETE FROM test_data WHERE order_id = '%s'", lr_eval_string("{paramOrderID}")); rc = mysql_query(db_connection, lr_eval_string("{paramDeleteQuery}")); if (rc != 0) { lr_error_message("%s", mysql_error(db_connection)); mysql_close(db_connection); lr_abort(); } // Free the MySQL object created by mysql_init mysql_close(db_connection); return 0; } JDS Australia » Tech Tips » The “is it done yet” loop The “is it done yet” loop February 9th, 2009 Posted by Stuart Moncrieff No Comments » Occasionally you will find that you must write some code in VuGen to continuously check that the system has completed something, before you continue. Two examples that I have found recently were: • A web application that generates reports. Click a button to generate a report. The report gets generated in the background on the report server. When the report is ready to be viewed, the web page will be updated with the report name appearing as a hyperlink. The virtual user should keep refreshing the page until the report is available, then they should view the report. Typically report generation takes 10 minutes. • A message-based system. An order message is placed on a queue, where it is picked up and processed by the system. Processing typically takes 30-90 seconds, and is complete when the status of the order is flagged as “complete” in the database. To determine the processing time for the order, the virtual user must repeadedly query the database using the order number. Once the order status is “complete”, a transaction can be created with the correct duration (using lr_set_transaction). If you have a basic understanding of your chosen VuGen user type, and know how to write the code for a loop, you might think that writing code to do this is easy. JDS Australia » Tech Tips » How to handle HTTP POSTs with a changing number of name-value pairs How to handle HTTP POSTs with a changing number of name-value pairs December 22nd, 2008 Posted by Stuart Moncrieff 3 Comments » Occasionally you will find that you need to create a VuGen script for a web application which changes the number of name-value pairs which are sent with a POST request. This tech tip shows you how to handle this situation by dynamically constructing a POST body. In the example below, you can see that the web_submit_data function for the updateItemsFromSearch request (which adds items to a shopping cart) has six item IDs and six item quantities. The web_subit_data function presents the name-value pairs of the POST request in an easy to read format, but doesn’t give too many clues as to how you might handle the case of submitting a varying numbers of item IDs, without writing an if statement and having separate web_submit_data functions for every possible number of items. Fortunately, there is an easy way to do this. Read on… web_submit_data("updateItemsFromSearch.do", "Action=http://www.example.com.au/catalog/updateItemsFromSearch.do", "Method=POST", "TargetFrame=", "RecContentType=text/html", "Referer=http://www.example.com.au/catalog/search.do?key=0/46EF7F373045033002000000AC193D36", "Snapshot=t10.inf", "Mode=HTML", ITEMDATA, "Name=sortOption", "Value=PRICE_ASCENDING", ENDITEM, "Name=pageselect", "Value=10", ENDITEM, "Name=page", "Value=", ENDITEM, "Name=itemPageSize", "Value=10", ENDITEM, "Name=next", "Value=addToBasket", ENDITEM, "Name=itemkey", "Value=46EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51", ENDITEM, "Name=order", "Value=", ENDITEM, "Name=itemquantity", "Value=1", ENDITEM, "Name=isExtendedResult", "Value=null", ENDITEM, "Name=display_scenario", "Value=products", ENDITEM, "Name=contractkey", "Value=", ENDITEM, "Name=contractitemkey", "Value=", ENDITEM, "Name=item[0].itemID", "Value=46EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51", ENDITEM, "Name=item[0].quantity", "Value=1", ENDITEM, "Name=item[1].itemID", "Value=46EF7F373045033002000000AC193D3640EBE620771F00A500000000AC193D52", ENDITEM, "Name=item[1].quantity", "Value=1", ENDITEM, "Name=item[2].itemID", "Value=46EF7F373045033002000000AC193D363F4191CB824400F3E1000000AC193D38", ENDITEM, "Name=item[2].quantity", "Value=1", ENDITEM, "Name=item[3].itemID", "Value=46EF7F373045033002000000AC193D36464DB497A33B005602000000AC193D51", ENDITEM, "Name=item[3].quantity", "Value=1", ENDITEM, "Name=item[4].itemID", "Value=46EF7F373045033002000000AC193D36484A7C7E995A8034E1008000AC193D51", ENDITEM, "Name=item[4].quantity", "Value=1", ENDITEM, "Name=item[5].itemID", "Value=46EF7F373045033002000000AC193D36484E990F2564C27EE1008000AC193D35", ENDITEM, "Name=item[5].quantity", "Value=1", ENDITEM, LAST); First, let’s take a look at what VuGen actually sends to the web server… POST /catalog/updateItemsFromSearch.do HTTP/1.1\r\n Content-Type: application/x-www-form-urlencoded\r\n Cache-Control: no-cache\r\n Referer: http://www.example.com.au/catalog/search.do?key=0/46EF7F373045033002000000AC193D36\r\n User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)\r\n Accept-Encoding: gzip, deflate\r\n Accept-Language: en-us\r\n Accept: */*\r\n Connection: Keep-Alive\r\n Host: www.example.com.au\r\n Cookie: JSESSIONID=(J2EE5934700)ID1010337953DB11208117568232223992End; saplb_*=(J2EE5934700)5934753\r\n Content-Length: 896\r\n \r\n sortOption=PRICE_ASCENDING&pageselect=10&page=&itemPageSize=10&next=addToBasket&itemkey=46 EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51&order=&itemquantity=1&isExt endedResult=null&display_scenario=products&contractkey=&contractitemkey=&item%5B0%5D.itemI D=46EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51&item%5B0%5D.quantity=1& item%5B1%5D.itemID=46EF7F373045033002000000AC193D3640EBE620771F00A500000000AC193D52&item%5 B1%5D.quantity=1&item%5B2%5D.itemID=46EF7F373045033002000000AC193D363F4191CB824400F3E10000 00AC193D38&item%5B2%5D.quantity=1&item%5B3%5D.itemID=46EF7F373045033002000000AC193D36464DB 497A33B005602000000AC193D51&item%5B3%5D.quantity=1&item%5B4%5D.itemID=46EF7F37304503300200 0000AC193D36484A7C7E995A8034E1008000AC193D51&item%5B4%5D.quantity=1&item%5B5%5D.itemID=46 EF7F373045033002000000AC193D36484E990F2564C27EE1008000AC193D35&item%5B5%5D.quantity=1 As you can see, the name-value pairs that were so easy to read in the web_submit_data function are really just sent as a big blob of text, with each name-value pair separated by an ampersand (&) and some characters URL-encoded (e.g. “[” becomes “%5B”). We can make our VuGen script look more like the actual HTTP request by regenerating the script with web_custom_request selected. Select Tools > Regenerate Script, then select the recording option of URL-based script. Under URL Advanced, select Use web_custom_request only.
You script will now look something like this…
web_custom_request("updateItemsFromSearch.do",
"URL=http://www.example.com.au/catalog/updateItemsFromSearch.do",
"Method=POST",
"Resource=0",
"RecContentType=text/html",
"Referer=http://www.example.com.au/catalog/search.do?key=0/46EF7F373045033002000000AC193D36",
"Snapshot=t207.inf",
"Mode=HTTP",
"Body=sortOption=PRICE_ASCENDING&pageselect=10&page=&itemPageSize=10&next=addToBasket&itemkey=46EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51&order=&itemquantity=1&isExtendedResult=null&display_scenario=products&contractkey=&contractitemkey=&item%5B0%5D.itemID=46EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51&item%5B0%5D.quantity=1&item%5B1%5D.itemID=46EF7F373045033002000000AC193D3640EBE620771F00A500000000AC193D52&item%5B1%5D.quantity=1&item%5B2%5D.itemID="
"46EF7F373045033002000000AC193D363F4191CB824400F3E1000000AC193D38&item%5B2%5D.quantity=1&item%5B3%5D.itemID=46EF7F373045033002000000AC193D36464DB497A33B005602000000AC193D51&item%5B3%5D.quantity=1&item%5B4%5D.itemID=46EF7F373045033002000000AC193D36484A7C7E995A8034E1008000AC193D51&item%5B4%5D.quantity=1&item%5B5%5D.itemID=46EF7F373045033002000000AC193D36484E990F2564C27EE1008000AC193D35&item%5B5%5D.quantity=1",
LAST);
The next step is to dynamically construct the string that will be sent as the body argument of the web_custom_request function. Do this by
• Correlate your values using ORD=All (line 4) from the response to your search request (line 15).
• Write a for loop to build a parameter containing your POST body (line 31). Save time by using lr_paramarr functions to extract the values from the parameter array you created with web_reg_find.
• Use web_custom_request to send your POST body.
lr_start_transaction("search for product");

// Save all item IDs returned by product search
web_reg_save_param("ItemID_array",
"LB= "RB='",
"Ord=All",
"Search=Body",
"RelFrameId=1",
"IgnoreRedirections=Yes",
LAST);

web_reg_find("Text=Search Results for {SearchTerm}", LAST);

web_url("Search",
"URL=http://www.example.com.au/catalog/search.do?key={SearchTerm}",
"TargetFrame=center2",
"Resource=0",
"RecContentType=text/html",
"Referer=http://www.example.com.au/",
"Snapshot=t8.inf",
"Mode=HTML",
LAST);

lr_end_transaction("search for product",LR_AUTO);
lr_think_time(5);
lr_start_transaction("add to basket");

// Construct variable length POST body.
lr_save_string(lr_eval_string("sortOption=PRICE_ASCENDING&pageselect=10&page=&itemPageSize=10&next=addToBasket&itemkey={ItemIDarray_1}&order=&itemquantity=1&isExtendedResult=null&display_scenario=products&contractkey=&contractitemkey="), "Body");
for (i=0; i lr_save_string(lr_paramarr_idx("ItemIDarray", i+1), "ItemID");
lr_save_int(i, "ItemIndex");
lr_save_string(lr_eval_string("{Body}&item%5B{ItemIndex}%5D.itemID={ItemID}&item%5B{ItemIndex}%5D.quantity=1"), "Body");
}

web_reg_find("Text=Mini Shopping Basket", LAST);

web_custom_request("updateItemsFromSearch.do",
"URL=http://www.example.com.au/catalog/updateItemsFromSearch.do",
"Method=POST",
"Resource=0",
"RecContentType=text/html",
"Referer=http://www.example.com.au/catalog/search.do?key={SearchTerm}",
"Snapshot=t207.inf",
"Mode=HTTP",
"Body={Body}", // use dynamically constructed POST body.
LAST);

lr_end_transaction("add to basket",LR_AUTO);
JDS Australia » Tech Tips » VuGen Scripting for YouTube Video
VuGen Scripting for YouTube Video
October 7th, 2009 Posted by Nick Wilton No Comments »
Video has seen a massive surgance on the internet with the launch of YouTube and other video sharing web sites. This raises some interesting challenges beyond simple scripting in VuGen; with a combination of Javascript, Adobe Flash and HTTP partial download support.
This article will show you how to play a video, and save it to your hard drive.

Okay, first step. Old style web video basically streams web video to the user on a “best-effort” basis…this sort of video can be recorded using the Media Player (MMS) protocol in VuGen. However Flash Video is comprised of an embedded flash SWF file which downloads a FLV video file over standard HTTP.
The steps within this example script for this tech tip are:
1. Go to the video page (e.g. http://www.youtube.com/watch?v=J—aiyznGQ)
2. Play the Video. Capturing the FLV file to your local disk.
For obvious reasons, we are removing any advertisting requests. The aim is to capture the YouTube video and save it to the local hard drive.
Getting Started
The first step is to create a new script using the standard Web (HTTP/HTML) protocol. After doing that, add the following three lines to your script.
web_set_max_html_param_len("50000000");
web_set_timeout("RECEIVE", "300");
web_set_timeout("STEP", "300");
These lines increase the parameter length limit to 50MB (for storing our video data), and increase the download timeout to 5 minutes (you may need to increase this further if you’re running on a slow connection.
The next step is to navigate to the Video. I’ve included the famous “Keyboard Cat” video, but I suggest that you parameterize this and navigate to any YouTube video.
web_reg_save_param("videoid", "LB=\"video_id\": \"", "RB=\"", "Ord=1", LAST);
web_reg_save_param("t", "LB=\"t\": \"", "RB=%3D\"", "Ord=1", LAST);
web_reg_save_param("fmt", "LB=fmt_url_map\": \"", "RB=%7", "Ord=1", LAST);
web_reg_save_param("swfplayer", "LB=canPlayV9Swf() ? \"", "RB=.swf", "Ord=1", "NotFound=Error", LAST);
web_reg_save_param("playbackhost", "LB=%7Chttp%3A%2F%2F", "RB=%", "Ord=1", "NotFound=Error", LAST);
web_reg_save_param("itag", "LB=itag%3D", "RB=%", "Ord=1", "NotFound=Error", LAST);
web_reg_save_param("ipbits", "LB=ipbits%3D", "RB=%", "Ord=1", "NotFound=Error", LAST);
web_reg_save_param("signature", "LB=signature%3D", "RB=%", "Ord=1", "NotFound=Error", LAST);
web_reg_save_param("sver", "LB=sver%3D", "RB=%", "Ord=1", "NotFound=Error", LAST);
web_reg_save_param("expire", "LB=expire%3D", "RB=%", "Ord=1", "NotFound=Error", LAST);
web_reg_save_param("key", "LB=key%3D", "RB=%", "Ord=1", "NotFound=Error", LAST);
web_reg_save_param("factor", "LB=factor%3D", "RB=%", "Ord=1", "NotFound=Error", LAST);
web_reg_save_param("burst", "LB=burst%3D", "RB=%", "Ord=1", "NotFound=Error", LAST);
web_reg_save_param("id", "LB=%26id%3D", "RB=%", "Ord=1", "NotFound=Error", LAST);

web_url("watch",
"URL=http://www.youtube.com/watch?v=J---aiyznGQ",
"Resource=0",
"RecContentType=text/html",
"Mode=HTML",
LAST);
Next we download the Flash player file and send a get_video request. The get_video request doesn’t actually return any content, but is part of how YouTube operates.
web_url("LoadPlayer",
"URL={swfplayer}.swf",
"TargetFrame=",
"Resource=0",
"Mode=HTML",
LAST);

web_url("get_video",
"URL=http://www.youtube.com/get_video?video_id={videoid}&t={t}=&el=detailpage&ps=&fmt={fmt}&noflv=1",
"TargetFrame=",
"Resource=0",
"Referer={swfplayer}.swf",
"Mode=HTML",
LAST);
The last step is to download the FLV file.
We use the code snippet from http://www.jds.net.au/tech-tips/vugen-code-snippets to save the downloaded FLV to our local disk.
web_add_header("x-flash-version", "10,0,32,18");
web_add_header("UA-CPU", "x86");

web_reg_save_param("FLVContents", "LB=", "RB=", "Search=Body", LAST);

web_url("videoplayback",
"URL=http://{playbackhost}/videoplayback?ip=0.0.0.0&sparams=id%2Cexpire%2Cip%2Cipbits%2Citag%2Cburst%2Cfactor&fexp=900018&itag={itag}&ipbits={ipbits}&signature={signature}&sver={sver}&expire={expire}&key={key}&factor={factor}&burst={burst}&id={id}&redirect_counter=1",
"TargetFrame=",
"Resource=0",
"Referer={swfplayer}.swf"
"Mode=HTML",
LAST);

size = web_get_int_property(HTTP_INFO_DOWNLOAD_SIZE);

jds_save_file(lr_eval_string("Keyboard Cat.flv"), lr_eval_string("{FLVContents}"), size);
That’s it !!!!
Just a word of caution. I don’t provide this script with the intention that you actually run Load Tests against YouTube…that would be a very bad idea. This is technical example for interest purposes only.
JDS Australia » Tech Tips » VuGen String Comparison Behaviour
VuGen String Comparison Behaviour
December 16th, 2008 Posted by Stuart Moncrieff 5 Comments »
Anyone who works with VuGen should know that they should compare strings using the standard C function strcmp(), rather than the equality operator (==).
In the example below, there are three string variables that each contain “hello world”. Comparing the strings using strcmp() shows that all the strings are the same, but comparing them using “==” gives TRUE for string1==string2, but FALSE for string1==string3.
I leave this as a challenge to the reader to explain this behaviour (please leave a comment below).

Here is the example code:
extern char* strtok(char *token, const char *delimiter);

Action()
{
char* string1 = "hello world";
char* string2 = "hello world";
char buf[12];
char* string3;

strcat(buf, "hello ");
string3 = (char*)strcat(buf, "world");
lr_output_message("string3: %s", string3);

// Compare two identical strings using strcmp
if (strcmp(string1, string2) == 0) {
lr_output_message("string1 and string2 match using strcmp");
} else {
lr_output_message("string1 and string2 do not match using strcmp");
}

// Compare two identical strings using "=="
if (string1 == string2) {
lr_output_message("string1 and string2 match using \"==\"");
} else {
lr_output_message("string1 and string2 do not match using \"==\"");
}

// Compare two identical strings using strcmp
if (strcmp(string1, string3) == 0) {
lr_output_message("string1 and string3 match using strcmp");
} else {
lr_output_message("string1 and string3 do not match using strcmp");
}

// Compare two identical strings using "=="
if (string1 == string3) {
lr_output_message("string1 and string3 match using \"==\"");
} else {
lr_output_message("string1 and string3 do not match using \"==\"");
}

return 0;
}
Here is the output from the code:
Running Vuser...
Starting iteration 1.
Starting action Action.
Action.c(12): string3: hello world
Action.c(18): string1 and string2 match using strcmp
Action.c(25): string1 and string2 match using "=="
Action.c(32): string1 and string3 match using strcmp
Action.c(41): string1 and string3 do not match using "=="
Ending action Action.
Ending iteration 1.
Ending Vuser...