This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,228 @@
// #define COVERAGE_ANALYTICS_LOGGING
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.TestTools.CodeCoverage.Utils;
using UnityEngine;
using UnityEngine.Analytics;
namespace UnityEditor.TestTools.CodeCoverage.Analytics
{
[Serializable]
internal class Timer
{
public void Start()
{
startTime = DateTime.Now;
}
public long elapsedTimeMs => (long)(DateTime.Now - startTime).TotalMilliseconds;
[SerializeField]
private DateTime startTime = DateTime.Now;
}
[Serializable]
internal class CoverageAnalytics : ScriptableSingleton<CoverageAnalytics>
{
[SerializeField]
private bool s_Registered;
[SerializeField]
private List<int> s_ResultsIdsList;
public CoverageAnalyticsEvent CurrentCoverageEvent;
public Timer CoverageTimer;
protected CoverageAnalytics() : base()
{
ResetEvents();
}
public void ResetEvents()
{
CurrentCoverageEvent = new CoverageAnalyticsEvent();
CoverageTimer = new Timer();
s_ResultsIdsList = new List<int>();
CurrentCoverageEvent.actionID = ActionID.Other;
CurrentCoverageEvent.coverageModeId = CoverageModeID.None;
CurrentCoverageEvent.numOfIncludedPaths = 0;
CurrentCoverageEvent.numOfExcludedPaths = 0;
}
public void StartTimer()
{
CoverageTimer.Start();
}
// See ResultsLogger.cs for details
public void AddResult(ResultID resultId)
{
s_ResultsIdsList.Add((int)resultId);
}
public void SendCoverageEvent(bool success)
{
CurrentCoverageEvent.success = success;
CurrentCoverageEvent.duration = CoverageTimer.elapsedTimeMs;
CurrentCoverageEvent.resultIds = s_ResultsIdsList.ToArray();
bool runFromCommandLine = CommandLineManager.instance.runFromCommandLine;
bool batchmode = CommandLineManager.instance.batchmode;
bool useProjectSettings = CommandLineManager.instance.useProjectSettings;
CurrentCoverageEvent.runFromCommandLine = runFromCommandLine;
CurrentCoverageEvent.batchmode = batchmode;
CurrentCoverageEvent.useProjectSettings = useProjectSettings;
if (batchmode && !useProjectSettings)
{
CurrentCoverageEvent.autogenerate = CommandLineManager.instance.generateBadgeReport || CommandLineManager.instance.generateHTMLReport || CommandLineManager.instance.generateAdditionalReports;
CurrentCoverageEvent.createBadges = CommandLineManager.instance.generateBadgeReport;
CurrentCoverageEvent.generateHistory = CommandLineManager.instance.generateHTMLReportHistory;
CurrentCoverageEvent.generateHTMLReport = CommandLineManager.instance.generateHTMLReport;
CurrentCoverageEvent.generateMetrics = CommandLineManager.instance.generateAdditionalMetrics;
CurrentCoverageEvent.generateTestReferences = CommandLineManager.instance.generateTestReferences;
CurrentCoverageEvent.generateAdditionalReports = CommandLineManager.instance.generateAdditionalReports;
CurrentCoverageEvent.dontClear = CommandLineManager.instance.dontClear;
CurrentCoverageEvent.useDefaultAssemblyFilters = !CommandLineManager.instance.assemblyFiltersSpecified;
CurrentCoverageEvent.useDefaultPathFilters = !CommandLineManager.instance.pathFiltersSpecified;
CurrentCoverageEvent.useDefaultResultsLoc = CommandLineManager.instance.coverageResultsPath.Length == 0;
CurrentCoverageEvent.useDefaultHistoryLoc = CommandLineManager.instance.coverageHistoryPath.Length == 0;
CurrentCoverageEvent.usePathReplacePatterns = CommandLineManager.instance.pathReplacingSpecified;
CurrentCoverageEvent.useSourcePaths = CommandLineManager.instance.sourcePathsSpecified;
CurrentCoverageEvent.usePathFiltersFromFile = CommandLineManager.instance.pathFiltersFromFileSpecified;
CurrentCoverageEvent.useAssemblyFiltersFromFile = CommandLineManager.instance.assemblyFiltersFromFileSpecified;
}
else
{
CurrentCoverageEvent.autogenerate = CommandLineManager.instance.generateBadgeReport || CommandLineManager.instance.generateHTMLReport || CommandLineManager.instance.generateAdditionalReports || CoveragePreferences.instance.GetBool("AutoGenerateReport", true);
CurrentCoverageEvent.autoOpenReport = CoveragePreferences.instance.GetBool("OpenReportWhenGenerated", true);
CurrentCoverageEvent.createBadges = CommandLineManager.instance.generateBadgeReport || CoveragePreferences.instance.GetBool("GenerateBadge", true);
CurrentCoverageEvent.generateHistory = CommandLineManager.instance.generateHTMLReportHistory || CoveragePreferences.instance.GetBool("IncludeHistoryInReport", true);
CurrentCoverageEvent.generateHTMLReport = CommandLineManager.instance.generateHTMLReport || CoveragePreferences.instance.GetBool("GenerateHTMLReport", true);
CurrentCoverageEvent.generateMetrics = CommandLineManager.instance.generateAdditionalMetrics || CoveragePreferences.instance.GetBool("GenerateAdditionalMetrics", false);
CurrentCoverageEvent.generateTestReferences = CommandLineManager.instance.generateTestReferences || CoveragePreferences.instance.GetBool("GenerateTestReferences", false);
CurrentCoverageEvent.generateAdditionalReports = CommandLineManager.instance.generateAdditionalReports || CoveragePreferences.instance.GetBool("GenerateAdditionalReports", false);
CurrentCoverageEvent.dontClear = CommandLineManager.instance.dontClear;
CurrentCoverageEvent.usePathReplacePatterns = CommandLineManager.instance.pathReplacingSpecified;
CurrentCoverageEvent.useSourcePaths = CommandLineManager.instance.sourcePathsSpecified;
CurrentCoverageEvent.usePathFiltersFromFile = CommandLineManager.instance.pathFiltersFromFileSpecified;
CurrentCoverageEvent.useAssemblyFiltersFromFile = CommandLineManager.instance.assemblyFiltersFromFileSpecified;
CurrentCoverageEvent.useDefaultAssemblyFilters = !CommandLineManager.instance.assemblyFiltersSpecified;
if (!CommandLineManager.instance.assemblyFiltersSpecified)
CurrentCoverageEvent.useDefaultAssemblyFilters = string.Equals(CoveragePreferences.instance.GetString("IncludeAssemblies", AssemblyFiltering.GetUserOnlyAssembliesString()), AssemblyFiltering.GetUserOnlyAssembliesString(), StringComparison.InvariantCultureIgnoreCase);
CurrentCoverageEvent.useDefaultPathFilters = !CommandLineManager.instance.pathFiltersSpecified;
if (!CommandLineManager.instance.pathFiltersSpecified)
CurrentCoverageEvent.useDefaultPathFilters = string.Equals(CoveragePreferences.instance.GetString("PathsToInclude", string.Empty), string.Empty) && string.Equals(CoveragePreferences.instance.GetString("PathsToExclude", string.Empty), string.Empty);
CurrentCoverageEvent.useDefaultResultsLoc = CommandLineManager.instance.coverageResultsPath.Length == 0;
if (CommandLineManager.instance.coverageResultsPath.Length == 0)
CurrentCoverageEvent.useDefaultResultsLoc = string.Equals(CoveragePreferences.instance.GetStringForPaths("Path", string.Empty), CoverageUtils.GetProjectPath(), StringComparison.InvariantCultureIgnoreCase);
CurrentCoverageEvent.useDefaultHistoryLoc = CommandLineManager.instance.coverageHistoryPath.Length == 0;
if (CommandLineManager.instance.coverageHistoryPath.Length == 0)
CurrentCoverageEvent.useDefaultHistoryLoc = string.Equals(CoveragePreferences.instance.GetStringForPaths("HistoryPath", string.Empty), CoverageUtils.GetProjectPath(), StringComparison.InvariantCultureIgnoreCase);
}
#if UNITY_2020_1_OR_NEWER
CurrentCoverageEvent.inDebugMode = Compilation.CompilationPipeline.codeOptimization == Compilation.CodeOptimization.Debug;
#else
CurrentCoverageEvent.inDebugMode = true;
#endif
if (!runFromCommandLine || (runFromCommandLine && !batchmode && !CommandLineManager.instance.assemblyFiltersSpecified))
{
if (CurrentCoverageEvent.actionID == ActionID.ReportOnly)
{
string includeAssemblies = CoveragePreferences.instance.GetString("IncludeAssemblies", AssemblyFiltering.GetUserOnlyAssembliesString());
string[] includeAssembliesArray = includeAssemblies.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
CurrentCoverageEvent.includedAssemblies = includeAssembliesArray;
}
}
Send(EventName.codeCoverage, CurrentCoverageEvent);
ResetEvents();
}
public bool RegisterEvents()
{
if (!EditorAnalytics.enabled)
{
ResultsLogger.LogSessionItem("Editor analytics are disabled", LogVerbosityLevel.Info);
return false;
}
if (s_Registered)
{
return true;
}
var allNames = Enum.GetNames(typeof(EventName));
if (allNames.Any(eventName => !RegisterEvent(eventName)))
{
return false;
}
s_Registered = true;
return true;
}
private bool RegisterEvent(string eventName)
{
const string vendorKey = "unity.testtools.codecoverage";
var result = EditorAnalytics.RegisterEventWithLimit(eventName, 100, 1000, vendorKey);
switch (result)
{
case AnalyticsResult.Ok:
{
#if COVERAGE_ANALYTICS_LOGGING
ResultsLogger.LogSessionItem($"Registered analytics event: {eventName}", LogVerbosityLevel.Info);
#endif
return true;
}
case AnalyticsResult.TooManyRequests:
// this is fine - event registration survives domain reload (native)
return true;
default:
{
ResultsLogger.LogSessionItem($"Failed to register analytics event {eventName}. Result: {result}", LogVerbosityLevel.Error);
return false;
}
}
}
private void Send(EventName eventName, object eventData)
{
if (!RegisterEvents())
{
#if COVERAGE_ANALYTICS_LOGGING
Console.WriteLine($"[{CoverageSettings.PackageName}] Analytics disabled: event='{eventName}', time='{DateTime.Now:HH:mm:ss}', payload={EditorJsonUtility.ToJson(eventData, true)}");
#endif
return;
}
try
{
var result = EditorAnalytics.SendEventWithLimit(eventName.ToString(), eventData);
if (result == AnalyticsResult.Ok)
{
#if COVERAGE_ANALYTICS_LOGGING
ResultsLogger.LogSessionItem($"Event={eventName}, time={DateTime.Now:HH:mm:ss}, payload={EditorJsonUtility.ToJson(eventData, true)}", LogVerbosityLevel.Info);
#endif
}
else
{
ResultsLogger.LogSessionItem($"Failed to send analytics event {eventName}. Result: {result}", LogVerbosityLevel.Error);
}
}
catch (Exception)
{
// ignored
}
}
}
}

