How to get unique file name in LoadRunner?

In my previous LoadRunner VIDEO post, I shown and explained how to record PDF file saving in LoadRunner.
The provided code works correctly in LoadRunner VuGen or in LoadRunner Controller for one concurrent user. Here it is:
//Truncate to zero length or create file for writing.
fp = fopen("c:\\temp\\_file.pdf","wb");

//Process the data ...

//Write the data to an output file.
fwrite(bufData, nSize, 1, fp);
If you try to run this code with several concurrent users, all of them will try to create the same file and write to it. As a result, this will led to the error.

How to resolve this problem?
Each user has to get unique file name and create a file with the unique name .


Now, I'm going to show - how to generate unique file name in LoadRunner.

The first step is to get a timestamp as a part of file name.
The timestamp consists of:
  • date (month, day, year)
  • time (hours, minutes, seconds, milliseconds)
You can use the following simple code to get a full timestamp without milliseconds:
lr_save_datetime("%m%d%Y_%H%M%S", DATE_NOW, "prmTimeStamp");
lr_output_message(lr_eval_string("Timestamp is: {prmTimeStamp}"));
The result of this code is:
(click the image to enlarge it)
Do you know what does the string "%m%d%Y_%H%M%S" mean?
These are Datetime Format Codes. Please, refer to a table for their descriptions:
#
Datetime Format Code
Description
1
%m
month number (01-12)
2
%d
day of month (01-31)
3
%Y
year, including century (for example, 1988)
4
%H
hour (00-23)
5
%M
minute (00-59)
6
%S
seconds (00-59)

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

That's why I can propose the code, which get a timestamp with milliseconds:
ent timestamp:
typedef long time_t;

struct _timeb {
    time_t time;
    unsigned short millitm;
    short timezone;
    short dstflag;
};

struct tm {
    int tm_sec; // seconds after the minute - [0,59]
    int tm_min; // minutes after the hour - [0,59]
    int tm_hour; // hours since midnight - [0,23]
    int tm_mday; // day of the month - [1,31]
    int tm_mon; // months since January - [0,11]
    int tm_year; // years since 1900
    int tm_wday; // days since Sunday - [0,6]
    int tm_yday; // days since January 1 - [0,365]
    int tm_isdst; // daylight savings time flag
    #ifdef LINUX
    int tm_gmtoff;
    const char * tm_zone;
    #endif
};

struct _timeb tb;
struct tm * now;
char szFileName[256];

_tzset(); // Sets variables used by localtime
_ftime(&tb); // Gets the current timestamp

// Convert to time structure
now = (struct tm *)localtime(&tb.time);

sprintf(szFileName, "%02d%02d%04d_%02d%02d%02d_%03u",
    now->tm_mon + 1,
    now->tm_mday,
    now->tm_year + 1900,
    now->tm_hour,
    now->tm_min,
    now->tm_sec,
    tb.millitm);
lr_output_message("Timestamp is: %s", szFileName);
Let's execute this code and check the result:
(click the image to enlarge it)
That's much better :) We can generate timestamp with millisecond.
Unfortunately, the chances that concurrent users will generate the same file name at the same time still exist.

To resolve this issue, we can get unique ids per virtual users. This can be done with lr_whoami LoadRunner function:
int vuserid, scid;
char *groupid;

lr_whoami(&vuserid, &groupid, &scid);
lr_message("Group: %s, vuser id: %d, scenario id %d", groupid, vuserid, scid);
lr_whoami LoadRunner function returns information about the Vuser executing the script.

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

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

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

So, the final code is:
typedef long time_t;

struct _timeb {
    time_t time;
    unsigned short millitm;
    short timezone;
    short dstflag;
};

struct tm {
    int tm_sec; // seconds after the minute - [0,59]
    int tm_min; // minutes after the hour - [0,59]
    int tm_hour; // hours since midnight - [0,23]
    int tm_mday; // day of the month - [1,31]
    int tm_mon; // months since January - [0,11]
    int tm_year; // years since 1900
    int tm_wday; // days since Sunday - [0,6]
    int tm_yday; // days since January 1 - [0,365]
    int tm_isdst; // daylight savings time flag
    #ifdef LINUX
    int tm_gmtoff;
    const char * tm_zone;
    #endif
};

