Monday, June 22, 2009

12 Balls Solution

Here’s the solution to the 12 Balls puzzle that I mentioned a few days (gosh…a few weeks now) ago . To state it again:

You have 12 identical-looking balls. One of these balls has a different weight from all the others. You also have a two-pan balance for comparing weights. Using the balance in the smallest number of times possible, determine which ball has the unique weight, and also determine whether it is heavier or lighter than the others.

The third branch following the 1st decision is left blank because it is very similar to the 1st one. The larger image file is available here.

12 Balls Solution

Friday, June 19, 2009

Closed for Cleaning!

I wish somebody would pay me to create an application that would detect that the work restrooms are closed for cleaning and update all employees automatically. We probably have 2 times during the day when they are being cleaned and somehow my calls are synchronized with that time.

Here’s how that application would work at a distant high-level:

1. Have a camera pointed at the restroom door that monitors the door for appearance of a yellow sign put between the door edges
-or-
(slightly cheaper) Install an infra-red monitor that is flipped whenever the yellow sign is put between the door edges blocking the monitor
-or-
(even cheaper and less automated) have the janitor flip a switch whenever they start the process.

2. The trigger chosen above should send a message to Microsoft Office Communicator server (or any other instant messaging application) where a resource for each of the restrooms is setup (just like the meeting rooms). That should turn the status of the bathroom to “Busy” or “Unavailable”.

This way, each interested person (with synchronized bladders) can add the restrooms to their contact list and find out if they are available without getting up.

Thursday, May 28, 2009

12 Balls

Wednesday morning, out of sheer impulse I navigated to Willy Wu’s riddles site consisting of the best puzzle compilation on internet (I added the site to the links section as well). The puzzle that was staring at me was the 12 Balls one:

You have 12 identical-looking balls. One of these balls has a different weight from all the others. You also have a two-pan balance for comparing weights. Using the balance in the smallest number of times possible, determine which ball has the unique weight, and also determine whether it is heavier or lighter than the others.

I figured the solution once I was able to find time to put some random thoughts to this puzzle. I’m currently working on creating a visio diagram to display the solution but if you’re not familiar with the puzzle and feel challenged enough to attempt a stab at it without searching online for a solution, feel free.

If after putting some time into it, you’re feeling frustrated and just want the solution, go ahead and google. If you just want some hint(s) and not the complete solution, let me know and I’ll post it before I post the solution.

Wednesday, May 27, 2009

Canon EOS 5D Mark II

Link to a video shot entirely with 5D, a DSLR that supports capturing full HD movies: http://www.usa.canon.com/dlc/controller?act=GetArticleAct&articleID=2326

Really impressive.

Friday, January 30, 2009

QuickTest Pro: Handling XMLs using XMLDOM object

This is how diverse my writings are going to be. Testing tools, that’s it. I could write about my adventures in photography but that wouldn’t be very interesting until I put some more time and money into it. Even though I’ve been using my 50mm f/1.8 lens with some interesting results, I still feel that there’s lots more I need to learn before I can write about it …maybe someday soon. But this one is about a different, though as interesting topic - handling XML objects in QTP.

Background:

I’m currently involved in test automation for IDM application. If you are familiar with Sun’s IDM product, you know that all objects are stored in database as XML and these objects can be viewed/checked out/edited through the debug interface. Now the way we have customized it is that managers (users with Manager role) or end users are allowed to submit requests for creating users or modifying users’ attributes. The requests are submitted through the user interface and the workflows take care of taking appropriate actions in the background. There is very little information displayed on the web page once the request is submitted that can be used to validate if the action actually succeeded or not. The only way to validate that the action was successful is to either login through admin interface and check the task results or through debug pages, pull up the user’s XML object and verify the changes have gone through.

As I was creating the BPT components and test cases to handle different test scenarios, it was increasingly obvious to me that the most foolproof way to validate the results of the test would be to create a component that validates the user’s XML. I wasn’t much familiar with handling XML objects in VBScript so I left it for later. And later is now (or was yesterday). So I spent yesterday going through the XML Document Object Model (DOM) and Microsoft’s implementation of it and creating a VBScript function to validate an XML’s attribute value. It worked out fine with a little initial struggle.

