📜 ⬆️ ⬇️

Windows Phone and continuous integration into TeamCity

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

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

It is necessary:
  1. Configure another TeamCity build agent on the VMWare virtual machine.
  2. Run unit tests for assemblies and collecting the results of their execution in TeamCity statistics.


Setting up the build agent


A virtual machine with Windows Server 2008 R2 is used as a build agent. At first, the setup seemed simple - we put 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 started right away, control of code coverage appeared using the built-in TeamCity dotCover:

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

Running emulator


I caught fire, I took up the launch of the assembly of Windows Phone. This is where the fun began.
It must be said that in TeamCity we already had an assembly set up, during which we had to execute certain data preparation code. This code was used in a “live” application, and it was too lazy to cut it off from the Silverlight Runtime. Therefore, during assembly, the code was executed in the emulator called by the TeamCity agent. This scheme worked on the existing build agent.
However, it was possible to set up a similar assembly on the new build agent only after a couple of days, filled mostly with curses. Here are the main shoals that I caught:

By the way, through the shortcut created when installing the SDK, the emulator does not start with me. 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 up. The emulator warned when it started that for normal operation it needs a normal video card, which it was not possible to "forward" to the virtual machine. Despite the fact that after such a formidable warning the emulator worked fine, the OK button had to be pressed by someone. It was assumed that the emulator will be started by the TeamCity agent service and will not appear on the screen, so there was no one to press the button.
In a desperate attempt, I tried to code with this unfortunate button, but it did not work when the emulator was started from the agent service.
The only thing I could come up with in the end was to run the build agent not as a service, but as an application. In this case, the emulator started up normally, and the button could be pressed manually (it is enough to do it only after the server is rebooted - once started, the emulator is used for all subsequent builds). Nevertheless, I left the code for automatically pressing a button so that the reboot of the virtual machine was “unattended”.
Launching the agent as an application turned out to be simple - there is a good set of bat-files in the agent's bin directory (c: \ BuildAgent \ bin), which can be used to tear down the agent's service that is no longer needed, and also to run the agent as an application ( there is this topic).
So, we register the necessary bat-file in autostart, we configure automatic input after system start (for "unattended" reboot) and voila! Build works!
The final touches were the launch of the emulator and the blocking of the workstation in the agent launch script. Running the emulator was needed to eliminate the delay in the first build, performed after the virtual machine rebooted, and the lock - because the administrator asked (“no need when the console of the machine is open!”).
The next step was to add unit tests to the build script. Tests, as I said, were launched under the Unit Test Framework that comes with the Silverlight Toolkit. The run on the emulator was already a passable stage, so the tests started up without problems:


Transfer of test results to TeamCity


Further, it was logical that the tests would not just run, but “crash” the assembly if they failed. To do this, it was necessary to transfer the results of the tests to TeamCity.
Another retreat - we ran the emulator from the build scripts via the CoreCon API, thanks for Justin Angel and arty87 . That is, in the build script, a console application was launched that actually controlled the emulator.
The first solution that came to my head and was immediately tested was to “crash” the assembly with a fallen test, leaving the emulator control application with a nonzero exit code. At the same time, you could still write something to the console - then it can be seen in the TeamCity build log.
But I wanted beauty, so that TeamCity could keep statistics on the tests, and even during the course of the assembly it was obvious how they are being performed (very meditatively, by the way). Therefore, the Service Message API was excavated, which in fact turned out to "catch" a special kind of tags from the console output. Two features emerged here:
  1. Tags must be single line. TeamCity perceives a line break as the end of a tag, so the tag turns out to be of the wrong format and is ignored. The documentation in the example shows the transfer of the translation of strings by the sequence "| n | r", but this did not work for me.
  2. Tags should not be too long. Attempting to cut the line breaks from the stektrais and pass it as details in the testFailed tag led TeamCity to arbitrarily split the resulting long string somewhere into 300 characters with all that it implies. Interestingly, the rest of the string is clearly longer than 300 characters in length in the Build Log without any partitions. I have not experimented in detail and decided to display the error frame just in Build Log

It remains to solve two tasks: to catch the fact of the start and end of tests and transfer it all from the code executed in the emulator to the emulator control utility (after all, the utility should write to the console, not the emulator on which the tests are run).
The first task was solved simply. In the Unit-test framework, it is possible to subscribe to the events of the start and end of tests, test classes and entire assemblies, in the handlers of which we can learn a lot of information (test result, Exception if the test fails, start and end times, etc.). I didn’t look for any documentation on the framework, it turned out to be easier to learn using the “spear method”.
So, to run unit tests with output, 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; } 

The modification is to call the function GetSettings. This is the 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 following our tests. The code of event handlers is trivial, I will not give it.
Now we need to transfer the results of the surveillance to the emulator control program, where we can output them to the console as tags of the Message Message API.
Isolated Storage was used as a transmission channel, as was actually described in the article . However, in contrast to the proposed FileDeployer file sharing class was used RemoteIsolatedStorage.
However, this method of transmission was not very good. Since we wanted to send information about tests in real time, the test results were immediately recorded in the Isolated Storage file on the emulator, and the management utility periodically read this file and output the newly obtained results to 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. Solved the problem with a “crutch” - catching recording errors and repeating errors. Of course, according to Feng Shui, you need to use a more suitable method of data exchange with the emulator, for example, via IP. However, I didn’t want to bother with this, because the long-awaited result had already been obtained:


There remains an unfulfilled desire to control more and code coverage with unit tests. As it turned out, normally it would not be possible to control the code coverage in the SIlverlight Runtime. Big people advise recompiling test code in a regular .NET CLR to get coverage. However, with the existing volume of tests, sometimes very rigidly tied to the Silverlight Runtime, it was considered to be impractical. Nevertheless, the dream has remained, and I will try to realize it on the beginning of a small project. I hope everything will turn out and I can share my joy.

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

Source: https://habr.com/ru/post/175661/


All Articles