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 @@
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,679 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor.TestTools.CodeCoverage.CommandLineParser;
using UnityEditor.TestTools.CodeCoverage.Utils;
using UnityEngine;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class CommandLineManager : CommandLineManagerImplementation
{
private static CommandLineManager s_Instance = null;
public static CommandLineManager instance
{
get
{
if (s_Instance == null)
s_Instance = new CommandLineManager();
return s_Instance;
}
}
protected CommandLineManager() : base(Environment.GetCommandLineArgs())
{
}
}
internal class CommandLineManagerImplementation
{
public bool runFromCommandLine
{
get;
private set;
}
public string coverageResultsPath
{
get;
private set;
}
public string coverageHistoryPath
{
get;
private set;
}
public bool generateAdditionalMetrics
{
get;
private set;
}
public bool generateTestReferences
{
get;
private set;
}
public bool generateHTMLReportHistory
{
get;
private set;
}
public bool generateHTMLReport
{
get;
private set;
}
public bool generateBadgeReport
{
get;
private set;
}
public bool generateAdditionalReports
{
get;
private set;
}
public bool useProjectSettings
{
get;
private set;
}
public bool generateRootEmptyReport
{
get;
private set;
}
public bool dontClear
{
get;
private set;
}
public bool verbosityLevelSpecified
{
get;
private set;
}
public bool assemblyFiltersSpecified
{
get;
private set;
}
public bool assemblyFiltersFromFileSpecified
{
get;
private set;
}
public bool pathFiltersSpecified
{
get;
private set;
}
public bool pathFiltersFromFileSpecified
{
get;
private set;
}
public bool pathReplacingSpecified
{
get;
private set;
}
public string sourcePaths
{
get;
private set;
}
public bool sourcePathsSpecified
{
get;
private set;
}
public AssemblyFiltering assemblyFiltering
{
get;
private set;
}
public PathFiltering pathFiltering
{
get;
private set;
}
public PathReplacing pathReplacing
{
get;
private set;
}
public bool runTests
{
get;
private set;
}
public bool batchmode
{
get;
private set;
}
public bool burstDisabled
{
get;
private set;
}
private string m_CoverageOptionsArg;
private string m_IncludeAssemblies;
private string m_ExcludeAssemblies;
private string m_IncludePaths;
private string m_ExcludePaths;
private string m_PathReplacePatterns;
public CommandLineManagerImplementation(string[] commandLineArgs)
{
runFromCommandLine = false;
coverageResultsPath = string.Empty;
coverageHistoryPath = string.Empty;
sourcePaths = string.Empty;
generateAdditionalMetrics = false;
generateTestReferences = false;
generateHTMLReportHistory = false;
generateHTMLReport = false;
generateBadgeReport = false;
generateAdditionalReports = false;
useProjectSettings = false;
generateRootEmptyReport = false;
dontClear = false;
verbosityLevelSpecified = false;
assemblyFiltersSpecified = false;
pathFiltersSpecified = false;
pathReplacingSpecified = false;
sourcePathsSpecified = false;
pathFiltersFromFileSpecified = false;
assemblyFiltering = new AssemblyFiltering();
pathFiltering = new PathFiltering();
pathReplacing = new PathReplacing();
runTests = false;
batchmode = false;
burstDisabled = false;
m_CoverageOptionsArg = string.Empty;
m_IncludeAssemblies = string.Empty;
m_ExcludeAssemblies = string.Empty;
m_IncludePaths = string.Empty;
m_ExcludePaths = string.Empty;
m_PathReplacePatterns = string.Empty;
CommandLineOptionSet optionSet = new CommandLineOptionSet(
new CommandLineOption("enableCodeCoverage", () => { runFromCommandLine = true; }),
new CommandLineOption("coverageResultsPath", filePathArg => { SetCoverageResultsPath(filePathArg); }),
new CommandLineOption("coverageHistoryPath", filePathArg => { SetCoverageHistoryPath(filePathArg); }),
new CommandLineOption("coverageOptions", optionsArg => { AddCoverageOptions(optionsArg); }),
new CommandLineOption("runTests", () => { runTests = true; }),
new CommandLineOption("batchmode", () => { batchmode = true; }),
new CommandLineOption("-burst-disable-compilation", () => { burstDisabled = true; })
);
optionSet.Parse(commandLineArgs);
ValidateCoverageResultsPath();
ValidateCoverageHistoryPath();
if (runFromCommandLine)
ParseCoverageOptions();
}
private void SetCoverageResultsPath(string filePathArg)
{
if (coverageResultsPath != string.Empty)
{
ResultsLogger.Log(ResultID.Warning_MultipleResultsPaths, coverageResultsPath);
}
else
{
if (filePathArg != null)
{
coverageResultsPath = CoverageUtils.NormaliseFolderSeparators(filePathArg);
}
}
}
private void ValidateCoverageResultsPath()
{
if (!CoverageUtils.EnsureFolderExists(coverageResultsPath))
coverageResultsPath = string.Empty;
}
private void SetCoverageHistoryPath(string filePathArg)
{
if (coverageHistoryPath != string.Empty)
{
ResultsLogger.Log(ResultID.Warning_MultipleHistoryPaths, coverageHistoryPath);
}
else
{
if (filePathArg != null)
{
coverageHistoryPath = CoverageUtils.NormaliseFolderSeparators(filePathArg);
}
}
}
private void ValidateCoverageHistoryPath()
{
if (!CoverageUtils.EnsureFolderExists(coverageHistoryPath))
coverageHistoryPath = string.Empty;
}
private void AddCoverageOptions(string coverageOptionsArg)
{
if (coverageOptionsArg != null)
{
coverageOptionsArg = coverageOptionsArg.Trim('\'');
if (coverageOptionsArg != string.Empty)
{
if (m_CoverageOptionsArg == string.Empty)
{
m_CoverageOptionsArg = coverageOptionsArg;
}
else
{
m_CoverageOptionsArg += ";";
m_CoverageOptionsArg += coverageOptionsArg;
}
}
}
}
private void ParseCoverageOptions()
{
// Make sure there is no trailing quotes at the end of the options
m_CoverageOptionsArg = m_CoverageOptionsArg.TrimEnd('"');
// 'sourcePaths' option is moved at the beginning to ensure it is handled first,
// since it may be needed for other options
var options = m_CoverageOptionsArg.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList();
if (options.Count > 1)
{
var sourcePath = options.FirstOrDefault(option => option.StartsWith("SOURCEPATHS:", StringComparison.InvariantCultureIgnoreCase));
if (sourcePath != null)
{
var sourcePathIndex = options.IndexOf(sourcePath);
var firstElement = options[0];
options[sourcePathIndex] = firstElement;
options[0] = sourcePath;
}
}
string[] coverageOptions = options.ToArray();
foreach (string optionArgsStr in coverageOptions)
{
if (optionArgsStr.Length == 0)
continue;
string optionName = optionArgsStr;
string optionArgs = string.Empty;
int indexOfColon = optionArgsStr.IndexOf(':');
if (indexOfColon > 0)
{
optionName = optionArgsStr.Substring(0, indexOfColon);
optionArgs = optionArgsStr.Substring(indexOfColon+1);
}
switch (optionName.ToUpperInvariant())
{
case "GENERATEADDITIONALMETRICS":
generateAdditionalMetrics = true;
break;
case "GENERATEHTMLREPORTHISTORY":
generateHTMLReportHistory = true;
break;
case "GENERATEHTMLREPORT":
generateHTMLReport = true;
break;
case "GENERATEBADGEREPORT":
generateBadgeReport = true;
break;
case "GENERATEADDITIONALREPORTS":
generateAdditionalReports = true;
break;
case "GENERATEROOTEMPTYREPORT":
generateRootEmptyReport = true;
break;
case "DONTCLEAR":
dontClear = true;
break;
case "GENERATETESTREFERENCES":
generateTestReferences = true;
break;
case "USEPROJECTSETTINGS":
if (batchmode)
useProjectSettings = true;
else
ResultsLogger.Log(ResultID.Warning_UseProjectSettingsNonBatchmode);
break;
case "VERBOSITY":
if (optionArgs.Length > 0)
{
verbosityLevelSpecified = true;
switch (optionArgs.ToUpperInvariant())
{
case "VERBOSE":
ResultsLogger.VerbosityLevel = LogVerbosityLevel.Verbose;
break;
case "INFO":
ResultsLogger.VerbosityLevel = LogVerbosityLevel.Info;
break;
case "WARNING":
ResultsLogger.VerbosityLevel = LogVerbosityLevel.Warning;
break;
case "ERROR":
ResultsLogger.VerbosityLevel = LogVerbosityLevel.Error;
break;
case "OFF":
ResultsLogger.VerbosityLevel = LogVerbosityLevel.Off;
break;
}
}
break;
case "ASSEMBLYFILTERS":
if (optionArgs.Length > 0)
{
assemblyFiltersSpecified = true;
string[] assemblyFilters = optionArgs.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
ParseAssemblyFilters(assemblyFilters);
}
break;
case "PATHFILTERSFROMFILE":
if (optionArgs.Length > 0)
{
pathFiltersFromFileSpecified = true;
ResultsLogger.Log(ResultID.Warning_PathFiltersFromFileDeprecation);
if (File.Exists(optionArgs))
{
try
{
ParsePathFilters( GetPathFiltersFromFile(optionArgs) );
}
catch (Exception e)
{
ResultsLogger.Log(ResultID.Warning_FailedToExtractPathFiltersFromFile, e.Message, optionArgs);
}
}
}
break;
case "PATHFILTERS":
if (optionArgs.Length > 0)
{
string[] pathFilters = optionArgs.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
ParsePathFilters(pathFilters);
}
break;
case "FILTERSFROMFILE":
if (optionArgs.Length > 0)
{
try
{
JsonFile jsonFile = GetFiltersFromFile(optionArgs);
if (jsonFile != null)
{
string[] pathFilters = ConvertToFilterArray(jsonFile.pathsInclude, jsonFile.pathsExclude);
if (pathFilters != null && pathFilters.Length > 0)
{
pathFiltersFromFileSpecified = true;
ParsePathFilters(pathFilters);
}
string[] assemblyFilters = ConvertToFilterArray(jsonFile.assembliesInclude, jsonFile.assembliesExclude);
if (assemblyFilters != null && assemblyFilters.Length > 0)
{
assemblyFiltersFromFileSpecified = true;
ParseAssemblyFilters(assemblyFilters);
}
}
}
catch (Exception e)
{
ResultsLogger.Log(ResultID.Warning_FailedToExtractFiltersFromFile, e.Message, optionArgs);
}
}
break;
case "PATHREPLACEPATTERNS":
if (optionArgs.Length > 0)
{
pathReplacingSpecified = true;
m_PathReplacePatterns = optionArgs;
}
break;
case "SOURCEPATHS":
if (optionArgs.Length > 0)
{
string[] rawSourcePaths = optionArgs.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < rawSourcePaths.Length; ++i)
{
if (sourcePaths.Length > 0)
sourcePaths += ",";
sourcePaths += CoverageUtils.NormaliseFolderSeparators(rawSourcePaths[i]);
}
if (sourcePaths.Length > 0)
sourcePathsSpecified = true;
}
break;
default:
ResultsLogger.Log(ResultID.Warning_UnknownCoverageOptionProvided, optionArgsStr);
break;
}
}
if (m_IncludeAssemblies.Length == 0)
{
// If there are no inlcudedAssemblies specified but there are includedPaths specified
// then include all project assemblies so path filtering can take precedence over assembly filtering,
// othewise if there are no includedPaths specified neither then inlcude just the user assemblies (found under the Assets folder)
if (m_IncludePaths.Length > 0)
m_IncludeAssemblies = AssemblyFiltering.GetAllProjectAssembliesString();
else
m_IncludeAssemblies = AssemblyFiltering.GetUserOnlyAssembliesString();
}
assemblyFiltering.Parse(m_IncludeAssemblies, m_ExcludeAssemblies);
pathFiltering.Parse(m_IncludePaths, m_ExcludePaths);
pathReplacing.Parse(m_PathReplacePatterns);
}
private void ParseAssemblyFilters(string[] assemblyFilters)
{
for (int i = 0; i < assemblyFilters.Length; ++i)
{
string filter = assemblyFilters[i];
string filterBody = filter.Length > 1 ? filter.Substring(1) : string.Empty;
if (filter.StartsWith("+", StringComparison.OrdinalIgnoreCase))
{
if (m_IncludeAssemblies.Length > 0)
m_IncludeAssemblies += ",";
if (filterBody.StartsWith("<", StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(filterBody, AssemblyFiltering.kAssetsAlias, StringComparison.OrdinalIgnoreCase))
m_IncludeAssemblies += AssemblyFiltering.GetUserOnlyAssembliesString();
else if (string.Equals(filterBody, AssemblyFiltering.kAllAlias, StringComparison.OrdinalIgnoreCase))
m_IncludeAssemblies += AssemblyFiltering.GetAllProjectAssembliesString();
else if (string.Equals(filterBody, AssemblyFiltering.kPackagesAlias, StringComparison.OrdinalIgnoreCase))
m_IncludeAssemblies += AssemblyFiltering.GetPackagesOnlyAssembliesString();
else if (string.Equals(filterBody, AssemblyFiltering.kCoreAlias, StringComparison.OrdinalIgnoreCase))
m_IncludeAssemblies += AssemblyFiltering.kCoreAssemblies;
}
else
{
m_IncludeAssemblies += filterBody;
}
}
else if (filter.StartsWith("-", StringComparison.OrdinalIgnoreCase))
{
if (m_ExcludeAssemblies.Length > 0)
m_ExcludeAssemblies += ",";
if (filterBody.StartsWith("<", StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(filterBody, AssemblyFiltering.kAssetsAlias, StringComparison.OrdinalIgnoreCase))
m_ExcludeAssemblies += AssemblyFiltering.GetUserOnlyAssembliesString();
else if (string.Equals(filterBody, AssemblyFiltering.kAllAlias, StringComparison.OrdinalIgnoreCase))
m_ExcludeAssemblies += AssemblyFiltering.GetAllProjectAssembliesString();
else if (string.Equals(filterBody, AssemblyFiltering.kPackagesAlias, StringComparison.OrdinalIgnoreCase))
m_ExcludeAssemblies += AssemblyFiltering.GetPackagesOnlyAssembliesString();
else if (string.Equals(filterBody, AssemblyFiltering.kCoreAlias, StringComparison.OrdinalIgnoreCase))
m_ExcludeAssemblies += AssemblyFiltering.kCoreAssemblies;
}
else
{
m_ExcludeAssemblies += filterBody;
}
}
else
{
ResultsLogger.Log(ResultID.Warning_AssemblyFiltersNotPrefixed, filter);
}
}
}
private void ParsePathFilters(string[] pathFilters)
{
var sources = new string[0];
if (sourcePathsSpecified)
sources = sourcePaths.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < pathFilters.Length; ++i)
{
string filter = pathFilters[i];
string filterBody = filter.Length > 1 ? filter.Substring(1) : string.Empty;
var isRelative = !filterBody.StartsWith("*") && !filterBody.StartsWith("?") && string.IsNullOrEmpty(Path.GetPathRoot(filterBody));
//If current path is relative - expand it to an absolute path using specified source paths
if (isRelative && sourcePathsSpecified)
{
string expandedPaths = string.Empty;
foreach (var source in sources)
{
if (expandedPaths.Length > 0)
expandedPaths += ",";
expandedPaths += CoverageUtils.NormaliseFolderSeparators(Path.Combine(source, filterBody));
}
filterBody = expandedPaths;
}
if (filter.StartsWith("+", StringComparison.OrdinalIgnoreCase))
{
if (m_IncludePaths.Length > 0)
m_IncludePaths += ",";
m_IncludePaths += filterBody;
}
else if (filter.StartsWith("-", StringComparison.OrdinalIgnoreCase))
{
if (m_ExcludePaths.Length > 0)
m_ExcludePaths += ",";
m_ExcludePaths += filterBody;
}
else
{
ResultsLogger.Log(ResultID.Warning_PathFiltersNotPrefixed, filter);
}
}
if (m_IncludePaths.Length > 0 || m_ExcludePaths.Length > 0)
pathFiltersSpecified = true;
}
internal string[] GetPathFiltersFromFile(string path)
{
var paths = new List<string>();
foreach (var line in File.ReadAllLines(path))
{
var entry = line.Trim();
paths.Add(CoverageUtils.NormaliseFolderSeparators(entry));
}
return paths.ToArray();
}
internal JsonFile GetFiltersFromFile(string path)
{
string jsonString = JsonUtils.CleanJsonString(File.ReadAllText(path));
JsonUtils.ValidateJsonKeys(jsonString);
JsonFile jsonFile = JsonUtility.FromJson<JsonFile>(jsonString);
return jsonFile;
}
internal string[] ConvertToFilterArray(string[] include, string[] exclude)
{
var filtersList = new List<string>();
if (include != null && include.Length > 0)
{
foreach (var filter in include)
{
filtersList.Add($"+{filter}");
}
}
if (exclude != null && exclude.Length > 0)
{
foreach (var filter in exclude)
{
filtersList.Add($"-{filter}");
}
}
return filtersList.ToArray();
}
}
}

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:

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:

View File

@ -0,0 +1,118 @@
using UnityEditor.SettingsManagement;
using UnityEditor.TestTools.CodeCoverage.Utils;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class CoveragePreferences : CoveragePreferencesImplementation
{
private static CoveragePreferences s_Instance = null;
private const string k_PackageName = "com.unity.testtools.codecoverage";
public static CoveragePreferences instance
{
get
{
if (s_Instance == null)
s_Instance = new CoveragePreferences();
return s_Instance;
}
}
protected CoveragePreferences() : base(k_PackageName)
{
}
}
internal class CoveragePreferencesImplementation
{
private const string k_ProjectPathAlias = "{ProjectPath}";
protected Settings m_Settings;
public CoveragePreferencesImplementation(string packageName)
{
m_Settings = new Settings(packageName);
}
public bool GetBool(string key, bool defaultValue, SettingsScope scope = SettingsScope.Project)
{
if (m_Settings.ContainsKey<bool>(key, scope))
{
return m_Settings.Get<bool>(key, scope, defaultValue);
}
return defaultValue;
}
public int GetInt(string key, int defaultValue, SettingsScope scope = SettingsScope.Project)
{
if (m_Settings.ContainsKey<int>(key, scope))
{
return m_Settings.Get<int>(key, scope, defaultValue);
}
return defaultValue;
}
public string GetStringForPaths(string key, string defaultValue, SettingsScope scope = SettingsScope.Project)
{
string value = GetString(key, defaultValue, scope);
value = value.Replace(k_ProjectPathAlias, CoverageUtils.GetProjectPath());
return value;
}
public string GetString(string key, string defaultValue, SettingsScope scope = SettingsScope.Project)
{
if (m_Settings.ContainsKey<string>(key, scope))
{
return m_Settings.Get<string>(key, scope, defaultValue);
}
return defaultValue;
}
public void SetBool(string key, bool value, SettingsScope scope = SettingsScope.Project)
{
m_Settings.Set<bool>(key, value, scope);
m_Settings.Save();
}
public void SetInt(string key, int value, SettingsScope scope = SettingsScope.Project)
{
m_Settings.Set<int>(key, value, scope);
m_Settings.Save();
}
public void SetStringForPaths(string key, string value, SettingsScope scope = SettingsScope.Project)
{
value = CoverageUtils.NormaliseFolderSeparators(value, false);
value = value.Replace(CoverageUtils.GetProjectPath(), k_ProjectPathAlias);
SetString(key, value, scope);
}
public void SetString(string key, string value, SettingsScope scope = SettingsScope.Project)
{
m_Settings.Set<string>(key, value, scope);
m_Settings.Save();
}
public void DeleteBool(string key, SettingsScope scope = SettingsScope.Project)
{
m_Settings.DeleteKey<bool>(key, scope);
m_Settings.Save();
}
public void DeleteInt(string key, SettingsScope scope = SettingsScope.Project)
{
m_Settings.DeleteKey<int>(key, scope);
m_Settings.Save();
}
public void DeleteString(string key, SettingsScope scope = SettingsScope.Project)
{
m_Settings.DeleteKey<string>(key, scope);
m_Settings.Save();
}
}
}

View File

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

View File

@ -0,0 +1,9 @@
namespace UnityEditor.TestTools.CodeCoverage
{
internal enum CoverageReportType
{
Full,
FullEmpty,
CoveredMethodsOnly
}
}

View File

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

View File

@ -0,0 +1,236 @@
using UnityEditor.TestTools.CodeCoverage.Analytics;
using UnityEditor.TestTools.CodeCoverage.Utils;
using UnityEditor.TestTools.TestRunner.Api;
using UnityEngine;
using UnityEngine.TestTools;
#if NO_COV_EDITORPREF
using System.Linq;
using UnityEditor.PackageManager;
#endif
namespace UnityEditor.TestTools.CodeCoverage
{
internal class CoverageReporterListener : ScriptableObject, ICallbacks
{
private CoverageReporterManager m_CoverageReporterManager;
private bool m_IsConnectedToPlayer;
#if TEST_FRAMEWORK_1_3_OR_NEWER
private bool m_Temp_RunFinishedCalled;
#endif
public void SetCoverageReporterManager(CoverageReporterManager manager)
{
m_CoverageReporterManager = manager;
}
public void RunStarted(ITestAdaptor testsToRun)
{
if (!Coverage.enabled)
return;
#if TEST_FRAMEWORK_1_3_OR_NEWER
m_Temp_RunFinishedCalled = false;
#endif
m_IsConnectedToPlayer = CoverageUtils.IsConnectedToPlayer;
if (m_IsConnectedToPlayer)
{
ResultsLogger.Log(ResultID.Warning_StandaloneUnsupported);
return;
}
if (CoverageRunData.instance.isRunning || EditorApplication.isCompiling)
return;
CoverageRunData.instance.Start();
m_CoverageReporterManager.CreateCoverageReporter();
ICoverageReporter coverageReporter = m_CoverageReporterManager.CoverageReporter;
if (coverageReporter != null)
coverageReporter.OnRunStarted(testsToRun);
}
public void RunFinished(ITestResultAdaptor result)
{
#if TEST_FRAMEWORK_1_3_OR_NEWER
if (m_Temp_RunFinishedCalled)
return;
#endif
if (!Coverage.enabled)
return;
if (CoverageRunData.instance.isRecording || m_IsConnectedToPlayer)
return;
CoverageRunData.instance.Stop();
if (!CoverageRunData.instance.DidTestsRun())
return;
ICoverageReporter coverageReporter = m_CoverageReporterManager.CoverageReporter;
if (coverageReporter != null)
coverageReporter.OnRunFinished(result);
m_CoverageReporterManager.GenerateReport();
#if TEST_FRAMEWORK_1_3_OR_NEWER
m_Temp_RunFinishedCalled = true;
#endif
}
public void TestStarted(ITestAdaptor test)
{
if (!Coverage.enabled)
return;
if (CoverageRunData.instance.HasLastIgnoredSuiteID() || m_IsConnectedToPlayer)
return;
if (test.RunState == RunState.Ignored)
{
if (test.IsSuite)
CoverageRunData.instance.SetLastIgnoredSuiteID(test.Id);
return;
}
if (test.IsSuite)
return;
CoverageRunData.instance.IncrementTestRunCount();
ICoverageReporter coverageReporter = m_CoverageReporterManager.CoverageReporter;
if (coverageReporter != null)
coverageReporter.OnTestStarted(test);
}
public void TestFinished(ITestResultAdaptor result)
{
if (!Coverage.enabled)
return;
if (m_IsConnectedToPlayer)
return;
if (result.Test.RunState == RunState.Ignored)
{
if (result.Test.IsSuite && string.Equals(CoverageRunData.instance.GetLastIgnoredSuiteID(), result.Test.Id))
CoverageRunData.instance.SetLastIgnoredSuiteID(string.Empty);
}
else if (!CoverageRunData.instance.HasLastIgnoredSuiteID() && !result.Test.IsSuite)
{
ICoverageReporter coverageReporter = m_CoverageReporterManager.CoverageReporter;
if (coverageReporter != null)
coverageReporter.OnTestFinished(result);
}
#if TEST_FRAMEWORK_1_3_OR_NEWER
// Temporary fix for UTF issue https://issuetracker.unity3d.com/issues/registered-callbacks-dont-work-after-domain-reload
// so that RunFinished is called on the last TestFinished
if (result.Test.IsSuite && result.Test.Parent == null)
{
RunFinished(result);
}
#endif
}
}
[InitializeOnLoad]
internal static class CoverageReporterStarter
{
public readonly static CoverageReporterManager CoverageReporterManager;
static CoverageReporterStarter()
{
#if NO_COV_EDITORPREF
if (!CommandLineManager.instance.runFromCommandLine)
{
bool localCoverageEnabled = CoveragePreferences.instance.GetBool("EnableCodeCoverage", false);
if (localCoverageEnabled != Coverage.enabled)
Coverage.enabled = localCoverageEnabled;
PackageManager.Events.registeringPackages += OnRegisteringPackages;
}
#endif
if (!Coverage.enabled)
return;
#if CONDITIONAL_IGNORE_SUPPORTED
ConditionalIgnoreAttribute.AddConditionalIgnoreMapping("IgnoreForCoverage", true);
#endif
CoverageReporterListener listener = ScriptableObject.CreateInstance<CoverageReporterListener>();
#if TEST_FRAMEWORK_1_3_OR_NEWER
TestRunnerApi.RegisterTestCallback(listener);
#else
TestRunnerApi api = ScriptableObject.CreateInstance<TestRunnerApi>();
api.RegisterCallbacks(listener);
#endif
CoverageSettings coverageSettings = new CoverageSettings()
{
resultsPathFromCommandLine = CommandLineManager.instance.coverageResultsPath,
historyPathFromCommandLine = CommandLineManager.instance.coverageHistoryPath
};
CoverageReporterManager = new CoverageReporterManager(coverageSettings);
listener.SetCoverageReporterManager(CoverageReporterManager);
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
// Generate a report if running from the command line,
// generateHTMLReport or generateBadgeReport or generateAdditionalReports is passed to -coverageOptions
// and -runTests has not been passed to the command line,
if (CommandLineManager.instance.runFromCommandLine &&
CoverageReporterManager.ShouldAutoGenerateReport() &&
!CommandLineManager.instance.runTests &&
!CoverageRunData.instance.reportWasGenerated)
{
// Start the timer for analytics for Report only
CoverageAnalytics.instance.StartTimer();
CoverageAnalytics.instance.CurrentCoverageEvent.actionID = ActionID.ReportOnly;
coverageSettings.rootFolderPath = CoverageUtils.GetRootFolderPath(coverageSettings);
coverageSettings.historyFolderPath = CoverageUtils.GetHistoryFolderPath(coverageSettings);
CoverageReporterManager.ReportGenerator.Generate(coverageSettings);
}
}
#if NO_COV_EDITORPREF
static void OnRegisteringPackages(PackageRegistrationEventArgs args)
{
if (args.removed.Any(info => info.name == "com.unity.testtools.codecoverage"))
{
Coverage.enabled = false;
}
}
#endif
static void OnBeforeAssemblyReload()
{
if (!CoverageRunData.instance.isRunning)
return;
if (!CoverageRunData.instance.DidTestsRun())
return;
if (CoverageRunData.instance.isRecording && CoverageRunData.instance.isRecordingPaused)
return;
ICoverageReporter coverageReporter = CoverageReporterManager.CoverageReporter;
if (coverageReporter != null)
coverageReporter.OnBeforeAssemblyReload();
}
static void OnAfterAssemblyReload()
{
if (!CoverageRunData.instance.isRunning)
return;
CoverageReporterManager.CreateCoverageReporter();
}
}
}

View File

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

View File

@ -0,0 +1,129 @@
using UnityEngine;
using UnityEditor.TestTools.CodeCoverage.OpenCover;
using UnityEditor.TestTools.CodeCoverage.Analytics;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class CoverageReporterManager
{
private readonly CoverageSettings m_CoverageSettings = null;
private ICoverageReporter m_CoverageReporter = null;
CoverageReportGenerator m_ReportGenerator = null;
public CoverageReporterManager(CoverageSettings coverageSettings)
{
m_CoverageSettings = coverageSettings;
}
public ICoverageReporter CoverageReporter
{
get
{
if (m_CoverageReporter == null)
{
CreateCoverageReporter();
}
return m_CoverageReporter;
}
}
public void CreateCoverageReporter()
{
m_CoverageReporter = null;
// Use OpenCover format as currently this is the only one supported
CoverageFormat coverageFormat = CoverageFormat.OpenCover;
switch (coverageFormat)
{
case CoverageFormat.OpenCover:
m_CoverageSettings.resultsFileExtension = "xml";
m_CoverageSettings.resultsFolderSuffix = "-opencov";
m_CoverageSettings.resultsFileName = CoverageRunData.instance.isRecording ? "RecordingCoverageResults" : "TestCoverageResults";
m_CoverageReporter = new OpenCoverReporter();
break;
}
if (m_CoverageReporter != null)
{
m_CoverageReporter.OnInitialise(m_CoverageSettings);
}
}
public bool ShouldAutoGenerateReport()
{
bool shouldAutoGenerateReport = false;
bool cmdLineGenerateHTMLReport = CommandLineManager.instance.generateHTMLReport;
bool cmdLineGenerateBadge = CommandLineManager.instance.generateBadgeReport;
bool cmdLineGenerateAdditionalReports = CommandLineManager.instance.generateAdditionalReports;
bool generateHTMLReport = CoveragePreferences.instance.GetBool("GenerateHTMLReport", true);
bool generateAdditionalReports = CoveragePreferences.instance.GetBool("GenerateAdditionalReports", false);
bool generateBadge = CoveragePreferences.instance.GetBool("GenerateBadge", true);
bool autoGenerateReport = CoveragePreferences.instance.GetBool("AutoGenerateReport", true);
if (CommandLineManager.instance.runFromCommandLine)
{
if (CommandLineManager.instance.batchmode)
{
if (CommandLineManager.instance.useProjectSettings)
{
shouldAutoGenerateReport = cmdLineGenerateHTMLReport ||
cmdLineGenerateBadge ||
cmdLineGenerateAdditionalReports ||
(autoGenerateReport && (generateHTMLReport || generateBadge || generateAdditionalReports));
}
else
{
shouldAutoGenerateReport = cmdLineGenerateHTMLReport || cmdLineGenerateBadge || cmdLineGenerateAdditionalReports;
}
}
else
{
shouldAutoGenerateReport = cmdLineGenerateHTMLReport ||
cmdLineGenerateBadge ||
cmdLineGenerateAdditionalReports ||
(autoGenerateReport && (generateHTMLReport || generateBadge || generateAdditionalReports));
}
}
else
{
shouldAutoGenerateReport = autoGenerateReport && (generateHTMLReport || generateBadge || generateAdditionalReports);
}
return shouldAutoGenerateReport;
}
public void GenerateReport()
{
if (ShouldAutoGenerateReport())
{
if (m_CoverageSettings != null)
{
CoverageAnalytics.instance.CurrentCoverageEvent.actionID = ActionID.DataReport;
ReportGenerator.Generate(m_CoverageSettings);
}
}
else
{
// Clear ProgressBar left from saving results to file,
// otherwise continue on the same ProgressBar
EditorUtility.ClearProgressBar();
// Send Analytics event (Data Only)
CoverageAnalytics.instance.SendCoverageEvent(true);
}
}
public CoverageReportGenerator ReportGenerator
{
get
{
if (m_ReportGenerator == null)
m_ReportGenerator = new CoverageReportGenerator();
return m_ReportGenerator;
}
}
}
}

View File

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

View File

@ -0,0 +1,91 @@
using System.IO;
using UnityEditor.TestTools.CodeCoverage.Utils;
namespace UnityEditor.TestTools.CodeCoverage
{
internal abstract class CoverageResultWriterBase<T> where T : class
{
protected CoverageSettings m_CoverageSettings;
public T CoverageSession { get; set; }
protected CoverageResultWriterBase(CoverageSettings coverageSettings)
{
m_CoverageSettings = coverageSettings;
}
public virtual void WriteCoverageSession(CoverageReportType reportType)
{
#if UNITY_2020_1_OR_NEWER
if (Compilation.CompilationPipeline.codeOptimization == Compilation.CodeOptimization.Release)
{
ResultsLogger.Log(ResultID.Warning_DebugCodeOptimization);
if (!CommandLineManager.instance.batchmode)
{
if (EditorUtility.DisplayDialog(
L10n.Tr("Code Coverage"),
L10n.Tr($"Code Coverage requires Code Optimization to be set to debug mode in order to obtain accurate coverage information. Do you want to switch to debug mode?\n\nNote that you would need to rerun {(CoverageRunData.instance.isRecording ? "the Coverage Recording session." : "the tests.")}"),
L10n.Tr("Switch to debug mode"),
L10n.Tr("Cancel")))
{
Compilation.CompilationPipeline.codeOptimization = Compilation.CodeOptimization.Debug;
EditorPrefs.SetBool("ScriptDebugInfoEnabled", true);
}
}
}
#endif
#if BURST_INSTALLED
if (EditorPrefs.GetBool("BurstCompilation", false) && !CommandLineManager.instance.burstDisabled)
{
ResultsLogger.Log(ResultID.Warning_BurstCompilationEnabled);
}
#endif
}
public void SetupCoveragePaths()
{
string folderName = CoverageUtils.GetProjectFolderName();
string resultsRootDirectoryName = string.Concat(folderName, m_CoverageSettings.resultsFolderSuffix);
bool isRecording = CoverageRunData.instance.isRecording;
// We want to save in the 'Recording' subdirectory of the results folder when recording
#if TEST_FRAMEWORK_2_0_OR_NEWER
string testSuite = isRecording ? "Recording" : "Automated";
#else
string testSuite = isRecording ? "Recording" : CoverageRunData.instance.testSuite;
#endif
string directoryName = CoverageUtils.JoinPaths(resultsRootDirectoryName, testSuite != null ? testSuite : "");
m_CoverageSettings.rootFolderPath = CoverageUtils.GetRootFolderPath(m_CoverageSettings);
m_CoverageSettings.historyFolderPath = CoverageUtils.GetHistoryFolderPath(m_CoverageSettings);
string filePath = CoverageUtils.JoinPaths(directoryName, m_CoverageSettings.resultsFileName);
filePath = CoverageUtils.JoinPaths(m_CoverageSettings.rootFolderPath, filePath);
filePath = CoverageUtils.NormaliseFolderSeparators(filePath);
CoverageUtils.EnsureFolderExists(Path.GetDirectoryName(filePath));
m_CoverageSettings.resultsRootFolderPath = CoverageUtils.NormaliseFolderSeparators(CoverageUtils.JoinPaths(m_CoverageSettings.rootFolderPath, resultsRootDirectoryName));
m_CoverageSettings.resultsFolderPath = CoverageUtils.NormaliseFolderSeparators(CoverageUtils.JoinPaths(m_CoverageSettings.rootFolderPath, directoryName));
m_CoverageSettings.resultsFilePath = filePath;
}
public void ClearCoverageFolderIfExists()
{
CoverageUtils.ClearFolderIfExists(m_CoverageSettings.resultsFolderPath, "*.xml");
}
public string GetRootFullEmptyPath()
{
return CoverageUtils.JoinPaths(m_CoverageSettings.rootFolderPath, m_CoverageSettings.resultsFileName + "_fullEmpty" + "." + m_CoverageSettings.resultsFileExtension);
}
protected string GetNextFullFilePath()
{
int nextFileIndex = m_CoverageSettings.hasPersistentRunData ? CoverageUtils.GetNumberOfFilesInFolder(m_CoverageSettings.resultsFolderPath, "*.xml", SearchOption.TopDirectoryOnly) : 0;
string fullFilePath = m_CoverageSettings.resultsFilePath + "_" + nextFileIndex.ToString("D4") + "." + m_CoverageSettings.resultsFileExtension;
return fullFilePath;
}
}
}

View File

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

View File

@ -0,0 +1,269 @@
using System;
using UnityEditor.TestTools.CodeCoverage.Analytics;
using UnityEngine;
namespace UnityEditor.TestTools.CodeCoverage
{
[Serializable]
internal class CoverageRunDataImplementation
{
[SerializeField]
private bool m_IsRunning = false;
[SerializeField]
private int m_TestRunCount = 0;
[SerializeField]
private string m_LastIgnoredSuite = string.Empty;
[SerializeField]
private bool m_IsRecording = false;
[SerializeField]
private bool m_IsRecordingPaused = false;
[SerializeField]
private bool m_ReportWasGenerated = false;
[SerializeField]
private bool m_IsGeneratingReport = false;
[SerializeField]
private string m_TestSuite = string.Empty;
public void Start(bool setupEvents = true)
{
m_LastIgnoredSuite = string.Empty;
m_IsRunning = true;
m_TestRunCount = 0;
if (setupEvents)
{
CoverageAnalytics.instance.CurrentCoverageEvent.actionID = ActionID.DataOnly;
CoverageAnalytics.instance.CurrentCoverageEvent.coverageModeId = CoverageModeID.TestRunner;
CoverageAnalytics.instance.StartTimer();
CoverageEventData.instance.StartSession(SessionMode.TestRunner);
}
}
public void Stop()
{
m_LastIgnoredSuite = string.Empty;
m_IsRunning = false;
}
public void StartRecording(bool setupEvents = true)
{
Start(setupEvents);
IncrementTestRunCount();
m_IsRecording = true;
m_IsRecordingPaused = false;
if (setupEvents)
{
CoverageAnalytics.instance.CurrentCoverageEvent.coverageModeId = CoverageModeID.Recording;
CoverageEventData.instance.StartSession(SessionMode.Recording);
}
}
public void PauseRecording()
{
m_IsRecordingPaused = true;
}
public void UnpauseRecording()
{
m_IsRecordingPaused = false;
}
public void StopRecording()
{
Stop();
m_IsRecording = false;
m_IsRecordingPaused = false;
}
public bool isRunning
{
get { return m_IsRunning; }
}
public bool isRecording
{
get { return m_IsRecording; }
}
public bool isRecordingPaused
{
get { return m_IsRecordingPaused; }
}
public bool isGeneratingReport
{
get { return m_IsGeneratingReport; }
}
public bool reportWasGenerated
{
get { return m_ReportWasGenerated; }
}
public void ReportGenerationStart()
{
m_IsGeneratingReport = true;
m_ReportWasGenerated = false;
}
public void ReportGenerationEnd(bool success)
{
m_IsGeneratingReport = false;
m_ReportWasGenerated = success;
}
public void IncrementTestRunCount()
{
m_TestRunCount++;
}
public bool DidTestsRun()
{
return m_TestRunCount > 0;
}
public void SetLastIgnoredSuiteID(string id)
{
m_LastIgnoredSuite = id;
}
public bool HasLastIgnoredSuiteID()
{
return m_LastIgnoredSuite.Length > 0;
}
public string GetLastIgnoredSuiteID()
{
return m_LastIgnoredSuite;
}
public string testSuite
{
set
{
if (!string.IsNullOrEmpty(value))
m_TestSuite = value;
}
get { return m_TestSuite; }
}
}
[Serializable]
internal class CoverageRunData : ScriptableSingleton<CoverageRunData>
{
[SerializeField]
private CoverageRunDataImplementation m_CoverageRunDataImplementation = null;
protected CoverageRunData() : base()
{
m_CoverageRunDataImplementation = new CoverageRunDataImplementation();
}
public bool isRunning
{
get { return m_CoverageRunDataImplementation.isRunning; }
}
public bool isRecording
{
get { return m_CoverageRunDataImplementation.isRecording; }
}
public bool isRecordingPaused
{
get { return m_CoverageRunDataImplementation.isRecordingPaused; }
}
public bool reportWasGenerated
{
get { return m_CoverageRunDataImplementation.reportWasGenerated; }
}
public void IncrementTestRunCount()
{
m_CoverageRunDataImplementation.IncrementTestRunCount();
}
public bool DidTestsRun()
{
return m_CoverageRunDataImplementation.DidTestsRun();
}
public void SetLastIgnoredSuiteID(string id)
{
m_CoverageRunDataImplementation.SetLastIgnoredSuiteID(id);
}
public bool HasLastIgnoredSuiteID()
{
return m_CoverageRunDataImplementation.HasLastIgnoredSuiteID();
}
public string GetLastIgnoredSuiteID()
{
return m_CoverageRunDataImplementation.GetLastIgnoredSuiteID();
}
public void Start()
{
m_CoverageRunDataImplementation.Start();
}
public void Stop()
{
m_CoverageRunDataImplementation.Stop();
}
public void StartRecording()
{
m_CoverageRunDataImplementation.StartRecording();
}
public void StopRecording()
{
m_CoverageRunDataImplementation.StopRecording();
}
public void PauseRecording()
{
m_CoverageRunDataImplementation.PauseRecording();
Events.InvokeOnCoverageSessionPaused();
}
public void UnpauseRecording()
{
m_CoverageRunDataImplementation.UnpauseRecording();
Events.InvokeOnCoverageSessionUnpaused();
}
public bool isGeneratingReport
{
get { return m_CoverageRunDataImplementation.isGeneratingReport; }
}
public void ReportGenerationStart()
{
m_CoverageRunDataImplementation.ReportGenerationStart();
}
public void ReportGenerationEnd(bool success)
{
m_CoverageRunDataImplementation.ReportGenerationEnd(success);
}
public string testSuite
{
set { m_CoverageRunDataImplementation.testSuite = value; }
get { return m_CoverageRunDataImplementation.testSuite; }
}
}
}

