|
|
@ -54,6 +54,8 @@ namespace ICSharpCode.CodeCoverage |
|
|
|
public bool IsConstructor { get; private set; } |
|
|
|
public bool IsConstructor { get; private set; } |
|
|
|
public bool IsStatic { get; private set; } |
|
|
|
public bool IsStatic { get; private set; } |
|
|
|
public List<CodeCoverageSequencePoint> SequencePoints { get; private set; } |
|
|
|
public List<CodeCoverageSequencePoint> SequencePoints { get; private set; } |
|
|
|
|
|
|
|
public CodeCoverageSequencePoint BodyStartSP { get; private set; } |
|
|
|
|
|
|
|
public CodeCoverageSequencePoint BodyFinalSP { get; private set; } |
|
|
|
public List<CodeCoverageBranchPoint> BranchPoints { get; private set; } |
|
|
|
public List<CodeCoverageBranchPoint> BranchPoints { get; private set; } |
|
|
|
|
|
|
|
|
|
|
|
public bool IsGetter { get; private set; } |
|
|
|
public bool IsGetter { get; private set; } |
|
|
@ -99,6 +101,9 @@ namespace ICSharpCode.CodeCoverage |
|
|
|
this.IsStatic = this.GetBooleanAttributeValue("isStatic"); |
|
|
|
this.IsStatic = this.GetBooleanAttributeValue("isStatic"); |
|
|
|
if ( !String.IsNullOrEmpty( this.FileID ) ) { |
|
|
|
if ( !String.IsNullOrEmpty( this.FileID ) ) { |
|
|
|
this.SequencePoints = this.GetSequencePoints(); |
|
|
|
this.SequencePoints = this.GetSequencePoints(); |
|
|
|
|
|
|
|
this.BodyStartSP = getBodyStartSP(this.SequencePoints); |
|
|
|
|
|
|
|
this.BodyFinalSP = getBodyFinalSP(this.SequencePoints); |
|
|
|
|
|
|
|
this.SequencePoints = this.FilterSequencePoints(this.SequencePoints); |
|
|
|
this.BranchPoints = this.GetBranchPoints(); |
|
|
|
this.BranchPoints = this.GetBranchPoints(); |
|
|
|
this.BranchCoverageRatio = this.GetBranchRatio(); |
|
|
|
this.BranchCoverageRatio = this.GetBranchRatio(); |
|
|
|
this.BranchCoverage = this.GetBranchCoverage(); |
|
|
|
this.BranchCoverage = this.GetBranchCoverage(); |
|
|
@ -136,9 +141,95 @@ namespace ICSharpCode.CodeCoverage |
|
|
|
|
|
|
|
|
|
|
|
sps.Add(sp); |
|
|
|
sps.Add(sp); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SP's are originaly ordered by CIL offset
|
|
|
|
|
|
|
|
// but ccrewrite can move offset of
|
|
|
|
|
|
|
|
// Contract.Requires before method signature SP (xxx{) and
|
|
|
|
|
|
|
|
// Contract.Ensures after method closing SP (})
|
|
|
|
|
|
|
|
// So sort SP's back by line/column
|
|
|
|
|
|
|
|
sps.OrderBy(item => item.Line).OrderBy(item => item.Column); |
|
|
|
return sps; |
|
|
|
return sps; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Find method-body start SequencePoint "xxxx {"
|
|
|
|
|
|
|
|
public static CodeCoverageSequencePoint getBodyStartSP(IEnumerable<CodeCoverageSequencePoint> sPoints) { |
|
|
|
|
|
|
|
CodeCoverageSequencePoint startSeqPoint = null; |
|
|
|
|
|
|
|
bool startFound = false; |
|
|
|
|
|
|
|
foreach (CodeCoverageSequencePoint sPoint in sPoints) { |
|
|
|
|
|
|
|
if ( sPoint.Content == "{") { |
|
|
|
|
|
|
|
if (startSeqPoint == null) startSeqPoint = sPoint; |
|
|
|
|
|
|
|
startFound = true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
startSeqPoint = sPoint; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return startFound == true? startSeqPoint : null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Find method-body final SequencePoint "}"
|
|
|
|
|
|
|
|
public static CodeCoverageSequencePoint getBodyFinalSP(IEnumerable<CodeCoverageSequencePoint> sps) { |
|
|
|
|
|
|
|
CodeCoverageSequencePoint finalSeqPoint = null; |
|
|
|
|
|
|
|
foreach (CodeCoverageSequencePoint sp in Enumerable.Reverse(sps)) { |
|
|
|
|
|
|
|
if ( sp.Content == "}") { |
|
|
|
|
|
|
|
if (finalSeqPoint == null) { |
|
|
|
|
|
|
|
finalSeqPoint = sp; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// check for ccrewrite duplicate
|
|
|
|
|
|
|
|
else if (sp.Line == finalSeqPoint.Line && |
|
|
|
|
|
|
|
sp.Column == finalSeqPoint.Column && |
|
|
|
|
|
|
|
sp.EndLine == finalSeqPoint.EndLine && |
|
|
|
|
|
|
|
sp.EndColumn == finalSeqPoint.EndColumn && |
|
|
|
|
|
|
|
sp.Offset < finalSeqPoint.Offset) { |
|
|
|
|
|
|
|
finalSeqPoint = sp; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return finalSeqPoint; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<CodeCoverageSequencePoint> FilterSequencePoints(List<CodeCoverageSequencePoint> sps) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<CodeCoverageSequencePoint> returnList = sps; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (sps.Count > 2 && |
|
|
|
|
|
|
|
this.BodyStartSP != null && |
|
|
|
|
|
|
|
this.BodyFinalSP != null ) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// After ccrewrite ContractClass/ContractClassFor
|
|
|
|
|
|
|
|
// sequence point(s) from another file/class/method
|
|
|
|
|
|
|
|
// is inserted into this method sequence points
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// To remove alien sequence points, all sequence points on lines
|
|
|
|
|
|
|
|
// before method signature and after end-brackets xxx{} are removed
|
|
|
|
|
|
|
|
// If ContractClassFor is in another file but matches this method lines
|
|
|
|
|
|
|
|
// then, afaik, not much can be done to remove inserted alien SP's
|
|
|
|
|
|
|
|
List<CodeCoverageSequencePoint> selected = new List<CodeCoverageSequencePoint>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var point in sps) { |
|
|
|
|
|
|
|
if ( |
|
|
|
|
|
|
|
(point.Line > BodyStartSP.Line || (point.Line == BodyStartSP.Line && point.Column >= BodyStartSP.Column)) && |
|
|
|
|
|
|
|
(point.Line < BodyFinalSP.Line || (point.Line == BodyFinalSP.Line && point.Column < BodyFinalSP.Column)) |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
selected.Add (point); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// After ccrewrite ContractClass/ContractClassFor
|
|
|
|
|
|
|
|
// duplicate method end-sequence-point (}) is added
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Add first finalSP (can be a duplicate)
|
|
|
|
|
|
|
|
// Note: IL.Offset of second duplicate finalSP will
|
|
|
|
|
|
|
|
// extend branch coverage outside method-end "}",
|
|
|
|
|
|
|
|
// and that can lead to wrong branch coverage display!
|
|
|
|
|
|
|
|
if (object.ReferenceEquals (point, this.BodyFinalSP)) { |
|
|
|
|
|
|
|
selected.Add (point); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
returnList = selected; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return returnList; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int GetSequencePointsCount() { |
|
|
|
int GetSequencePointsCount() { |
|
|
|
XElement summary = this.element.Element("Summary"); |
|
|
|
XElement summary = this.element.Element("Summary"); |
|
|
|
if ( summary != null ) { |
|
|
|
if ( summary != null ) { |
|
|
@ -167,30 +258,6 @@ namespace ICSharpCode.CodeCoverage |
|
|
|
return bps; |
|
|
|
return bps; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Find method-body start SequencePoint "{"
|
|
|
|
|
|
|
|
public static CodeCoverageSequencePoint getBodyStartSP(IEnumerable<CodeCoverageSequencePoint> sps) { |
|
|
|
|
|
|
|
CodeCoverageSequencePoint startSeqPoint = null; |
|
|
|
|
|
|
|
foreach (CodeCoverageSequencePoint sp in sps) { |
|
|
|
|
|
|
|
if ( sp.Content == "{") { |
|
|
|
|
|
|
|
startSeqPoint = sp; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return startSeqPoint; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Find method-body final SequencePoint "}"
|
|
|
|
|
|
|
|
public static CodeCoverageSequencePoint getBodyFinalSP(IEnumerable<CodeCoverageSequencePoint> sps) { |
|
|
|
|
|
|
|
CodeCoverageSequencePoint finalSeqPoint = null; |
|
|
|
|
|
|
|
foreach (CodeCoverageSequencePoint sp in Enumerable.Reverse(sps)) { |
|
|
|
|
|
|
|
if ( sp.Content == "}") { |
|
|
|
|
|
|
|
finalSeqPoint = sp; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return finalSeqPoint; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Tuple<int,int> GetBranchRatio () { |
|
|
|
Tuple<int,int> GetBranchRatio () { |
|
|
|
|
|
|
|
|
|
|
|
// goal: Get branch ratio and exclude (rewriten) Code Contracts branches
|
|
|
|
// goal: Get branch ratio and exclude (rewriten) Code Contracts branches
|
|
|
@ -206,24 +273,22 @@ namespace ICSharpCode.CodeCoverage |
|
|
|
|
|
|
|
|
|
|
|
// This sequence point offset is used to skip CCRewrite(n) BranchPoint's (Requires)
|
|
|
|
// This sequence point offset is used to skip CCRewrite(n) BranchPoint's (Requires)
|
|
|
|
// and '{' branches at static methods
|
|
|
|
// and '{' branches at static methods
|
|
|
|
CodeCoverageSequencePoint startSeqPoint = getBodyStartSP(this.SequencePoints); |
|
|
|
if (this.BodyStartSP == null) { return null; } // empty body
|
|
|
|
if (Object.ReferenceEquals(null, startSeqPoint)) { return null; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This sequence point offset is used to skip CCRewrite(n) BranchPoint's (Ensures)
|
|
|
|
// This sequence point offset is used to skip CCRewrite(n) BranchPoint's (Ensures)
|
|
|
|
CodeCoverageSequencePoint finalSeqPoint = getBodyFinalSP(this.SequencePoints); |
|
|
|
if (this.BodyFinalSP == null) { return null; } // empty body
|
|
|
|
if (Object.ReferenceEquals(null, finalSeqPoint)) { return null; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Connect Sequence & Branches
|
|
|
|
// Connect Sequence & Branches
|
|
|
|
IEnumerator<CodeCoverageSequencePoint> SPEnumerator = this.SequencePoints.GetEnumerator(); |
|
|
|
IEnumerator<CodeCoverageSequencePoint> SPEnumerator = this.SequencePoints.GetEnumerator(); |
|
|
|
CodeCoverageSequencePoint currSeqPoint = startSeqPoint; |
|
|
|
CodeCoverageSequencePoint currSeqPoint = BodyStartSP; |
|
|
|
int nextSeqPointOffset = startSeqPoint.Offset; |
|
|
|
int nextSeqPointOffset = BodyStartSP.Offset; |
|
|
|
|
|
|
|
|
|
|
|
foreach (var bp in this.BranchPoints) { |
|
|
|
foreach (var bp in this.BranchPoints) { |
|
|
|
|
|
|
|
|
|
|
|
// ignore branches outside of method body
|
|
|
|
// ignore branches outside of method body offset range
|
|
|
|
if (bp.Offset < startSeqPoint.Offset) |
|
|
|
if (bp.Offset < BodyStartSP.Offset) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
if (bp.Offset > finalSeqPoint.Offset) |
|
|
|
if (bp.Offset > BodyFinalSP.Offset) |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
// Sync with SequencePoint
|
|
|
|
// Sync with SequencePoint
|
|
|
|