skip to main |
skip to sidebar
One thing I never knew: Parenthesis matter in QTP (and VBScript). And they surely make a difference. I discovered this during debugging a function call. There was different behaviour between these two function calls:
foo(bar)
call foo(bar)
After some research over the internet and in QTP self, I came to the following conclusions: Passing an argument to a function surrounded by parenthesis means: "Protect me" or in other words: treat me as byVal even if it is defined in the function as byRef.
Example:
sub samplesub (a, b) ' a and b are by default handled as byRef
a = a + b
end sub
And this is happening when we call samplesub:
x = 1
y = 2
z = 4
samplesub x, y
samplesub (z), y
msgbox x ' displays "3"
msgbox z ' displays "4" because z was passed as if it was passed byVal
The same applies when you call a function:
function samplefunc(c)
c = c^2-1
samplefunc = (c mod 2 = 1)
end function
q = 8
samplefunc q
msgbox q ' returns 63
' When you accidentally forgot to call:
p = 9
samplefunc(p)
msgbox p ' returns 9, because p is returned byVal
' With call:
r = 10
call samplefunc(r)
msgbox r ' returns 99, because r is returned byRef
' With call and argument protected:
s = 11
call samplefunc( (s) )
msgbox s ' returns 11, s is returned byVal
' And a last example of a function call with multiple argument with combined protection:
call multifunc( (IamProtected), IamUnprotected )
Rules in short:
A sub/function call with an argument in protected mode overrides a byRef in the function.
A sub/function call with an argument in unprotected mode is returned byRef by default unless it is overridden in the function by a byVal.
An literal or const is always returned byVal.
Syntax proposal:
OK, it is ugly, but if you use parenthesis because they are part of the call, you should use them with spaces between the first and last argument and no space between the function:
call f( a, b )
If you want to use arguments in protected mode, you should use no spaces between the parenthesis and the arguments, but do use them between the function/sub and the parenthesis belonging to the function/sub call:
f (a), (b)
or
call f( (a), (b) )
Getting the text form a standard tooltip in an HTML document with WinRunner took me some time to figure out. Actually it is one small statement that will do the trick, but finding out what statement kept me searching for a while.
win_get_info("{class: window, MSW_class:\"tooltips_class32\",displayed: 1}","text",text);
This will do it. Really.
And to wrap it into a function:
public function get_tooltip(object, out text)
{
auto hwnd, rc;
# First set the cursor position to the left corner of the screen. When you don't do this,
# a formerly captured tooltip can be hovering over the object, making it impossible
# for WinRunner to locate it.
rc = move_locator_abs(1, 1);
# Move the cursor to the object containing the tooltip
web_cursor_to_obj(object, 1, 1);
# Move the cursor back and forth to trigger the tooltip to pop up
move_locator_rel(0,1);
move_locator_rel(0,-1);
# Wait for the tooltip. This is time consuming, something to keep in mind when
# you want to use this function
wait(2);
# Check on visibility of the tooltip window, otherwise a standard tooltip does not exist for this object
if((rc = win_exists("{class: window, MSW_class: \"tooltips_class32\",displayed: 1}")) == E_OK)
# Capture the text
return win_get_info("{class: window, MSW_class: \"tooltips_class32\",displayed: 1}","text",text);
return rc;
}
Keep in mind, this only works with standard tooltips, recognizable on the title='[text]' attribute in the HTML tag (not always visible when CSS is used). When the tooltip is a customized tooltip created by some JavaScript, most probably it is created as a frame object and you'll have to capture the text of that frame object in stead.
Yesterday was the day of the TestNet Voorjaars Event. TestNet is the national Dutch organization for testers. There were some great presentations and off course the warm bath experience of seeing lots of old collegues. That is one advantage of being Dutch: The test community is not very large in numbers. TestNet has around 1200 members, while the test community is as large of 6000 professional testers.
The theme was TestTooling, so I fitted well. Actually, I was invited to do a presentation, which I did with Marc Koper, a collegue of mine.
If you are interested in the presentations (some real good ones included), you can find them at the TestNet Library Page. However, most of them are in Dutch.
If you are especially interested in our presentation, it is called "A tool with a fool is only a tool". It is an introduction in how you can set op test automation (Performance and Test execution) in a test organisation with lots of tips and tricks. Well, for the best tips and tricks, you should have been there, but later this month, I will put some of them on my blog.
Update: The abstract of our presentation
Right now, I am almost finishing Gödel, Escher, Bach written by Douglas R. Hofstadter. Well, I'm on page 614, so I still have around 200 pages to go, but for me it feels like almost finishing. And this inspired me to a little piece of code in VBScript:
Dim a, p
Dim YmPool(8)
Do
mIP = afNe(a+3)*2
NextElem = el
If YmPool((a+2)*3) = aDig Or p = 1 Then
InTest = a
LitNuod "nametag", "Gateman"
Do Until a = t
Set NineHt1 = ProgId
a = (3*(2+a))
Loop
MyFile = Me
Let xEn = 2*(3+a)
End If
a = PI Mod (8)
Loop
myMid p, amId
Do you see the catch?
My next challenge: Write an actual snippet in VBScript this way that makes sense and that is actually usable.
IBAN stands for International Bank Account Number and is the old new toy of the banking community. Also hot in Europe because of SEPA. IBAN should make life easier, and maybe it does. For IT guys, IBAN is just another standard. And despite IT guys like standards (that is why they have so many of them), IBAN is a standard designed by the banking people making things a little more complicated.
The things you want to do with IBAN is validate it or just calculate the checksum on your own. The formula for the checksum is not very complex, but has some twists in it. For example, when dealing with Alpha characters, the A is transposed to 10, the B to 11 etc. In the IT world, we would transpose A to 65, B to 66… The things you don't want is validate them exactly right for for every country on this little planet. Maybe they want it, but definitely, you don't. And if they want it, get yourself a new toy called SOAP and connect to it through a service.
After searching the internet, I discovered that code for IBAN validation through any Visual Basic language was rare. I gathered the snippets I found useful and created my own IBAN functions.
How it works is all in the comments in the code, keeping your scripts maintainable and documented if you want to use it:
' This code was created by Bas M. Dam and first published on
' http://automated-chaos.blogspot.com
' You can use and distribute this code freely, as long as you
' keep this commentblock intact.
' RETRIEVING THE CHECKSUM
' There are two methods to get the checksum. The first is the
' one used in automated processes where there is an iban prototype.
' the checksum is replaced by zeros:
' MsgBox getIBANchecksum("LC00BANK1234567890", empty) 'returns 86
' The other way is a more user fiendly appraoch if only the separate
' segments are known like bank code or clearing house:
' MsgBox getIBANchecksum("BANK1234567890", "LC") 'returns 86
' CREATE AN IBAN NUMBER
' This is implemented in the makeIBAN() function for your convenience
' Msgbox makeIBAN("LC", "BANK", empty, "1234567890")
' returns LC86BANK1234567890
' Or just the simple implementation:
' Msgbox makeIBAN("LCBANK1234567890", empty, empty, empty)
' returns LC86BANK1234567890
' CHECK AN IBAN NUMBER
' And finally, you want to check if something is IBAN. You can
' use the getIBANchecksum function for it. If the result is 97,
' then you have a real IBAN, when it returns -1, there is something
' wrong with the IBAN and if it returns another number, the checksum
' is not correct
' Msgbox getIBANchecksum("LC86BANK1234567890", empty) 'returns 97
' Msgbox getIBANchecksum("LC68BANK1234567890", empty) 'returns 18
' Msgbox getIBANchecksum("LC68BANK1234567891", empty) 'returns 88
' Msgbox getIBANchecksum("LC86BANK123456789%", empty) 'returns -1
' To do this the simple way, you can make use of the isIBAN() function
' that simply returns True or False:
' Msgbox isIBAN("LC86BANK1234567890") 'returns True
' Msgbox isIBAN("LC68BANK1234567890") 'returns False
' Msgbox isIBAN("LC86BANK123456789%") 'returns False
' SPECIAL CHARACTERS
' You can use typographical characters as stated in the skipChars string.
' For now, the following characters can be used: space.-_,/
' These characters are often used to make an IBAN more readible, but are
' not taken into the checksum calculation. between the landcode
' and checksum, never a typographical character can be used.
' Msgbox isIBAN("LC86 BANK 1234 5678 90") 'returns True
' Msgbox isIBAN("LC86BANK1234.56.78.90") 'returns True
' Msgbox isIBAN("LC-86-BANK-1234-567890") 'returns False, there can not
'be a separation char between
'landcode and checksum.
' Msgbox isIBAN("LC*86*BANK*1234*567890") 'returns False, * is not a special char
' Function to check on an IBAN
Public Function isIBAN(sIban)
isIBAN = (getIBANchecksum(sIban, empty) = 97)
End Function
' Function to create an IBAN. Any of the arguments can be empty, as
' long as the first not empty argument starts with the landcode
Public function makeIBAN(landcode, bankcode, sortcode, accountnr)
dim realLandcode, sPurged
sPurged = mid(landcode & bankcode & sortcode & accountnr, 3)
realLandcode = left(landcode & bankcode & sortcode & accountnr, 2)
makeIBAN = realLandcode & getIBANchecksum(sPurged, realLandcode) & sPurged
End Function
' Function to get an IBAN checksum. Landcode can be empty, but then, the landcode
' must be included in the first two characters of sIban, followed by two zero's
Public Function getIBANchecksum(sIban, landcode)
Dim sLCCS 'Land Code and Check Sum
Dim sIbanMixed
Dim sIbanDigits
Dim char
Dim i
Dim skipChars
skipChars = " .-_,/"
' Marginal length check
If Len(sIban) < 5 Or Len(sIban) > 35 Then
getIBANchecksum = -1
Exit Function
End If
If landcode = empty Then
sLCCS = Left(sIban, 4) ' Extract land code and check sum
sIbanMixed = Right(sIban, Len(sIban) - 4) & UCase(sLCCS)
else
sLCCS = landcode & "00"
sIbanMixed = sIban & UCase(sLCCS)
End If
For i = 1 To Len(sIbanMixed)
char = Mid(sIbanMixed, i, 1)
'Check on digits
If IsNumeric(char) Then
sIbanDigits = sIbanDigits & char
'Check on typographical characters
elseif instr(skipChars, char) Then
'skip this character, but continue
'Check on non-uppercase other characters
elseif Asc(char) < 65 OR Asc(char) > 90 then
getIBANchecksum = -1
Exit function
'Transform characters to digits
else
sIbanDigits = sIbanDigits & (Asc(char) - 55)
End If
Next
getIBANchecksum = 98 - largeModulus(sIbanDigits, 97)
End Function
' Calculates the modulus of large integers that are actually
' strings. Also usefull for implementation in Excel VBA
' (there is a known bug in Excel and large number modulus)
Private Function largeModulus(sNumber, modulus)
Dim i, sRebuild(), j, r
j = 0
sNumber = cStr(sNumber)
For i = 1 To Len(sNumber) + 6 Step 6
ReDim Preserve sRebuild(j)
sRebuild(j) = Mid(sNumber, i, 6)
j = j + 1
Next
r = sRebuild(0) Mod modulus
For i = 0 To UBound(sRebuild) - 1
r = (r & sRebuild(i + 1)) Mod modulus
Next
largeModulus = r
End Function
The knowledge about the IBAN validation and some code tricks I retrieved from the internet, so it is my turn to to give it back to the community. The functions are also useful in Excel VBA, but not extensively tested. The isIBAN() function is great to use it in your spreadsheet itself, or use it as conditional formatting:

DONG, Housekeeping message: If you have questions, additional information, an opinion or just something to share, you are very welcome to leave it in the comments. I like to get personal mail too, but as mentioned on coding horrors: on a blog, the comments are the best part. And since I just started, I still need some!
In addition to a question I received through email: The class to create a queue on the linked list I published Monday, and how to perform a count on this queue.
Public function queue()
set queue = new clsQueue
End Function
Class clsQueue
Private ll
Private Sub Class_Initialize ' Setup Initialize event.
set ll = linkedList ' Create a linked list instance
End Sub
Public function pop()
If ll.count = 0 Then exit function ' No items in the list
pop = ll.getfirst() ' Return the last item
ll.deleteFirst ' and delete it
End Function
Public sub push(element)
ll.add element ' Add an item to the list
End sub
Public function peek()
peek = ll.getfirst() ' Peek at the top item
End Function
Public property get count()
'return the amount of nodes
count = ll.count
End Property
End Class
And this is the part where inheritance and polymorphism is missing. The class looks kind of the same as the stack class, but .getlast() is replaced by .getfirst(). I wish everything in life was this simple… The count property is added, so you can now count the items in the queue. If you want to add a count property to the stack, just do the same in the clsStack.