Test Run

Test automation with Windows XP Embedded

Dr. James McCaffrey and Mike Hall

Code download available at:TestRun2007_10.exe(1402 KB)

Contents

The Application under Test
The Test Automation
Porting UI Test Automation to a Windows XP Embedded System
Creating Components
OS Components
Build Your Image
Deploy Your Image
A Quick Recap

Have you ever worked with the Windows® XP Embedded operating system? This is essentially a developer-customized, slimmed-down version of the Windows XP operating system intended for use on PC-like devices—game consoles, information kiosks, network appliances, and the like. With the rapid growth in the use of mobile and embedded devices, this platform is increasingly important.

I've discovered that there is a sort of knowledge gap between software test engineers and developers who work with the Windows XP Embedded-based software systems. To help bridge that gap, in this month's column I'm joined by Mike Hall, a Senior Product Manager in the Microsoft® Mobile and Embedded Devices group and an expert on Windows XP Embedded. Together we will explain how you can create lightweight but powerful UI test automation for software systems that run on Windows XP Embedded.

The best way for us to show you where we are headed is by providing you with a screenshot. Figure 1 shows some lightweight UI test automation in action. But what is not immediately apparent from the screenshot is that the automation is executing on a device that is running Windows XP Embedded.

Figure 1 UI Automation on Windows XP Embedded

Figure 1** UI Automation on Windows XP Embedded **(Click the image for a larger view)

If you look closely at Figure 1, you'll see that the UI test automation is exercising a simplistic but representative Windows form-based application similar to one you might find on a kiosk device running Windows XP Embedded. The test automation is a console application that:

  • launches the form-based application under test and manipulates the app
  • simulates a search for a product with text "idg" in the product name
  • examines the resulting app state for "111" in the results
  • determines a test scenario result

Behind the scenes, the test automation is manipulating the application under test by using the Microsoft .NET Framework P/Invoke mechanism to call Win32® API functions, such as FindWindow, SendMessage, and GetMenu.

In this month's column, we will briefly discuss the application under test, then look at the test automation, which produced the results shown in Figure 1. Next, we will describe in detail how to use the tools in Windows Embedded Studio to create a Windows XP Embedded system that contains the application and test automation in a relatively simple way. We will also examine a more sophisticated technique that creates the test automation as a custom Windows XP Embedded shell. We will conclude with a brief discussion of how you can adapt and extend the ideas presented here to meet your own needs.

If you are not familiar with Windows XP Embedded, you'll find this column a great introduction to an interesting technology. And if you're not familiar with Win32-based UI automation using a .NET Framework interface, you'll find this column a good introduction to the technique.

The Application under Test

Let's take a look at the key code for the Windows form-based application under test—the heart of a typical Windows XP Embedded software system. All the code for the application and the test automation is included in the code download that accompanies this article. As you can see in Figure 1, the dummy application has basic user controls, including a TextBox control, a ComboBox control, a Button control, and a ListBox control. In a realistic scenario, a Windows XP Embedded search application would likely retrieve search results from a database server or perhaps a Web service. Here the application maintains a local data store in the form of a simple Product class:

public class Product {
    public string id, name, price;
    public Product(string id, string name, string price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
}

The data for Product instances are stored in an ArrayList collection and are supplied when the main Form object loads:

private ArrayList al = new ArrayList();
private void Form1_Load(object sender, System.EventArgs e) {
    Product p1 = new Product("111", "Widget", "$11.11");
    Product p2 = new Product("222", "Gadget", "$22.22");
    Product p3 = new Product("333", "Thingy", "$33.33");
    al.Add(p1);
    al.Add(p2);
    al.Add(p3);
}

Because we are performing UI automation, it doesn't really matter to us where or how the search application retrieves its data—no matter where the result data comes from, it will be reflected in the application's UI, which can be examined to determine a test scenario pass/fail result. All the application logic occurs in the search Button control's click method. The application begins by crudely checking to see if the user has specified a search criterion—by product ID or by product description. If not, the app fires off a MessageBox window with an error message:

if (comboBox1.Text == "" || textBox1.Text == "") {
    MessageBox.Show("You must enter search criteria and term", "Error");
}
listBox1.Items.Clear();

After clearing the ListBox control, which holds the search results, the application retrieves the search criteria/target string and traverses through the ArrayList data store, looking for matches:

string filter = textBox1.Text;
if (comboBox1.Text == "Product ID") {
    foreach(Product p in al) {
        if (p.id.IndexOf(filter) >= 0) {
            listBox1.Items.Add(p.id + " " + p.name + " " + p.price);
        }
    }
} else if (comboBox1.Text == "Product Name") {
    // search by Name similarly 
}

Let us emphasize that we are deliberately not using good coding style or practices in order to keep the code short and understandable, and also to simulate the relatively unrefined nature of an application in the early stages of development. In fact, the sample code has several significant logic flaws you might enjoy hunting for.

If you are new to Windows XP Embedded, you may be asking yourself, "What does this application have to do with Windows XP Embedded? It looks like a perfectly ordinary Windows Forms-based application." Your observation would be correct—and this is an important point. Because Windows XP Embedded is essentially a componentized version of Windows XP, all your development skills transfer directly over. This gives Windows XP Embedded a huge advantage over alternative development platforms for embedded devices. It's not necessary for you to learn an entirely new platform and tool set, you have access to a large support community, you can use the power of the .NET Framework library, and, as this column explains, you can make use of powerful test automation techniques.

The Test Automation

There are several ways to create UI test automation. The technique we use here is to write a C# console application test harness that manipulates the form-based application under test by calling native Win32 API functions using the P/Invoke mechanism. The September 2005 Test Run column, "Low-Level UI Test Automation" (available at msdn.microsoft.com/msdnmag/issues/05/09/testrun), discusses this topic in detail.

We begin the test automation by launching the application under test using the static Start method of the Process class in the System.Diagnostics namespace:

Console.WriteLine("\nLaunching Windows application"); 
Process p = Process.Start(".\\TheAppToTest.exe");

Notice that the sample code assumes the application under test executable is in the same directory as our test harness executable. As you'll see shortly, this design is especially convenient for test automation on Windows XP Embedded systems. After launching the application under test, we use the Win32 FindWindow function to obtain a handle to the Form object of the application under test as shown in Figure 2.

Figure 2 Obtaining a Handle

Console.WriteLine("\nLooking for Form1");
IntPtr ptrToForm;
bool formFound = false;
int numTries = 0;
while (!formFound && numTries < 100) {
    ++numTries;
    ptrToForm = FindWindow(null, "Form1");
    if (ptrToForm == IntPtr.Zero) {
        Console.WriteLine("Form1 not found yet. . . ");
        Thread.Sleep(100);
    } else {
        formFound = true;
        Console.WriteLine("Form1 found with ptr = " + ptrToForm.ToString());
    }
}
if (numTries >= 100) throw new Exception("Form was not found");

The heart of the P/Invoke technique is to import the Win32 FindWindow function. We do this by placing a DllImport attribute in the test harness:

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

You can loosely interpret this attribute to mean: "Import a Win32 function from the user32.dll library. The Win32 function is named FindWindow (though the system should also check for the same name ending with 'A' or 'W'). And automatically deal with ASCII and Unicode character encoding issues. The C# alias is a static method that returns a .NET IntPtr type, which represents the handle to a window. The C# alias is named FindWindow and accepts two string arguments—the window control class name and the control friendly name."

The code in Figure 2 attempts to find the Form window; if the Form window is not found (typically because the Form object has not yet initialized), the code waits 100 milliseconds and then tries again. If the Form object is not found after 100 attempts, the test automation throws an exception. After the automation harness launches the application under test, the harness manipulates the Form object by resizing and moving the Form. For example:

Console.WriteLine("\nMoving Form1 around"); 
SetWindowPos(ptrToForm, IntPtr.Zero, 100, 200, 336, 404, 0);

This uses the Win32 SetWindowPos function to position the upper left-hand corner of the Form object at screen coordinates (100,200) with a width of 336 pixels and a height of 404 pixels.

The associated DllImport attribute for our C# alias is:

[DllImport("user32.dll", EntryPoint = "SetWindowPos")] 
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);

After manipulating the Form object, we obtain handles to the child window controls on the Form. For example, the test harness code used to get a handle to the button1 control is:

Console.WriteLine("Looking for button1");
IntPtr btn = FindWindowEx(ptrToForm, IntPtr.Zero, null, "Search");
if (btn == IntPtr.Zero) 
  throw new Exception("Could not find search button");
else 
  Console.WriteLine("button1 found with ptr = " + butt.ToString());

This uses the Win32 FindWindowEx function to obtain a handle of a child control with Name attribute "Search".

The code used to get a handle to the TextBox control is a bit trickier:

Console.WriteLine("\nLooking for textBox1");
IntPtr tb = FindWindowByIndex(ptrToForm, 3);
if (tb == IntPtr.Zero) 
  throw new Exception("Could not find textBox");
else 
  Console.WriteLine("textBox1 found with ptr = " + tb.ToString());

Here we are using an extremely handy program-defined method named FindWindowByIndex.

Although some window controls have Name attributes (such as Form1), many do not. To obtain a handle to a non-named window control you can use FindWindowByIndex, defined as:

static IntPtr FindWindowByIndex(IntPtr hwndParent, int index) {
    int ct = 0;
    IntPtr result = IntPtr.Zero;
    do {
        result = FindWindowEx(hwndParent, result, null, null);
        if (result != IntPtr.Zero)++ct;
    } while (ct < index && result != IntPtr.Zero);
    return result;
}

The idea is that even though some window controls do not have Name attributes, all window controls have an implied index number. You can use the Win32 function FindWindowEx and the implied index number of a control to get a handle to the control. The implied index number of a window control is not the same as the tab control order. The implied index number is determined by the order in which the window control is added to a parent window. So, in our example, we specify FindWindowByIndex(ptrToForm, 3) because the application under test code contains:

this.label1 = new System.Windows.Forms.Label(); // index 0
this.label2 = new System.Windows.Forms.Label(); // 1 
this.comboBox1 = new System.Windows.Forms.ComboBox(); // 2 
this.textBox1 = new System.Windows.Forms.TextBox(); // 3 <- 
this.button1 = new System.Windows.Forms.Button(); // etc.

The remaining code in the test harness manipulates the application under test by sending characters to the TextBox control, clicking on the Button control, waiting for an error MessageBox to appear, clicking on its OK button, and so on. The test harness finishes by examining the contents of the ListBox control to determine a pass or fail scenario result.

Porting UI Test Automation to a Windows XP Embedded System

A key concept in Windows XP Embedded systems is the notion of the operating system shell, as opposed to a shell application running on an operating system. Every Windows-based operating system must have exactly one, and only one, primary shell interface. For example, on the standard Windows XP operating system, the Explorer Shell is typically the primary shell, though you can also have several application shells, such as the cmd.exe command shell, the Windows PowerShell® shell, and so on. Since Windows XP Embedded is a customizable, componentized version of Windows XP, you are not limited to using the Explorer Shell as your primary shell—you can choose any of several shells to use as your primary shell and you can even create a custom primary shell.

In the sidebar, "Test Automation as a Custom Shell," we demonstrate how to port UI test automation to a Windows XP Embedded system when you've decided to use a non-custom primary shell. In the next section, we will present a more sophisticated approach for porting test automation when using a custom primary shell.

In documentation terminology, "device" can refer either to the set of equipment on which Windows XP Embedded will be running (such as an information kiosk) or to an individual hardware item (such as a mouse, keyboard, or display adapter). In this example, the target device is a laptop PC. In a more realistic scenario, the target device would probably be something less PC-like—perhaps something with a reduced keyboard or a small video display. But the Windows XP Embedded development principles are exactly the same.

The target device for our example has a single physical hard drive with a C: partition and a D: partition. Our intention is to copy the Windows XP Embedded system onto the D: partition. In order to create a Windows XP Embedded system, you can use the Windows Embedded Studio, which provides a collection of tools and a back-end database of software components. You do not typically install the Windows XP Embedded tools onto the target device; you install them on a development machine (usually running Windows XP), create a system image for your custom Windows XP Embedded OS and application on your development machine, and then deploy the image to the target device.

