Monday, February 8, 2010

AutomationElement.SetFocus() is Unreliable

Microsoft's UiAutomation framework has been fairly useful in my quest to automate all our GUI testing.  My previous solution involved Ruby, WinUser32Ruby, and RSpec (which was a fun solution), but I got tired of try to wrap Win32 with Ruby.

UiAutomation seemed to solve all my problems right up until I tried to bring an AutomationElement to the foreground with a call to AutomationElement.SetFocus().  It would randomly fail.  About 90% of the time the window in question would pop up and the test would pass with flying colors.  But every once in a while the test would fail.  Some quick debugging lead me to question the reliability of SetFocus().

Rather than monkey around with SetFocus(), Full Trust, and the .NET Security Framework I decide to rely on good old Win32.  For everyone who's being driven crazy by SetFocus(), all three of us, here is my win32 based SetForegroundWindow() method.

public bool SetForegroundWindow (AutomationElement elm, uint retries)
{
    Util.ValidateNotNull ("elm", elm);

    try
    {
        if (retries < RETRY_LIMIT)
        {
            // Using Win32 to set foreground window because
            // AutomationElement.SetFocus() is unreliable

            // Get handle to the element
            IntPtr other = FindWindow (null, elm.Current.Name);

            // Get the Process ID for the element we are trying to
            // set as the foreground element
            int other_id = GetWindowThreadProcessId (
                other, IntPtr.Zero);

            // Get the Process ID for the current process
            int this_id = GetWindowThreadProcessId (
                Process.GetCurrentProcess ().Handle, IntPtr.Zero);

            // Attach the current process's input to that of the 
            // given element. We have to do this otherwise the
            // WM_SETFOCUS message will be ignored by the element.
            bool success = 
                AttachThreadInput (this_id, other_id, true);

            // Make the Win32 call
            IntPtr previous = SetForegroundWindow (other);

            if (IntPtr.Zero.Equals(previous))
            {
                // Trigger re-try
                throw new Exception(
                    "SetForegroundWindow failed");
            }
            else
            {
                Log ("  focus set");
            }

            return true;
        }

        // Exceeded retry limit, failed!
        return false;
    }
    catch
    {
        retries++;
        uint time = retries * RETRY_FACTOR;
        Log ("  Could not SetFocus(), retry in {0} ms", time);

        Thread.Sleep ((int)time);

        return SetForegroundWindow (elm, retries);
    }
}
This method relies on Win32 calls, so you must import the requisite Win32 functions.
using System.Runtime.InteropServices;

...

[DllImport ("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr FindWindow (
  string lpClassName, string lpWindowName);

[DllImport ("user32.dll", CharSet = CharSet.Auto)]
static extern bool AttachThreadInput (
    int idAttach, int idAttachTo, bool fAttach);

[DllImport ("user32.dll", CharSet = CharSet.Auto)]
static extern int GetWindowThreadProcessId (
    IntPtr hWnd, IntPtr lpdwProcessId);

[DllImport ("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SetForegroundWindow (IntPtr hWnd);

Saturday, January 16, 2010

Waiting for Windows to Load in Automated GUI Testing

I recently started using Microsoft's UiAutomation framework to do some automated GUI testing. It's slightly better than using Win32 directly (subject of a separate post), but, like all things based in reality, leaves a little something to be desired.

The Ui Automation framework is built around the AutomationElement. The AutomationElement provides methods for finding child windows by their name (or title) and by Automation ID. This is great. With the help of UISpy it's very easy to find any element in an application (even ones with no title).

In the wonderful world of automated GUI testing you often cannot predict how long it will take for a particular UI element to load. Consequently, you are forever writing Thread.Sleep(3000). Not only does this get real old real fast, but it's not very robust since the continuous integration (CI) server running the tests could run significantly slower then your development box.

To solve this problem I wrapped AutomationElement.FindFirst() in a new method FindElementByName() and added some logic to handle the fact that the element may not be immediately available.
/// <summary>
/// Find an element by its Title or Name
/// </summary>
/// <param name="root">
/// The root element to start searching from
/// </param>
/// <param name="name">
/// The Title or Name of the element to search for
/// </param>
/// <param name="retries">
/// The maximum number of times to retry if the element is
/// not found
/// </param>
/// <returns></returns>
public AutomationElement FindElementByName (AutomationElement root,
   string name, uint retries)
{
   Util.ValidateNotEmpty ("name", name);

   Condition c = new PropertyCondition (
   AutomationElement.NameProperty, name,
   PropertyConditionFlags.IgnoreCase);

   if (null == root)
   {
      root = AutomationElement.RootElement;
   }

   // First look for imediate children
   AutomationElement ae = root.FindFirst (
      TreeScope.Element | TreeScope.Children, c);

   if (null == ae)
   {
      ae = root.FindFirst (
         TreeScope.Element | TreeScope.Descendants, c);
   }

   // If we have not found the window by now then it may be slow in
   // opening, so we wait 'time' sec and try again.
   if (null == ae && retries < RETRY_LIMIT)
   {
      retries++;
      uint time = RETRY_FACTOR * retries;

      Log ("  Element \"{0}\" not found, retrying in {1} ms",
         name, time);

      Thread.Sleep ((int)time);

      ae = FindElementByName (root, name, retries);
   }
   return ae;
}

Thursday, January 7, 2010

Validating Arguments

How many times have you seen (or, shame on you, written) this snippet of code to validate that an argument is not null?
public String StopDoingThis(MyRediculousObj foobar)
{
   if (null == foobar)
   {
      throw new ArgumentNullException (
      String.Format ("StopDoingThis: 'foobar' cannot be null",
      GetCallingMethod (), variable_name));
   }

   foreach (Foo foo in foobar.InfiniteFoo())
   {
      Console.WriteLine("Stop it! Please stop it!");
   }

   return "Realy, please stop it!";
}
Why do coders insist on writing this same chunk of code over and over again? I know, it's obvious: "Coders are lazy."

But if we're so lazy why then hasn't every coder from New York to Thailand encapsulated this ever so common validation. I dunno, but here it is. Use it and stop writing this damn validation at the top of every freakin' method!
public String NiceAndClean(FabulousObj sonice)
{
   ValidateNotNull("sonice", sonice)

   return "Look how nice and clean that was :)";
}

public void ValidateNotNull (
   string variable_name, Object parameter)
{
   if (null == parameter)
   {
      throw new ArgumentNullException (
      String.Format ("{0}: '{1}' cannot be null",
      GetCallingMethod (), variable_name));
   }
}

private string GetCallingMethod ()
{
   StackTrace stackTrace = new StackTrace ();

   // We get the second (2) frame.  The first frame would
   // be the method ('Method B') calling GetCallingMethod().
   // What the user really wants in the method that called
   // 'Method B', so we want the second frame.
   return stackTrace.GetFrame (2).GetMethod ().Name;
}
If you're truely a lazy coder then I'd expect you to write a whole bunch of these: ValidateNotEmpty, ValidateBounds, ValidateThisThatAndTheOtherThing. They're quicker than cut and paste, easier to read, and make your code look better than the dude in the cube next to you :p