Wednesday, November 20, 2013

How to generate GUID in LoadRunner


Compatibility:  Java (all version), SOAP UI, LoadRunner/VuGen (all version)

Today I came across a SOAP request that uses a java function (java.util.UUID.randomUUID()) to generate random number. In very high level, this java function generate strong pseudo random number using randomUUID() function from java util library. Here is the part of the SOAP request with Java function:

${=java.util.UUID.randomUUID()}
ABC


If I need to generate this random number using Java, that is easy. Here is 
the example:

import java.util.*;

public class UUIDGenerator {
    public static void main(String[] args) {
        // creating UUID
        UUID uid = UUID.fromString("12300000-afcd-11bd-b23e-10b96e4ef00d");
        // checking the value of random UUID
        System.out.println("Randomly generated UUID value: "+uid.randomUUID());
    }
}

Output would be:

Randomly generated UUID value: 69b9c547-2278-45d0-a632-b627e30119aa

But the problem was to convert this solution to LoadRunner. Thanks to Scotte 
Moore for coming up with the creative solution. Here are the steps:
•    Create a test using the protocols that generate C scripts.
•    Download the “ole32.dll” file from online (If you google it, you should be able to find it)
•    Save that “ole32.dll” file into the test folder so that lr_load_dll() function can load this dll
•    Finally, you can either create seperate functions to generate GUID/randomUUID or can enabled in the action that will use it.

Action() {
    lr_guid_gen();
    return 0;
}

int lr_guid_gen(){
    typedef struct _GUID{
        unsigned long Data1;
        unsigned short Data2;
        unsigned short Data3;
        unsigned char Data4[8];
    } 
    GUID;
    GUID m_guid;
    char buf[50];
    char bufx[50];
    lr_load_dll ("ole32.dll");
    CoCreateGuid(&m_guid);

    sprintf (buf, "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
    m_guid.Data1, m_guid.Data2, m_guid.Data3,
    m_guid.Data4[0], m_guid.Data4[1], m_guid.Data4[2], m_guid.Data4[3],
    m_guid.Data4[4], m_guid.Data4[5], m_guid.Data4[6], m_guid.Data4[7]);

    lr_save_string(buf, "lrGUID");
    lr_output_message(lr_eval_string(buf));
    return 0;
}
Output would be similar to following:

Action.c(54): {1FC2EFD0-3887-45D7-9432-5682802E6D82}

Common Problems in Flex Applications using LoadRunner(Flex Issues)

This article lists the common problems & solutions that performance engineers come across when testing flex applications.

Problem : Overlapped transmission error occurs when a flex script is run for the first time from controller. But the same script works fine in VuGen.

Error -27740: Overlapped transmission of request to “www.url.com” for URL “http://www.url.com/ProdApp/” failed: WSA_IO_PENDING.


Solution : The transmission of data to the server failed. It could be a network, router, or server problem. The word Overlapped refers to the way LoadRunner sends data in order to get a Web Page Breakdown. To resolve this problem, add the following statement to the beginning of the script to disable the breakdown of the “First Buffer” into server and network time.

web_set_sockets_option (“OVERLAPPED_SEND”, “0″);


Problem  : During script replay Vugen crashes due to mmdrv error. mmdrv has encountered a problem and needs to close. Additional details Error: mmdrv.exe caused an Microsoft C++ Exception in module kernel32.dll at 001B:7C81EB33, RaiseException () +0082 byte(s)

Solution : The cause of this issue is unknown. HP released a patch that can be downloaded from their site.

Problem : AMF error: Failed to find request and response

Solution
: LoadRunner web protocol has a mechanism to prevent large body requests to appear in the action files, by having the body in the lrw_custom_body.h. In AMF and Flex protocol, LR cannot handle these values and fails to generate the steps. Follow these steps to fix the problem:

1. Go to the “generation log”

2. Search for the highest value for “Content-Length”

3. Go to /config

4. Open vugen.ini

