Skip to content

Commit 6985035

Browse files
authored
Merge pull request #31 from tonerdo/branch-coverage
Preliminary branch coverage support
2 parents d296fde + 65a374e commit 6985035

File tree

10 files changed

+78
-32
lines changed

10 files changed

+78
-32
lines changed

src/coverlet.core/Coverage.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,27 +53,27 @@ public CoverageResult GetCoverageResult()
5353
{
5454
if (methods.TryGetValue(line.Method, out Lines lines))
5555
{
56-
documents[doc.Path][line.Class][line.Method].Add(line.Number, line.Hits);
56+
documents[doc.Path][line.Class][line.Method].Add(line.Number, new LineInfo { Hits = line.Hits, IsBranchPoint = line.IsBranchTarget });
5757
}
5858
else
5959
{
6060
documents[doc.Path][line.Class].Add(line.Method, new Lines());
61-
documents[doc.Path][line.Class][line.Method].Add(line.Number, line.Hits);
61+
documents[doc.Path][line.Class][line.Method].Add(line.Number, new LineInfo { Hits = line.Hits, IsBranchPoint = line.IsBranchTarget });
6262
}
6363
}
6464
else
6565
{
6666
documents[doc.Path].Add(line.Class, new Methods());
6767
documents[doc.Path][line.Class].Add(line.Method, new Lines());
68-
documents[doc.Path][line.Class][line.Method].Add(line.Number, line.Hits);
68+
documents[doc.Path][line.Class][line.Method].Add(line.Number, new LineInfo { Hits = line.Hits, IsBranchPoint = line.IsBranchTarget });
6969
}
7070
}
7171
else
7272
{
7373
documents.Add(doc.Path, new Classes());
7474
documents[doc.Path].Add(line.Class, new Methods());
7575
documents[doc.Path][line.Class].Add(line.Method, new Lines());
76-
documents[doc.Path][line.Class][line.Method].Add(line.Number, line.Hits);
76+
documents[doc.Path][line.Class][line.Method].Add(line.Number, new LineInfo { Hits = line.Hits, IsBranchPoint = line.IsBranchTarget });
7777
}
7878
}
7979
}
@@ -99,7 +99,7 @@ private void CalculateCoverage()
9999
{
100100
var info = lines[i].Split(',');
101101
// Ignore malformed lines
102-
if (info.Length != 3)
102+
if (info.Length != 4)
103103
continue;
104104

105105
var document = result.Documents.FirstOrDefault(d => d.Path == info[0]);
@@ -108,11 +108,15 @@ private void CalculateCoverage()
108108

109109
int start = int.Parse(info[1]);
110110
int end = int.Parse(info[2]);
111+
bool target = info[3] == "B";
111112

112113
for (int j = start; j <= end; j++)
113114
{
114115
var line = document.Lines.First(l => l.Number == j);
115116
line.Hits = line.Hits + 1;
117+
118+
if (j == start)
119+
line.IsBranchTarget = target;
116120
}
117121
}
118122

src/coverlet.core/CoverageResult.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66

77
namespace Coverlet.Core
88
{
9-
public class Lines : SortedDictionary<int, int> { }
9+
public class LineInfo
10+
{
11+
public int Hits { get; set; }
12+
public bool IsBranchPoint { get; set; }
13+
}
14+
15+
public class Lines : SortedDictionary<int, LineInfo> { }
1016
public class Methods : Dictionary<string, Lines> { }
1117
public class Classes : Dictionary<string, Methods> { }
1218
public class Documents : Dictionary<string, Classes> { }

src/coverlet.core/CoverageSummary.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public CoverageSummaryResult CalculateSummary()
2525
foreach (var line in method.Value)
2626
{
2727
totalLines++;
28-
if (line.Value > 0)
28+
if (line.Value.Hits > 0)
2929
linesCovered++;
3030
}
3131
}

src/coverlet.core/Instrumentation/Instrumenter.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
124124
document.Lines.Add(new Line { Number = i, Class = method.DeclaringType.FullName, Method = method.FullName });
125125
}
126126

