Windows Phone and continuous integration in TeamCity

    I want to share my experience in setting up a continuous integration system for the Windows Phone 7 project in Team City. I hope that I save those who go the same path, the time spent by me and nerves.

    1. A rather massive Windows Phone 7 application with unit tests implemented using the Silverlight Toolkit .
    2. Customized build of the application in TeamCity without running unit tests. The build agent is a “physical” (in a sense, not a virtual) machine.

    It is necessary:
    1. Set up another TeamCity build agent on a virtual machine under VMWare.
    2. Run unit tests during assemblies and collect the results of their execution in TeamCity statistics.

    Build Agent Setup

    A virtual machine with Windows Server 2008 R2 is used as a build agent. At first, the setup seemed simple - install Visual Studio, the Windows Phone SDK and the agent itself (the agent is downloaded directly from the site of the deployed TeamCity). The test project without Silverlight “took off” without problems - unit tests immediately got confused, code coverage control using the built-in TeamCity dotCover appeared:

    It was nice to get such a result in 5 minutes from scratch!

    Emulator launch

    I caught fire and started to build Windows Phone. Here the fun began.
    I must say that in TeamCity we already had the assembly configured, during which it was necessary to execute a certain data preparation code. This code was used in a "live" application, and it was too lazy to cut it off from Silverlight Runtime. Therefore, during assembly, the code was executed in an emulator called by the TeamCity agent. Such a scheme worked on an existing build agent.
    However, it was possible to set up a similar assembly on the new build agent only after a couple of days, mostly filled with abuse. Here are the main shoals that I caught:
    • The Windows Phone SDK was not installed - complained that he needed Windows 7 without recognizing the server OS. Google and MSDN helped out . Since I had a fully downloaded SDK, after editing baseline.dat according to the instructions, I ran setup.exe without keys, and everything was installed as it should.
    • The emulator did not start because Windows Media Player was not installed . Installing it under Windows Server 2008 was also not quite intuitive .
    • They say that the emulator may not start without certain flags and manual (!) Editing the configs of the virtual machine, but this has passed me. Perhaps because I needed a Windows Phone 7 emulator, and dancing with a tambourine was described for a Windows Phone 8 emulator. In addition, the admin assured that the settings for the virtual machine were “all inclusive”.
    • The emulator did not start until the RAM volume of the virtual machine was increased from 1 GB to 2 GB. It did not start silently - not a hint of a reason!

    By the way, through the shortcut created when installing the SDK, my emulator does not start. I did not understand why, because when I call it from the management utility, it starts normally.

    The biggest problem surfaced at the end when the emulator finally started. The emulator at startup warned that for normal operation it needs a normal video card, which it failed to “forward” to the virtual machine. Despite the fact that after such a formidable warning, the emulator worked fine, someone had to press the OK button. It was assumed that the emulator would be launched by the TeamCity agent service and would not appear on the screen, so there was no one to press the button.
    In a desperate attempt, I tried to press codethis ill-fated button, but it did not work when starting the emulator from the agent service.
    The only thing I could eventually come up with was to launch the build agent not as a service, but as an application. In this case, the emulator started normally, and the button could be pressed manually (it is enough to do this only after rebooting the server - once launched, the emulator is used for all subsequent assemblies). Nevertheless, I left the code for automatically clicking on the button so that the reboot of the virtual machine was “unattended”.
    Running the agent as an application turned out to be simple - in the bin directory of the agent (c: \ BuildAgent \ bin by default) there is a good set of bat-files with which you can demolish the agent service that is no longer needed and also run the agent as an application ( there is a topic here).
    So, we write the desired bat-file into autorun, configure automatic login after the system starts (for a “maintenance-free” reboot) and voila! Build works!
    The final touches were launching the emulator and locking the workstation in the script to run the agent. The emulator was needed to start in order to exclude the delay in the first assembly, which was performed after the virtual machine reboot, and the lock because the admin asked (“it is not worthwhile when the machine’s console is open!”).
    The next step was to add unit tests to the build script. Tests, as I said, were run under the Unit Test Framework, which comes bundled with the Silverlight Toolkit. Running on the emulator was already a completed step, so the tests started without problems:

    Transfer test results to TeamCity

    Further, it was logical that the tests would not just run, but “crash” the assembly if it failed. To do this, it was necessary to transfer the results of the tests to TeamCity.
    Another retreat - we controlled the emulator from assembly scripts via the CoreCon API, for which thanks Justin Angel and arty87 . That is, in the build script, a console application was launched, which actually controlled the emulator.
    The first solution that came to mind and was immediately tested was to “knock down” the assembly with the fallen test, exiting the emulator control application with a non-zero exit code. At the same time, you could still write something to the console - then you can see it in the build log of TeamCity.
    But I wanted beauty that TeamCity kept statistics of tests, and even during the assembly it was clear how they were being performed (very meditative, by the way). Therefore, the Service Message API was excavated , which in fact turned out to be a “catch” of a special type of tag from the console output. Two features surfaced here:
    1. Tags must be single-line. TeamCity interprets a line break as the end of a tag, so the tag is in the wrong format and is ignored. The documentation in the example shows the transfer of line feed characters by the sequence "| n | r", but this did not work for me.
    2. Tags should not be too long. An attempt to cut line breaks from the stack trace and pass it as details in the testFailed tag led to TeamCity arbitrarily breaking the resulting long string somewhere into 300 characters with all the consequences. Interestingly, the remainder of a string that is clearly longer than 300 characters was shown in Build Log without any splits. I didn’t experiment in detail, and I decided to display error errors simply in Build Log

    It remains to solve two problems: to catch the fact of launching and completing the tests and transfer it all from the code executed in the emulator to the emulator management utility (after all, the utility should write to the console, and not the emulator on which the tests are run).
    The first task was solved simply. In the framework for Unit tests, it is possible to subscribe to events of launch and completion of tests, test classes and entire assemblies, in the handlers of which we can find out a lot of information (test result, Exception when the test fails, start and end time, etc.). I didn’t really look for documentation on the framework, it turned out to be easier to learn using the “poke method”.
    So, to run unit tests with the output of the result, we slightly modify the standard start code:

    private void MainPage_Loaded(object sender, RoutedEventArgs e) 
      var testPage = UnitTestSystem.CreateTestPage(GetSettings()) as IMobileTestPage;
      BackKeyPress += (x, xe) => xe.Cancel = testPage.NavigateBack(); 
      (Application.Current.RootVisual as PhoneApplicationFrame).Content = testPage; 

    Modification consists in calling the GetSettings function. Here is this very function:
    public static UnitTestSettings GetSettings()
        var settings = UnitTestSystem.CreateDefaultSettings();
        settings.TestHarness.TestClassStarting += TestHarnessTestClassStarting;
        settings.TestHarness.TestClassCompleted += TestHarnessTestClassCompleted;
        settings.TestHarness.TestMethodStarting += TestHarnessTestMethodStarting; 
        settings.TestHarness.TestMethodCompleted += TestHarnessTestMethodCompleted;
        return settings;

    Now we are closely monitoring our tests. The code of event handlers is trivial, I will not cite it.
    Now we need to transfer the tracking results to the emulator management program, where we can output them to the console in the form of Service Message API tags.
    Isolated Storage was used as the transmission channel, as was actually described in the article . True, unlike the proposed FileDeployer, the RemoteIsolatedStorage class was used to exchange files.
    However, this transmission method was not very good. Since I wanted to transmit information about tests in real time, the test results were immediately written to the Isolated Storage file on the emulator, and the management utility periodically read this file and displayed the newly obtained results in the console. Due to locks on the file, the record periodically “fell”, which did not affect the tests being performed, but led to the loss of information about them. They solved the problem with a “crutch” - catching write errors and retrying errors. Of course, in Feng Shui, you need to use a more suitable way of exchanging data with the emulator, for example, via IP. However, I did not want to bother with this, since the long-awaited result has already been obtained:

    The desire to control the code coverage with unit-tests remained unfulfilled. As it turned out, it is not possible to control code coverage normally in SIlverlight Runtime. Larger people advise recompiling the test code in the regular .NET CLR to get coverage. However, with the available volume of tests, sometimes very rigidly tied to the Silverlight Runtime, it was considered impractical to do this. Nevertheless, the dream remained, and I will try to realize it on the small project that has begun. I hope everything works out and I can share my joy.

    PS This is my first post, so I'm ready for constructive criticism and suggestions.

    Also popular now: