Msbuild: преобразование одной группы элементов в другую группу элементов с другой структурой - PullRequest
1 голос
/ 05 апреля 2011

Я, возможно, задаю здесь не тот вопрос, и я открыт для этого, поэтому я немного расскажу о том, что я пытаюсь сделать.Я вызываю mstest через проект msbuild после динамического поиска всех тестовых сборок.Я вызываю mstest отдельно для каждой тестовой сборки, чтобы результаты можно было импортировать в teamcity (мой сервер CI), как только они стали доступны, вместо того, чтобы ждать завершения всех из них, прежде чем показывать какой-либо прогресс в TC.

Проблема заключается в том, что при этом запускается один тест за раз, и в сочетании с медленными издержками (даже на четырехъядерном процессоре i7 mstest требует 3-5 секунд на открытие для каждого проекта) и многими тестамивыполнение тестов занимает несколько минут.

Используя задачу msbuild с BuildInParallel = true (и вызывая с параметром / m), можно создавать несколько проектов одновременно.

Итак, что я пытаюсь сделать, это

  • получить список всех * .Tests.dll
  • Вызов цели ExecMsTest в том жепараллельно, для каждого .dll

    <PropertyGroup>
            <MsTestExePath Condition="'$(MsTestExePath)'==''">C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe</MsTestExePath>
            <MsTestSettingsPath Condition="'$(MsTestSettingsPath)'==''">Project.testsettings</MsTestSettingsPath>
    </PropertyGroup>
    <ItemGroup>
            <TestAssemblies Include="**\bin\**\*.Tests.dll" />
    </ItemGroup>
    
    <Target Name="RunTests">
            <Message Text="Found test assemblies: @(TestAssemblies)" />
    
            <MakeDir Directories="TestResults" />
    
            <MsBuild Projects="@(ProjectsToBuild)" Targets="ExecMsTest" BuildInParallel="True" />
    </Target>
    
    <Target Name="ExecMsTest">
            <Message Text="Running tests in $(TestAssembly)" />
            <!-- show TC progress -->
            <Message Text="##teamcity[progressMessage 'Running tests in $(TestAssembly).dll']" Importance="High" />
    
            <PropertyGroup>
                    <MsTestCommand>"$(MsTestExePath)" /testcontainer:"$(TestAssembly)" /resultsfile:"TestResults\$(TestAssembly).trx" /testsettings:"$(MsTestSettingsPath)"</MsTestCommand>
            </PropertyGroup>
            <!-- Message Text="Exec: $(MsTestCommand)" / -->
            <Exec Command="$(MsTestCommand)" ContinueOnError="true" /> 
    
    <!-- import data to teamcity test results -->
            <Message Text="##teamcity[importData type='mstest' path='TestResults\$(TestAssembly).trx']" />
            <Message Text="Tests complete from $(TestAssembly)" />
    

Однако это не совсем верно.Вы можете видеть, что моя группа элементов называется TestAssemblies, но я передаю @ (ProjectsToBuild) в mstest.Это связано с тем, что для задания msbuild требуется группа элементов в другом формате, например:

    <ItemGroup>
            <ProjectsToBuild Include="Project.mstest.proj">
                    <Properties>TestAssembly=Project.UI.Tests</Properties>
            </ProjectsToBuild>
            <ProjectsToBuild Include="Project.mstest.proj">
                    <Properties>TestAssembly=Project.Model.Tests</Properties>
            </ProjectsToBuild>
    </ItemGroup>

Так что это суть моего вопроса, если я даже задаю правильный вопрос: как мне преобразоватьTestAssemblies предметная группа в нечто похожее на предметную группу ProjectsToBuild?

В случае, если это не очевидно, имена элементов в TestAssemblies являются именами * .tests.Dll, в то время как мне нужно, чтобы это имя было внутри элемента, а имя элемента ProjectsToBuild было всемфайл Project.mstest.proj (так как все они вызывают один и тот же файл).


Благодаря @Spider M9 это работает:

    <ItemGroup>
            <TestAssemblies Include="**\bin\**\*.Tests.dll" />
    </ItemGroup>

    <Target Name="RunTests">
            <Message Text="Found test assemblies: @(TestAssemblies)" />
            <ItemGroup>
                    <TestAssembliesToBuild Include="Project.mstest.proj">
                            <Properties>TestAssembly=%(TestAssemblies.FileName);FullPath=%(TestAssemblies.FullPath)</Properties>
                    </TestAssembliesToBuild>
            </ItemGroup>

            <MakeDir Directories="TestResults" />

            <MsBuild Projects="@(TestAssembliesToBuild)" Targets="ExecMsTest" BuildInParallel="True" />
    </Target>

Запуск msbuild single-Потоковая, вся моя сборка (которая включает компиляцию, создание снимков приложений и баз данных, развертывание схемы для пары баз данных, которые используются в некоторых модульных тестах, а затем, наконец, запуск mstest) заняла около 9m30s.После этого изменения прошло ~ 7м.

Однако, прежде чем получить ответ на этот вопрос, я просто попытался запустить один экземпляр mstest, чтобы посмотреть, насколько он улучшится, и это занимает около 4m50s (из которых mstest занимает чуть более 1 минуты для запуска).Недостатком является то, что я должен подождать, пока все тесты будут завершены, прежде чем получить результаты, но, учитывая потрясающее улучшение с 6 до 1 м, это вполне приемлемый компромисс.

Для ясности, единственное отличие состоит в том, что mstest запускается один раз, а не десять раз, и, вероятно, есть много преимуществ многозадачности.Я запускаю это на Core i7-860 (4 физических ядра, 8 логических ядер), и я подозреваю, что количество ядер сильно повлияет на уровень улучшения, который вносит это изменение.

Вот мои новые RunTests:

    <Target Name="RunTests">
            <Message Text="Found test assemblies: @(TestAssemblies)" />

            <MakeDir Directories="TestResults" />

            <!-- this executes mstest once, and runs all assemblies at the same time. Faster, but no output to TC until they're all completed -->
            <PropertyGroup>
                    <MsTestCommand>"$(MsTestExePath)" @(TestAssemblies->'/testcontainer:"%(FullPath)"', ' ') /resultsfile:"TestResults\Results.trx" /testsettings:"$(MsTestSettingsPath)"</MsTestCommand>
            </PropertyGroup>

            <Message Text="##teamcity[progressMessage 'Running tests']" Importance="High" />
            <Message Text="Exec: $(MsTestCommand)" />
            <Exec Command="$(MsTestCommand)" ContinueOnError="true" />
            <Message Text="##teamcity[importData type='mstest' path='TestResults\Results.trx']" />
    </Target>

также вам нужен файл testsettings с: <Execution parallelTestCount="0"> (0 означает автоопределение, по умолчанию 1) и необходимо вызвать msbuild с помощью /m параметр и / или <Msbuild BulidInParallel="true">

1 Ответ

2 голосов
/ 05 апреля 2011

Попробуйте это:

<ItemGroup>
   <TestAssemblies Include="**\bin\**\*.Tests.dll" />
   <TestAssembliesToBuild Include="Project.mstest.proj">
     <Properties>TestAssembly=%(TestAssemblies.FileName)</Properties>
   </TestAssembliesToBuild>
</ItemGroup>
<Message Text="'%(TestAssembliesToBuild.Properties)'" />
...