Archive for the 'development' Category

Published by breki on 21 Apr 2008

White Facade - Fluent Interface For Writing Tests Using White

A few days ago I wrote about my first experiences in using White. I mentioned I wanted to create a facade around the White API in order to avoid constantly retyping the same code for common use cases. Since fluent interfaces are in fashion now, I decided to create a fluent facade. Just joking, of course.

I’ll present the facade through a few tests of a Kosmos GUI, which is currently under construction. Kosmos GUI is a MDI application for displaying maps from OpenStreetMap geo data. You have to load a Kosmos project, which contains information about where to find the data and how to render it. So after starting the application, the first user action would typically be to open the project:

Kosmos1

After clicking the File|Open Project menu item, a dialog for selecting the Kosmos project file appears:

Kosmos2

One you have selected the project file, a progress box is shown with an option to cancel the operation:

Kosmos3

If you let it finish, a map appears and some additional menu items are now available:

Kosmos4

Test Code

Here’s a sample test case which goes through the above steps:

[Test]
public void OpenProject()
{
   facade.ClickMenu ("File", "Open Project…")
      .FileDialog ("Open Kosmos Project File",
         @”D:\MyStuff\projects\OsmUtils\trunk\Data\Kosmos\KosmosProjectExample1.xml")
      .MainWindow ().ModalDialog ("Loading Kosmos Project")
      .MainWindow().Menu ("View", "Zoom In").AssertIsEnabled (true)
      .ClickMenu ("View", "Zoom In")
      .ClickMenu ("View", "Zoom Out")
      .ClickMenu ("View", "Zoom All");
}

After loading the project the test case executes some of the zoom functions available in the menu. And here’s a test case which cancels the project loading (and then checks that the View|Zoom In menu item is still not enabled):

[Test]
public void OpenProjectCanceled ()
{
   facade.ClickMenu ("File", "Open Project…")
      .FileDialog ("Open Kosmos Project File",
         @”D:\MyStuff\projects\OsmUtils\trunk\Data\Kosmos\KosmosProjectExample1.xml")
      .MainWindow().ModalDialog ("Loading Kosmos Project").Button ("Cancel").Click()
      .MainWindow ().Menu ("View", "Zoom In").AssertIsEnabled (false);
}

Hopefully the code is self-explanatory. The basic idea is for the facade to remember the current window or other UI element to perform actions on. You can change the active window at any moment. The facade is also supplied with some basic assertion methods useful in the test code (they throw InvalidOperationExceptions, so you can use them with any unit test framework).

Oh I almost forgot. This is how you instantiate the facade:

WhiteFacade facade = WhiteFacade.Run (applicationFilePath);

And if the facade does not offer enough for you, you can always access the underlying White’s Application object through facade.Application property.

Download

You can download the latest version of the WhiteFacade class from here. I have to warn you though: the class is in its infancy and will evolve through my effort on testing Kosmos GUI. It provides only for some basic use cases, but I think those common cases represent the majority of the GUI test code. I just wanted to give you an idea on how to make writing GUI test code as painless as possible.

Published by breki on 19 Apr 2008

Using White To Test Kosmos GUI

After a few weeks of work on my new framework for multidocument interfaces called BrekiViews (yes, I know I promised to write a little about it, but I just didn’t have enough time and concentration to put the brainstorms in my head into some coherent words) and refactoring Kosmos code to use it, I’m finally able to show some basic stuff in the new Kosmos GUI application. It’s all pretty simple at the moment, I got just one menu item working: File | Open Project. But I wanted to use this opportunity and start using White from ThoughtWorks to write automated GUI tests, since I was looking forward to trying the White out.

Documentation (Or The Lack Of It)

Unfortunately, the first impressions aren’t that good. The worst problem is lack of documentation. There are some pages written on the project’s CodePlex page and I also stumbled on a few posts which have some more examples on how to use it, but in general the API is undocumented. NDoc documentation that comes in the latest release just contains the API reference, without any human-generated documentation. It even states that it’s based on .NET Framework 1.1, which was obviously autogenerated and is probably wrong, since the API includes generics. I haven’t checked the source code directly though, maybe there is something there.

Yes I know, it’s an open source project and I shouldn’t expect too much, but I still think that such a project for which the whole purpose is to be used through an API should contain at least some documentation. Otherwise you spend a lot of time experimenting with the API to achieve the results.

Keyboard Problems

Like most other UI testing frameworks, White too suffers from quirks. I wanted to start the “Open Project” menu action using the keyboard shortcut, so I tried with:

mainWindow.Keyboard.HoldKey (KeyboardInput.SpecialKeys.CONTROL);
mainWindow.Keyboard.Enter ("o");

Which ended up forcing me to restart the computer, since the OS started acting very funny, as if the Ctrl key was still being pressed. I guess I should have added

mainWindow.Keyboard.LeaveKey (KeyboardInput.SpecialKeys.CONTROL);

to unpress the Ctrl key, but I’m not sure, since I couldn’t find any documentation on using the keyboard in White. Anyway, a helper method that would accept a shortcut as a string (something like KeyboardInput.Press (”Ctrl+O”) would be very helpful).

Clicking The Menus

After abandoning the keyboard approach, I tried to start “Open Project” by clicking on the menu item. I did some experimenting and finally managed to write a basic test, here’s the whole test fixture:

   [TestFixture]
    public class BasicTests
    {
        [Test]
        public void OpenProject()
        {
            Menu fileMenu = mainWindow.MenuBar.MenuItem ("File");
            fileMenu.Click();
            Menu openProjectMenu = fileMenu.SubMenu ("Open Project…");
            openProjectMenu.Click();
        }

        [SetUp]
        public void Setup()
        {
            application = Application.Launch (@”..\..\..\Kosmos.Gui\bin\Debug\Kosmos.Gui.exe");
            mainWindow = application.GetWindow ("Kosmos GUI", InitializeOption.NoCache);

            Assert.IsNotNull(mainWindow);
            Assert.IsTrue (mainWindow.DisplayState == DisplayState.Restored);
        }

        [TearDown]
        public void Teardown()
        {
            application.Kill();
        }

        private Application application;
        private Window mainWindow;
    }

White Facade

From my experience with other UI testing frameworks, if you intend to do more than a few simple UI tests it is a good idea to create some sort of a facade around the testing API. This way you simplify the interface to common use cases you typically use and eliminate any nasty quirks (like memory or resource leaks). Bil Simser has done something to that affect for White, although I don’t support his views on (not) using Setup and Teardown methods in unit tests.

Even better: why not implement a fluent inteface facade? Something like

WhiteFacade.Run(kosmosGuiPath).Press ("Ctrl+O")

or

WhiteFacade.Run(kosmosGuiPath).ClickMenu ("File", "Open Project…")

would be much nicer than the above code, don’t you think? Okey, I’m oversimplifying, I know :), but I will try to do something in the direction of a fluent interface facade, since I don’t intend to abandon the White just yet.

Published by breki on 07 Apr 2008

UTC Time And .NET: A Constant Hassle

I keep finding new ways to loose half a day in debugging and troubleshooting when working with timezones and UTC time in .NET Framework.

A little background into my problem: we’re trying to use UTC exclusively in our application. So, for example, whenever we ask for current time, we use DateTime.UtcNow property instead of DateTime.Now. And also we are strict on a policy of naming all variables that hold UTC time with an Utc suffix, example: currentTimeUtc. This is just in case nobody gets confused of the timezone used in a variable. This is especially useful when our application communicates with the world, since some external services work with local time only, so there needs to be a conversion. And I recommend using this policy on database columns too, for the same reason.

The latest hassle has to do with converting UTC time to and from a string. As we discovered, we naively belived that all that was necessary was to use DateTime.ToString ("u", CultureInfo.InvariantCulture) to convert the UTC timestamp into the string. Later if we wanted to convert it back into an UTC time, we simply called DateTime.Parse (timeString, CultureInfo.InvariantCulture) to get the DateTime value back.

Off course this doesn’t work. The ToString() method converts the time into "2002-12-10 22:13:50Z" format. But the Parse() method (well actually the override we used) first parses it into UTC time and then automatically converts it into the local time. So we end up with a variable marked as UTC but actually holding the local time, which is no good.

The solution (or what I hope to be the solution) is to use
DateTime.Parse (timeString, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal) override. Let me quote the MSDN documentation on the AdjustToUniversal enumeration value:

Indicates that the date and time will be returned as a Coordinated Universal Time (UTC). If the input string denoted a Local time (through a time zone specifier or AssumeLocal) then the date and time is converted from the local time zone to UTC. If the input string denoted a UTC time (through a time zone specifier or AssumeUniversal) then no conversion occurs. If the input string did not denote a Local or UTC time (no time zone specifier, and neither AssumeLocal nor AssumeUniversal was included), then no conversion occurs and the Kind of the resulting DateTime is Unspecified. Cannot be used with RoundTripKind. 

Just another reminder that if you want to use UTC times in your applications, you shouldn’t assume anything. At least when the .NET Framework is concerned ;)

Published by breki on 31 Mar 2008

Tip: How To Speed-Up Sandcastle

Simply remove (or rename) the Data/Reflection directory, so that Sandcastle cannot find the information about .NET Framework libraries. You will loose the ability to have MSDN help on .NET Framework classes links in your documentation, but I think this is not crucial for day-to-day builds, compared to the substantial increase in your build speed. And also, by deleting the contents of the Reflection folder you’ll gain some 200 MB of space on your disk.

Published by breki on 02 Mar 2008

Friday Goodies - 07. March

.NET Development

Development In General

Published by breki on 29 Feb 2008

Multiple instance Windows service in C#

The standard way of installing Windows services using the Visual Studio Windows Service project and the InstallUtil.exe tool does not allow the user to specify the service name after the project has been compiled. This is problematic if you want to install two or more instances of a Windows service on the same computer. So in this post I propose a way to achieve this without much effort.

The procedure consists of the steps described below. I’ll assume that you already have a Visual Studio project and you want to extend it with the multiple instance support:

1. Add a new XML file named App.InstallConfig.xml to your project. It should contain the following XML code:

<installconfig>
    <servicename>My Service Name</servicename>
</installconfig>

2. Add a post-build step to the project:

copy $(ProjectDir)App.InstallConfig.xml $(TargetPath).InstallConfig.xml

This will copy the installation configuration file from the project directory to the target and will replace the ‘App’ name with the name of your project’s assembly.

3. Add a new private method to the main (project) installer class:

private void ConfigureInstaller ()
{
    // find the install configuration file
    string assemblyLocation = Assembly.GetExecutingAssembly ().Location;
    string installConfigFile = assemblyLocation + ".InstallConfig.xml";

    // now load the file and parse it
    XmlDocument xmlDoc = new XmlDocument ();
    xmlDoc.Load (installConfigFile);

    XmlNode serviceNameNode = xmlDoc.SelectSingleNode ("InstallConfig/ServiceName");
    if (serviceNameNode != null)
    {
        string serviceName = serviceNameNode.InnerText.Trim ();
        this.ListenerServiceInstaller.ServiceName = serviceName;
    }
}

This method opens the mentioned configuration file and reads the InstallConfig/ServiceName setting which should contain the actual name of the Windows service instance. It uses this name to set the ServiceInstaller.ServiceName before the installation is initiated.

4. And finally, add the call of the ConfigureInstaller() method to the project installer constructor, like this:

public ProjectInstaller()
{
   // This call is required by the Designer.
   InitializeComponent();

   ConfigureInstaller ();
}

NOTE: replace the name of the ProjectInstaller constructor with your own class name.

This should be it. To install the service under the desired name, you just edit the .InstallConfig.xml file before calling the InstallUtil tool.

Extending this further

You can use the same system to include some other Windows service parameters (like the user account under which it will run, for example). The installer class provided by the .NET Framework does not allow setting the Windows service’s description, but there is a way to do this (see the “Adding a description to a .NET Windows Service” article on CodeProject). Then you can add the description tag in the .InstallConfig.xml file and have a different description for each of the service instances.

Also check out the “Windows Services Can Install Themselves” article on how to create self-installing Windows services (be sure to read the article’s comments, they contain some additional tips).

Published by breki on 22 Feb 2008

Friday Goodies - 22. February

.NET Development

Development

Miscellaneous

Published by breki on 15 Feb 2008

Friday Goodies - 15. February

.NET Development

Development

GPS

Published by breki on 08 Feb 2008

Friday Goodies - 08. February

Development

Web Services

.NET Development

VisualStudio

GPS

Misc

Published by breki on 07 Feb 2008

Tracking activity on your project in Subversion

A few months ago I was looking for some way of tracking activity on our new software project. What I wanted is a free and simple tool which could analyze Subversion logs and generate some statistics and graphs on lines of code, commits and similar stuff.

So I did a search on Google, SourceForge and some other search engines and I found two tools: StatSVN and (oddly enough) SvnStat. The latter generates a single HTML page with a collection of statistics graphs on daily commits (here is a sample report), while the former is more sophisticated and does analysis on lines of code, file and directory sizes, commit activity analyzed per hours of day and days of week and other things (see sample report). It even shows a repository heatmap - a clickable hierarchical map of your SVN repository with indicated increases or decreases of lines of code per directory or file.

The biggest difference between them is that StatSVN not only analyzes the SVN log file, but also goes through the latest version of your SVN repository. That’s how it calculates the lines of code in your project, for example.

I decided to use both of them, since they complement each other (SvnStat generates some graphs which the other one does not). I created a batch script and set it to run as a scheduled task on our build server. Here is a generalized version of the script, you have to fill in your specifics (see below):

PATH %PATH%;C:\Program Files\Subversion\bin
svn update <1>
svn log <1> -v –xml –non-interactive ><2>
cd D:\Programs\Development\SvnStat-1.0
java -classpath SvnStat-all.jar de.agentlab.svnstat.SvnStat -jar SvnStat-all.jar -r <2> -d <3>
cd D:\Programs\Development\statsvn-0.3.1
java -jar statsvn.jar -exclude <4> -output-dir <4> -cache-dir cache <2> <1>

<1> = a file path to your checked out repository

<2> = a place where you want to put the SVN XML log file (the whole file path including the name)

<3> = a place where the first (SvnStat) report will be generated

<4> = a place where the second (StateSVN) report will be generated

One warning: the reports also analyze activity for each SVN user, so watch out if you have members in your team that feel that this is a little too Big Brother-ish.

Next »