View File

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

View File

@ -0,0 +1,28 @@
using UnityEngine;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class CoverageSettings
{
public const string ReportFolderName = "Report";
public const string ReportHistoryFolderName = "Report-history";
public const string PackageName = "Code Coverage";
public string rootFolderPath;
public string rootFolderName = "CodeCoverage";
public string resultsPathFromCommandLine;
public string resultsFileName = "TestCoverageResults";
public string resultsFileExtension;
public string resultsFolderSuffix;
public string resultsFolderPath;
public string resultsRootFolderPath;
public string resultsFilePath;
public string historyPathFromCommandLine;
public string historyFolderPath;
public string overrideIncludeAssemblies;
public bool overrideGenerateHTMLReport = false;
public bool hasPersistentRunData = true;
public bool resetCoverageData = true;
public bool revealReportInFinder = true;
}
}

View File

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

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ffd1d4863b8a4651b0579e45633f513a
timeCreated: 1635864462

View File

@ -0,0 +1,44 @@
using System;
using System.Reflection;
using UnityEngine.TestTools;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class CoverageStats : ICoverageStatsProvider
{
public CoveredSequencePoint[] GetSequencePointsFor(MethodBase method)
{
return Coverage.GetSequencePointsFor(method);
}
public CoveredMethodStats[] GetStatsFor(Type type)
{
return Coverage.GetStatsFor(type);
}
public CoveredMethodStats GetStatsFor(MethodBase methodBase)
{
return Coverage.GetStatsFor(methodBase);
}
public CoveredMethodStats[] GetStatsFor(MethodBase[] methodBases)
{
return Coverage.GetStatsFor(methodBases);
}
public CoveredMethodStats[] GetStatsForAllCoveredMethods()
{
return Coverage.GetStatsForAllCoveredMethods();
}
public void ResetAll()
{
Coverage.ResetAll();
}
public void ResetFor(MethodBase methodBase)
{
Coverage.ResetFor(methodBase);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4fec00afe51f4874b335c9b75256f9e0
timeCreated: 1635864697

View File

@ -0,0 +1,17 @@
using System;
using System.Reflection;
using UnityEngine.TestTools;
namespace UnityEditor.TestTools.CodeCoverage
{
internal interface ICoverageStatsProvider
{
CoveredSequencePoint[] GetSequencePointsFor(MethodBase method);
CoveredMethodStats[] GetStatsFor(Type type);
CoveredMethodStats GetStatsFor(MethodBase methodBase);
CoveredMethodStats[] GetStatsFor(MethodBase[] methodBases);
CoveredMethodStats[] GetStatsForAllCoveredMethods();
void ResetAll();
void ResetFor(MethodBase methodBase);
}
}

View File

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

View File

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

View File

@ -0,0 +1,1096 @@
using System;
using System.IO;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEditor.TestTools.CodeCoverage.Utils;
using UnityEditor.TestTools.TestRunner;
using UnityEditor.TestTools.CodeCoverage.Analytics;
using UnityEditorInternal;
using System.Collections.Generic;
using System.Linq;
namespace UnityEditor.TestTools.CodeCoverage
{
[ExcludeFromCoverage]
internal class CodeCoverageWindow : EditorWindow
{
private bool m_EnableCodeCoverage;
#if !UNITY_2019_3_OR_NEWER
private bool m_HasLatestScriptingRuntime;
#endif
private string m_CodeCoveragePath;
private string m_CodeCoverageHistoryPath;
private FolderDropDownMenu m_ResultsFolderDropDownMenu;
private FolderDropDownMenu m_HistoryFolderDropDownMenu;
private CoverageFormat m_CodeCoverageFormat;
private bool m_IncludeHistoryInReport;
private string m_AssembliesToInclude;
private int m_AssembliesToIncludeLength;
private string m_PathsToInclude;
private string m_PathsToExclude;
private PathToAddDropDownMenu m_AddPathToIncludeDropDownMenu;
private PathToAddDropDownMenu m_AddPathToExcludeDropDownMenu;
private CoverageReportGenerator m_ReportGenerator;
private bool m_GenerateHTMLReport;
private bool m_GenerateAdditionalReports;
private bool m_GenerateBadge;
private bool m_GenerateAdditionalMetrics;
private bool m_GenerateTestReferences;
private bool m_AutoGenerateReport;
private bool m_OpenReportWhenGenerated;
private CoverageSettings m_CoverageSettings;
private static readonly Vector2 s_WindowMinSizeNormal = new Vector2(445, 65);
private bool m_LoseFocus = false;
private bool m_GenerateReport = false;
private bool m_StopRecording = false;
private ReorderableList m_PathsToIncludeReorderableList;
private ReorderableList m_PathsToExcludeReorderableList;
private List<string> m_PathsToIncludeList;
private List<string> m_PathsToExcludeList;
private Vector2 m_WindowScrollPosition = new Vector2(0, 0);
private readonly string kLatestScriptingRuntimeMessage = L10n.Tr("Code Coverage requires the latest Scripting Runtime Version (.NET 4.x). You can set this in the Player Settings.");
private readonly string kCodeCoverageDisabledNoRestartMessage = L10n.Tr("Code Coverage should be enabled in order to generate Coverage data and reports.\nNote that Code Coverage can affect the Editor performance.");
private readonly string kEnablingCodeCoverageMessage = L10n.Tr("Enabling Code Coverage will not take effect until Unity is restarted.");
private readonly string kDisablingCodeCoverageMessage = L10n.Tr("Disabling Code Coverage will not take effect until Unity is restarted.");
private readonly string kCodeOptimizationMessage = L10n.Tr("Code Coverage requires Code Optimization to be set to debug mode in order to obtain accurate coverage information.");
private readonly string kBurstCompilationOnMessage = L10n.Tr("Code Coverage requires Burst Compilation to be disabled in order to obtain accurate coverage information.");
private readonly string kSelectCoverageDirectoryMessage = L10n.Tr("Select the Coverage results directory");
private readonly string kSelectCoverageHistoryDirectoryMessage = L10n.Tr("Select the Coverage Report history directory");
private readonly string kClearDataMessage = L10n.Tr("Are you sure you would like to clear the Coverage results from previous test runs or from previous Coverage Recording sessions? Note that you cannot undo this action.");
private readonly string kClearHistoryMessage = L10n.Tr("Are you sure you would like to clear the coverage report history? Note that you cannot undo this action.");
private readonly string kNoAssembliesSelectedMessage = L10n.Tr("Make sure you have included at least one assembly.");
private readonly string kSettingOverriddenMessage = L10n.Tr("{0} is overridden by the {1} command line argument.");
private readonly string kSettingsOverriddenMessage = L10n.Tr("{0} are overridden by the {1} command line argument.");
private readonly string[] kVerbosityLevelLabels = new string[]
{
"Verbose",
"Info",
"Warning",
"Error",
"Off"
};
private void Update()
{
if (m_GenerateReport)
{
// Start the timer for analytics on 'Generate from Last' (report only)
CoverageAnalytics.instance.StartTimer();
CoverageAnalytics.instance.CurrentCoverageEvent.actionID = ActionID.ReportOnly;
CoverageAnalytics.instance.CurrentCoverageEvent.generateFromLast = true;
m_ReportGenerator.Generate(m_CoverageSettings);
m_GenerateReport = false;
}
if (m_StopRecording)
{
CodeCoverage.StopRecordingInternal();
m_StopRecording = false;
}
}
public void LoseFocus()
{
m_LoseFocus = true;
}
public string AssembliesToInclude
{
set
{
m_AssembliesToInclude = value.TrimStart(',').TrimEnd(',');
m_AssembliesToIncludeLength = m_AssembliesToInclude.Length;
CoveragePreferences.instance.SetString("IncludeAssemblies", m_AssembliesToInclude);
}
}
public string PathsToInclude
{
set
{
m_PathsToInclude = value.TrimStart(',').TrimEnd(',');
m_PathsToInclude = CoverageUtils.NormaliseFolderSeparators(m_PathsToInclude, false);
CoveragePreferences.instance.SetStringForPaths("PathsToInclude", m_PathsToInclude);
m_PathsToIncludeList = m_PathsToInclude.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
m_PathsToIncludeReorderableList.list = m_PathsToIncludeList;
CoverageAnalytics.instance.CurrentCoverageEvent.updateIncludedPaths = true;
}
}
public string PathsToExclude
{
set
{
m_PathsToExclude = value.TrimStart(',').TrimEnd(',');
m_PathsToExclude = CoverageUtils.NormaliseFolderSeparators(m_PathsToExclude, false);
CoveragePreferences.instance.SetStringForPaths("PathsToExclude", m_PathsToExclude);
m_PathsToExcludeList = new List<string>(m_PathsToExclude.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
m_PathsToExcludeReorderableList.list = m_PathsToExcludeList;
CoverageAnalytics.instance.CurrentCoverageEvent.updateExcludedPaths = true;
}
}
static class Styles
{
static bool s_Initialized;
public static readonly GUIContent SwitchToDebugCodeOptimizationButton = EditorGUIUtility.TrTextContent("Switch to debug mode");
public static readonly GUIContent SwitchBurstCompilationOffButton = EditorGUIUtility.TrTextContent("Disable Burst Compilation");
public static readonly GUIContent CodeCoverageResultsLocationLabel = EditorGUIUtility.TrTextContent("Results Location", "Specify the folder where the coverage results and report are saved to. The default location is the Project's folder.\n\nClick the dropdown to open the containing folder, change the location or reset to the default location.");
public static readonly GUIContent CodeCoverageHistoryLocationLabel = EditorGUIUtility.TrTextContent("Report History Location", "Specify the folder where the coverage report history is saved to. The default location is the Project's folder.\n\nClick the dropdown to open the containing folder, change the location or reset to the default location.");
public static readonly GUIContent ResultsFolderButton = EditorGUIUtility.TrIconContent(EditorIcons.FolderOpened, "Specify the folder where the coverage results and report are saved to.\n\nClick this to open the containing folder, change the location or reset to the default location.");
public static readonly GUIContent HistoryFolderButton = EditorGUIUtility.TrIconContent(EditorIcons.FolderOpened, "Specify the folder where the coverage report history is saved to.\n\nClick this to open the containing folder, change the location or reset to the default location.");
public static readonly GUIContent CoverageSettingsLabel = EditorGUIUtility.TrTextContent("Settings");
public static readonly GUIContent CoverageReportOptionsLabel = EditorGUIUtility.TrTextContent("Report Options");
public static readonly GUIContent EnableCodeCoverageLabel = EditorGUIUtility.TrTextContent("Enable Code Coverage", "Check this to enable Code Coverage. This is required in order to generate Coverage data and reports. Note that Code Coverage can affect the Editor performance.");
public static readonly GUIContent CodeCoverageFormat = EditorGUIUtility.TrTextContent("Coverage Format", "The Code Coverage format used when saving the results.");
public static readonly GUIContent GenerateAdditionalMetricsLabel = EditorGUIUtility.TrTextContent("Additional Metrics", "Check this to generate and include additional metrics in the HTML report. These currently include Cyclomatic Complexity and Crap Score calculations for each method.");
public static readonly GUIContent CoverageHistoryLabel = EditorGUIUtility.TrTextContent("Report History", "Check this to generate and include the coverage history in the HTML report.");
public static readonly GUIContent AssembliesToIncludeLabel = EditorGUIUtility.TrTextContent("Included Assemblies", "Specify the assemblies that will be included in the coverage results.\n\nClick the dropdown to view and select or deselect the assemblies.");
public static readonly GUIContent AssembliesToIncludeDropdownLabel = EditorGUIUtility.TrTextContent("<this will contain a list of the assemblies>", "<this will contain a list of the assemblies>");
public static readonly GUIContent AssembliesToIncludeEmptyDropdownLabel = EditorGUIUtility.TrTextContent(" Select", "Click this to view and select or deselect the assemblies.");
public static readonly GUIContent PathsToIncludeLabel = EditorGUIUtility.TrTextContent("Included Paths", "Click Add (+) to specify individual folders and files to include in coverage results. You can use globbing to filter the paths. If the list is empty, Unity includes all files in the Included Assemblies.\n\nTo remove an individual list entry, select the entry and then click Remove (-).");
public static readonly GUIContent PathsToExcludeLabel = EditorGUIUtility.TrTextContent("Excluded Paths", "Click Add (+) to specify individual folders and files to exclude from coverage results. You can use globbing to filter the paths.\n\nTo remove an individual list entry, select the entry and then click Remove (-).");
public static readonly GUIContent VerbosityLabel = EditorGUIUtility.TrTextContent("Log Verbosity Level", "Click the dropdown to set the verbosity level for the editor and console logs.\n\nVerbose: All logs\nInfo: Logs, Warnings and Errors\nWarning: Warnings and Errors\nError: Only Errors\nOff: No logs");
public static readonly GUIContent GenerateHTMLReportLabel = EditorGUIUtility.TrTextContent("HTML Report", "Check this to generate an HTML report.");
public static readonly GUIContent GenerateAdditionalReportsLabel = EditorGUIUtility.TrTextContent("Additional Reports", "Check this to generate SonarQube, Cobertura and LCOV reports.");
public static readonly GUIContent GenerateBadgeReportLabel = EditorGUIUtility.TrTextContent("Summary Badges", "Check this to generate coverage summary badges in SVG and PNG format.");
public static readonly GUIContent GenerateTestRunnerReferencesLabel = EditorGUIUtility.TrTextContent("Test Runner References", "Check this to include test references to the generated coverage results and enable the 'Coverage by test methods' section in the HTML report, allowing you to see how each test contributes to the overall coverage.");
public static readonly GUIContent AutoGenerateReportLabel = EditorGUIUtility.TrTextContent("Auto Generate Report", "Check this to generate the report automatically after the Test Runner has finished running the tests or the Coverage Recording session has completed.");
public static readonly GUIContent OpenReportWhenGeneratedLabel = EditorGUIUtility.TrTextContent("Auto Open Report", "Check this to open the coverage report automatically after it has been generated.");
public static readonly GUIContent GenerateReportButtonLabel = EditorGUIUtility.TrTextContent("Generate Report", "Generates a coverage report from the last set of tests that were run in the Test Runner or from the last Coverage Recording session.");
public static readonly GUIContent ClearCoverageButtonLabel = EditorGUIUtility.TrTextContent("Clear Results", "Clears the Coverage results from previous test runs or from previous Coverage Recording sessions, for the current project.");
public static readonly GUIContent ClearHistoryButtonLabel = EditorGUIUtility.TrTextContent("Clear History", "Clears the coverage report history.");
public static readonly GUIContent StartRecordingButton = EditorGUIUtility.TrIconContent("Record Off", "Record coverage data.");
public static readonly GUIContent StopRecordingButton = EditorGUIUtility.TrIconContent("Record On", "Stop recording coverage data.");
public static readonly GUIContent PauseRecordingButton = EditorGUIUtility.TrIconContent("PauseButton", "Pause recording coverage data.");
public static readonly GUIContent UnpauseRecordingButton = EditorGUIUtility.TrIconContent("PlayButton", "Resume recording coverage data.");
public static readonly GUIContent HelpIcon = EditorGUIUtility.TrIconContent("_Help", "Open Reference for Code Coverage.");
public static readonly GUIStyle settings = new GUIStyle();
public static void Init()
{
if (s_Initialized)
return;
s_Initialized = true;
settings.margin = new RectOffset(8, 4, 10, 4);
}
}
[MenuItem("Window/Analysis/Code Coverage")]
public static void ShowWindow()
{
#if TEST_FRAMEWORK_1_1_18_OR_NEWER
TestRunnerWindow.ShowWindow();
#else
TestRunnerWindow.ShowPlaymodeTestsRunnerWindowCodeBased();
#endif
CodeCoverageWindow window = GetWindow<CodeCoverageWindow>(typeof(TestRunnerWindow));
window.Show();
}
private void OnFocus()
{
Repaint();
}
private void ResetWindow()
{
CodeCoverageWindow window = GetWindow<CodeCoverageWindow>(typeof(TestRunnerWindow));
window.minSize = s_WindowMinSizeNormal;
window.titleContent = EditorGUIUtility.TrTextContentWithIcon(L10n.Tr("Code Coverage"), EditorIcons.CoverageWindow);
}
private void InitCodeCoverageWindow()
{
m_CoverageSettings = new CoverageSettings()
{
resultsPathFromCommandLine = CommandLineManager.instance.coverageResultsPath,
historyPathFromCommandLine = CommandLineManager.instance.coverageHistoryPath
};
m_CodeCoveragePath = CoveragePreferences.instance.GetStringForPaths("Path", string.Empty);
m_CodeCoverageHistoryPath = CoveragePreferences.instance.GetStringForPaths("HistoryPath", string.Empty);
m_CodeCoverageFormat = (CoverageFormat)CoveragePreferences.instance.GetInt("Format", 0);
m_GenerateAdditionalMetrics = CoveragePreferences.instance.GetBool("GenerateAdditionalMetrics", false);
m_GenerateTestReferences = CoveragePreferences.instance.GetBool("GenerateTestReferences", false);
m_IncludeHistoryInReport = CoveragePreferences.instance.GetBool("IncludeHistoryInReport", true);
m_AssembliesToInclude = GetIncludedAssemblies();
m_AssembliesToIncludeLength = m_AssembliesToInclude.Length;
m_PathsToInclude = CoveragePreferences.instance.GetStringForPaths("PathsToInclude", string.Empty);
m_PathsToExclude = CoveragePreferences.instance.GetStringForPaths("PathsToExclude", string.Empty);
CodeCoverage.VerbosityLevel = (LogVerbosityLevel)CoveragePreferences.instance.GetInt("VerbosityLevel", (int)LogVerbosityLevel.Info);
m_ReportGenerator = new CoverageReportGenerator();
m_GenerateHTMLReport = CoveragePreferences.instance.GetBool("GenerateHTMLReport", true);
m_GenerateAdditionalReports = CoveragePreferences.instance.GetBool("GenerateAdditionalReports", false);
m_GenerateBadge = CoveragePreferences.instance.GetBool("GenerateBadge", true);
m_AutoGenerateReport = CoveragePreferences.instance.GetBool("AutoGenerateReport", true);
m_OpenReportWhenGenerated = CoveragePreferences.instance.GetBool("OpenReportWhenGenerated", true);
m_ResultsFolderDropDownMenu = new FolderDropDownMenu(this, FolderType.Results);
m_HistoryFolderDropDownMenu = new FolderDropDownMenu(this, FolderType.History);
m_AddPathToIncludeDropDownMenu = new PathToAddDropDownMenu(this, PathFilterType.Include);
m_AddPathToExcludeDropDownMenu = new PathToAddDropDownMenu(this, PathFilterType.Exclude);
m_PathsToIncludeList = new List<string>(m_PathsToInclude.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
m_PathsToIncludeReorderableList = new ReorderableList(m_PathsToIncludeList, typeof(String), true, false, true, true);
m_PathsToIncludeReorderableList.headerHeight = 0;
m_PathsToExcludeList = new List<string>(m_PathsToExclude.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
m_PathsToExcludeReorderableList = new ReorderableList(m_PathsToExcludeList, typeof(String), true, false, true, true);
m_PathsToExcludeReorderableList.headerHeight = 0;
UpdateCoverageSettings();
m_GenerateReport = false;
m_StopRecording = false;
ResetWindow();
}
void UpdateCoverageSettings()
{
if (m_CoverageSettings != null)
{
m_CoverageSettings.rootFolderPath = CoverageUtils.GetRootFolderPath(m_CoverageSettings);
m_CoverageSettings.historyFolderPath = CoverageUtils.GetHistoryFolderPath(m_CoverageSettings);
if (m_CodeCoverageFormat == CoverageFormat.OpenCover)
{
m_CoverageSettings.resultsFolderSuffix = "-opencov";
string folderName = CoverageUtils.GetProjectFolderName();
string resultsRootDirectoryName = string.Concat(folderName, m_CoverageSettings.resultsFolderSuffix);
m_CoverageSettings.resultsRootFolderPath = CoverageUtils.NormaliseFolderSeparators(CoverageUtils.JoinPaths(m_CoverageSettings.rootFolderPath, resultsRootDirectoryName));
}
}
}
private string GetIncludedAssemblies()
{
string assembliesFromPreferences = CoveragePreferences.instance.GetString("IncludeAssemblies", AssemblyFiltering.GetUserOnlyAssembliesString());
string filteredAssemblies = AssemblyFiltering.RemoveAssembliesThatNoLongerExist(assembliesFromPreferences);
CoveragePreferences.instance.SetString("IncludeAssemblies", filteredAssemblies);
return filteredAssemblies;
}
private void OnEnable()
{
InitCodeCoverageWindow();
}
public void OnGUI()
{
Styles.Init();
EditorGUIUtility.labelWidth = 190f;
GUILayout.BeginVertical();
float toolbarHeight = 0;
#if !UNITY_2019_3_OR_NEWER
using (new EditorGUI.DisabledScope(!m_HasLatestScriptingRuntime))
#endif
{
toolbarHeight = DrawToolbar();
}
// Window scrollbar
float maxHeight = Mathf.Max(position.height, 0) - toolbarHeight;
m_WindowScrollPosition = EditorGUILayout.BeginScrollView(m_WindowScrollPosition, GUILayout.Height(maxHeight));
GUILayout.BeginVertical(Styles.settings);
#if !UNITY_2019_3_OR_NEWER
CheckScriptingRuntimeVersion();
#endif
CheckCoverageEnabled();
#if UNITY_2020_1_OR_NEWER
CheckCodeOptimization();
#endif
#if BURST_INSTALLED
CheckBurstCompilation();
#endif
#if !UNITY_2019_3_OR_NEWER
using (new EditorGUI.DisabledScope(!m_HasLatestScriptingRuntime))
#endif
{
// Draw Settings
EditorGUILayout.LabelField(Styles.CoverageSettingsLabel, EditorStyles.boldLabel);
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning))
{
DrawCoverageSettings();
}
// Draw Coverage Report Options
GUILayout.Space(10);
EditorGUILayout.LabelField(Styles.CoverageReportOptionsLabel, EditorStyles.boldLabel);
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning))
{
DrawCoverageReportOptions();
}
}
GUILayout.EndVertical();
EditorGUILayout.EndScrollView();
GUILayout.EndVertical();
HandleFocusRepaint();
}
float DrawToolbar()
{
GUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Space(10);
// Coverage Recording button
bool isRunning = CoverageRunData.instance.isRunning;
bool isRecording = CoverageRunData.instance.isRecording;
bool isRecordingPaused = CoverageRunData.instance.isRecordingPaused;
using (new EditorGUI.DisabledScope((isRunning && !isRecording) || m_StopRecording || m_AssembliesToIncludeLength == 0 || !Coverage.enabled || m_EnableCodeCoverage != Coverage.enabled || EditorApplication.isCompiling))
{
if (EditorGUILayout.DropdownButton(isRecording ? Styles.StopRecordingButton : Styles.StartRecordingButton, FocusType.Keyboard, EditorStyles.toolbarButton))
{
if (isRecording)
{
m_StopRecording = true;
}
else
{
CodeCoverage.StartRecordingInternal();
}
}
}
using (new EditorGUI.DisabledScope(!isRecording || m_StopRecording || m_AssembliesToIncludeLength == 0 || !Coverage.enabled || m_EnableCodeCoverage != Coverage.enabled || EditorApplication.isCompiling))
{
if (EditorGUILayout.DropdownButton(isRecordingPaused ? Styles.UnpauseRecordingButton : Styles.PauseRecordingButton, FocusType.Keyboard, EditorStyles.toolbarButton))
{
if (isRecordingPaused)
{
CodeCoverage.UnpauseRecordingInternal();
}
else
{
CodeCoverage.PauseRecordingInternal();
}
}
}
using (new EditorGUI.DisabledScope((!m_GenerateHTMLReport && !m_GenerateBadge && !m_GenerateAdditionalReports) || !DoesResultsRootFolderExist() || CoverageRunData.instance.isRunning || m_GenerateReport || m_AssembliesToIncludeLength == 0 || !Coverage.enabled || m_EnableCodeCoverage != Coverage.enabled || EditorApplication.isCompiling))
{
if (EditorGUILayout.DropdownButton(Styles.GenerateReportButtonLabel, FocusType.Keyboard, EditorStyles.toolbarButton))
{
m_GenerateReport = true;
}
}
GUILayout.FlexibleSpace();
using (new EditorGUI.DisabledScope(!DoesResultsRootFolderExist() || CoverageRunData.instance.isRunning))
{
if (EditorGUILayout.DropdownButton(Styles.ClearCoverageButtonLabel, FocusType.Keyboard, EditorStyles.toolbarButton))
{
ClearResultsRootFolderIfExists();
}
}
using (new EditorGUI.DisabledScope(!DoesReportHistoryExist() || CoverageRunData.instance.isRunning))
{
if (EditorGUILayout.DropdownButton(Styles.ClearHistoryButtonLabel, FocusType.Keyboard, EditorStyles.toolbarButton))
{
ClearReportHistoryFolderIfExists();
}
}
DrawHelpIcon();
GUILayout.EndHorizontal();
return EditorStyles.toolbar.fixedHeight;
}
void DrawHelpIcon()
{
if (GUILayout.Button(Styles.HelpIcon, EditorStyles.toolbarButton))
{
PackageManager.PackageInfo packageInfo = PackageManager.PackageInfo.FindForAssetPath("Packages/com.unity.testtools.codecoverage");
if (packageInfo != null)
{
string shortVersion = packageInfo.version.Substring(0, packageInfo.version.IndexOf('.', packageInfo.version.IndexOf('.') + 1));
string documentationUrl = $"https://docs.unity3d.com/Packages/com.unity.testtools.codecoverage@{shortVersion}";
Application.OpenURL(documentationUrl);
}
}
}
void HandleFocusRepaint()
{
Rect r = EditorGUILayout.GetControlRect();
// Lose focus if mouse is down outside of UI elements
if (Event.current.type == EventType.MouseDown && !r.Contains(Event.current.mousePosition))
{
m_LoseFocus = true;
}
if (m_LoseFocus)
{
GUI.FocusControl("");
m_PathsToIncludeReorderableList.ReleaseKeyboardFocus();
m_PathsToExcludeReorderableList.ReleaseKeyboardFocus();
m_LoseFocus = false;
Repaint();
}
}
#if !UNITY_2019_3_OR_NEWER
void CheckScriptingRuntimeVersion()
{
m_HasLatestScriptingRuntime = PlayerSettings.scriptingRuntimeVersion == ScriptingRuntimeVersion.Latest;
if (!m_HasLatestScriptingRuntime)
{
EditorGUILayout.HelpBox(kLatestScriptingRuntimeMessage, MessageType.Warning);
GUILayout.Space(5);
}
}
#endif
void CheckCoverageEnabled()
{
#if UNITY_2020_2_OR_NEWER
m_EnableCodeCoverage = Coverage.enabled;
#else
m_EnableCodeCoverage = EditorPrefs.GetBool("CodeCoverageEnabled", false);
#endif
}
#if UNITY_2020_1_OR_NEWER
void CheckCodeOptimization()
{
if (Compilation.CompilationPipeline.codeOptimization == Compilation.CodeOptimization.Release)
{
EditorGUILayout.HelpBox(kCodeOptimizationMessage, MessageType.Warning);
using (new EditorGUI.DisabledScope(EditorApplication.isCompiling))
{
if (GUILayout.Button(Styles.SwitchToDebugCodeOptimizationButton))
{
CoverageAnalytics.instance.CurrentCoverageEvent.switchToDebugMode = true;
Compilation.CompilationPipeline.codeOptimization = Compilation.CodeOptimization.Debug;
EditorPrefs.SetBool("ScriptDebugInfoEnabled", true);
}
}
GUILayout.Space(5);
}
}
#endif
#if BURST_INSTALLED
void CheckBurstCompilation()
{
if (EditorPrefs.GetBool("BurstCompilation", false))
{
EditorGUILayout.HelpBox(kBurstCompilationOnMessage, MessageType.Warning);
if (GUILayout.Button(Styles.SwitchBurstCompilationOffButton))
{
CoverageAnalytics.instance.CurrentCoverageEvent.switchBurstOff = true;
EditorApplication.ExecuteMenuItem("Jobs/Burst/Enable Compilation");
}
GUILayout.Space(5);
}
}
#endif
void CheckIfIncludedAssembliesIsEmpty()
{
if (m_AssembliesToIncludeLength == 0)
{
EditorGUILayout.HelpBox(kNoAssembliesSelectedMessage, MessageType.Warning);
}
}
void DrawCodeCoverageLocation()
{
GUILayout.BeginHorizontal();
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.coverageResultsPath.Length > 0;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
Rect textFieldPosition = EditorGUILayout.GetControlRect();
textFieldPosition = EditorGUI.PrefixLabel(textFieldPosition, Styles.CodeCoverageResultsLocationLabel);
EditorGUI.SelectableLabel(textFieldPosition, settingPassedInCmdLine ? CommandLineManager.instance.coverageResultsPath : m_CodeCoveragePath, EditorStyles.textField);
bool autoDetect = !CoverageUtils.EnsureFolderExists(m_CodeCoveragePath);
if (autoDetect)
{
SetDefaultCoverageLocation();
}
Rect btnPosition = GUILayoutUtility.GetRect(Styles.ResultsFolderButton, EditorStyles.miniPullDown, GUILayout.MaxWidth(38));
if (EditorGUI.DropdownButton(btnPosition, Styles.ResultsFolderButton, FocusType.Keyboard, EditorStyles.miniPullDown))
{
m_ResultsFolderDropDownMenu.Show(btnPosition, m_CodeCoveragePath, kSelectCoverageDirectoryMessage);
}
}
GUILayout.EndHorizontal();
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "Results Location", "-coverageResultsPath"), MessageType.Warning);
}
}
void DrawCodeCoverageHistoryLocation()
{
GUILayout.BeginHorizontal();
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.coverageHistoryPath.Length > 0;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
Rect textFieldPosition = EditorGUILayout.GetControlRect();
textFieldPosition = EditorGUI.PrefixLabel(textFieldPosition, Styles.CodeCoverageHistoryLocationLabel);
EditorGUI.SelectableLabel(textFieldPosition, settingPassedInCmdLine ? CommandLineManager.instance.coverageHistoryPath : m_CodeCoverageHistoryPath, EditorStyles.textField);
bool autoDetect = !CoverageUtils.EnsureFolderExists(m_CodeCoverageHistoryPath);
if (autoDetect)
{
SetDefaultCoverageHistoryLocation();
}
Rect btnPosition = GUILayoutUtility.GetRect(Styles.HistoryFolderButton, EditorStyles.miniPullDown, GUILayout.MaxWidth(38));
if (EditorGUI.DropdownButton(btnPosition, Styles.HistoryFolderButton, FocusType.Keyboard, EditorStyles.miniPullDown))
{
m_HistoryFolderDropDownMenu.Show(btnPosition, m_CodeCoverageHistoryPath, kSelectCoverageHistoryDirectoryMessage);
}
}
GUILayout.EndHorizontal();
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "History Location", "-coverageHistoryPath"), MessageType.Warning);
}
}
void DrawIncludedAssemblies()
{
GUILayout.BeginHorizontal();
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.assemblyFiltersSpecified;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
EditorGUILayout.PrefixLabel(Styles.AssembliesToIncludeLabel);
Rect buttonRect = EditorGUILayout.GetControlRect(GUILayout.MinWidth(10));
Styles.AssembliesToIncludeDropdownLabel.text = string.Concat(" ", m_AssembliesToInclude);
Styles.AssembliesToIncludeDropdownLabel.tooltip = m_AssembliesToInclude.Replace(",", "\n");
if (EditorGUI.DropdownButton(buttonRect, m_AssembliesToInclude.Length > 0 ? Styles.AssembliesToIncludeDropdownLabel : Styles.AssembliesToIncludeEmptyDropdownLabel, FocusType.Keyboard, EditorStyles.miniPullDown))
{
CoverageAnalytics.instance.CurrentCoverageEvent.enterAssembliesDialog = true;
GUI.FocusControl("");
PopupWindow.Show(buttonRect, new IncludedAssembliesPopupWindow(this, m_AssembliesToInclude) { Width = buttonRect.width });
}
}
GUILayout.EndHorizontal();
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "Included Assemblies", "-coverageOptions: assemblyFilters"), MessageType.Warning);
}
}
void DrawPathFiltering()
{
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.pathFiltersSpecified;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
DrawIncludedPaths();
DrawExcludedPaths();
}
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingsOverriddenMessage, "Included/Excluded Paths", "-coverageOptions: pathFilters/pathFiltersFromFile"), MessageType.Warning);
}
}
void DrawIncludedPaths()
{
GUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(Styles.PathsToIncludeLabel);
GUILayout.BeginVertical();
GUILayout.Space(4);
EditorGUI.BeginChangeCheck();
m_PathsToIncludeReorderableList.drawElementCallback = DrawPathsToIncludeListItem;
m_PathsToIncludeReorderableList.onChangedCallback = (rl) => { PathsToInclude = string.Join(",", rl.list as List<string>); };
m_PathsToIncludeReorderableList.onAddDropdownCallback = (rect, rl) => { m_AddPathToIncludeDropDownMenu.Show(rect, m_PathsToInclude); };
m_PathsToIncludeReorderableList.DoLayoutList();
if (EditorGUI.EndChangeCheck())
{
OnPathsListChange(m_PathsToIncludeReorderableList, PathFilterType.Include);
}
GUILayout.EndVertical();
GUILayout.Space(2);
GUILayout.EndHorizontal();
}
void DrawExcludedPaths()
{
GUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(Styles.PathsToExcludeLabel);
GUILayout.BeginVertical();
GUILayout.Space(4);
EditorGUI.BeginChangeCheck();
m_PathsToExcludeReorderableList.drawElementCallback = DrawPathsToExcludeListItem;
m_PathsToExcludeReorderableList.onChangedCallback = (rl) => { PathsToExclude = string.Join(",", rl.list as List<string>); };
m_PathsToExcludeReorderableList.onAddDropdownCallback = (rect, rl) => { m_AddPathToExcludeDropDownMenu.Show(rect, m_PathsToExclude); };
m_PathsToExcludeReorderableList.DoLayoutList();
if (EditorGUI.EndChangeCheck())
{
OnPathsListChange(m_PathsToExcludeReorderableList, PathFilterType.Exclude);
}
GUILayout.EndVertical();
GUILayout.Space(2);
GUILayout.EndHorizontal();
}
void DrawPathsToIncludeListItem(Rect rect, int index, bool isActive, bool isFocused)
{
if (index >= 0 && index < m_PathsToIncludeList.Count)
{
string pathToInclude = m_PathsToIncludeList[index].Replace(",", "");
m_PathsToIncludeReorderableList.list[index] = EditorGUI.TextField(rect, pathToInclude);
}
}
void DrawPathsToExcludeListItem(Rect rect, int index, bool isActive, bool isFocused)
{
if (index >= 0 && index < m_PathsToExcludeList.Count)
{
string pathToExclude = m_PathsToExcludeList[index];
m_PathsToExcludeReorderableList.list[index] = EditorGUI.TextField(rect, pathToExclude);
}
}
void OnPathsListChange(ReorderableList rl, PathFilterType pathFilterType)
{
var pathsList = rl.list as List<string>;
int listSize = pathsList.Count;
for (int i = 0; i < listSize; ++i)
{
var itemStr = pathsList[i];
itemStr = itemStr.Replace(",", "");
if (string.IsNullOrWhiteSpace(itemStr))
{
itemStr = "-";
}
pathsList[i] = itemStr;
}
if (pathFilterType == PathFilterType.Include)
PathsToInclude = string.Join(",", pathsList);
else if (pathFilterType == PathFilterType.Exclude)
PathsToExclude = string.Join(",", pathsList);
}
void CheckIfCodeCoverageIsDisabled()
{
if (!m_EnableCodeCoverage)
{
EditorGUILayout.HelpBox(kCodeCoverageDisabledNoRestartMessage, MessageType.Warning);
}
#if !UNITY_2020_2_OR_NEWER
if (m_EnableCodeCoverage != Coverage.enabled)
{
if (m_EnableCodeCoverage)
EditorGUILayout.HelpBox(kEnablingCodeCoverageMessage, MessageType.Warning);
else
EditorGUILayout.HelpBox(kDisablingCodeCoverageMessage, MessageType.Warning);
}
#endif
}
void DrawVerbosityLevel()
{
GUILayout.Space(2);
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.verbosityLevelSpecified;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
EditorGUI.BeginChangeCheck();
int verbosityLevel = EditorGUILayout.Popup(Styles.VerbosityLabel, (int)CodeCoverage.VerbosityLevel, kVerbosityLevelLabels);
if (EditorGUI.EndChangeCheck())
{
CoveragePreferences.instance.SetInt("VerbosityLevel", verbosityLevel);
CodeCoverage.VerbosityLevel = (LogVerbosityLevel)verbosityLevel;
}
}
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "Logs Verbosity Level", "-coverageOptions: verbosity"), MessageType.Warning);
}
}
void DrawEnableCoverageCheckbox()
{
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine;
using (new EditorGUI.DisabledScope(EditorApplication.isCompiling || settingPassedInCmdLine))
{
EditorGUI.BeginChangeCheck();
m_EnableCodeCoverage = EditorGUILayout.Toggle(Styles.EnableCodeCoverageLabel, m_EnableCodeCoverage, GUILayout.ExpandWidth(false));
if (EditorGUI.EndChangeCheck())
{
CoveragePreferences.instance.SetBool("EnableCodeCoverage", m_EnableCodeCoverage);
#if UNITY_2020_2_OR_NEWER
Coverage.enabled = m_EnableCodeCoverage;
#else
EditorPrefs.SetBool("CodeCoverageEnabled", m_EnableCodeCoverage);
EditorPrefs.SetBool("CodeCoverageEnabledMessageShown", false);
SettingsService.NotifySettingsProviderChanged();
#endif
}
}
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "Enable Code Coverage", "-enableCodeCoverage"), MessageType.Warning);
}
else
{
CheckIfCodeCoverageIsDisabled();
}
}
void DrawGenerateHTMLReportCheckbox()
{
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.generateHTMLReport;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
EditorGUI.BeginChangeCheck();
m_GenerateHTMLReport = EditorGUILayout.Toggle(Styles.GenerateHTMLReportLabel, m_GenerateHTMLReport, GUILayout.ExpandWidth(false));
if (EditorGUI.EndChangeCheck())
{
CoveragePreferences.instance.SetBool("GenerateHTMLReport", m_GenerateHTMLReport);
}
}
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "HTML Report", "-coverageOptions: generateHtmlReport"), MessageType.Warning);
}
}
void DrawGenerateAdditionalReportsCheckbox()
{
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.generateAdditionalReports;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
EditorGUI.BeginChangeCheck();
m_GenerateAdditionalReports = EditorGUILayout.Toggle(Styles.GenerateAdditionalReportsLabel, m_GenerateAdditionalReports, GUILayout.ExpandWidth(false));
if (EditorGUI.EndChangeCheck())
{
CoveragePreferences.instance.SetBool("GenerateAdditionalReports", m_GenerateAdditionalReports);
}
}
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "Additional Reports", "-coverageOptions: generateAdditionalReports"), MessageType.Warning);
}
}
void DrawGenerateBadgeReportCheckbox()
{
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.generateBadgeReport;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
EditorGUI.BeginChangeCheck();
m_GenerateBadge = EditorGUILayout.Toggle(Styles.GenerateBadgeReportLabel, m_GenerateBadge, GUILayout.ExpandWidth(false));
if (EditorGUI.EndChangeCheck())
{
CoveragePreferences.instance.SetBool("GenerateBadge", m_GenerateBadge);
}
}
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "Summary Badges", "-coverageOptions: generateBadgeReport"), MessageType.Warning);
}
}
void DrawGenerateHistoryCheckbox()
{
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.generateHTMLReportHistory;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
EditorGUI.BeginChangeCheck();
using (new EditorGUI.DisabledScope(!m_GenerateHTMLReport && !m_GenerateAdditionalReports))
{
m_IncludeHistoryInReport = EditorGUILayout.Toggle(Styles.CoverageHistoryLabel, m_IncludeHistoryInReport, GUILayout.ExpandWidth(false));
if (EditorGUI.EndChangeCheck())
{
CoveragePreferences.instance.SetBool("IncludeHistoryInReport", m_IncludeHistoryInReport);
}
}
}
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "Report History", "-coverageOptions: generateHtmlReportHistory"), MessageType.Warning);
}
}
void DrawGenerateAdditionalMetricsCheckbox()
{
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.generateAdditionalMetrics;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
EditorGUI.BeginChangeCheck();
m_GenerateAdditionalMetrics = EditorGUILayout.Toggle(Styles.GenerateAdditionalMetricsLabel, m_GenerateAdditionalMetrics, GUILayout.ExpandWidth(false));
if (EditorGUI.EndChangeCheck())
{
CoveragePreferences.instance.SetBool("GenerateAdditionalMetrics", m_GenerateAdditionalMetrics);
}
}
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "Additional Metrics", "-coverageOptions: generateAdditionalMetrics"), MessageType.Warning);
}
}
void DrawGenerateTestRunnerReferencesCheckbox()
{
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && CommandLineManager.instance.generateTestReferences;
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
EditorGUI.BeginChangeCheck();
m_GenerateTestReferences = EditorGUILayout.Toggle(Styles.GenerateTestRunnerReferencesLabel, m_GenerateTestReferences, GUILayout.ExpandWidth(false));
if (EditorGUI.EndChangeCheck())
{
CoveragePreferences.instance.SetBool("GenerateTestReferences", m_GenerateTestReferences);
}
}
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "Test Runner References", "-coverageOptions: generateTestReferences"), MessageType.Warning);
}
}
void DrawAutoGenerateReportCheckbox()
{
bool settingPassedInCmdLine = CommandLineManager.instance.runFromCommandLine && (CommandLineManager.instance.generateHTMLReport || CommandLineManager.instance.generateBadgeReport);
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning || settingPassedInCmdLine))
{
EditorGUI.BeginChangeCheck();
m_AutoGenerateReport = EditorGUILayout.Toggle(Styles.AutoGenerateReportLabel, m_AutoGenerateReport, GUILayout.ExpandWidth(false));
if (EditorGUI.EndChangeCheck())
{
CoveragePreferences.instance.SetBool("AutoGenerateReport", m_AutoGenerateReport);
}
}
if (settingPassedInCmdLine)
{
EditorGUILayout.HelpBox(string.Format(kSettingOverriddenMessage, "Auto Generate Report", CommandLineManager.instance.generateHTMLReport ? "-coverageOptions: generateHtmlReport" : "-coverageOptions: generateBadgeReport"), MessageType.Warning);
}
}
void DrawOpenReportWhenGeneratedCheckbox()
{
using (new EditorGUI.DisabledScope(CoverageRunData.instance.isRunning))
{
EditorGUI.BeginChangeCheck();
m_OpenReportWhenGenerated = EditorGUILayout.Toggle(Styles.OpenReportWhenGeneratedLabel, m_OpenReportWhenGenerated, GUILayout.ExpandWidth(false));
if (EditorGUI.EndChangeCheck())
{
CoveragePreferences.instance.SetBool("OpenReportWhenGenerated", m_OpenReportWhenGenerated);
}
}
}
void DrawCoverageSettings()
{
DrawEnableCoverageCheckbox();
DrawCodeCoverageLocation();
DrawCodeCoverageHistoryLocation();
DrawIncludedAssemblies();
CheckIfIncludedAssembliesIsEmpty();
DrawPathFiltering();
DrawVerbosityLevel();
}
void DrawCoverageReportOptions()
{
DrawGenerateHTMLReportCheckbox();
DrawGenerateAdditionalReportsCheckbox();
DrawGenerateHistoryCheckbox();
DrawGenerateBadgeReportCheckbox();
DrawGenerateAdditionalMetricsCheckbox();
DrawGenerateTestRunnerReferencesCheckbox();
DrawAutoGenerateReportCheckbox();
DrawOpenReportWhenGeneratedCheckbox();
}
private bool DoesResultsRootFolderExist()
{
if (m_CoverageSettings == null)
return false;
string resultsRootFolderPath = m_CoverageSettings.resultsRootFolderPath;
return CoverageUtils.GetNumberOfFilesInFolder(resultsRootFolderPath, "*.xml", SearchOption.AllDirectories) > 0;
}
private void ClearResultsRootFolderIfExists()
{
CoverageAnalytics.instance.CurrentCoverageEvent.clearData = true;
if (!EditorUtility.DisplayDialog(L10n.Tr("Clear Results"), kClearDataMessage, L10n.Tr("Clear"), L10n.Tr("Cancel")))
return;
if (m_CoverageSettings == null)
return;
string resultsRootFolderPath = m_CoverageSettings.resultsRootFolderPath;
CoverageUtils.ClearFolderIfExists(resultsRootFolderPath, "*.xml");
}
public string GetResultsRootFolder()
{
if (m_CoverageSettings == null)
return m_CodeCoveragePath;
string resultsRootFolderPath = m_CoverageSettings.resultsRootFolderPath;
CoverageUtils.EnsureFolderExists(resultsRootFolderPath);
return resultsRootFolderPath;
}
private bool DoesReportHistoryExist()
{
if (m_CoverageSettings == null)
return false;
string historyFolderPath = m_CoverageSettings.historyFolderPath;
return CoverageUtils.GetNumberOfFilesInFolder(historyFolderPath, "????-??-??_??-??-??_CoverageHistory.xml", SearchOption.TopDirectoryOnly) > 0;
}
private void ClearReportHistoryFolderIfExists()
{
CoverageAnalytics.instance.CurrentCoverageEvent.clearHistory = true;
if (!EditorUtility.DisplayDialog(L10n.Tr("Clear Report History"), kClearHistoryMessage, L10n.Tr("Clear"), L10n.Tr("Cancel")))
return;
if (m_CoverageSettings == null)
return;
string historyFolderPath = m_CoverageSettings.historyFolderPath;
CoverageUtils.ClearFolderIfExists(historyFolderPath, "????-??-??_??-??-??_CoverageHistory.xml");
}
public string GetReportHistoryFolder()
{
if (m_CoverageSettings == null)
return m_CodeCoverageHistoryPath;
string historyFolderPath = m_CoverageSettings.historyFolderPath;
CoverageUtils.EnsureFolderExists(historyFolderPath);
return historyFolderPath;
}
public void SetCoverageLocation(string folderPath)
{
if (CoverageUtils.IsValidFolder(folderPath))
{
m_CodeCoveragePath = folderPath;
CoveragePreferences.instance.SetStringForPaths("Path", m_CodeCoveragePath);
UpdateCoverageSettings();
}
}
public void SetDefaultCoverageLocation()
{
SetCoverageLocation(CoverageUtils.GetProjectPath());
}
public void SetCoverageHistoryLocation(string folderPath)
{
if (CoverageUtils.IsValidFolder(folderPath))
{
m_CodeCoverageHistoryPath = folderPath;
CoveragePreferences.instance.SetStringForPaths("HistoryPath", m_CodeCoverageHistoryPath);
UpdateCoverageSettings();
}
}
public void SetDefaultCoverageHistoryLocation()
{
SetCoverageHistoryLocation(CoverageUtils.GetProjectPath());
}
}
}