View File

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

View File

@@ -0,0 +1,22 @@
namespace UnityEditor.TestTools.CodeCoverage.Analytics
{
internal enum EventName
{
codeCoverage
}
internal enum ActionID
{
Other = 0,
DataOnly = 1,
ReportOnly = 2,
DataReport = 3
}
internal enum CoverageModeID
{
None = 0,
TestRunner = 1,
Recording = 2
}
}

View File

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

View File

@@ -0,0 +1,111 @@
using System;
namespace UnityEditor.TestTools.CodeCoverage.Analytics
{
[Serializable]
internal class CoverageAnalyticsEvent
{
// The action performed on the event (batchmode compatible)
public ActionID actionID;
// Array of resultsIds (batchmode compatible)
public int[] resultIds;
// The coverage mode that was performed on successful action (batchmode compatible)
public CoverageModeID coverageModeId;
// Duration (in ms) for which the Coverage session lasted (batchmode compatible)
public long duration;
// Was the coverage session result a success (batchmode compatible)
public bool success;
// Did the user run the editor from the command line
public bool runFromCommandLine;
// Did the user run the editor in batch mode
public bool batchmode;
// Did the user pass the useProjectSettings option in batch mode (batchmode only)
public bool useProjectSettings;
// Did the user have Generate HTML Report selected (batchmode compatible)
public bool generateHTMLReport;
// Did the user have Generate History selected (batchmode compatible)
public bool generateHistory;
// Did the user have Generate Badges selected (batchmode compatible)
public bool createBadges;
// Did the user have Generate Additional Metrics selected (batchmode compatible)
public bool generateMetrics;
// Did the user have Generate Test Runner References selected (batchmode compatible)
public bool generateTestReferences;
// Did the user have Generate Additional reports selected (batchmode compatible)
public bool generateAdditionalReports;
// Did the user have passed the dontClear coverage option (batchmode compatible)
public bool dontClear;
// Did the user have Auto Generate Report selected (batchmode compatible)
public bool autogenerate;
// Did the user have Auto Open Report selected
public bool autoOpenReport;
// Did the user select the Clear Data button
public bool clearData;
// Did the user select the Clear History button
public bool clearHistory;
// Did the user select the Generate From Last button
public bool generateFromLast;
// Did the user switch to Debug mode (Code Optimization) in the Coverage window
public bool switchToDebugMode;
// Is the editor in Code Optimization: Debug mode (batchmode compatible)
public bool inDebugMode;
// Did the user disable Burst Compilation in the Coverage window
public bool switchBurstOff;
// Did the user select a new Results location or uses the default one (batchmode compatible)
public bool useDefaultResultsLoc;
// Did the user select a new History location or uses the default one (batchmode compatible)
public bool useDefaultHistoryLoc;
// Did the user specify different assemblies from the default ones (batchmode compatible)
public bool useDefaultAssemblyFilters;
// Did the user specify different paths filtering from the default one (batchmode compatible)
public bool useDefaultPathFilters;
// Did the user enter the Selected Assemblies dialog/dropdown
public bool enterAssembliesDialog;
// Did the user update any assemblies via the Selected Assemblies dialog/dropdown
public bool updateAssembliesDialog;
// Did the user update Included Paths
public bool updateIncludedPaths;
// Did the user select Add Folder for Included Paths
public bool selectAddFolder_IncludedPaths;
// Did the user select Add File for Included Paths
public bool selectAddFile_IncludedPaths;
// How many paths are included (batchmode compatible)
public int numOfIncludedPaths;
// Did the user update Excluded Paths
public bool updateExcludedPaths;
// Did the user select Add Folder for Excluded Paths
public bool selectAddFolder_ExcludedPaths;
// Did the user select Add File for Excluded Paths
public bool selectAddFile_ExcludedPaths;
// How many paths are excluded (batchmode compatible)
public int numOfExcludedPaths;
// Did the user use the Coverage API to StartRecording (batchmode compatible)
public bool useAPI_StartRec;
// Did the user use the Coverage API to StopRecording (batchmode compatible)
public bool useAPI_StopRec;
// Did the user use the Coverage API to PauseRecording (batchmode compatible)
public bool useAPI_PauseRec;
// Did the user use the Coverage API to UnpauseRecording (batchmode compatible)
public bool useAPI_UnpauseRec;
// Array of individual included assembly names (batchmode compatible)
public string[] includedAssemblies;
// Array of individual excluded assembly names (batchmode only)
public string[] excludedAssemblies;
// Did the user use the onCoverageSessionStarted event (batchmode compatible)
public bool useEvent_onCoverageSessionStarted;
// Did the user use the onCoverageSessionFinished event (batchmode compatible)
public bool useEvent_onCoverageSessionFinished;
// Did the user use the onCoverageSessionPaused event (batchmode compatible)
public bool useEvent_onCoverageSessionPaused;
// Did the user use the onCoverageSessionUnpaused event (batchmode compatible)
public bool useEvent_onCoverageSessionUnpaused;
// Did the user specify path replace patterns (command line only)
public bool usePathReplacePatterns;
// Did the user specify source paths (command line only)
public bool useSourcePaths;
// Did the user specify path filters from file option (command line only)
public bool usePathFiltersFromFile;
// Did the user specify assembly filters from file option (command line only)
public bool useAssemblyFiltersFromFile;
}
}

