You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
12 KiB
C#

1 year ago
// #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
}
}
}
}