View File

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

View File

@ -0,0 +1,78 @@
using UnityEngine;
using UnityEditor.TestTools.CodeCoverage.Utils;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class FolderDropDownMenu
{
GenericMenu m_Menu;
string m_Path;
string m_Message;
readonly CodeCoverageWindow m_Parent;
readonly FolderType m_FolderType;
static class Styles
{
public static readonly GUIContent ShowInExplorerLabel = EditorGUIUtility.TrTextContent("Open Containing Folder");
public static readonly GUIContent ChangeLocationLabel = EditorGUIUtility.TrTextContent("Change Location");
public static readonly GUIContent ResetToDefaultLabel = EditorGUIUtility.TrTextContent("Reset to Default Location");
}
public FolderDropDownMenu(CodeCoverageWindow parent, FolderType type)
{
m_Parent = parent;
m_FolderType = type;
}
private void PopulateMenu()
{
m_Menu = new GenericMenu();
m_Menu.AddItem(Styles.ShowInExplorerLabel, false, () => ShowInExplorer());
m_Menu.AddItem(Styles.ChangeLocationLabel, false, () => ChangeLocation());
if (m_Path.Equals(CoverageUtils.GetProjectPath()))
m_Menu.AddDisabledItem(Styles.ResetToDefaultLabel);
else
m_Menu.AddItem(Styles.ResetToDefaultLabel, false, () => ResetToDefault());
}
public void Show(Rect position, string folderPath, string message)
{
m_Path = folderPath;
m_Message = message;
PopulateMenu();
m_Menu.DropDown(position);
}
private void ShowInExplorer()
{
string path = m_FolderType == FolderType.Results ?
m_Parent.GetResultsRootFolder() :
m_Parent.GetReportHistoryFolder();
EditorUtility.RevealInFinder(path);
}
private void ChangeLocation()
{
string candidate = CoverageUtils.BrowseForDir(m_Path, m_Message);
if (m_FolderType == FolderType.Results)
m_Parent.SetCoverageLocation(candidate);
else
m_Parent.SetCoverageHistoryLocation(candidate);
m_Parent.LoseFocus();
}
private void ResetToDefault()
{
if (m_FolderType == FolderType.Results)
m_Parent.SetDefaultCoverageLocation();
else
m_Parent.SetDefaultCoverageHistoryLocation();
m_Parent.LoseFocus();
}
}
}