5. Add the following:

[WebRecorder]

BodySize=

6. Regenerate the script

Problem : There are duplicate AMF calls in the recording log as well as in the generated code.

Solution : Capture level may be set to Socket and WinInet, make sure under Recording Options –> Network –> Port Mapping –> Capture level is set to WinInet (only)

Problem : A Flex script which has Flex_AMF and Flex_RTMP calls, on replay will have mismatch in the tree view between the request and the response. After looking in the replay log we can see that correct calls are being made but they are being displayed incorrectly in the tree view (only the replay in tree view is incorrect). Sometimes it shows the previous or next Flex_AMF call in the tree view in place of the Flex_RTMP call.

Solution : This issue has been identified as a bug by R&D in LR 9.51 and LR 9.52. R&D issued a new flexreplay.dll which resolved the issue and will be included in the next Service Pack.

Problem  : Flex protocol script fails with “Error: Encoding of AMF message failed” or “Error: Decoding of AMF message failed”

Solution : The cause for this error is the presence of special characters (>, <, & etc…) in the flex request. Send the request enclosed in CDATA Example: XXXXXXX & CO. INC. in script to

Problem  : When creating a Multi Protocol Script that contains FLEX and WEB protocols sometimes VuGen closes automatically without any warning/error message displayed. This happens when the Web protocol is set to be in HTML Mode. When in URL mode the crash does not occur. There is no error code except a generic Windows message stating the VuGen needs to close.

Solution : This issue can be seen on Machines that are running on Windows XP, and using Mfc80.dll. Refer to Microsoft KB Article in the link below that provides a solution for the same. Microsoft released a hot fix for Windows specific issue that can cause VuGen to close.

http://support.microsoft.com/kb/961894

Problem  : When recording a FLEX script, RTMP calls are not being captured correctly so the corresponding FLEX_RTMP_Connect functions are not generated in the script.

Solution : First set the Port Mapping (Choose Recording options –> Network –> Port Mapping –> set Capture Level to ‘Socket level and WinINet level data’) set to ‘Socket level and if this doesn’t help, follow the next step. Record a FLEX + Winsock script. In Port mapping section, Set the Send-Receive buffer size threshold to 1500 under the options. Create a new entry and select Service ID as SOCKET, enter the Port (such as 2037 or whatever port the FLEX application is using for connection), Connection Type as Plain, Record Type as Proxy, and Target Server can be the default value(Any Server).

Problem  : Replaying a Flex script containing a flex_rtmp_send() that has an XML argument string may result in the mmdrv process crashing with a failure in a Microsoft DLL.

Solution : The VuGen script generation functionality does not handle the XML parameter string within the function correctly. This results in the mmdrv process crashing during replay. If you have the 9.51 version, installing a specific patch (flex9.51rup.zip) or service pack 2 will resolve the problem

Problem  : During the test executions in controller, sometimes the scripts throw an error ‘Decoding of AMF message failed. Error is: Externalizable parsing failed’.

Solution : This is mostly due to the file transfer problem. It is always advised to place the jar files in a share path common to all load agents.

Other Flex Supported Load Testing Tools:


There are other Commercial & Open Source tools available, that support the flex application testing. Some tools (For example, Neoload) have much considerable support for RTMP even when compared to LoadRunner. The way all these tools test the flex application is quite similar, each tool has its own AMF/XML conversion engine, which serializes the binary data to readable XML format

Open Source
* Data Services Stress Testing Framework

* JMeter

Commercial Tools


* Silk Performer by Borland

* NeoLoad by Neotys

* WebLOAD by RadView

Performance Improvement Recommendations

When it comes to performance improvement of an application, our first concern would be to enhance the scalability for a specified hardware & software configuration.

* In case of flex, the scalability issues derive from the fact that BlazeDS is deployed in a conventional Java Servlet container, and performance/scalability of BlazeDS also depends on the number of concurrent connections supported by the server such as Tomcat, WebSphere, Web Logic … BlazeDS runs in a servlet container, which maintains a thread pool.

