Recently I had a project where I was using Microsoft Visual Studio 2008 Team System for development and CruiseControl.NET for doing continuous integration. I
usually care about code coverage when running unit tests, so I decided to integrate the code profiling tool included in Visual Studio Team System as part of my build process, in order to produce a code coverage report with each build.
I figured that wouldn’t be too hard, all I had to do in my build script was to invoke Visual Studio’s test runner’s executable (usually found in C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\MSTest.exe) passing an option to enable code coverage profiling, and grab the output in a file that CruiseControl.NET would later use to produce the build report.
However, I quickly found out that Visual Studio’s test runner produces code coverage output in a binary proprietary format while CruiseControl.NET uses XML in order to generate its reports. Ouch!
Luckily, Microsoft distributes a .NET API that can be used to convert the content of code coverage files produced by Visual Studio into XML. Pheeew!
The library is contained in the Microsoft.VisualStudio.Coverage.Analysis.dll assembly, which can be found in the Visual Studio 2008 Team System installation folder (usually in C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies). So all I had to do was to add an extra step in the build process to invoke that library and do the conversion.
Since I am using MSBuild to run the build, I encapsulated the code in an MSBuild task which you can find over here at the MSDN Code Gallery. There isn’t really much to it, the actual conversion is easily done in a couple of lines of code:
// You need to specify the directory containing // the binaries that have been profiled by MSTest CoverageInfoManager.SymPath = symbolsDirPath; CoverageInfoManager.ExePath = symbolsDirPath; // The input file is the binary output produced by MSTest CoverageInfo info = CoverageInfoManager.CreateInfoFromFile(inputFilePath); CoverageDS dataSet = info.BuildDataSet(null); dataSet.WriteXml(outputFilePath);
Then, the task can be invoked from the MSBuild script with:
<!-- Imports the task from the assembly --> <UsingTask TaskName="ConvertVSCoverageToXml" AssemblyFile="CI.MSBuild.Tasks.dll" /> <!-- The values of the 'OutputPath' and 'TestConfigName' variables must be the same as the arguments passed to MSTest.exe with the /resultsfile and /runconfig options --> <ConvertVSCoverageToXml
CoverageFiles="$(OutputPath)\$(TestConfigName)\In\$(ComputerName)\data.coverage"
SymbolsDirectory="$(OutputPath)\$(TestConfigName)\Out" OutputDirectory="$(OutputPath)" />
This could easily be achieved in much the same way with an NAnt task, if that’s your build tool of choice.
Download VSCoverageToXml MSBuild Task
/Enrico



14/10/2008 at 20:43
Nice!
10/02/2009 at 02:13
Thanks :-)
09/07/2009 at 19:33
This is prefect, but I am having a problem in TFS 2008 SP1.
With:
CoverageFiles=”$(OutputPath)\$(TestConfigName)\In\$(ComputerName)\data.coverage”
The:
$(OutputPath)\$(TestConfigName)
part seems to be resolving to nothing. I have found:
$(TestResultsRoot)
gets pretty close, but not quite there. There is a GUID that is used for the directory before the In directory. How do I get that as a MSBUILD property?
10/07/2009 at 16:05
I haven’t used the TFS Build Agent to run the builds, so I’m not sure about what path the $(TestResultsRoot) variable gets set to at runtime.
I manually wrote our MSBuild script which is run by CruiseControl.NET, and in that I explicitly set the output directory for the test results by invoking MSTest.exe with the /resultsfile:$(OutputPath)\mstest.log option.
The name of the test output directory relative to the $(OutputPath) path can be then defined in the Test Run Configuration File (*.testrunconfig). You could set it to a fixed value, for example “TestWithCoverage”, which will make the result in the path “$(OutputPath)\TestWithCoverage\In\$(ComputerName)\data.coverage”.
Hope this helps.
/Enrico
10/07/2009 at 17:43
I edited the task to get the GUID off of the .trx file. It wasn’t too difficult, I just thought there would be a MSBUILD property that would get me there without that extra work. Thanks for your help.
11/07/2009 at 13:32
I’m glad you found a solution :-)