Skip to content
This repository was archived by the owner on Nov 15, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build/installer.build
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<include name="Gendarme.Rules.Maintainability.dll" />
<include name="log4net.dll" />
<include name="log4net.config" />
<include name="CrashReporter.NET.dll" />
</fileset>
<fileset basedir="${solution.folder}\bin\Release\x86" prefix="x86">
<include name="OpenCover.Profiler.dll" />
Expand Down
8 changes: 7 additions & 1 deletion main/OpenCover.Framework/Model/SequencePoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,16 @@ public class SequencePoint : InstrumentationPoint, IDocumentReference
/// </summary>
[XmlAttribute("ec")]
public int EndColumn { get; set; }


/// <summary>
/// Count of merged branches
/// </summary>
[XmlAttribute("bec")]
public int BranchExitsCount { get; set; }

/// <summary>
/// Visit count of merged branches
/// </summary>
[XmlAttribute("bev")]
public int BranchExitsVisit { get; set; }

Expand Down
1 change: 1 addition & 0 deletions main/OpenCover.Framework/OpenCover.Framework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
<Compile Include="Symbols\CecilSymbolManager.cs" />
<Compile Include="Symbols\ISymbolManager.cs" />
<Compile Include="Symbols\SymbolFolder.cs" />
<Compile Include="Utility\CodeCoverageStringTextSource.cs" />
<Compile Include="Utility\IPerfCounters.cs" />
<Compile Include="Utility\PerfCounters.cs" />
</ItemGroup>
Expand Down
61 changes: 58 additions & 3 deletions main/OpenCover.Framework/Persistance/BasePersistance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using OpenCover.Framework.Communication;
using OpenCover.Framework.Model;
using OpenCover.Framework.Utility;
using log4net;

namespace OpenCover.Framework.Persistance
Expand Down Expand Up @@ -230,10 +231,44 @@ private void RemoveUnreferencedFiles()
}
}

// static readonly empty collections, saves creation time of new empty ones
private static readonly SequencePoint[] emptySeqPoints = new SequencePoint[0];
private static readonly BranchPoint[] emptyBranchPoints = new BranchPoint[0];
private static readonly List<BranchPoint> emptyBranchList = new List<BranchPoint>(0);

// Dictionary with stored source files per module
private Dictionary<uint, CodeCoverageStringTextSource> sourceRepository = new Dictionary<uint, CodeCoverageStringTextSource>();

// Return if SequencePoint cannot have user defined branches
// So far have detected sequencePoints with branches on '{' '}' (static methods)
// and within keyword 'in'. My guess, VB 'In' is same as C# 'in'
private bool CanContainUserBranches ( SequencePoint sp ) {
if (sp.StartLine == sp.EndLine) {
string content = "";
int spLength = sp.EndColumn - sp.StartColumn;
switch (spLength) {
case 1:
case 2:
if (sp.FileId != 0) {
CodeCoverageStringTextSource source = null;
sourceRepository.TryGetValue(sp.FileId, out source);
if (source != null) {
content = source.GetText(sp);
}
}
break;
}
switch (content) {
case "{": //C#
case "}": //C#
case "in": //C#
case "In": //VB
return false;
}
}
return true;
}