Please keep in mind that what I’m providing below is in no way a complete solution of using XMLDOM to do whatever you want to do. All I needed to do was to retrieve an attribute value from an XML using the XPath that is provided as input parameter and to compare it with the expected value. What you want do with XMLDOM may be different and based on your application’s and automation needs. The reason I’m posting this is that if you’re using XMLDOM with QTP/VBScript for the first time, you can avoid the initial struggle that I went through and get the job done faster.

For Microsoft DOM reference (this is where I got most of the information I needed), visit this: http://msdn.microsoft.com/en-us/library/ms764730(VS.85).aspx. The XPath language is described at: http://www.w3.org/TR/xpath

Solution:

1. The first step is to create a parser object and load the XML. The XML can be from a file or from a string. Here I’m using the loadXML() method to load it from the innertext property of a WebElement object that I retrieved earlier. To use a file, use the load() method instead.

Set xmlDoc = CreateObject("Microsoft.XMLDOM")
xmlDoc.async = false
xmlDoc.loadXML(objs(0).GetROProperty("innertext"))




You set the async property to false so that it doesn’t move on before the document is completely loaded. The reason I’m using CreateObject(“Microsoft.XMLDOM”) instead of XMLUtil.CreateXML() which is provided in QTP OM Reference is that when I used that, it gave me an error specifying that it couldn’t find the DTD file referenced in the XML. It seems that it needs the DTD if it’s mentioned in XML to be able to load it. I had the DTD and when I uploaded it to my local component folder, it worked fine. But since I could be running the test on a remote host, I didn’t want to have to upload that DTD to all my host machines. And I didn’t look into any other way. But if you decided to use the XMLData object provided by QTP, the objects and method names are different even though the overall steps will be the same. For example, to load the document, you’ll use:




Set xmlDoc = XMLUtil.CreateXML()
xmlDoc.load(objs(0).GetROProperty("innertext"))




2. Once you have the XML object, you need to get to the desired element. I kept it simple and asked for the XPath to the desired element as an input parameter. Once I have that, I used the selectNodes() method to get all the nodes in the desired XPath.




Set objNodes = xmlDoc.selectNodes(nodeXPath)




This gives me a collection of all the nodes (specifically, the IXMLDOMNodeList object) that match the specified parameter ‘nodeXPath’. With QTP XMLData, you can use:




Set objNodes = xmlDoc.ChildElementsByPath(nodeXPath)




If nodeXPath is invalid or doesn’t match any elements, the length of the collection will be 0.



3. So now, I need to check the length of the collection and get to the actual element that I want. Since I didn’t need to care if multiple elements match the XPath, I just took the first element in the collection. The item property returns a single node (IXMLDOMNode) from the collection specified by the index, in this case 0.




If objNodes.length > 0 Then
Set objNode = objNodes.item(0)




4. Now that I have the actual element, I need to get the value of specified attribute. The getAttribute(name) method returns the value of the ‘name’ attribute. The attribute name and its expected value are passed as input parameters:




If StrComp(attrValue, objNode.getAttribute(attrName), vbTextCompare) = 0 Then
'expected value matches actual value




attrValue is the input parameter containing the expected value and attrName is the attribute name. If it matches, I write a Pass in the results and if not, it’s a fail. Once done, I clean up the objects.




Set xmlDoc = Nothing
Set objNodes = Nothing
Set objNode = Nothing




That’s pretty much it. Once we start using the component actively, I may come up with some more ideas to improve it. I’ll write about it if they provide me enough of a challenge.



----------------------------------------------------------------------------------



Sample XML:




<User name="abcdefg" creator="Configurator" email="a.a@x.com" disabled="false" locked="false">
<Roles>
<ObjectRef name="Employee" isWeak="false"/>
</Roles>
<Attribute name="employmenttype" type="string" value="Employee" syntax="string"/>
<Attribute name="firstname" type="string" value="a" syntax="string"/>
<Attribute name="fullname" type="string" value="a, a" syntax="string"/>
<Attribute name="ismanager" type="string" value="true" syntax="string"/>
<Attribute name="lastname" type="string" value="a" syntax="string"/>
<Attribute name="middlename" type="string" value="M" syntax="string"/>
<Attribute name="phone" type="string" value="9999999999" syntax="string"/>
<AdminRoles>
<ObjectRef type="AdminRole" name="Manager" isWeak="false"/>
</AdminRoles>
</User>



For example, the XPath to get to <Attribute> with name=employmenttype is: User/Attribute[@name='employmenttype’]

Wednesday, October 8, 2008

LoadRunner: Creating manual web_custom_request for HTTP file uploads

There was a recent forum post in Advanced LoadRunner yahoo group asking how LoadRunner captures the file upload requests and if there was a way to customize that request. At that time, I replied that it may be possible to generate a manual web_custom_request by using the underlying raw HTTP request. I hadn't tried it then but it sounded interesting enough to be marked as to-do. I recently got some time to try it and it was fun.

Problem:

As mentioned in the forum post, 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);

There are 2 obvious drawbacks to this:

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

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

Solution:

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 multipart 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. (Webscarab related post here).

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

WebscarabFileUploadCapture  

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.

Friday, August 8, 2008

Of Code Reviewers and Food Critics

For last 2-3 months, I was involved in testing our in-house customizations of Sun's IdM product. My responsibilities included not only leading the QA effort for the customizations but also to support the development effort by reviewing and suggesting improvements to the code/design.

So engrossed in my newfound glory as a unit tester/code reviewer, I realized that it may be easy for me to look at the code and find out a problem with it. But if I had to write something like that myself, I'd end up spending much more of my mind and time than it usually would take an average programmer. That realization reminded me of something I had recently heard in a movie.

The movie was "Ratatouille" and the scene was when Anton Ego starts writing his critique after visiting Gustaeu's Restaurant and having a meal cooked by the new but unknown chef Remy:

In many ways, the work of a critic is easy. We risk very little yet enjoy a position over those who offer up their work and their selves to our judgment. We thrive on negative criticism, which is fun to write and to read. But the bitter truth we critics must face, is that in the grand scheme of things, the average piece of junk is more meaningful than our criticism designating it so.

The parallels between a food critic as described above and what I was doing seemed surrealistically close, so much that I had to finish this post (after realizing it had been lying in my drafts for a few weeks). In some ways, that was exactly what I was doing. I enjoyed a position over those who wrote the code because finding out a problem with a piece of code drew much more attention than the original act of writing it. I thrived on negative criticism because it would get immediate credit, instead of the work of a developer which was the reason my role existed in the first place.

There are some fundamental differences as well. One being that Anton Ego, as a food critic does not have to care about the success of the Restaurant he is reviewing. This affords him the luxury of being overtly aggressive and have a dismissive attitude. My role on the other hand (both as a tester and code reviewer), is as responsible for the success of the product as any other stakeholder. That is the reason I'm as much pained when I find an issue in some code that I previously have reviewed and may have overlooked as the developer with whom I have to revisit the information once again. But the basic responsibility of both is still the same: to challenge the creator of an artifact to produce a better product which aligns with or exceeds the expectations of the users/consumers.

----------------------------------------------------------------------------------

The project is nearing its completion now and in hindsight, this is what I have to say:

This was a dream project for me. The kind of project that comes like once in a few years and offers amazing challenges with tremendous learning opportunities. Right kind of people, right kind of responsibilities and right kind of control over what I wanted to do. As much as I enjoyed working every moment in this project, I have to realize that in my enthusiasm, I may have stepped over some boundaries. I may have offered some unwarranted advices that were probably irrelevant or I may have criticized something to hide my lack of competence. I will take full responsibility for all the misgivings I caused, in however form I may deserve. But I do hope it will be realized that most of whatever I did was aimed at the common cause of project's success, and the fact that I enjoyed every bit of it and tried to make it enjoyable for everyone else should make it all the more worthwhile.