* Each thread is assigned to client request and returns to a reusable pool after the request is processed. When a particular client request uses a thread for a longer duration, the thread is being locked by that corresponding client till the request is processed. So the number of the concurrent users in BlazeDS depends on the number of threads a particular servlet container can hold.

* While BlazeDS is preconfigured with just 10 simultaneous connections, it can be increased to several hundred, and the actual number depends on the server’s threading configuration, CPU and the size of its JVM heap memory. This number can also be affected by the number of messages processed by server in the unit of time and size of the messages.

* Tomcat or WebSphere can support upto several hundred of users, and any servlet container that supports Servlets 3.0, BlazeDS can be used in the more demanding applications that require support of thousands concurrent users.

Based on our project experience in performance testing Flex applications using LoadRunner we have pointed out some of the common problems that might arise during performance testing Flex applications. This will save you a lot of time as we have also provided the solutions to troubleshoot the errors if they occur.

How to upload files in Scripting Load Runner(Vugen)

How to upload any local or network files with Load runner scripting:

 There are several options that do the same work:

Running test locally :
Record script where you upload a local file. I suggest placing file in “C:\temp” before recording.
Run the test using same local machines. Test will pass since script and upload file both located in the same machine during recording and test execution.
Keep in mind to replace single backslash with double backslashes in the script. Here is the sample script:
web_submit_data("AddAttachment.aspx",
 "Action=https://learningshared.com/AddAttachment.aspx",
 "Method=POST",
 "EncType=multipart/form-data",
 "RecContentType=text/html",
 "Snapshot=t7.inf",
 "Mode=HTML",
 ITEMDATA,
     "Name=__VIEWSTATE", "Value={ViewState_Value_1}", ENDITEM,
     "Name=__EVENTVALIDATION", "Value={EVENTVALIDATION_1}", ENDITEM,
     "Name=FileUpload1", "Value=C:\\Temp\\my_uploaded_file.pdf", "File=yes", ENDITEM,
     "Name=Button1", "Value=Upload", ENDITEM,
 LAST);

Running test with any network load generator:This method is similar to the first method. Therefore, record the script the same way as above.
Create a network share location with read & write access. Place files those need to be uploaded.
Replace local file location with network file location. Make sure to replace single backslash with double backslashes in the script. Here is the sample script:
web_submit_data("AddAttachment.aspx",
 "Action=https://learningshared.com/AddAttachment.aspx",
 "Method=POST",
 "EncType=multipart/form-data",
 "RecContentType=text/html",
 "Snapshot=t7.inf",
 "Mode=HTML",
 ITEMDATA,
     "Name=__VIEWSTATE", "Value={ViewState_Value_1}", ENDITEM,
     "Name=__EVENTVALIDATION", "Value={EVENTVALIDATION_1}", ENDITEM,
     "Name=FileUpload1", "Value=\\\\shared_network_location\\my_uploaded_file.pdf", "File=yes", ENDITEM,
     "Name=Button1", "Value=Upload", ENDITEM,
 LAST);

 Attach upload file with scripts:
  • This method is similar to the first method. Record the script with a local file.
  • Add all the files (to be uploaded) to scripts by clicking “Add Files to Scripts…” menu from “Files -> Add Files to Scripts…”
  • Now clear file location and just leave the file name. These scripts should run from any load generators in the network or can be run from Performance center ALM too.
web_submit_data("AddAttachment.aspx",
 "Action=https://learningshared.com/AddAttachment.aspx",
 "Method=POST",
 "EncType=multipart/form-data",
 "RecContentType=text/html",
 "Snapshot=t7.inf",
 "Mode=HTML",
 ITEMDATA,
     "Name=__VIEWSTATE", "Value={ViewState_Value_1}", ENDITEM,
     "Name=__EVENTVALIDATION", "Value={EVENTVALIDATION_1}", ENDITEM,
     "Name=FileUpload1", "Value=my_uploaded_file.pdf", "File=yes", ENDITEM,
     "Name=Button1", "Value=Upload", ENDITEM,
 LAST);