private void PopulateInstrumentedPoints()
{

Expand All @@ -256,15 +291,25 @@ from method in @class.Methods.Where(x => !x.ShouldSerializeSkippedDueTo())

#region Module File/FileID Dictionary

sourceRepository = new Dictionary<uint, CodeCoverageStringTextSource>();
var filesDictionary = new Dictionary<string,uint>();
foreach (var file in (module.Files ?? new File[0]).Where(file => !filesDictionary.ContainsKey(file.FullPath ?? "")))
foreach (var file in (module.Files ?? new File[0]).Where(file => !String.IsNullOrWhiteSpace(file.FullPath) && !filesDictionary.ContainsKey(file.FullPath)))
{
filesDictionary.Add(file.FullPath ?? "", file.UniqueId);
var source = CodeCoverageStringTextSource.GetSource(file.FullPath);
if (source != null) sourceRepository.Add (file.UniqueId, source);
filesDictionary.Add(file.FullPath, file.UniqueId);
}

#endregion

foreach (var @class in (module.Classes ?? new Class[0]).Where(x => !x.ShouldSerializeSkippedDueTo()))
#region TODO:? Merge Compiler Extracted/Generated Methods (enumerator methods)

// Store repeated Query
var classesQuery = (module.Classes ?? new Class[0]).Where(x => !x.ShouldSerializeSkippedDueTo());

#endregion

foreach (var @class in classesQuery)
{

foreach (var method in (@class.Methods ?? new Method[0]).Where(x => !x.ShouldSerializeSkippedDueTo()))
Expand Down Expand Up @@ -339,6 +384,16 @@ from method in @class.Methods.Where(x => !x.ShouldSerializeSkippedDueTo())
}
}

#endregion

#region Remove Compiler Generated Branches

foreach (var sp in sPoints) {
if (sp != null && sp.BranchPoints != null && sp.BranchPoints.Count != 0 && !CanContainUserBranches(sp)) {
sp.BranchPoints = emptyBranchList;
}
}

#endregion

#region Merge Branch-Exits for each Sequence
Expand Down
254 changes: 254 additions & 0 deletions main/OpenCover.Framework/Utility/CodeCoverageStringTextSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
// Copyright (c) https://github.com/ddur
// This code is distributed under MIT license

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using OpenCover.Framework.Model;

