Unity-jump-proj
This commit is contained in:
@ -0,0 +1,8 @@
|
||||
namespace UnityEditor.TestTools.CodeCoverage
|
||||
{
|
||||
internal enum CoverageFormat
|
||||
{
|
||||
OpenCover = 0,
|
||||
DotCover = 1
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8419585fc64d77e4d8e18b7311df59a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 401e5b541d6e30e489f5751eec729fab
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Mono.Reflection;
|
||||
|
||||
namespace UnityEditor.TestTools.CodeCoverage.OpenCover
|
||||
{
|
||||
internal static class CyclomaticComplexity
|
||||
{
|
||||
private static List<Instruction> targets = new List<Instruction>();
|
||||
|
||||
public static int CalculateCyclomaticComplexity(this MethodBase method)
|
||||
{
|
||||
if (method == null || method.GetMethodBody() == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool hasSwitch = false;
|
||||
foreach (Instruction ins in method.GetInstructions())
|
||||
{
|
||||
if (ins.OpCode.OperandType == OperandType.InlineSwitch)
|
||||
{
|
||||
hasSwitch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSwitch)
|
||||
{
|
||||
return GetSwitchCyclomaticComplexity(method);
|
||||
}
|
||||
return GetFastCyclomaticComplexity(method);
|
||||
}
|
||||
|
||||
private static int GetFastCyclomaticComplexity(MethodBase method)
|
||||
{
|
||||
int cc = 1;
|
||||
foreach (Instruction ins in method.GetInstructions())
|
||||
{
|
||||
switch (ins.OpCode.FlowControl)
|
||||
{
|
||||
case FlowControl.Branch:
|
||||
// detect ternary pattern
|
||||
Instruction previous = ins.Previous;
|
||||
if (previous != null && previous.OpCode.Name.StartsWith("ld"))
|
||||
{
|
||||
++cc;
|
||||
}
|
||||
break;
|
||||
|
||||
case FlowControl.Cond_Branch:
|
||||
++cc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return cc;
|
||||
}
|
||||
|
||||
private static int GetSwitchCyclomaticComplexity(MethodBase method)
|
||||
{
|
||||
Instruction previous = null;
|
||||
Instruction branch = null;
|
||||
int cc = 1;
|
||||
|
||||
foreach (Instruction ins in method.GetInstructions())
|
||||
{
|
||||
switch (ins.OpCode.FlowControl)
|
||||
{
|
||||
case FlowControl.Branch:
|
||||
if (previous == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// detect ternary pattern
|
||||
previous = ins.Previous;
|
||||
if (previous.OpCode.Name.StartsWith("ld"))
|
||||
{
|
||||
cc++;
|
||||
}
|
||||
|
||||
// or 'default' (xmcs)
|
||||
if (previous.OpCode.FlowControl == FlowControl.Cond_Branch)
|
||||
{
|
||||
branch = (previous.Operand as Instruction);
|
||||
// branch can be null (e.g. switch -> Instruction[])
|
||||
if ((branch != null) && targets.Contains(branch) && !targets.Contains(ins))
|
||||
{
|
||||
targets.Add(ins);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FlowControl.Cond_Branch:
|
||||
// note: a single switch (C#) with sparse values can be broken into several swicth (IL)
|
||||
// that will use the same 'targets' and must be counted only once
|
||||
if (ins.OpCode.OperandType == OperandType.InlineSwitch)
|
||||
{
|
||||
AccumulateSwitchTargets(ins);
|
||||
}
|
||||
else
|
||||
{
|
||||
// some conditional branch can be related to the sparse switch
|
||||
branch = (ins.Operand as Instruction);
|
||||
previous = branch.Previous;
|
||||
if ((previous != null) && previous.Previous.OpCode.OperandType != OperandType.InlineSwitch)
|
||||
{
|
||||
if (!targets.Contains(branch))
|
||||
{
|
||||
cc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// count all unique targets (and default if more than one C# switch is used)
|
||||
cc += targets.Count;
|
||||
targets.Clear();
|
||||
|
||||
return cc;
|
||||
}
|
||||
|
||||
private static void AccumulateSwitchTargets(Instruction ins)
|
||||
{
|
||||
Instruction[] cases = (Instruction[])ins.Operand;
|
||||
foreach (Instruction target in cases)
|
||||
{
|
||||
// ignore targets that are the next instructions (xmcs)
|
||||
if (target != ins.Next && !targets.Contains(target))
|
||||
targets.Add(target);
|
||||
}
|
||||
// add 'default' branch (if one exists)
|
||||
Instruction next = ins.Next;
|
||||
if (next.OpCode.FlowControl == FlowControl.Branch)
|
||||
{
|
||||
Instruction unc = FindFirstUnconditionalBranchTarget(cases[0]);
|
||||
if (unc != next.Operand && !targets.Contains(next.Operand as Instruction))
|
||||
targets.Add(next.Operand as Instruction);
|
||||
}
|
||||
}
|
||||
|
||||
private static Instruction FindFirstUnconditionalBranchTarget(Instruction ins)
|
||||
{
|
||||
while (ins != null)
|
||||
{
|
||||
if (FlowControl.Branch == ins.OpCode.FlowControl)
|
||||
return ((Instruction)ins.Operand);
|
||||
|
||||
ins = ins.Next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74449815e747b4b1ba000bf66f12e01a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c2ede2081f2f4c45a6f7f4c92e3a735
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,63 @@
|
||||
//
|
||||
// OpenCover - S Wilde
|
||||
//
|
||||
// This source code is released under the MIT License; see the accompanying license file.
|
||||
//
|
||||
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// a branch point
|
||||
/// </summary>
|
||||
public class BranchPoint : InstrumentationPoint, IDocumentReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Line of the branching instruction
|
||||
/// </summary>
|
||||
[XmlAttribute("sl")]
|
||||
public int StartLine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A path that can be taken
|
||||
/// </summary>
|
||||
[XmlAttribute("path")]
|
||||
public int Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of OffsetPoints between Offset and EndOffset (exclusive)
|
||||
/// </summary>
|
||||
[XmlAttribute("offsetchain")]
|
||||
public System.Collections.Generic.List<int> OffsetPoints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should offset points be serialized
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool ShouldSerializeOffsetPoints()
|
||||
{
|
||||
return OffsetPoints.Maybe(_ => _.Any());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Last Offset == EndOffset.
|
||||
/// Can be same as Offset
|
||||
/// </summary>
|
||||
[XmlAttribute("offsetend")]
|
||||
public int EndOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The file associated with the supplied startline
|
||||
/// </summary>
|
||||
[XmlAttribute("fileid")]
|
||||
public uint FileId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The url to the document if an entry was not mapped to an id
|
||||
/// </summary>
|
||||
[XmlAttribute("url")]
|
||||
public string Document { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88cac9839726d46718b126833c76c5e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,49 @@
|
||||
//
|
||||
// OpenCover - S Wilde
|
||||
//
|
||||
// This source code is released under the MIT License; see the accompanying license file.
|
||||
//
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// An entity that contains methods
|
||||
/// </summary>
|
||||
public class Class : SummarySkippedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// instantiate
|
||||
/// </summary>
|
||||
public Class()
|
||||
{
|
||||
Methods = new Method[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The full name of the class
|
||||
/// </summary>
|
||||
public string FullName { get; set; }
|
||||
|
||||
[XmlIgnore]
|
||||
internal File[] Files { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of methods that make up the class
|
||||
/// </summary>
|
||||
public Method[] Methods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If a class was skipped by instrumentation, supply the reason why
|
||||
/// </summary>
|
||||
/// <param name="reason"></param>
|
||||
public override void MarkAsSkipped(SkippedMethod reason)
|
||||
{
|
||||
SkippedDueTo = reason;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0d40fe321f9746dcbccb27824986511
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,54 @@
|
||||
//
|
||||
// OpenCover - S Wilde
|
||||
//
|
||||
// This source code is released under the MIT License; see the accompanying license file.
|
||||
//
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// A coverage session
|
||||
/// </summary>
|
||||
public class CoverageSession
|
||||
{
|
||||
private string _version;
|
||||
|
||||
/// <summary>
|
||||
/// initialise a coverage session
|
||||
/// </summary>
|
||||
public CoverageSession()
|
||||
{
|
||||
Modules = new Module[0];
|
||||
Summary = new Summary();
|
||||
_version = GetType().Assembly.GetName().Version.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// A unique session identifier
|
||||
/// </summary>
|
||||
public string SessionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A Summary of results for the session
|
||||
/// </summary>
|
||||
public Summary Summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of modules that have been profiled under the session
|
||||
/// </summary>
|
||||
public Module[] Modules { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current version
|
||||
/// </summary>
|
||||
[XmlAttribute("Version")]
|
||||
public string Version {
|
||||
get { return _version; }
|
||||
// ReSharper disable once ValueParameterNotUsed
|
||||
set { /* intentionally left blank */} }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 561dcfbc5a4094415b3d8348f1a6b738
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,68 @@
|
||||
//
|
||||
// OpenCover - S Wilde
|
||||
//
|
||||
// This source code is released under the MIT License; see the accompanying license file.
|
||||
//
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// A file reference within the coverage session and is used to point to an existing File entity
|
||||
/// </summary>
|
||||
public class FileRef
|
||||
{
|
||||
/// <summary>
|
||||
/// The uniqueid of a file in a coverage session
|
||||
/// </summary>
|
||||
[XmlAttribute("uid")]
|
||||
public UInt32 UniqueId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// File details of a source file
|
||||
/// </summary>
|
||||
public class File : FileRef
|
||||
{
|
||||
private static int _uId;
|
||||
|
||||
static readonly List<File> Files = new List<File>();
|
||||
|
||||
internal static void ResetAfterLoading()
|
||||
{
|
||||
_uId = (int)Files.Max(x => x.UniqueId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A standard constructor
|
||||
/// </summary>
|
||||
public File()
|
||||
{
|
||||
UniqueId = (UInt32)Interlocked.Increment(ref _uId);
|
||||
Files.Add(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The path to file
|
||||
/// </summary>
|
||||
[XmlAttribute("fullPath")]
|
||||
public string FullPath { get; set; }
|
||||
}
|
||||
|
||||
internal class FileEqualityComparer : IEqualityComparer<File>
|
||||
{
|
||||
public bool Equals(File x, File y)
|
||||
{
|
||||
return x.FullPath == y.FullPath;
|
||||
}
|
||||
|
||||
public int GetHashCode(File obj)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ea0122a5084b4beca6bbb008fea821e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace OpenCover.Framework
|
||||
{
|
||||
internal static class HelperExtensions
|
||||
{
|
||||
public static TRet Maybe<T, TRet>(this T value, Func<T, TRet> action, TRet defValue = default(TRet))
|
||||
where T : class
|
||||
{
|
||||
return (value != null) ? action(value) : defValue;
|
||||
}
|
||||
|
||||
public static T Do<T>(this T value, Action<T> action)
|
||||
where T : class
|
||||
{
|
||||
if (value != null)
|
||||
action(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static T Try<T>(this T value, Action<T> action)
|
||||
where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
if (value != null)
|
||||
action(value);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignore error
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a74e938125304007a53cf9733cd1208
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,23 @@
|
||||
//
|
||||
// OpenCover - S Wilde
|
||||
//
|
||||
// This source code is released under the MIT License; see the accompanying license file.
|
||||
//
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// A point may have a document reference
|
||||
/// </summary>
|
||||
public interface IDocumentReference
|
||||
{
|
||||
/// <summary>
|
||||
/// The document url
|
||||
/// </summary>
|
||||
string Document { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The document id after lookup
|
||||
/// </summary>
|
||||
uint FileId { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebffa2e2071ad4f79a3641b47e74b25e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// An instrumentable point
|
||||
/// </summary>
|
||||
public class InstrumentationPoint
|
||||
{
|
||||
private static int _instrumentPoint;
|
||||
private static readonly object LockObject = new object();
|
||||
private static readonly List<InstrumentationPoint> InstrumentPoints;
|
||||
|
||||
static InstrumentationPoint()
|
||||
{
|
||||
_instrumentPoint = 0;
|
||||
InstrumentPoints = new List<InstrumentationPoint>(8192) {null};
|
||||
}
|
||||
|
||||
internal static void Clear()
|
||||
{
|
||||
InstrumentPoints.Clear();
|
||||
InstrumentPoints.Add(null);
|
||||
_instrumentPoint = 0;
|
||||
}
|
||||
|
||||
internal static void ResetAfterLoading()
|
||||
{
|
||||
var points = InstrumentPoints
|
||||
.Where(x => x != null)
|
||||
.GroupBy(x => x.UniqueSequencePoint)
|
||||
.Select(g => g.OrderBy(x => x.OrigSequencePoint).First())
|
||||
.ToList();
|
||||
|
||||
var max = (int)points.Max(x => x.UniqueSequencePoint);
|
||||
|
||||
InstrumentPoints.Clear();
|
||||
InstrumentPoints.Add(null);
|
||||
|
||||
for (var i = 1; i <= max; i++)
|
||||
{
|
||||
var point = new SequencePoint();
|
||||
InstrumentPoints[i] = point;
|
||||
point.UniqueSequencePoint = (uint)i;
|
||||
}
|
||||
|
||||
foreach (var instrumentationPoint in points)
|
||||
{
|
||||
InstrumentPoints[(int)instrumentationPoint.UniqueSequencePoint] = instrumentationPoint;
|
||||
}
|
||||
|
||||
_instrumentPoint = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the number of visit points
|
||||
/// </summary>
|
||||
public static int Count {
|
||||
get { return InstrumentPoints.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of recorded visit points for this identifier
|
||||
/// </summary>
|
||||
/// <param name="spid">the sequence point identifier - NOTE 0 is not used</param>
|
||||
public static int GetVisitCount(uint spid)
|
||||
{
|
||||
return InstrumentPoints[(int) spid].VisitCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a number of recorded visit ppints against this identifier
|
||||
/// </summary>
|
||||
/// <param name="spid">the sequence point identifier - NOTE 0 is not used</param>
|
||||
/// <param name="trackedMethodId">the id of a tracked method - Note 0 means no method currently tracking</param>
|
||||
/// <param name="amount">the number of visit points to add</param>
|
||||
public static bool AddVisitCount(uint spid, uint trackedMethodId, int amount)
|
||||
{
|
||||
if (spid != 0 && spid < InstrumentPoints.Count)
|
||||
{
|
||||
var point = InstrumentPoints[(int) spid];
|
||||
point.VisitCount += amount;
|
||||
if (point.VisitCount < 0)
|
||||
{
|
||||
point.VisitCount = int.MaxValue;
|
||||
}
|
||||
if (trackedMethodId != 0)
|
||||
{
|
||||
AddOrUpdateTrackingPoint(trackedMethodId, amount, point);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void AddOrUpdateTrackingPoint(uint trackedMethodId, int amount, InstrumentationPoint point)
|
||||
{
|
||||
point._tracked = point._tracked ?? new List<TrackedMethodRef>();
|
||||
var tracked = point._tracked.Find(x => x.UniqueId == trackedMethodId);
|
||||
if (tracked == null)
|
||||
{
|
||||
tracked = new TrackedMethodRef {UniqueId = trackedMethodId, VisitCount = amount};
|
||||
point._tracked.Add(tracked);
|
||||
}
|
||||
else
|
||||
{
|
||||
tracked.VisitCount += amount;
|
||||
if (tracked.VisitCount < 0)
|
||||
tracked.VisitCount = int.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
private List<TrackedMethodRef> _tracked;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise
|
||||
/// </summary>
|
||||
public InstrumentationPoint()
|
||||
{
|
||||
lock (LockObject)
|
||||
{
|
||||
UniqueSequencePoint = (uint)++_instrumentPoint;
|
||||
InstrumentPoints.Add(this);
|
||||
OrigSequencePoint = UniqueSequencePoint;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store the number of visits
|
||||
/// </summary>
|
||||
[XmlAttribute("vc")]
|
||||
public int VisitCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A unique number
|
||||
/// </summary>
|
||||
[XmlAttribute("uspid")]
|
||||
public UInt32 UniqueSequencePoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An order of the point within the method
|
||||
/// </summary>
|
||||
[XmlAttribute("ordinal")]
|
||||
public UInt32 Ordinal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The IL offset of the point
|
||||
/// </summary>
|
||||
[XmlAttribute("offset")]
|
||||
public int Offset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to hide an instrumentation point
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public bool IsSkipped { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of tracked methods
|
||||
/// </summary>
|
||||
public TrackedMethodRef[] TrackedMethodRefs
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_tracked != null)
|
||||
{
|
||||
return _tracked.ToArray();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
_tracked = null;
|
||||
if (value == null)
|
||||
return;
|
||||
_tracked = new List<TrackedMethodRef>(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public UInt32 OrigSequencePoint { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86783b018856d4f82ab6105c443f2ffa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,195 @@
|
||||
//
|
||||
// OpenCover - S Wilde
|
||||
//
|
||||
// This source code is released under the MIT License; see the accompanying license file.
|
||||
//
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// An method entity that can be instrumented
|
||||
/// </summary>
|
||||
public class Method : SummarySkippedEntity
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The MetadataToken used to identify this entity within the assembly
|
||||
/// </summary>
|
||||
public int MetadataToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The full name of the method (method-definition), includes return-type namespace-class::call-name(argument-types)
|
||||
/// </summary>
|
||||
[XmlElement("Name")]
|
||||
public string FullName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A reference to a file in the file collection (used to help visualisation)
|
||||
/// </summary>
|
||||
public FileRef FileRef { get; set; }
|
||||
|
||||
internal UInt32 FileRefUniqueId {
|
||||
get { return FileRef == null? 0 : FileRef.UniqueId; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of sequence points that have been produced for this method
|
||||
/// </summary>
|
||||
public SequencePoint[] SequencePoints {
|
||||
get {
|
||||
return _sequencePoints;
|
||||
}
|
||||
set {
|
||||
_sequencePoints = value ?? new SequencePoint[0];
|
||||
}
|
||||
}
|
||||
private SequencePoint[] _sequencePoints = new SequencePoint[0];
|
||||
|
||||
/// <summary>
|
||||
/// A list of branch points that have been identified for this method
|
||||
/// </summary>
|
||||
public BranchPoint[] BranchPoints {
|
||||
get {
|
||||
return _branchPoints;
|
||||
}
|
||||
set {
|
||||
_branchPoints = value ?? new BranchPoint[0];
|
||||
}
|
||||
}
|
||||
private BranchPoint[] _branchPoints = new BranchPoint[0];
|
||||
|
||||
/// <summary>
|
||||
/// A method point to identify the entry of a method
|
||||
/// </summary>
|
||||
public InstrumentationPoint MethodPoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Has the method been visited
|
||||
/// </summary>
|
||||
[XmlAttribute("visited")]
|
||||
public bool Visited { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the cyclomatic complexity of this method.
|
||||
/// </summary>
|
||||
/// <remarks>Calculated using the Gendarme rules library</remarks>
|
||||
[XmlAttribute("cyclomaticComplexity")]
|
||||
public int CyclomaticComplexity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the NPath complexity of this method.
|
||||
/// </summary>
|
||||
/// <remarks>Product of path branches (ie:path0=2;path1=3;path2=2 =>2*3*2==12</remarks>
|
||||
[XmlAttribute("nPathComplexity")]
|
||||
public int NPathComplexity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the sequence coverage of this method
|
||||
/// </summary>
|
||||
/// <remarks>Rounded for ease</remarks>
|
||||
[XmlAttribute("sequenceCoverage")]
|
||||
public decimal SequenceCoverage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the branch coverage of this method
|
||||
/// </summary>
|
||||
/// <remarks>Rounded for ease</remarks>
|
||||
[XmlAttribute("branchCoverage")]
|
||||
public decimal BranchCoverage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the crap score of this method
|
||||
/// based on the following calculation
|
||||
/// CRAP1(m) = comp(m)^2 * (1 – cov(m)/100)^3 + comp(m)
|
||||
/// </summary>
|
||||
/// <remarks>Rounded for ease</remarks>
|
||||
[XmlAttribute("crapScore")]
|
||||
public decimal CrapScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this method a constructor
|
||||
/// </summary>
|
||||
[XmlAttribute("isConstructor")]
|
||||
public bool IsConstructor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this method static
|
||||
/// </summary>
|
||||
[XmlAttribute("isStatic")]
|
||||
public bool IsStatic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this method a property getter
|
||||
/// </summary>
|
||||
[XmlAttribute("isGetter")]
|
||||
public bool IsGetter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this method a property setter
|
||||
/// </summary>
|
||||
[XmlAttribute("isSetter")]
|
||||
public bool IsSetter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Mark an entity as skipped
|
||||
/// </summary>
|
||||
/// <param name="reason">Provide a reason</param>
|
||||
public override void MarkAsSkipped(SkippedMethod reason)
|
||||
{
|
||||
SkippedDueTo = reason;
|
||||
if (MethodPoint != null)
|
||||
MethodPoint.IsSkipped = true;
|
||||
MethodPoint = null;
|
||||
SequencePoints = null;
|
||||
BranchPoints = null;
|
||||
}
|
||||
|
||||
#region IsGenerated & CallName
|
||||
|
||||
/// <summary>
|
||||
/// True if this.FullName matches generated-method-regex-pattern
|
||||
/// </summary>
|
||||
internal bool IsGenerated {
|
||||
get {
|
||||
if (_resolvedIsGenerated == null) {
|
||||
_resolvedIsGenerated = !(string.IsNullOrEmpty(FullName) || FullName.Trim().Length == 0)
|
||||
&& FullName.Contains("__") // quick test before using regex heavy weapon
|
||||
&& IsGeneratedMethodRegex.IsMatch(FullName);
|
||||
}
|
||||
return _resolvedIsGenerated == true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method "::CallName(". (Name excluding return type, namespace and arguments)
|
||||
/// </summary>
|
||||
internal string CallName {
|
||||
get {
|
||||
if (_resolvedCallName != null)
|
||||
return _resolvedCallName;
|
||||
_resolvedCallName = string.Empty; // init resolve value
|
||||
if (!(string.IsNullOrEmpty(FullName) || FullName.Trim().Length == 0)) {
|
||||
var startIndex = FullName.IndexOf("::", StringComparison.Ordinal);
|
||||
startIndex += 2;
|
||||
var finalIndex = FullName.IndexOf('(', startIndex);
|
||||
if (startIndex > 1 && finalIndex > startIndex) {
|
||||
_resolvedCallName = FullName // resolve cache
|
||||
.Substring(startIndex, finalIndex - startIndex);
|
||||
}
|
||||
}
|
||||
return _resolvedCallName;
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _resolvedIsGenerated;
|
||||
private string _resolvedCallName;
|
||||
private const RegexOptions RegexOptions = System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.ExplicitCapture;
|
||||
private static readonly Regex IsGeneratedMethodRegex = new Regex(@"(<[^\s:>]+>\w__\w)", RegexOptions);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f1f74c5e9b1146168b87690081420c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Unity.TestTools.CodeCoverage.Editor.OpenCover.Model",
|
||||
"references": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": []
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19c3df1967929405fba735480b3da9a7
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,77 @@
|
||||
//
|
||||
// OpenCover - S Wilde
|
||||
//
|
||||
// This source code is released under the MIT License; see the accompanying license file.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// The details of a module
|
||||
/// </summary>
|
||||
public class Module : SummarySkippedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// simple constructor
|
||||
/// </summary>
|
||||
public Module()
|
||||
{
|
||||
Aliases = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The full path name to the module
|
||||
/// </summary>
|
||||
public string ModulePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// GetlastWriteTimeUtc
|
||||
/// </summary>
|
||||
public DateTime ModuleTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of aliases
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public IList<string> Aliases { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the module
|
||||
/// </summary>
|
||||
public string ModuleName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The files that make up the module
|
||||
/// </summary>
|
||||
public File[] Files { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The classes that make up the module
|
||||
/// </summary>
|
||||
public Class[] Classes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Methods that are being tracked i.e. test methods
|
||||
/// </summary>
|
||||
public TrackedMethod[] TrackedMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A hash of the file used to group them together (especially when running against mstest)
|
||||
/// </summary>
|
||||
[XmlAttribute("hash")]
|
||||
public string ModuleHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Mark an entity as skipped
|
||||
/// </summary>
|
||||
/// <param name="reason">Provide a reason</param>
|
||||
public override void MarkAsSkipped(SkippedMethod reason)
|
||||
{
|
||||
SkippedDueTo = reason;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7c00a2c9a9fd476c947d35ae18e21fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,124 @@
|
||||
//
|
||||
// OpenCover - S Wilde
|
||||
//
|
||||
// This source code is released under the MIT License; see the accompanying license file.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// a sequence point
|
||||
/// </summary>
|
||||
public class SequencePoint : InstrumentationPoint, IDocumentReference
|
||||
{
|
||||
/// <summary>
|
||||
/// The start line of the sequence point
|
||||
/// </summary>
|
||||
[XmlAttribute("sl")]
|
||||
public int StartLine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The start column of the sequence point
|
||||
/// </summary>
|
||||
[XmlAttribute("sc")]
|
||||
public int StartColumn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The end line of the sequence point
|
||||
/// </summary>
|
||||
[XmlAttribute("el")]
|
||||
public int EndLine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The end column of the sequence point
|
||||
/// </summary>
|
||||
[XmlAttribute("ec")]
|
||||
public int EndColumn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Count of merged branches
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// The number of branch exits
|
||||
/// </summary>
|
||||
[XmlAttribute("bec")]
|
||||
public int BranchExitsCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Visit count of merged branches
|
||||
/// </summary>
|
||||
[XmlAttribute("bev")]
|
||||
public int BranchExitsVisit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The file associated with the supplied startline
|
||||
/// </summary>
|
||||
[XmlAttribute("fileid")]
|
||||
public uint FileId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The url to the document if an entry was not mapped to an id
|
||||
/// </summary>
|
||||
[XmlAttribute("url")]
|
||||
public string Document { get; set; }
|
||||
|
||||
internal List<BranchPoint> BranchPoints {
|
||||
get{
|
||||
return _branchPoints;
|
||||
}
|
||||
set{
|
||||
_branchPoints = value ?? new List<BranchPoint>();
|
||||
}
|
||||
}
|
||||
private List<BranchPoint> _branchPoints = new List<BranchPoint>();
|
||||
|
||||
/// <summary>
|
||||
/// Property
|
||||
/// </summary>
|
||||
public bool IsSingleCharSequencePoint {
|
||||
get {
|
||||
return (StartLine == EndLine) && (EndColumn - StartColumn) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SonnarQube wants no more than 3 boolean conditions
|
||||
/// </summary>
|
||||
/// <param name="sp"></param>
|
||||
/// <returns></returns>
|
||||
private bool IsLineEqual (SequencePoint sp) {
|
||||
return StartLine == sp.StartLine && EndLine == sp.EndLine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SonnarQube wants no more than 3 boolean conditions
|
||||
/// </summary>
|
||||
/// <param name="sp"></param>
|
||||
/// <returns></returns>
|
||||
private bool IsColumnEqual (SequencePoint sp) {
|
||||
return StartColumn == sp.StartColumn && EndColumn == sp.EndColumn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is Start/End Line/Column equal
|
||||
/// </summary>
|
||||
/// <param name="sp"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsPositionEqual (SequencePoint sp) {
|
||||
return sp != null && IsLineEqual (sp) && IsColumnEqual (sp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is FileId equal? (If FileId is 0 then file is unknown)
|
||||
/// </summary>
|
||||
/// <param name="sp"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsFileIdEqual (SequencePoint sp) {
|
||||
return sp != null && FileId != 0 && FileId == sp.FileId;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 448b5b9c7773f48be878eb5f56008baf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,33 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity can be skipped from coverage but needs to supply a reason
|
||||
/// </summary>
|
||||
public abstract class SkippedEntity
|
||||
{
|
||||
private SkippedMethod? _skippedDueTo;
|
||||
|
||||
/// <summary>
|
||||
/// If this class has been skipped then this value will describe why
|
||||
/// </summary>
|
||||
[XmlAttribute("skippedDueTo")]
|
||||
public SkippedMethod SkippedDueTo
|
||||
{
|
||||
get { return _skippedDueTo.GetValueOrDefault(); }
|
||||
set { _skippedDueTo = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If this class has been skipped then this value will allow the data to be serialized
|
||||
/// </summary>
|
||||
public bool ShouldSerializeSkippedDueTo() { return _skippedDueTo.HasValue; }
|
||||
|
||||
/// <summary>
|
||||
/// Mark an entity as skipped
|
||||
/// </summary>
|
||||
/// <param name="reason">Provide a reason</param>
|
||||
public abstract void MarkAsSkipped(SkippedMethod reason);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2adfa90d9eaaa4702b2457c09db2f5e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,64 @@
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes how a method or class was skipped
|
||||
/// </summary>
|
||||
public enum SkippedMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity was skipped due to a matching exclusion attribute filter
|
||||
/// </summary>
|
||||
Attribute = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Entity was skipped due to a matching exclusion file filter
|
||||
/// </summary>
|
||||
File = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Entity was skipped due to a matching exclusion module/class filter
|
||||
/// </summary>
|
||||
Filter = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Entity was skipped due to a missing PDB
|
||||
/// </summary>
|
||||
MissingPdb = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Entity was skipped by inference (usually related to File filters)
|
||||
/// </summary>
|
||||
Inferred = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Entity (method) was skipped as it is an auto-implemented property.
|
||||
/// </summary>
|
||||
AutoImplementedProperty = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Entity (method) was skipped as it is native code.
|
||||
/// </summary>
|
||||
NativeCode = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Entity (method) was skipped for other reasons.
|
||||
/// </summary>
|
||||
Unknown = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Entity (dll) was skipped due to folder exclusion.
|
||||
/// </summary>
|
||||
FolderExclusion = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Entity (method) was skipped due to being a delegate.
|
||||
/// </summary>
|
||||
Delegate = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Entity (method) was skipped due to being an F# internal implementation
|
||||
/// detail (in either a record or discriminated union type).
|
||||
/// </summary>
|
||||
FSharpInternal = 11,
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e49bf94f7d5348e2a48c3a09f3de201
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// A summary of results
|
||||
/// </summary>
|
||||
public class Summary
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of sequence points
|
||||
/// </summary>
|
||||
[XmlAttribute("numSequencePoints")]
|
||||
public int NumSequencePoints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of sequence points visited
|
||||
/// </summary>
|
||||
[XmlAttribute("visitedSequencePoints")]
|
||||
public int VisitedSequencePoints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of branch points
|
||||
/// </summary>
|
||||
[XmlAttribute("numBranchPoints")]
|
||||
public int NumBranchPoints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of branch points visited
|
||||
/// </summary>
|
||||
[XmlAttribute("visitedBranchPoints")]
|
||||
public int VisitedBranchPoints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the sequence coverage?
|
||||
/// </summary>
|
||||
/// <remarks>Rounded for ease</remarks>
|
||||
[XmlAttribute("sequenceCoverage")]
|
||||
public decimal SequenceCoverage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the branch coverage?
|
||||
/// </summary>
|
||||
/// <remarks>Rounded for ease</remarks>
|
||||
[XmlAttribute("branchCoverage")]
|
||||
public decimal BranchCoverage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the maximum cyclomatic complexity.
|
||||
/// </summary>
|
||||
/// <remarks>Calculated using the Gendarme rules library</remarks>
|
||||
[XmlAttribute("maxCyclomaticComplexity")]
|
||||
public int MaxCyclomaticComplexity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the minimum cyclomatic complexity.
|
||||
/// </summary>
|
||||
/// <remarks>Calculated using the Gendarme rules library</remarks>
|
||||
[XmlAttribute("minCyclomaticComplexity")]
|
||||
public int MinCyclomaticComplexity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the maximum crap score
|
||||
/// </summary>
|
||||
[XmlAttribute("maxCrapScore")]
|
||||
public decimal MaxCrapScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the minimum crap score.
|
||||
/// </summary>
|
||||
[XmlAttribute("minCrapScore")]
|
||||
public decimal MinCrapScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the number of visited classes
|
||||
/// </summary>
|
||||
[XmlAttribute("visitedClasses")]
|
||||
public int VisitedClasses { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the total number of classes
|
||||
/// </summary>
|
||||
[XmlAttribute("numClasses")]
|
||||
public int NumClasses { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the number of visited methods
|
||||
/// </summary>
|
||||
[XmlAttribute("visitedMethods")]
|
||||
public int VisitedMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What is the total number of methods
|
||||
/// </summary>
|
||||
[XmlAttribute("numMethods")]
|
||||
public int NumMethods { get; set; }
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f84724c9beb0444b0b1a7166317d7262
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,28 @@
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// A skipped entity that also carries a Summary object which is not
|
||||
/// always serialized
|
||||
/// </summary>
|
||||
public abstract class SummarySkippedEntity : SkippedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialise
|
||||
/// </summary>
|
||||
protected SummarySkippedEntity()
|
||||
{
|
||||
Summary = new Summary();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Summary of results for a entity
|
||||
/// </summary>
|
||||
public Summary Summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Control serialization of the Summary object
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool ShouldSerializeSummary() { return !ShouldSerializeSkippedDueTo(); }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1b80d503cba7422cb21829a6891cde2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace OpenCover.Framework.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// A reference to a tracked method
|
||||
/// </summary>
|
||||
public class TrackedMethodRef
|
||||
{
|
||||
/// <summary>
|
||||
/// unique id assigned
|
||||
/// </summary>
|
||||
[XmlAttribute("uid")]
|
||||
public UInt32 UniqueId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The visit count
|
||||
/// </summary>
|
||||
[XmlAttribute("vc")]
|
||||
public int VisitCount { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A method being tracked
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class TrackedMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// unique id assigned
|
||||
/// </summary>
|
||||
[XmlAttribute("uid")]
|
||||
public UInt32 UniqueId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The MetadataToken used to identify this entity within the assembly
|
||||
/// </summary>
|
||||
[XmlAttribute("token")]
|
||||
public int MetadataToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method being tracked
|
||||
/// </summary>
|
||||
[XmlAttribute("name")]
|
||||
public string FullName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The reason/plugin why the method is being tracked
|
||||
/// </summary>
|
||||
[XmlAttribute("strategy")]
|
||||
public string Strategy { get; set; }
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7c779bb30ff546c08fce46737f83ec5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6c4c078b09304bfeaae896e2e4ffa9e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,130 @@
|
||||
//
|
||||
// ByteBuffer.cs
|
||||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@novell.com)
|
||||
//
|
||||
// (C) 2009 - 2010 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mono.Reflection {
|
||||
|
||||
class ByteBuffer {
|
||||
|
||||
internal byte [] buffer;
|
||||
internal int position;
|
||||
|
||||
public ByteBuffer (byte [] buffer)
|
||||
{
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public byte ReadByte ()
|
||||
{
|
||||
CheckCanRead (1);
|
||||
return buffer [position++];
|
||||
}
|
||||
|
||||
public byte [] ReadBytes (int length)
|
||||
{
|
||||
CheckCanRead (length);
|
||||
var value = new byte [length];
|
||||
Buffer.BlockCopy (buffer, position, value, 0, length);
|
||||
position += length;
|
||||
return value;
|
||||
}
|
||||
|
||||
public short ReadInt16 ()
|
||||
{
|
||||
CheckCanRead (2);
|
||||
short value = (short) (buffer [position]
|
||||
| (buffer [position + 1] << 8));
|
||||
position += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
public int ReadInt32 ()
|
||||
{
|
||||
CheckCanRead (4);
|
||||
int value = buffer [position]
|
||||
| (buffer [position + 1] << 8)
|
||||
| (buffer [position + 2] << 16)
|
||||
| (buffer [position + 3] << 24);
|
||||
position += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
public long ReadInt64 ()
|
||||
{
|
||||
CheckCanRead (8);
|
||||
uint low = (uint) (buffer [position]
|
||||
| (buffer [position + 1] << 8)
|
||||
| (buffer [position + 2] << 16)
|
||||
| (buffer [position + 3] << 24));
|
||||
|
||||
uint high = (uint) (buffer [position + 4]
|
||||
| (buffer [position + 5] << 8)
|
||||
| (buffer [position + 6] << 16)
|
||||
| (buffer [position + 7] << 24));
|
||||
|
||||
long value = (((long) high) << 32) | low;
|
||||
position += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
public float ReadSingle ()
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian) {
|
||||
var bytes = ReadBytes (4);
|
||||
Array.Reverse (bytes);
|
||||
return BitConverter.ToSingle (bytes, 0);
|
||||
}
|
||||
|
||||
CheckCanRead (4);
|
||||
float value = BitConverter.ToSingle (buffer, position);
|
||||
position += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
public double ReadDouble ()
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian) {
|
||||
var bytes = ReadBytes (8);
|
||||
Array.Reverse (bytes);
|
||||
return BitConverter.ToDouble (bytes, 0);
|
||||
}
|
||||
|
||||
CheckCanRead (8);
|
||||
double value = BitConverter.ToDouble (buffer, position);
|
||||
position += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
void CheckCanRead (int count)
|
||||
{
|
||||
if (position + count > buffer.Length)
|
||||
throw new ArgumentOutOfRangeException ();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68735f878b89447b4925a73a9074dfe9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,45 @@
|
||||
//
|
||||
// Disassembler.cs
|
||||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@novell.com)
|
||||
//
|
||||
// (C) 2009 - 2010 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Mono.Reflection {
|
||||
|
||||
public static class Disassembler {
|
||||
|
||||
public static IList<Instruction> GetInstructions (this MethodBase self)
|
||||
{
|
||||
if (self == null)
|
||||
throw new ArgumentNullException ("self");
|
||||
|
||||
return MethodBodyReader.GetInstructions (self).AsReadOnly ();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2c36198237a34870a85b171e9097887
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,156 @@
|
||||
//
|
||||
// Instruction.cs
|
||||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@novell.com)
|
||||
//
|
||||
// (C) 2009 - 2010 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
|
||||
namespace Mono.Reflection {
|
||||
|
||||
public sealed class Instruction {
|
||||
|
||||
int offset;
|
||||
OpCode opcode;
|
||||
object operand;
|
||||
|
||||
Instruction previous;
|
||||
Instruction next;
|
||||
|
||||
public int Offset {
|
||||
get { return offset; }
|
||||
}
|
||||
|
||||
public OpCode OpCode {
|
||||
get { return opcode; }
|
||||
}
|
||||
|
||||
public object Operand {
|
||||
get { return operand; }
|
||||
internal set { operand = value; }
|
||||
}
|
||||
|
||||
public Instruction Previous {
|
||||
get { return previous; }
|
||||
internal set { previous = value; }
|
||||
}
|
||||
|
||||
public Instruction Next {
|
||||
get { return next; }
|
||||
internal set { next = value; }
|
||||
}
|
||||
|
||||
public int Size {
|
||||
get {
|
||||
int size = opcode.Size;
|
||||
|
||||
switch (opcode.OperandType) {
|
||||
case OperandType.InlineSwitch:
|
||||
size += (1 + ((Instruction []) operand).Length) * 4;
|
||||
break;
|
||||
case OperandType.InlineI8:
|
||||
case OperandType.InlineR:
|
||||
size += 8;
|
||||
break;
|
||||
case OperandType.InlineBrTarget:
|
||||
case OperandType.InlineField:
|
||||
case OperandType.InlineI:
|
||||
case OperandType.InlineMethod:
|
||||
case OperandType.InlineString:
|
||||
case OperandType.InlineTok:
|
||||
case OperandType.InlineType:
|
||||
case OperandType.ShortInlineR:
|
||||
size += 4;
|
||||
break;
|
||||
case OperandType.InlineVar:
|
||||
size += 2;
|
||||
break;
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.ShortInlineI:
|
||||
case OperandType.ShortInlineVar:
|
||||
size += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
internal Instruction (int offset, OpCode opcode)
|
||||
{
|
||||
this.offset = offset;
|
||||
this.opcode = opcode;
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
var instruction = new StringBuilder ();
|
||||
|
||||
AppendLabel (instruction, this);
|
||||
instruction.Append (':');
|
||||
instruction.Append (' ');
|
||||
instruction.Append (opcode.Name);
|
||||
|
||||
if (operand == null)
|
||||
return instruction.ToString ();
|
||||
|
||||
instruction.Append (' ');
|
||||
|
||||
switch (opcode.OperandType) {
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
AppendLabel (instruction, (Instruction) operand);
|
||||
break;
|
||||
case OperandType.InlineSwitch:
|
||||
var labels = (Instruction []) operand;
|
||||
for (int i = 0; i < labels.Length; i++) {
|
||||
if (i > 0)
|
||||
instruction.Append (',');
|
||||
|
||||
AppendLabel (instruction, labels [i]);
|
||||
}
|
||||
break;
|
||||
case OperandType.InlineString:
|
||||
instruction.Append ('\"');
|
||||
instruction.Append (operand);
|
||||
instruction.Append ('\"');
|
||||
break;
|
||||
default:
|
||||
instruction.Append (operand);
|
||||
break;
|
||||
}
|
||||
|
||||
return instruction.ToString ();
|
||||
}
|
||||
|
||||
static void AppendLabel (StringBuilder builder, Instruction instruction)
|
||||
{
|
||||
builder.Append ("IL_");
|
||||
builder.Append (instruction.offset.ToString ("x4"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c54348b43e9e464fa20499d8315beed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,306 @@
|
||||
//
|
||||
// MethodBodyReader.cs
|
||||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@novell.com)
|
||||
//
|
||||
// (C) 2009 - 2010 Novell, Inc. (http://www.novell.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mono.Reflection {
|
||||
|
||||
class MethodBodyReader {
|
||||
|
||||
static readonly OpCode [] one_byte_opcodes;
|
||||
static readonly OpCode [] two_bytes_opcodes;
|
||||
|
||||
static MethodBodyReader ()
|
||||
{
|
||||
one_byte_opcodes = new OpCode [0xe1];
|
||||
two_bytes_opcodes = new OpCode [0x1f];
|
||||
|
||||
var fields = typeof (OpCodes).GetFields (
|
||||
BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
foreach (var field in fields) {
|
||||
var opcode = (OpCode) field.GetValue (null);
|
||||
if (opcode.OpCodeType == OpCodeType.Nternal)
|
||||
continue;
|
||||
|
||||
if (opcode.Size == 1)
|
||||
one_byte_opcodes [opcode.Value] = opcode;
|
||||
else
|
||||
two_bytes_opcodes [opcode.Value & 0xff] = opcode;
|
||||
}
|
||||
}
|
||||
|
||||
readonly MethodBase method;
|
||||
readonly MethodBody body;
|
||||
readonly Module module;
|
||||
readonly Type [] type_arguments;
|
||||
readonly Type [] method_arguments;
|
||||
readonly ByteBuffer il;
|
||||
readonly ParameterInfo this_parameter;
|
||||
readonly ParameterInfo [] parameters;
|
||||
readonly IList<LocalVariableInfo> locals;
|
||||
readonly List<Instruction> instructions;
|
||||
|
||||
MethodBodyReader (MethodBase method)
|
||||
{
|
||||
this.method = method;
|
||||
|
||||
this.body = method.GetMethodBody ();
|
||||
if (this.body == null)
|
||||
throw new ArgumentException ("Method has no body");
|
||||
|
||||
var bytes = body.GetILAsByteArray ();
|
||||
if (bytes == null)
|
||||
throw new ArgumentException ("Can not get the body of the method");
|
||||
|
||||
if (!(method is ConstructorInfo))
|
||||
method_arguments = method.GetGenericArguments ();
|
||||
|
||||
if (method.DeclaringType != null)
|
||||
type_arguments = method.DeclaringType.GetGenericArguments ();
|
||||
|
||||
if (!method.IsStatic)
|
||||
this.this_parameter = new ThisParameter (method);
|
||||
this.parameters = method.GetParameters ();
|
||||
this.locals = body.LocalVariables;
|
||||
this.module = method.Module;
|
||||
this.il = new ByteBuffer (bytes);
|
||||
this.instructions = new List<Instruction> ((bytes.Length + 1) / 2);
|
||||
}
|
||||
|
||||
void ReadInstructions ()
|
||||
{
|
||||
Instruction previous = null;
|
||||
|
||||
while (il.position < il.buffer.Length) {
|
||||
var instruction = new Instruction (il.position, ReadOpCode ());
|
||||
|
||||
ReadOperand (instruction);
|
||||
|
||||
if (previous != null) {
|
||||
instruction.Previous = previous;
|
||||
previous.Next = instruction;
|
||||
}
|
||||
|
||||
instructions.Add (instruction);
|
||||
previous = instruction;
|
||||
}
|
||||
|
||||
ResolveBranches ();
|
||||
}
|
||||
|
||||
void ReadOperand (Instruction instruction)
|
||||
{
|
||||
switch (instruction.OpCode.OperandType) {
|
||||
case OperandType.InlineNone:
|
||||
break;
|
||||
case OperandType.InlineSwitch:
|
||||
int length = il.ReadInt32 ();
|
||||
int base_offset = il.position + (4 * length);
|
||||
int [] branches = new int [length];
|
||||
for (int i = 0; i < length; i++)
|
||||
branches [i] = il.ReadInt32 () + base_offset;
|
||||
|
||||
instruction.Operand = branches;
|
||||
break;
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
instruction.Operand = (((sbyte) il.ReadByte ()) + il.position);
|
||||
break;
|
||||
case OperandType.InlineBrTarget:
|
||||
instruction.Operand = il.ReadInt32 () + il.position;
|
||||
break;
|
||||
case OperandType.ShortInlineI:
|
||||
if (instruction.OpCode == OpCodes.Ldc_I4_S)
|
||||
instruction.Operand = (sbyte) il.ReadByte ();
|
||||
else
|
||||
instruction.Operand = il.ReadByte ();
|
||||
break;
|
||||
case OperandType.InlineI:
|
||||
instruction.Operand = il.ReadInt32 ();
|
||||
break;
|
||||
case OperandType.ShortInlineR:
|
||||
instruction.Operand = il.ReadSingle ();
|
||||
break;
|
||||
case OperandType.InlineR:
|
||||
instruction.Operand = il.ReadDouble ();
|
||||
break;
|
||||
case OperandType.InlineI8:
|
||||
instruction.Operand = il.ReadInt64 ();
|
||||
break;
|
||||
case OperandType.InlineSig:
|
||||
instruction.Operand = module.ResolveSignature (il.ReadInt32 ());
|
||||
break;
|
||||
case OperandType.InlineString:
|
||||
instruction.Operand = module.ResolveString (il.ReadInt32 ());
|
||||
break;
|
||||
case OperandType.InlineTok:
|
||||
{
|
||||
int metaDataToken = il.ReadInt32 ();
|
||||
try
|
||||
{
|
||||
instruction.Operand = module.ResolveMember (metaDataToken, type_arguments, method_arguments);
|
||||
}
|
||||
catch(BadImageFormatException)
|
||||
{
|
||||
instruction.Operand = module.ResolveMember (metaDataToken);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperandType.InlineType:
|
||||
instruction.Operand = module.ResolveType (il.ReadInt32 (), type_arguments, method_arguments);
|
||||
break;
|
||||
case OperandType.InlineMethod:
|
||||
{
|
||||
int metaDataToken = il.ReadInt32 ();
|
||||
try
|
||||
{
|
||||
instruction.Operand = module.ResolveMethod (metaDataToken, type_arguments, method_arguments);
|
||||
}
|
||||
catch(TypeLoadException)
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperandType.InlineField:
|
||||
instruction.Operand = module.ResolveField (il.ReadInt32 (), type_arguments, method_arguments);
|
||||
break;
|
||||
case OperandType.ShortInlineVar:
|
||||
instruction.Operand = GetVariable (instruction, il.ReadByte ());
|
||||
break;
|
||||
case OperandType.InlineVar:
|
||||
instruction.Operand = GetVariable (instruction, il.ReadInt16 ());
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
void ResolveBranches ()
|
||||
{
|
||||
foreach (var instruction in instructions) {
|
||||
switch (instruction.OpCode.OperandType) {
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
instruction.Operand = GetInstruction (instructions, (int) instruction.Operand);
|
||||
break;
|
||||
case OperandType.InlineSwitch:
|
||||
var offsets = (int []) instruction.Operand;
|
||||
var branches = new Instruction [offsets.Length];
|
||||
for (int j = 0; j < offsets.Length; j++)
|
||||
branches [j] = GetInstruction (instructions, offsets [j]);
|
||||
|
||||
instruction.Operand = branches;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Instruction GetInstruction (List<Instruction> instructions, int offset)
|
||||
{
|
||||
var size = instructions.Count;
|
||||
if (offset < 0 || offset > instructions [size - 1].Offset)
|
||||
return null;
|
||||
|
||||
int min = 0;
|
||||
int max = size - 1;
|
||||
while (min <= max) {
|
||||
int mid = min + ((max - min) / 2);
|
||||
var instruction = instructions [mid];
|
||||
var instruction_offset = instruction.Offset;
|
||||
|
||||
if (offset == instruction_offset)
|
||||
return instruction;
|
||||
|
||||
if (offset < instruction_offset)
|
||||
max = mid - 1;
|
||||
else
|
||||
min = mid + 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
object GetVariable (Instruction instruction, int index)
|
||||
{
|
||||
return TargetsLocalVariable (instruction.OpCode)
|
||||
? (object) GetLocalVariable (index)
|
||||
: (object) GetParameter (index);
|
||||
}
|
||||
|
||||
static bool TargetsLocalVariable (OpCode opcode)
|
||||
{
|
||||
return opcode.Name.Contains ("loc");
|
||||
}
|
||||
|
||||
LocalVariableInfo GetLocalVariable (int index)
|
||||
{
|
||||
return locals [index];
|
||||
}
|
||||
|
||||
ParameterInfo GetParameter (int index)
|
||||
{
|
||||
if (method.IsStatic)
|
||||
return parameters [index];
|
||||
|
||||
if (index == 0)
|
||||
return this_parameter;
|
||||
|
||||
return parameters [index - 1];
|
||||
}
|
||||
|
||||
OpCode ReadOpCode ()
|
||||
{
|
||||
byte op = il.ReadByte ();
|
||||
return op != 0xfe
|
||||
? one_byte_opcodes [op]
|
||||
: two_bytes_opcodes [il.ReadByte ()];
|
||||
}
|
||||
|
||||
public static List<Instruction> GetInstructions (MethodBase method)
|
||||
{
|
||||
var reader = new MethodBodyReader (method);
|
||||
reader.ReadInstructions ();
|
||||
return reader.instructions;
|
||||
}
|
||||
|
||||
class ThisParameter : ParameterInfo
|
||||
{
|
||||
public ThisParameter (MethodBase method)
|
||||
{
|
||||
this.MemberImpl = method;
|
||||
this.ClassImpl = method.DeclaringType;
|
||||
this.NameImpl = "this";
|
||||
this.PositionImpl = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a8056948ac804bf891bd101b3216a55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Unity.TestTools.CodeCoverage.Editor.OpenCover.Mono.Reflection",
|
||||
"references": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": []
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eecdf9bdfcb2949619db458f3e24c5e2
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,1062 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using OpenCover.Framework.Model;
|
||||
using Module = OpenCover.Framework.Model.Module;
|
||||
using ModelFile = OpenCover.Framework.Model.File;
|
||||
using File = System.IO.File;
|
||||
using UnityEditor.TestTools.CodeCoverage.Utils;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace UnityEditor.TestTools.CodeCoverage.OpenCover
|
||||
{
|
||||
internal class OpenCoverReporter : ICoverageReporter
|
||||
{
|
||||
private readonly ICoverageStatsProvider m_StatsProvider;
|
||||
private readonly ICoverageReporterFilter m_ReporterFilter;
|
||||
private OpenCoverResultWriter m_Writer;
|
||||
|
||||
private List<MethodBase> m_ExcludedMethods = null;
|
||||
private List<string> m_ExcludedTypes = null;
|
||||
|
||||
private ITestAdaptor m_CurrentTestData;
|
||||
|
||||
private static readonly Dictionary<string, string> m_Operators = new Dictionary<string, string>
|
||||
{
|
||||
{ "op_Addition", "operator+" },
|
||||
{ "op_UnaryPlus", "operator+" },
|
||||
{ "op_Increment", "operator++" },
|
||||
{ "op_Subtraction", "operator-" },
|
||||
{ "op_UnaryNegation", "operator-" },
|
||||
{ "op_Decrement", "operator--" },
|
||||
{ "op_Multiply", "operator*" },
|
||||
{ "op_Division", "operator/" },
|
||||
{ "op_Modulus", "operator%" },
|
||||
{ "op_ExclusiveOr", "operator^" },
|
||||
{ "op_BitwiseAnd", "operator&" },
|
||||
{ "op_BitwiseOr", "operator|" },
|
||||
{ "op_LeftShift", "operator<<" },
|
||||
{ "op_RightShift", "operator>>" },
|
||||
{ "op_Equality", "operator==" },
|
||||
{ "op_Inequality", "operator!=" },
|
||||
{ "op_GreaterThan", "operator>" },
|
||||
{ "op_LessThan", "operator<" },
|
||||
{ "op_GreaterThanOrEqual", "operator>=" },
|
||||
{ "op_LessThanOrEqual", "operator<=" },
|
||||
{ "op_OnesComplement", "operator~" },
|
||||
{ "op_LogicalNot", "operator!" },
|
||||
{ "op_True", "operator true" },
|
||||
{ "op_False", "operator false" }
|
||||
};
|
||||
|
||||
public OpenCoverReporter() : this(new OpenCoverReporterFilter(), new CoverageStats())
|
||||
{
|
||||
}
|
||||
|
||||
internal OpenCoverReporter(ICoverageReporterFilter reporterFilter, ICoverageStatsProvider statsProvider)
|
||||
{
|
||||
m_ReporterFilter = reporterFilter;
|
||||
m_StatsProvider = statsProvider;
|
||||
}
|
||||
|
||||
public ICoverageReporterFilter GetReporterFilter()
|
||||
{
|
||||
return m_ReporterFilter;
|
||||
}
|
||||
|
||||
public void OnBeforeAssemblyReload()
|
||||
{
|
||||
if (m_CurrentTestData != null && m_ReporterFilter.ShouldGenerateTestReferences())
|
||||
{
|
||||
OutputVisitedCoverageReport(CoverageRunData.instance.isRecording, true, m_CurrentTestData);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputVisitedCoverageReport(true, !CoverageRunData.instance.isRecording);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCoverageRecordingPaused()
|
||||
{
|
||||
OutputVisitedCoverageReport();
|
||||
}
|
||||
|
||||
public void OnInitialise(CoverageSettings settings)
|
||||
{
|
||||
if (!m_ReporterFilter.ShouldGenerateTestReferences() && settings.resetCoverageData)
|
||||
{
|
||||
m_StatsProvider.ResetAll();
|
||||
}
|
||||
|
||||
m_ReporterFilter.SetupFiltering();
|
||||
|
||||
CoverageRunData.instance.testSuite = TestContext.Parameters.Get("platform");
|
||||
|
||||
if (m_Writer == null)
|
||||
{
|
||||
m_Writer = new OpenCoverResultWriter(settings);
|
||||
}
|
||||
m_Writer.SetupCoveragePaths();
|
||||
}
|
||||
|
||||
public void OnRunStarted(ITestAdaptor testsToRun)
|
||||
{
|
||||
Events.InvokeOnCoverageSessionStarted();
|
||||
|
||||
CoverageRunData.instance.testSuite = TestContext.Parameters.Get("platform");
|
||||
|
||||
if (m_Writer != null)
|
||||
{
|
||||
if (!CommandLineManager.instance.dontClear)
|
||||
m_Writer.ClearCoverageFolderIfExists();
|
||||
m_Writer.SetupCoveragePaths();
|
||||
|
||||
OutputFullEmptyReport();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRunFinished(ITestResultAdaptor testResults)
|
||||
{
|
||||
if (CoverageRunData.instance.isRecording || !m_ReporterFilter.ShouldGenerateTestReferences())
|
||||
{
|
||||
OutputVisitedCoverageReport(false);
|
||||
}
|
||||
|
||||
Events.InvokeOnCoverageSessionFinished();
|
||||
}
|
||||
|
||||
public void OnTestStarted(ITestAdaptor test)
|
||||
{
|
||||
if (m_ReporterFilter.ShouldGenerateTestReferences())
|
||||
{
|
||||
m_CurrentTestData = test;
|
||||
m_StatsProvider.ResetAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnTestFinished(ITestResultAdaptor result)
|
||||
{
|
||||
if (m_ReporterFilter.ShouldGenerateTestReferences())
|
||||
{
|
||||
OutputVisitedCoverageReport(CoverageRunData.instance.isRecording, true, result.Test);
|
||||
}
|
||||
}
|
||||
|
||||
private void OutputFullEmptyReport()
|
||||
{
|
||||
if (m_Writer != null)
|
||||
{
|
||||
// Exit if generateRootEmptyReport arg is passed and a root fullEmpty report has already been generated
|
||||
if (CommandLineManager.instance.generateRootEmptyReport)
|
||||
{
|
||||
string rootFullEmptyPath = m_Writer.GetRootFullEmptyPath();
|
||||
if (File.Exists(rootFullEmptyPath))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CommandLineManager.instance.batchmode)
|
||||
EditorUtility.DisplayProgressBar(OpenCoverReporterStyles.ProgressTitle.text, OpenCoverReporterStyles.ProgressWritingFile.text, 0.85f);
|
||||
|
||||
CoverageSession coverageSession = GenerateOpenCoverSession(CoverageReportType.FullEmpty);
|
||||
if (coverageSession != null)
|
||||
{
|
||||
m_Writer.CoverageSession = coverageSession;
|
||||
m_Writer.WriteCoverageSession(CoverageReportType.FullEmpty);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResultsLogger.Log(ResultID.Warning_NoCoverageResultsSaved, CoverageUtils.GetFilteringLogParams(m_ReporterFilter.GetAssemblyFiltering(), m_ReporterFilter.GetPathFiltering()) );
|
||||
}
|
||||
|
||||
if (!CommandLineManager.instance.batchmode)
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
}
|
||||
|
||||
public void OutputVisitedCoverageReport(bool clearProgressBar = true, bool logNoCoverageResultsSavedWarning = true, ITestAdaptor testData = null)
|
||||
{
|
||||
if (!CommandLineManager.instance.batchmode)
|
||||
EditorUtility.DisplayProgressBar(OpenCoverReporterStyles.ProgressTitle.text, OpenCoverReporterStyles.ProgressWritingFile.text, 0.85f);
|
||||
|
||||
MethodInfo testMethodInfo = testData != null ? testData.Method.MethodInfo : null;
|
||||
|
||||
CoverageSession coverageSession = GenerateOpenCoverSession(CoverageReportType.CoveredMethodsOnly, testMethodInfo);
|
||||
if (coverageSession != null && m_Writer != null)
|
||||
{
|
||||
m_Writer.CoverageSession = coverageSession;
|
||||
m_Writer.WriteCoverageSession(CoverageReportType.CoveredMethodsOnly);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logNoCoverageResultsSavedWarning)
|
||||
{
|
||||
if (CoverageRunData.instance.isRecordingPaused)
|
||||
{
|
||||
ResultsLogger.Log(ResultID.Warning_NoVisitedCoverageResultsSavedRecordingPaused, CoverageUtils.GetFilteringLogParams(m_ReporterFilter.GetAssemblyFiltering(), m_ReporterFilter.GetPathFiltering()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ResultsLogger.Log(ResultID.Warning_NoVisitedCoverageResultsSaved, CoverageUtils.GetFilteringLogParams(m_ReporterFilter.GetAssemblyFiltering(), m_ReporterFilter.GetPathFiltering()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clearProgressBar)
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
private bool IsSpecialMethod(MethodBase methodBase)
|
||||
{
|
||||
return methodBase.IsSpecialName && ((methodBase.Attributes & MethodAttributes.HideBySig) != 0);
|
||||
}
|
||||
|
||||
private bool IsConstructor(MethodBase methodBase)
|
||||
{
|
||||
return IsSpecialMethod(methodBase) && methodBase.MemberType == MemberTypes.Constructor && methodBase.Name == ".ctor";
|
||||
}
|
||||
|
||||
private bool IsStaticConstructor(MethodBase methodBase)
|
||||
{
|
||||
return IsSpecialMethod(methodBase) && methodBase.IsStatic && methodBase.MemberType == MemberTypes.Constructor && methodBase.Name == ".cctor";
|
||||
}
|
||||
|
||||
private bool IsPropertySetter(MethodBase methodBase)
|
||||
{
|
||||
return IsSpecialMethod(methodBase) && methodBase.Name.StartsWith("set_");
|
||||
}
|
||||
|
||||
private bool IsPropertyGetter(MethodBase methodBase)
|
||||
{
|
||||
return IsSpecialMethod(methodBase) && methodBase.Name.StartsWith("get_");
|
||||
}
|
||||
|
||||
private bool IsOperator(MethodBase methodBase)
|
||||
{
|
||||
return IsSpecialMethod(methodBase) && methodBase.Name.StartsWith("op_");
|
||||
}
|
||||
|
||||
private bool IsAnonymousOrInnerMethod(MethodBase methodBase)
|
||||
{
|
||||
char[] invalidChars = { '<', '>' };
|
||||
return methodBase.Name.IndexOfAny(invalidChars) != -1;
|
||||
}
|
||||
|
||||
private string GenerateOperatorName(MethodBase methodBase)
|
||||
{
|
||||
string operatorName = string.Empty;
|
||||
|
||||
if (!m_Operators.TryGetValue(methodBase.Name, out operatorName))
|
||||
{
|
||||
switch (methodBase.Name)
|
||||
{
|
||||
case "op_Implicit":
|
||||
operatorName = $"implicit operator {GetReturnTypeName(methodBase)}";
|
||||
break;
|
||||
|
||||
case "op_Explicit":
|
||||
operatorName = $"explicit operator {GetReturnTypeName(methodBase)}";
|
||||
break;
|
||||
|
||||
default:
|
||||
operatorName = $"unknown operator {methodBase.Name}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return operatorName;
|
||||
}
|
||||
|
||||
private string GetReturnTypeName(MethodBase methodBase)
|
||||
{
|
||||
string returnTypeName = string.Empty;
|
||||
MethodInfo methodInfo = methodBase as MethodInfo;
|
||||
if (methodInfo != null)
|
||||
{
|
||||
returnTypeName = GenerateTypeName(methodInfo.ReturnType);
|
||||
}
|
||||
|
||||
return returnTypeName;
|
||||
}
|
||||
|
||||
internal string GenerateMethodName(MethodBase methodBase)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (methodBase.IsStatic)
|
||||
{
|
||||
sb.Append("static ");
|
||||
}
|
||||
|
||||
string returnTypeName = GetReturnTypeName(methodBase);
|
||||
if (returnTypeName != string.Empty)
|
||||
{
|
||||
sb.Append(returnTypeName);
|
||||
sb.Append(' ');
|
||||
}
|
||||
|
||||
StringBuilder methodStringBuilder = new StringBuilder();
|
||||
|
||||
methodStringBuilder.Append(GenerateTypeName(methodBase.DeclaringType));
|
||||
methodStringBuilder.Append(".");
|
||||
bool lastDotSubstituted = false;
|
||||
|
||||
if (IsConstructor(methodBase) || IsStaticConstructor(methodBase))
|
||||
{
|
||||
methodStringBuilder.Append(GenerateConstructorName(methodBase.DeclaringType));
|
||||
}
|
||||
else if (IsOperator(methodBase))
|
||||
{
|
||||
lastDotSubstituted = SubstituteLastDotWithDoubleColon(ref methodStringBuilder);
|
||||
methodStringBuilder.Append(GenerateOperatorName(methodBase));
|
||||
}
|
||||
else if (IsAnonymousOrInnerMethod(methodBase))
|
||||
{
|
||||
lastDotSubstituted = SubstituteLastDotWithDoubleColon(ref methodStringBuilder);
|
||||
methodStringBuilder.Append(methodBase.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
methodStringBuilder.Append(methodBase.Name);
|
||||
}
|
||||
|
||||
if (!lastDotSubstituted)
|
||||
{
|
||||
SubstituteLastDotWithDoubleColon(ref methodStringBuilder);
|
||||
}
|
||||
|
||||
sb.Append(methodStringBuilder.ToString());
|
||||
|
||||
if (methodBase.IsGenericMethodDefinition)
|
||||
{
|
||||
Type[] types = methodBase.GetGenericArguments();
|
||||
sb.Append(GenerateGenericTypeList(types));
|
||||
}
|
||||
sb.Append('(');
|
||||
ParameterInfo[] parameterInfos = methodBase.GetParameters();
|
||||
for (int i=0; i<parameterInfos.Length; ++i)
|
||||
{
|
||||
sb.Append(GenerateTypeName(parameterInfos[i].ParameterType));
|
||||
|
||||
if (i != parameterInfos.Length - 1)
|
||||
sb.Append(", ");
|
||||
}
|
||||
sb.Append(')');
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private bool SubstituteLastDotWithDoubleColon(ref StringBuilder sb)
|
||||
{
|
||||
bool substituted = false;
|
||||
|
||||
if ( sb.Length > 0)
|
||||
{
|
||||
int lastDotPos = -1;
|
||||
for (int i = sb.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (sb[i] == '.')
|
||||
{
|
||||
lastDotPos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastDotPos != -1 )
|
||||
{
|
||||
sb.Remove(lastDotPos, 1);
|
||||
sb.Insert(lastDotPos, "::");
|
||||
substituted = true;
|
||||
}
|
||||
}
|
||||
|
||||
return substituted;
|
||||
}
|
||||
|
||||
private string GenerateTypeName(Type type)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (type != null)
|
||||
{
|
||||
if (type.IsGenericTypeDefinition || type.IsGenericType)
|
||||
{
|
||||
sb.Append(GenerateGenericTypeName(type));
|
||||
}
|
||||
else if (type.IsGenericParameter)
|
||||
{
|
||||
sb.Append(type.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(type.FullName);
|
||||
}
|
||||
|
||||
// Replace + with / so as nested classes appear in the same file
|
||||
sb.Replace('+', '/');
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private string GenerateGenericTypeName(Type type)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (type != null)
|
||||
{
|
||||
if (type.IsGenericTypeDefinition || type.IsGenericType)
|
||||
{
|
||||
// When IsGenericType the FullName includes unnecessary information and thus cannot be used.
|
||||
// Therefore we use the Name instead and add the Namespace at the beginning
|
||||
if (!type.IsGenericTypeDefinition && type.IsGenericType && type.Namespace != string.Empty)
|
||||
{
|
||||
sb.Append(type.Namespace);
|
||||
sb.Append('.');
|
||||
}
|
||||
|
||||
string[] splitTypes = type.IsGenericTypeDefinition ? type.FullName.Split('+') : type.Name.Split('+');
|
||||
Type[] genericTypeArguments = type.GetGenericArguments();
|
||||
int genericTypeArgumentIndex = 0;
|
||||
|
||||
int numOfTypes = splitTypes.Length;
|
||||
for (int i = 0; i < numOfTypes; ++i)
|
||||
{
|
||||
string splitType = splitTypes[i];
|
||||
|
||||
int genericSeparatorIndex = splitType.LastIndexOf('`');
|
||||
if (genericSeparatorIndex != -1)
|
||||
{
|
||||
sb.Append(splitType.Substring(0, genericSeparatorIndex));
|
||||
string argumentIndexStr = splitType.Substring(genericSeparatorIndex+1);
|
||||
|
||||
int numOfArguments;
|
||||
if (Int32.TryParse(argumentIndexStr, out numOfArguments))
|
||||
{
|
||||
sb.Append("[");
|
||||
for (int j = 0; j < numOfArguments; ++j)
|
||||
{
|
||||
if (genericTypeArgumentIndex < genericTypeArguments.Length)
|
||||
{
|
||||
sb.Append($"{genericTypeArguments[genericTypeArgumentIndex++].Name}");
|
||||
|
||||
if (j < numOfArguments - 1)
|
||||
sb.Append(",");
|
||||
}
|
||||
}
|
||||
sb.Append("]");
|
||||
}
|
||||
|
||||
if (i < numOfTypes - 1)
|
||||
sb.Append("/");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(splitType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private string GenerateConstructorName(Type type)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
string typeName = type.Name;
|
||||
|
||||
int genericSeparatorIndex = typeName.LastIndexOf('`');
|
||||
if (genericSeparatorIndex != -1)
|
||||
{
|
||||
sb.Append(typeName.Substring(0, genericSeparatorIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(typeName);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private string GenerateGenericTypeList(Type[] genericTypes)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append('[');
|
||||
for (int i = 0; i < genericTypes.Length; ++i)
|
||||
{
|
||||
sb.Append(genericTypes[i].Name);
|
||||
|
||||
if (i != genericTypes.Length - 1)
|
||||
sb.Append(", ");
|
||||
}
|
||||
sb.Append(']');
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
internal CoverageSession GenerateOpenCoverSession(CoverageReportType reportType, MethodInfo testMethodInfo = null)
|
||||
{
|
||||
ResultsLogger.LogSessionItem("Started OpenCover Session", LogVerbosityLevel.Info);
|
||||
CoverageSession coverageSession = null;
|
||||
|
||||
UInt32 fileUID = 0;
|
||||
UInt32 trackedMethodUID = 0;
|
||||
List<Module> moduleList = new List<Module>();
|
||||
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
float progressInterval = 0.9f / assemblies.Length;
|
||||
float currentProgress = 0.0f;
|
||||
|
||||
bool shouldGenerateAdditionalMetrics = m_ReporterFilter.ShouldGenerateAdditionalMetrics();
|
||||
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
string assemblyName = assembly.GetName().Name.ToLower();
|
||||
|
||||
currentProgress += progressInterval;
|
||||
|
||||
if (!CommandLineManager.instance.batchmode)
|
||||
EditorUtility.DisplayProgressBar(OpenCoverReporterStyles.ProgressTitle.text, OpenCoverReporterStyles.ProgressGatheringResults.text, currentProgress);
|
||||
|
||||
if (!m_ReporterFilter.ShouldProcessAssembly(assemblyName))
|
||||
{
|
||||
ResultsLogger.LogSessionItem($"Excluded assembly (Assembly Filtering): {assemblyName}", LogVerbosityLevel.Verbose);
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (assembly.GetCustomAttribute<ExcludeFromCoverageAttribute>() != null ||
|
||||
assembly.GetCustomAttribute<ExcludeFromCodeCoverageAttribute>() != null)
|
||||
{
|
||||
ResultsLogger.LogSessionItem($"Excluded assembly (ExcludeFromCoverage): {assemblyName}", LogVerbosityLevel.Info);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
ResultsLogger.Log(ResultID.Warning_ExcludeAttributeAssembly, assemblyName);
|
||||
}
|
||||
|
||||
List<Class> coveredClasses = new List<Class>();
|
||||
List<string> filesNotFound = new List<string>();
|
||||
Dictionary<string, UInt32> fileList = new Dictionary<string, UInt32>();
|
||||
Type[] assemblyTypes = null;
|
||||
m_ExcludedMethods = null;
|
||||
m_ExcludedTypes = null;
|
||||
|
||||
try
|
||||
{
|
||||
assemblyTypes = assembly.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
// This exception can be thrown if some of the types from this assembly can't be loaded. If this
|
||||
// happens, the Types property array contains a Type for all loaded types and null for each
|
||||
// type that couldn't be loaded.
|
||||
assemblyTypes = ex.Types;
|
||||
m_ReporterFilter.ShouldProcessAssembly(assemblyName);
|
||||
}
|
||||
|
||||
if (assemblyTypes == null)
|
||||
{
|
||||
ResultsLogger.Log(ResultID.Assert_NullAssemblyTypes);
|
||||
continue;
|
||||
}
|
||||
|
||||
ResultsLogger.LogSessionItem($"Processing assembly: {assemblyName}", LogVerbosityLevel.Info);
|
||||
|
||||
foreach (Type type in assemblyTypes)
|
||||
{
|
||||
// The type can be null if the ReflectionTypeLoadException has been thrown previously.
|
||||
if (type == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string className = type.FullName;
|
||||
|
||||
try
|
||||
{
|
||||
if (type.GetCustomAttribute<ExcludeFromCoverageAttribute>() != null ||
|
||||
type.GetCustomAttribute<ExcludeFromCodeCoverageAttribute>() != null ||
|
||||
CheckIfParentMemberIsExcluded(type))
|
||||
{
|
||||
ResultsLogger.LogSessionItem($"Excluded class (ExcludeFromCoverage): {className}, assembly: {assemblyName}", LogVerbosityLevel.Info);
|
||||
if (m_ExcludedTypes == null)
|
||||
m_ExcludedTypes = new List<string>();
|
||||
m_ExcludedTypes.Add(type.FullName);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
ResultsLogger.Log(ResultID.Warning_ExcludeAttributeClass, className, assemblyName);
|
||||
}
|
||||
|
||||
ResultsLogger.LogSessionItem($"Processing class: {className}, assembly: {assemblyName}", LogVerbosityLevel.Verbose);
|
||||
|
||||
CoveredMethodStats[] classMethodStatsArray = m_StatsProvider.GetStatsFor(type);
|
||||
if (classMethodStatsArray.Length > 0)
|
||||
{
|
||||
List<Method> coveredMethods = new List<Method>();
|
||||
|
||||
foreach (CoveredMethodStats classMethodStats in classMethodStatsArray)
|
||||
{
|
||||
MethodBase method = classMethodStats.method;
|
||||
|
||||
if (method == null)
|
||||
continue;
|
||||
|
||||
string methodName = method.Name;
|
||||
|
||||
try
|
||||
{
|
||||
if (method.GetCustomAttribute<ExcludeFromCoverageAttribute>() != null ||
|
||||
method.GetCustomAttribute<ExcludeFromCodeCoverageAttribute>() != null ||
|
||||
CheckIfParentMemberIsExcluded(method))
|
||||
{
|
||||
ResultsLogger.LogSessionItem($"Excluded method (ExcludeFromCoverage): {methodName}, class: {className}, assembly: {assemblyName}", LogVerbosityLevel.Info);
|
||||
if (m_ExcludedMethods == null)
|
||||
m_ExcludedMethods = new List<MethodBase>();
|
||||
m_ExcludedMethods.Add(method);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
ResultsLogger.Log(ResultID.Warning_ExcludeAttributeMethod, methodName, className, assemblyName);
|
||||
}
|
||||
|
||||
if (IsGetterSetterPropertyExcluded(method, type))
|
||||
{
|
||||
ResultsLogger.LogSessionItem($"Excluded method (ExcludeFromCoverage): {methodName}, class: {className}, assembly: {assemblyName}", LogVerbosityLevel.Info);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (classMethodStats.totalSequencePoints > 0)
|
||||
{
|
||||
List<SequencePoint> coveredSequencePoints = new List<SequencePoint>();
|
||||
|
||||
uint fileId = 0;
|
||||
int totalVisitCount = 0;
|
||||
CoveredSequencePoint[] classMethodSequencePointsArray = m_StatsProvider.GetSequencePointsFor(method);
|
||||
foreach (CoveredSequencePoint classMethodSequencePoint in classMethodSequencePointsArray)
|
||||
{
|
||||
// Skip hidden sequence points
|
||||
if (classMethodSequencePoint.line == 0xfeefee) // 16707566
|
||||
{
|
||||
ResultsLogger.LogSessionItem($"Ignored sequence point - Invalid line number: {classMethodSequencePoint.line} in method: {methodName} in class: {className}", LogVerbosityLevel.Info);
|
||||
continue;
|
||||
}
|
||||
|
||||
string filename = classMethodSequencePoint.filename;
|
||||
if (filesNotFound.Contains(filename) || !m_ReporterFilter.ShouldProcessFile(filename))
|
||||
{
|
||||
ResultsLogger.LogSessionItem($"Excluded method (Path Filtering): {methodName}, class: {className}, assembly: {assemblyName}", LogVerbosityLevel.Verbose);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fileList.TryGetValue(filename, out fileId))
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
filesNotFound.Add(filename);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileId = ++fileUID;
|
||||
fileList.Add(filename, fileId);
|
||||
}
|
||||
}
|
||||
|
||||
SequencePoint coveredSequencePoint = new SequencePoint();
|
||||
coveredSequencePoint.FileId = fileId;
|
||||
coveredSequencePoint.StartLine = (int)classMethodSequencePoint.line;
|
||||
coveredSequencePoint.StartColumn = (int)classMethodSequencePoint.column;
|
||||
coveredSequencePoint.EndLine = (int)classMethodSequencePoint.line;
|
||||
coveredSequencePoint.EndColumn = (int)classMethodSequencePoint.column;
|
||||
coveredSequencePoint.VisitCount = reportType == CoverageReportType.FullEmpty ? 0 : (int)classMethodSequencePoint.hitCount;
|
||||
totalVisitCount += coveredSequencePoint.VisitCount;
|
||||
coveredSequencePoint.Offset = (int)classMethodSequencePoint.ilOffset;
|
||||
|
||||
if (testMethodInfo != null)
|
||||
{
|
||||
SetupTrackedMethod(coveredSequencePoint, trackedMethodUID);
|
||||
}
|
||||
|
||||
coveredSequencePoints.Add(coveredSequencePoint);
|
||||
}
|
||||
|
||||
bool includeMethod = true;
|
||||
|
||||
if (reportType == CoverageReportType.CoveredMethodsOnly && totalVisitCount == 0 && !type.IsGenericType && !method.IsGenericMethod)
|
||||
{
|
||||
includeMethod = false;
|
||||
}
|
||||
else if (coveredSequencePoints.Count == 0)
|
||||
{
|
||||
includeMethod = false;
|
||||
}
|
||||
|
||||
if (includeMethod)
|
||||
{
|
||||
Method coveredMethod = new Method();
|
||||
coveredMethod.MetadataToken = method.MetadataToken;
|
||||
coveredMethod.FullName = GenerateMethodName(method);
|
||||
coveredMethod.FileRef = new FileRef() { UniqueId = fileId };
|
||||
coveredMethod.IsConstructor = IsConstructor(method) || IsStaticConstructor(method);
|
||||
coveredMethod.IsStatic = method.IsStatic;
|
||||
coveredMethod.IsSetter = IsPropertySetter(method);
|
||||
coveredMethod.IsGetter = IsPropertyGetter(method);
|
||||
coveredMethod.SequencePoints = coveredSequencePoints.ToArray();
|
||||
if (shouldGenerateAdditionalMetrics)
|
||||
{
|
||||
coveredMethod.CyclomaticComplexity = method.CalculateCyclomaticComplexity();
|
||||
}
|
||||
|
||||
ResultsLogger.LogSessionItem($"Processing method: {coveredMethod.FullName}, class: {className}, assembly: {assemblyName}", LogVerbosityLevel.Verbose);
|
||||
|
||||
coveredMethods.Add(coveredMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (coveredMethods.Count > 0)
|
||||
{
|
||||
Class coveredClass = new Class();
|
||||
coveredClass.FullName = GenerateTypeName(type);
|
||||
coveredClass.Methods = coveredMethods.ToArray();
|
||||
coveredClasses.Add(coveredClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (coveredClasses.Count != 0)
|
||||
{
|
||||
Module module = new Module();
|
||||
module.ModuleName = assembly.GetName().Name;
|
||||
|
||||
if (testMethodInfo != null)
|
||||
{
|
||||
SetupTrackedMethod(module, testMethodInfo, trackedMethodUID);
|
||||
trackedMethodUID++;
|
||||
}
|
||||
|
||||
List<ModelFile> coveredFileList = new List<ModelFile>();
|
||||
foreach (KeyValuePair<string, UInt32> fileEntry in fileList)
|
||||
{
|
||||
ModelFile coveredFile = new ModelFile();
|
||||
coveredFile.FullPath = CoverageUtils.NormaliseFolderSeparators(fileEntry.Key);
|
||||
if (CommandLineManager.instance.pathReplacingSpecified)
|
||||
coveredFile.FullPath = CommandLineManager.instance.pathReplacing.ReplacePath(coveredFile.FullPath);
|
||||
coveredFile.UniqueId = fileEntry.Value;
|
||||
|
||||
coveredFileList.Add(coveredFile);
|
||||
}
|
||||
module.Files = coveredFileList.ToArray();
|
||||
module.Classes = coveredClasses.ToArray();
|
||||
moduleList.Add(module);
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleList.Count > 0)
|
||||
{
|
||||
coverageSession = new CoverageSession();
|
||||
coverageSession.Modules = moduleList.ToArray();
|
||||
|
||||
if (reportType != CoverageReportType.FullEmpty)
|
||||
ProcessGenericMethods(coverageSession);
|
||||
|
||||
foreach (Module coveredModule in moduleList)
|
||||
{
|
||||
foreach (Class coveredClass in coveredModule.Classes)
|
||||
{
|
||||
foreach (Method coveredMethod in coveredClass.Methods)
|
||||
{
|
||||
UpdateMethodSummary(coveredMethod);
|
||||
}
|
||||
UpdateClassSummary(coveredClass);
|
||||
}
|
||||
UpdateModuleSummary(coveredModule);
|
||||
}
|
||||
|
||||
UpdateSessionSummary(coverageSession);
|
||||
}
|
||||
|
||||
ResultsLogger.LogSessionItem("Finished OpenCover Session", LogVerbosityLevel.Info);
|
||||
|
||||
return coverageSession;
|
||||
}
|
||||
|
||||
internal bool IsGetterSetterPropertyExcluded(MethodBase method, Type type)
|
||||
{
|
||||
if (IsPropertySetter(method) || IsPropertyGetter(method))
|
||||
{
|
||||
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
|
||||
PropertyInfo property = properties.FirstOrDefault(pInfo => pInfo.GetMethod?.Name.Equals(method.Name) == true || pInfo.SetMethod?.Name.Equals(method.Name) == true);
|
||||
|
||||
if (property?.GetCustomAttribute<ExcludeFromCoverageAttribute>() != null ||
|
||||
property?.GetCustomAttribute<ExcludeFromCodeCoverageAttribute>() != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetupTrackedMethod(SequencePoint sequencePoint, uint id)
|
||||
{
|
||||
TrackedMethodRef trackedMethodRef = new TrackedMethodRef();
|
||||
trackedMethodRef.UniqueId = id;
|
||||
trackedMethodRef.VisitCount = sequencePoint.VisitCount;
|
||||
TrackedMethodRef[] trackedMethodRefs = new TrackedMethodRef[1];
|
||||
trackedMethodRefs[0] = trackedMethodRef;
|
||||
sequencePoint.TrackedMethodRefs = trackedMethodRefs;
|
||||
}
|
||||
|
||||
private void SetupTrackedMethod(Module module, MethodInfo testMethodInfo, uint id)
|
||||
{
|
||||
if (module.TrackedMethods == null)
|
||||
{
|
||||
TrackedMethod trackedMethod = new TrackedMethod();
|
||||
trackedMethod.UniqueId = id;
|
||||
trackedMethod.MetadataToken = testMethodInfo.MetadataToken;
|
||||
trackedMethod.FullName = GenerateMethodName(testMethodInfo);
|
||||
trackedMethod.Strategy = "Test";
|
||||
module.TrackedMethods = new TrackedMethod[1];
|
||||
module.TrackedMethods[0] = trackedMethod;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMethodSummary(Method coveredMethod)
|
||||
{
|
||||
int totalSequencePoints = coveredMethod.SequencePoints.Length;
|
||||
int numCoveredSequencePoints = coveredMethod.SequencePoints.Count(sp => sp.VisitCount > 0);
|
||||
|
||||
coveredMethod.Visited = numCoveredSequencePoints != 0;
|
||||
coveredMethod.Summary.NumClasses = 0;
|
||||
coveredMethod.Summary.VisitedClasses = 0;
|
||||
coveredMethod.Summary.NumMethods = 1;
|
||||
coveredMethod.Summary.VisitedMethods = (coveredMethod.Visited) ? 1 : 0;
|
||||
coveredMethod.Summary.NumSequencePoints = totalSequencePoints;
|
||||
coveredMethod.Summary.VisitedSequencePoints = numCoveredSequencePoints;
|
||||
CalculateSummarySequenceCoverage(coveredMethod.Summary);
|
||||
coveredMethod.SequenceCoverage = coveredMethod.Summary.SequenceCoverage;
|
||||
if (m_ReporterFilter.ShouldGenerateAdditionalMetrics())
|
||||
{
|
||||
coveredMethod.Summary.MaxCyclomaticComplexity = coveredMethod.CyclomaticComplexity;
|
||||
coveredMethod.Summary.MinCyclomaticComplexity = coveredMethod.CyclomaticComplexity;
|
||||
coveredMethod.CrapScore = CalculateCrapScore(coveredMethod.CyclomaticComplexity, coveredMethod.SequenceCoverage);
|
||||
coveredMethod.Summary.MaxCrapScore = coveredMethod.CrapScore;
|
||||
coveredMethod.Summary.MinCrapScore = coveredMethod.CrapScore;
|
||||
}
|
||||
}
|
||||
|
||||
internal decimal CalculateCrapScore(int cyclomaticComplexity, decimal sequenceCoverage)
|
||||
{
|
||||
decimal crapScore = Math.Round((decimal)Math.Pow(cyclomaticComplexity, 2) *
|
||||
(decimal)Math.Pow(1.0 - (double)(sequenceCoverage / (decimal)100.0), 3.0) +
|
||||
cyclomaticComplexity,
|
||||
2);
|
||||
|
||||
return crapScore;
|
||||
}
|
||||
|
||||
private void UpdateClassSummary(Class coveredClass)
|
||||
{
|
||||
coveredClass.Summary.NumClasses = 1;
|
||||
UpdateSummary(coveredClass.Summary, coveredClass.Methods);
|
||||
coveredClass.Summary.VisitedClasses = (coveredClass.Summary.VisitedMethods > 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
private void UpdateModuleSummary(Module coveredModule)
|
||||
{
|
||||
UpdateSummary(coveredModule.Summary, coveredModule.Classes);
|
||||
}
|
||||
|
||||
private void UpdateSessionSummary(CoverageSession coverageSession)
|
||||
{
|
||||
UpdateSummary(coverageSession.Summary, coverageSession.Modules);
|
||||
}
|
||||
|
||||
private void UpdateSummary(Summary summary, SummarySkippedEntity[] entities)
|
||||
{
|
||||
if (entities.Length > 0)
|
||||
{
|
||||
foreach (Summary entitySummary in entities.Select(entity => entity.Summary))
|
||||
{
|
||||
summary.NumSequencePoints += entitySummary.NumSequencePoints;
|
||||
summary.VisitedSequencePoints += entitySummary.VisitedSequencePoints;
|
||||
summary.NumBranchPoints += entitySummary.NumBranchPoints;
|
||||
summary.VisitedBranchPoints += entitySummary.VisitedBranchPoints;
|
||||
summary.NumMethods += entitySummary.NumMethods;
|
||||
summary.VisitedMethods += entitySummary.VisitedMethods;
|
||||
summary.NumClasses += entitySummary.NumClasses;
|
||||
summary.VisitedClasses += entitySummary.VisitedClasses;
|
||||
}
|
||||
|
||||
summary.MaxCyclomaticComplexity = entities.Max(entity => entity.Summary.MaxCyclomaticComplexity);
|
||||
summary.MinCyclomaticComplexity = entities.Min(entity => entity.Summary.MinCyclomaticComplexity);
|
||||
summary.MaxCrapScore = entities.Max(entity => entity.Summary.MaxCrapScore);
|
||||
summary.MinCrapScore = entities.Min(entity => entity.Summary.MinCrapScore);
|
||||
|
||||
CalculateSummarySequenceCoverage(summary);
|
||||
CalculateSummaryBranchCoverage(summary);
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateSummarySequenceCoverage(Summary summary)
|
||||
{
|
||||
if (summary.NumSequencePoints > 0)
|
||||
{
|
||||
summary.SequenceCoverage = decimal.Round(100.0m * (summary.VisitedSequencePoints / (decimal)summary.NumSequencePoints), 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
summary.SequenceCoverage = 0.0m;
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateSummaryBranchCoverage(Summary summary)
|
||||
{
|
||||
if (summary.NumBranchPoints > 0)
|
||||
{
|
||||
summary.BranchCoverage = decimal.Round(100.0m * (summary.VisitedBranchPoints / (decimal)summary.NumBranchPoints), 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
summary.BranchCoverage = 0.0m;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessGenericMethods(CoverageSession coverageSession)
|
||||
{
|
||||
CoveredMethodStats[] coveredMethodStats = m_StatsProvider.GetStatsForAllCoveredMethods();
|
||||
|
||||
if (m_ReporterFilter.ShouldGenerateTestReferences())
|
||||
{
|
||||
foreach (Module module in coverageSession.Modules)
|
||||
{
|
||||
foreach (Class klass in module.Classes)
|
||||
{
|
||||
// Keep methods that have some coverage
|
||||
klass.Methods = klass.Methods.Where(method => coveredMethodStats.Any(coveredMethod => coveredMethod.method.MetadataToken == method.MetadataToken)).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (MethodBase method in coveredMethodStats.Select(coveredMethodStat => coveredMethodStat.method))
|
||||
{
|
||||
Type declaringType = method.DeclaringType;
|
||||
|
||||
if (!(declaringType.IsGenericType || method.IsGenericMethod))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string assemblyName = declaringType.Assembly.GetName().Name.ToLower();
|
||||
if (!m_ReporterFilter.ShouldProcessAssembly(assemblyName))
|
||||
{
|
||||
ResultsLogger.LogSessionItem($"Excluded generic method (Assembly Filtering): {method.Name}, assembly: {assemblyName}", LogVerbosityLevel.Verbose);
|
||||
continue;
|
||||
}
|
||||
|
||||
Module module = Array.Find(coverageSession.Modules, element => element.ModuleName.ToLower() == assemblyName);
|
||||
if (module != null)
|
||||
{
|
||||
string className = string.Empty;
|
||||
if (declaringType.IsGenericType)
|
||||
{
|
||||
Type genericTypeDefinition = declaringType.GetGenericTypeDefinition();
|
||||
className = GenerateTypeName(genericTypeDefinition);
|
||||
}
|
||||
else if (method.IsGenericMethod)
|
||||
{
|
||||
className = GenerateTypeName(declaringType);
|
||||
}
|
||||
|
||||
Class klass = Array.Find(module.Classes, element => element.FullName == className);
|
||||
if (klass != null)
|
||||
{
|
||||
Method targetMethod = Array.Find(klass.Methods, element => element.MetadataToken == method.MetadataToken);
|
||||
if (targetMethod != null)
|
||||
{
|
||||
ResultsLogger.LogSessionItem($"Processing generic method: {method.Name}, assembly: {assemblyName}", LogVerbosityLevel.Verbose);
|
||||
|
||||
CoveredSequencePoint[] coveredSequencePoints = m_StatsProvider.GetSequencePointsFor(method);
|
||||
foreach (CoveredSequencePoint coveredSequencePoint in coveredSequencePoints)
|
||||
{
|
||||
SequencePoint targetSequencePoint = Array.Find(targetMethod.SequencePoints, element => (element.StartLine == coveredSequencePoint.line && element.Offset == coveredSequencePoint.ilOffset));
|
||||
if (targetSequencePoint != null)
|
||||
{
|
||||
targetSequencePoint.VisitCount += (int)coveredSequencePoint.hitCount;
|
||||
if (targetSequencePoint.TrackedMethodRefs != null && targetSequencePoint.TrackedMethodRefs.Length > 0)
|
||||
{
|
||||
targetSequencePoint.TrackedMethodRefs[0].VisitCount += (int)coveredSequencePoint.hitCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the parent member (type or method) is excluded
|
||||
private bool CheckIfParentMemberIsExcluded(MemberInfo member)
|
||||
{
|
||||
if (m_ExcludedMethods == null && m_ExcludedTypes == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Type declaringType = member.DeclaringType;
|
||||
|
||||
while (declaringType != null)
|
||||
{
|
||||
// If parent type is excluded return true
|
||||
if (m_ExcludedTypes != null &&
|
||||
m_ExcludedTypes.Any(typeName => typeName == declaringType.FullName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_ExcludedMethods != null)
|
||||
{
|
||||
// If parent method is excluded return true
|
||||
foreach (var excludedMethod in m_ExcludedMethods)
|
||||
{
|
||||
if (declaringType.FullName == excludedMethod.DeclaringType.FullName &&
|
||||
CheckIfParentMethodIsExcluded(member.Name, excludedMethod.Name))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declaringType = declaringType.DeclaringType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckIfParentMethodIsExcluded(string methodName, string excludedMethodName)
|
||||
{
|
||||
return
|
||||
// Lambda method
|
||||
methodName.IndexOf($"<{excludedMethodName}>b__") != -1 ||
|
||||
// yield method
|
||||
methodName.IndexOf($"<{excludedMethodName}>d__") != -1;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 258e29582df66a741937fe410463ad97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,75 @@
|
||||
namespace UnityEditor.TestTools.CodeCoverage.OpenCover
|
||||
{
|
||||
internal class OpenCoverReporterFilter : ICoverageReporterFilter
|
||||
{
|
||||
private AssemblyFiltering m_AssemblyFiltering;
|
||||
private PathFiltering m_PathFiltering;
|
||||
|
||||
public void SetupFiltering()
|
||||
{
|
||||
if (!CommandLineManager.instance.runFromCommandLine || !CommandLineManager.instance.assemblyFiltersSpecified)
|
||||
{
|
||||
m_AssemblyFiltering = new AssemblyFiltering();
|
||||
|
||||
string includeAssemblies = CoveragePreferences.instance.GetString("IncludeAssemblies", AssemblyFiltering.GetUserOnlyAssembliesString());
|
||||
m_AssemblyFiltering.Parse(includeAssemblies, string.Empty);
|
||||
}
|
||||
|
||||
if (!CommandLineManager.instance.runFromCommandLine || !CommandLineManager.instance.pathFiltersSpecified)
|
||||
{
|
||||
m_PathFiltering = new PathFiltering();
|
||||
|
||||
string pathsToInclude = CoveragePreferences.instance.GetStringForPaths("PathsToInclude", string.Empty);
|
||||
string pathsToExclude = CoveragePreferences.instance.GetStringForPaths("PathsToExclude", string.Empty);
|
||||
|
||||
m_PathFiltering.Parse(pathsToInclude, pathsToExclude);
|
||||
}
|
||||
}
|
||||
|
||||
public AssemblyFiltering GetAssemblyFiltering()
|
||||
{
|
||||
if (CommandLineManager.instance.batchmode && !CommandLineManager.instance.useProjectSettings)
|
||||
return CommandLineManager.instance.assemblyFiltering;
|
||||
else
|
||||
return CommandLineManager.instance.assemblyFiltersSpecified ?
|
||||
CommandLineManager.instance.assemblyFiltering :
|
||||
m_AssemblyFiltering;
|
||||
}
|
||||
|
||||
public bool ShouldProcessAssembly(string assemblyName)
|
||||
{
|
||||
return GetAssemblyFiltering().IsAssemblyIncluded(assemblyName);
|
||||
}
|
||||
|
||||
public PathFiltering GetPathFiltering()
|
||||
{
|
||||
if (CommandLineManager.instance.batchmode && !CommandLineManager.instance.useProjectSettings)
|
||||
return CommandLineManager.instance.pathFiltering;
|
||||
else
|
||||
return CommandLineManager.instance.pathFiltersSpecified ?
|
||||
CommandLineManager.instance.pathFiltering :
|
||||
m_PathFiltering;
|
||||
}
|
||||
|
||||
public bool ShouldProcessFile(string filename)
|
||||
{
|
||||
return GetPathFiltering().IsPathIncluded(filename);
|
||||
}
|
||||
|
||||
public bool ShouldGenerateAdditionalMetrics()
|
||||
{
|
||||
if (CommandLineManager.instance.batchmode && !CommandLineManager.instance.useProjectSettings)
|
||||
return CommandLineManager.instance.generateAdditionalMetrics;
|
||||
else
|
||||
return CommandLineManager.instance.generateAdditionalMetrics || CoveragePreferences.instance.GetBool("GenerateAdditionalMetrics", false);
|
||||
}
|
||||
|
||||
public bool ShouldGenerateTestReferences()
|
||||
{
|
||||
if (CommandLineManager.instance.batchmode && !CommandLineManager.instance.useProjectSettings)
|
||||
return CommandLineManager.instance.generateTestReferences;
|
||||
else
|
||||
return CommandLineManager.instance.generateTestReferences || CoveragePreferences.instance.GetBool("GenerateTestReferences", false);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04c157fc0c43e40708bcbe2aa8738140
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.CodeCoverage
|
||||
{
|
||||
internal static class OpenCoverReporterStyles
|
||||
{
|
||||
public static readonly GUIContent ProgressTitle = EditorGUIUtility.TrTextContent("Code Coverage");
|
||||
public static readonly GUIContent ProgressGatheringResults = EditorGUIUtility.TrTextContent("Gathering Coverage results..");
|
||||
public static readonly GUIContent ProgressWritingFile = EditorGUIUtility.TrTextContent("Writing Coverage results to file..");
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ada728b01ae678c4eb6eaf209094ecb2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,36 @@
|
||||
using System.Xml.Serialization;
|
||||
using System.IO;
|
||||
using OpenCover.Framework.Model;
|
||||
using UnityEditor.TestTools.CodeCoverage.Utils;
|
||||
|
||||
namespace UnityEditor.TestTools.CodeCoverage.OpenCover
|
||||
{
|
||||
internal class OpenCoverResultWriter : CoverageResultWriterBase<CoverageSession>
|
||||
{
|
||||
public OpenCoverResultWriter(CoverageSettings coverageSettings) : base(coverageSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public override void WriteCoverageSession(CoverageReportType reportType)
|
||||
{
|
||||
bool atRoot = CommandLineManager.instance.generateRootEmptyReport && reportType == CoverageReportType.FullEmpty;
|
||||
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(CoverageSession));
|
||||
string fileFullPath = atRoot ? GetRootFullEmptyPath() : GetNextFullFilePath();
|
||||
if (!System.IO.File.Exists(fileFullPath))
|
||||
{
|
||||
using (TextWriter writer = new StreamWriter(fileFullPath))
|
||||
{
|
||||
serializer.Serialize(writer, CoverageSession);
|
||||
if (!CommandLineManager.instance.batchmode)
|
||||
EditorUtility.DisplayProgressBar(OpenCoverReporterStyles.ProgressTitle.text, OpenCoverReporterStyles.ProgressWritingFile.text, 1f);
|
||||
}
|
||||
|
||||
ResultsLogger.Log(reportType == CoverageReportType.CoveredMethodsOnly ? ResultID.Log_VisitedResultsSaved : ResultID.Log_ResultsSaved, fileFullPath);
|
||||
CoverageEventData.instance.AddSessionResultPath(fileFullPath);
|
||||
|
||||
base.WriteCoverageSession(reportType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fcb98740e9f34674a87ee205613f1de9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user