How to set up TeamCity/NAnt/Gallio/PartCover to show test results? - c#

This is my first time setting up teamcity and I am running into some issues displaying results. I want to have a build step that runs an NAnt script. The script should run my unit tests through PartCover and display results. The results should be:
Tests that pass/Tests that fail
Coverage report
But I don't really know how to set up the script or the settings or even where I should view these results (the artifacts section I'm guessing?). Using the following script below, everything runs ok but I am not able to view any reports.
<project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<loadtasks assembly="C:\Program Files\Gallio\bin\Gallio.NAntTasks.dll" />
<target name="test">
<gallio result-property="exitCode" failonerror="false" >
<runner-extension value="TeamCityExtension,Gallio.TeamCityIntegration" />
<files>
<include name="%system.teamcity.build.checkoutDir%\Trunk\MyLibrary.Testing\bin\Release\MyLibrary.Testing.dll"/>
</files>
</gallio>
<fail if="${exitCode != '0'}" >One or more tests failed. Please check the log for more details</fail>
</target>
</project>
For the .Net Coverage section, I have PartCover (2.2 or 2.3) selected but I don't have anything in the PartCover Arguments (should I?)
Thanks for your help!

In my experience you should not run Gallio directly. Instead you should run PartCover and specify in its command-line parameters Gallio as target.
You can find some advices on Nant+PartCover here:
Integrating PartCover.NET with NAnt

I ran into issues with NAnt and decided to just use MSBuild. MSBuild was easier to work with and gave very descriptive error messages. (I also found our license for NCover so used that as well). Here is my script for anyone interested. I found the code for it from various spots on the net.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CoverageDir>.\Tests\Output\Coverage</CoverageDir>
<CoverageFilesDir>.\Tests\Output\Coverage\files</CoverageFilesDir>
<BinDir>Testing\bin\x86\Release</BinDir>
<NCoverDir>C:\Program Files (x86)\NCover</NCoverDir>
<GallioDir>C:\Program Files (x86)\Gallio\bin</GallioDir>
</PropertyGroup>
<UsingTask TaskName="NCover" AssemblyFile="$(NCoverDir)\Build Task Plugins\NCover.MSBuildTasks.dll" />
<UsingTask TaskName="NCoverExplorer" AssemblyFile="$(NCoverDir)\Build Task Plugins\NCoverExplorer.MSBuildTasks.dll"/>
<!-- Specify the tests assemblies -->
<ItemGroup>
<TestAssemblies Include="$(BinDir)\library.Testing.dll" />
<CoverageAssemblies Include="$(BinDir)\library.dll" />
</ItemGroup>
<Target Name="Coverage">
<Message Text="Creating $(CoverageFilesDir)" />
<MakeDir Directories="$(CoverageFilesDir)"/>
<Message Text="##-------------------- Running Coverage Reports --------------------##" />
<Message Text="Coverage Assemblies #(TestAssemblies)" />
<!--Run NCover to gather coverage information-->
<NCover
ToolPath="$(NCoverDir)"
TestRunnerExe="$(GallioDir)\Gallio.Echo.exe"
TestRunnerArgs="%(TestAssemblies.FullPath)"
IncludeAssemblies="#(CoverageAssemblies)"
LogFile="$(CoverageFilesDir)\%(TestAssemblies.Filename)-ncover.log"
RegisterProfiler="false"
CoverageFile="$(CoverageFilesDir)\%(TestAssemblies.Filename)-coverage.xml"/>
<CreateItem Include="$(CoverageFilesDir)\*-coverage.xml">
<Output TaskParameter="Include" ItemName="CoverageReports"/>
</CreateItem>
<!--Generate coverage report-->
<NCoverExplorer
ToolPath="$(NCoverDir)"
ProjectName="Library Coverage"
ReportType="4"
Sort="CoveragePercentageAscending"
Filter="None"
OutputDir="$(CoverageDir)"
XmlReportName="CoverageReport.xml"
HtmlReportName="CoverageReport.html"
ShowExcluded="True"
SatisfactoryCoverage="15"
FailMinimum="False"
CoverageFiles="#(CoverageReports)"/>
<!-- In case one of the tests fails, make sure to stop TypeMock and unregister NCover. -->
<OnError ExecuteTargets="test-finally"/>
</Target>
<!-- Stopping unregistering NCover is a separate target because it has to happen -->
<!-- regardless of success or failure of the unit tests. Like the "finally" in a "try/finally" block. -->
<Target Name="test-finally">
<Exec Command="regsvr32 /u /s "$(NCoverDir)\CoverLib.dll"" ContinueOnError="true"/>
</Target>
</Project>

