Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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: 2 additions & 1 deletion src/coverlet.console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ static int Main(string[] args)
CommandOption includeDirectories = app.Option("--include-directory", "Include directories containing additional assemblies to be instrumented.", CommandOptionType.MultipleValue);
CommandOption excludeAttributes = app.Option("--exclude-by-attribute", "Attributes to exclude from code coverage.", CommandOptionType.MultipleValue);
CommandOption mergeWith = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue);
CommandOption useSourceLink = app.Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.", CommandOptionType.NoValue);

app.OnExecute(() =>
{
Expand All @@ -45,7 +46,7 @@ static int Main(string[] args)
if (!target.HasValue())
throw new CommandParsingException(app, "Target must be specified.");

Coverage coverage = new Coverage(module.Value, includeFilters.Values.ToArray(), includeDirectories.Values.ToArray(), excludeFilters.Values.ToArray(), excludedSourceFiles.Values.ToArray(), excludeAttributes.Values.ToArray(), mergeWith.Value());
Coverage coverage = new Coverage(module.Value, includeFilters.Values.ToArray(), includeDirectories.Values.ToArray(), excludeFilters.Values.ToArray(), excludedSourceFiles.Values.ToArray(), excludeAttributes.Values.ToArray(), mergeWith.Value(), useSourceLink.HasValue());
coverage.PrepareModules();

Process process = new Process();
Expand Down
65 changes: 58 additions & 7 deletions src/coverlet.core/Coverage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,38 @@
using Coverlet.Core.Symbols;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Coverlet.Core
{
public class Coverage
{
private string _module;
private string _identifier;
private string[] _excludeFilters;
private string[] _includeFilters;
private string[] _includeDirectories;
private string[] _excludeFilters;
private string[] _excludedSourceFiles;
private string _mergeWith;
private string[] _excludeAttributes;
private string _mergeWith;
private bool _useSourceLink;
private List<InstrumenterResult> _results;

public string Identifier
{
get { return _identifier; }
}

public Coverage(string module, string[] includeFilters, string[] includeDirectories, string[] excludeFilters, string[] excludedSourceFiles, string[] excludeAttributes, string mergeWith)
public Coverage(string module, string[] includeFilters, string[] includeDirectories, string[] excludeFilters, string[] excludedSourceFiles, string[] excludeAttributes, string mergeWith, bool useSourceLink)
{
_module = module;
_excludeFilters = excludeFilters;
_includeFilters = includeFilters;
_includeDirectories = includeDirectories ?? Array.Empty<string>();
_excludeFilters = excludeFilters;
_excludedSourceFiles = excludedSourceFiles;
_mergeWith = mergeWith;
_excludeAttributes = excludeAttributes;
_mergeWith = mergeWith;
_useSourceLink = useSourceLink;

_identifier = Guid.NewGuid().ToString();
_results = new List<InstrumenterResult>();
Expand Down Expand Up @@ -186,6 +189,15 @@ private void CalculateCoverage()
}

List<Document> documents = result.Documents.Values.ToList();
if (_useSourceLink && result.SourceLink != null)
{
var jObject = JObject.Parse(result.SourceLink)["documents"];
var sourceLinkDocuments = JsonConvert.DeserializeObject<Dictionary<string, string>>(jObject.ToString());
foreach (var document in documents)
{
document.Path = GetSourceLinkUrl(sourceLinkDocuments, document.Path);
}
}

using (var fs = new FileStream(result.HitsFilePath, FileMode.Open))
using (var br = new BinaryReader(fs))
Expand All @@ -199,9 +211,7 @@ private void CalculateCoverage()
for (int i = 0; i < hitCandidatesCount; ++i)
{
var hitLocation = result.HitCandidates[i];

var document = documentsList[hitLocation.docIndex];

int hits = br.ReadInt32();

if (hitLocation.isBranch)
Expand Down Expand Up @@ -248,5 +258,46 @@ private void CalculateCoverage()
InstrumentationHelper.DeleteHitsFile(result.HitsFilePath);
}
}

private string GetSourceLinkUrl(Dictionary<string, string> sourceLinkDocuments, string document)
{
if (sourceLinkDocuments.TryGetValue(document, out string url))
{
return url;
}

var keyWithBestMatch = string.Empty;
var relativePathOfBestMatch = string.Empty;

foreach (var sourceLinkDocument in sourceLinkDocuments)
{
string key = sourceLinkDocument.Key;
if (Path.GetFileName(key) != "*") continue;

string relativePath = Path.GetRelativePath(Path.GetDirectoryName(key), Path.GetDirectoryName(document));

if (relativePath.Contains("..")) continue;

if (relativePathOfBestMatch.Length == 0)
{
keyWithBestMatch = sourceLinkDocument.Key;
relativePathOfBestMatch = relativePath;
}

if (relativePath.Length < relativePathOfBestMatch.Length)
{
keyWithBestMatch = sourceLinkDocument.Key;
relativePathOfBestMatch = relativePath;
}
}

relativePathOfBestMatch = relativePathOfBestMatch == "." ? string.Empty : relativePathOfBestMatch;

string replacement = Path.Combine(relativePathOfBestMatch, Path.GetFileName(document));
replacement = replacement.Replace('\\', '/');

url = sourceLinkDocuments[keyWithBestMatch];
return url.Replace("*", replacement);
}
}
}
6 changes: 6 additions & 0 deletions src/coverlet.core/Instrumentation/Instrumenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ private void InstrumentModule()
var types = module.GetTypes();
AddCustomModuleTrackerToModule(module);

var sourceLinkDebugInfo = module.CustomDebugInformations.FirstOrDefault(c => c.Kind == CustomDebugInformationKind.SourceLink);
if (sourceLinkDebugInfo != null)
{
_result.SourceLink = ((SourceLinkDebugInformation)sourceLinkDebugInfo).Content;
}

foreach (TypeDefinition type in types)
{
var actualType = type.DeclaringType ?? type;
Expand Down
1 change: 1 addition & 0 deletions src/coverlet.core/Instrumentation/InstrumenterResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public InstrumenterResult()
public string Module;
public string HitsFilePath;
public string ModulePath;
public string SourceLink;
public Dictionary<string, Document> Documents { get; private set; }
public List<(bool isBranch, int docIndex, int start, int end)> HitCandidates { get; private set; }
}
Expand Down
4 changes: 3 additions & 1 deletion src/coverlet.core/coverlet.core.csproj
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyVersion>4.0.0</AssemblyVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Mono.Cecil" Version="0.10.1" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="System.Reflection.Metadata" Version="1.5.0" />
<PackageReference Include="System.Runtime.Extensions" Version="4.3.0" />
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="2.0.1" />
</ItemGroup>

Expand Down
35 changes: 21 additions & 14 deletions src/coverlet.msbuild.tasks/InstrumentationTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ public class InstrumentationTask : Task
{
private static Coverage _coverage;
private string _path;
private string _exclude;
private string _include;
private string _includeDirectory;
private string _exclude;
private string _excludeByFile;
private string _mergeWith;
private string _excludeByAttribute;
private string _mergeWith;
private bool _useSourceLink;

internal static Coverage Coverage
{
Expand All @@ -28,12 +29,6 @@ public string Path
set { _path = value; }
}

public string Exclude
{
get { return _exclude; }
set { _exclude = value; }
}

public string Include
{
get { return _include; }
Expand All @@ -46,35 +41,47 @@ public string IncludeDirectory
set { _includeDirectory = value; }
}

public string Exclude
{
get { return _exclude; }
set { _exclude = value; }
}

public string ExcludeByFile
{
get { return _excludeByFile; }
set { _excludeByFile = value; }
}

public string ExcludeByAttribute
{
get { return _excludeByAttribute; }
set { _excludeByAttribute = value; }
}

public string MergeWith
{
get { return _mergeWith; }
set { _mergeWith = value; }
}

public string ExcludeByAttribute
public bool UseSourceLink
{
get { return _excludeByAttribute; }
set { _excludeByAttribute = value; }
get { return _useSourceLink; }
set { _useSourceLink = value; }
}

public override bool Execute()
{
try
{
var excludedSourceFiles = _excludeByFile?.Split(',');
var excludeFilters = _exclude?.Split(',');
var includeFilters = _include?.Split(',');
var includeDirectories = _includeDirectory?.Split(',');
var excludeFilters = _exclude?.Split(',');
var excludedSourceFiles = _excludeByFile?.Split(',');
var excludeAttributes = _excludeByAttribute?.Split(',');

_coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _mergeWith);
_coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _mergeWith, _useSourceLink);
_coverage.PrepareModules();
}
catch (Exception ex)
Expand Down
3 changes: 2 additions & 1 deletion src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyVersion>2.3.0</AssemblyVersion>
</PropertyGroup>

Expand Down
9 changes: 5 additions & 4 deletions src/coverlet.msbuild/coverlet.msbuild.props
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CollectCoverage Condition="$(CollectCoverage) == ''">false</CollectCoverage>
<CoverletOutputFormat Condition="$(CoverletOutputFormat) == ''">json</CoverletOutputFormat>
<CoverletOutput Condition="$(CoverletOutput) == ''">$([MSBuild]::EnsureTrailingSlash('$(MSBuildProjectDirectory)'))</CoverletOutput>
<Include Condition="$(Include) == ''"></Include>
<IncludeDirectory Condition="$(IncludeDirectory) == ''"></IncludeDirectory>
<Exclude Condition="$(Exclude) == ''"></Exclude>
<ExcludeByFile Condition="$(ExcludeByFile) == ''"></ExcludeByFile>
<ExcludeByAttribute Condition="$(ExcludeByAttribute) == ''"></ExcludeByAttribute>
<MergeWith Condition="$(MergeWith) == ''"></MergeWith>
<UseSourceLink Condition="$(UseSourceLink) == ''">false</UseSourceLink>
<CoverletOutputFormat Condition="$(CoverletOutputFormat) == ''">json</CoverletOutputFormat>
<CoverletOutput Condition="$(CoverletOutput) == ''">$([MSBuild]::EnsureTrailingSlash('$(MSBuildProjectDirectory)'))</CoverletOutput>
<Threshold Condition="$(Threshold) == ''">0</Threshold>
<ThresholdType Condition="$(ThresholdType) == ''">line,branch,method</ThresholdType>
<IncludeDirectory Condition="$(IncludeDirectory) == ''"></IncludeDirectory>
<ExcludeByAttribute Condition="$(ExcludeByAttribute) == ''"></ExcludeByAttribute>
</PropertyGroup>
</Project>
10 changes: 6 additions & 4 deletions src/coverlet.msbuild/coverlet.msbuild.targets
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,27 @@
<Target Name="InstrumentModulesNoBuild" BeforeTargets="VSTest">
<Coverlet.MSbuild.Tasks.InstrumentationTask
Condition="'$(VSTestNoBuild)' == 'true' and $(CollectCoverage) == 'true'"
Path="$(TargetPath)"
Include="$(Include)"
IncludeDirectory="$(IncludeDirectory)"
Exclude="$(Exclude)"
ExcludeByFile="$(ExcludeByFile)"
MergeWith="$(MergeWith)"
ExcludeByAttribute="$(ExcludeByAttribute)"
Path="$(TargetPath)" />
MergeWith="$(MergeWith)"
UseSourceLink="$(UseSourceLink)" />
</Target>

<Target Name="InstrumentModulesAfterBuild" AfterTargets="BuildProject">
<Coverlet.MSbuild.Tasks.InstrumentationTask
Condition="'$(VSTestNoBuild)' != 'true' and $(CollectCoverage) == 'true'"
Path="$(TargetPath)"
Include="$(Include)"
IncludeDirectory="$(IncludeDirectory)"
Exclude="$(Exclude)"
ExcludeByFile="$(ExcludeByFile)"
MergeWith="$(MergeWith)"
ExcludeByAttribute="$(ExcludeByAttribute)"
Path="$(TargetPath)" />
MergeWith="$(MergeWith)"
UseSourceLink="$(UseSourceLink)" />
</Target>

<Target Name="GenerateCoverageResult" AfterTargets="VSTest">
Expand Down
2 changes: 1 addition & 1 deletion test/coverlet.core.tests/CoverageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void TestCoverage()
// Since Coverage only instruments dependancies, we need a fake module here
var testModule = Path.Combine(directory.FullName, "test.module.dll");

var coverage = new Coverage(testModule, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), string.Empty);
var coverage = new Coverage(testModule, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), string.Empty, false);
coverage.PrepareModules();

var result = coverage.GetCoverageResult();
Expand Down