View File

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

View File

@@ -0,0 +1,5 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.TestTools.CodeCoverage.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.TestTools.CodeCoverage.Editor.Tests.Performance")]
[assembly: InternalsVisibleTo("Unity.TestTools.CodeCoverage.Editor.CoverageStatsSerializer")]

View File

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

View File

@@ -0,0 +1,58 @@
{
"name": "Unity.TestTools.CodeCoverage.Editor",
"rootNamespace": "UnityEditor.TestTools.CodeCoverage",
"references": [
"GUID:27619889b8ba8c24980f49ee34dbb44a",
"GUID:0acc523941302664db1f4e527237feb3",
"GUID:19c3df1967929405fba735480b3da9a7",
"GUID:eecdf9bdfcb2949619db458f3e24c5e2",
"GUID:49818357e697641afb75d2f8acaf1861"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll",
"ReportGeneratorMerged.dll"
],
"autoReferenced": true,
"defineConstraints": [
"UNITY_2019_2_OR_NEWER"
],
"versionDefines": [
{
"name": "com.unity.test-framework",
"expression": "1.0.16",
"define": "CONDITIONAL_IGNORE_SUPPORTED"
},
{
"name": "com.unity.burst",
"expression": "1.1.1",
"define": "BURST_INSTALLED"
},
{
"name": "com.unity.test-framework",
"expression": "1.1.18",
"define": "TEST_FRAMEWORK_1_1_18_OR_NEWER"
},
{
"name": "Unity",
"expression": "2021.1.0b10",
"define": "NO_COV_EDITORPREF"
},
{
"name": "com.unity.test-framework",
"expression": "1.3.0",
"define": "TEST_FRAMEWORK_1_3_OR_NEWER"
},
{
"name": "com.unity.test-framework",
"expression": "2.0.0",
"define": "TEST_FRAMEWORK_2_0_OR_NEWER"
}
],
"noEngineReferences": false
}