struct _timeb tb;
struct tm * now;
char szFileName[256];

int vuserid, scid;
char *vusergroup;

_tzset(); // Sets variables used by localtime
_ftime(&tb); // Gets the current timestamp

// Convert to time structure
now = (struct tm *)localtime(&tb.time);

sprintf(szFileName, "%02d%02d%04d_%02d%02d%02d_%03u_%d_%d_%s",
    now->tm_mon + 1,
    now->tm_mday,
    now->tm_year + 1900,
    now->tm_hour,
    now->tm_min,
    now->tm_sec,
    tb.millitm,
    vuserid,
    scid,
    vusergroup);

lr_output_message
("File name is: %s", szFileName);
Note: You can download this source code from here.

The result of above code is:
Using the generated name (05312008_221557_116_7_0_group1) you can save the file into "05312008_221557_116_7_0_group1.pdf" or "05312008_221557_116_7_0_group1.txt" depending on the file type.


Related articles:


I hope, this solution will help you in LoadRunner scripting.
Thank you for your attention, dear reader.

--
Dmitry

QTP Descriptive programming - processing images

Imagine a practical situation with QuickTest Professional (QTP).
There are several images on a web page. You need to:
  • get the number of all images on the page
  • process each image (for example, get its location)
Do you know how to do that? If not, do not sorrow :) I'm going to show and explain how to do that.
Also, I'm going to explain QTP Descriptive Programming concepts.

Let's start with Google Book Search page:

I have recorded simple QTP script which:
  1. uses http://books.google.com as initial URL
  2. clicks 'Google Book Search' image:
So, the recorded QTP script is:
Browser("Google Book Search").Page("Google Book Search").Image("Google Book Search").Click

Why have I recorded this script?
Because it will help us to investigate QTP Object Repository (OR) and properties of image.

Let's open Object Repository:
And then we check image's properties saved in Object Repository:
(click the image to enlarge it)

Using "html tag" property and its value "IMG", we can create general description for all images on the page. For that I use QTP Description object.
Description object is used to create a 'Properties' collection object. Each 'Property' object contains a property name and value pair.
Set descImage = Description.Create
descImage("html tag").value = "IMG"
The above code:
  1. creates Description object
  2. creates "html tag" property and sets its value to "IMG"

You can use the following code to get number of all images on a Web page:
Dim descImage, listImages

' Create description for all images on a Web page.
' For that we use "html tag" property and its value "IMG"
Set descImage = Description.Create
descImage("html tag").value = "IMG"

' Get all images which match the above description
Set listImages = Browser("Google Book Search").Page("Google Book Search").ChildObjects(descImage)

' Show the number of found images
MsgBox "Found images: " & listImages.Count

Execute the above code and you will get a result like:


How does Description object work?

Actually, QTP Description object is a part of QTP Descriptive Programming.
QTP Descriptive Programming (DP) is a way of working with objects without Object Repository (OR).

In our example, we couldn't know all images on a Web page in advance. So, we couldn't add them into QTP
Object Repository. Instead of that, we created Description object with required property and its value.

Tips:
We can use several properties assigned to Description object. For example:
Set descImage = Description.Create
descImage("html tag").value = "IMG"
descImage("image type").value = "Image Link"

When we pass Description object to ChildObjects QTP function, it returns the collection of child objects contained within the object (Page("Google Book Search")) and matched to Description object.
That's why we've got the list of all images on a page.


How to get additional properties of found images?

As you remember, we planned to process each image (get its location).
Let's open OR and check the additional recorded properties:
(click the image to enlarge it)

There are "src" property, which contains the URL address of the image file.
To get this property, we can use GetROProperty QTP function.

The final code is:
Dim descImage, listImages

' Create description for all images on a Web page.
' For that we use "html tag" property and its value "IMG"
Set descImage = Description.Create
descImage("html tag").value = "IMG"

' Get all images which match the above description
Set listImages = Browser("Google Book Search").Page("Google Book Search").ChildObjects(descImage)

' Show the number of found images
MsgBox "Found images: " & listImages.Count

' Show location ("src" property) of each image
For i = 0 To listImages.Count - 1
    MsgBox
"Image #:" & (i+1) & ": " & listImages(i).GetROProperty("src")
Next


