2013年10月28日 星期一

WMTF - UIAL Overview

WMTF - UIAL Overview

UIAL Overview

The UI Abstraction Layer (UIAL) is a collection of C# classes that model globalized access to an applications user interface. The Device Automation Toolkit (DATK) provides C# classes for generic controls, whereas the UIAL exposes every individual instance of these controls for easy programmatic access. The UIAL hides details about each control, such as control ID or parent window, from the tests that need to use these controls. In this way test cases are isolated from changes in the UI - changes to details in UI controls only require changes in the corresponding UIAL, not in the hundreds of test cases that may require access to that control.

UIAL Components

Every application has its own set of UIAL projects - one for each supported device. Within each UIAL project you will have 4 important components:

Dialog Classes

Every dialog in an application is exposed in a Dialog class.
  • Derives from MobilityToolKit.MobilityDialog
  • Contains details about the dialog itself such as control ID and parent window.
  • Exposes a DATK object for every control contained within the dialog. The DATK objects are exposed as properties of the dialog class.
  • Contains a VerifyAllControls() method that checks to see that each non-optional control in the dialog can be found.
  • Exposes a static instance of the IdnHolder class for the dialog to provide access to localized strings found in the dialog.
  • Recently updated versions expose a DATK ControlBinding object for every control which gives you access to the WindowFinder used to bind to a control.
  • Example: %_WMTFROOT%\CalViewSample\AbstractionLayer\SmartPhone\CalViewMain.cs

Application Class

Each application has one application class to hold all the dialog classes. This is the starting point for locating any single UI element.
  • Contains a Launch() method used to start the application using the UI.
  • Contains a static property for each of the Dialog classes within the app.
  • Example: %_WMTFROOT%\CalViewSample\AbstractionLayer\SmartPhone\Microsoft.MobileDevices.AbstractionLayer.CalView.SmartPhone.cs

IdnHolder Classes

Each dialog in an application has an associated IdnHolder class to hold localized resources for the dialog.
  • Contains static IdnString instances for various string resources found in the dialog.
  • Example: CalViewSample\AbstractionLayer\SmartPhone\IdnHolders.cs

UIAL self-test

Each UIAL contains a self-test suite that walks through the UI to validate itself.

  • Contains Begin() and End() methods for test setup and cleanup.
  • Contains AbstractionLayerBVT() method which walks through the UI to call VerifyAllControls() on every dialog in the application. 
  • Verification is very simple - the test passes if AbstractionLayerBVT() completes without throwing an exception.
  • Example: %_WMTFROOT%\CalViewSample\AbstractionLayer\SmartPhone\Microsoft.MobileDevices.AbstractionLayer.CalView.SmartPhone.cs  

How a UIAL is used

The UIAL for an application is used primarily by the Area Library for the application. Briefly, the Area Library is a collection methods that accomplish common UI tasks. For instance, the Inbox Area Library might contain a method that configures a messaging account, while the Contacts Area Library might contain a method to create a new contact. In order to accomplish these tasks the Area Library has to interact with a large number of controls on the device. Rather than specify the details of each control itself the Area Library simply gets the DATK object for UI element that it is interested in from the UIAL and then interacts with that object. For example, in order to select an item in the sample CalView application, you would use:
CalViewUIAL.CalViewMainDialog.MainListView.SetItemSelected(index, true); The Area Library uses the UIAL to locate specific controls by narrowing down the location from the app, to the dialog, to the control itself. The Area Library only cares where the control is, not what properties it has, which provides a level of isolation from changes in the UI.

Creating a UIAL

To a large extent the UIAL consists of generated code, but area owner expertise is needed to finish the process. The following are the general steps that are used to generate a UIAL. The work items below are needed to complete the UIAL. Remember, when working with the UIAL, that most of the classes representing dialogs will be automatically regenerated, so making changes to these files is wasted work. For this reason when changes are needed in these dialog classes you need to either add tags to the associated RCML files, or you need to extend the class that needs changes and make the changes in the inherited class. This is not the case with the classes representing applications.

Generate RCML files for dialogs and windows.

Use RcmlSnap to take a snapshot for every window in your application. This could take a few minutes per dialog.

Rename Controls in RCML to be Semantically Correct.

See the section on Editing the RCML to find out how to do this. This could take about 15 - 30 minutes per file.

