diff --git a/src/AddIns/Analysis/CodeCoverage/Project/CodeCoverage.csproj b/src/AddIns/Analysis/CodeCoverage/Project/CodeCoverage.csproj index 3cc6476ce1..ff67b8a72a 100644 --- a/src/AddIns/Analysis/CodeCoverage/Project/CodeCoverage.csproj +++ b/src/AddIns/Analysis/CodeCoverage/Project/CodeCoverage.csproj @@ -61,6 +61,7 @@ + UserControl diff --git a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageBranchPoint.cs b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageBranchPoint.cs new file mode 100644 index 0000000000..cec47b1d92 --- /dev/null +++ b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageBranchPoint.cs @@ -0,0 +1,23 @@ +/* + * Created by SharpDevelop. + * User: ddur + * Date: 26/11/2013 + * Time: 02:58 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +using System; + +namespace ICSharpCode.CodeCoverage +{ + /// + /// Description of CodeCoverageBranchPoint. + /// + public class CodeCoverageBranchPoint + { + public int VisitCount { get; set; } + public int Offset { get; set; } + public int Path { get; set; } + } + +} diff --git a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageControl.cs b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageControl.cs index 5ed85b70ac..6a00d5a16b 100644 --- a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageControl.cs +++ b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageControl.cs @@ -300,6 +300,7 @@ namespace ICSharpCode.CodeCoverage item.SubItems.Add(sequencePoint.Column.ToString()); item.SubItems.Add(sequencePoint.EndLine.ToString()); item.SubItems.Add(sequencePoint.EndColumn.ToString()); + item.SubItems.Add(sequencePoint.BranchCovered.ToString()); item.Tag = sequencePoint; listView.Items.Add(item); diff --git a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageHighlighter.cs b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageHighlighter.cs index 2b7cab10e3..9ab6a865c2 100644 --- a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageHighlighter.cs +++ b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageHighlighter.cs @@ -94,7 +94,10 @@ namespace ICSharpCode.CodeCoverage public static Color GetSequencePointColor(CodeCoverageSequencePoint sequencePoint) { if (sequencePoint.VisitCount > 0) { - return CodeCoverageOptions.VisitedColor.ToWpf(); + if ( sequencePoint.BranchCovered ) { + return CodeCoverageOptions.VisitedColor.ToWpf(); + } + return CodeCoverageOptions.PartialVisitedColor.ToWpf(); } return CodeCoverageOptions.NotVisitedColor.ToWpf(); } diff --git a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageMethod.cs b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageMethod.cs index fc7cc8d600..014e3f97c8 100644 --- a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageMethod.cs +++ b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageMethod.cs @@ -48,6 +48,7 @@ namespace ICSharpCode.CodeCoverage this.BranchCoverage = element.BranchCoverage; this.BranchCoverageRatio = element.BranchCoverageRatio; this.SequencePointsCount = element.SequencePointsCount; + this.sequencePoints = element.SequencePoints; } diff --git a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageMethodElement.cs b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageMethodElement.cs index 257092c5c2..5479b04551 100644 --- a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageMethodElement.cs +++ b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageMethodElement.cs @@ -13,17 +13,24 @@ namespace ICSharpCode.CodeCoverage { XElement element; - private class branch { + private class branchOffset { + public int Offset; public int Visit; public int Count; + public branchOffset( int offset ) { + this.Offset = offset; + } } public CodeCoverageMethodElement(XElement element) { this.element = element; + this.SequencePoints = new List(); + this.BranchPoints = new List(); Init(); } + public string FileRef { get; private set; } public bool IsVisited { get; private set; } public int CyclomaticComplexity { get; private set; } public decimal SequenceCoverage { get; private set; } @@ -32,6 +39,8 @@ namespace ICSharpCode.CodeCoverage public Tuple BranchCoverageRatio { get; private set; } public bool IsConstructor { get; private set; } public bool IsStatic { get; private set; } + public List SequencePoints { get; private set; } + public List BranchPoints { get; private set; } public bool IsGetter { get; private set; } public bool IsSetter { get; private set; } @@ -47,46 +56,77 @@ namespace ICSharpCode.CodeCoverage IsGetter = GetBooleanAttributeValue("isGetter"); IsSetter = GetBooleanAttributeValue("isSetter"); + this.FileRef = GetFileRef(); this.IsVisited = this.GetBooleanAttributeValue("visited"); this.CyclomaticComplexity = (int)this.GetDecimalAttributeValue("cyclomaticComplexity"); - this.SequencePointsCount = this.GetSequencePointsCount(); - this.SequenceCoverage = this.GetDecimalAttributeValue("sequenceCoverage"); + this.SequencePoints = this.GetSequencePoints(); + this.SequencePointsCount = this.SequencePoints.Count; + //this.SequencePointsCount = this.GetSequencePointsCount(); + this.SequenceCoverage = (int)this.GetDecimalAttributeValue("sequenceCoverage"); + this.BranchPoints = this.GetBranchPoints(); this.BranchCoverageRatio = this.GetBranchRatio(); this.BranchCoverage = this.GetBranchCoverage(); this.IsConstructor = this.GetBooleanAttributeValue("isConstructor"); this.IsStatic = this.GetBooleanAttributeValue("isStatic"); } + + List GetSequencePoints() { + // get all SequencePoints + List sps = new List(); + var xSPoints = this.element + .Elements("SequencePoints") + .Elements("SequencePoint"); + foreach (XElement xSPoint in xSPoints) { + CodeCoverageSequencePoint sp = new CodeCoverageSequencePoint(); + sp.FileRef = this.FileRef; + sp.Line = (int)GetDecimalAttributeValue(xSPoint.Attribute("sl")); + sp.EndLine = (int)GetDecimalAttributeValue(xSPoint.Attribute("el")); + sp.Column = (int)GetDecimalAttributeValue(xSPoint.Attribute("sc")); + sp.EndColumn = (int)GetDecimalAttributeValue(xSPoint.Attribute("ec")); + sp.VisitCount = (int)GetDecimalAttributeValue(xSPoint.Attribute("vc")); + sp.Length = 1; + sp.BranchCovered = true; + sps.Add(sp); + } + return sps; + } int GetSequencePointsCount() { XElement summary = this.element.Element("Summary"); if ( summary != null ) { XAttribute nsp = summary.Attribute("numSequencePoints"); if ( nsp != null ) { - return (int)this.GetDecimalAttributeValue( nsp ); + return (int)GetDecimalAttributeValue( nsp ); } } return 0; } + List GetBranchPoints() { + // get all SequencePoints + List bps = new List(); + var xBPoints = this.element + .Elements("BranchPoints") + .Elements("BranchPoint"); + foreach (XElement xBPoint in xBPoints) { + CodeCoverageBranchPoint bp = new CodeCoverageBranchPoint(); + bp.VisitCount = (int)GetDecimalAttributeValue(xBPoint.Attribute("vc")); + bp.Offset = (int)GetDecimalAttributeValue(xBPoint.Attribute("offset")); + bp.Path = (int)GetDecimalAttributeValue(xBPoint.Attribute("path")); + bps.Add(bp); + } + return bps; + } + Tuple GetBranchRatio () { // goal: Get branch ratio and exclude (rewriten) Code Contracts branches - // get all BranchPoints - var bPoints = this.element - .Elements("BranchPoints") - .Elements("BranchPoint"); - - if ( bPoints == null || bPoints.Count() == 0 ) { + if ( this.BranchPoints == null || this.BranchPoints.Count() == 0 ) { return null; } - // get all SequencePoints - var sPoints = this.element - .Elements("SequencePoints") - .Elements("SequencePoint"); - // start & final method sequence points line and offset int methodStartLine = 0; int methodStartOffset = 0; @@ -105,14 +145,14 @@ namespace ICSharpCode.CodeCoverage // and then first next sequencePoint (and offset of first instruction in body) // This will skip Contract.Require SequencePoints // and help to filter-out Contract.Require BranchPoint's - foreach (XElement sPoint in sPoints) { - startLine = (int)GetDecimalAttributeValue(sPoint.Attribute("sl")); - finalLine = (int)GetDecimalAttributeValue(sPoint.Attribute("el")); - startChar = (int)GetDecimalAttributeValue(sPoint.Attribute("sc")); - finalChar = (int)GetDecimalAttributeValue(sPoint.Attribute("ec")); + foreach (CodeCoverageSequencePoint sp in this.SequencePoints) { + startLine = sp.Line; + finalLine = sp.EndLine; + startChar = sp.Column; + finalChar = sp.EndColumn; if ( nextMatch ) { methodStartLine = startLine; - methodStartOffset = (int)GetDecimalAttributeValue(sPoint.Attribute("offset")); + methodStartOffset = sp.Offset; break; } if ( startLine==finalLine && (finalChar-startChar)==1 ) { @@ -123,14 +163,14 @@ namespace ICSharpCode.CodeCoverage // find method body last SequencePoint and final offset (}) // This will skip Contract.Ensures SequencePoints // and help to filter-out Contract.Ensures BranchPoint's - foreach (XElement sPoint in sPoints.Reverse()) { - startLine = (int)GetDecimalAttributeValue(sPoint.Attribute("sl")); - finalLine = (int)GetDecimalAttributeValue(sPoint.Attribute("el")); - startChar = (int)GetDecimalAttributeValue(sPoint.Attribute("sc")); - finalChar = (int)GetDecimalAttributeValue(sPoint.Attribute("ec")); + foreach (CodeCoverageSequencePoint sp in Enumerable.Reverse(this.SequencePoints)) { + startLine = sp.Line; + finalLine = sp.EndLine; + startChar = sp.Column; + finalChar = sp.EndColumn; if ( startLine==finalLine && (finalChar-startChar)==1 ) { methodFinalLine = finalLine; - methodFinalOffset = (int)GetDecimalAttributeValue(sPoint.Attribute("offset")); + methodFinalOffset = sp.Offset; break; } } @@ -145,68 +185,83 @@ namespace ICSharpCode.CodeCoverage nextMatch = false; int lastOffset = 0; int lastLine = methodStartLine; - int currLine = 0; List> excludeList = new List>(); - foreach (XElement sPoint in sPoints) { - currLine = (int)GetDecimalAttributeValue(sPoint.Attribute("sl")); - if ( (currLine < methodStartLine) || currLine > methodFinalLine ) { continue ; } + foreach (CodeCoverageSequencePoint sp in this.SequencePoints) { + if ( (sp.Line < methodStartLine) || methodFinalLine < sp.Line ) { continue ; } - if (nextMatch && (currLine > lastLine)) { + if (nextMatch && (sp.Line > lastLine)) { nextMatch = false; - excludeList.Add(new Tuple ( lastOffset , (int)GetDecimalAttributeValue(sPoint.Attribute("offset")))); + excludeList.Add(new Tuple ( lastOffset , sp.Offset )); } // reversed line number? - if (!nextMatch && (currLine < lastLine)) { + if (!nextMatch && (sp.Line < lastLine)) { nextMatch = true; - lastOffset = (int)GetDecimalAttributeValue(sPoint.Attribute("offset")); + lastOffset = sp.Offset; } - lastLine = currLine; + lastLine = sp.Line; } - int visited = 0; - int offset = 0; + // Collect branch offsets within method boundary { } (exclude Contracts) + // and filter with excludeList + Dictionary branchDictionary = new Dictionary(); + foreach (CodeCoverageBranchPoint bp in this.BranchPoints) { - // collect all branch offsets - Dictionary branches = new Dictionary(); - foreach (XElement bPoint in bPoints) { - visited = (int)GetDecimalAttributeValue(bPoint.Attribute("vc")); - offset = (int)GetDecimalAttributeValue(bPoint.Attribute("offset")); - if ( offset > methodStartOffset && offset < methodFinalOffset ) { + if ( methodStartOffset <= bp.Offset && bp.Offset <= methodFinalOffset ) { // Apply exclude BranchPoint filter nextMatch = true; foreach (var range in excludeList) { - if (offset > range.Item1 && offset < range.Item2) { + if (range.Item1 < bp.Offset && bp.Offset < range.Item2) { + // exclude range match nextMatch = false; break; } } if (!nextMatch) { continue; } - // Add/insert coverage data - if ( branches.ContainsKey( offset ) ) { - branch update = branches[offset]; - update.Visit += visited!=0?1:0; + // update/insert branch offset coverage data + if ( branchDictionary.ContainsKey( bp.Offset ) ) { + branchOffset update = branchDictionary[bp.Offset]; + update.Visit += bp.VisitCount!=0?1:0; update.Count += 1; } else { - // Insert first branch - branches[offset] = new branch{Visit=visited!=0?1:0,Count=1}; + // Insert first branch at offset + branchOffset insert = new branchOffset(bp.Offset); + insert.Visit = bp.VisitCount!=0?1:0; + insert.Count = 1; + branchDictionary[insert.Offset] = insert; } } } - int totalVisit = 0; - int totalCount = 0; + int totalBranchVisit = 0; + int totalBranchCount = 0; + CodeCoverageSequencePoint sp_target = null; // Branch percentage will display only if code SequencePoints coverage is 100%, so ... // Do not add branch if branch is not visited at all because ... : // If "branch" is completely unvisited and 100% SequencePoints covered, // then that "branch" does not really exists in SOURCE code we try to cover - foreach ( branch item in branches.Values ) { - if ( item.Visit != 0 ) { - totalVisit += item.Visit; - totalCount += item.Count; + foreach ( branchOffset uniqueBranch in branchDictionary.Values ) { + if ( uniqueBranch.Visit != 0 ) { + totalBranchVisit += uniqueBranch.Visit; + totalBranchCount += uniqueBranch.Count; + + // not full branch coverage? + if ( uniqueBranch.Visit != uniqueBranch.Count ) { + // update matching sequence point branch covered + sp_target = null; + foreach ( CodeCoverageSequencePoint sp in this.SequencePoints ) { + if ( sp.Offset > uniqueBranch.Offset ) { + if ( !Object.ReferenceEquals( sp_target, null ) ) { + sp_target.BranchCovered = false; + } + break; + } + sp_target = sp; + } + } } } - return (totalCount!=0) ? new Tuple(totalVisit,totalCount) : null; + return (totalBranchCount!=0) ? new Tuple(totalBranchVisit,totalBranchCount) : null; } @@ -247,7 +302,15 @@ namespace ICSharpCode.CodeCoverage } return false; } - + + string GetFileRef() { + XElement fileId = element.Element("FileRef"); + if (fileId != null) { + return fileId.Attribute("uid").Value; + } + return String.Empty; + } + string GetMethodName() { XElement nameElement = element.Element("Name"); diff --git a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageOptions.cs b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageOptions.cs index 03190e0ab3..7f347af030 100644 --- a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageOptions.cs +++ b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageOptions.cs @@ -75,6 +75,23 @@ namespace ICSharpCode.CodeCoverage set { Properties.Set(VisitedForeColorProperty, value); } } + /// + /// Gets the colour that will be used when highlighting visited code. + /// + public static Color PartialVisitedColor { + get { return Properties.Get(VisitedColorProperty, Color.Yellow); } + set { Properties.Set(VisitedColorProperty, value); } + } + + /// + /// Gets the foreground colour that will be used when highlighting + /// visited code. + /// + public static Color PartialVisitedForeColor { + get { return Properties.Get(VisitedForeColorProperty, Color.Black); } + set { Properties.Set(VisitedForeColorProperty, value); } + } + /// /// Gets the colour that will be used when highlighting code that has not /// been visited. diff --git a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageResults.cs b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageResults.cs index 971da1e017..330930b2c8 100644 --- a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageResults.cs +++ b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageResults.cs @@ -111,12 +111,18 @@ namespace ICSharpCode.CodeCoverage { CodeCoverageMethod method = new CodeCoverageMethod(className, reader); module.Methods.Add(method); + /* move that code into CodeCoverageMethod/Element var points = reader .Elements("SequencePoints") .Elements("SequencePoint"); foreach (XElement point in points) { AddSequencePoint(method, point, reader); } + */ + // UPDATE SEQUENCE POINTS DOCUMENT + foreach ( var sp in method.SequencePoints ) { + sp.Document = GetFileName(sp.FileRef); + } return method; } diff --git a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageSequencePoint.cs b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageSequencePoint.cs index 4666eeacb7..8a4354dee5 100644 --- a/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageSequencePoint.cs +++ b/src/AddIns/Analysis/CodeCoverage/Project/Src/CodeCoverageSequencePoint.cs @@ -64,6 +64,7 @@ namespace ICSharpCode.CodeCoverage return !String.IsNullOrEmpty(Document); } + public string FileRef { get; set; } public string Document { get; set; } public int VisitCount { get; set; } public int Line { get; set; } @@ -71,6 +72,8 @@ namespace ICSharpCode.CodeCoverage public int EndLine { get; set; } public int EndColumn { get; set; } public int Length { get; set; } + public int Offset { get; set; } + public bool BranchCovered { get; set; } public override bool Equals(object obj) {