Creating a Windows XP Embedded image can seem confusing at first, but after a few iterations the process becomes easy to understand. Before we walk you through the details of a specific example, here's a high-level overview of the process. The first step of the image creation process is to analyze the individual hardware devices on your target device using a utility called the Target Analyzer Probe, contained in the Windows XP Embedded toolset. The next step is to create Windows XP Embedded components using a tool called Component Designer (more about this in a minute). Step three is to combine components and build them into a Windows XP Embedded image. The fourth step is to deploy the image to the target device. As you can see, the process is simple in principle.

Now we'll walk you through the details of creating Windows XP Embedded UI test automation. You begin by analyzing the hardware items on the target device using the Target Analyzer Probe (tap.exe; there is also a ta.exe program for use on 16-bit systems). Just copy the tap.exe utility onto the target device. How that copy is performed depends on what I/O devices the target device has available. (Note that the tap.exe utility requires Windows 2000 or Windows XP; alternatively you can use the Windows XP Embedded installation disk, which is bootable for this purpose.)

Once tap.exe is copied onto the target device, you can simply execute it. The result is a file named devices.pmq (often called a "pumpkin file" by insiders), which is stored in the same directory as tap.exe. This file contains a list of all the hardware items found on the target device. And since the file is in XML format, you can read it with any text editor to verify that all the hardware devices were found. Furthermore, you can manually create or edit the file if necessary. After devices.pmq has been created, copy it from the target device to the development machine.

Creating Components

The next few steps involve creating Components using a GUI-based tool called Component Designer. You can think of a Windows XP Embedded component as a package of related software. Note that in this article we use "Component" (uppercase C) when referring to a Windows XP Embedded package and "component" (lowercase) when making a general reference to a collection of items.

Windows Embedded tools work with Components rather than with individual files because the large number of files involved in a Windows XP Embedded image make managing individual files awkward. You need to create two custom Windows XP Embedded Components—one that encapsulates our form-based application and its console-based test automation, and one that encapsulates the appropriate hardware device drivers.

Prepare by creating a C:\Automation directory on the development machine and placing TheAppToTest.exe (the application) and Demo.exe (the automation) in this directory. This is not strictly necessary, but as we will show you shortly, it is a convenient approach when creating a test automation Component.

First we'll step you through making the test automation component, then we'll go through creation of the device driver component. Start by launching the Component Designer tool from the desktop Start menu, then select File | New from the main menu. You can save your configuration (using File | Save) as AutoTestAutomationComponent.sld (this is typically called a slide file) in any convenient directory. However, if you save your .sld configuration files in the same directory as the Component resource files, the entire Component will be portable since all the files are located in the same directory.

In the Component Designer left-hand navigation pane, expand the Windows XP Embedded Client (x86) item. Now create the Repository object for your test automation Component. You can think of a Repository as representing physical storage for the files which make up a Component. Simply right-click on the Repositories item and select Add Repository. In the right-hand data pane, name your Repository AutoTestAutomationRepository and modify the Source Path property to C:\Automation to indicate the location of the automation files on your development machine (as opposed to the final location on the target device). There are a number of other Repository attributes you can set at this point, but to keep things simple we won't get into those right now.

Now go back to the left-hand navigation pane in Component Designer and right-click on Components and select Add Component. At this point, the left-hand pane of Component Designer should look like the screen shown in Figure 3 and the right-hand pane should look like the screen shown in Figure 4. In the Component Properties area in the right-hand pane, you'll see that the Name attribute of the component has been set to AutoTestAutomationComponent.

Figure 3 Component Designer Left-Hand Pane

Figure 3** Component Designer Left-Hand Pane **

Figure 4 Component Designer Right-Hand Pane

Figure 4** Component Designer Right-Hand Pane **(Click the image for a larger view)

Next, to associate the Repository you just created with the Component, click on the "Repositories ..." button and select the AutoTestAutomationRepository entry from the Select Repository dialog box, and then click OK. You'll notice that most Windows XP Embedded objects, including Repositories, have a revision number that is automatically appended to the object name—this is a big help in keeping your objects organized. Now, in the left-hand pane, add references to the physical files by right-clicking on the Files item and then selecting Add | Multiple Files. In the Add Component File Resources dialog box, navigate to your app and automation files on the development machine (C:\Automation in this case), select TheAppToTest.exe and Demo.exe files using the <Ctrl> key, and click Open.

