Browse Source

CodeCoverage: Merge branch points on OffsetEnd

pull/67/head
Dragan 12 years ago
parent
commit
9e04fe810d
  1. 3
      src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageBranchPoint.cs
  2. 150
      src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageMethodElement.cs
  3. 2
      src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageSequencePoint.cs
  4. 48
      src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageStringTextSource.cs

3
src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageBranchPoint.cs

@ -11,8 +11,9 @@ namespace ICSharpCode.CodeCoverage
public class CodeCoverageBranchPoint public class CodeCoverageBranchPoint
{ {
public int VisitCount { get; set; } public int VisitCount { get; set; }
public int Offset { get; set; }
public int Path { get; set; } public int Path { get; set; }
public int Offset { get; set; }
public int OffsetEnd { get; set; }
} }
} }

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

@ -17,16 +17,6 @@ namespace ICSharpCode.CodeCoverage
XElement element; XElement element;
CodeCoverageResults parent; CodeCoverageResults parent;
private class branchOffset {
public int Offset;
public int Visit;
public int Count;
public CodeCoverageSequencePoint SeqPoint;
public branchOffset( int offset ) {
this.Offset = offset;
}
}
public CodeCoverageMethodElement(XElement element, CodeCoverageResults parent) public CodeCoverageMethodElement(XElement element, CodeCoverageResults parent)
{ {
this.parent = parent; this.parent = parent;
@ -156,6 +146,7 @@ namespace ICSharpCode.CodeCoverage
bp.VisitCount = (int)GetDecimalAttributeValue(xBPoint.Attribute("vc")); bp.VisitCount = (int)GetDecimalAttributeValue(xBPoint.Attribute("vc"));
bp.Offset = (int)GetDecimalAttributeValue(xBPoint.Attribute("offset")); bp.Offset = (int)GetDecimalAttributeValue(xBPoint.Attribute("offset"));
bp.Path = (int)GetDecimalAttributeValue(xBPoint.Attribute("path")); bp.Path = (int)GetDecimalAttributeValue(xBPoint.Attribute("path"));
bp.OffsetEnd = (int)GetDecimalAttributeValue(xBPoint.Attribute("offsetend"));
bps.Add(bp); bps.Add(bp);
} }
return bps; return bps;
@ -176,19 +167,24 @@ namespace ICSharpCode.CodeCoverage
// Find method-body first SequencePoint "{" // Find method-body first SequencePoint "{"
// and then first next sequencePoint (and offset of that instruction in body) // and then first next sequencePoint (and offset of that instruction in body)
// This will be used to skip CCRewrite(n) BranchPoint's (Requires) // This is used to skip CCRewrite(n) BranchPoint's (Requires)
// and '{' branches at static methods
bool startSPFound = false;
CodeCoverageSequencePoint startSeqPoint = null; CodeCoverageSequencePoint startSeqPoint = null;
foreach (CodeCoverageSequencePoint sp in this.SequencePoints) { foreach (CodeCoverageSequencePoint sp in this.SequencePoints) {
if ( sp.Content == "{") { if (startSPFound) {
startSeqPoint = sp; startSeqPoint = sp;
break; break;
} }
if ( sp.Content == "{") {
startSPFound = true;
}
} }
Debug.Assert (!Object.ReferenceEquals(null, startSeqPoint)); Debug.Assert (!Object.ReferenceEquals(null, startSeqPoint));
if (Object.ReferenceEquals(null, startSeqPoint)) { return null; } if (Object.ReferenceEquals(null, startSeqPoint)) { return null; }
// Find method-body last SequencePoint "}" and offset // Find method-body last SequencePoint "}" and offset
// This will be used to skip CCRewrite(n) BranchPoint's (Ensures) // This is used to skip CCRewrite(n) BranchPoint's (Ensures)
CodeCoverageSequencePoint finalSeqPoint = null; CodeCoverageSequencePoint finalSeqPoint = null;
foreach (CodeCoverageSequencePoint sp in Enumerable.Reverse(this.SequencePoints)) { foreach (CodeCoverageSequencePoint sp in Enumerable.Reverse(this.SequencePoints)) {
if ( sp.Content == "}") { if ( sp.Content == "}") {
@ -199,74 +195,94 @@ namespace ICSharpCode.CodeCoverage
Debug.Assert ( !Object.ReferenceEquals( null, finalSeqPoint) ); Debug.Assert ( !Object.ReferenceEquals( null, finalSeqPoint) );
if (Object.ReferenceEquals(null, finalSeqPoint)) { return null; } if (Object.ReferenceEquals(null, finalSeqPoint)) { return null; }
// Connect Sequence & Branches
IEnumerator<CodeCoverageSequencePoint> SPEnumerator = this.SequencePoints.GetEnumerator(); IEnumerator<CodeCoverageSequencePoint> SPEnumerator = this.SequencePoints.GetEnumerator();
CodeCoverageSequencePoint branchSeqPoint = startSeqPoint; CodeCoverageSequencePoint currSeqPoint = startSeqPoint;
int nextOffset = branchSeqPoint.Offset; int nextSeqPointOffset = startSeqPoint.Offset;
// Merge BranchPoints on same offset foreach (var bp in this.BranchPoints) {
// Exclude BranchPoints outside of method boundary {...} => exclude CCRewrite(n) Contracts
// Exclude BranchPoints from excludeOffsetList // exclude CCRewrite(n) contracts
Dictionary<int, branchOffset> branchDictionary = new Dictionary<int, branchOffset>();
foreach (CodeCoverageBranchPoint bp in this.BranchPoints) {
// exclude CCRewrite(n) contracts
if (bp.Offset < startSeqPoint.Offset) if (bp.Offset < startSeqPoint.Offset)
continue; continue;
if (bp.Offset > finalSeqPoint.Offset) if (bp.Offset > finalSeqPoint.Offset)
break; break;
// merge BranchPoint's with same offset // Sync with SequencePoint
if ( !branchDictionary.ContainsKey( bp.Offset ) ) { while ( nextSeqPointOffset < bp.Offset ) {
// Insert BranchPoint coverage at offset currSeqPoint = SPEnumerator.Current;
branchOffset insert = new branchOffset(bp.Offset); if ( SPEnumerator.MoveNext() ) {
insert.Visit = bp.VisitCount!=0?1:0; nextSeqPointOffset = SPEnumerator.Current.Offset;
insert.Count = 1; } else {
nextSeqPointOffset = int.MaxValue;
// attach Sequence to Branch-Offset
while ( nextOffset < insert.Offset ) {
branchSeqPoint = SPEnumerator.Current;
if ( SPEnumerator.MoveNext() ) {
nextOffset = SPEnumerator.Current.Offset;
} else {
nextOffset = int.MaxValue;
}
} }
insert.SeqPoint = branchSeqPoint;
branchDictionary[insert.Offset] = insert;
} else {
// Update BranchPoint coverage at offset
branchOffset update = branchDictionary[bp.Offset];
update.Visit += bp.VisitCount!=0?1:0;
update.Count += 1;
} }
if (currSeqPoint.Branches == null) {
currSeqPoint.Branches = new List<CodeCoverageBranchPoint>();
}
// Add Branch to Branches
currSeqPoint.Branches.Add(bp);
} }
// for (int i = this.SequencePoints.Count-1; i >= 0; i--) {
// if (this.SequencePoints[i].Content.StartsWith("Assert")) {
// this.SequencePoints.RemoveAt(i);
// }
// }
// Merge sp.Branches on exit-offset
// Calculate Method Branch coverage // Calculate Method Branch coverage
int totalBranchVisit = 0; int totalBranchVisit = 0;
int totalBranchCount = 0; int totalBranchCount = 0;
foreach ( branchOffset uniqueBranch in branchDictionary.Values ) { int pointBranchVisit = 0;
int pointBranchCount = 0;
Dictionary<int, CodeCoverageBranchPoint> bpExits = new Dictionary<int, CodeCoverageBranchPoint>();
foreach (var sp in this.SequencePoints) {
// Generated "in" code for IEnumerables contains hidden "try/catch/finally" branches that // SequencePoint covered & has branches?
// one do not want or cannot cover by test-case because is handled earlier at same method. if (sp.VisitCount != 0 && sp.Branches != null) {
// ie: NullReferenceException in foreach loop is pre-handled at method entry, ie. by Contract.Require(items!=null)
if (uniqueBranch.SeqPoint.Content != "in") {
// Branch coverage will display only if sequence coverage is 100%, so ... // Generated "in" code for IEnumerables contains hidden "try/catch/finally" branches that
// Ignore branch-offset if is not visited at all because ... : // one do not want or cannot cover by test-case because is handled earlier at same method.
// if SequencePoint is covered and branch-offset within that SequencePoint not visited (uncovered), // ie: NullReferenceException in foreach loop is pre-handled at method entry, ie. by Contract.Require(items!=null)
// then that branch-offset does not really exists in SOURCE code we try to cover // exclude Contract class (EnsuresOnThrow)
if ( uniqueBranch.Visit != 0 ) { // exclude NUnit Assert class
totalBranchVisit += uniqueBranch.Visit; if (sp.Content == "in" ||
totalBranchCount += uniqueBranch.Count; sp.Content.StartsWith("Assert.") ||
sp.Content.StartsWith("Assert ") ||
// not full branch coverage? sp.Content.StartsWith("Contract.") ||
if ( uniqueBranch.Visit != uniqueBranch.Count ) { sp.Content.StartsWith("Contract ")
// update attached SequencePoint.BranchCoverage to false (== partial branch coverage) ) {
uniqueBranch.SeqPoint.BranchCoverage = false; sp.Branches = null;
} continue; // skip
} }
}
// Merge sp.Branches on OffsetEnd using bpExits key
bpExits.Clear();
foreach (var bp in sp.Branches) {
if (!bpExits.ContainsKey(bp.OffsetEnd)) {
bpExits[bp.OffsetEnd] = bp; // insert branch
} else {
bpExits[bp.OffsetEnd].VisitCount += bp.VisitCount; // update branch
}
}
// Compute branch coverage
pointBranchVisit = 0;
pointBranchCount = 0;
foreach (var bp in bpExits.Values) {
pointBranchVisit += bp.VisitCount == 0? 0 : 1 ;
pointBranchCount += 1;
}
// Not full coverage?
if (pointBranchVisit != pointBranchCount) {
sp.BranchCoverage = false; // part-covered
}
totalBranchVisit += pointBranchVisit;
totalBranchCount += pointBranchCount;
}
if (sp.Branches != null)
sp.Branches = null; // release memory
} }
return (totalBranchCount!=0) ? new Tuple<int,int>(totalBranchVisit,totalBranchCount) : null; return (totalBranchCount!=0) ? new Tuple<int,int>(totalBranchVisit,totalBranchCount) : null;

2
src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageSequencePoint.cs

@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Xml.Linq; using System.Xml.Linq;
namespace ICSharpCode.CodeCoverage namespace ICSharpCode.CodeCoverage
@ -75,6 +76,7 @@ namespace ICSharpCode.CodeCoverage
public int Length { get; set; } public int Length { get; set; }
public int Offset { get; set; } public int Offset { get; set; }
public bool BranchCoverage { get; set; } public bool BranchCoverage { get; set; }
public List<CodeCoverageBranchPoint> Branches { get; set; }
public override bool Equals(object obj) public override bool Equals(object obj)
{ {

48
src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageStringTextSource.cs

@ -22,44 +22,44 @@ namespace ICSharpCode.CodeCoverage
private readonly lineInfo[] lines; private readonly lineInfo[] lines;
public CodeCoverageStringTextSource(string source) public CodeCoverageStringTextSource(string source)
{ {
// protect readonly field from source mutation(s)
this.textSource = source.Clone() as string; // disconnect from source this.textSource = string.Copy(source);
lineInfo line;
List<lineInfo> lineInfoList = new List<lineInfo>(); List<lineInfo> lineInfoList = new List<lineInfo>();
int offset = 0; int offset = 0;
int counter = 0; int counter = 0;
bool nl = false; bool nl = false;
bool cr = false; bool cr = false;
bool lf = false; bool lf = false;
lineInfo sl;
foreach ( short ch in textSource ) { foreach ( ushort ch in textSource ) {
switch (ch) { switch (ch) {
case 13: case 13:
if (cr||lf) { if (lf || cr) {
nl = true; nl = true; // cr after [cr]lf
} else { } else {
cr = true; cr = true; // cr found
} }
break; break;
case 10: case 10:
if (lf) { if (lf) {
nl = true; nl = true; // lf after lf
} else { } else {
lf = true; lf = true; // lf found
} }
break; break;
default: default:
if (cr||lf) { if (cr||lf) {
nl = true; nl = true; // any noncrlf char after cr|lf
} }
break; break;
} }
if (nl) { if (nl) { // nl detected - add line
sl = new lineInfo(); line = new lineInfo();
sl.Offset = offset; line.Offset = offset;
sl.Length = counter - offset; line.Length = counter - offset;
lineInfoList.Add(sl); lineInfoList.Add(line);
offset = counter; offset = counter;
cr = false; cr = false;
lf = false; lf = false;
@ -67,10 +67,14 @@ namespace ICSharpCode.CodeCoverage
} }
++counter; ++counter;
} }
sl = new lineInfo();
sl.Offset = offset; // Add last line
sl.Length = counter - offset; line = new lineInfo();
lineInfoList.Add(sl); line.Offset = offset;
line.Length = counter - offset;
lineInfoList.Add(line);
// Store to readonly field
lines = lineInfoList.ToArray(); lines = lineInfoList.ToArray();
} }
@ -158,6 +162,12 @@ namespace ICSharpCode.CodeCoverage
return text.ToString(); return text.ToString();
} }
public int LinesCount {
get {
return lines.Length;
}
}
/// <summary>Return SequencePoint enumerated line /// <summary>Return SequencePoint enumerated line
/// </summary> /// </summary>
/// <param name="lineNr"></param> /// <param name="lineNr"></param>

Loading…
Cancel
Save