Skip to content

Remove static Coverage object #409

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 3 commits into from
May 13, 2019
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
25 changes: 22 additions & 3 deletions src/coverlet.core/Coverage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,17 @@ public Coverage(string module,
_results = new List<InstrumenterResult>();
}

public void PrepareModules()
public Coverage(CoveragePrepareResult prepareResult, ILogger logger)
{
this._identifier = prepareResult.Identifier;
this._module = prepareResult.Module;
this._mergeWith = prepareResult.MergeWith;
this._useSourceLink = prepareResult.UseSourceLink;
this._results = new List<InstrumenterResult>(prepareResult.Results);
_logger = logger;
}

public CoveragePrepareResult PrepareModules()
{
string[] modules = InstrumentationHelper.GetCoverableModules(_module, _includeDirectories, _includeTestAssembly);
string[] excludes = InstrumentationHelper.GetExcludedFiles(_excludedSourceFiles);
Expand Down Expand Up @@ -101,6 +111,15 @@ public void PrepareModules()
}
}
}

return new CoveragePrepareResult()
{
Identifier = _identifier,
Module = _module,
MergeWith = _mergeWith,
UseSourceLink = _useSourceLink,
Results = _results.ToArray()
};
}

public CoverageResult GetCoverageResult()
Expand Down Expand Up @@ -245,7 +264,7 @@ private void CalculateCoverage()

if (hitLocation.isBranch)
{
var branch = document.Branches[(hitLocation.start, hitLocation.end)];
var branch = document.Branches[new BranchKey(hitLocation.start, hitLocation.end)];
branch.Hits += hits;
}
else
Expand All @@ -263,7 +282,7 @@ private void CalculateCoverage()
// we'll remove all MoveNext() not covered branch
foreach (var document in result.Documents)
{
List<KeyValuePair<(int, int), Branch>> branchesToRemove = new List<KeyValuePair<(int, int), Branch>>();
List<KeyValuePair<BranchKey, Branch>> branchesToRemove = new List<KeyValuePair<BranchKey, Branch>>();
foreach (var branch in document.Value.Branches)
{
//if one branch is covered we search the other one only if it's not covered
Expand Down
40 changes: 40 additions & 0 deletions src/coverlet.core/CoveragePrepareResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.IO;
using System.Text;

using Coverlet.Core.Instrumentation;
using Newtonsoft.Json;

namespace Coverlet.Core
{
public class CoveragePrepareResult
{
public string Identifier { get; set; }
public string Module { get; set; }
public string MergeWith { get; set; }
public bool UseSourceLink { get; set; }
public InstrumenterResult[] Results { get; set; }

public static CoveragePrepareResult Deserialize(Stream serializedInstrumentState)
{
var serializer = new JsonSerializer();
using (var sr = new StreamReader(serializedInstrumentState))
using (var jsonTextReader = new JsonTextReader(sr))
{
return serializer.Deserialize<CoveragePrepareResult>(jsonTextReader);
}
}

public static Stream Serialize(CoveragePrepareResult instrumentState)
{
var serializer = new JsonSerializer();
MemoryStream ms = new MemoryStream();
using (var sw = new StreamWriter(ms, Encoding.UTF8, 1024, true))
{
serializer.Serialize(sw, instrumentState);
sw.Flush();
ms.Position = 0;
return ms;
}
}
}
}
8 changes: 3 additions & 5 deletions src/coverlet.core/Instrumentation/Instrumenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,7 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
document.Lines.Add(i, new Line { Number = i, Class = method.DeclaringType.FullName, Method = method.FullName });
}

var entry = (false, document.Index, sequencePoint.StartLine, sequencePoint.EndLine);
_result.HitCandidates.Add(entry);
_result.HitCandidates.Add(new HitCandidate(false, document.Index, sequencePoint.StartLine, sequencePoint.EndLine));

return AddInstrumentationInstructions(method, processor, instruction, _result.HitCandidates.Count - 1);
}
Expand All @@ -392,7 +391,7 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
_result.Documents.Add(document.Path, document);
}

var key = (branchPoint.StartLine, (int)branchPoint.Ordinal);
BranchKey key = new BranchKey(branchPoint.StartLine, (int)branchPoint.Ordinal);
if (!document.Branches.ContainsKey(key))
{
document.Branches.Add(key,
Expand Down Expand Up @@ -422,8 +421,7 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
}
}

var entry = (true, document.Index, branchPoint.StartLine, (int)branchPoint.Ordinal);
_result.HitCandidates.Add(entry);
_result.HitCandidates.Add(new HitCandidate(true, document.Index, branchPoint.StartLine, (int)branchPoint.Ordinal));

return AddInstrumentationInstructions(method, processor, instruction, _result.HitCandidates.Count - 1);
}
Expand Down
74 changes: 65 additions & 9 deletions src/coverlet.core/Instrumentation/InstrumenterResult.cs
Original file line number Diff line number Diff line change
@@ -1,43 +1,99 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using Newtonsoft.Json;

namespace Coverlet.Core.Instrumentation
{
internal class Line
public class Line
{
public int Number;
public string Class;
public string Method;
public int Hits;
}

internal class Branch : Line
public class Branch : Line
{
public int Offset;
public int EndOffset;
public int Path;
public uint Ordinal;
}

internal class Document
public class BranchKeyConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return JsonConvert.DeserializeObject<BranchKey>(value.ToString());
}

public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(BranchKey);
}
}

[TypeConverter(typeof(BranchKeyConverter))]
public class BranchKey : IEquatable<BranchKey>
Copy link
Collaborator Author

@MarcoRossignoli MarcoRossignoli May 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tuple is not serializable by json serializer.

{
public BranchKey(int line, int ordinal) => (Line, Ordinal) = (line, ordinal);

public int Line { get; set; }
public int Ordinal { get; set; }

public override bool Equals(object obj) => Equals(obj);

public bool Equals(BranchKey other) => other is BranchKey branchKey && branchKey.Line == this.Line && branchKey.Ordinal == this.Ordinal;

public override int GetHashCode()
{
return (this.Line, this.Ordinal).GetHashCode();
}

public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}

public class Document
{
public Document()
{
Lines = new Dictionary<int, Line>();
Branches = new Dictionary<(int Line, int Ordinal), Branch>();
Branches = new Dictionary<BranchKey, Branch>();
}

public string Path;
public int Index;
public Dictionary<int, Line> Lines { get; private set; }
public Dictionary<(int Line, int Ordinal), Branch> Branches { get; private set; }
public Dictionary<BranchKey, Branch> Branches { get; private set; }
}

public class HitCandidate
{
public HitCandidate(bool isBranch, int docIndex, int start, int end) => (this.isBranch, this.docIndex, this.start, this.end) = (isBranch, docIndex, start, end);

public bool isBranch { get; set; }
public int docIndex { get; set; }
public int start { get; set; }
public int end { get; set; }
}

internal class InstrumenterResult
public class InstrumenterResult
{
public InstrumenterResult()
{
Documents = new Dictionary<string, Document>();
HitCandidates = new List<(bool isBranch, int docIndex, int start, int end)>();
HitCandidates = new List<HitCandidate>();
}

public string Module;
Expand All @@ -46,6 +102,6 @@ public InstrumenterResult()
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; }
public List<HitCandidate> HitCandidates { get; private set; }
}
}
}
16 changes: 15 additions & 1 deletion src/coverlet.msbuild.tasks/CoverageResultTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class CoverageResultTask : Task
private double _threshold;
private string _thresholdType;
private string _thresholdStat;
private ITaskItem _instrumenterState;
private MSBuildLogger _logger;

[Required]
Expand Down Expand Up @@ -55,6 +56,13 @@ public string ThresholdStat
set { _thresholdStat = value; }
}

[Required]
public ITaskItem InstrumenterState
{
get { return _instrumenterState; }
set { _instrumenterState = value; }
}

public CoverageResultTask()
{
_logger = new MSBuildLogger(Log);
Expand All @@ -66,7 +74,13 @@ public override bool Execute()
{
Console.WriteLine("\nCalculating coverage result...");

var coverage = InstrumentationTask.Coverage;
if (InstrumenterState is null || !File.Exists(InstrumenterState.ItemSpec))
{
_logger.LogError("Result of instrumentation task not found");
return false;
}

var coverage = new Coverage(CoveragePrepareResult.Deserialize(new FileStream(InstrumenterState.ItemSpec, FileMode.Open)), this._logger);
var result = coverage.GetCoverageResult();

var directory = Path.GetDirectoryName(_output);
Expand Down
27 changes: 19 additions & 8 deletions src/coverlet.msbuild.tasks/InstrumentationTask.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using Coverlet.Core;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
Expand All @@ -7,7 +8,6 @@ namespace Coverlet.MSbuild.Tasks
{
public class InstrumentationTask : Task
{
private static Coverage _coverage;
private string _path;
private string _include;
private string _includeDirectory;
Expand All @@ -18,13 +18,9 @@ public class InstrumentationTask : Task
private bool _singleHit;
private string _mergeWith;
private bool _useSourceLink;
private ITaskItem _instrumenterState;
private readonly MSBuildLogger _logger;

internal static Coverage Coverage
{
get { return _coverage; }
}

[Required]
public string Path
{
Expand Down Expand Up @@ -86,6 +82,13 @@ public bool UseSourceLink
set { _useSourceLink = value; }
}

[Output]
public ITaskItem InstrumenterState
{
get { return _instrumenterState; }
set { _instrumenterState = value; }
}

public InstrumentationTask()
{
_logger = new MSBuildLogger(Log);
Expand All @@ -101,8 +104,16 @@ public override bool Execute()
var excludedSourceFiles = _excludeByFile?.Split(',');
var excludeAttributes = _excludeByAttribute?.Split(',');

_coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _includeTestAssembly, _singleHit, _mergeWith, _useSourceLink, _logger);
_coverage.PrepareModules();
Coverage coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _includeTestAssembly, _singleHit, _mergeWith, _useSourceLink, _logger);
CoveragePrepareResult prepareResult = coverage.PrepareModules();
InstrumenterState = new TaskItem(System.IO.Path.GetTempFileName());
using (var instrumentedStateFile = new FileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write))
{
using (Stream serializedState = CoveragePrepareResult.Serialize(prepareResult))
{
serializedState.CopyTo(instrumentedStateFile);
}
}
}
catch (Exception ex)
{
Expand Down
13 changes: 9 additions & 4 deletions src/coverlet.msbuild.tasks/coverlet.msbuild.targets
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
IncludeTestAssembly="$(IncludeTestAssembly)"
SingleHit="$(SingleHit)"
MergeWith="$(MergeWith)"
UseSourceLink="$(UseSourceLink)" />
UseSourceLink="$(UseSourceLink)" >
<Output TaskParameter="InstrumenterState" PropertyName="InstrumenterState"/>
</Coverlet.MSbuild.Tasks.InstrumentationTask>
</Target>

<Target Name="InstrumentModulesAfterBuild" AfterTargets="BuildProject">
Expand All @@ -30,7 +32,9 @@
IncludeTestAssembly="$(IncludeTestAssembly)"
SingleHit="$(SingleHit)"
MergeWith="$(MergeWith)"
UseSourceLink="$(UseSourceLink)" />
UseSourceLink="$(UseSourceLink)" >
<Output TaskParameter="InstrumenterState" PropertyName="InstrumenterState"/>
</Coverlet.MSbuild.Tasks.InstrumentationTask>
</Target>

<Target Name="GenerateCoverageResult" AfterTargets="VSTest">
Expand All @@ -40,7 +44,8 @@
OutputFormat="$(CoverletOutputFormat)"
Threshold="$(Threshold)"
ThresholdType="$(ThresholdType)"
ThresholdStat="$(ThresholdStat)" />
ThresholdStat="$(ThresholdStat)"
InstrumenterState="$(InstrumenterState)"/>
</Target>

</Project>
</Project>