Now you will add a Component Dependency. Because both the form-based application and the console-based test automation in this example are built upon the .NET Framework 2.0, the final Windows XP Embedded image must include a .NET Framework 2.0 Component. By explicitly specifying this dependency, you can implicitly instruct the Target Designer tool (which, as you recall, combines all the Components into a final, working, Windows XP Embedded image) to include the .NET Framework 2.0 Component. Right-click on the Component or Group Dependency item and then select Add | Component Dependency. (We will explain what a Group Dependency is in the next section of this column.) At this point, Component Designer will connect to a back end database of Components, which was installed as part of the Windows XP Embedded Studio tools.

In the Add Component Dependency dialog, navigate to Software | System | System Services | Other, select the .NET Framework 2.0 Component (as shown in Figure 5), and click OK. Depending on which version of the Windows XP Embedded toolset you are using, you may have to install the .NET Framework 2.0 Component separately. The list of Components is dynamic and growing—Windows XP Embedded has a big support community that includes third-party engineers and companies as well as Microsoft itself.

Figure 5 Add Component Dependency

Figure 5** Add Component Dependency **(Click the image for a larger view)

At this point, the test automation Component is complete, so save your .sld configuration and close the Component Designer tool. Now you need to transfer the custom Component into the back-end database via the Component Database Manager tool, which you launch from the Start menu on your desktop. After the tool connects to the back end database, click the Import button on the main Database tab. In the Import SLD dialog box, browse to the .sld file (in this case AutoTestAutomationComponent.sld) and then click Import. The process is very quick and after reviewing the log messages, just click Close and Close. The test automation Component is now available to the Target Designer tool.

Now you need to create the second Component, one that encapsulates all your device drivers and other software required to support the individual hardware items on the target device. This is a very easy process thanks to the devices.pmq file, which lists all the target device hardware. Launch the Component Designer tool again, but this time click File | Import. On the Choose File for Import dialog, navigate to the devices.pmq file and click Open. This causes an Import File dialog box to appear—just click the Start button. The import process takes several minutes because Component Designer is not merely importing the XML data in devices.pmq; it is actually parsing the file, determining which software files are needed to support the hardware devices and creating a Component from that information. In other words, most of the work you would have to do manually when creating a one-off test automation Component is done automatically for the hardware component. You should get an "Import session completed successfully" message and then you can close the Import File dialog.

If you expand the Components item and select the devices item, you can rename the Component to AutoHardwareComponent. Now save your configuration as AutoHardwareComponent.sld and close the Component Designer tool. After that you can use the Component Database Manager tool to import the hardware Component into the back-end database, just as you did for the test automation Component.

OS Components

Now that the test automation and hardware Components have been created, you are ready to combine them with operating system Components to create a Windows XP Embedded image. This is where you use the Target Designer tool.

Launch Target Designer from the Start menu and click File | New. In the resulting New Configuration dialog, name the configuration, AutoTestAutomation for this example, and click OK. The Target Designer UI has three main panes. The left-hand pane lists Components that are available to add to your Windows XP Embedded image. The center pane lists Components that are currently in your image. The right-hand pane displays information about any item that is selected in the left-hand or center pane.