This code shows a location of each image from a Web page.
There is a message box with a location of first image:
Then the second image:
and so on for others images...

Tips: Use the table of the most popular properties of Image QTP control:
#
Property name
Description
1
altThe object's tooltip text
2
href
The location to which the browser navigates when the image is clicked
3
nameThe object's name
4
srcThe image source, i.e., the URL address of the image file
5
visible
Indicates whether the object is visible.
Note: Only True and False are valid values for this property. 1 and 0 are not valid values.
Please, refer Quicktest Professional Help for detailed info on image identification properties .

Tips: You can use the same approach to work with others UI controls - Links, WebEdit, WebList, etc.


Related articles:


Dear readers,
Do you have interesting thoughts/articles/notes on automated testing?
Please, feel free to send them. Join our team!


--
Dmitry Motevich

Automated testing without automated testing tools

How many automated testing tools exist in the world?
Hm... I think, hundreds - Selenium, QuickTest Professional, SilkTest, Jmeter, LoadRunner, STAF, Watir, Canoo WebTest, and so on...

Is is possible to perform an automated testing without automated testing tools? Sure!

I will show how to perform automated testing of web applications on Internet Explorer (IE) browser.
The main advantage is that these tests can be run on any Windows computer without any additional sofware required.

Let's see steps of sample test:
  1. Open IE and navigate to google.com:
    Navigation bar
  2. Set value of edit box to 'Easy way to automate testing':
    Filled edit box
  3. Click 'Google Search' button:
    Click 'Google Search' button
  4. If a search results page contains a link to this blog (http://motevich.blogspot.com) then click this link:
  5. Last step is to close the IE window
I've automated this test with an instance of the InternetExplorer object.
Details info about InternetExplorer object is located here.

I hope, the source code of my test is clean and understandable. Here it is:
Option Explicit
Dim objIE, objLink
Set objIE = OpenBrowser("http://google.com")

' View the HTML source on Google's page to see the 'q' and 'btnG' values
objIE.Document.All("q").Value = "Easy way to automate testing"
objIE.Document.All("btnG").Click
WaitForLoad(objIE)

' Find a link to http://motevich.blogspot.com
Set objLink = GetLinkByHref(objIE, "motevich.blogspot.com")

' If found, then click it
If (False = IsNull(objLink)) Then
    objLink.Click
    WaitForLoad(objIE)
End If

' Close IE window
objIE.Quit
WScript.StdOut.Write("Script completed successfully...")


''''''''''''''''''''''''''''''''''''''''''''''
' Functions 

' Opens IE and navigates to specified URL
Private Function OpenBrowser(URL)
    Dim ie
    Set ie = CreateObject("InternetExplorer.Application")

    ie.Visible = True
    ie.Navigate2 URL
    WaitForLoad(ie)
    
    Set OpenBrowser = ie
End Function

' Waits for page fully loaded
Private Sub WaitForLoad(ie)
    Const WAIT_TIMEOUT = 100

    While (ie.Busy) or (ie.ReadyState <> 4' READYSTATE_COMPLETE = 4
        WScript.Sleep(WAIT_TIMEOUT)
    Wend
End Sub

' Gets Link by 'href' attribute.
' Note: If your need, you can write another function - GetLinkByText
Private Function GetLinkByHref(ie, href)
    Dim Link
    
    For Each Link In ie.Document.Links
        If Instr(LCase(Link.href), LCase(href)) > 0 Then
            Set GetLinkByHref = Link
            Exit Function
        End If
    Next

    Set GetLinkByHref = Null
End Function


How to run this file?
  1. Save this code to file, for example to ieauto.vbs.
  2. To execute this file, run from command line: cscript ieauto.vbs
    or paste this command to bat-file:
    and run the bat-file from command line.
    Note: You can download archived sample files from here.

The result of execution is:
Test runs and performs all steps correctly (it opens IE, fills values, clicks button, clicks link, closes IE).

Conclusion:
You can create and use instances of the InternetExplorer object to perform and to test all actions, which you run manually.
Or to do some routine operations in a browser, for example - filling forms, creating test users, and so on.

--
Dmitry Motevich

QTP - How to capture tool tip of link?

My friend asked me - How to capture a tool tip text in QTP?

For example, how to get tool tip ('Go to My Yahoo!') from the yahoo page:
Tooltip on Yahoo page
This QTP tutorial shows and explains How to get tool tip in QuickTest Professional.

Actually, this is not a difficult task. The steps are:
  1. Place mouse cursor over the link
  2. Wait for tool tip
  3. Get text of shown tool tip

This QTP script captures a text of a tool tip:
' Place mouse cursor over the link
Browser("Yahoo!").Page("Yahoo!").WebElement("text:=My Yahoo!").FireEvent "onmouseover"
wait 1
' Grab tooltip
ToolTip = Window("nativeclass:=tooltips_class32").GetROProperty("text")

Please, pay attention on details:
  1. We use FireEvent("onmouseover") to simulate mouse placing over the link
  2. Function wait(1) waits 1 second for a tool tip
  3. To get tool tip text, we use:
    Window("nativeclass:=tooltips_class32").GetROProperty("text")

Let's run our code and compare captured tool tip with expected ('Go to My Yahoo!'):
As you can see, above QTP script captures correct text of a Tool tip.
I hope, you will add this script to your QTP functions library.


Related articles:


Dear readers,
Do you have comments or questions on this topic? If yes, please post them.

It's very important to know your opinions...
They allow to create more useful, interesting, and informative blog. This is my primary goal.


--
Dmitry

Examples on LoadRunner Regular Expressions

I'm going to show and explain how to use Regular Expressions in LoadRunner.

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


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

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


I thanks Charlie Weiblen and Tim Koopmans for the solution. I modified it slightly.
So, here it is:
  1. Download and unpack Binaries and Developer files for PCRE (Perl Compatible Regular Expressions).
    These and others files are available on Pcre for Windows page.

  2. Unzip downloaded archives into c:\pcre
    C:\pcre folder
  3. Сomment out the include for stdlib.h file in:
    • C:\pcre\include\pcre.h
    • C:\pcre\include\pcreposix.h
    Commented stdlib.h file
  4. In your LoadRunner script, add to globals.h:
    • #include "c:\\pcre\\include\\pcre.h"
    • #include "c:\\pcre\\include\\pcreposix.h"
    Edited globals.h file
  5. Add the match() function to vuser_init section:
    //////////////////////////////////////////////////////////////////////////
    /// 'match' function matches a 'pattern' against a given 'subject'
    /// It returns 1 for a match, or 0 for a non-match / error
    int match(const char *subject, const char *pattern)
    {
    int rc; // Returned code
    regex_t re; // Compiled regexp pattern

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

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

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

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

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

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

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

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

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

How to get a matched strings (RegExp patterns and subpatterns)

To get a matched (found) strings, we have to update our match() function.
That's why I created matchex() ('match' EXtended) function.
  1. Add the matchex() function to vuser_init section
    //////////////////////////////////////////////////////////////////////////
    /// 'matchex' (EXtended) function matches a 'pattern' against a given 'subject'
    /// It returns number of matches:
    /// 0 - for a non-match or error
    /// 1 and more - for successful matches
    int matchex(const char *subject, const char *pattern, int nmatch, regmatch_t *pmatch)
    {
    int rc; // Returned code
    regex_t re; // Compiled regexp pattern

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

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

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

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

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

    return rc;
    }

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


    What is an information about each matched substring?


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

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

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

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

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


    What is a subpattern in Regular Expression?

    It is a part of the RegExp pattern surrounded with parenthesis - "(" and ")".

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

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

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

Another example:

Let's practise with an example I mentioned early:
For example, we have to extract the name of month from these strings:
  • "abc 1st of May 2008xyz"
  • "abc 25th of February 2031"
  • etc
We can use the following pattern:
  • \d.+([A-Z]\w+)\s+\d{4}
The name of month will be matches by subpattern ([A-Z]\w+).

Please, see LoadRunner script, which captures and prints name of months:
Note: Pay attention that I use arr[1] to get info about substring.
As you remember, arr[0] contains info about the entire matched pattern, arr[1], arr[2], and so on contain info about matched subpattern.


Summary:
I've explained, shown and demonstrated how to use Regular Expressions (RegExp) in LoadRunner.
I hope, this knowledge will help you to create advanced LoadRunner scripts.



Related articles:


--
Thank you, my readers!
Dmitry Motevich