Skip to content
This repository was archived by the owner on Oct 16, 2020. It is now read-only.

Commit 5752cd3

Browse files
committed
Merge pull request #335 from ddur/master
CodeCoverage again
2 parents 1c663b0 + 7d4177f commit 5752cd3

2 files changed

Lines changed: 119 additions & 106 deletions

File tree

src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageMethodElement.cs

Lines changed: 105 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
2-
//
2+
//
33
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
44
// software and associated documentation files (the "Software"), to deal in the Software
55
// without restriction, including without limitation the rights to use, copy, modify, merge,
66
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
77
// to whom the Software is furnished to do so, subject to the following conditions:
8-
//
8+
//
99
// The above copyright notice and this permission notice shall be included in all copies or
1010
// substantial portions of the Software.
11-
//
11+
//
1212
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
1313
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
1414
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
@@ -32,8 +32,8 @@ public class CodeCoverageMethodElement
3232
XElement element;
3333
CodeCoverageResults parent;
3434

35-
public CodeCoverageMethodElement(XElement element)
36-
: this (element, null) {}
35+
public CodeCoverageMethodElement(XElement element)
36+
: this (element, null) {}
3737
public CodeCoverageMethodElement(XElement element, CodeCoverageResults parent)
3838
{
3939
this.parent = parent;
@@ -67,7 +67,7 @@ public CodeCoverageMethodElement(XElement element, CodeCoverageResults parent)
6767
public bool IsProperty {
6868
get { return IsGetter || IsSetter; }
6969
}
70-
70+
7171
void Init()
7272
{
7373
MethodName = GetMethodName();
@@ -77,24 +77,24 @@ void Init()
7777
this.FileID = GetFileRef();
7878
this.FileName = String.Empty;
7979
if (!String.IsNullOrEmpty(this.FileID)) {
80-
if (parent != null) {
81-
this.FileName = parent.GetFileName(this.FileID);
82-
if ( File.Exists(this.FileName) ) {
83-
if (cacheFileName != this.FileName) {
84-
cacheFileName = this.FileName;
85-
cacheDocument = null;
86-
try {
87-
using (Stream stream = new FileStream(this.FileName, FileMode.Open, FileAccess.Read)) {
88-
try {
89-
stream.Position = 0;
90-
string textSource = ICSharpCode.AvalonEdit.Utils.FileReader.ReadFileContent(stream, Encoding.Default);
91-
cacheDocument = new CodeCoverageStringTextSource(textSource);
92-
} catch {}
93-
}
94-
} catch {}
95-
}
96-
}
97-
}
80+
if (parent != null) {
81+
this.FileName = parent.GetFileName(this.FileID);
82+
if ( File.Exists(this.FileName) ) {
83+
if (cacheFileName != this.FileName) {
84+
cacheFileName = this.FileName;
85+
cacheDocument = null;
86+
try {
87+
using (Stream stream = new FileStream(this.FileName, FileMode.Open, FileAccess.Read)) {
88+
try {
89+
stream.Position = 0;
90+
string textSource = ICSharpCode.AvalonEdit.Utils.FileReader.ReadFileContent(stream, Encoding.Default);
91+
cacheDocument = new CodeCoverageStringTextSource(textSource);
92+
} catch {}
93+
}
94+
} catch {}
95+
}
96+
}
97+
}
9898
}
9999

100100
this.IsVisited = this.GetBooleanAttributeValue("visited");
@@ -104,25 +104,26 @@ void Init()
104104
this.IsConstructor = this.GetBooleanAttributeValue("isConstructor");
105105
this.IsStatic = this.GetBooleanAttributeValue("isStatic");
106106
if ( !String.IsNullOrEmpty( this.FileID ) ) {
107-
this.SequencePoints = this.GetSequencePoints();
108-
this.BodyStartSP = getBodyStartSP(); // before OrderBy Line/Col
107+
this.GetSequencePoints();
108+
this.GetSequencePointsContent();
109+
this.getBodyStartSP(); // before OrderBy Line/Col
110+
this.getBodyFinalSP(); // before orderBy Line/Col
111+
this.FilterSequencePoints(); // before orderBy Line/Col
112+
this.GetBranchPoints();
113+
this.GetBranchRatio();
114+
this.GetBranchCoverage();
115+
109116
// SP's are originaly ordered by CIL offset
110117
// but ccrewrite can move offset of
111118
// Contract.Requires before method signature SP { and
112119
// Contract.Ensures after method closing SP }
113120
// So sort SP's back by line/column
114121
this.SequencePoints.OrderBy(item => item.Line).OrderBy(item => item.Column);
115-
this.BodyFinalSP = getBodyFinalSP(); // after orderBy Line/Col
116-
this.SequencePoints = this.FilterSequencePoints(this.SequencePoints);
117-
this.BranchPoints = this.GetBranchPoints();
118-
this.BranchCoverageRatio = this.GetBranchRatio();
119-
this.BranchCoverage = this.GetBranchCoverage();
120122
}
121123
}
122124

123-
List<CodeCoverageSequencePoint> GetSequencePoints() {
125+
void GetSequencePoints() {
124126

125-
List<CodeCoverageSequencePoint> sps = new List<CodeCoverageSequencePoint>();
126127
var xSPoints = this.element
127128
.Elements("SequencePoints")
128129
.Elements("SequencePoint");
@@ -136,52 +137,68 @@ List<CodeCoverageSequencePoint> GetSequencePoints() {
136137
sp.Column = (int)GetDecimalAttributeValue(xSPoint.Attribute("sc"));
137138
sp.EndColumn = (int)GetDecimalAttributeValue(xSPoint.Attribute("ec"));
138139
sp.VisitCount = (int)GetDecimalAttributeValue(xSPoint.Attribute("vc"));
139-
if (cacheFileName == sp.Document && cacheDocument != null) {
140-
sp.Content = cacheDocument.GetText(sp);
141-
if (sp.Line != sp.EndLine) {
142-
sp.Content = Regex.Replace (sp.Content, @"\s+", " ");
143-
}
144-
sp.Length = Regex.Replace (sp.Content, @"\s", "").Length; // ignore white-space for coverage%
145-
} else {
146-
sp.Content = String.Empty;
147-
sp.Length = 0;
148-
}
149140
sp.Offset = (int)GetDecimalAttributeValue(xSPoint.Attribute("offset"));
150141
sp.BranchCoverage = true;
142+
sp.Content = String.Empty;
143+
sp.Length = 0;
151144

152-
sps.Add(sp);
145+
this.SequencePoints.Add(sp);
153146
}
154-
return sps;
155147
}
156148

157-
// Find method-body start SequencePoint "{"
149+
void GetSequencePointsContent()
150+
{
151+
if (cacheFileName == this.FileName && cacheDocument != null) {
152+
foreach (var sp in this.SequencePoints) {
153+
GetSequencePointContent(sp);
154+
}
155+
}
156+
}
157+
158+
void GetSequencePointContent(CodeCoverageSequencePoint sp)
159+
{
160+
// ccrewrite will cause lots of invalid calls to GetText()!
161+
if (cacheFileName == sp.Document && cacheDocument != null) {
162+
sp.Content = cacheDocument.GetText(sp); // never returns null
163+
if (sp.Content != String.Empty) {
164+
if (sp.Line != sp.EndLine) {
165+
// merge lines to single line
166+
sp.Content = Regex.Replace(sp.Content, @"\s+", " ");
167+
}
168+
// SequencePoint.Length counts all but whitespace
169+
sp.Length = Regex.Replace(sp.Content, @"\s", "").Length;
170+
}
171+
}
172+
}
173+
174+
// Find method-body start SequencePoint "{" (sp.Content required)
158175
// Sequence points expected to be ordered by Offset
159176
// Cannot just get first one because of ccrewrite&ContractClassFor
160-
public CodeCoverageSequencePoint getBodyStartSP() {
177+
void getBodyStartSP() {
161178
bool startPointFound = false;
162179
CodeCoverageSequencePoint startSeqPoint = null;
163-
foreach (CodeCoverageSequencePoint sPoint in this.SequencePoints) {
164-
if ( sPoint.Content == "{") {
180+
foreach (CodeCoverageSequencePoint sp in this.SequencePoints) {
181+
if ( sp.Content == "{") {
165182
if ( this.IsConstructor ) {
166183
// take previous/last one if not null
167-
startSeqPoint = startSeqPoint?? sPoint;
184+
startSeqPoint = startSeqPoint?? sp;
168185
}
169186
else {
170-
startSeqPoint = sPoint;
187+
startSeqPoint = sp;
171188
}
172189
startPointFound = true;
173190
break;
174191
}
175-
startSeqPoint = sPoint;
192+
startSeqPoint = sp;
176193
}
177-
return startPointFound? startSeqPoint: null;
194+
this.BodyStartSP = startPointFound? startSeqPoint: null;
178195
}
179196

180-
// Find method-body final SequencePoint "}"
181-
// Sequence points expected to be ordered by Line/Column
182-
public CodeCoverageSequencePoint getBodyFinalSP() {
197+
// Find method-body final SequencePoint "}" (sp.Content required)
198+
// Sequence points expected to be ordered by Offset
199+
void getBodyFinalSP() {
183200
CodeCoverageSequencePoint finalSeqPoint = null;
184-
foreach (CodeCoverageSequencePoint sp in this.SequencePoints) {
201+
foreach (CodeCoverageSequencePoint sp in ((IEnumerable<CodeCoverageSequencePoint>)this.SequencePoints).Reverse()) {
185202
if ( sp.Content == "}") {
186203
if (finalSeqPoint == null) {
187204
finalSeqPoint = sp;
@@ -193,20 +210,17 @@ public CodeCoverageSequencePoint getBodyFinalSP() {
193210
sp.EndColumn == finalSeqPoint.EndColumn &&
194211
sp.Offset < finalSeqPoint.Offset) {
195212
finalSeqPoint = sp;
196-
}
197-
else if (sp.Line < finalSeqPoint.Line) {
213+
// duplicate found, so far no reason to expect "triplicate" :)
198214
break;
199215
}
200216
}
201217
}
202-
return finalSeqPoint;
218+
this.BodyFinalSP = finalSeqPoint;
203219
}
204220

205-
List<CodeCoverageSequencePoint> FilterSequencePoints(List<CodeCoverageSequencePoint> sps) {
206-
207-
List<CodeCoverageSequencePoint> returnList = sps;
221+
void FilterSequencePoints() {
208222

209-
if (sps.Count > 2 &&
223+
if (this.SequencePoints.Count != 0 &&
210224
this.BodyStartSP != null &&
211225
this.BodyFinalSP != null ) {
212226

@@ -218,31 +232,31 @@ List<CodeCoverageSequencePoint> FilterSequencePoints(List<CodeCoverageSequencePo
218232
// before method signature and after end-brackets xxx{} are removed
219233
// If ContractClassFor is in another file but interleaves this method lines
220234
// then, afaik, not much can be done to remove inserted alien SP's
221-
List<CodeCoverageSequencePoint> selected = new List<CodeCoverageSequencePoint>();
222-
223-
foreach (var point in sps) {
224-
if (
225-
(point.Line > BodyStartSP.Line || (point.Line == BodyStartSP.Line && point.Column >= BodyStartSP.Column)) &&
226-
(point.Line < BodyFinalSP.Line || (point.Line == BodyFinalSP.Line && point.Column < BodyFinalSP.Column))
227-
) {
235+
var selected = new List<CodeCoverageSequencePoint>();
236+
237+
foreach (var point in this.SequencePoints) {
238+
239+
// if Content.Length is 0, GetText() is failed by ccrewrite inserted invalid SequencePoint
240+
if (point.Content.Length != 0
241+
&& (point.Line > BodyStartSP.Line || (point.Line == BodyStartSP.Line && point.Column >= BodyStartSP.Column))
242+
&& (point.Line < BodyFinalSP.Line || (point.Line == BodyFinalSP.Line && point.Column < BodyFinalSP.Column))
243+
) {
228244
selected.Add (point);
229245
}
230246
// After ccrewrite ContractClass/ContractClassFor
231-
// duplicate method end-sequence-point (}) is added
247+
// duplicate method end-sequence-point "}" is added
232248
//
233-
// Add first finalSP (can be a duplicate)
249+
// Add only first finalSP (can be a duplicate)
234250
// Note: IL.Offset of second duplicate finalSP will
235251
// extend branch coverage outside method-end "}",
236252
// and that can lead to wrong branch coverage display!
237253
if (object.ReferenceEquals (point, this.BodyFinalSP)) {
238-
selected.Add (point);
254+
selected.Add (point);
239255
}
240256
}
241257

242-
returnList = selected;
258+
this.SequencePoints = selected;
243259
}
244-
245-
return returnList;
246260
}
247261

248262
int GetSequencePointsCount() {
@@ -256,9 +270,8 @@ int GetSequencePointsCount() {
256270
return 0;
257271
}
258272

259-
List<CodeCoverageBranchPoint> GetBranchPoints() {
273+
void GetBranchPoints() {
260274
// get all BranchPoints
261-
List<CodeCoverageBranchPoint> bps = new List<CodeCoverageBranchPoint>();
262275
var xBPoints = this.element
263276
.Elements("BranchPoints")
264277
.Elements("BranchPoint");
@@ -268,34 +281,34 @@ List<CodeCoverageBranchPoint> GetBranchPoints() {
268281
bp.Offset = (int)GetDecimalAttributeValue(xBPoint.Attribute("offset"));
269282
bp.Path = (int)GetDecimalAttributeValue(xBPoint.Attribute("path"));
270283
bp.OffsetEnd = (int)GetDecimalAttributeValue(xBPoint.Attribute("offsetend"));
271-
bps.Add(bp);
284+
this.BranchPoints.Add(bp);
272285
}
273-
return bps;
274286
}
275287

276-
Tuple<int,int> GetBranchRatio () {
277-
278-
// goal: Get branch ratio and exclude (rewriten) Code Contracts branches
288+
void GetBranchRatio () {
279289

290+
// goal: Get branch ratio, merge branch-exits and exclude (rewriten) Code Contracts branches
291+
this.BranchCoverageRatio = null;
292+
280293
if ( this.BranchPoints == null
281-
|| this.BranchPoints.Count() == 0
294+
|| this.BranchPoints.Count == 0
282295
|| this.SequencePoints == null
283296
|| this.SequencePoints.Count == 0
284297
)
285298
{
286-
return null;
299+
return;
287300
}
288301

289302
// This sequence point offset is used to skip CCRewrite(n) BranchPoint's (Requires)
290303
// and '{' branches at static methods
291-
if (this.BodyStartSP == null) { return null; } // empty body
304+
if (this.BodyStartSP == null) { return; } // empty body
292305

293306
// This sequence point offset is used to skip CCRewrite(n) BranchPoint's (Ensures)
294-
if (this.BodyFinalSP == null) { return null; } // empty body
307+
if (this.BodyFinalSP == null) { return; } // empty body
295308

296309
// Connect Sequence & Branches
297310
IEnumerator<CodeCoverageSequencePoint> SPEnumerator = this.SequencePoints.GetEnumerator();
298-
CodeCoverageSequencePoint currSeqPoint = BodyStartSP;
311+
CodeCoverageSequencePoint currSeqPoint = this.BodyStartSP;
299312
int nextSeqPointOffset = BodyStartSP.Offset;
300313

301314
foreach (var bp in this.BranchPoints) {
@@ -379,13 +392,13 @@ Tuple<int,int> GetBranchRatio () {
379392
sp.Branches = null; // release memory
380393
}
381394

382-
return (totalBranchCount!=0) ? new Tuple<int,int>(totalBranchVisit,totalBranchCount) : null;
395+
this.BranchCoverageRatio = (totalBranchCount!=0) ? new Tuple<int,int>(totalBranchVisit,totalBranchCount) : null;
383396

384397
}
385398

386-
decimal GetBranchCoverage () {
399+
void GetBranchCoverage () {
387400

388-
return this.BranchCoverageRatio == null ? 0m : ((decimal)(this.BranchCoverageRatio.Item1*100))/((decimal)this.BranchCoverageRatio.Item2);
401+
this.BranchCoverage = this.BranchCoverageRatio == null ? 0m : ((decimal)(this.BranchCoverageRatio.Item1*100))/((decimal)this.BranchCoverageRatio.Item2);
389402

390403
}
391404

0 commit comments

Comments
 (0)