Related

How to set a configuration parameter using service messages in the project file

Description
TeamCity side: I defined a Configuration Parameter named major.minor.patch with an empty value. This can be accessed in TeamCity build steps as %major.minor.patch%
Visual Studio side:
In my .csproj file I added the code below to set major.minor.patch to the current version of my assembly:
<PropertyGroup Condition="'$(TEAMCITY_BUILD_PROPERTIES_FILE)' == ''">
<TeamCityBuild>true</TeamCityBuild>
</PropertyGroup>
<!-- Other stuff... -->
<Target Name="TeamCity" AfterTargets="Build" Condition="'$(TeamCityBuild)' == 'true'">
<GetAssemblyIdentity AssemblyFiles="obj\$(ConfigurationName)\$(TargetFileName)">
<Output TaskParameter="Assemblies" ItemName="AssemblyItentity"/>
</GetAssemblyIdentity>
<Message Text="##teamcity[setParameter name='major.minor.patch' value='%(AssemblyIdentity.Version)'"/>
Problem
However, this code seems not working since the Parameter is still empty.
I also tried defining my parameter as an environment variable: env.maor.minor.patch, still no chance.
Question
How can I do this?
How to set a Configuration Parameter in TeamCity to my assembly version in my C# project?
Try setting the Importance of the Message to High
Message Text="##teamcity[setParameter name='major.minor.patch' value='%(AssemblyIdentity.Version)'" Importance="high" />

How does the MSBuild:Compile generator work

I'm trying to use the MSBuild:Compile generator to trigger a compilation of my custom file type when the file is saved in Visual Studio (should work like a custom tool but with msbuild). The build process itself is working but it doesn't seem to be triggered if the file is saved.
Can someone explain what exactly the MSBuild:Compile entry is doing? As far I have just seen this used in the antlr msbuild scripts and for XAML.
Below I have an extract of the msbuild setup I use to compile a *.myext file to a *.g.ts file.
My targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="SampleNamespace.CustomCompilerTask" AssemblyFile="MyTask.dll" />
<PropertyGroup>
<PrepareResourcesDependsOn>
CustomLayoutCompile;
$(PrepareResourcesDependsOn)
</PrepareResourcesDependsOn>
</PropertyGroup>
<ItemDefinitionGroup>
<CustomTypeCompile>
<Generator>MSBuild:Compile</Generator>
</CustomTypeCompile>
</ItemDefinitionGroup>
<Target Name="CustomLayoutCompile" Inputs="#(TypeScriptCompile);#(CustomTypeCompile)" Outputs="#(CustomTypeCompile->'%(RootDir)%(Directory)%(Filename).g.ts')">
<CustomCompilerTask TypeScriptFiles="#(TypeScriptCompile)" LayoutFiles="#(CustomTypeCompile)" />
</Target>
</Project>
Entries in the project file:
....
<ItemGroup>
<TypeScriptCompile Include="MyControl.ts">
<DependentUpon>MyControl.myext</DependentUpon>
</TypeScriptCompile>
<TypeScriptCompile Include="MyControl.g.ts">
<DependentUpon>MyControl.myext</DependentUpon>
</TypeScriptCompile>
</ItemGroup>
<ItemGroup>
<CustomTypeCompile Include="MyControl.myext">
<Generator>MSBuild:Compile</Generator>
</CustomTypeCompile>
</ItemGroup>
....
<Import Project="path/to/my/target/file/mytargets.targets" />
....
I am using VS 2019 and was suffering from the same exact issue. I finally figured out a workaround, which make the issue looks more like a VisualStudio/MSBuild bug to me. My workaround is that when you define your ItemDefinitionGroup, define a custom/extended file property page like below. It worked for me. Hope it also works for everyone else too.
<ItemGroup>
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)CustomPerperties.CSharp.xml">
<Context>File;BrowseObject</Context>
</PropertyPageSchema>
<AvailableItemName Include="CustomTypeCompile" />
</ItemGroup>

Remove stylecop msbuild integration

