Skip to content

Commit c405f68

Browse files
tasadar2dbmeneses
authored andcommitted
Added support for visual studio 15 code coverage tool (#321)
* Added setup configuration nuget package * Added factory to assist with instantiating a setup configuration query object * Added a few diagnostic logs * Injection of setup configuration factory * Added setup configuration as primary option to resolve code coverage tool path If this fails, the registry method is used as a backup Updated diagnostic logs to illustrate what paths are taken * Added coverage tool path to vs path * formatting
1 parent fbaf1a0 commit c405f68

File tree

7 files changed

+188
-3
lines changed

7 files changed

+188
-3
lines changed

SonarQube.TeamBuild.Integration/CoverageReportConverter.cs

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* along with this program; if not, write to the Free Software Foundation,
1818
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1919
*/
20-
20+
2121
using Microsoft.Win32;
2222
using SonarQube.Common;
2323
using System;
@@ -26,12 +26,14 @@
2626
using System.IO;
2727
using System.Linq;
2828
using System.Text.RegularExpressions;
29+
using Microsoft.VisualStudio.Setup.Configuration;
2930

3031
namespace SonarQube.TeamBuild.Integration
3132
{
3233
public class CoverageReportConverter : ICoverageReportConverter
3334
{
3435
private const int ConversionTimeoutInMs = 60000;
36+
private readonly IVisualStudioSetupConfigurationFactory setupConfigurationFactory;
3537

3638
/// <summary>
3739
/// Registry containing information about installed VS versions
@@ -43,8 +45,26 @@ public class CoverageReportConverter : ICoverageReportConverter
4345
/// </summary>
4446
private const string TeamToolPathandExeName = @"Team Tools\Dynamic Code Coverage Tools\CodeCoverage.exe";
4547

48+
/// <summary>
49+
/// Code coverage package name for Visual Studio setup configuration
50+
/// </summary>
51+
private const string CodeCoverageInstallationPackage = "Microsoft.VisualStudio.TestTools.CodeCoverage";
52+
4653
private string conversionToolPath;
4754

55+
#region Public methods
56+
57+
public CoverageReportConverter()
58+
: this(new VisualStudioSetupConfigurationFactory())
59+
{ }
60+
61+
public CoverageReportConverter(IVisualStudioSetupConfigurationFactory setupConfigurationFactory)
62+
{
63+
this.setupConfigurationFactory = setupConfigurationFactory;
64+
}
65+
66+
#endregion Public methods
67+
4868
#region IReportConverter interface
4969

5070
public bool Initialize(ILogger logger)
@@ -94,11 +114,76 @@ public bool ConvertToXml(string inputFilePath, string outputFilePath, ILogger lo
94114

95115
#region Private methods
96116

97-
private static string GetExeToolPath(ILogger logger)
117+
private string GetExeToolPath(ILogger logger)
118+
{
119+
logger.LogDebug(Resources.CONV_DIAG_LocatingCodeCoverageTool);
120+
return GetExeToolPathFromSetupConfiguration(logger) ??
121+
GetExeToolPathFromRegistry(logger);
122+
}
123+
124+
#region Code Coverage Tool path from setup configuration
125+
126+
private string GetExeToolPathFromSetupConfiguration(ILogger logger)
98127
{
99128
string toolPath = null;
100129

101-
logger.LogDebug(Resources.CONV_DIAG_LocatingCodeCoverageTool);
130+
logger.LogDebug(Resources.CONV_DIAG_LocatingCodeCoverageToolSetupConfiguration);
131+
ISetupConfiguration configurationQuery = setupConfigurationFactory.GetSetupConfigurationQuery();
132+
if (configurationQuery != null)
133+
{
134+
IEnumSetupInstances instanceEnumerator = configurationQuery.EnumInstances();
135+
136+
int fetched;
137+
ISetupInstance[] tempInstance = new ISetupInstance[1];
138+
139+
List<ISetupInstance2> instances = new List<ISetupInstance2>();
140+
//Enumerate the configuration instances
141+
do
142+
{
143+
instanceEnumerator.Next(1, tempInstance, out fetched);
144+
if (fetched > 0)
145+
{
146+
ISetupInstance2 instance = (ISetupInstance2)tempInstance[0];
147+
if (instance.GetPackages().Any(p => p.GetId() == CodeCoverageInstallationPackage))
148+
{
149+
//Store instances that have code coverage package installed
150+
instances.Add((ISetupInstance2)tempInstance[0]);
151+
}
152+
}
153+
} while (fetched > 0);
154+
155+
if (instances.Count > 1)
156+
{
157+
logger.LogDebug(Resources.CONV_DIAG_MultipleVsVersionsInstalled, string.Join(", ", instances.Select(i => i.GetInstallationVersion())));
158+
}
159+
160+
//Get the installation path for the latest visual studio found
161+
var visualStudioPath = instances.OrderByDescending(i => i.GetInstallationVersion())
162+
.Select(i => i.GetInstallationPath())
163+
.FirstOrDefault();
164+
165+
if (visualStudioPath != null)
166+
{
167+
toolPath = Path.Combine(visualStudioPath, TeamToolPathandExeName);
168+
}
169+
}
170+
else
171+
{
172+
logger.LogDebug(Resources.CONV_DIAG_SetupConfigurationNotSupported);
173+
}
174+
175+
return toolPath;
176+
}
177+
178+
#endregion Code Coverage Tool path from setup configuration
179+
180+
#region Code Coverage Tool path from registry
181+
182+
private static string GetExeToolPathFromRegistry(ILogger logger)
183+
{
184+
string toolPath = null;
185+
186+
logger.LogDebug(Resources.CONV_DIAG_LocatingCodeCoverageToolRegistry);
102187
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(VisualStudioRegistryPath, false))
103188
{
104189
// i.e. no VS installed
@@ -192,6 +277,8 @@ private static double TryGetVersionAsDouble(string versionKey)
192277
return result;
193278
}
194279

280+
#endregion Code Coverage Tool path from registry
281+
195282
// was internal
196283
public static bool ConvertBinaryToXml(string converterExeFilePath, string inputBinaryFilePath, string outputXmlFilePath, ILogger logger)
197284
{
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.VisualStudio.Setup.Configuration;
2+
3+
namespace SonarQube.TeamBuild.Integration
4+
{
5+
public interface IVisualStudioSetupConfigurationFactory
6+
{
7+
/// <summary>
8+
/// Attempts to instantiate a queryable setup configuration object.
9+
/// </summary>
10+
/// <returns></returns>
11+
ISetupConfiguration GetSetupConfigurationQuery();
12+
}
13+
}

SonarQube.TeamBuild.Integration/Resources.Designer.cs

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SonarQube.TeamBuild.Integration/Resources.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,19 @@
123123
<data name="CONV_DIAG_LocatingCodeCoverageTool" xml:space="preserve">
124124
<value>Attempting to locate the CodeCoverage.exe tool...</value>
125125
</data>
126+
<data name="CONV_DIAG_LocatingCodeCoverageToolRegistry" xml:space="preserve">
127+
<value>Attempting to locate the CodeCoverage.exe tool using registry...</value>
128+
</data>
129+
<data name="CONV_DIAG_LocatingCodeCoverageToolSetupConfiguration" xml:space="preserve">
130+
<value>Attempting to locate the CodeCoverage.exe tool using setup configuration...</value>
131+
</data>
126132
<data name="CONV_DIAG_MultipleVsVersionsInstalled" xml:space="preserve">
127133
<value>Multiple versions of VS are installed: {0}</value>
128134
<comment>First parameter: a list of VS versions</comment>
129135
</data>
136+
<data name="CONV_DIAG_SetupConfigurationNotSupported" xml:space="preserve">
137+
<value>Visual Studio setup configuration was not found.</value>
138+
</data>
130139
<data name="CONV_ERROR_ConversionToolFailed" xml:space="preserve">
131140
<value>Failed to convert the downloaded code coverage tool to XML. No code coverage information will be uploaded to SonarQube.
132141
Check that the downloaded code coverage file ({0}) is valid by opening it in Visual Studio. If it is not, check that the internet security settings on the build machine allow files to be downloaded from the Team Foundation Server machine.</value>

SonarQube.TeamBuild.Integration/SonarQube.TeamBuild.Integration.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@
5656
<!-- End of Team Foundation references -->
5757
<!-- ***************************************************************** -->
5858
<ItemGroup>
59+
<Reference Include="Microsoft.VisualStudio.Setup.Configuration.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
60+
<HintPath>..\packages\Microsoft.VisualStudio.Setup.Configuration.Interop.1.11.2273\lib\net35\Microsoft.VisualStudio.Setup.Configuration.Interop.dll</HintPath>
61+
<EmbedInteropTypes>True</EmbedInteropTypes>
62+
</Reference>
5963
<Reference Include="System" />
6064
<Reference Include="System.Core" />
6165
<Reference Include="System.Net.Http" />
@@ -76,6 +80,7 @@
7680
<Compile Include="CoverageReportDownloader.cs" />
7781
<Compile Include="CoverageReportProcessorBase.cs" />
7882
<Compile Include="Interfaces\ITeamBuildSettings.cs" />
83+
<Compile Include="Interfaces\IVisualStudioSetupConfigurationFactory.cs" />
7984
<Compile Include="TfsLegacyCoverageReportProcessor.cs" />
8085
<Compile Include="CoverageReportUrlProvider.cs" />
8186
<Compile Include="Interfaces\ICoverageReportProcessor.cs" />
@@ -91,6 +96,7 @@
9196
<Compile Include="TeamBuildAnalysisSettings.cs" />
9297
<Compile Include="TeamBuildSettings.cs" />
9398
<Compile Include="TrxFileReader.cs" />
99+
<Compile Include="VisualStudioSetupConfigurationFactory.cs" />
94100
</ItemGroup>
95101
<ItemGroup>
96102
<ProjectReference Include="..\SonarQube.Common\SonarQube.Common.csproj">
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
using Microsoft.VisualStudio.Setup.Configuration;
4+
5+
namespace SonarQube.TeamBuild.Integration
6+
{
7+
public class VisualStudioSetupConfigurationFactory : IVisualStudioSetupConfigurationFactory
8+
{
9+
/// <summary>
10+
/// COM class not registered exception
11+
/// </summary>
12+
private const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154);
13+
14+
[DllImport("Microsoft.VisualStudio.Setup.Configuration.Native.dll", ExactSpelling = true, PreserveSig = true)]
15+
private static extern int GetSetupConfiguration([MarshalAs(UnmanagedType.Interface), Out] out ISetupConfiguration configuration, IntPtr reserved);
16+
17+
public ISetupConfiguration GetSetupConfigurationQuery()
18+
{
19+
ISetupConfiguration setupConfiguration = null;
20+
try
21+
{
22+
setupConfiguration = new SetupConfiguration();
23+
}
24+
catch (COMException ex) when (ex.HResult == REGDB_E_CLASSNOTREG)
25+
{
26+
//Attempt to access the native library
27+
try
28+
{
29+
ISetupConfiguration query;
30+
return GetSetupConfiguration(out query, IntPtr.Zero) < 0 ? null : query;
31+
}
32+
catch (DllNotFoundException)
33+
{
34+
//Setup configuration is not supported
35+
return null;
36+
}
37+
}
38+
39+
return setupConfiguration;
40+
}
41+
}
42+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3+
<package id="Microsoft.VisualStudio.Setup.Configuration.Interop" version="1.11.2273" targetFramework="net45" developmentDependency="true" />
34
<package id="SonarAnalyzer.CSharp" version="1.10.0" targetFramework="net45" developmentDependency="true" />
45
</packages>

0 commit comments

Comments
 (0)