Socket level data WinInet level data in load runner

There are situations where recording application using protocol Web (HTTP/HTML) encounters problem such as (1) not recording events, (2) not launching or (3) crashing. The context may deviate however the below list aims to provide a quick-and-dirty resolution to the problems. Obviously, the quick-and-dirty may work/not work in different environment and situations, however it will help you scope down the problem in a sequential and logical manner.

1. Use another machine to record

2. Use web_custom_request

3. Configure Port Mappings using “WinInetlevel data”  or “Socket level and WinInet level data”. And if necessary,

define the port and certs. 4. Use Older Recording Engine

5. Use LoadRunner as Proxy Server on port 7777 for  Recording (For Older Recording Engine)

6. Determine OS Support Status and application type

7. Determine the actual protocol and port number using

Winsock protocol or network sniffing tools (WireShark Network Analyzer)

Ok, let’s move to the details on the rationals and steps

Our Requirement is  to get your HTTP application record!

[1] Use another machine to record

Install VuGen on another machine and proceed with recording. What we are doing here, is to differ a problematic machine to a working one (at a machine-level troubleshooting). The existing machine may have problems due to a clone image, restricted privileges or previous installation of programs (unless you are pretty sure that your machine is fully workable with Vugen).

[2] Use web_custom_request

Use the basic web request methods for communicating to the server. This is needed when Vugen (sometimes) is unable to recognise the HTTP traffic and compose into web_submit_data APIs. Therefore, by instructing VuGen to record using web_custom_request only, this will detect all unknown communications been used in a HTTP mode to be recorded but in a more unreadable or unfriendly web_custom_request script.

1.1 Create a new Multi-Protocol Web (HTTP/HTML) script; do not create a Single Protocol script.

1.2 In the recording options, select recording to be URL- based script (not HTML mode) and select “Use web_custom_request only”. Proceed with the recording.

[3] Configure Port Mapping
Some applications may use ports that are different from the browser. The default HTTP port is 80 by most application but this may not be used by the application that you are recording against. In such situation, you may need to configure Vugento capture the HTTP traffic by mapping the ports to the service type. The service type other than HTTP are FTP, Socket, etc depending on your application needs. In this way, Vugen will become a proxy and capture traffic on the defined port.

1.1 In Recording Options ensure that you are already using web_custom_request only as stated in the previously.

1.2 Go to Port Mappings, remove all registered Entry and add New Entry with the known information (such as port number and target server). Note that there should not be any duplicate port numbers.

1.3 If 1.2 didn’t resolve the problem, try using “WinInet Level data” for the Capture Level and record the application.

1.4 If 1.3 didn’t resolve the problem, try using “Socket level and WinInet level data” for the Capture Level and record the application.

1.5 If the above didn’t resolve the problem, edit the port mapping entry; change the “Record Type” to “Direct” and to “Proxy” if you using SSL. If you know the ciphers used and type, you can configure it here.

1.6 For SSL, either client-side or server-side, refer to “Configuring the Port Mappings” of the Vugen User Guide for the detailed information. You will have to provide the certificates in either Base64 or PEM format. PEM format can be converted with a tool called openssl.exe availablie with the installer as well as online search.

[4] Use Older Recording Engine


This is to utilize the old recording engine prior version

8.1. The old version records at the proxy level whereas the new engine records on the process level.

[5] Use LoadRunner as Proxy Server for Recording (For Older Recording Engine)

If [4], didn’t work out right, you have to specifically configure LoadRunner as a proxy to record all communication via port 7777. Take note that this is in the old recording engine.

1.1 In Recording Options, under Browser, set the application to launch manually with “Manually launch an application”

1.2 Under Recording Proxy, select “No Proxy”.