The left-hand pane will automatically read the back-end database and list any Components you have created—in this case AutoHardwareComponent and AutoTestAutomationComponent. Add the hardware Component by right-clicking on AutoHardwareComponent and selecting Add, and then add the AutoTestAutomationComponent in the same way (the order in which you do this doesn't make a difference).

After adding the two custom Components, you need to add core OS Components. While you could examine the thousands of Components available (typically over 10,000), a much easier approach is to use a prebuilt meta-Component. In the left-hand Target Designer pane, expand the Design Templates item to reveal several options. For this example, select the Windows-based Terminal Professional template, then right-click, and select Add. This adds a large number of individual software Components that are commonly used in a PC-like device. There are several templates available, including Retail Point of Sale Terminal, Home Gateway, Network Attached Storage, and so on. The downside to using a Component template is that you are very likely to get Components that you don't actually need. A good approach is to use a template simply as a starting point and then remove any unneeded Components.

In the center pane, select Settings and then in the right-hand pane click Target Device Settings | Show. These entries refer to the target device, not the development machine. So, in this case, you should leave the boot drive as C: but change C: to D: in the Windows folder, Program Files folder, and Documents and Settings folder entries. This is because, as we mentioned earlier, the plan is to copy the Windows XP Embedded image onto the D: partition of the device's single hard drive.

Edit the Boot ARC path entry from multi(0)disk(0)rdisk(0)partition(1) to multi(0)disk(0)rdisk(0)partition(2). The rdisk (physical drives) items are 0-based but partition items are 1-based. And change the boot partition size to 5000 (MB). This is a magic value for any boot partition that is 5GB or larger. If you specify a value of, say, 700MB, then you can potentially create a bootable CD. For this example, just leave the Partition cluster size as 4096 bytes. This is a good time to save the configuration, saving as AutoTestAutomation.slx.

Build Your Image

Test Automation as a Custom Shell

In some cases, developers using Windows XP Embedded want to implement a custom primary shell. For example, imagine the form-based application shown in Figure 1 acting as the entire UI. This approach forces user activity strictly onto the application and generally prevents interaction with such programs as Windows Explorer and Internet Explorer®. In a custom shell scenario, normal test automation techniques don't generally work because you do not launch an OS shell as you would an app.

One test automation solution in a Windows XP Embedded custom shell scenario is to create a test automation Component using the Windows Embedded Studio tools and configure that Component as the primary shell. Here's a quick overview of the process. (For a detailed description of creating custom Windows XP Embedded Shells, check out Mike Hall's article "Creating a Windows XP Embedded Shell" at msdn2.microsoft.com/ms838335.)

To create a custom test automation primary shell, you first create a test automation Component using the Component Designer tool exactly as I've described in this column. After that, when in the Component Designer, you must configure four settings to turn the Component into a special primary shell Component.

  1. Set the Component Prototype property to Software | System | User Interface | Shells | Windows Shells | Shell Prototype Component.
  2. Add an Advanced Property with these settings: Name = cmiShellPath (case sensitive), Format = String, and Value = \Automation\Demo.exe (for example, the path to the test automation).
  3. Set a group membership to Group Memberships | Add Group Membership | Dependencies | Shell.
  4. Set a second group membership to Group Memberships | Add Group Membership | Software | System | User Interface | Shells.

After you have configured these four settings to create your special primary shell Component, the Component will be listed in the Target Designer tool as a UI shell along with the Explorer Shell, the Command Shell, and so on. Then, if you add the custom automation shell Component to the Windows XP Embedded image, you'll find that the result will be an operating system that boots immediately into the test automation, exercising the associated form-based application.

You're almost ready to combine your Components into an image. But before attempting a build, you should check the Component dependencies to determine if you have accidentally left any required Components out of the configuration. This is an important step. An operating system is a complex piece of software and without a tool such as Target Designer, manually determining all the component dependencies would be very difficult.

On the main menu bar, click Configuration | Check Dependencies. A dialog opens and displays progress messages. The dependency check automatically imports any required Components into the configuration. After the dependency check finishes, you can close the dialog box. In most cases, there will be some errors displayed in a task list in the bottom pane of Target Designer. If you right-click on an error, you'll get an Action option that offers recommended fixes. For instance, one of the errors on this example says "Regional and Language Options requires at least one additional enabled component not in the configuration." If you right-click and select Action, you'll get a Resolve Dependency dialog box where you can select English from a list of languages and then click Add. For this sample, you should also add an NT Loader Component, an NTFS Format Component, and an Explorer shell Component to resolve the other errors.

Now you can click Configuration | Build Target Image. When you do this you get a build dialog with the name of your configuration (AutoTestAutomation.slx). You should then click the Build button and Target Designer will warn you that you've made changes to the configuration—because you added four new Components to resolve the errors found by the dependency check—and ask whether you want to rerun the dependency check. Click Yes and a second dependency check runs, but this one is much quicker. After the second dependency check is complete, presumably with no errors, you can close the Dependency Check dialog and the build process will begin.