Generate your UIAL project.

  • Run RCtoCS to generate the cs files for your application windows, and include them in your project.
  • Run UIALAppGen to generate the code for the application and main dialog classes.

Expose IdnStrings.

  • IdnStrings are a way to globalize strings found in the product, so that test automation can be easily globalized.
  • Use CeStrip to extract the resource strings and IDs from each module in your application.
  • RCTOCS will produce a skeleton file to hold your IDN strings called IdnHolders.cs along with the other classes for the UIAL. In this file you will find a class for each of the dialogs in your UIAL. Assuming that globalization of your test automation is important, the task here is to populate these classes with IdnString objects for strings that are exposed from your dialogs.
  • Each class contains an example that will help you see how to create the IdnString object. Depending on the size of your area and the number of strings this could take a couple hours.

UIAL Self-Test.


Every UIAL project has an auto generated self-test to smoke test the UIAL. A self-test is a convenient way of ensuring a minimal level of correct functionality of the UIAL. To implement this, you need to modify the test to open every dialog before calling the validation method on that dialog. This will take a couple hours at least.

Maintaining a UIAL

When UI or resource changes are made in the product the UIAL must be updated to reflect these changes.

Re-run RcmlSnap to update the RCML files

  • Run RcmlSnap on a new or updated window. With updated windows, make sure that RcmlSnap has access to the existing .rcml file, so that it can round-trip changes you've made to it previously.

Re-run RCTOCS to update the UIAL files

  • See RCtoCS for information on running the tool.

Re-run CeStrip to get an updated list of resource strings.

  • See CeStrip for information on running the tool.
  • Look at the IdnHolders.cs file for your UIAL project, a new class will be added for every new RCML file.
  • Add any new resource strings to the corresponding dialog classes in IdnHolders.cs.

Make sure the UIAL self-test passes


  • Build and the UIAL using Visual Studio.
  • Run the UIAL self-test by deploying the UIAL project with the Tux.Net project as the startup project. 

Tools used to work with a UIAL

These are the tools used when working with a UIAL:
  • RCtoCS: Tool generates UIAL Dialog classes based on RCML files that have been decorated with specific tags.
  • RcmlSnap: Tool generates RCML based on the top-level window currently displayed on a device. 
  • GenFinder: Tool determines Datk.WindowFinder object that should be used to locate a specific control on a device.
  • CeStrip: Tool which extracts resource strings in name=value pairs from a Windows CE binary.
  • UIALAppGen: Tool generates a UIAL Application class based on a number of UIAL Dialog class files. 

How to decorate RCML files for use by RCTOCS

In order to auto-generate abstraction layer code for your application, you must first mark up the .RCML files that correspond to each dialog in the application.
RCML is XML, and the tool that reads it and converts it to C# code is looking for a specific set of attributes that need to be added to the existing tags. What follows is a list of the tags you want to change, the attributes you want to add to each tag, and an example of each attribute. Attributes are case-sensitive.
  • <PAGE>
    • APPNAME="appName": This is the name of your app to be appended to your project namespace when the C# code is generated.
    • FRIENDLYNAME="FindReplace": This will be the name of the parent class containing all the child controls. Don't add "Dialog" as part of the text, since that will be appended to your FRIENDLYNAME by the codegen tools.
    • NAMESPACE="Microsoft.MobileDevices.AbstractionLayer.pOffice": This is the parent namespace onto which APPNAME will be appended. Do not put the value inside the APPNAME attribute into your NAMESPACE value. If you have a parent app namespace, like pOffice or pOutlook, this is the right place to add it.
    • PROJECT="PocketPC": Choices are PocketPC and Smartphone.
    • DIRECTORY="\abstractionlayer\<appName>\<sub-appName>\<project>": This is a relative path where the generated files will be copied.
    • CSHELP="The FindReplace dialog": This is a place to put text that will be added to comments in the generated code.
    • PROCESSNAME="yourapp.exe" - (OPTIONAL): This attribute will add a property to the dialog window finder that tells it to match this dialog only if it is owned by the provided process name. This can be helpful if the wrong dialog is being found when you try to initialize a specific UIAL object.
    • SCOPE="private" - (OPTIONAL): This attribute will cause a base and a derived UIAL class to be generated by rctocs. The derived class can safely be hand-edited. Include this attribute if you need to override any of the UIAL defaults. If you don't need to do this, leave this attribute off.
    • BASECLASS="ClassName:(parameterString)" - (OPTIONAL): This attribute specifies that the UIAL class generated for this dialog should be derived from base class "ClassName" and that the constructor should pass "paramaterString" to the base constructor.  The base class needs to derive from MobilityToolKit.MobilityDialog.
    • OBSCURED="true" - (OPTIONAL): This attribute will force the .Visible property of the mainFinder object to be set to false. This worked around a bug in the DATK that caused exceptions when a UIAL object is initialized with the .Visible property set to true.  This should no longer be necessary.
    • IGNORETEXT="true" - (OPTIONAL): This attribute will prevent rctocs from initializing the .Text property of the mainFinder object. This is useful under circumstances where the main dialog's titlebar text changes at runtime, or the auto-generated IDN value corresponding to the window title is wrong, for whatever reason. NOTE: including this attribute will cause any value inside the IDNTEXT attribute to not be generated.
    • IDNTEXT="YourDialogIdns.mainWindowTitle" - (OPTIONAL): If present, the contents of this attribute will be used as the value of the .Text property of the mainFinder object, in place of an auto-generated IDN value. We recommend that the value you use here is a reference to a member variable contained within the IdnHolder class that belongs to this dialog. An instance of this class is already provided by rctocs, for your convenience, and looks something like this:
      /// <summary>
      /// IdnHolder object holding all the owner-maintained strings for AlignDialog./// </summary>

      public readonly AlignDialogIdnHolder AlignDialogIdns = new AlignDialogIdnHolder();
  • <BUTTON>, <CHECKBOX>, <COMBOBOX>, <LABEL>, <LISTBOX>, <SPINNER>, <EDIT> (the following attributes apply to all of these tags)
    • FRIENDLYNAME="ShowFormattingCheckBox": This will be the name of the control in the generated code. You should append the control type to 1 or 2 words describing the control.
    • CSHELP="Check this box to toggle formatting display": This is a place to put text that will be added to comments in the generated code.
    • OBSCURED="true" - (OPTIONAL): This attribute will force the .Visible property of the control's WindowFinder object to be set to false. This worked around a bug in the DATK that caused exceptions when a UIAL object is initialized with the .Visible property set to true.  This should no longer be necessary.
    • OPTIONAL="true" - (OPTIONAL): This attribute will prevent the control from being added to the dialog's VerifyAllControls() method. This is useful for controls that are present and visible only under specific circumstances at run-time.
    • MODIFIER="override" - (OPTIONAL): Use this to specify a modifier for the control (override, virtual, public, private, etc.) where public is the default if not specified.
    • IDNTEXT="YourDialogIdns.mainWindowTitle" - (OPTIONAL): If present, the contents of this attribute will be used as the value of the .Text property of the control's WindowFinder object. This is useful under circumstances where identification by control ID alone is insufficient or error-prone. We recommend that the value you use here is a reference to a member variable contained within the IdnHolder class that belongs to this dialog. An instance of this class is already provided by rctocs, for your convenience, and looks something like this:
      /// <summary>
      /// IdnHolder object holding all the owner-maintained strings for AlignDialog./// </summary>
      public readonly AlignDialogIdnHolder AlignDialogIdns = new AlignDialogIdnHolder();
As you choose friendly names for your controls you want to postfix the DATK Control type to the end of the variable name. For example, if you have a combo box that sets the Type of a search you could name it SearchTypeComboBox. Or, if you have a textbox that accepts a name you could name it NameTextBox. Here is the list of DATK control names that should be used:
  • Button
  • CheckBox
  • ColumnHeader
  • ComboBox
  • ContextMenu
  • Control
  • HtmlControl
  • HtmlLink
  • HtmlLinks
  • Label
  • LabelEditControl
  • ListBox
  • ListView
  • ListViewItem
  • MainMenu
  • Menu
  • MenuItem
  • RadioButton
  •  RichEditBox
  • SBTextBox
  • SIPFloater
  • SIPWindow
  • SliderControl
  • Softkeys
  • TabControl
  • TabPage
  • TextBox
  • ToolBar
  • ToolBarButton
  • VirtualListView
After your changes have been made, drag each file into Internet Explorer to confirm that it parses correctly.

沒有留言:

張貼留言