1.3 On a separate machine (e.g. Machine A), set the Proxy to the LoadRunner machine with port 7777. (NOTE: Proxy server can be configured on the LR machine itself).

1.4 Start recording on LoadRunner machine.

1.5 Invoke the application on Machine A.

1.6 You should be seeing events counted on the LoadRunner machine. It means the target application accesses the LR machine and LR is recording it.

[6] Determine OS Support Status and application type

At this point, you have tried almost everything (and have come to theend of the road/solutions). My recommendation is to check out with Mercury/HP support, on the support level of the target application. Also check them out if they had encountered such problems before.

[7] Use Winsock protocol or network sniffing tools

Using WinSock protocol, you can determine the actual communication method by the client to the server. With this evidence, you can bring forward a discussion with your client on the unknown communication method. Another good tool to use is the WireShark Network Analyzer (formerly known as Ethereal Network Analyzer) to identify the communication and port being used.

Web_convert_param() function in Load Runner

I recorded a script against an Java application that ultimately sends a HTTP POST request to the server being tested with an XML in the Body of the request. The script came out fine, except that the body (XML content) was URL encoded. It would have been fine if I didn't need to parameterize the data in the body but since I had to, I realized in a few seconds that I would have to do some extra work to convert the data from parameter file to URL encoded format. The data in parameter file is of the form:

Fname,Mname,Lname,Gen,SSN,A1,A2,A3,A4,A5,City,State,Zip
John,,Smith,,123456789,123,Main,St,#,88,TestCity,TestState,987654321

However, in the script, address is one field that is a concatenated string of A1 - A5 parameters with a space between each of them.

Further Research:
A1 - A5 are the components that make up the street address that is supposed to be concatenated to one field in the script. No problem.
sprintf(as,"%s %s %s %s %s", lr_eval_string("{a1}"), 
lr_eval_string("{a2}"), lr_eval_string("{a3}"), 
lr_eval_string("{a4}"), lr_eval_string("{a5}"));
lr_save_string(as, "addressStreet");

…but the problem is there are spaces in between this components, which we all know get converted to a '+' character in URL encoding. Ok, so I could've just used sprintf(as,"%s+%s+%s+%s+%s",…
But some of the addresses also have '#' signs that get converted into '%23'. Now I had 2 options:
1. remove all the addresses that have a '#' sign
2. write a URLEncode(char *) function that does the obvious.

In the 1st option the addresses can have other characters that need to get URL encoded as well. And if a sizeable chunk of the data given to me has these characters, I could lose a lot of records. Also I didn't want to have to modify the data every time I get a new 10,000 record file.
2nd option seemed the way to go. I ran my tests by removing the records with '#' character.

Solution:

Pass the 'XMLSource' parameter as an input to the web_convert_param function, and store the result as 'TargetXML'
web_convert_param("TargetXML", "SourceString={XMLSource}", "SourceEncoding=HTML", "TargetEncoding=URL", LAST);

So this web_convert_param function seemed to do exactly what I needed. I tried it out with a couple of different strings and it did as promised. So my new script had:
sprintf(as,"%s %s %s %s %s", lr_eval_string("{a1}"), lr_eval_string("{a2}"), lr_eval_string("{a3}"), lr_eval_string("{a4}"), lr_eval_string("{a5}"));
lr_save_string(as, "addressStreet");

web_convert_param("encAddressStreet", "SourceString={addressStreet}", "SourceEncoding=PLAIN", "TargetEncoding=URL", LAST);
lr_output_message("%s", lr_eval_string("{encAddressStreet}"));

Creating web_custom_request for HTTP file uploads manually

LoadRunner captures any form based file uploads as web_submit_data with the file name (and location) within the list of data within the request. An example request:

web_submit_data("FileUpload",
"Action={URL}",
"Method=POST",
"EncType=multipart/form-data",
"TargetFrame=",
"RecContentType=text/html",
"Mode=HTML",
ITEMDATA,
"Name=File", "Value=C:\\testdata\\readme1.txt", "File=yes", ENDITEM,
LAST);