View File

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

View File

@ -0,0 +1,8 @@
namespace UnityEditor.TestTools.CodeCoverage
{
internal enum FolderType
{
Results = 0,
History = 1
}
}

View File

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

View File

@ -0,0 +1,90 @@
using UnityEngine;
using UnityEditor.IMGUI.Controls;
namespace UnityEditor.TestTools.CodeCoverage
{
class IncludedAssembliesPopupWindow : PopupWindowContent
{
readonly SearchField m_SearchField;
readonly IncludedAssembliesTreeView m_TreeView;
const float kWindowHeight = 221;
public float Width { get; set; }
static class Styles
{
public static readonly GUIContent SelectLabel = EditorGUIUtility.TrTextContent("Select:");
public static readonly GUIContent SelectAllButtonLabel = EditorGUIUtility.TrTextContent("All", "Click this to select and include all the assemblies in the project. This includes both the assemblies under the 'Assets' folder and packages.\n\nIf searching, it will apply only to the assemblies visible in the list.");
public static readonly GUIContent SelectAssetsButtonLabel = EditorGUIUtility.TrTextContent("Assets", "Click this to select and include only the assemblies under the 'Assets' folder.\n\nIf searching, it will apply only to the assemblies visible in the list.");
public static readonly GUIContent SelectPackagesButtonLabel = EditorGUIUtility.TrTextContent("Packages", "Click this to select and include only the Packages' assemblies.\n\nIf searching, it will apply only to the assemblies visible in the list.");
public static readonly GUIContent DeselectAllButtonLabel = EditorGUIUtility.TrTextContent("Deselect All", "Click this to deselect and exclude all the assemblies.\n\nIf searching, it will apply only to the assemblies visible in the list.");
}
public IncludedAssembliesPopupWindow(CodeCoverageWindow parent, string assembliesToInclude)
{
m_SearchField = new SearchField();
m_TreeView = new IncludedAssembliesTreeView(parent, assembliesToInclude);
}
public override void OnGUI(Rect rect)
{
const int border = 4;
const int topPadding = 12;
const int searchHeight = 20;
const int buttonHeight = 16;
const int remainTop = topPadding + searchHeight + buttonHeight + border + border;
float selectLabelWidth = EditorStyles.boldLabel.CalcSize(Styles.SelectLabel).x;
float selectAllWidth = EditorStyles.miniButton.CalcSize(Styles.SelectAllButtonLabel).x;
float selectAssetsWidth = EditorStyles.miniButton.CalcSize(Styles.SelectAssetsButtonLabel).x;
float selectPackagesWidth = EditorStyles.miniButton.CalcSize(Styles.SelectPackagesButtonLabel).x;
float deselectAllWidth = EditorStyles.miniButton.CalcSize(Styles.DeselectAllButtonLabel).x;
Rect searchRect = new Rect(border, topPadding, rect.width - border * 2, searchHeight);
Rect selectLabelRect = new Rect(border, topPadding + searchHeight + border, selectLabelWidth, searchHeight);
Rect selectAllRect = new Rect(border + selectLabelWidth + border, topPadding + searchHeight + border, selectAllWidth, buttonHeight);
Rect selectAssetsRect = new Rect(border + selectLabelWidth + border + selectAllWidth + border, topPadding + searchHeight + border, selectAssetsWidth, buttonHeight);
Rect selectPackagesRect = new Rect(border + selectLabelWidth + border + selectAllWidth + border + selectAssetsWidth + border, topPadding + searchHeight + border, selectPackagesWidth, buttonHeight);
Rect deselectAllRect = new Rect(rect.width - border - deselectAllWidth, topPadding + searchHeight + border, deselectAllWidth, buttonHeight);
Rect remainingRect = new Rect(border, remainTop, rect.width - border * 2, rect.height - remainTop - border);
m_TreeView.searchString = m_SearchField.OnGUI(searchRect, m_TreeView.searchString);
GUI.Label(selectLabelRect, Styles.SelectLabel, EditorStyles.boldLabel);
if (GUI.Button(selectAllRect, Styles.SelectAllButtonLabel, EditorStyles.miniButton))
{
m_TreeView.SelectAll();
}
if (GUI.Button(deselectAllRect, Styles.DeselectAllButtonLabel, EditorStyles.miniButton))
{
m_TreeView.DeselectAll();
}
if (GUI.Button(selectAssetsRect, Styles.SelectAssetsButtonLabel, EditorStyles.miniButton))
{
m_TreeView.SelectAssets();
}
if (GUI.Button(selectPackagesRect, Styles.SelectPackagesButtonLabel, EditorStyles.miniButton))
{
m_TreeView.SelectPackages();
}
m_TreeView.OnGUI(remainingRect);
}
public override Vector2 GetWindowSize()
{
return new Vector2(Mathf.Max(Width, m_TreeView.Width), kWindowHeight);
}
public override void OnOpen()
{
m_SearchField.SetFocus();
base.OnOpen();
}
}
}

View File

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

View File

@ -0,0 +1,184 @@
using UnityEditor.IMGUI.Controls;
using UnityEditor.Compilation;
using System.Text.RegularExpressions;
using System.Text;
using System.Linq;
using System;
using UnityEngine;
using UnityEditor.TestTools.CodeCoverage.Analytics;
namespace UnityEditor.TestTools.CodeCoverage
{
class IncludedAssembliesTreeView : TreeView
{
string m_AssembliesToInclude;
readonly CodeCoverageWindow m_Parent;
const float kCheckBoxWidth = 42f;
public float Width { get; set; } = 100f;
public IncludedAssembliesTreeView(CodeCoverageWindow parent, string assembliesToInclude)
: base(new TreeViewState())
{
m_AssembliesToInclude = assembliesToInclude;
m_Parent = parent;
showAlternatingRowBackgrounds = true;
showBorder = true;
Reload();
}
protected override bool CanMultiSelect(TreeViewItem item)
{
return false;
}
protected override TreeViewItem BuildRoot()
{
string[] includeAssemblyFilters = m_AssembliesToInclude.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
Regex[] includeAssemblies = includeAssemblyFilters
.Select(f => AssemblyFiltering.CreateFilterRegex(f))
.ToArray();
TreeViewItem root = new TreeViewItem(-1, -1);
bool developerMode = EditorPrefs.GetBool("DeveloperMode", false);
if (developerMode)
{
System.Reflection.Assembly[] assemblies = AssemblyFiltering.GetAllProjectAssembliesInternal();
int assembliesLength = assemblies.Length;
GUIContent textContent = new GUIContent();
for (int i = 0; i < assembliesLength; ++i)
{
System.Reflection.Assembly assembly = assemblies[i];
bool enabled = includeAssemblies.Any(f => f.IsMatch(assembly.GetName().Name.ToLowerInvariant()));
root.AddChild(new AssembliesTreeViewItem() { id = i + 1, displayName = assembly.GetName().Name, Enabled = enabled });
textContent.text = assembly.GetName().Name;
float itemWidth = TreeView.DefaultStyles.label.CalcSize(textContent).x + kCheckBoxWidth;
if (Width < itemWidth)
Width = itemWidth;
}
}
else
{
Assembly[] assemblies = AssemblyFiltering.GetAllProjectAssemblies();
int assembliesLength = assemblies.Length;
GUIContent textContent = new GUIContent();
for (int i = 0; i < assembliesLength; ++i)
{
Assembly assembly = assemblies[i];
bool enabled = includeAssemblies.Any(f => f.IsMatch(assembly.name.ToLowerInvariant()));
root.AddChild(new AssembliesTreeViewItem() { id = i + 1, displayName = assembly.name, Enabled = enabled });
textContent.text = assembly.name;
float itemWidth = TreeView.DefaultStyles.label.CalcSize(textContent).x + kCheckBoxWidth;
if (Width < itemWidth)
Width = itemWidth;
}
}
return root;
}
protected override void RowGUI(RowGUIArgs args)
{
AssembliesTreeViewItem item = args.item as AssembliesTreeViewItem;
EditorGUI.BeginChangeCheck();
bool enabled = EditorGUI.ToggleLeft(args.rowRect, args.label, item.Enabled);
if (EditorGUI.EndChangeCheck())
{
item.Enabled = enabled;
ApplyChanges();
}
}
public void SelectAll()
{
ToggleAll(true);
}
public void DeselectAll()
{
ToggleAll(false);
}
public void SelectAssets()
{
m_AssembliesToInclude = AssemblyFiltering.GetUserOnlyAssembliesString();
SelectFromString(m_AssembliesToInclude);
}
public void SelectPackages()
{
m_AssembliesToInclude = AssemblyFiltering.GetPackagesOnlyAssembliesString();
SelectFromString(m_AssembliesToInclude);
}
private void SelectFromString(string assembliesToInclude)
{
string[] includeAssemblyFilters = assembliesToInclude.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
Regex[] includeAssemblies = includeAssemblyFilters
.Select(f => AssemblyFiltering.CreateFilterRegex(f))
.ToArray();
foreach (var child in rootItem.children)
{
AssembliesTreeViewItem childItem = child as AssembliesTreeViewItem;
bool enabled = includeAssemblies.Any(f => f.IsMatch(childItem.displayName.ToLowerInvariant()));
if (searchString == null)
childItem.Enabled = enabled;
else if (DoesItemMatchSearch(child, searchString))
childItem.Enabled = enabled;
}
ApplyChanges();
}
private void ToggleAll(bool enabled)
{
foreach (var child in rootItem.children)
{
AssembliesTreeViewItem childItem = child as AssembliesTreeViewItem;
if (searchString == null)
childItem.Enabled = enabled;
else if (DoesItemMatchSearch(child, searchString))
childItem.Enabled = enabled;
}
ApplyChanges();
}
void ApplyChanges()
{
CoverageAnalytics.instance.CurrentCoverageEvent.updateAssembliesDialog = true;
StringBuilder sb = new StringBuilder();
foreach(var child in rootItem.children)
{
AssembliesTreeViewItem childItem = child as AssembliesTreeViewItem;
if (childItem.Enabled)
{
if (sb.Length > 0)
sb.Append(",");
sb.Append(childItem.displayName);
}
}
m_Parent.AssembliesToInclude = sb.ToString();
m_Parent.Repaint();
}
}
class AssembliesTreeViewItem : TreeViewItem
{
public bool Enabled { get; set; }
}
}