namespace OpenCover.Framework.Utility
{
/// <summary>StringTextSource (ReadOnly)
/// <remarks>Line and column counting starts at 1.</remarks>
/// <remarks>IDocument/ITextBuffer/ITextSource fails returning single char "{"?</remarks>
/// </summary>
public class CodeCoverageStringTextSource
{
private readonly string textSource;
private struct lineInfo {
public int Offset;
public int Length;
}
private readonly lineInfo[] lines;
/// <summary>
/// Constructor
/// </summary>
/// <param name="source"></param>
public CodeCoverageStringTextSource(string source)
{
this.textSource = source;

lineInfo line;
var lineInfoList = new List<lineInfo>();
int offset = 0;
int counter = 0;
bool newLine = false;
bool cr = false;
bool lf = false;

foreach ( ushort ch in textSource ) {
switch (ch) {
case 0xD:
if (lf||cr) {
newLine = true; // cr after cr|lf
} else {
cr = true; // cr found
}
break;
case 0xA:
if (lf) {
newLine = true; // lf after lf
} else {
lf = true; // lf found
}
break;
default:
if (cr||lf) {
newLine = true; // any non-line-end char after any line-end
}
break;
}
if (newLine) { // newLine detected - add line
line = new lineInfo();
line.Offset = offset;
line.Length = counter - offset;
lineInfoList.Add(line);
offset = counter;
cr = false;
lf = false;
newLine = false;
}
++counter;
}

// Add last line
line = new lineInfo();
line.Offset = offset;
line.Length = counter - offset;
lineInfoList.Add(line);

// Store to readonly field
lines = lineInfoList.ToArray();
}

/// <summary>Return text/source using SequencePoint line/col info
/// </summary>
/// <param name="sp"></param>
/// <returns></returns>
public string GetText(SequencePoint sp) {
return this.GetText(sp.StartLine, sp.StartColumn, sp.EndLine, sp.EndColumn );
}

/// <summary>Return text at Line/Column/EndLine/EndColumn position
/// <remarks>Line and Column counting starts at 1.</remarks>
/// </summary>
/// <param name="Line"></param>
/// <param name="Column"></param>
/// <param name="EndLine"></param>
/// <param name="EndColumn"></param>
/// <returns></returns>
public string GetText(int Line, int Column, int EndLine, int EndColumn) {

var text = new StringBuilder();
string line;
bool argOutOfRange;

if (Line==EndLine) {

#region One-Line request
line = GetLine(Line);

//Debug.Assert(!(Column < 1), "Column < 1");
//Debug.Assert(!(Column > EndColumn), "Column > EndColumn");
//Debug.Assert(!(EndColumn > line.Length + 1), string.Format ("Single Line EndColumn({0}) > line.Length({1})",EndColumn, line.Length ));
//Debug.Assert(!(EndColumn > line.Length + 1), line);

argOutOfRange = Column < 1
|| Column > EndColumn
|| EndColumn > line.Length;
if (!argOutOfRange) {
text.Append(line.Substring(Column-1,EndColumn-Column));
}
#endregion

} else if (Line<EndLine) {

#region Multi-line request

#region First line
line = GetLine(Line);

//Debug.Assert(!(Column < 1), "Column < 1");
//Debug.Assert(!(Column > line.Length), string.Format ("First MultiLine EndColumn({0}) > line.Length({1})",EndColumn, line.Length ));

argOutOfRange = Column < 1
|| Column > line.Length;
if (!argOutOfRange) {
text.Append(line.Substring(Column-1));
}
#endregion

#region More than two lines
for ( int lineIndex = Line+1; lineIndex < EndLine; lineIndex++ ) {
text.Append ( GetLine ( lineIndex ) );
}
#endregion

#region Last line
line = GetLine(EndLine);

//Debug.Assert(!(EndColumn < 1), "EndColumn < 1");
//Debug.Assert(!(EndColumn > line.Length), string.Format ("Last MultiLine EndColumn({0}) > line.Length({1})",EndColumn, line.Length ));

argOutOfRange = EndColumn < 1
|| EndColumn > line.Length;
if (!argOutOfRange) {
text.Append(line.Substring(0,EndColumn));
}
#endregion

#endregion

} else {
//Debug.Fail("Line > EndLine");
}
return text.ToString();
}

/// <summary>
/// Return number of lines in source
/// </summary>
public int LinesCount {
get {
return lines.Length;
}
}

/// <summary>Return SequencePoint enumerated line
/// </summary>
/// <param name="LineNo"></param>
/// <returns></returns>
public string GetLine ( int LineNo ) {

string retString = String.Empty;

if ( LineNo > 0 && LineNo <= lines.Length ) {
lineInfo lineInfo = lines[LineNo-1];
retString = textSource.Substring(lineInfo.Offset, lineInfo.Length);
} else {
//Debug.Fail( "Line number out of range" );
}

return retString;
}

/// <summary>
///
/// </summary>
/// <param name="ToIndent"></param>
/// <param name="TabSize"></param>
/// <returns></returns>
public static string IndentTabs ( string ToIndent, int TabSize ) {

string retString = ToIndent;
if ( ToIndent.Contains ( "\t" ) ) {
int counter = 0;
int remains = 0;
int repeat = 0;
char prevChar = char.MinValue;
var indented = new StringBuilder();
foreach ( char currChar in ToIndent ) {
if ( currChar == '\t' ) {
remains = counter % TabSize;
repeat = remains == 0 ? TabSize : remains;
indented.Append( ' ', repeat );
} else {
indented.Append ( currChar, 1 );
if ( char.IsLowSurrogate(currChar)
&& char.IsHighSurrogate(prevChar)
) { --counter; }
}
prevChar = currChar;
++counter;
}
retString = indented.ToString();
}
return retString;
}

/// <summary>
/// Get line-parsed source from file name
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
public static CodeCoverageStringTextSource GetSource(string filename) {

var retSource = (CodeCoverageStringTextSource)null;
try {
using (Stream stream = new FileStream(filename, FileMode.Open, FileAccess.Read)) {
try {
stream.Position = 0;
using (var reader = new StreamReader (stream, Encoding.Default, true)) {
retSource = new CodeCoverageStringTextSource(reader.ReadToEnd());
}
} catch (Exception) {}
}
} catch (Exception) {}

return retSource;
}

}
}
Binary file added tools/CrashReporterSigned/CrashReporter.NET.dll
Binary file not shown.