Unity-jump-proj

This commit is contained in:
2024-09-09 11:07:16 +03:00
parent 2c29906bbf
commit fd96a5627d
13707 changed files with 866380 additions and 0 deletions

View File

@ -0,0 +1,8 @@
namespace UnityEditor.TestTools.CodeCoverage
{
internal enum CoverageFormat
{
OpenCover = 0,
DotCover = 1
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8419585fc64d77e4d8e18b7311df59a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 401e5b541d6e30e489f5751eec729fab
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 74449815e747b4b1ba000bf66f12e01a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9c2ede2081f2f4c45a6f7f4c92e3a735
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88cac9839726d46718b126833c76c5e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c0d40fe321f9746dcbccb27824986511
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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 */} }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 561dcfbc5a4094415b3d8348f1a6b738
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4ea0122a5084b4beca6bbb008fea821e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a74e938125304007a53cf9733cd1208
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ebffa2e2071ad4f79a3641b47e74b25e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86783b018856d4f82ab6105c443f2ffa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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 =&gt;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
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f1f74c5e9b1146168b87690081420c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,14 @@
{
"name": "Unity.TestTools.CodeCoverage.Editor.OpenCover.Model",
"references": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": []
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 19c3df1967929405fba735480b3da9a7
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7c00a2c9a9fd476c947d35ae18e21fd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 448b5b9c7773f48be878eb5f56008baf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2adfa90d9eaaa4702b2457c09db2f5e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3e49bf94f7d5348e2a48c3a09f3de201
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f84724c9beb0444b0b1a7166317d7262
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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(); }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b1b80d503cba7422cb21829a6891cde2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d7c779bb30ff546c08fce46737f83ec5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b6c4c078b09304bfeaae896e2e4ffa9e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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 ();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 68735f878b89447b4925a73a9074dfe9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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 ();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f2c36198237a34870a85b171e9097887
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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"));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c54348b43e9e464fa20499d8315beed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a8056948ac804bf891bd101b3216a55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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": []
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: eecdf9bdfcb2949619db458f3e24c5e2
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 258e29582df66a741937fe410463ad97
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 04c157fc0c43e40708bcbe2aa8738140
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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..");
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ada728b01ae678c4eb6eaf209094ecb2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fcb98740e9f34674a87ee205613f1de9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: