Skip to content

Commit 09f91f2

Browse files
authored
Merge pull request #409 from MarcoRossignoli/removestatics2
Remove static Coverage object
2 parents bb1f388 + 0e0f65f commit 09f91f2

File tree

7 files changed

+173
-30
lines changed

7 files changed

+173
-30
lines changed

src/coverlet.core/Coverage.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,17 @@ public Coverage(string module,
6161
_results = new List<InstrumenterResult>();
6262
}
6363

64-
public void PrepareModules()
64+
public Coverage(CoveragePrepareResult prepareResult, ILogger logger)
65+
{
66+
this._identifier = prepareResult.Identifier;
67+
this._module = prepareResult.Module;
68+
this._mergeWith = prepareResult.MergeWith;
69+
this._useSourceLink = prepareResult.UseSourceLink;
70+
this._results = new List<InstrumenterResult>(prepareResult.Results);
71+
_logger = logger;
72+
}
73+
74+
public CoveragePrepareResult PrepareModules()
6575
{
6676
string[] modules = InstrumentationHelper.GetCoverableModules(_module, _includeDirectories, _includeTestAssembly);
6777
string[] excludes = InstrumentationHelper.GetExcludedFiles(_excludedSourceFiles);
@@ -101,6 +111,15 @@ public void PrepareModules()
101111
}
102112
}
103113
}
114+
115+
return new CoveragePrepareResult()
116+
{
117+
Identifier = _identifier,
118+
Module = _module,
119+
MergeWith = _mergeWith,
120+
UseSourceLink = _useSourceLink,
121+
Results = _results.ToArray()
122+
};
104123
}
105124

106125
public CoverageResult GetCoverageResult()
@@ -245,7 +264,7 @@ private void CalculateCoverage()
245264

246265
if (hitLocation.isBranch)
247266
{
248-
var branch = document.Branches[(hitLocation.start, hitLocation.end)];
267+
var branch = document.Branches[new BranchKey(hitLocation.start, hitLocation.end)];
249268
branch.Hits += hits;
250269
}
251270
else
@@ -263,7 +282,7 @@ private void CalculateCoverage()
263282
// we'll remove all MoveNext() not covered branch
264283
foreach (var document in result.Documents)
265284
{
266-
List<KeyValuePair<(int, int), Branch>> branchesToRemove = new List<KeyValuePair<(int, int), Branch>>();
285+
List<KeyValuePair<BranchKey, Branch>> branchesToRemove = new List<KeyValuePair<BranchKey, Branch>>();
267286
foreach (var branch in document.Value.Branches)
268287
{
269288
//if one branch is covered we search the other one only if it's not covered
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.IO;
2+
using System.Text;
3+
4+
using Coverlet.Core.Instrumentation;
5+
using Newtonsoft.Json;
6+
7+
namespace Coverlet.Core
8+
{
9+
public class CoveragePrepareResult
10+
{
11+
public string Identifier { get; set; }
12+
public string Module { get; set; }
13+
public string MergeWith { get; set; }
14+
public bool UseSourceLink { get; set; }
15+
public InstrumenterResult[] Results { get; set; }
16+
17+
public static CoveragePrepareResult Deserialize(Stream serializedInstrumentState)
18+
{
19+
var serializer = new JsonSerializer();
20+
using (var sr = new StreamReader(serializedInstrumentState))
21+
using (var jsonTextReader = new JsonTextReader(sr))
22+
{
23+
return serializer.Deserialize<CoveragePrepareResult>(jsonTextReader);
24+
}
25+
}
26+
27+
public static Stream Serialize(CoveragePrepareResult instrumentState)
28+
{
29+
var serializer = new JsonSerializer();
30+
MemoryStream ms = new MemoryStream();
31+
using (var sw = new StreamWriter(ms, Encoding.UTF8, 1024, true))
32+
{
33+
serializer.Serialize(sw, instrumentState);
34+
sw.Flush();
35+
ms.Position = 0;
36+
return ms;
37+
}
38+
}
39+
}
40+
}

src/coverlet.core/Instrumentation/Instrumenter.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,7 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
377377
document.Lines.Add(i, new Line { Number = i, Class = method.DeclaringType.FullName, Method = method.FullName });
378378
}
379379

380-
var entry = (false, document.Index, sequencePoint.StartLine, sequencePoint.EndLine);
381-
_result.HitCandidates.Add(entry);
380+
_result.HitCandidates.Add(new HitCandidate(false, document.Index, sequencePoint.StartLine, sequencePoint.EndLine));
382381

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

395-
var key = (branchPoint.StartLine, (int)branchPoint.Ordinal);
394+
BranchKey key = new BranchKey(branchPoint.StartLine, (int)branchPoint.Ordinal);
396395
if (!document.Branches.ContainsKey(key))
397396
{
398397
document.Branches.Add(key,
@@ -422,8 +421,7 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
422421
}
423422
}
424423

425-
var entry = (true, document.Index, branchPoint.StartLine, (int)branchPoint.Ordinal);
426-
_result.HitCandidates.Add(entry);
424+
_result.HitCandidates.Add(new HitCandidate(true, document.Index, branchPoint.StartLine, (int)branchPoint.Ordinal));
427425

428426
return AddInstrumentationInstructions(method, processor, instruction, _result.HitCandidates.Count - 1);
429427
}
Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,99 @@
1+
using System;
12
using System.Collections.Generic;
3+
using System.ComponentModel;
4+
using System.Globalization;
5+
using System.IO;
6+
using Newtonsoft.Json;
27

38
namespace Coverlet.Core.Instrumentation
49
{
5-
internal class Line
10+
public class Line
611
{
712
public int Number;
813
public string Class;
914
public string Method;
1015
public int Hits;
1116
}
1217

13-
internal class Branch : Line
18+
public class Branch : Line
1419
{
1520
public int Offset;
1621
public int EndOffset;
1722
public int Path;
1823
public uint Ordinal;
1924
}
2025

21-
internal class Document
26+
public class BranchKeyConverter : TypeConverter
27+
{
28+
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
29+
{
30+
return sourceType == typeof(string);
31+
}
32+
33+
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
34+
{
35+
return JsonConvert.DeserializeObject<BranchKey>(value.ToString());
36+
}
37+
38+
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
39+
{
40+
return destinationType == typeof(BranchKey);
41+
}
42+
}
43+
44+
[TypeConverter(typeof(BranchKeyConverter))]
45+
public class BranchKey : IEquatable<BranchKey>
46+
{
47+
public BranchKey(int line, int ordinal) => (Line, Ordinal) = (line, ordinal);
48+
49+
public int Line { get; set; }
50+
public int Ordinal { get; set; }
51+
52+
public override bool Equals(object obj) => Equals(obj);
53+
54+
public bool Equals(BranchKey other) => other is BranchKey branchKey && branchKey.Line == this.Line && branchKey.Ordinal == this.Ordinal;
55+
56+
public override int GetHashCode()
57+
{
58+
return (this.Line, this.Ordinal).GetHashCode();
59+
}
60+
61+
public override string ToString()
62+
{
63+
return JsonConvert.SerializeObject(this);
64+
}
65+
}
66+
67+
public class Document
2268
{
2369
public Document()
2470
{
2571
Lines = new Dictionary<int, Line>();
26-
Branches = new Dictionary<(int Line, int Ordinal), Branch>();
72+
Branches = new Dictionary<BranchKey, Branch>();
2773
}
2874

2975
public string Path;
3076
public int Index;
3177
public Dictionary<int, Line> Lines { get; private set; }
32-
public Dictionary<(int Line, int Ordinal), Branch> Branches { get; private set; }
78+
public Dictionary<BranchKey, Branch> Branches { get; private set; }
79+
}
80+
81+
public class HitCandidate
82+
{
83+
public HitCandidate(bool isBranch, int docIndex, int start, int end) => (this.isBranch, this.docIndex, this.start, this.end) = (isBranch, docIndex, start, end);
84+
85+
public bool isBranch { get; set; }
86+
public int docIndex { get; set; }
87+
public int start { get; set; }
88+
public int end { get; set; }
3389
}
3490

35-
internal class InstrumenterResult
91+
public class InstrumenterResult
3692
{
3793
public InstrumenterResult()
3894
{
3995
Documents = new Dictionary<string, Document>();
40-
HitCandidates = new List<(bool isBranch, int docIndex, int start, int end)>();
96+
HitCandidates = new List<HitCandidate>();
4197
}
4298

4399
public string Module;
@@ -46,6 +102,6 @@ public InstrumenterResult()
46102
public string ModulePath;
47103
public string SourceLink;
48104
public Dictionary<string, Document> Documents { get; private set; }
49-
public List<(bool isBranch, int docIndex, int start, int end)> HitCandidates { get; private set; }
105+
public List<HitCandidate> HitCandidates { get; private set; }
50106
}
51-
}
107+
}

src/coverlet.msbuild.tasks/CoverageResultTask.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class CoverageResultTask : Task
1818
private double _threshold;
1919
private string _thresholdType;
2020
private string _thresholdStat;
21+
private ITaskItem _instrumenterState;
2122
private MSBuildLogger _logger;
2223

2324
[Required]
@@ -55,6 +56,13 @@ public string ThresholdStat
5556
set { _thresholdStat = value; }
5657
}
5758

59+
[Required]
60+
public ITaskItem InstrumenterState
61+
{
62+
get { return _instrumenterState; }
63+
set { _instrumenterState = value; }
64+
}
65+
5866
public CoverageResultTask()
5967
{
6068
_logger = new MSBuildLogger(Log);
@@ -66,7 +74,13 @@ public override bool Execute()
6674
{
6775
Console.WriteLine("\nCalculating coverage result...");
6876

69-
var coverage = InstrumentationTask.Coverage;
77+
if (InstrumenterState is null || !File.Exists(InstrumenterState.ItemSpec))
78+
{
79+
_logger.LogError("Result of instrumentation task not found");
80+
return false;
81+
}
82+
83+
var coverage = new Coverage(CoveragePrepareResult.Deserialize(new FileStream(InstrumenterState.ItemSpec, FileMode.Open)), this._logger);
7084
var result = coverage.GetCoverageResult();
7185

7286
var directory = Path.GetDirectoryName(_output);

src/coverlet.msbuild.tasks/InstrumentationTask.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.IO;
23
using Coverlet.Core;
34
using Microsoft.Build.Framework;
45
using Microsoft.Build.Utilities;
@@ -7,7 +8,6 @@ namespace Coverlet.MSbuild.Tasks
78
{
89
public class InstrumentationTask : Task
910
{
10-
private static Coverage _coverage;
1111
private string _path;
1212
private string _include;
1313
private string _includeDirectory;
@@ -18,13 +18,9 @@ public class InstrumentationTask : Task
1818
private bool _singleHit;
1919
private string _mergeWith;
2020
private bool _useSourceLink;
21+
private ITaskItem _instrumenterState;
2122
private readonly MSBuildLogger _logger;
2223

23-
internal static Coverage Coverage
24-
{
25-
get { return _coverage; }
26-
}
27-
2824
[Required]
2925
public string Path
3026
{
@@ -86,6 +82,13 @@ public bool UseSourceLink
8682
set { _useSourceLink = value; }
8783
}
8884

85+
[Output]
86+
public ITaskItem InstrumenterState
87+
{
88+
get { return _instrumenterState; }
89+
set { _instrumenterState = value; }
90+
}
91+
8992
public InstrumentationTask()
9093
{
9194
_logger = new MSBuildLogger(Log);
@@ -101,8 +104,16 @@ public override bool Execute()
101104
var excludedSourceFiles = _excludeByFile?.Split(',');
102105
var excludeAttributes = _excludeByAttribute?.Split(',');
103106

104-
_coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _includeTestAssembly, _singleHit, _mergeWith, _useSourceLink, _logger);
105-
_coverage.PrepareModules();
107+
Coverage coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _includeTestAssembly, _singleHit, _mergeWith, _useSourceLink, _logger);
108+
CoveragePrepareResult prepareResult = coverage.PrepareModules();
109+
InstrumenterState = new TaskItem(System.IO.Path.GetTempFileName());
110+
using (var instrumentedStateFile = new FileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write))
111+
{
112+
using (Stream serializedState = CoveragePrepareResult.Serialize(prepareResult))
113+
{
114+
serializedState.CopyTo(instrumentedStateFile);
115+
}
116+
}
106117
}
107118
catch (Exception ex)
108119
{

src/coverlet.msbuild.tasks/coverlet.msbuild.targets

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
IncludeTestAssembly="$(IncludeTestAssembly)"
1616
SingleHit="$(SingleHit)"
1717
MergeWith="$(MergeWith)"
18-
UseSourceLink="$(UseSourceLink)" />
18+
UseSourceLink="$(UseSourceLink)" >
19+
<Output TaskParameter="InstrumenterState" PropertyName="InstrumenterState"/>
20+
</Coverlet.MSbuild.Tasks.InstrumentationTask>
1921
</Target>
2022

2123
<Target Name="InstrumentModulesAfterBuild" AfterTargets="BuildProject">
@@ -30,7 +32,9 @@
3032
IncludeTestAssembly="$(IncludeTestAssembly)"
3133
SingleHit="$(SingleHit)"
3234
MergeWith="$(MergeWith)"
33-
UseSourceLink="$(UseSourceLink)" />
35+
UseSourceLink="$(UseSourceLink)" >
36+
<Output TaskParameter="InstrumenterState" PropertyName="InstrumenterState"/>
37+
</Coverlet.MSbuild.Tasks.InstrumentationTask>
3438
</Target>
3539

3640
<Target Name="GenerateCoverageResult" AfterTargets="VSTest">
@@ -40,7 +44,8 @@
4044
OutputFormat="$(CoverletOutputFormat)"
4145
Threshold="$(Threshold)"
4246
ThresholdType="$(ThresholdType)"
43-
ThresholdStat="$(ThresholdStat)" />
47+
ThresholdStat="$(ThresholdStat)"
48+
InstrumenterState="$(InstrumenterState)"/>
4449
</Target>
4550

46-
</Project>
51+
</Project>

0 commit comments

Comments
 (0)