In this we may get 2 drawbacks
  • For this kind of script, the files to be uploaded have to be transferred to each of the Load Generator machines being used in the scenario. Or they have to be transferred to a shared network location so that both the machine used for creating the script and the load generator machines can reference it.
  • As mentioned in the forum post, any parameterization in the file contents is not possible. If the scenario requires files to be uploaded iteratively with unique names, that many files have to be created manually 
There are 2 solutions to this and one is more universally acceptable (by web servers) than the other. I'll describe both and let you decide which one you want to use. I also highly recommend reading RFC1867 which provides excellent background on how multi part file uploads are implemented in HTML form based submissions.

1. web_custom_request POST: To arrive at the solution, I did exactly what I usually do when I'm stuck with some HTML script or want to see the raw HTTP request being sent to any web server. I opened Webscarab to capture the HTTP request that was being sent for file uploads.

Here's a screenshot of the HTTP post request as captured by Webscarab:



And here's the relevant portions of the raw request:


Content-Type: multipart/form-data; boundary=---------------------------327572003712859
Content-length: 228

-----------------------------327572003712859
Content-Disposition: form-data; name="File"; filename="readme1.txt"
Content-Type: text/plain

testdata
readme
readme
123456789
-----------------------------327572003712859--

The Content-Type HTTP header is what specifies the submission to be a multipart data submission. The "boundary" is a string that doesn't occur in the file data and is used to mark the boundaries of the data being sent. Content-length, as the name implies is the length of the data that is being sent.

The body of the request starts with the boundary string and is followed by the 2 headers to specify the content-disposition which includes the name of the file to be uploaded. Content-type specifies the encoding of the data being submitted. Following that within the body is a blank line and the actual contents of the file that will be uploaded. After the contents is the boundary once again to signify end of the submission data.

So this kinda makes it easier to create a web_custom_request. The Content-Type HTTP header is specified by "EncType" attribute of the function. Content-length header can be ignored since web_custom_request itself generates it by calculating the length of the data at runtime. The rest goes in the "Body" attribute starting with the boundary. Content-Disposition specifies among other things the name of the file to be uploaded and this can be parameterized. Content-Type is the type of file being uploaded and can be text/plain, image/gif etc. And in the end is the boundary once again. The final request looks like this:


web_custom_request("{rndString}.txt",
"URL={testURL}",
"Method=POST",
"EncType=multipart/form-data; boundary=---------------------------17773322701763",
"TargetFrame=",
"Resource=1",
"RecContentType=application/octet-stream",
"Body=-----------------------------17773322701763\r\n"
"Content-Disposition: form-data; name=\"File\"; filename=\"{rndString}.txt\"\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"testdata\r\n"
"readme\r\n"
"readme\r\n"
"{rndString}\r\n"
"-----------------------------17773322701763--\r\n",
LAST);

I have parameterized the file name to be a random string so I can run this script iteratively without having to worry about server rejecting duplicate files. I can also parameterize the contents of the file within the body to whatever I like so that each file is unique or based on some other logic.

2. web_custom_request PUT: The second option is more straightforward but less universally accepted. I've read that not all web servers implement this. It involves using the HTTP PUT to create a web_custom_request. For more on different HTTP methods, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html. It mentions that:


The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource.

I've also read that PUT is more efficient so if your test site implements the uploads using POST, better do it that way instead. Nevertheless, here is the web_custom_request with PUT:

web_custom_request("{rndString}.txt",
"URL={URL}/{rndString}.txt",
"Method=PUT",
"Resource=1",
"RecContentType=application/octet-stream",
"Body=testdata\r\n"
"readme\r\n"
"readme\r\n"
"{rndString}\r\n",
LAST);

The resulting function is straightforward, URL attribute is the URI identifying the file to be uploaded and Body is the exact contents of the file.