Skip to content

Coverlet Intergration with VSTest #410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.proj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<RemoveDir Directories="$(OutputPath)" Condition="Exists('$(MSBuildThisFileDirectory)\build')" />
<Exec Command="dotnet build &quot;$(MSBuildThisFileDirectory)src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj&quot; -c $(Configuration)" />
<Exec Command="dotnet build &quot;$(MSBuildThisFileDirectory)src\coverlet.console\coverlet.console.csproj&quot; -c $(Configuration)" />
<Exec Command="dotnet build &quot;$(MSBuildThisFileDirectory)src\coverlet.collector\coverlet.collector.csproj&quot; -c $(Configuration)" />
</Target>

<Target Name="PublishMSBuildTaskProject" AfterTargets="BuildAllProjects">
Expand All @@ -25,11 +26,13 @@

<Target Name="RunTests" AfterTargets="CopyMSBuildScripts">
<Exec Command="dotnet test &quot;$(MSBuildThisFileDirectory)test\coverlet.core.tests\coverlet.core.tests.csproj&quot; -c $(Configuration) /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Include=[coverlet.*]*"/>
<Exec Command="dotnet test &quot;$(MSBuildThisFileDirectory)test\coverlet.collector.tests\coverlet.collector.tests.csproj&quot; -c $(Configuration) /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Include=[coverlet.*]*"/>
</Target>

<Target Name="CreateNuGetPackage" AfterTargets="RunTests" Condition="$(Configuration) == 'Release'">
<Exec Command="dotnet pack &quot;$(MSBuildThisFileDirectory)src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj&quot; -c $(Configuration) -o $(OutputPath)" />
<Exec Command="dotnet pack &quot;$(MSBuildThisFileDirectory)src\coverlet.console\coverlet.console.csproj&quot; -c $(Configuration) -o $(OutputPath)" />
<Exec Command="dotnet pack &quot;$(MSBuildThisFileDirectory)src\coverlet.collector\coverlet.collector.csproj&quot; -c $(Configuration) -p:NuspecFile=&quot;$(MSBuildThisFileDirectory)src\coverlet.collector\coverlet.collector.nuspec&quot; -o $(OutputPath)" />
</Target>

</Project>
14 changes: 14 additions & 0 deletions coverlet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.testsubject", "tes
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.performancetest", "test\coverlet.core.performancetest\coverlet.core.performancetest.csproj", "{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector", "src\coverlet.collector\coverlet.collector.csproj", "{F5B2C45B-274B-43D6-9565-8B50659CFE56}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.collector.tests", "test\coverlet.collector.tests\coverlet.collector.tests.csproj", "{5ED4FA81-8F8C-4211-BA88-7573BD63262E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -49,6 +53,14 @@ Global
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.Build.0 = Release|Any CPU
{F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.Build.0 = Release|Any CPU
{5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -60,6 +72,8 @@ Global
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
{AE117FAA-C21D-4F23-917E-0C8050614750} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{F5B2C45B-274B-43D6-9565-8B50659CFE56} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
{5ED4FA81-8F8C-4211-BA88-7573BD63262E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10}
Expand Down
168 changes: 168 additions & 0 deletions src/coverlet.collector/DataCollection/AttachmentManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using System;
using System.ComponentModel;
using System.IO;
using coverlet.collector.Resources;
using Coverlet.Collector.Utilities;
using Coverlet.Collector.Utilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;

namespace Coverlet.Collector.DataCollection
{
/// <summary>
/// Manages coverage report attachments
/// </summary>
internal class AttachmentManager : IDisposable
{
private readonly DataCollectionSink _dataSink;
private readonly TestPlatformEqtTrace _eqtTrace;
private readonly TestPlatformLogger _logger;
private readonly DataCollectionContext _dataCollectionContext;
private readonly IFileHelper _fileHelper;
private readonly IDirectoryHelper _directoryHelper;
private readonly string _reportFileName;
private readonly string _reportDirectory;

public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext dataCollectionContext, TestPlatformLogger logger, TestPlatformEqtTrace eqtTrace, string reportFileName)
: this(dataSink,
dataCollectionContext,
logger,
eqtTrace,
reportFileName,
Guid.NewGuid().ToString(),
new FileHelper(),
new DirectoryHelper())
{
}

public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext dataCollectionContext, TestPlatformLogger logger, TestPlatformEqtTrace eqtTrace, string reportFileName, string reportDirectoryName, IFileHelper fileHelper, IDirectoryHelper directoryHelper)
{
// Store input variabless
_dataSink = dataSink;
_dataCollectionContext = dataCollectionContext;
_logger = logger;
_eqtTrace = eqtTrace;
_reportFileName = reportFileName;
_fileHelper = fileHelper;
_directoryHelper = directoryHelper;

// Report directory to store the coverage reports.
_reportDirectory = Path.Combine(Path.GetTempPath(), reportDirectoryName);

// Register events
_dataSink.SendFileCompleted += this.OnSendFileCompleted;
}

/// <summary>
/// Sends coverage report to test platform
/// </summary>
/// <param name="coverageReport">Coverage report</param>
public void SendCoverageReport(string coverageReport)
{
// Save coverage report to file
string coverageReportPath = this.SaveCoverageReport(coverageReport);

// Send coverage attachment to test platform.
this.SendAttachment(coverageReportPath);
}

/// <summary>
/// Disposes attachment manager
/// </summary>
public void Dispose()
{
// Unregister events
try
{
if (_dataSink != null)
{
_dataSink.SendFileCompleted -= this.OnSendFileCompleted;
}
this.CleanupReportDirectory();
}
catch (Exception ex)
{
_logger.LogWarning(ex.ToString());
}
}

/// <summary>
/// Saves coverage report to file system
/// </summary>
/// <param name="report">Coverage report</param>
/// <returns>Coverage report file path</returns>
private string SaveCoverageReport(string report)
{
try
{
_directoryHelper.CreateDirectory(_reportDirectory);
string filePath = Path.Combine(_reportDirectory, _reportFileName);
_fileHelper.WriteAllText(filePath, report);
_eqtTrace.Info("{0}: Saved coverage report to path: '{1}'", CoverletConstants.DataCollectorName, filePath);

return filePath;
}
catch (Exception ex)
{
string errorMessage = string.Format(Resources.FailedToSaveCoverageReport, CoverletConstants.DataCollectorName, _reportFileName, _reportDirectory);
throw new CoverletDataCollectorException(errorMessage, ex);
}
}

/// <summary>
/// SendFileCompleted event handler
/// </summary>
/// <param name="sender">Sender</param>
/// <param name="e">Event args</param>
public void OnSendFileCompleted(object sender, AsyncCompletedEventArgs e)
{
try
{
_eqtTrace.Verbose("{0}: SendFileCompleted received", CoverletConstants.DataCollectorName);
this.CleanupReportDirectory();
}
catch (Exception ex)
{
_logger.LogWarning(ex.ToString());
this.Dispose();
}
}

/// <summary>
/// Sends attachment file to test platform
/// </summary>
/// <param name="attachmentPath">Attachment file path</param>
private void SendAttachment(string attachmentPath)
{
if (_fileHelper.Exists(attachmentPath))
{
// Send coverage attachment to test platform.
_eqtTrace.Verbose("{0}: Sending attachment to test platform", CoverletConstants.DataCollectorName);
_dataSink.SendFileAsync(_dataCollectionContext, attachmentPath, false);
}
else
{
_eqtTrace.Warning("{0}: Attachment file does not exist", CoverletConstants.DataCollectorName);
}
}

/// <summary>
/// Cleans up coverage report directory
/// </summary>
private void CleanupReportDirectory()
{
try
{
if (_directoryHelper.Exists(_reportDirectory))
{
_directoryHelper.Delete(_reportDirectory, true);
_eqtTrace.Verbose("{0}: Deleted report directory: '{1}'", CoverletConstants.DataCollectorName, _reportDirectory);
}
}
catch (Exception ex)
{
string errorMessage = string.Format(Resources.FailedToCleanupReportDirectory, CoverletConstants.DataCollectorName, _reportDirectory);
throw new CoverletDataCollectorException(errorMessage, ex);
}
}
}
}
106 changes: 106 additions & 0 deletions src/coverlet.collector/DataCollection/CoverageManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System;
using coverlet.collector.Resources;
using Coverlet.Collector.Utilities;
using Coverlet.Collector.Utilities.Interfaces;
using Coverlet.Core;
using Coverlet.Core.Logging;
using Coverlet.Core.Reporters;

namespace Coverlet.Collector.DataCollection
{
/// <summary>
/// Manages coverlet coverage
/// </summary>
internal class CoverageManager
{
private readonly Coverage _coverage;

private ICoverageWrapper _coverageWrapper;

public IReporter Reporter { get; }

public CoverageManager(CoverletSettings settings, TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, ICoverageWrapper coverageWrapper)
: this(settings,
new ReporterFactory(settings.ReportFormat).CreateReporter(),
new CoverletLogger(eqtTrace, logger),
coverageWrapper)
{
}

public CoverageManager(CoverletSettings settings, IReporter reporter, ILogger logger, ICoverageWrapper coverageWrapper)
{
// Store input vars
Reporter = reporter;
_coverageWrapper = coverageWrapper;

// Coverage object
_coverage = _coverageWrapper.CreateCoverage(settings, logger);
}

/// <summary>
/// Instrument modules
/// </summary>
public void InstrumentModules()
{
try
{
// Instrument modules
_coverageWrapper.PrepareModules(_coverage);
}
catch (Exception ex)
{
string errorMessage = string.Format(Resources.InstrumentationException, CoverletConstants.DataCollectorName);
throw new CoverletDataCollectorException(errorMessage, ex);
}
}

/// <summary>
/// Gets coverlet coverage report
/// </summary>
/// <returns>Coverage report</returns>
public string GetCoverageReport()
{
// Get coverage result
CoverageResult coverageResult = this.GetCoverageResult();

// Get coverage report in default format
string coverageReport = this.GetCoverageReport(coverageResult);
return coverageReport;
}

/// <summary>
/// Gets coverlet coverage result
/// </summary>
/// <returns>Coverage result</returns>
private CoverageResult GetCoverageResult()
{
try
{
return _coverageWrapper.GetCoverageResult(_coverage);
}
catch (Exception ex)
{
string errorMessage = string.Format(Resources.CoverageResultException, CoverletConstants.DataCollectorName);
throw new CoverletDataCollectorException(errorMessage, ex);
}
}

/// <summary>
/// Gets coverage report from coverage result
/// </summary>
/// <param name="coverageResult">Coverage result</param>
/// <returns>Coverage report</returns>
private string GetCoverageReport(CoverageResult coverageResult)
{
try
{
return Reporter.Report(coverageResult);
}
catch (Exception ex)
{
string errorMessage = string.Format(Resources.CoverageReportException, CoverletConstants.DataCollectorName);
throw new CoverletDataCollectorException(errorMessage, ex);
}
}
}
}
54 changes: 54 additions & 0 deletions src/coverlet.collector/DataCollection/CoverageWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Coverlet.Collector.Utilities.Interfaces;
using Coverlet.Core;
using Coverlet.Core.Logging;

namespace Coverlet.Collector.DataCollection
{
/// <summary>
/// Implementation for wrapping over Coverage class in coverlet.core
/// </summary>
internal class CoverageWrapper : ICoverageWrapper
{
/// <summary>
/// Creates a coverage object from given coverlet settings
/// </summary>
/// <param name="settings">Coverlet settings</param>
/// <param name="coverletLogger">Coverlet logger</param>
/// <returns>Coverage object</returns>
public Coverage CreateCoverage(CoverletSettings settings, ILogger coverletLogger)
{
return new Coverage(
settings.TestModule,
settings.IncludeFilters,
settings.IncludeDirectories,
settings.ExcludeFilters,
settings.ExcludeSourceFiles,
settings.ExcludeAttributes,
settings.IncludeTestAssembly,
settings.SingleHit,
settings.MergeWith,
settings.UseSourceLink,
coverletLogger);
}

/// <summary>
/// Gets the coverage result from provided coverage object
/// </summary>
/// <param name="coverage">Coverage</param>
/// <returns>The coverage result</returns>
public CoverageResult GetCoverageResult(Coverage coverage)
{
return coverage.GetCoverageResult();
}

/// <summary>
/// Prepares modules for getting coverage.
/// Wrapper over coverage.PrepareModules
/// </summary>
/// <param name="coverage"></param>
public void PrepareModules(Coverage coverage)
{
coverage.PrepareModules();
}
}
}
Loading