I have a solution with about 80 projects in it. Each project has StyleCop MSBuild integration enabled:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(ProgramFiles)\MSBuild\Microsoft\StyleCop\v4.4\Microsoft.StyleCop.targets" />
I need to remove this from ALL projects. is there any way short of checking out and hand-editing each and every .csproj file?
The StyleCop target, as defined in Microsoft.StyleCop.targets is conditional on property StyleCopEnabled.
Here is a snippet from Microsoft.StyleCop.targets:
<!-- Define target: StyleCop -->
<Target Name="StyleCop" Condition="'$(StyleCopEnabled)' != 'false'">
<Message Text="Forcing full StyleCop reanalysis." Condition="'$(StyleCopForceFullAnalysis)' == 'true'" Importance="Low" />
... snip ...
The quick way to disable stylecop is to pass global setting for StyleCopEnabled. E.g. if you are building from command line, the command would be: msbuild MyProject.proj /p:StyleCopEnabled=false
I had a similiar problem, still manual editing, but this worked for me. Opened all of the .csproj file in notepad by searching in the directory of the projects, then selecting all and "Edit with Notepad++". Then do a Find-Replace with "Find All in All Open Documents" to remove the import line:
<Import Project="$(ProjectDir)\..\StyleCop.targets" Condition=" '$(OS)' == 'Windows_NT' " />

Detect the user triggering the build in VS2012 using MSBuild

I am using VS2012, I have a target but i only want it to execute it when it equals a particular value. Now the value i would like to use is the "requested by" description in TFS 2010 which indicates the user that raised the build.
But i do not know yet how to detect the user who raised the build in the MSBuild framework.
Does anyone know how i could go about implementing this? I have a looked in the Well Known Metadata of MSBuild and it doesn't contain the metadata i would like.
Below is my target:
<Target Name="AfterBuild" Condition="Release">
<Message Text="..RUNNING TESTS.." />
<PropertyGroup>
<TestSuccessOrNot>1</TestSuccessOrNot>
</PropertyGroup>
<Exec Command='"C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\MSTest.exe" /testcontainer:C:\\Tests\\MyUnitTests.dll /test:T5278 /detail:testname '>
<Output TaskParameter="ExitCode" PropertyName="TestSuccessOrNot" />
</Exec>
<Error Condition="$(TestSuccessOrNot) == 1" Text="Unit tests fail!" />
Ideally i would like to pass that value(user that raised the build) into the Condition attribute of my target.
Any ideas?
Kind Regards,
The properties are RequestedBy and RequestedFor, but not sure when they differ. The condition should look like Condition=" '$(RequestedBy)' == 'anyUserName'" For users this will be the display name, while for triggered builds it will be [CollectionName]\Project Collection Service Accounts

How to integrate conditional logic in postbuild events

Hi I have a visual studio project which includes postbuildevents in the following form:
MyTool.exe $(ProjectDir)somesrcfile.txt $(TargetDir)sometargetfile.bin
Now I want to add some logic saying that these steps are taking place only if the files have changed. In peudocode:
if (somesrcfile.txt is newer than sometargetfile.bin)
{
MyTool.exe $(ProjectDir)somesrcfile.txt $(TargetDir)sometargetfile.bin
}
Can I do this with MsBuild?
EDIT:
I just tried it with a simple copy command but it seems not to work. Also the message is not displayed when I build the solution.
<ItemGroup>
<MyTextFile Include="*.txt" />
</ItemGroup>
<Target Name="Build" Inputs="#(MyTextFile)" Outputs="#(MyTextFile->'%(Filename).bin')">
<CustomBuild>
<Message>Encoding files...</Message>
<Command>
copy %(Identity) %(Filename).bin
</Command>
<Outputs>$(OutDir)%(Identity)</Outputs>
</CustomBuild>
</Target>
Yes, it is possible by using the Inputs and Outputs attributes on your target.
See: How to: Build incrementally
In your case, it would look something like this:
<Target Name="AfterBuild" DependsOnTargets="Test">
</Target>
<ItemGroup>
<MyTextFile Include="*.txt" />
</ItemGroup>
<Target Name="Test" Inputs="#(MyTextFile)" Outputs="#(MyTextFile->'%(FileName).bin')">
<Message Text="Copying #(MyTextFile)" Importance="high"/>
<Copy SourceFiles="#(MyTextFile)" DestinationFiles="#(MyTextFile->'%(FileName).bin')" />
</Target>
This target will only run if input files are newer than output files.

Categories