- PVSM.RU - https://www.pvsm.ru -
Хочу поделиться опытом в настройке системы непрерывной интеграции для проекта Windows Phone 7 в Team City. Надеюсь, сэкономлю тем, кто пойдёт той же тропой, потраченные мной самим время и нервы.
Дано:
Необходимо:
В качестве билд-агента используется виртуальная машина с Windows Server 2008 R2. Настройка поначалу казалось простой — ставим Visual Studio, Windows Phone SDK и самого агента (агент скачивается прямо с сайта развёрнутого TeamCity). Тестовый проект без Silverlight «взлетел» без проблем — сразу запутились unit-тесты, появился контроль покрытия кода с использованием встроенного в TeamCity dotCover:
Приятно было получить такой результат за 5 минут с нуля!
Загоревшись, я взялся за запуск сборки Windows Phone. Здесь и началось самое интересное.
Нужно сказать, что в TeamCity у нас уже была настроена сборка, в процессе которой нужно было выполнять определённый код подготовки данных. Этот код использовался в «живом» приложении, и его поленились отрезать от Silverlight Runtime. Поэтому при сборке код выполнялся в эмуляторе, вызываемом агентом TeamCity. Такая схема работала на имеющемся билд-агенте.
Однако настроить аналогичную сборку на новом билд-агенте удалось только через пару дней, наполненных в основном руганью. Вот основные косяки, которые я поймал:
Кстати, через ярлык, создаваемый при установке SDK, эмулятор у меня так и не запускается. Не стал разбираться почему, поскольку при вызове его из утилиты управления он стартует нормально.
Самая большая проблема всплыла в конце, когда эмулятор наконец-то запустился. Эмулятор при запуске предупреждал, что для нормальной работы ему нужна [6] нормальная видеокарта, которую «пробросить» в виртуальную машину не получилось. Несмотря на то, что после такого грозного предупреждения эмулятор нормально работал, кнопку ОК нужно было кому-то нажать. Предполагалось, что эмулятор будет запускаться службой агента TeamCity и не будет появляться на экране, поэтому кнопку было нажимать некому.
В отчаянной попытке я пытался кодом нажать [7] эту злосчастную кнопку, но это не сработало при запуске эмулятора из службы агента.
Единственное, что я смог в итоге придумать — запускать билд-агента не как службу, а как приложение. В этом случае эмулятор запускался нормально, и на кнопку можно было нажать вручную (это достаточно делать только после перезагрузки сервера — будучи однажды запущенным, эмулятор используется для всех последующих сборок). Тем не менее я оставил код автоматического нажатия на кнопку, чтобы перезагрузка вирутальной машины была «необслуживаемой».
Запуск агента как приложения оказался несложным — в каталоге bin агента (c:BuildAgentbin по умолчанию) есть хороший набор bat-файлов, которыми можно снести ненужную уже службу агента, а также запустить агента как приложение (здесь [8] есть на эту тему).
Итак, прописываем нужный bat-файл в автозапуск, настраиваем автоматический вход после запуска системы [9] (для «необслуживаемой» перезагрузки) и вуаля! Сборка работает!
Последними штрихами явились запуск эмулятора и блокировка рабочей станции в скрипте запуска агента. Запуск эмулятора был нужен, чтобы исключить задержку в первой сборке, выполняемой после перезагрузки виртуальной машины, а блокировка — потому что админ попросил («негоже, когда консоль машины открыта!»).
Следующим этапом стало добавление в скрипт сборки запуска unit-тестов. Тесты, как я уже говорил, запускались под Unit Test Framework, идущем в комплекте с Silverlight Toolkit. Запуск на эмуляторе был уже пройденным этапом, поэтому тесты завелись без проблем:
Далее логично захотелось, чтобы тесты не просто запускались, а «валили» сборку при непрохождении. Для этого нужно было передать в TeamCity результаты выполнения тестов.
Очередное отступление — мы управляли эмулятором из сборочных скриптов через CoreCon API, за что [10] спасибо Justin Angel [11] и arty87 [12]. То есть, в скрипте сборки запускалось консольное приложение, которое собственно и управляло эмулятором.
Первое решение, которое пришло в голову и сразу было опробовано — «повалить» сборку при поваленном тесте, выйдя из приложения управления эмулятором с ненулевым exit code. При этом можно было ещё написать что-то в консоль — потом это можно будет увидеть в build log-е TeamCity.
Но хотелось красоты, чтобы TeamCity вел статистику тестов, да ещё и по ходу сборки было видно, как они выполняются (очень медитативно, между прочим). Поэтому было раскопано Service Message API [13], которое по факту оказалось «отловом» специального вида тегов из консольного вывода. Тут всплыло две особенности:
Осталось решить две задачи: отловить факт запуска и завершения тестов и передать это всё из кода, выполняемого в эмуляторе, в утилиту управления эмулятором (в консоль ведь должна писать утилита, а не эмулятор, на котором запускаются тесты).
Первая задача решилась просто. В фреймворке для Unit-тестов есть возможность подписаться на события запуска и завершения тестов, тестовых классов и целых сборок, в обработчиках которых мы можем узнать много информации (результат теста, Exception при сбое теста, время начала и завершения и т.п.). Документации по фреймворку я особо не искал, проще оказалось изучить «методом тыка».
Итак, для запуска unit-тестов с выводом результата мы немного модифицируем стандартный стартовый код:
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;
}
Модификация заключается в вызове функции GetSettings. Вот эта самая функция:
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;
}
Теперь мы пристально следим за нашими тестами. Код обработчиков событий тривиален, приводить его не буду.
Теперь нам нужно передать результаты слежки в программу управления эмулятором, где и вывести их в консоль в виде тегов Service Message API.
В качестве канала передачи использовался Isolated Storage, как собственно и было описано в статье [10]. Правда, в отличие от предложенного FileDeployer для обмена файлами использовался класс RemoteIsolatedStorage.
Однако этот способ передачи оказался не очень хорош. Поскольку хотелось передачи информации о тестах в режиме реального времени, результаты тестов сразу записывались в файл Isolated Storage на эмуляторе, а утилита управления периодически вычитывала этот файл и выводила в консоль вновь полученные результаты. Из-за блокировок на файл запись периодически «падала», что никак не отражалось на выполняемых тестах, но приводило к потере информации о них. Решили проблему «костылём» — ловлей ошибок записи и повторами при ошибках. Конечно, по фен-шуй надо использовать более подходящий способ обмена данными с эмулятором, например через IP. Однако заморачиваться на этом уже не хотелось, поскольку долгожданный результат уже был получен:
Осталось нереализованным желание контролировать ещё и покрытие кода unit-тестами. Как оказалось, нормально контролировать покрытие кода в SIlverlight Runtime не получится. Большие люди советуют [14] перекомпилировать тестируемый код в обычном .NET CLR для получения покрытия. Однако с имеющимся объемом тестов, порой весьма жестко привязанных к Silverlight Runtime, это посчитали делать нецелесообразным. Тем не менее мечта осталась, и я попробую её реализовать на начавшемся небольшом проекте. Надеюсь, всё получится и я смогу поделиться своей радостью.
P.S. Это мой первый пост, поэтому готов к конструктивной критике и предложениям.
Автор: lvr
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/windows-phone/31732
Ссылки в тексте:
[1] Silverlight Toolkit: http://silverlight.codeplex.com/
[2] гугл и MSDN: http://blogs.msdn.com/b/astebner/archive/2010/05/02/10005980.aspx
[3] не был установлен Windows Media Player: http://www.kunal-chowdhury.com/2011/09/solution-for-program-can-start-because.html
[4] интуитивной: http://social.technet.microsoft.com/Forums/en-US/winservergen/thread/4db0c13a-9fb4-41a0-aebb-9c47df8a3536/
[5] Говорят: http://www.developer.nokia.com/Community/Wiki/Windows_Phone_8_SDK_on_a_Virtual_Machine_with_Working_Emulator
[6] нужна: http://habrastorage.org/storage2/abf/02a/7e6/abf02a7e621d9a1d6ef15152d441d024.jpg
[7] нажать: http://stackoverflow.com/questions/2744111/sending-keystrokes-to-a-program
[8] здесь: http://confluence.jetbrains.com/display/TCD7/Setting+up+and+Running+Additional+Build+Agents
[9] автоматический вход после запуска системы: http://guruadmin.ru/page/how-to-enable-autologon-in-windows-7-windows-server-2008
[10] за что: http://habrahabr.ru/post/117294/
[11] Justin Angel: http://justinangel.net/WindowsPhone7EmulatorAutomation
[12] arty87: http://habrahabr.ru/users/arty87/
[13] Service Message API: http://confluence.jetbrains.com/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingTests
[14] советуют: http://channel9.msdn.com/posts/Code-Coverage-for-Silverlight-and-Windows-Phone-ViewModels
[15] Источник: http://habrahabr.ru/post/175661/
Нажмите здесь для печати.