Friday, November 12, 2010

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

No comments:

Post a Comment