127-
string marker = $"{document.Path},{sequencePoint.StartLine},{sequencePoint.EndLine}";
127+
string flag = IsBranchTarget(processor, instruction) ? "B" : "L";
128+
string marker = $"{document.Path},{sequencePoint.StartLine},{sequencePoint.EndLine},{flag}";
128129

129130
var pathInstr = Instruction.Create(OpCodes.Ldstr, _result.HitsFilePath);
130131
var markInstr = Instruction.Create(OpCodes.Ldstr, marker);
@@ -137,6 +138,23 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
137138
return pathInstr;
138139
}
139140

141+
private bool IsBranchTarget(ILProcessor processor, Instruction instruction)
142+
{
143+
foreach (var _instruction in processor.Body.Instructions)
144+
{
145+
if (_instruction.Operand is Instruction target)
146+
{
147+
if (target == instruction)
148+
return true;
149+
}
150+
151+
if (_instruction.Operand is Instruction[] targets)
152+
return targets.Any(t => t == instruction);
153+
}
154+
155+
return false;
156+
}
157+
140158
private void ReplaceInstructionTarget(Instruction instruction, Instruction oldTarget, Instruction newTarget)
141159
{
142160
if (instruction.Operand is Instruction _instruction)

src/coverlet.core/Instrumentation/InstrumenterResult.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ internal class Line
77
public int Number;
88
public string Class;
99
public string Method;
10+
public bool IsBranchTarget;
1011
public int Hits;
1112
}
1213

src/coverlet.core/Reporters/OpenCoverReporter.cs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ public string Format(CoverageResult result)
2222

2323
XmlElement modules = xml.CreateElement("Modules");
2424

25-
int numSequencePoints = 0, numClasses = 0, numMethods = 0;
26-
int visitedSequencePoints = 0, visitedClasses = 0, visitedMethods = 0;
25+
int numSequencePoints = 0, numBranchPoints = 0, numClasses = 0, numMethods = 0;
26+
int visitedSequencePoints = 0, visitedBranchPoints = 0, visitedClasses = 0, visitedMethods = 0;
2727

2828
int i = 1;
2929

@@ -87,7 +87,7 @@ public string Format(CoverageResult result)
8787
fileRef.SetAttribute("uid", i.ToString());
8888

8989
XmlElement methodPoint = xml.CreateElement("MethodPoint");
90-
methodPoint.SetAttribute("vc", meth.Value.Select(l => l.Value).Sum().ToString());
90+
methodPoint.SetAttribute("vc", meth.Value.Select(l => l.Value.Hits).Sum().ToString());
9191
methodPoint.SetAttribute("upsid", "0");
9292
methodPoint.SetAttribute("type", "xsi", "SequencePoint");
9393
methodPoint.SetAttribute("ordinal", j.ToString());
@@ -102,14 +102,16 @@ public string Format(CoverageResult result)
102102

103103
// They're really just lines
104104
XmlElement sequencePoints = xml.CreateElement("SequencePoints");
105+
XmlElement branchPoints = xml.CreateElement("BranchPoints");
105106
XmlElement methodSummary = xml.CreateElement("Summary");
106107
int k = 0;
108+
int kBr = 0;
107109
var methodVisited = false;
108110