View File

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

View File

@@ -0,0 +1,196 @@
using UnityEditor.TestTools.CodeCoverage.Analytics;
using UnityEditor.TestTools.CodeCoverage.Utils;
using UnityEngine.TestTools;
namespace UnityEditor.TestTools.CodeCoverage
{
/// <summary>
/// Utility class for the CodeCoverage API.
/// </summary>
/// <example>
/// The following example loads a scene, starts coverage recording, initializes a number of instances of a prefab, then pauses the recording to load another scene, unpauses the recording, initializes a number of instances of a different prefab and finally stops the recording.
/// It also sets the verbosity level to Verbose, so all logs are printed to the editor log.
/// <code>
/// using UnityEngine;
/// using UnityEditor;
/// using UnityEditor.TestTools.CodeCoverage;
/// using UnityEditor.SceneManagement;
///
/// public class CoverageApiTest : MonoBehaviour
/// {
/// [MenuItem("CodeCoverage/Run Recording")]
/// static void RunRecording()
/// {
/// CodeCoverage.VerbosityLevel = LogVerbosityLevel.Verbose;
///
/// int i;
///
/// EditorSceneManager.OpenScene("Assets/Scenes/Scene1.unity");
///
/// CodeCoverage.StartRecording();
///
/// for (i = 0; i &lt; 1000; ++i)
/// {
/// Instantiate(Resources.Load("ComplexPrefab1"));
/// }
///
/// CodeCoverage.PauseRecording();
///
/// EditorSceneManager.OpenScene("Assets/Scenes/Scene2.unity");
///
/// CodeCoverage.UnpauseRecording();
///
/// for (i = 0; i &lt; 1000; ++i)
/// {
/// Instantiate(Resources.Load("ComplexPrefab2"));
/// }
///
/// CodeCoverage.StopRecording();
/// }
/// }
/// </code>
/// </example>
public static class CodeCoverage
{
private static CoverageReporterManager s_CoverageReporterManager;
/// <summary>
/// Sets the verbosity level used in editor and console logs. The default level is <see cref="LogVerbosityLevel.Info"/>.
/// </summary>
/// <value>
/// The verbosity level used in editor and console logs.
/// </value>
public static LogVerbosityLevel VerbosityLevel
{
set
{
ResultsLogger.VerbosityLevel = value;
}
get
{
return ResultsLogger.VerbosityLevel;
}
}
/// <summary>
/// Call this to start a new coverage recording session.
/// </summary>
public static void StartRecording()
{
CoverageAnalytics.instance.CurrentCoverageEvent.useAPI_StartRec = true;
StartRecordingInternal();
}
internal static void StartRecordingInternal()
{
bool isRunning = CoverageRunData.instance.isRunning;
if (!isRunning)
{
Coverage.ResetAll();
CoverageRunData.instance.StartRecording();
if (s_CoverageReporterManager == null)
s_CoverageReporterManager = CoverageReporterStarter.CoverageReporterManager;
s_CoverageReporterManager.CreateCoverageReporter();
ICoverageReporter coverageReporter = s_CoverageReporterManager.CoverageReporter;
if (coverageReporter != null)
coverageReporter.OnRunStarted(null);
}
}
/// <summary>
/// Call this to pause the recording on the current coverage recording session.
/// </summary>
public static void PauseRecording()
{
CoverageAnalytics.instance.CurrentCoverageEvent.useAPI_PauseRec = true;
PauseRecordingInternal();
}
internal static void PauseRecordingInternal()
{
bool isRunning = CoverageRunData.instance.isRunning;
if (isRunning)
{
if (CoverageRunData.instance.isRecording && !CoverageRunData.instance.isRecordingPaused)
{
if (s_CoverageReporterManager == null)
s_CoverageReporterManager = CoverageReporterStarter.CoverageReporterManager;
ICoverageReporter coverageReporter = s_CoverageReporterManager.CoverageReporter;
if (coverageReporter != null)
coverageReporter.OnCoverageRecordingPaused();
CoverageRunData.instance.PauseRecording();
}
}
}
/// <summary>
/// Call this to continue recording on the current coverage recording session, after having paused the recording.
/// </summary>
public static void UnpauseRecording()
{
CoverageAnalytics.instance.CurrentCoverageEvent.useAPI_UnpauseRec = true;
UnpauseRecordingInternal();
}
internal static void UnpauseRecordingInternal()
{
bool isRunning = CoverageRunData.instance.isRunning;
if (isRunning)
{
if (CoverageRunData.instance.isRecording && CoverageRunData.instance.isRecordingPaused)
{
Coverage.ResetAll();
CoverageRunData.instance.UnpauseRecording();
}
}
}
/// <summary>
/// Call this to end the current coverage recording session.
/// </summary>
public static void StopRecording()
{
CoverageAnalytics.instance.CurrentCoverageEvent.useAPI_StopRec = true;
StopRecordingInternal();
}
internal static void StopRecordingInternal()
{
bool isRunning = CoverageRunData.instance.isRunning;
if (isRunning)
{
if (CoverageRunData.instance.isRecording)
{
if (CoverageRunData.instance.isRecordingPaused)
Coverage.ResetAll();
if (s_CoverageReporterManager == null)
s_CoverageReporterManager = CoverageReporterStarter.CoverageReporterManager;
ICoverageReporter coverageReporter = s_CoverageReporterManager.CoverageReporter;
if (coverageReporter != null)
coverageReporter.OnRunFinished(null);
CoverageRunData.instance.StopRecording();
s_CoverageReporterManager.GenerateReport();
}
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,45 @@
using System;
using System.Linq;
namespace UnityEditor.TestTools.CodeCoverage.CommandLineParser
{
internal class CommandLineOption : ICommandLineOption
{
readonly Action<string> m_ArgAction;
public CommandLineOption(string argName, Action action)
{
ArgName = argName;
m_ArgAction = s => action();
}
public CommandLineOption(string argName, Action<string> action)
{
ArgName = argName;
m_ArgAction = action;
}
public CommandLineOption(string argName, Action<string[]> action)
{
ArgName = argName;
m_ArgAction = s => action(SplitStringToArray(s));
}
public string ArgName { get; private set; }
public void ApplyValue(string value)
{
m_ArgAction(value);
}
static string[] SplitStringToArray(string value)
{
if (string.IsNullOrEmpty(value))
{
return new string[] { };
}
return value.Split(';').ToArray();
}
}
}

View File

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

View File

@@ -0,0 +1,49 @@
using System;
namespace UnityEditor.TestTools.CodeCoverage.CommandLineParser
{
internal class CommandLineOptionSet
{
readonly ICommandLineOption[] m_Options;
public CommandLineOptionSet(params ICommandLineOption[] options)
{
m_Options = options;
}
public void Parse(string[] args)
{
var i = 0;
while (i < args.Length)
{
var arg = args[i];
if (!arg.StartsWith("-"))
{
i++;
continue;
}
string value = null;
if (i + 1 < args.Length && !args[i + 1].StartsWith("-"))
{
value = args[i + 1];
i++;
}
ApplyValueToMatchingOptions(arg, value);
i++;
}
}
private void ApplyValueToMatchingOptions(string argName, string value)
{
foreach (var option in m_Options)
{
if ("-" + option.ArgName == argName)
{
option.ApplyValue(value);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace UnityEditor.TestTools.CodeCoverage.CommandLineParser
{
interface ICommandLineOption
{
string ArgName { get; }
void ApplyValue(string value);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,13 @@
using UnityEngine;
namespace UnityEditor.TestTools.CodeCoverage
{
[InitializeOnLoad]
internal class CompatibilityInitializer
{
static CompatibilityInitializer()
{
Debug.LogError("[Code Coverage] The Code Coverage package is not compatible with versions of Unity earlier than 2019.2.");
}
}
}

View File

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

View File

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

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:

Some files were not shown because too many files have changed in this diff Show More