September 15, 2006

Running UI Automation Routines Concurrently

Filed under: Automation — Marcus Tettmar @ 11:55 am

Macro Scheduler is able to automate other applications by simulating what a user does. There are some exceptions – e.g. it is possible to automate Internet Explorer and Microsoft Office applications through their COM interfaces. But for the vast majority of applications the only way to automate them is by simulating user input and manipulating their UI (User Interface) just as an ordinary user does. Therefore Macro Scheduler has to simulate a human to do what it does best.

Two people would have a hard time trying to use the same PC at the same time. Imagine Fred and Sally sitting in front of the same computer, both trying to use the keyboard at once. Fred is trying to write a letter in Word and Sally is writing an email in Outlook. At the same time. Can you imagine what would happen? Utter chaos is what would happen. Fred opens Word and starts typing. Sally grabs the mouse and opens Outlook. In an instant focus has switched away from Word and Fred’s keystrokes land elsewhere. Outlook hasn’t finished opening yet and for a brief while the Desktop was the active window. Fred’s keystrokes therefore landed on some icons and started opening other applications randomly. Nightmare. Miraculously they manage to sort that out and now Sally has Outlook open as the active window and starts typing. Fred quickly hits ALT-TAB to bring Word back and types some more of his letter. But Sally is still hitting keys on the keyboard so parts of her email end up in Fred’s letter. I think you get the picture. It’s more like a game of “Tug Of War”.

The only way two people could effectively use the same computer is to take turns. Either Fred writes his letter and then tells Sally she can write her email, or, if they want to do bits at a time it might work like this:

Fred: “I’m typing a letter in Word, please don’t touch the keyboard”
Sally: “OK, I’ll wait”
Time Passes
Fred: “OK, I’m done writing in Word for a while, your turn”
Sally: “OK, I’ve activated Outlook. Please don’t touch the keyboard until I say”
Fred: “OK, I’ll wait”
Time Passes
Sally: “OK, I’m done, your turn”

You get the idea. Now imagine that there’s a little red flag on top of the monitor. When Fred starts his letter he raises the flag. When he’s ready and happy to let Sally take over, he lowers it. Sally can now take over, and raises the flag. Essentially they are using a form of semaphore to coordinate their sharing of the computer.

Two Macro Scheduler scripts could do the same thing. Only instead of a little red flag on top of the monitor we could use a value in an INI file, or a registry entry. We’ll call this value RunStatus and it could be initialised to IDLE. Script A needs to focus a window and send some keystrokes to it. It first checks to see if RunStatus is IDLE. If not it waits until it is. It then sets RunStatus to ACTIVE and does what it needs to do before setting RunStatus back to IDLE. Script B meanwhile wants to activate Outlook and click on a toolbar button. It finds RunStatus is IDLE, so waits until Script A has set it back to ACTIVE, and so on. We might put these simple subroutines in both scripts:

SRT>WaitForIdle
  ReadIniFile>ctrl.ini,Share,RunStatus,IsIdle
  Wait>0.2
  If>IsIdle=ACTIVE,WaitForIdle
  EditIniFile>ctrl.ini,Share,RunStatus,ACTIVE
END>WaitForIdle
SRT>SetIdle
  EditIniFile>ctrl.ini,Share,RunStatus,IDLE
End>SetIdle

And then before any part of the script where we need to manipulate the User Interface we can call the WaitIdle subroutine:

GoSub>WaitForIdle

And when we’re done activating windows, or sending keystrokes, we can do:

GoSub>SetIdle

We’ve implemented a very simple form of semaphore for task communication in a multitasking environment. Using this approach we can allow two UI scripts to run concurrently without interfering with each other. If necessary, we can get a bit cleverer. If Script A must perform some automation only after the Script B has completed a specific task, we could create a different flag, or set the flag to a certain value. Script A waits for this value before it can continue.

Clearly it is expensive and wasteful to use one PC for each automation routine you need to run, so solutions like this save money and this is one approach you can use. But depending on the complexity of your scripts you may need to add lots of status checks and pauses. A much simpler solution is to use Virtualization. Install Microsoft Virtual PC, or VMWare and run each automation in its own virtual machine. Effectively you end up multiplexing the physical PC into several virtual PCs and your scripts can run safely in their own virtual machines without interfering with each other.

But on those occasions you need two UI scripts to run at the same time and interact with the same environment, our simple little flag hoisting solution is one you can use. Simple, but effective. But note that at this stage we haven’t considered file-locking and what happens if both scripts read or try to change the flag at exactly the same time. A more robust solution might be to use a value in a SQL database and take advantage of the database’s built in record locking.

A similar approach can even be used to control distributed scripts. With scripts on multiple networked computers, and the INI file in a network share (or using a SQL database), a controlling script can be created which modifies the flag. The networked scripts wait for the active flag before doing anything, and can be paused when the controlling script changes the flag. And flags can be used to synchronize scripts on different machines. This approach has been used successfully in load testing environments where hundreds of Macro Scheduler scripts are simulating tasks simultaneously to create load on the system being tested. You can read more about using Macro Scheduler for automated testing here.