109111
foreach (var lines in meth.Value)
110112
{
111113
XmlElement sequencePoint = xml.CreateElement("SequencePoint");
112-
sequencePoint.SetAttribute("vc", lines.Value.ToString());
114+
sequencePoint.SetAttribute("vc", lines.Value.Hits.ToString());
113115
sequencePoint.SetAttribute("upsid", lines.Key.ToString());
114116
sequencePoint.SetAttribute("ordinal", k.ToString());
115117
sequencePoint.SetAttribute("sl", lines.Key.ToString());
@@ -121,12 +123,27 @@ public string Format(CoverageResult result)
121123
sequencePoint.SetAttribute("fileid", i.ToString());
122124
sequencePoints.AppendChild(sequencePoint);
123125

126+
if (lines.Value.IsBranchPoint)
127+
{
128+
XmlElement branchPoint = xml.CreateElement("BranchPoint");
129+
branchPoint.SetAttribute("vc", lines.Value.Hits.ToString());
130+
branchPoint.SetAttribute("upsid", lines.Key.ToString());
131+
branchPoint.SetAttribute("ordinal", kBr.ToString());
132+
branchPoint.SetAttribute("sl", lines.Key.ToString());
133+
branchPoint.SetAttribute("fileid", i.ToString());
134+
branchPoints.AppendChild(branchPoint);
135+
kBr++;
136+
numBranchPoints++;
137+
}
138+
124139
numSequencePoints++;
125-
if (lines.Value > 0)
140+
if (lines.Value.Hits > 0)
126141
{
127142
visitedSequencePoints++;
128143
classVisited = true;
129144
methodVisited = true;
145+
if (lines.Value.IsBranchPoint)
146+
visitedBranchPoints++;
130147
}
131148

132149
k++;
@@ -137,9 +154,9 @@ public string Format(CoverageResult result)
137154
visitedMethods++;
138155

139156
methodSummary.SetAttribute("numSequencePoints", meth.Value.Count().ToString());
140-
methodSummary.SetAttribute("visitedSequencePoints", meth.Value.Where(l => l.Value > 0).Count().ToString());
141-
methodSummary.SetAttribute("numBranchPoints", "0");
142-
methodSummary.SetAttribute("visitedBranchPoints", "0");
157+
methodSummary.SetAttribute("visitedSequencePoints", meth.Value.Where(l => l.Value.Hits > 0).Count().ToString());
158+
methodSummary.SetAttribute("numBranchPoints", meth.Value.Where(l => l.Value.IsBranchPoint).Count().ToString());
159+
methodSummary.SetAttribute("visitedBranchPoints", meth.Value.Where(l => l.Value.IsBranchPoint && l.Value.Hits > 0).Count().ToString());
143160
methodSummary.SetAttribute("sequenceCoverage", "0");
144161
methodSummary.SetAttribute("branchCoverage", "0");
145162
methodSummary.SetAttribute("maxCyclomaticComplexity", "0");
@@ -154,7 +171,7 @@ public string Format(CoverageResult result)
154171
method.AppendChild(methodName);
155172
method.AppendChild(fileRef);
156173
method.AppendChild(sequencePoints);
157-
method.AppendChild(xml.CreateElement("BranchPoints"));
174+
method.AppendChild(branchPoints);
158175
method.AppendChild(methodPoint);
159176
methods.AppendChild(method);
160177
j++;
@@ -165,9 +182,9 @@ public string Format(CoverageResult result)
165182
visitedClasses++;
166183

167184
classSummary.SetAttribute("numSequencePoints", cls.Value.Select(c => c.Value.Count).Sum().ToString());
168-
classSummary.SetAttribute("visitedSequencePoints", cls.Value.Select(c => c.Value.Where(l => l.Value > 0).Count()).Sum().ToString());
169-
classSummary.SetAttribute("numBranchPoints", "0");
170-
classSummary.SetAttribute("visitedBranchPoints", "0");
185+
classSummary.SetAttribute("visitedSequencePoints", cls.Value.Select(c => c.Value.Where(l => l.Value.Hits > 0).Count()).Sum().ToString());
186+
classSummary.SetAttribute("numBranchPoints", cls.Value.Select(c => c.Value.Count(l => l.Value.IsBranchPoint)).Sum().ToString());
187+
classSummary.SetAttribute("visitedBranchPoints", cls.Value.Select(c => c.Value.Where(l => l.Value.Hits > 0 && l.Value.IsBranchPoint).Count()).Sum().ToString());
171188
classSummary.SetAttribute("sequenceCoverage", "0");
172189
classSummary.SetAttribute("branchCoverage", "0");
173190
classSummary.SetAttribute("maxCyclomaticComplexity", "0");
@@ -192,8 +209,8 @@ public string Format(CoverageResult result)
192209

193210
coverageSummary.SetAttribute("numSequencePoints", numSequencePoints.ToString());
194211
coverageSummary.SetAttribute("visitedSequencePoints", visitedSequencePoints.ToString());
195-
coverageSummary.SetAttribute("numBranchPoints", "0");
196-
coverageSummary.SetAttribute("visitedBranchPoints", "0");
212+
coverageSummary.SetAttribute("numBranchPoints", numBranchPoints.ToString());
213+
coverageSummary.SetAttribute("visitedBranchPoints", visitedBranchPoints.ToString());
197214
coverageSummary.SetAttribute("sequenceCoverage", "0");
198215
coverageSummary.SetAttribute("branchCoverage", "0");
199216
coverageSummary.SetAttribute("maxCyclomaticComplexity", "0");

test/coverlet.core.tests/CoverageSummaryTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ public void TestCalculateSummary()
1414
CoverageResult result = new CoverageResult();
1515
result.Identifier = Guid.NewGuid().ToString();
1616
Lines lines = new Lines();
17-
lines.Add(1, 1);
18-
lines.Add(2, 0);
17+
lines.Add(1, new LineInfo { Hits = 1 });
18+
lines.Add(2, new LineInfo { Hits = 0 });
1919
Methods methods = new Methods();
2020
methods.Add("System.Void Coverlet.Core.Tests.CoverageSummaryTests.TestCalculateSummary()", lines);
2121
Classes classes = new Classes();

test/coverlet.core.tests/Reporters/JsonReporterTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ public void TestFormat()
1111
CoverageResult result = new CoverageResult();
1212
result.Identifier = Guid.NewGuid().ToString();
1313
Lines lines = new Lines();
14-
lines.Add(1, 1);
15-
lines.Add(2, 0);
14+
lines.Add(1, new LineInfo { Hits = 1 });
15+
lines.Add(2, new LineInfo { Hits = 0 });
1616
Methods methods = new Methods();
1717
methods.Add("System.Void Coverlet.Core.Reporters.Tests.JsonReporterTests.TestFormat()", lines);
1818
Classes classes = new Classes();

test/coverlet.core.tests/Reporters/LcovReporterTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ public void TestFormat()
1111
CoverageResult result = new CoverageResult();
1212
result.Identifier = Guid.NewGuid().ToString();
1313
Lines lines = new Lines();
14-
lines.Add(1, 1);
15-
lines.Add(2, 0);
14+
lines.Add(1, new LineInfo { Hits = 1 });
15+
lines.Add(2, new LineInfo { Hits = 0 });
1616
Methods methods = new Methods();
1717
methods.Add("System.Void Coverlet.Core.Reporters.Tests.LcovReporterTests.TestFormat()", lines);
1818
Classes classes = new Classes();

test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ public void TestFilesHaveUniqueIdsOverMultipleModules()
3939
private static Documents CreateFirstDocuments()
4040
{
4141
Lines lines = new Lines();
42-
lines.Add(1, 1);
43-
lines.Add(2, 0);
42+
lines.Add(1, new LineInfo { Hits = 1 });
43+
lines.Add(2, new LineInfo { Hits = 0 });
4444
Methods methods = new Methods();
4545
methods.Add("System.Void Coverlet.Core.Reporters.Tests.OpenCoverReporterTests.TestFormat()", lines);
4646
Classes classes = new Classes();
@@ -53,8 +53,8 @@ private static Documents CreateFirstDocuments()
5353
private static Documents CreateSecondDocuments()
5454
{
5555
Lines lines = new Lines();
56-
lines.Add(1, 1);
57-
lines.Add(2, 0);
56+
lines.Add(1, new LineInfo { Hits = 1 });
57+
lines.Add(2, new LineInfo { Hits = 0 });
5858

5959
Methods methods = new Methods();
6060
methods.Add("System.Void Some.Other.Module.TestClass.TestMethod()", lines);

0 commit comments

Comments
 (0)