In Target Designer, selecting Tools | Options | Build lets you configure the default location where Windows XP Embedded images will be stored on your system. You can also specify a location for a particular build in the Build dialog box. It's important to note that if a Windows XP Embedded image already exists, a warning message will alert you that the existing image will be overwritten—this is usually fine.

After the build finishes, don't be surprised if you get a few warnings. You can review these to make sure they are only minor issues (such as a warning that you are using evaluation software) that can be safely ignored.

Deploy Your Image

Now you're ready to deploy your Windows XP Embedded image to the target device. The new image consists of four files and one directory that contains four subdirectories. Three of the files—boot.ini, ntdetect.com, and ntldr—are the key files needed to start a Windows XP-based operating system. The fourth file—weruntime.ini—is essentially a special Windows XP Embedded key that enables Windows XP Embedded.

Copy these four files from the development machine to the boot drive on the target device. Exactly how you copy these files will depend on what types of I/O devices the target device has. In this case, because the target device is a laptop with a regular Windows XP installation on the C: drive, you can simply boot the target device and copy the files using the CD/DVD drive. The main Windows XP Embedded image is located in a directory named DriveD (because the destination on the target device is the D: drive). This directory contains three directories: Documents and Setting, Program Files, and Windows. In addition, there's also an Automation directory, which holds the form-based application (TheAppToTest.exe) and console-based automation (Demo.exe). Copy these four directories to the D: partition on the target device.

Before you attempt to boot up the target device, you'll want to make one small edit to make the device multi-bootable. Simply edit the Windows XP Embedded image's boot.ini file, adding the following line to the [operating systems] section:

multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /fastdetect

This allows you to easily boot your target device into a regular Windows XP session on the C: drive in case something goes wrong with the Windows XP Embedded image on the D: partition. An alternative approach, if your target device has a floppy disk drive, is to create a bootable floppy disk for recovery. Or you can just keep your original Windows XP installation CD handy and use it to boot into Recovery Mode if necessary.

Now comes the moment of truth. Power up your target device and if your Windows XP Embedded image is correct, the target device will boot into the customized system. A special set of programs collectively called First Boot Agent (FBA) will initialize the system on first boot (FBA does not run on subsequent boots).

At this point, you can execute the test automation. Because this example uses an Explorer Shell component, the system has a GUI interface. You can use the Windows-E keyboard combination to launch Windows Explorer, navigate to D:\Automation, and double-click on the file Demo.exe to launch the test automation. The result will resemble the screenshot in Figure 1. Another approach is to include the CMD - Windows Command Processor Component in your Windows XP Embedded image. This will give you a cmd.exe command shell you can use to launch the test automation.

A Quick Recap

Let us recap what we've seen so far and explain the advantages of this technique. Because Windows XP Embedded is essentially a version of the Windows XP operating system, you can create powerful UI test automation for it using standard programming techniques.

One approach to creating UI automation for Windows XP Embedded systems is to create a Component that includes the primary application and its associated test automation contained in a Windows XP Embedded image. Another approach is to create a normal image, without the test automation Component, and then port the automation over to the target device separately.

The test automation Component approach has three significant advantages. First, creating a test automation Component modularizes your test effort, making it easier to maintain and manage. Second, because the back-end Windows XP Embedded Component database can be located on a remote server, a test automation Component can be easily shared by a team of developers and testers. Third, creating a test automation Component allows the Component's possibly complex software dependencies to be automatically checked by the Target Designer tool.

Send your questions and comments to testrun@microsoft.com.

Dr. James McCaffrey works for Volt Information Sciences, Inc., where he manages technical training for software engineers working at Microsoft's Redmond, Washington campus. He has worked on several Microsoft products including Internet Explorer and MSN Search. James is the author of .NET Test Automation Recipes (Apress, 2006). James can be reached at jmccaffrey@volt.com or v-jammc@microsoft.com.

Mike Hall is a Senior Technical Product Manager in the Mobile and Embedded Devices Group at Microsoft. Mike has been working at Microsoft for more than 10 years and currently works with Windows CE, Windows XP Embedded, and Windows Embedded for Point of Service. Mike presents at a number of Microsoft and third-party events and writes a blog discussing embedded technologies.