View File

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

View File

@ -0,0 +1,8 @@
namespace UnityEditor.TestTools.CodeCoverage
{
internal enum PathFilterType
{
Include = 0,
Exclude = 1
}
}

View File

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

View File

@ -0,0 +1,73 @@
using UnityEditor.TestTools.CodeCoverage.Analytics;
using UnityEngine;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class PathToAddDropDownMenu
{
GenericMenu m_Menu;
string m_PathsToFilter;
readonly PathToAddHandler m_AddPathToIncludeHandler;
readonly PathToAddHandler m_AddPathToExcludeHandler;
readonly PathFilterType m_PathFilterType;
static class Styles
{
public static readonly GUIContent AddFolderLabel = EditorGUIUtility.TrTextContent("Add Folder");
public static readonly GUIContent AddFileLabel = EditorGUIUtility.TrTextContent("Add File");
}
public PathToAddDropDownMenu(CodeCoverageWindow parent, PathFilterType type)
{
m_PathFilterType = type;
m_AddPathToIncludeHandler = new PathToAddHandler(parent, PathFilterType.Include);
m_AddPathToExcludeHandler = new PathToAddHandler(parent, PathFilterType.Exclude);
}
private void PopulateMenu()
{
m_Menu = new GenericMenu();
m_Menu.AddItem(Styles.AddFolderLabel, false, () => AddFolder());
m_Menu.AddItem(Styles.AddFileLabel, false, () => AddFile());
}
public void Show(Rect position, string pathsToFilter)
{
m_PathsToFilter = pathsToFilter;
PopulateMenu();
m_Menu.DropDown(position);
}
private void AddFolder()
{
if (m_PathFilterType == PathFilterType.Include)
{
CoverageAnalytics.instance.CurrentCoverageEvent.selectAddFolder_IncludedPaths = true;
m_AddPathToIncludeHandler.BrowseForDir(m_PathsToFilter);
}
else
{
CoverageAnalytics.instance.CurrentCoverageEvent.selectAddFolder_ExcludedPaths = true;
m_AddPathToExcludeHandler.BrowseForDir(m_PathsToFilter);
}
}
private void AddFile()
{
if (m_PathFilterType == PathFilterType.Include)
{
CoverageAnalytics.instance.CurrentCoverageEvent.selectAddFile_IncludedPaths = true;
m_AddPathToIncludeHandler.BrowseForFile(m_PathsToFilter);
}
else
{
CoverageAnalytics.instance.CurrentCoverageEvent.selectAddFile_ExcludedPaths = true;
m_AddPathToExcludeHandler.BrowseForFile(m_PathsToFilter);
}
}
}
}

View File

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

View File

@ -0,0 +1,73 @@
using System.Linq;
using System;
using UnityEngine;
using UnityEditor.TestTools.CodeCoverage.Utils;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class PathToAddHandler
{
string m_PathsToFilter;
readonly CodeCoverageWindow m_Parent;
readonly PathFilterType m_PathFilterType;
private readonly string kSelectIncludedDirectoryMessage = L10n.Tr($"Select directory to include");
private readonly string kSelectIncludedFileMessage = L10n.Tr("Select file to include");
private readonly string kSelectExcludedDirectoryMessage = L10n.Tr($"Select directory to exclude");
private readonly string kSelectExcludedFileMessage = L10n.Tr("Select file to exclude");
public PathToAddHandler(CodeCoverageWindow parent, PathFilterType type)
{
m_Parent = parent;
m_PathFilterType = type;
}
public void BrowseForDir(string pathsToFilter)
{
m_PathsToFilter = pathsToFilter;
string candidate = CoverageUtils.BrowseForDir(Application.dataPath, m_PathFilterType == PathFilterType.Include ? kSelectIncludedDirectoryMessage : kSelectExcludedDirectoryMessage);
if (CoverageUtils.IsValidFolder(candidate))
{
candidate = string.Concat(candidate, "/**");
UpdatePathToFilter(candidate);
}
}
public void BrowseForFile(string pathsToFilter)
{
m_PathsToFilter = pathsToFilter;
string candidate = CoverageUtils.BrowseForFile(Application.dataPath, m_PathFilterType == PathFilterType.Include ? kSelectIncludedFileMessage : kSelectExcludedFileMessage);
if (CoverageUtils.IsValidFile(candidate))
{
UpdatePathToFilter(candidate);
}
}
private void UpdatePathToFilter(string candidate)
{
string[] pathFilters = m_PathsToFilter.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
candidate = CoverageUtils.NormaliseFolderSeparators(candidate);
if (!pathFilters.Contains(candidate))
{
if (m_PathsToFilter.Length > 0)
m_PathsToFilter += ",";
m_PathsToFilter += candidate;
if (m_PathFilterType == PathFilterType.Include)
{
m_Parent.PathsToInclude = m_PathsToFilter;
}
else
{
m_Parent.PathsToExclude = m_PathsToFilter;
}
m_Parent.LoseFocus();
}
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.TestTools.CodeCoverage
{
[Serializable]
internal class CoverageEventDataImplementation
{
[SerializeField]
private SessionMode m_CoverageSessionMode;
[SerializeField]
private List<string> m_CoverageSessionResultPaths;
public void StartSession(SessionMode coverageSessionMode)
{
m_CoverageSessionMode = coverageSessionMode;
m_CoverageSessionResultPaths = new List<string>();
}
public void AddSessionResultPath(string path)
{
if (m_CoverageSessionResultPaths != null)
{
m_CoverageSessionResultPaths.Add(path);
}
}
public SessionMode CoverageSessionMode
{
get { return m_CoverageSessionMode; }
}
public List<string> CoverageSessionResultPaths
{
get { return m_CoverageSessionResultPaths; }
}
}
[Serializable]
internal class CoverageEventData : ScriptableSingleton<CoverageEventData>
{
[SerializeField]
private CoverageEventDataImplementation m_CoverageEventDataImplementation = null;
protected CoverageEventData() : base()
{
m_CoverageEventDataImplementation = new CoverageEventDataImplementation();
}
public void StartSession(SessionMode coverageSessionMode)
{
m_CoverageEventDataImplementation.StartSession(coverageSessionMode);
}
public void AddSessionResultPath(string path)
{
m_CoverageEventDataImplementation.AddSessionResultPath(path);
}
public SessionEventInfo GetCoverageSessionInfo()
{
SessionEventInfo info = new SessionEventInfo(m_CoverageEventDataImplementation.CoverageSessionMode, m_CoverageEventDataImplementation.CoverageSessionResultPaths);
return info;
}
}
}

View File

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

View File

@ -0,0 +1,110 @@
using System;
using UnityEditor.TestTools.CodeCoverage.Analytics;
namespace UnityEditor.TestTools.CodeCoverage
{
/// <summary>
/// Events invoked during a code coverage session. A code coverage session is the period between starting and finishing capturing code coverage data.
/// </summary>
/// <example>
/// In the following example we create event handler methods which subscribe to the <see cref="Events.onCoverageSessionStarted"/>, <see cref="Events.onCoverageSessionFinished"/>, <see cref="Events.onCoverageSessionPaused"/> and <see cref="Events.onCoverageSessionUnpaused"/> events.
/// We use the <see href="https://docs.unity3d.com/ScriptReference/InitializeOnLoadAttribute.html">InitializeOnLoad</see> attribute to make sure that they will resubscribe on Domain Reload, when we enter Play Mode for example.
/// <code>
/// using UnityEngine;
/// using UnityEditor;
/// using UnityEditor.TestTools.CodeCoverage;
///
/// [InitializeOnLoad]
/// public class CoverageSessionListener
/// {
/// static CoverageSessionListener()
/// {
/// Events.onCoverageSessionStarted += OnSessionStarted;
/// Events.onCoverageSessionFinished += OnSessionFinished;
/// Events.onCoverageSessionPaused += OnSessionPaused;
/// Events.onCoverageSessionUnpaused += OnSessionUnpaused;
/// }
///
/// static void OnSessionStarted(SessionEventInfo args)
/// {
/// Debug.Log($"{args.SessionMode} Code Coverage Session Started");
/// }
/// static void OnSessionFinished(SessionEventInfo args)
/// {
/// Debug.Log($"{args.SessionMode} Code Coverage Session Finished");
///
/// string paths = string.Empty;
/// foreach (string path in args.SessionResultPaths)
/// paths = string.Concat(paths, "\n", path);
///
/// Debug.Log($"Code Coverage Results were saved in: {paths}");
/// }
///
/// static void OnSessionPaused(SessionEventInfo args)
/// {
/// Debug.Log($"{args.SessionMode} Code Coverage Session Paused");
/// }
///
/// static void OnSessionUnpaused(SessionEventInfo args)
/// {
/// Debug.Log($"{args.SessionMode} Code Coverage Session Unpaused");
/// }
/// }
/// </code>
/// </example>
public static class Events
{
/// <summary>
/// This event is invoked when a code coverage session is started.
/// </summary>
public static event Action<SessionEventInfo> onCoverageSessionStarted;
/// <summary>
/// This event is invoked when a code coverage session is finished.
/// </summary>
public static event Action<SessionEventInfo> onCoverageSessionFinished;
/// <summary>
/// This event is invoked when a code coverage session is paused.
/// </summary>
public static event Action<SessionEventInfo> onCoverageSessionPaused;
/// <summary>
/// This event is invoked when a code coverage session is unpaused.
/// </summary>
public static event Action<SessionEventInfo> onCoverageSessionUnpaused;
internal static void InvokeOnCoverageSessionStarted()
{
if (onCoverageSessionStarted != null)
{
CoverageAnalytics.instance.CurrentCoverageEvent.useEvent_onCoverageSessionStarted = true;
onCoverageSessionStarted.Invoke(CoverageEventData.instance.GetCoverageSessionInfo());
}
}
internal static void InvokeOnCoverageSessionFinished()
{
if (onCoverageSessionFinished != null)
{
CoverageAnalytics.instance.CurrentCoverageEvent.useEvent_onCoverageSessionFinished = true;
onCoverageSessionFinished.Invoke(CoverageEventData.instance.GetCoverageSessionInfo());
}
}
internal static void InvokeOnCoverageSessionPaused()
{
if (onCoverageSessionPaused != null)
{
CoverageAnalytics.instance.CurrentCoverageEvent.useEvent_onCoverageSessionPaused = true;
onCoverageSessionPaused.Invoke(CoverageEventData.instance.GetCoverageSessionInfo());
}
}
internal static void InvokeOnCoverageSessionUnpaused()
{
if (onCoverageSessionUnpaused != null)
{
CoverageAnalytics.instance.CurrentCoverageEvent.useEvent_onCoverageSessionUnpaused = true;
onCoverageSessionUnpaused.Invoke(CoverageEventData.instance.GetCoverageSessionInfo());
}
}
}
}

View File

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

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace UnityEditor.TestTools.CodeCoverage
{
/// <summary>
/// The code coverage session information retuned by the coverage session <see cref="Events"/>.
/// </summary>
public class SessionEventInfo
{
/// <summary>
/// The code coverage session mode.
/// </summary>
public SessionMode SessionMode { get; internal set; }
/// <summary>
/// The coverage results paths of the files or folders created during the code coverage session.
/// </summary>
public List<string> SessionResultPaths { get; internal set; }
internal SessionEventInfo(SessionMode mode, List<string> resultPaths)
{
SessionMode = mode;
SessionResultPaths = resultPaths;
}
}
}

View File

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

View File

@ -0,0 +1,17 @@
namespace UnityEditor.TestTools.CodeCoverage
{
/// <summary>
/// The code coverage session mode.
/// </summary>
public enum SessionMode
{
/// <summary>
/// Describes a code coverage session triggered by automated testing, using the Test Runner.
/// </summary>
TestRunner = 0,
/// <summary>
/// Describes a code coverage session triggered by Coverage Recording.
/// </summary>
Recording = 1
}
}

View File

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

View File

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

View File

@ -0,0 +1,182 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor.Compilation;
using UnityEditor.TestTools.CodeCoverage.Utils;
using System.Collections.Generic;
using UnityEditor.TestTools.CodeCoverage.Analytics;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class AssemblyFiltering
{
public const string kCoreAssemblies = "unityeditor*,unityengine*";
public const string kAssetsAlias = "<assets>";
public const string kPackagesAlias = "<packages>";
public const string kAllAlias = "<all>";
public const string kCoreAlias = "<core>";
public string includedAssemblies
{
get;
private set;
}
public string excludedAssemblies
{
get;
private set;
}
private Regex[] m_IncludeAssemblies;
private Regex[] m_ExcludeAssemblies;
public AssemblyFiltering()
{
m_IncludeAssemblies = new Regex[] { };
m_ExcludeAssemblies = new Regex[] { };
}
public void Parse(string includeAssemblies, string excludeAssemblies)
{
includedAssemblies = includeAssemblies;
excludedAssemblies = excludeAssemblies;
string[] includeAssemblyFilters = includeAssemblies.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Distinct().ToArray();
string[] excludeAssemblyFilters = excludeAssemblies.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Distinct().ToArray();
CoverageAnalytics.instance.CurrentCoverageEvent.includedAssemblies = includeAssemblyFilters;
CoverageAnalytics.instance.CurrentCoverageEvent.excludedAssemblies = excludeAssemblyFilters;
m_IncludeAssemblies = includeAssemblyFilters
.Select(f => CreateFilterRegex(f))
.ToArray();
m_ExcludeAssemblies = excludeAssemblyFilters
.Select(f => CreateFilterRegex(f))
.ToArray();
}
public bool IsAssemblyIncluded(string name)
{
name = name.ToLowerInvariant();
if (m_ExcludeAssemblies.Any(f => f.IsMatch(name)))
{
return false;
}
else
{
return m_IncludeAssemblies.Any(f => f.IsMatch(name));
}
}
public static System.Reflection.Assembly[] GetAllProjectAssembliesInternal()
{
System.Reflection.Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
Array.Sort(assemblies, (x, y) => String.Compare(x.GetName().Name, y.GetName().Name));
string allAssemblyFiltersString = GetAllProjectAssembliesString() + ",unityeditor*,unityengine*,unity.*";
string[] allAssemblyFilters = allAssemblyFiltersString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
Regex[] assembliesRegex = allAssemblyFilters
.Select(f => CreateFilterRegex(f))
.ToArray();
System.Reflection.Assembly[] filteredAssemblies = assemblies.Where(assembly => assembliesRegex.Any(regex => regex.IsMatch(assembly.GetName().Name.ToLowerInvariant()))).ToArray();
return filteredAssemblies;
}
public static Assembly[] GetAllProjectAssemblies()
{
Assembly[] assemblies = CompilationPipeline.GetAssemblies();
Array.Sort(assemblies, (x, y) => String.Compare(x.name, y.name));
return assemblies;
}
public static string GetAllProjectAssembliesString()
{
Assembly[] assemblies = GetAllProjectAssemblies();
string assembliesString = "";
int assembliesLength = assemblies.Length;
for (int i=0; i<assembliesLength; ++i)
{
assembliesString += assemblies[i].name;
if (i < assembliesLength - 1)
assembliesString += ",";
}
return assembliesString;
}
public static string GetUserOnlyAssembliesString()
{
return GetStartsWithAssembliesString("Assets");
}
public static string GetPackagesOnlyAssembliesString()
{
return GetStartsWithAssembliesString("Packages");
}
private static string GetStartsWithAssembliesString(string startsWithStr)
{
Assembly[] assemblies = GetAllProjectAssemblies();
List<string> foundAssemblies = new List<string>();
string assembliesString = "";
int assembliesLength = assemblies.Length;
int i;
for (i = 0; i < assembliesLength; ++i)
{
string name = assemblies[i].name;
string[] sourceFiles = assemblies[i].sourceFiles;
if (sourceFiles.Length > 0 &&
sourceFiles[0].StartsWith(startsWithStr, StringComparison.InvariantCultureIgnoreCase))
{
foundAssemblies.Add(name);
}
}
int foundAssembliesLength = foundAssemblies.Count;
for (i = 0; i < foundAssembliesLength; ++i)
{
assembliesString += foundAssemblies[i];
if (i < foundAssembliesLength - 1)
assembliesString += ",";
}
return assembliesString;
}
public static Regex CreateFilterRegex(string filter)
{
filter = filter.ToLowerInvariant();
return new Regex(CoverageUtils.GlobToRegex(filter), RegexOptions.Compiled);
}
public static string RemoveAssembliesThatNoLongerExist(string assembliesString)
{
IEnumerable<string> currentAssemblies;
bool developerMode = EditorPrefs.GetBool("DeveloperMode", false);
if (developerMode)
{
currentAssemblies = GetAllProjectAssembliesInternal().Select(x => x.GetName().Name);
}
else
{
currentAssemblies = GetAllProjectAssemblies().Select(x => x.name);
}
string[] assemblyNames = assembliesString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
IEnumerable<string> filteredAssemblyNames = assemblyNames.Where(x => currentAssemblies.Contains(x));
return string.Join(",", filteredAssemblyNames);
}
}
}

View File

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

View File

@ -0,0 +1,11 @@
namespace UnityEditor.TestTools.CodeCoverage
{
[System.Serializable]
internal class JsonFile
{
public string[] assembliesInclude;
public string[] assembliesExclude;
public string[] pathsInclude;
public string[] pathsExclude;
}
}

View File

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

View File

@ -0,0 +1,86 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor.TestTools.CodeCoverage.Analytics;
using UnityEditor.TestTools.CodeCoverage.Utils;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class PathFiltering
{
public string includedPaths
{
get;
private set;
}
public string excludedPaths
{
get;
private set;
}
private Regex[] m_IncludePaths;
private Regex[] m_ExcludePaths;
private bool m_HasIncludePaths;
private bool m_HasExcludePaths;
public PathFiltering()
{
m_IncludePaths = new Regex[] { };
m_ExcludePaths = new Regex[] { };
}
public void Parse(string includePaths, string excludePaths)
{
includedPaths = includePaths;
excludedPaths = excludePaths;
string[] includePathFilters = includePaths.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Distinct().ToArray();
string[] excludePathFilters = excludePaths.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Distinct().ToArray();
m_IncludePaths = includePathFilters
.Where(f => f != "-")
.Select(f => CreateFilterRegex(f))
.ToArray();
m_ExcludePaths = excludePathFilters
.Where(f => f != "-")
.Select(f => CreateFilterRegex(f))
.ToArray();
CoverageAnalytics.instance.CurrentCoverageEvent.numOfIncludedPaths = m_IncludePaths.Length;
CoverageAnalytics.instance.CurrentCoverageEvent.numOfExcludedPaths = m_ExcludePaths.Length;
m_HasIncludePaths = m_IncludePaths.Length > 0;
m_HasExcludePaths = m_ExcludePaths.Length > 0;
}
public bool IsPathIncluded(string name)
{
if (!m_HasIncludePaths && !m_HasExcludePaths)
return true;
name = name.ToLowerInvariant();
name = CoverageUtils.NormaliseFolderSeparators(name, true);
if (m_ExcludePaths.Any(f => f.IsMatch(name)))
{
return false;
}
else
{
return !m_HasIncludePaths || m_IncludePaths.Any(f => f.IsMatch(name));
}
}
Regex CreateFilterRegex(string filter)
{
filter = filter.ToLowerInvariant();
filter = CoverageUtils.NormaliseFolderSeparators(filter, true);
return new Regex(CoverageUtils.GlobToRegex(filter), RegexOptions.Compiled);
}
}
}

View File

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

View File

@ -0,0 +1,16 @@
using UnityEditor.TestTools.TestRunner.Api;
namespace UnityEditor.TestTools.CodeCoverage
{
interface ICoverageReporter
{
ICoverageReporterFilter GetReporterFilter();
void OnInitialise(CoverageSettings settings);
void OnRunStarted(ITestAdaptor testsToRun);
void OnRunFinished(ITestResultAdaptor testResults);
void OnTestStarted(ITestAdaptor test);
void OnTestFinished(ITestResultAdaptor result);
void OnBeforeAssemblyReload();
void OnCoverageRecordingPaused();
}
}

View File

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

View File

@ -0,0 +1,13 @@
namespace UnityEditor.TestTools.CodeCoverage
{
interface ICoverageReporterFilter
{
void SetupFiltering();
AssemblyFiltering GetAssemblyFiltering();
bool ShouldProcessAssembly(string assemblyName);
PathFiltering GetPathFiltering();
bool ShouldProcessFile(string filename);
bool ShouldGenerateAdditionalMetrics();
bool ShouldGenerateTestReferences();
}
}

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

View File

@ -0,0 +1,96 @@
fileFormatVersion: 2
guid: 5e4d1277a09266944b3431a0a44d0862
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

View File

@ -0,0 +1,96 @@
fileFormatVersion: 2
guid: 37164f8d49df70f448b593fa86e488f5
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using UnityEngine;
namespace UnityEditor.TestTools.CodeCoverage
{
internal static class EditorIcons
{
public static Texture2D FolderOpened { get; private set; }
public static Texture2D CoverageWindow { get; private set; }
static EditorIcons()
{
FolderOpened = GetTexture("FolderOpened.png");
CoverageWindow = GetTexture("CodeCoverage.png");
}
static Texture2D GetTexture(string path) => EditorGUIUtility.FindTexture("Packages/com.unity.testtools.codecoverage/Editor/Icons/" + path);
}
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

View File

@ -0,0 +1,98 @@
fileFormatVersion: 2
guid: 1935829ae7024da448814ef05b31e6c4
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

View File

@ -0,0 +1,98 @@
fileFormatVersion: 2
guid: dd4bd79e2c603964e8cbf04d0851a210
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

View File

@ -0,0 +1,96 @@
fileFormatVersion: 2
guid: 48e45b7f100ee4b48a02e05135e6cece
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

View File

@ -0,0 +1,96 @@
fileFormatVersion: 2
guid: 8616c9d86a8504a4b8b5c65bb6a5644f
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

View File

@ -0,0 +1,98 @@
fileFormatVersion: 2
guid: 663a080499383ea4e9297061febf5ef7
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

View File

@ -0,0 +1,98 @@
fileFormatVersion: 2
guid: 327ac160fa4588b4ab06124fe53fdbd1
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,29 @@
namespace UnityEditor.TestTools.CodeCoverage
{
/// <summary>
/// The verbosity level used in editor and console logs.
/// </summary>
public enum LogVerbosityLevel
{
/// <summary>
/// All logs will be printed in Verbose.
/// </summary>
Verbose = 0,
/// <summary>
/// Logs, Warnings and Errors will be printed in Info.
/// </summary>
Info = 1,
/// <summary>
/// Warnings and Errors will be printed in Warning.
/// </summary>
Warning = 2,
/// <summary>
/// Only Errors will be printed in Error.
/// </summary>
Error = 3,
/// <summary>
/// No logs will be printed in Off.
/// </summary>
Off = 4
}
}

View File

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

View File

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using UnityEditor.TestTools.CodeCoverage.Analytics;
using UnityEngine;
namespace UnityEditor.TestTools.CodeCoverage.Utils
{
struct ResultData
{
// The type of the result (log, warning, error, assert)
public LogType type;
// Result message
public string message;
public ResultData(LogType type, string message)
{
this.type = type;
this.message = message;
}
}
internal enum ResultID
{
Log_ResultsSaved = 0,
Log_ReportSaved = 1,
Error_FailedReport = 2,
Error_FailedReportNoCoverageResults = 3,
Error_FailedReportNoAssemblies = 4,
Assert_NullAssemblyTypes = 5,
Warning_DebugCodeOptimization = 6,
Warning_AssemblyFiltersNotPrefixed = 7,
Warning_PathFiltersNotPrefixed = 8,
Warning_MultipleResultsPaths = 9,
Warning_MultipleHistoryPaths = 10,
Warning_NoCoverageResultsSaved = 11,
Warning_FailedToDeleteDir = 12,
Warning_FailedToDeleteFile = 13,
Warning_FailedReportNullCoverageSettings = 14,
Warning_BurstCompilationEnabled = 15,
Warning_ExcludeAttributeAssembly = 16,
Warning_ExcludeAttributeClass = 17,
Warning_ExcludeAttributeMethod = 18,
Warning_StandaloneUnsupported = 19,
Warning_UseProjectSettingsNonBatchmode = 20,
Warning_FailedToExtractPathFiltersFromFile = 21,
Warning_FailedReportNullCoverageFilters = 22,
Log_VisitedResultsSaved = 23,
Warning_NoVisitedCoverageResultsSaved = 24,
Warning_NoVisitedCoverageResultsSavedRecordingPaused = 25,
Warning_UnknownCoverageOptionProvided = 26,
Warning_FailedToExtractFiltersFromFile = 27,
Warning_FilterFileContainsDuplicateKeys = 28,
Warning_FilterFileContainsInvalidKey = 29,
Warning_PathFiltersFromFileDeprecation = 30,
}
internal static class ResultsLogger
{
internal static LogVerbosityLevel VerbosityLevel = LogVerbosityLevel.Info;
static Dictionary<ResultID, ResultData> s_Results = new Dictionary<ResultID, ResultData>()
{
{ ResultID.Log_ResultsSaved, new ResultData(LogType.Log, "Code Coverage results for all sequence points were saved in {0}") },
{ ResultID.Log_VisitedResultsSaved, new ResultData(LogType.Log, "Code Coverage results for visited sequence points were saved in {0}") },
{ ResultID.Log_ReportSaved, new ResultData(LogType.Log, "Code Coverage Report was generated in {0}\nIncluded Assemblies: {1}\nExcluded Assemblies: {2}\nIncluded Paths: {3}\nExcluded Paths: {4}") },
{ ResultID.Error_FailedReport, new ResultData(LogType.Error, "Failed to generate Code Coverage Report.\nIncluded Assemblies: {0}\nExcluded Assemblies: {1}\nIncluded Paths: {2}\nExcluded Paths: {3}") },
{ ResultID.Error_FailedReportNoCoverageResults, new ResultData(LogType.Error, "Failed to generate Code Coverage Report. No code coverage results found.\nIncluded Assemblies: {0}\nExcluded Assemblies: {1}\nIncluded Paths: {2}\nExcluded Paths: {3}") },
{ ResultID.Error_FailedReportNoAssemblies, new ResultData(LogType.Error, "Failed to generate Code Coverage Report. Make sure you have included at least one assembly before generating a report.\nIncluded Assemblies: {0}\nExcluded Assemblies: {1}\nIncluded Paths: {2}\nExcluded Paths: {3}") },
{ ResultID.Assert_NullAssemblyTypes, new ResultData(LogType.Assert, "assemblyTypes cannot be null") },
{ ResultID.Warning_DebugCodeOptimization, new ResultData(LogType.Warning, "Code Coverage requires Code Optimization to be set to debug mode in order to obtain accurate coverage information. Switch to debug mode in the Editor (bottom right corner, select the Bug icon > Switch to debug mode), using the CompilationPipeline api by setting 'CompilationPipeline.codeOptimization = CodeOptimization.Debug' or by passing '-debugCodeOptimization' to the command line in batchmode.") },
{ ResultID.Warning_AssemblyFiltersNotPrefixed, new ResultData(LogType.Warning, "'-coverageOptions assemblyFilters' argument {0} would not be applied as it is not prefixed with +/-.") },
{ ResultID.Warning_PathFiltersNotPrefixed, new ResultData(LogType.Warning, "'-coverageOptions pathFilters' argument {0} would not be applied as it is not prefixed with +/-.") },
{ ResultID.Warning_MultipleResultsPaths, new ResultData(LogType.Warning, "'-coverageResultsPath' has already been specified on the command-line. Keeping the original setting: '{0}'.") },
{ ResultID.Warning_MultipleHistoryPaths, new ResultData(LogType.Warning, "'-coverageHistoryPath' has already been specified on the command-line. Keeping the original setting: '{0}'.") },
{ ResultID.Warning_NoCoverageResultsSaved, new ResultData(LogType.Warning, "Code coverage results for all sequence points were not saved.\nIncluded Assemblies: {0}\nExcluded Assemblies: {1}\nIncluded Paths: {2}\nExcluded Paths: {3}") },
{ ResultID.Warning_NoVisitedCoverageResultsSaved, new ResultData(LogType.Warning, "Code coverage results were not saved. Visited sequence points not found. \nIncluded Assemblies: {0}\nExcluded Assemblies: {1}\nIncluded Paths: {2}\nExcluded Paths: {3}") },
{ ResultID.Warning_NoVisitedCoverageResultsSavedRecordingPaused, new ResultData(LogType.Warning, "Code coverage results were not saved because coverage recording was paused. \nIncluded Assemblies: {0}\nExcluded Assemblies: {1}\nIncluded Paths: {2}\nExcluded Paths: {3}") },
{ ResultID.Warning_FailedToDeleteDir, new ResultData(LogType.Warning, "Failed to delete directory: {0}") },
{ ResultID.Warning_FailedToDeleteFile, new ResultData(LogType.Warning, "Failed to delete file: {0}") },
{ ResultID.Warning_FailedReportNullCoverageSettings, new ResultData(LogType.Warning, "Failed to generate Code Coverage Report. CoverageSettings was not set.") },
{ ResultID.Warning_FailedReportNullCoverageFilters, new ResultData(LogType.Warning, "Failed to generate Code Coverage Report. Error parsing Coverage filtering.") },
{ ResultID.Warning_BurstCompilationEnabled, new ResultData(LogType.Warning, "Code Coverage requires Burst Compilation to be disabled in order to obtain accurate coverage information. To disable Burst Compilation uncheck Jobs > Burst > Enable Compilation or pass '--burst-disable-compilation' to the command line in batchmode.") },
{ ResultID.Warning_ExcludeAttributeAssembly, new ResultData(LogType.Warning, "Not able to detect custom attribute ExcludeFromCoverage in Assembly: {0}") },
{ ResultID.Warning_ExcludeAttributeClass, new ResultData(LogType.Warning, "Not able to detect custom attribute ExcludeFromCoverage in Class: {0}, Assembly: {1}") },
{ ResultID.Warning_ExcludeAttributeMethod, new ResultData(LogType.Warning, "Not able to detect custom attribute ExcludeFromCoverage in Method: {0}, Class: {1}, Assembly: {2}") },
{ ResultID.Warning_StandaloneUnsupported, new ResultData(LogType.Warning, "Code Coverage is not supported in standalone currently. Code Coverage Results and Report will not be generated.") },
{ ResultID.Warning_UseProjectSettingsNonBatchmode, new ResultData(LogType.Warning, "'-coverageOptions useProjectSettings' can only be used in batchmode and it does not take effect when running the editor from the command-line in non-batchmode.") },
{ ResultID.Warning_FailedToExtractPathFiltersFromFile, new ResultData(LogType.Warning, "Failed to extract path filters from file: Exception '{0}' while reading '{1}'") },
{ ResultID.Warning_UnknownCoverageOptionProvided, new ResultData(LogType.Warning, "Unknown coverage option provided: '{0}'") },
{ ResultID.Warning_FailedToExtractFiltersFromFile, new ResultData(LogType.Warning, "Failed to extract filters from file: Exception '{0}' while reading '{1}'") },
{ ResultID.Warning_FilterFileContainsDuplicateKeys, new ResultData(LogType.Warning, "Duplicate keys exist in the json file used for filtering: {0}") },
{ ResultID.Warning_FilterFileContainsInvalidKey, new ResultData(LogType.Warning, "Invalid key(s) in the json file used for filtering: '{0}'") },
{ ResultID.Warning_PathFiltersFromFileDeprecation, new ResultData(LogType.Warning, "The 'pathFiltersFromFile' option will be deprecated in the next package major release. Please use the 'filtersFromFile' option instead.") },
};
public static bool Log(ResultID resultId, params string[] extraParams)
{
if (!s_Results.ContainsKey(resultId))
{
Debug.LogWarning($"[{CoverageSettings.PackageName}] ResultsLogger could not find result with id: {resultId}");
return false;
}
ResultData result = s_Results[resultId];
string message = string.Concat(
$"[{CoverageSettings.PackageName}] ",
extraParams.Length > 0 ? string.Format(result.message, extraParams) : result.message);
switch (result.type)
{
case LogType.Warning:
if (VerbosityLevel <= LogVerbosityLevel.Warning)
Debug.LogWarning(message);
CoverageAnalytics.instance.AddResult(resultId);
break;
case LogType.Error:
if (VerbosityLevel <= LogVerbosityLevel.Error)
Debug.LogError(message);
CoverageAnalytics.instance.AddResult(resultId);
CoverageAnalytics.instance.SendCoverageEvent(false);
break;
case LogType.Assert:
CoverageAnalytics.instance.AddResult(resultId);
CoverageAnalytics.instance.SendCoverageEvent(false);
Debug.Assert(false, message);
break;
case LogType.Log:
default:
if (VerbosityLevel <= LogVerbosityLevel.Info)
Debug.Log(message);
CoverageAnalytics.instance.AddResult(resultId);
break;
}
return true;
}
public static bool LogSessionItem(string message, LogVerbosityLevel logLevel = LogVerbosityLevel.Info)
{
if (string.IsNullOrEmpty(message))
return false;
message = message.Replace('{', '[').Replace('}', ']');
if (logLevel >= VerbosityLevel)
{
message = string.Format($"[{CoverageSettings.PackageName}] {message}");
Console.WriteLine(message);
return true;
}
return false;
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEditor.TestTools.CodeCoverage.Utils;
using UnityEngine;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class PathReplacing
{
private readonly Dictionary<Regex, string> m_PathReplacePatterns;
private bool m_HasPathReplacePatterns;
public PathReplacing()
{
m_PathReplacePatterns = new Dictionary<Regex, string>();
}
public void Parse(string replacePatterns)
{
string[] replacePatternsArray = replacePatterns.Split(',');
// There should be at least one pair
m_HasPathReplacePatterns = replacePatternsArray.Length > 1;
if (m_HasPathReplacePatterns)
{
// If an odd number of elements is passed trim the last element
int evenArrayLength = replacePatternsArray.Length % 2 == 0 ? replacePatternsArray.Length : replacePatternsArray.Length - 1;
for (int i = 0; i < evenArrayLength; i = i + 2)
{
m_PathReplacePatterns.Add(CreateFilterRegex(replacePatternsArray[i]), replacePatternsArray[i + 1]);
}
}
}
public string ReplacePath(string path)
{
if (m_HasPathReplacePatterns)
{
string newPath = CoverageUtils.NormaliseFolderSeparators(path);
foreach (Regex replacePattern in m_PathReplacePatterns.Keys)
{
Match match = replacePattern.Match(newPath);
if (match.Success)
path = path.Replace(match.Value, m_PathReplacePatterns[replacePattern]);
}
}
return path;
}
Regex CreateFilterRegex(string filter)
{
filter = CoverageUtils.NormaliseFolderSeparators(filter);
return new Regex(CoverageUtils.GlobToRegex(filter, false), RegexOptions.Compiled | RegexOptions.IgnoreCase);
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,300 @@
using System;
using System.IO;
using System.Text;
using Palmmedia.ReportGenerator.Core;
using Palmmedia.ReportGenerator.Core.Logging;
using UnityEditor.TestTools.CodeCoverage.Utils;
using ILogger = Palmmedia.ReportGenerator.Core.Logging.ILogger;
using Palmmedia.ReportGenerator.Core.CodeAnalysis;
using UnityEditor.TestTools.CodeCoverage.Analytics;
namespace UnityEditor.TestTools.CodeCoverage
{
internal class CoverageReportGenerator
{
public void Generate(CoverageSettings coverageSettings)
{
CoverageReporterManager coverageReporterManager = CoverageReporterStarter.CoverageReporterManager;
AssemblyFiltering assemblyFiltering = null;
PathFiltering pathFiltering = null;
if (coverageReporterManager != null)
{
coverageReporterManager.CoverageReporter.GetReporterFilter().SetupFiltering();
assemblyFiltering = coverageReporterManager.CoverageReporter.GetReporterFilter().GetAssemblyFiltering();
pathFiltering = coverageReporterManager.CoverageReporter.GetReporterFilter().GetPathFiltering();
}
if (assemblyFiltering == null || pathFiltering == null)
{
ResultsLogger.Log(ResultID.Warning_FailedReportNullCoverageFilters);
}
CoverageRunData.instance.ReportGenerationStart();
if (coverageSettings == null)
{
EditorUtility.ClearProgressBar();
ResultsLogger.Log(ResultID.Warning_FailedReportNullCoverageSettings);
CoverageRunData.instance.ReportGenerationEnd(false);
return;
}
string includeAssemblies = assemblyFiltering != null ? assemblyFiltering.includedAssemblies : string.Empty;
// If override for include assemblies is set in coverageSettings, use overrideIncludeAssemblies instead
if (!String.IsNullOrEmpty(coverageSettings.overrideIncludeAssemblies))
includeAssemblies = coverageSettings.overrideIncludeAssemblies;
string[] assemblyFilters = includeAssemblies.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (assemblyFilters.Length == 0)
{
EditorUtility.ClearProgressBar();
ResultsLogger.Log(ResultID.Error_FailedReportNoAssemblies, CoverageUtils.GetFilteringLogParams(assemblyFiltering, pathFiltering));
CoverageRunData.instance.ReportGenerationEnd(false);
return;
}
for (int i = 0; i < assemblyFilters.Length; i++)
{
assemblyFilters[i] = "+" + assemblyFilters[i];
}
string rootFolderPath = coverageSettings.rootFolderPath;
if (CoverageUtils.GetNumberOfFilesInFolder(rootFolderPath, "*.xml", SearchOption.AllDirectories) == 0)
{
EditorUtility.ClearProgressBar();
ResultsLogger.Log(ResultID.Error_FailedReportNoCoverageResults, CoverageUtils.GetFilteringLogParams(assemblyFiltering, pathFiltering));
CoverageRunData.instance.ReportGenerationEnd(false);
return;
}
// Only include xml files with the correct filename format
string sourceXmlPath = CoverageUtils.JoinPaths(rootFolderPath, "**");
string testResultsXmlPath = CoverageUtils.JoinPaths(sourceXmlPath, "TestCoverageResults_????.xml");
string recordingResultsXmlPath = CoverageUtils.JoinPaths(sourceXmlPath, "RecordingCoverageResults_????.xml");
string rootFullEmptyResultsXmlPath = CoverageUtils.JoinPaths(rootFolderPath, "TestCoverageResults_fullEmpty.xml");
string[] reportFilePatterns = new string[] { testResultsXmlPath, recordingResultsXmlPath, rootFullEmptyResultsXmlPath };
bool includeHistoryInReport = CommandLineManager.instance.batchmode && !CommandLineManager.instance.useProjectSettings ?
CommandLineManager.instance.generateHTMLReportHistory :
CommandLineManager.instance.generateHTMLReportHistory || CoveragePreferences.instance.GetBool("IncludeHistoryInReport", true);
string historyDirectory = includeHistoryInReport ? coverageSettings.historyFolderPath : null;
string targetDirectory = CoverageUtils.JoinPaths(rootFolderPath, CoverageSettings.ReportFolderName);
string[] sourceDirectories = CommandLineManager.instance.sourcePathsSpecified ? CommandLineManager.instance.sourcePaths.Split(',') : new string[] { };
bool generateHTMLReport = CommandLineManager.instance.batchmode && !CommandLineManager.instance.useProjectSettings ?
CommandLineManager.instance.generateHTMLReport :
CommandLineManager.instance.generateHTMLReport || CoveragePreferences.instance.GetBool("GenerateHTMLReport", true);
if (coverageSettings.overrideGenerateHTMLReport)
generateHTMLReport = true;
bool generateBadge = CommandLineManager.instance.batchmode && !CommandLineManager.instance.useProjectSettings ?
CommandLineManager.instance.generateBadgeReport :
CommandLineManager.instance.generateBadgeReport || CoveragePreferences.instance.GetBool("GenerateBadge", true);
bool generateAdditionalReports = CommandLineManager.instance.batchmode && !CommandLineManager.instance.useProjectSettings ?
CommandLineManager.instance.generateAdditionalReports :
CommandLineManager.instance.generateAdditionalReports || CoveragePreferences.instance.GetBool("GenerateAdditionalReports", false);
string reportTypesString = "xmlSummary,MarkdownSummary,JsonSummary,";
if (generateHTMLReport)
reportTypesString += "Html,";
if (generateBadge)
reportTypesString += "Badges,";
if (generateAdditionalReports)
reportTypesString += "SonarQube,lcov,Cobertura,";
string[] reportTypes = reportTypesString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
string[] plugins = new string[] { };
bool includeAdditionalMetrics = coverageReporterManager != null &&
coverageReporterManager.CoverageReporter.GetReporterFilter().ShouldGenerateAdditionalMetrics();
string[] classFilters = new string[] { };
string[] fileFilters = new string[] { };
string verbosityLevel = null;
string tag = null;
ReportConfiguration config = new ReportConfiguration(
reportFilePatterns,
targetDirectory,
sourceDirectories,
historyDirectory,
reportTypes,
plugins,
assemblyFilters,
classFilters,
fileFilters,
verbosityLevel,
tag);
DebugFactory loggerFactory = new DebugFactory();
LoggerFactory.Configure(loggerFactory);
try
{
if (!CommandLineManager.instance.batchmode)
EditorUtility.DisplayProgressBar(ReportGeneratorStyles.ProgressTitle.text, ReportGeneratorStyles.ProgressInfoCreating.text, 0f);
if (Directory.Exists(targetDirectory))
Directory.Delete(targetDirectory, true);
Generator generator = new Generator();
ResultsLogger.LogSessionItem("Initializing report generation..", LogVerbosityLevel.Info);
if (generator.GenerateReport(config, new Settings() { DisableRiskHotspots = !includeAdditionalMetrics }, new RiskHotspotsAnalysisThresholds()))
{
ResultsLogger.Log(ResultID.Log_ReportSaved, CoverageUtils.GetFilteringLogParams(assemblyFiltering, pathFiltering, new string[] { targetDirectory }));
CoverageRunData.instance.ReportGenerationEnd(true);
// Send Analytics event (Report Only / Data & Report)
CoverageAnalytics.instance.SendCoverageEvent(true);
if (!CommandLineManager.instance.batchmode &&
coverageSettings.revealReportInFinder &&
CoveragePreferences.instance.GetBool("OpenReportWhenGenerated", true))
{
string indexHtm = CoverageUtils.JoinPaths(targetDirectory, "index.htm");
if (File.Exists(indexHtm))
EditorUtility.RevealInFinder(indexHtm);
else
EditorUtility.RevealInFinder(targetDirectory);
}
}
else
{
ResultsLogger.Log(ResultID.Error_FailedReport, CoverageUtils.GetFilteringLogParams(assemblyFiltering, pathFiltering));
CoverageRunData.instance.ReportGenerationEnd(false);
}
}
finally
{
EditorUtility.ClearProgressBar();
}
}
}
class DebugFactory : ILoggerFactory
{
public VerbosityLevel VerbosityLevel { get; set; }
public DebugLogger Logger { get; set; }
public DebugFactory()
{
Logger = new DebugLogger();
}
public ILogger GetLogger(Type type)
{
return Logger;
}
}
class DebugLogger : ILogger
{
public VerbosityLevel VerbosityLevel { get; set; }
readonly StringBuilder m_StringBuilder = new StringBuilder();
public void Debug(string message)
{
m_StringBuilder.AppendLine(message);
}
public void DebugFormat(string format, params object[] args)
{
string message = string.Format(format, args);
m_StringBuilder.AppendLine(message);
if (string.Equals(format, "Finished parsing \'{0}\' {1}/{2}") ||
string.Equals(format, "Parsing of {0} files completed") ||
string.Equals(format, "Coverage report parsing took {0:f1} seconds") ||
string.Equals(format, "Initializing report builders for report types: {0}") ||
string.Equals(format, "Analyzing {0} classes") ||
string.Equals(format, "Report generation took {0:f1} seconds") )
{
ResultsLogger.LogSessionItem(message, LogVerbosityLevel.Info);
}
else
{
ResultsLogger.LogSessionItem(message, LogVerbosityLevel.Verbose);
}
if (!CommandLineManager.instance.batchmode)
{
if (string.Equals(format, "Creating report {0}/{1} (Assembly: {2}, Class: {3})"))
{
if (args.Length >= 2)
{
if (float.TryParse(string.Format("{0}", args[0]), out float currentNum) &&
float.TryParse(string.Format("{0}", args[1]), out float totalNum) &&
currentNum <= totalNum &&
currentNum > 0 &&
totalNum > 0)
{
float progress = (currentNum + 1) / totalNum;
EditorUtility.DisplayProgressBar(ReportGeneratorStyles.ProgressTitle.text, ReportGeneratorStyles.ProgressInfoCreating.text, progress);
}
}
}
}
}
public void Error(string message)
{
m_StringBuilder.AppendLine(message);
}
public void ErrorFormat(string format, params object[] args)
{
string message = string.Format(format, args);
m_StringBuilder.AppendLine(message);
ResultsLogger.LogSessionItem(message, LogVerbosityLevel.Error);
}
public void Info(string message)
{
m_StringBuilder.AppendLine(message);
}
public void InfoFormat(string format, params object[] args)
{
string message = string.Format(format, args);
m_StringBuilder.AppendLine(message);
if (string.Equals(format, "Writing report file \'{0}\'"))
{
ResultsLogger.LogSessionItem(message, LogVerbosityLevel.Verbose);
}
else
{
ResultsLogger.LogSessionItem(message, LogVerbosityLevel.Info);
}
}
public void Warn(string message)
{
m_StringBuilder.AppendLine(message);
}
public void WarnFormat(string format, params object[] args)
{
string message = string.Format(format, args);
m_StringBuilder.AppendLine(message);
ResultsLogger.LogSessionItem(message, LogVerbosityLevel.Verbose);
}
public override string ToString()
{
return m_StringBuilder.ToString();
}
}
}

View File

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

View File

@ -0,0 +1,10 @@
using UnityEngine;
namespace UnityEditor.TestTools.CodeCoverage
{
internal static class ReportGeneratorStyles
{
public static readonly GUIContent ProgressTitle = EditorGUIUtility.TrTextContent("Code Coverage");
public static readonly GUIContent ProgressInfoCreating = EditorGUIUtility.TrTextContent("Generating the report..");
}
}

View File

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

View File

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

View File

@ -0,0 +1,379 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor.Networking.PlayerConnection;
using UnityEngine;
using UnityEngine.TestTools;
namespace UnityEditor.TestTools.CodeCoverage.Utils
{
internal static class CoverageUtils
{
public static bool IsConnectedToPlayer
{
get
{
return EditorConnection.instance.ConnectedPlayers.Count > 0;
}
}
public static string NormaliseFolderSeparators(string folderPath, bool stripTrailingSlash = false)
{
if (folderPath != null)
{
folderPath = folderPath.Replace('\\', '/');
if (stripTrailingSlash)
{
folderPath = folderPath.TrimEnd('/');
}
}
return folderPath;
}
public static bool EnsureFolderExists(string folderPath)
{
if (string.IsNullOrEmpty(folderPath))
return false;
if (!Directory.Exists(folderPath))
{
try
{
Directory.CreateDirectory(folderPath);
}
catch (Exception)
{
return false;
}
}
return true;
}
public static string GetProjectFolderName()
{
string[] projectPathArray = GetProjectPath().Split('/');
Debug.Assert(projectPathArray.Length > 0);
string folderName = projectPathArray[projectPathArray.Length - 1];
char[] invalidChars = Path.GetInvalidPathChars();
StringBuilder folderNameStringBuilder = new StringBuilder();
foreach (char c in folderName)
{
if (invalidChars.Contains(c))
{
folderNameStringBuilder.Append('_');
}
else
{
folderNameStringBuilder.Append(c);
}
}
return folderNameStringBuilder.ToString();
}
public static string StripAssetsFolderIfExists(string folderPath)
{
if (folderPath != null)
{
string toTrim = "Assets";
folderPath = folderPath.TrimEnd(toTrim.ToCharArray());
}
return folderPath;
}
public static string GetProjectPath()
{
return NormaliseFolderSeparators(StripAssetsFolderIfExists(Application.dataPath), true);
}
public static string GetRootFolderPath(CoverageSettings coverageSettings)
{
string rootFolderPath = string.Empty;
string coverageFolderPath = string.Empty;
if (CommandLineManager.instance.batchmode && !CommandLineManager.instance.useProjectSettings)
{
if (coverageSettings.resultsPathFromCommandLine.Length > 0)
{
coverageFolderPath = coverageSettings.resultsPathFromCommandLine;
EnsureFolderExists(coverageFolderPath);
}
}
else
{
if (CommandLineManager.instance.runFromCommandLine && coverageSettings.resultsPathFromCommandLine.Length > 0)
{
coverageFolderPath = coverageSettings.resultsPathFromCommandLine;
EnsureFolderExists(coverageFolderPath);
}
else
{
coverageFolderPath = CoveragePreferences.instance.GetStringForPaths("Path", string.Empty);
}
}
string projectPath = GetProjectPath();
if (EnsureFolderExists(coverageFolderPath))
{
coverageFolderPath = NormaliseFolderSeparators(coverageFolderPath, true);
// Add 'CodeCoverage' directory if coverageFolderPath is projectPath
if (string.Equals(coverageFolderPath, projectPath, StringComparison.InvariantCultureIgnoreCase))
rootFolderPath = JoinPaths(coverageFolderPath, coverageSettings.rootFolderName);
// else user coverageFolderPath as the root folder
else
rootFolderPath = coverageFolderPath;
}
else
{
// Add 'CodeCoverage' directory to projectPath if coverageFolderPath is not valid
rootFolderPath = JoinPaths(projectPath, coverageSettings.rootFolderName);
}
return rootFolderPath;
}
public static string GetHistoryFolderPath(CoverageSettings coverageSettings)
{
string historyFolderPath = string.Empty;
string rootFolderPath = coverageSettings.rootFolderPath;
if (CommandLineManager.instance.batchmode && !CommandLineManager.instance.useProjectSettings)
{
if (coverageSettings.historyPathFromCommandLine.Length > 0)
{
historyFolderPath = coverageSettings.historyPathFromCommandLine;
EnsureFolderExists(historyFolderPath);
}
}
else
{
if (CommandLineManager.instance.runFromCommandLine && coverageSettings.historyPathFromCommandLine.Length > 0)
{
historyFolderPath = coverageSettings.historyPathFromCommandLine;
EnsureFolderExists(historyFolderPath);
}
else
{
historyFolderPath = CoveragePreferences.instance.GetStringForPaths("HistoryPath", string.Empty);
}
}
bool addHistorySubDir = false;
string projectPath = GetProjectPath();
if (EnsureFolderExists(historyFolderPath))
{
historyFolderPath = NormaliseFolderSeparators(historyFolderPath, true);
// If historyFolderPath == rootFolderPath, add 'Report-history' sub directory in rootFolderPath
if (string.Equals(historyFolderPath, rootFolderPath, StringComparison.InvariantCultureIgnoreCase))
{
addHistorySubDir = true;
}
// If historyFolderPath == projectPath, add 'CodeCoverage' directory to projectPath
// and add 'Report-history' sub directory in rootFolderPath
else if (string.Equals(historyFolderPath, projectPath, StringComparison.InvariantCultureIgnoreCase))
{
rootFolderPath = JoinPaths(projectPath, coverageSettings.rootFolderName);
addHistorySubDir = true;
}
// otherwise keep the original historyFolderPath
}
else
{
// If historyFolderPath is not valid, add 'CodeCoverage' directory to projectPath
// and add 'Report-history' sub directory in rootFolderPath
rootFolderPath = JoinPaths(projectPath, coverageSettings.rootFolderName);
addHistorySubDir = true;
}
if (addHistorySubDir)
{
historyFolderPath = JoinPaths(rootFolderPath, CoverageSettings.ReportHistoryFolderName);
}
return historyFolderPath;
}
public static string JoinPaths(string pathLeft, string pathRight)
{
string[] pathsToJoin = new string[] { pathLeft, pathRight };
return string.Join("/", pathsToJoin);
}
public static int GetNumberOfFilesInFolder(string folderPath, string filePattern, SearchOption searchOption)
{
if (folderPath != null && Directory.Exists(folderPath))
{
string[] files = Directory.GetFiles(folderPath, filePattern, searchOption);
return files.Length;
}
return 0;
}
public static void ClearFolderIfExists(string folderPath, string filePattern)
{
if (folderPath != null && Directory.Exists(folderPath))
{
DirectoryInfo dirInfo = new DirectoryInfo(folderPath);
foreach (FileInfo file in dirInfo.GetFiles(filePattern))
{
try
{
file.Delete();
}
catch (Exception)
{
ResultsLogger.Log(ResultID.Warning_FailedToDeleteFile, file.FullName);
}
}
foreach (DirectoryInfo dir in dirInfo.GetDirectories())
{
try
{
dir.Delete(true);
}
catch (Exception)
{
ResultsLogger.Log(ResultID.Warning_FailedToDeleteDir, dir.FullName);
}
}
}
}
public static bool IsValidFolder(string folderPath)
{
return !string.IsNullOrEmpty(folderPath) && Directory.Exists(folderPath);
}
public static bool IsValidFile(string filePath)
{
return !string.IsNullOrEmpty(filePath) && File.Exists(filePath);
}
private static HashSet<char> regexSpecialChars = new HashSet<char>(new[] { '[', '\\', '^', '$', '.', '|', '?', '*', '+', '(', ')' });
public static string GlobToRegex(string glob, bool startEndConstrains = true)
{
var regex = new StringBuilder();
var characterClass = false;
char prevChar = Char.MinValue;
if (startEndConstrains)
regex.Append("^");
foreach (var c in glob)
{
if (characterClass)
{
if (c == ']')
{
characterClass = false;
}
regex.Append(c);
continue;
}
switch (c)
{
case '*':
if (prevChar == '*')
regex.Append(".*"); //if it's double * pattern then don't stop at folder separator
else
regex.Append("[^\\n\\r/]*"); //else match everything except folder separator (and new line)
break;
case '?':
regex.Append("[^\\n\\r/]");
break;
case '[':
characterClass = true;
regex.Append(c);
break;
default:
if (regexSpecialChars.Contains(c))
{
regex.Append('\\');
}
regex.Append(c);
break;
}
prevChar = c;
}
if (startEndConstrains)
regex.Append("$");
return regex.ToString();
}
public static string[] GetFilteringLogParams(AssemblyFiltering assemblyFiltering, PathFiltering pathFiltering, string[] otherParams = null)
{
string[] logParams = { assemblyFiltering != null && assemblyFiltering.includedAssemblies.Length > 0 ? assemblyFiltering.includedAssemblies : "<Not specified>",
assemblyFiltering != null && assemblyFiltering.excludedAssemblies.Length > 0 ? assemblyFiltering.excludedAssemblies : "<Not specified>",
pathFiltering != null && pathFiltering.includedPaths.Length > 0 ? pathFiltering.includedPaths : "<Not specified>",
pathFiltering != null && pathFiltering.excludedPaths.Length > 0 ? pathFiltering.excludedPaths : "<Not specified>" };
if (otherParams != null && otherParams.Length > 0)
logParams = otherParams.Concat(logParams).ToArray();
return logParams;
}
[ExcludeFromCoverage]
public static string BrowseForDir(string directory, string title)
{
if (string.IsNullOrEmpty(directory))
{
string variable = "ProgramFiles";
#if UNITY_EDITOR_OSX
variable = "HOME";
#endif
string candidateDirectory = Environment.GetEnvironmentVariable(variable);
if (IsValidFolder(candidateDirectory))
directory = candidateDirectory;
}
directory = EditorUtility.OpenFolderPanel(title, directory, string.Empty);
EditorWindow.FocusWindowIfItsOpen(typeof(CodeCoverageWindow));
if (!IsValidFolder(directory))
return string.Empty;
return directory;
}
[ExcludeFromCoverage]
public static string BrowseForFile(string directory, string title)
{
if (string.IsNullOrEmpty(directory))
{
string variable = "ProgramFiles";
#if UNITY_EDITOR_OSX
variable = "HOME";
#endif
string candidateDirectory = Environment.GetEnvironmentVariable(variable);
if (IsValidFolder(candidateDirectory))
directory = candidateDirectory;
}
string file = EditorUtility.OpenFilePanel(title, directory, "cs");
EditorWindow.FocusWindowIfItsOpen(typeof(CodeCoverageWindow));
if (!IsValidFile(file))
return string.Empty;
return file;
}
}
}

View File

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

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor.TestTools.CodeCoverage.Utils;
namespace UnityEditor.TestTools.CodeCoverage
{
internal static class JsonUtils
{
internal readonly static string[] m_validKeys = { "assembliesInclude", "assembliesExclude", "pathsInclude", "pathsExclude" };
// removes whitespace from places it would be problematic
internal static string CleanJsonString(string jsonString)
{
string result = jsonString;
// removes whitespace before the json key's colons
string regexSpaceBetweenKeyAndColon = @"(?<=\"")\s+(?=:)";
result = Regex.Replace(result, regexSpaceBetweenKeyAndColon, string.Empty, RegexOptions.None, TimeSpan.FromSeconds(5));
// trims whitespace from end of keys + values
string regexTrailingWhitespace = @"\s+(?=\"")";
result = Regex.Replace(result, regexTrailingWhitespace, string.Empty, RegexOptions.None, TimeSpan.FromSeconds(5));
// trims whitespace from beginning of keys + values
string regexPrecedingWhitespace = @"(?<=\"")\s+";
result = Regex.Replace(result, regexPrecedingWhitespace, string.Empty, RegexOptions.None, TimeSpan.FromSeconds(5));
return result;
}
internal static void ValidateJsonKeys(string jsonString)
{
// grabs all json keys by looking before the colons
Regex jsonKeyRegex = new Regex(@"[a-zA-Z0-9\s]+(?=\"":)", RegexOptions.None, TimeSpan.FromSeconds(5));
string[] keys = jsonKeyRegex.Matches(jsonString)
.Cast<Match>()
.Select(m => m.Value)
.ToArray();
if (!AreAllKeysUnique(keys, out string[] duplicateKeys))
ResultsLogger.Log(ResultID.Warning_FilterFileContainsDuplicateKeys, string.Join(",", duplicateKeys));
if (!AreAllKeysValid(keys, m_validKeys, out string[] invalidKeys))
ResultsLogger.Log(ResultID.Warning_FilterFileContainsInvalidKey, string.Join(",", invalidKeys));
}
internal static bool AreAllKeysUnique(string[] keys, out string[] duplicateKeys)
{
duplicateKeys = keys.GroupBy(key => key, StringComparer.InvariantCultureIgnoreCase)
.Where(keyGroup => keyGroup.Count() > 1)
.Select(keyGroup => keyGroup.Key)
.ToArray();
if (duplicateKeys.Length > 0) return false;
return true;
}
internal static bool AreAllKeysValid(string[] keys, string[] validKeys, out string[] invalidKeys)
{
invalidKeys = keys.Where(keyToCheck => !validKeys.Any(validKey => keyToCheck.Equals(validKey, StringComparison.InvariantCultureIgnoreCase)))
.Distinct()
.ToArray();
if (invalidKeys.Length > 0) return false;
return true;
}
}
}

View File

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