Initial commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa423365b1ce06a4dbdc6fb4a8597bfa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 891bb6b9f5094c36b59c3788e21c8213
|
||||
timeCreated: 1638532925
|
||||
@@ -0,0 +1,162 @@
|
||||
#if !UNITY_2023_2_OR_NEWER
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEditor.TestTools.TestRunner.TestRun;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api.Analytics
|
||||
{
|
||||
internal static class AnalyticsReporter
|
||||
{
|
||||
private const string VendorKey = "unity.test-framework";
|
||||
private const string RunFinishedEventName = "runFinished";
|
||||
private const string AnalyzeTestTreeName = "analyzeTestTree";
|
||||
|
||||
private static bool isSetUp;
|
||||
private static IDictionary<string, bool> methodsAnalyzed;
|
||||
private static IDictionary<string, bool> typesAnalyzed;
|
||||
|
||||
private static void SetUpIfNeeded()
|
||||
{
|
||||
if (isSetUp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isSetUp = true;
|
||||
EditorAnalytics.RegisterEventWithLimit(RunFinishedEventName, 60, 30, VendorKey);
|
||||
EditorAnalytics.RegisterEventWithLimit(AnalyzeTestTreeName, 3, 30, VendorKey);
|
||||
}
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
private static void RegisterCallbacks()
|
||||
{
|
||||
ScriptableObject.CreateInstance<TestRunnerApi>().RegisterCallbacks(new AnalyticsTestCallback(ReportRunFinished));
|
||||
}
|
||||
|
||||
private static void ReportRunFinished(ITestResultAdaptor testResult)
|
||||
{
|
||||
SetUpIfNeeded();
|
||||
|
||||
var activeRuns = TestJobDataHolder.instance.TestRuns;
|
||||
if (activeRuns.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var executionSettings = activeRuns[0].executionSettings;
|
||||
var filter = executionSettings.filters.First();
|
||||
var runFinishedData = new RunFinishedData
|
||||
{
|
||||
totalTests = testResult.Test.TestCaseCount,
|
||||
numPassedTests = testResult.PassCount,
|
||||
numFailedTests = testResult.FailCount,
|
||||
numInconclusiveTests = testResult.InconclusiveCount,
|
||||
numSkippedTests = testResult.SkipCount,
|
||||
testModeFilter = (int)filter.testMode,
|
||||
targetPlatform = executionSettings.targetPlatform != null ? executionSettings.targetPlatform.ToString() : "editor",
|
||||
runSynchronously = executionSettings.runSynchronously,
|
||||
isCustomRunner = false,
|
||||
isFiltering = executionSettings.filters.Any(f => f.HasAny()),
|
||||
isAutomated = IsCommandLineArgSet("-automated"),
|
||||
isFromCommandLine = IsCommandLineArgSet("-runTests"),
|
||||
totalTestDuration = testResult.Duration,
|
||||
totalRunDuration = (DateTime.Now - Convert.ToDateTime(activeRuns[0].startTime)).TotalSeconds
|
||||
};
|
||||
|
||||
EditorAnalytics.SendEventWithLimit(RunFinishedEventName, runFinishedData, 1);
|
||||
}
|
||||
|
||||
private static bool IsCommandLineArgSet(string command)
|
||||
{
|
||||
return Environment.GetCommandLineArgs().Any(c => c == command);
|
||||
}
|
||||
|
||||
internal static void AnalyzeTestTreeAndReport(ITest testTree)
|
||||
{
|
||||
SetUpIfNeeded();
|
||||
|
||||
typesAnalyzed = new Dictionary<string, bool>();
|
||||
methodsAnalyzed = new Dictionary<string, bool>();
|
||||
var data = new TestTreeData();
|
||||
AnalyzeTestTreeNode(testTree, data);
|
||||
EditorAnalytics.SendEventWithLimit(AnalyzeTestTreeName, data, 1);
|
||||
}
|
||||
|
||||
private static void AnalyzeTestTreeNode(ITest node, TestTreeData data)
|
||||
{
|
||||
var attributes = GetAttributes(node).ToArray();
|
||||
if (attributes.OfType<TestAttribute>().Any())
|
||||
{
|
||||
data.numTestAttributes++;
|
||||
}
|
||||
if (attributes.OfType<UnityTestAttribute>().Any())
|
||||
{
|
||||
data.numUnityTestAttributes++;
|
||||
}
|
||||
if (attributes.OfType<CategoryAttribute>().Any())
|
||||
{
|
||||
data.numCategoryAttributes++;
|
||||
}
|
||||
if (attributes.OfType<TestFixtureAttribute>().Any())
|
||||
{
|
||||
data.numTestFixtureAttributes++;
|
||||
}
|
||||
if (attributes.OfType<ConditionalIgnoreAttribute>().Any())
|
||||
{
|
||||
data.numConditionalIgnoreAttributes++;
|
||||
}
|
||||
if (attributes.OfType<UnityPlatformAttribute>().Any())
|
||||
{
|
||||
data.numUnityPlatformAttributes++;
|
||||
}
|
||||
|
||||
if (node.HasChildren)
|
||||
{
|
||||
foreach (var test in node.Tests)
|
||||
{
|
||||
AnalyzeTestTreeNode(test, data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data.totalNumberOfTests++;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<NUnitAttribute> GetAttributes(ITest node)
|
||||
{
|
||||
if (node.Method != null)
|
||||
{
|
||||
var key = $"{node.MethodName},{node.ClassName}";
|
||||
if (methodsAnalyzed.ContainsKey(key))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
methodsAnalyzed[key] = true;
|
||||
foreach (var attribute in (node).Method.GetCustomAttributes<NUnitAttribute>(true))
|
||||
{
|
||||
yield return attribute;
|
||||
}
|
||||
|
||||
var typeKey = node.Method.TypeInfo.FullName;
|
||||
if (typesAnalyzed.ContainsKey(typeKey))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
typesAnalyzed[typeKey] = true;
|
||||
foreach (var attribute in node.Method.TypeInfo.GetCustomAttributes<NUnitAttribute>(true))
|
||||
{
|
||||
yield return attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a85430cc5a4a4279a992be322de12b29
|
||||
timeCreated: 1638532946
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api.Analytics
|
||||
{
|
||||
internal class AnalyticsTestCallback : ICallbacks
|
||||
{
|
||||
private Action<ITestResultAdaptor> _runFinishedCallback;
|
||||
|
||||
public AnalyticsTestCallback(Action<ITestResultAdaptor> runFinishedCallback)
|
||||
{
|
||||
_runFinishedCallback = runFinishedCallback;
|
||||
}
|
||||
|
||||
public void RunStarted(ITestAdaptor testsToRun)
|
||||
{
|
||||
}
|
||||
|
||||
public void RunFinished(ITestResultAdaptor result)
|
||||
{
|
||||
_runFinishedCallback(result);
|
||||
}
|
||||
|
||||
public void TestStarted(ITestAdaptor test)
|
||||
{
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResultAdaptor result)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5e82966bb8646269e2e46b6ddf2d89f
|
||||
timeCreated: 1638535350
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api.Analytics
|
||||
{
|
||||
internal class RunFinishedData
|
||||
{
|
||||
public int totalTests;
|
||||
public int numPassedTests;
|
||||
public int numFailedTests;
|
||||
public int numInconclusiveTests;
|
||||
public int numSkippedTests;
|
||||
public int testModeFilter;
|
||||
public bool isAutomated;
|
||||
public bool isFromCommandLine;
|
||||
public bool isFiltering;
|
||||
public string targetPlatform;
|
||||
public double totalTestDuration;
|
||||
public double totalRunDuration;
|
||||
public bool runSynchronously;
|
||||
public bool isCustomRunner;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc781c79a6ac490f817bb70a01490d5c
|
||||
timeCreated: 1638533438
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api.Analytics
|
||||
{
|
||||
internal class TestTreeData
|
||||
{
|
||||
public int totalNumberOfTests;
|
||||
public int numTestAttributes;
|
||||
public int numUnityTestAttributes;
|
||||
public int numCategoryAttributes;
|
||||
public int numTestFixtureAttributes;
|
||||
public int numConditionalIgnoreAttributes;
|
||||
public int numRequiresPlayModeAttributesTrue;
|
||||
public int numRequiresPlayModeAttributesFalse;
|
||||
public int numUnityPlatformAttributes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 697ab794770540d6951f83d62b8fa444
|
||||
timeCreated: 1639043038
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class CallbacksDelegator : ICallbacksDelegator
|
||||
{
|
||||
private static CallbacksDelegator s_instance;
|
||||
public static CallbacksDelegator instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_instance == null)
|
||||
{
|
||||
s_instance = new CallbacksDelegator(CallbacksHolder.instance.GetAll, new TestAdaptorFactory());
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Func<ICallbacks[]> m_CallbacksProvider;
|
||||
private readonly ITestAdaptorFactory m_AdaptorFactory;
|
||||
|
||||
// Note that in the event of a domain reload the filter is not reapplied and will be null
|
||||
private ITestFilter m_TestRunFilter;
|
||||
|
||||
public CallbacksDelegator(Func<ICallbacks[]> callbacksProvider, ITestAdaptorFactory adaptorFactory)
|
||||
{
|
||||
m_CallbacksProvider = callbacksProvider;
|
||||
m_AdaptorFactory = adaptorFactory;
|
||||
}
|
||||
|
||||
public void RunStarted(ITest testsToRun)
|
||||
{
|
||||
m_AdaptorFactory.ClearResultsCache();
|
||||
var testRunnerTestsToRun = m_AdaptorFactory.Create(testsToRun, m_TestRunFilter);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.RunStarted(testRunnerTestsToRun));
|
||||
}
|
||||
|
||||
public void RunStartedRemotely(byte[] testsToRunData)
|
||||
{
|
||||
var testData = Deserialize<RemoteTestResultDataWithTestData>(testsToRunData);
|
||||
var testsToRun = m_AdaptorFactory.BuildTree(testData);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.RunStarted(testsToRun));
|
||||
}
|
||||
|
||||
public void RunFinished(ITestResult testResults)
|
||||
{
|
||||
var testResult = m_AdaptorFactory.Create(testResults);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.RunFinished(testResult));
|
||||
}
|
||||
|
||||
public void RunFinishedRemotely(byte[] testResultsData)
|
||||
{
|
||||
var remoteTestResult = Deserialize<RemoteTestResultDataWithTestData>(testResultsData);
|
||||
var testResult = m_AdaptorFactory.Create(remoteTestResult.results.First(), remoteTestResult);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.RunFinished(testResult));
|
||||
}
|
||||
|
||||
public void RunFailed(string failureMessage)
|
||||
{
|
||||
Debug.LogError(failureMessage);
|
||||
TryInvokeAllCallbacks(callbacks =>
|
||||
{
|
||||
var errorCallback = callbacks as IErrorCallbacks;
|
||||
if (errorCallback != null)
|
||||
{
|
||||
errorCallback.OnError(failureMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void TestStarted(ITest test)
|
||||
{
|
||||
var testRunnerTest = m_AdaptorFactory.Create(test);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.TestStarted(testRunnerTest));
|
||||
}
|
||||
|
||||
public void TestStartedRemotely(byte[] testStartedData)
|
||||
{
|
||||
var testData = Deserialize<RemoteTestResultDataWithTestData>(testStartedData);
|
||||
var testsToRun = m_AdaptorFactory.BuildTree(testData);
|
||||
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.TestStarted(testsToRun));
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResult result)
|
||||
{
|
||||
var testResult = m_AdaptorFactory.Create(result);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.TestFinished(testResult));
|
||||
}
|
||||
|
||||
public void TestFinishedRemotely(byte[] testResultsData)
|
||||
{
|
||||
var remoteTestResult = Deserialize<RemoteTestResultDataWithTestData>(testResultsData);
|
||||
var testResult = m_AdaptorFactory.Create(remoteTestResult.results.First(), remoteTestResult);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.TestFinished(testResult));
|
||||
}
|
||||
|
||||
public void TestTreeRebuild(ITest test)
|
||||
{
|
||||
using (new ProfilerMarker(nameof(TestTreeRebuild)).Auto())
|
||||
{
|
||||
m_AdaptorFactory.ClearTestsCache();
|
||||
ITestAdaptor testAdaptor;
|
||||
using (new ProfilerMarker("CreateTestAdaptors").Auto())
|
||||
testAdaptor = m_AdaptorFactory.Create(test);
|
||||
TryInvokeAllCallbacks(callbacks =>
|
||||
{
|
||||
var rebuildCallbacks = callbacks as ITestTreeRebuildCallbacks;
|
||||
if (rebuildCallbacks != null)
|
||||
{
|
||||
rebuildCallbacks.TestTreeRebuild(testAdaptor);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTestRunFilter(ITestFilter filter)
|
||||
{
|
||||
m_TestRunFilter = filter;
|
||||
}
|
||||
|
||||
private void TryInvokeAllCallbacks(Action<ICallbacks> callbackAction)
|
||||
{
|
||||
foreach (var testRunnerApiCallback in m_CallbacksProvider())
|
||||
{
|
||||
try
|
||||
{
|
||||
callbackAction(testRunnerApiCallback);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static T Deserialize<T>(byte[] data)
|
||||
{
|
||||
return JsonUtility.FromJson<T>(Encoding.UTF8.GetString(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0de03ebd74e2b474fa23d05ab42d0cd8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class CallbacksHolder : ScriptableSingleton<CallbacksHolder>, ICallbacksHolder
|
||||
{
|
||||
private List<CallbackWithPriority> m_Callbacks = new List<CallbackWithPriority>();
|
||||
public void Add(ICallbacks callback, int priority)
|
||||
{
|
||||
m_Callbacks.Add(new CallbackWithPriority(callback, priority));
|
||||
}
|
||||
|
||||
public void Remove(ICallbacks callback)
|
||||
{
|
||||
m_Callbacks.RemoveAll(callbackWithPriority => callbackWithPriority.Callback == callback);
|
||||
}
|
||||
|
||||
public ICallbacks[] GetAll()
|
||||
{
|
||||
return m_Callbacks.OrderByDescending(callback => callback.Priority).Select(callback => callback.Callback).ToArray();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_Callbacks.Clear();
|
||||
}
|
||||
|
||||
private struct CallbackWithPriority
|
||||
{
|
||||
public ICallbacks Callback;
|
||||
public int Priority;
|
||||
public CallbackWithPriority(ICallbacks callback, int priority)
|
||||
{
|
||||
Callback = callback;
|
||||
Priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
// Sometimes - such as when we want to test the test framework itself - it's necessary to launch a test run from
|
||||
// inside a test. Because callbacks are registered globally, this can cause a lot of confusion (e.g. the in-test
|
||||
// run will emit UTP messages, utterly confusing UTR). In such circumstances the safest thing to do is to
|
||||
// temporarily suppress all registered callbacks for the duration of the in-test run. This method can be called
|
||||
// to set up a using() block which will suppress the callbacks for the scope.
|
||||
public IDisposable TemporarilySuppressCallbacks()
|
||||
{
|
||||
return new Suppressor(this);
|
||||
}
|
||||
|
||||
private sealed class Suppressor : IDisposable
|
||||
{
|
||||
private readonly CallbacksHolder _instance;
|
||||
private readonly List<CallbackWithPriority> _suppressed;
|
||||
|
||||
public Suppressor(CallbacksHolder instance)
|
||||
{
|
||||
_instance = instance;
|
||||
_suppressed = new List<CallbackWithPriority>(instance.m_Callbacks);
|
||||
instance.m_Callbacks.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_instance.m_Callbacks.AddRange(_suppressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4884ccc3528cb2e40a0e6f0a19a2b35b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal.Filters;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// A set of execution settings defining how to run tests, using the <see cref="TestRunnerApi"/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ExecutionSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance with a given set of filters, if any.
|
||||
/// </summary>
|
||||
/// <param name="filtersToExecute">Set of filters</param>
|
||||
public ExecutionSettings(params Filter[] filtersToExecute)
|
||||
{
|
||||
filters = filtersToExecute;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private BuildTarget m_TargetPlatform;
|
||||
[SerializeField]
|
||||
private bool m_HasTargetPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// An instance of <see cref="ITestRunSettings"/> to set up before running tests on a Player.
|
||||
/// </summary>
|
||||
// Note: Is not available after serialization
|
||||
public ITestRunSettings overloadTestRunSettings;
|
||||
|
||||
[SerializeField]
|
||||
internal Filter filter;
|
||||
///<summary>
|
||||
///A collection of <see cref="Filter"/> to execute tests on.
|
||||
///</summary>
|
||||
[SerializeField]
|
||||
public Filter[] filters;
|
||||
/// <summary>
|
||||
/// Note that this is only supported for EditMode tests, and that tests which take multiple frames (i.e. [UnityTest] tests, or tests with [UnitySetUp] or [UnityTearDown] scaffolding) will be filtered out.
|
||||
/// </summary>
|
||||
/// <value>If true, the call to Execute() will run tests synchronously, guaranteeing that all tests have finished running by the time the call returns.</value>
|
||||
[SerializeField]
|
||||
public bool runSynchronously;
|
||||
/// <summary>
|
||||
/// The time, in seconds, the editor should wait for heartbeats after starting a test run on a player. This defaults to 10 minutes.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public int playerHeartbeatTimeout = 60 * 10;
|
||||
|
||||
[SerializeField]
|
||||
internal string[] orderedTestNames;
|
||||
|
||||
[SerializeField]
|
||||
internal IgnoreTest[] ignoreTests;
|
||||
|
||||
[SerializeField]
|
||||
internal FeatureFlags featureFlags;
|
||||
|
||||
[SerializeField]
|
||||
internal int randomOrderSeed;
|
||||
|
||||
internal string playerSavePath { get; set; }
|
||||
internal int retryCount { get; set; }
|
||||
internal int repeatCount { get; set; }
|
||||
|
||||
internal bool EditModeIncluded()
|
||||
{
|
||||
return filters.Any(f => IncludesTestMode(f.testMode, TestMode.EditMode));
|
||||
}
|
||||
|
||||
internal bool PlayModeInEditorIncluded()
|
||||
{
|
||||
return filters.Any(f => IncludesTestMode(f.testMode, TestMode.PlayMode) && targetPlatform == null);
|
||||
}
|
||||
|
||||
internal bool PlayerIncluded()
|
||||
{
|
||||
return filters.Any(f => IncludesTestMode(f.testMode, TestMode.PlayMode) && targetPlatform != null);
|
||||
}
|
||||
|
||||
private static bool IncludesTestMode(TestMode testMode, TestMode modeToCheckFor)
|
||||
{
|
||||
return (testMode & modeToCheckFor) == modeToCheckFor;
|
||||
}
|
||||
|
||||
internal ITestFilter BuildNUnitFilter()
|
||||
{
|
||||
return new OrFilter(filters.Select(f => f.ToRuntimeTestRunnerFilter(runSynchronously).BuildNUnitFilter()).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="BuildTarget"/> platform to run the test on. If set to null, then the Editor is the target for the tests.
|
||||
/// </summary>
|
||||
internal BuildTarget? targetPlatform
|
||||
{
|
||||
get { return m_HasTargetPlatform ? (BuildTarget?)m_TargetPlatform : null; }
|
||||
set
|
||||
{
|
||||
{
|
||||
if (value.HasValue)
|
||||
{
|
||||
m_HasTargetPlatform = true;
|
||||
m_TargetPlatform = value.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HasTargetPlatform = false;
|
||||
m_TargetPlatform = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of ToString() that builds a string composed of the execution settings.
|
||||
/// </summary>
|
||||
/// <returns>The current execution settings as a string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
stringBuilder.AppendLine($"{nameof(ExecutionSettings)} with details:");
|
||||
stringBuilder.AppendLine($"{nameof(targetPlatform)} = {targetPlatform}");
|
||||
stringBuilder.AppendLine($"{nameof(playerHeartbeatTimeout)} = {playerHeartbeatTimeout}");
|
||||
|
||||
if (filters.Length == 0)
|
||||
{
|
||||
stringBuilder.AppendLine($"{nameof(filters)} = {{}}");
|
||||
}
|
||||
|
||||
for (int i = 0; i < filters.Length; i++)
|
||||
{
|
||||
stringBuilder.AppendLine($"{nameof(filters)}[{i}] = ");
|
||||
var filterStrings = filters[i]
|
||||
.ToString()
|
||||
.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
|
||||
.ToArray();
|
||||
|
||||
foreach (var filterString in filterStrings)
|
||||
{
|
||||
stringBuilder.AppendLine($" {filterString}");
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoreTests == null || ignoreTests.Length == 0)
|
||||
{
|
||||
stringBuilder.AppendLine($"{nameof(ignoreTests)} = {{}}");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < ignoreTests.Length; i++)
|
||||
{
|
||||
stringBuilder.AppendLine($"{nameof(ignoreTests)}[{i}] = {ignoreTests[i]}");
|
||||
}
|
||||
}
|
||||
|
||||
if (featureFlags == null)
|
||||
{
|
||||
stringBuilder.AppendLine($"{nameof(featureFlags)} = null");
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.AppendLine("Feature Flags:");
|
||||
stringBuilder.AppendLine($" {nameof(featureFlags.fileCleanUpCheck)} = {featureFlags.fileCleanUpCheck}");
|
||||
stringBuilder.AppendLine($" {nameof(featureFlags.requiresSplashScreen)} = {featureFlags.requiresSplashScreen}");
|
||||
stringBuilder.AppendLine($" {nameof(featureFlags.strictDomainReload)} = {featureFlags.strictDomainReload}");
|
||||
stringBuilder.AppendLine($" {nameof(featureFlags.disableNestedEnumeratorBugfix)} = {featureFlags.disableNestedEnumeratorBugfix}");
|
||||
}
|
||||
|
||||
|
||||
stringBuilder.AppendLine();
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eea34a28297f9bc4c9f4c573bc8d5d1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.TestTools.TestRunner.GUI;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// The filter class provides the <see cref="TestRunnerApi"/> with a specification of what tests to run when [running tests programmatically](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-run-tests.html).
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class Filter
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum flag that specifies if Edit Mode or Play Mode tests should run.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public TestMode testMode;
|
||||
/// <summary>
|
||||
/// The full name of the tests to match the filter. This is usually in the format FixtureName.TestName. If the test has test arguments, then include them in parenthesis. E.g. MyTestClass2.MyTestWithMultipleValues(1).
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public string[] testNames;
|
||||
/// <summary>
|
||||
/// The same as testNames, except that it allows for Regex. This is useful for running specific fixtures or namespaces. E.g. "^MyNamespace\\." Runs any tests where the top namespace is MyNamespace.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public string[] groupNames;
|
||||
/// <summary>
|
||||
/// The name of a [Category](https://nunit.org/docs/2.2.7/category.html) to include in the run. Any test or fixtures runs that have a Category matching the string.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public string[] categoryNames;
|
||||
/// <summary>
|
||||
/// The name of assemblies included in the run. That is the assembly name, without the .dll file extension. E.g., MyTestAssembly
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public string[] assemblyNames;
|
||||
/// <summary>
|
||||
/// The <see cref="BuildTarget"/> platform to run the test on. If set to null, then the Editor is the target for the tests.
|
||||
/// Obsolete. Use the targetPlatform property on the <see cref="ExecutionSettings"/>.
|
||||
/// </summary>
|
||||
public BuildTarget? targetPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of ToString() that builds a string composed of the filter values.
|
||||
/// </summary>
|
||||
/// <returns>The current filter values as a string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
stringBuilder.AppendLine($"{nameof(Filter)} with settings:");
|
||||
stringBuilder.AppendLine($"{nameof(testMode)} = {testMode}");
|
||||
stringBuilder.AppendLine($"{nameof(targetPlatform)} = {targetPlatform}");
|
||||
stringBuilder.AppendLine($"{nameof(testNames)} = " + (testNames == null ? "null" : string.Join(", ", testNames)));
|
||||
stringBuilder.AppendLine($"{nameof(groupNames)} = " + (groupNames == null ? "null" : string.Join(", ", groupNames)));
|
||||
stringBuilder.AppendLine($"{nameof(categoryNames)} = " + (categoryNames == null ? "null" : string.Join(", ", categoryNames)));
|
||||
stringBuilder.AppendLine($"{nameof(assemblyNames)} = " + (assemblyNames == null ? "null" : string.Join(", ", assemblyNames)));
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
internal RuntimeTestRunnerFilter ToRuntimeTestRunnerFilter(bool synchronousOnly)
|
||||
{
|
||||
return new RuntimeTestRunnerFilter
|
||||
{
|
||||
testMode = ConvertTestMode(testMode),
|
||||
testNames = testNames,
|
||||
categoryNames = categoryNames,
|
||||
groupNames = groupNames,
|
||||
assemblyNames = assemblyNames,
|
||||
synchronousOnly = synchronousOnly
|
||||
};
|
||||
}
|
||||
|
||||
private static TestPlatform ConvertTestMode(TestMode testMode)
|
||||
{
|
||||
if (testMode == (TestMode.EditMode | TestMode.PlayMode))
|
||||
{
|
||||
return TestPlatform.All;
|
||||
}
|
||||
|
||||
if (testMode == TestMode.EditMode)
|
||||
{
|
||||
return TestPlatform.EditMode;
|
||||
}
|
||||
|
||||
if (testMode == TestMode.PlayMode)
|
||||
{
|
||||
return TestPlatform.PlayMode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
internal bool HasAny()
|
||||
{
|
||||
return assemblyNames != null && assemblyNames.Any()
|
||||
|| categoryNames != null && categoryNames.Any()
|
||||
|| groupNames != null && groupNames.Any()
|
||||
|| testNames != null && testNames.Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05f92e4a2414cb144a92157752dfa324
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Callbacks in the <see cref="TestRunnerApi"/> for the test stages when running tests.
|
||||
/// </summary>
|
||||
public interface ICallbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// A callback invoked when a test run is started.
|
||||
/// </summary>
|
||||
/// <param name="testsToRun">The full loaded test tree.</param>
|
||||
void RunStarted(ITestAdaptor testsToRun);
|
||||
/// <summary>
|
||||
/// A callback invoked when a test run is finished.
|
||||
/// </summary>
|
||||
/// <param name="result">The result of the test run.</param>
|
||||
void RunFinished(ITestResultAdaptor result);
|
||||
/// <summary>
|
||||
/// A callback invoked when each individual node of the test tree has started executing.
|
||||
/// </summary>
|
||||
/// <param name="test">The test node currently executed.</param>
|
||||
void TestStarted(ITestAdaptor test);
|
||||
/// <summary>
|
||||
/// A callback invoked when each individual node of the test tree has finished executing.
|
||||
/// </summary>
|
||||
/// <param name="result">The result of the test tree node after it had been executed.</param>
|
||||
void TestFinished(ITestResultAdaptor result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93eea84e53d0226479c9a584f19427b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal interface ICallbacksDelegator
|
||||
{
|
||||
void RunStarted(ITest testsToRun);
|
||||
void RunStartedRemotely(byte[] testsToRunData);
|
||||
void RunFinished(ITestResult testResults);
|
||||
void RunFinishedRemotely(byte[] testResultsData);
|
||||
void RunFailed(string failureMessage);
|
||||
void TestStarted(ITest test);
|
||||
void TestStartedRemotely(byte[] testStartedData);
|
||||
void TestFinished(ITestResult result);
|
||||
void TestFinishedRemotely(byte[] testResultsData);
|
||||
void TestTreeRebuild(ITest test);
|
||||
void SetTestRunFilter(ITestFilter filter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f8f74fe8e363da42875d9cab025d3b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal interface ICallbacksHolder
|
||||
{
|
||||
void Add(ICallbacks callback, int priority);
|
||||
void Remove(ICallbacks callback);
|
||||
ICallbacks[] GetAll();
|
||||
void Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d742f2caefd9f934d9f19dad07a08e6f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// An extended version of the <see cref="ICallbacks"/>, which get invoked if the test run fails due to a build error or if any <see cref="UnityEngine.TestTools.IPrebuildSetup"/> has a failure.
|
||||
/// </summary>
|
||||
public interface IErrorCallbacks : ICallbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Method invoked on failure.
|
||||
/// </summary>
|
||||
/// <param name="message">
|
||||
/// The error message detailing the reason for the run to fail.
|
||||
/// </param>
|
||||
void OnError(string message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a06c562b0c5eb046bcb876a29f93c98
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// ```ITestAdaptor``` is a representation of a node in the test tree implemented as a wrapper around the [NUnit](http://www.nunit.org/) [ITest](https://github.com/nunit/nunit/blob/master/src/NUnitFramework/framework/Interfaces/ITest.cs) interface.
|
||||
/// </summary>
|
||||
public interface ITestAdaptor
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the test tree node. The ID can change if you add new tests to the suite. Use UniqueName, if you want to have a more permanent point of reference.
|
||||
/// </summary>
|
||||
string Id { get; }
|
||||
/// <summary>
|
||||
/// The name of the test. E.g.,```MyTest```.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
/// <summary>
|
||||
/// The full name of the test. E.g., ```MyNamespace.MyTestClass.MyTest```.
|
||||
/// </summary>
|
||||
string FullName { get; }
|
||||
/// <summary>
|
||||
/// The total number of test cases in the node and all sub-nodes.
|
||||
/// </summary>
|
||||
int TestCaseCount { get; }
|
||||
/// <summary>
|
||||
/// Whether the node has any children.
|
||||
/// </summary>
|
||||
bool HasChildren { get; }
|
||||
/// <summary>
|
||||
/// True if the node is a test suite/fixture, false otherwise.
|
||||
/// </summary>
|
||||
bool IsSuite { get; }
|
||||
/// <summary>
|
||||
/// The child nodes.
|
||||
/// </summary>
|
||||
IEnumerable<ITestAdaptor> Children { get; }
|
||||
/// <summary>
|
||||
/// The parent node, if any.
|
||||
/// </summary>
|
||||
ITestAdaptor Parent { get; }
|
||||
/// <summary>
|
||||
/// The test case timeout in milliseconds. Note that this value is only available on TestFinished.
|
||||
/// </summary>
|
||||
int TestCaseTimeout { get; }
|
||||
/// <summary>
|
||||
/// The type of test class as an ```NUnit``` <see cref="ITypeInfo"/>. If the node is not a test class, then the value is null.
|
||||
/// </summary>
|
||||
ITypeInfo TypeInfo { get; }
|
||||
/// <summary>
|
||||
/// The Nunit <see cref="IMethodInfo"/> of the test method. If the node is not a test method, then the value is null.
|
||||
/// </summary>
|
||||
IMethodInfo Method { get; }
|
||||
/// <summary>
|
||||
/// The array of arguments that the test method/fixture will be invoked with.
|
||||
/// </summary>
|
||||
object[] Arguments { get; }
|
||||
/// <summary>
|
||||
/// An array of the categories applied to the test or fixture.
|
||||
/// </summary>
|
||||
string[] Categories { get; }
|
||||
/// <summary>
|
||||
/// Returns true if the node represents a test assembly, false otherwise.
|
||||
/// </summary>
|
||||
bool IsTestAssembly { get; }
|
||||
/// <summary>
|
||||
/// The run state of the test node. Either ```NotRunnable```, ```Runnable```, ```Explicit```, ```Skipped```, or ```Ignored```.
|
||||
/// </summary>
|
||||
RunState RunState { get; }
|
||||
/// <summary>
|
||||
/// The description of the test.
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
/// <summary>
|
||||
/// The skip reason. E.g., if ignoring the test.
|
||||
/// </summary>
|
||||
string SkipReason { get; }
|
||||
/// <summary>
|
||||
/// The ID of the parent node.
|
||||
/// </summary>
|
||||
string ParentId { get; }
|
||||
/// <summary>
|
||||
/// The full name of the parent node.
|
||||
/// </summary>
|
||||
string ParentFullName { get; }
|
||||
/// <summary>
|
||||
/// A unique generated name for the test node. E.g., ```Tests.dll/MyNamespace/MyTestClass/[Tests][MyNamespace.MyTestClass.MyTest]```.
|
||||
/// </summary>
|
||||
string UniqueName { get; }
|
||||
/// <summary>
|
||||
/// A unique name of the parent node. E.g., ```Tests.dll/MyNamespace/[Tests][MyNamespace.MyTestClass][suite]```.
|
||||
/// </summary>
|
||||
string ParentUniqueName { get; }
|
||||
/// <summary>
|
||||
/// The child index of the node in its parent.
|
||||
/// </summary>
|
||||
int ChildIndex { get; }
|
||||
/// <summary>
|
||||
/// The mode of the test. Either **Edit Mode** or **Play Mode**.
|
||||
/// </summary>
|
||||
TestMode TestMode { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85dd7af03f02aea4aae13a3945e3b313
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal interface ITestAdaptorFactory
|
||||
{
|
||||
ITestAdaptor Create(ITest test);
|
||||
ITestAdaptor Create(RemoteTestData testData);
|
||||
ITestResultAdaptor Create(ITestResult testResult);
|
||||
ITestAdaptor Create(ITest test, ITestFilter filter);
|
||||
ITestResultAdaptor Create(RemoteTestResultData testResult, RemoteTestResultDataWithTestData allData);
|
||||
ITestAdaptor BuildTree(RemoteTestResultDataWithTestData data);
|
||||
IEnumerator<ITestAdaptor> BuildTreeAsync(RemoteTestResultDataWithTestData data);
|
||||
void ClearResultsCache();
|
||||
void ClearTestsCache();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 803abab0f7e17044db56f8760186dbd1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// The `ITestResultAdaptor` is the representation of the test results for a node in the test tree implemented as a wrapper around the [NUnit](http://www.nunit.org/) [ITest](https://github.com/nunit/nunit/blob/master/src/NUnitFramework/framework/Interfaces/ITestResults.cs) interface.
|
||||
/// </summary>
|
||||
public interface ITestResultAdaptor
|
||||
{
|
||||
/// <summary>
|
||||
/// The test details of the test result tree node as a <see cref="TestAdaptor"/>
|
||||
/// </summary>
|
||||
ITestAdaptor Test { get; }
|
||||
///<summary>
|
||||
///The name of the test node.
|
||||
///</summary>
|
||||
string Name { get; }
|
||||
/// <summary>
|
||||
/// Gets the full name of the test result
|
||||
/// </summary>
|
||||
///<value>
|
||||
///The name of the test result.
|
||||
///</value>
|
||||
string FullName { get; }
|
||||
///<summary>
|
||||
///Gets the state of the result as a string.
|
||||
///</summary>
|
||||
///<value>
|
||||
///It returns one of these values: `Inconclusive`, `Skipped`, `Skipped:Ignored`, `Skipped:Explicit`, `Passed`, `Failed`, `Failed:Error`, `Failed:Cancelled`, `Failed:Invalid`
|
||||
///</value>
|
||||
string ResultState { get; }
|
||||
///<summary>
|
||||
///Gets the status of the test as an enum.
|
||||
///</summary>
|
||||
///<value>
|
||||
///It returns one of these values:`Inconclusive`, `Skipped`, `Passed`, or `Failed`
|
||||
///</value>
|
||||
TestStatus TestStatus { get; }
|
||||
/// <summary>
|
||||
/// Gets the elapsed time for running the test in seconds
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Time in seconds.
|
||||
/// </value>
|
||||
double Duration { get; }
|
||||
/// <summary>
|
||||
/// Gets or sets the time the test started running.
|
||||
/// </summary>
|
||||
///<value>
|
||||
///A DataTime object.
|
||||
///</value>
|
||||
DateTime StartTime { get; }
|
||||
///<summary>
|
||||
///Gets or sets the time the test finished running.
|
||||
///</summary>
|
||||
///<value>
|
||||
///A DataTime object.
|
||||
///</value>
|
||||
DateTime EndTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The message associated with a test failure or with not running the test
|
||||
/// </summary>
|
||||
string Message { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Any stacktrace associated with an error or failure. Not available in the Compact Framework 1.0.
|
||||
/// </summary>
|
||||
string StackTrace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of asserts executed when running the test and all its children.
|
||||
/// </summary>
|
||||
int AssertCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of test cases that failed when running the test and all its children.
|
||||
/// </summary>
|
||||
int FailCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of test cases that passed when running the test and all its children.
|
||||
/// </summary>
|
||||
int PassCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of test cases that were skipped when running the test and all its children.
|
||||
/// </summary>
|
||||
int SkipCount { get; }
|
||||
|
||||
/// <summary>
|
||||
///The number of test cases that were inconclusive when running the test and all its children.
|
||||
/// </summary>
|
||||
int InconclusiveCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Accessing HasChildren should not force creation of the Children collection in classes implementing this interface.
|
||||
/// </summary>
|
||||
/// <value>True if this result has any child results.</value>
|
||||
bool HasChildren { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the the collection of child results.
|
||||
/// </summary>
|
||||
IEnumerable<ITestResultAdaptor> Children { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets any text output written to this result.
|
||||
/// </summary>
|
||||
string Output { get; }
|
||||
/// <summary>
|
||||
/// Use this to save the results to an XML file
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The test results as an `NUnit` XML node.
|
||||
/// </returns>
|
||||
TNode ToXml();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f90cfe4bf5cfb44f84a5b11387f2a42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// ITestRunSettings lets you set any of the global settings right before building a Player for a test run and then reverts the settings afterward. ITestRunSettings implements
|
||||
/// [IDisposable](https://docs.microsoft.com/en-us/dotnet/api/system.idisposable?view=netframework-4.8), and runs after building the Player with tests.
|
||||
/// </summary>
|
||||
public interface ITestRunSettings : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// A method called before building the Player.
|
||||
/// </summary>
|
||||
void Apply();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ae2ce6274819484fa8747a28cebdf3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal interface ITestRunnerApi
|
||||
{
|
||||
string Execute(ExecutionSettings executionSettings);
|
||||
void RegisterCallbacks<T>(T testCallbacks, int priority = 0) where T : ICallbacks;
|
||||
void UnregisterCallbacks<T>(T testCallbacks) where T : ICallbacks;
|
||||
void RetrieveTestList(TestMode testMode, Action<ITestAdaptor> callback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7842a837a4b13e41ae16193db753418
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal interface ITestTreeRebuildCallbacks : ICallbacks
|
||||
{
|
||||
void TestTreeRebuild(ITestAdaptor test);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4230e406313f1db43a4b548e7a3ad2e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
[Serializable]
|
||||
internal class IgnoreTest
|
||||
{
|
||||
public string test { get; set; }
|
||||
public string ignoreComment { get; set; }
|
||||
|
||||
public UnityEngine.TestTools.IgnoreTest ParseToEngine()
|
||||
{
|
||||
return new UnityEngine.TestTools.IgnoreTest
|
||||
{
|
||||
test = test,
|
||||
ignoreComment = ignoreComment
|
||||
};
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"'{test}': '{ignoreComment}'";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a785dd14005a471483d6732fabeee0ea
|
||||
timeCreated: 1675763396
|
||||
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class ResultsWriter
|
||||
{
|
||||
private const string k_nUnitVersion = "3.5.0.0";
|
||||
|
||||
private const string k_TestRunNode = "test-run";
|
||||
private const string k_Id = "id";
|
||||
private const string k_Testcasecount = "testcasecount";
|
||||
private const string k_Result = "result";
|
||||
private const string k_Total = "total";
|
||||
private const string k_Passed = "passed";
|
||||
private const string k_Failed = "failed";
|
||||
private const string k_Inconclusive = "inconclusive";
|
||||
private const string k_Skipped = "skipped";
|
||||
private const string k_Asserts = "asserts";
|
||||
private const string k_EngineVersion = "engine-version";
|
||||
private const string k_ClrVersion = "clr-version";
|
||||
private const string k_StartTime = "start-time";
|
||||
private const string k_EndTime = "end-time";
|
||||
private const string k_Duration = "duration";
|
||||
|
||||
private const string k_TimeFormat = "u";
|
||||
|
||||
public void WriteResultToFile(ITestResultAdaptor result, string filePath)
|
||||
{
|
||||
Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "Saving results to: {0}", filePath);
|
||||
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(filePath))
|
||||
{
|
||||
CreateDirectory(filePath);
|
||||
}
|
||||
|
||||
using (var fileStream = File.CreateText(filePath))
|
||||
{
|
||||
WriteResultToStream(result, fileStream);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError("Saving result file failed.");
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateDirectory(string filePath)
|
||||
{
|
||||
var driectoryPath = Path.GetDirectoryName(filePath);
|
||||
if (!String.IsNullOrEmpty(driectoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(driectoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteResultToStream(ITestResultAdaptor result, StreamWriter streamWriter, XmlWriterSettings settings = null)
|
||||
{
|
||||
settings = settings ?? new XmlWriterSettings();
|
||||
settings.Indent = true;
|
||||
settings.NewLineOnAttributes = false;
|
||||
|
||||
using (var xmlWriter = XmlWriter.Create(streamWriter, settings))
|
||||
{
|
||||
WriteResultsToXml(result, xmlWriter);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteResultsToXml(ITestResultAdaptor result, XmlWriter xmlWriter)
|
||||
{
|
||||
// XML format as specified at https://github.com/nunit/docs/wiki/Test-Result-XML-Format
|
||||
|
||||
var testRunNode = new TNode(k_TestRunNode);
|
||||
|
||||
testRunNode.AddAttribute(k_Id, "2");
|
||||
testRunNode.AddAttribute(k_Testcasecount, (result.PassCount + result.FailCount + result.SkipCount + result.InconclusiveCount).ToString());
|
||||
testRunNode.AddAttribute(k_Result, result.ResultState);
|
||||
testRunNode.AddAttribute(k_Total, (result.PassCount + result.FailCount + result.SkipCount + result.InconclusiveCount).ToString());
|
||||
testRunNode.AddAttribute(k_Passed, result.PassCount.ToString());
|
||||
testRunNode.AddAttribute(k_Failed, result.FailCount.ToString());
|
||||
testRunNode.AddAttribute(k_Inconclusive, result.InconclusiveCount.ToString());
|
||||
testRunNode.AddAttribute(k_Skipped, result.SkipCount.ToString());
|
||||
testRunNode.AddAttribute(k_Asserts, result.AssertCount.ToString());
|
||||
testRunNode.AddAttribute(k_EngineVersion, k_nUnitVersion);
|
||||
testRunNode.AddAttribute(k_ClrVersion, Environment.Version.ToString());
|
||||
testRunNode.AddAttribute(k_StartTime, result.StartTime.ToString(k_TimeFormat));
|
||||
testRunNode.AddAttribute(k_EndTime, result.EndTime.ToString(k_TimeFormat));
|
||||
testRunNode.AddAttribute(k_Duration, result.Duration.ToString());
|
||||
|
||||
var resultNode = result.ToXml();
|
||||
testRunNode.ChildNodes.Add(resultNode);
|
||||
|
||||
testRunNode.WriteTo(xmlWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29d603e0a726a9043b3503112271844a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// The RunState enum indicates whether a test can be executed.
|
||||
/// </summary>
|
||||
public enum RunState
|
||||
{
|
||||
/// <summary>
|
||||
/// The test is not runnable.
|
||||
/// </summary>
|
||||
NotRunnable,
|
||||
|
||||
/// <summary>
|
||||
/// The test is runnable.
|
||||
/// </summary>
|
||||
Runnable,
|
||||
|
||||
/// <summary>
|
||||
/// The test can only be run explicitly
|
||||
/// </summary>
|
||||
Explicit,
|
||||
|
||||
/// <summary>
|
||||
/// The test has been skipped. This value may appear on a Test when certain attributes are used to skip the test.
|
||||
/// </summary>
|
||||
Skipped,
|
||||
|
||||
/// <summary>
|
||||
/// The test has been ignored. May appear on a Test, when the IgnoreAttribute is used.
|
||||
/// </summary>
|
||||
Ignored
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bb59cb2f66d156418ca1bd1e2703233
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.TestTools.Utils;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class TestAdaptor : ITestAdaptor
|
||||
{
|
||||
internal TestAdaptor(ITest test, ITestAdaptor[] children = null)
|
||||
{
|
||||
Id = test.Id;
|
||||
Name = test.Name;
|
||||
var childIndex = -1;
|
||||
if (test.Properties["childIndex"].Count > 0)
|
||||
{
|
||||
childIndex = (int)test.Properties["childIndex"][0];
|
||||
}
|
||||
FullName = TestExtensions.GetFullName(test.FullName, childIndex);
|
||||
TestCaseCount = test.TestCaseCount;
|
||||
HasChildren = test.HasChildren;
|
||||
IsSuite = test.IsSuite;
|
||||
if (UnityTestExecutionContext.CurrentContext != null)
|
||||
{
|
||||
TestCaseTimeout = UnityTestExecutionContext.CurrentContext.TestCaseTimeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
TestCaseTimeout = UnityWorkItem.k_DefaultTimeout;
|
||||
}
|
||||
|
||||
TypeInfo = test.TypeInfo;
|
||||
Method = test.Method;
|
||||
Arguments = test is TestMethod testMethod ? testMethod.parms?.Arguments : (test as TestSuite)?.Arguments;
|
||||
Categories = test.GetAllCategoriesFromTest().Distinct().ToArray();
|
||||
IsTestAssembly = test is TestAssembly;
|
||||
RunState = (RunState)Enum.Parse(typeof(RunState), test.RunState.ToString());
|
||||
Description = (string)test.Properties.Get(PropertyNames.Description);
|
||||
SkipReason = test.GetSkipReason();
|
||||
ParentId = test.GetParentId();
|
||||
ParentFullName = test.GetParentFullName();
|
||||
UniqueName = test.GetUniqueName();
|
||||
ParentUniqueName = test.GetParentUniqueName();
|
||||
ChildIndex = childIndex;
|
||||
|
||||
var testPlatform = test.Properties.Get("platform");
|
||||
if (testPlatform is TestPlatform platform)
|
||||
{
|
||||
TestMode = PlatformToTestMode(platform);
|
||||
}
|
||||
|
||||
Children = children;
|
||||
}
|
||||
|
||||
public void SetParent(ITestAdaptor parent)
|
||||
{
|
||||
Parent = parent;
|
||||
if (parent != null)
|
||||
{
|
||||
TestMode = parent.TestMode;
|
||||
}
|
||||
}
|
||||
|
||||
internal TestAdaptor(RemoteTestData test)
|
||||
{
|
||||
Id = test.id;
|
||||
Name = test.name;
|
||||
FullName = TestExtensions.GetFullName(test.fullName, test.ChildIndex);
|
||||
TestCaseCount = test.testCaseCount;
|
||||
HasChildren = test.hasChildren;
|
||||
IsSuite = test.isSuite;
|
||||
m_ChildrenIds = test.childrenIds;
|
||||
TestCaseTimeout = test.testCaseTimeout;
|
||||
Categories = test.Categories;
|
||||
IsTestAssembly = test.IsTestAssembly;
|
||||
RunState = (RunState)Enum.Parse(typeof(RunState), test.RunState.ToString());
|
||||
Description = test.Description;
|
||||
SkipReason = test.SkipReason;
|
||||
ParentId = test.ParentId;
|
||||
UniqueName = test.UniqueName;
|
||||
ParentUniqueName = test.ParentUniqueName;
|
||||
ParentFullName = test.ParentFullName;
|
||||
ChildIndex = test.ChildIndex;
|
||||
TestMode = TestMode.PlayMode;
|
||||
}
|
||||
|
||||
internal void ApplyChildren(IEnumerable<TestAdaptor> allTests)
|
||||
{
|
||||
Children = m_ChildrenIds.Select(id => allTests.First(t => t.Id == id)).ToArray();
|
||||
if (!string.IsNullOrEmpty(ParentId))
|
||||
{
|
||||
Parent = allTests.FirstOrDefault(t => t.Id == ParentId);
|
||||
}
|
||||
}
|
||||
|
||||
private static TestMode PlatformToTestMode(TestPlatform testPlatform)
|
||||
{
|
||||
switch (testPlatform)
|
||||
{
|
||||
case TestPlatform.All:
|
||||
return TestMode.EditMode | TestMode.PlayMode;
|
||||
case TestPlatform.EditMode:
|
||||
return TestMode.EditMode;
|
||||
case TestPlatform.PlayMode:
|
||||
return TestMode.PlayMode;
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public string Id { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public string FullName { get; private set; }
|
||||
public int TestCaseCount { get; private set; }
|
||||
public bool HasChildren { get; private set; }
|
||||
public bool IsSuite { get; private set; }
|
||||
public IEnumerable<ITestAdaptor> Children { get; private set; }
|
||||
public ITestAdaptor Parent { get; private set; }
|
||||
public int TestCaseTimeout { get; private set; }
|
||||
public ITypeInfo TypeInfo { get; private set; }
|
||||
public IMethodInfo Method { get; private set; }
|
||||
public object[] Arguments { get; }
|
||||
private string[] m_ChildrenIds;
|
||||
public string[] Categories { get; private set; }
|
||||
public bool IsTestAssembly { get; private set; }
|
||||
public RunState RunState { get; }
|
||||
public string Description { get; }
|
||||
public string SkipReason { get; }
|
||||
public string ParentId { get; }
|
||||
public string ParentFullName { get; }
|
||||
public string UniqueName { get; }
|
||||
public string ParentUniqueName { get; }
|
||||
public int ChildIndex { get; }
|
||||
public TestMode TestMode { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e0e62db88935c74288c97c907243bd0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class TestAdaptorFactory : ITestAdaptorFactory
|
||||
{
|
||||
private Dictionary<string, TestAdaptor> m_TestAdaptorCache = new Dictionary<string, TestAdaptor>();
|
||||
private Dictionary<string, TestResultAdaptor> m_TestResultAdaptorCache = new Dictionary<string, TestResultAdaptor>();
|
||||
public ITestAdaptor Create(ITest test)
|
||||
{
|
||||
var cacheKey = string.Concat(test.GetUniqueName(),test.Properties.Get("platform"));
|
||||
if (test.Properties.ContainsKey("platform"))
|
||||
{
|
||||
cacheKey = string.Concat(cacheKey, test.Properties.Get("platform"));
|
||||
}
|
||||
var elementIsModified = test.Properties.ContainsKey(OrderedTestSuiteModifier.suiteIsReorderedProperty);
|
||||
if (!elementIsModified && m_TestAdaptorCache.ContainsKey(cacheKey))
|
||||
{
|
||||
return m_TestAdaptorCache[cacheKey];
|
||||
}
|
||||
|
||||
var adaptor = new TestAdaptor(test, test.Tests.Select(Create).ToArray());
|
||||
foreach (var child in adaptor.Children)
|
||||
{
|
||||
(child as TestAdaptor).SetParent(adaptor);
|
||||
}
|
||||
|
||||
if (!elementIsModified)
|
||||
{
|
||||
m_TestAdaptorCache[cacheKey] = adaptor;
|
||||
}
|
||||
return adaptor;
|
||||
}
|
||||
|
||||
public ITestAdaptor Create(RemoteTestData testData)
|
||||
{
|
||||
return new TestAdaptor(testData);
|
||||
}
|
||||
|
||||
public ITestResultAdaptor Create(ITestResult testResult)
|
||||
{
|
||||
var cacheKey = string.Join(";", testResult.Test.GetUniqueName(), testResult.Test.GetRetryIteration(), testResult.Test.GetRepeatIteration());
|
||||
if (m_TestResultAdaptorCache.ContainsKey(cacheKey))
|
||||
{
|
||||
return m_TestResultAdaptorCache[cacheKey];
|
||||
}
|
||||
var adaptor = new TestResultAdaptor(testResult, Create(testResult.Test), testResult.Children.Select(Create).ToArray());
|
||||
m_TestResultAdaptorCache[cacheKey] = adaptor;
|
||||
return adaptor;
|
||||
}
|
||||
|
||||
public ITestAdaptor Create(ITest test, ITestFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
return Create(test);
|
||||
|
||||
if (!filter.Pass(test))
|
||||
{
|
||||
if (test.Parent == null)
|
||||
{
|
||||
// Create an empty root.
|
||||
return new TestAdaptor(test, children: new ITestAdaptor[0]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var children = test.Tests
|
||||
.Select(c => Create(c, filter))
|
||||
.Where(c => c != null)
|
||||
.ToArray();
|
||||
|
||||
var adaptor = new TestAdaptor(test, children: children);
|
||||
|
||||
foreach (var child in adaptor.Children)
|
||||
(child as TestAdaptor).SetParent(adaptor);
|
||||
|
||||
return adaptor;
|
||||
}
|
||||
|
||||
public ITestResultAdaptor Create(RemoteTestResultData testResult, RemoteTestResultDataWithTestData allData)
|
||||
{
|
||||
return new TestResultAdaptor(testResult, allData);
|
||||
}
|
||||
|
||||
public ITestAdaptor BuildTree(RemoteTestResultDataWithTestData data)
|
||||
{
|
||||
var tests = data.tests.Select(remoteTestData => new TestAdaptor(remoteTestData)).ToList();
|
||||
|
||||
foreach (var test in tests)
|
||||
{
|
||||
test.ApplyChildren(tests);
|
||||
}
|
||||
|
||||
return tests.First();
|
||||
}
|
||||
|
||||
public IEnumerator<ITestAdaptor> BuildTreeAsync(RemoteTestResultDataWithTestData data)
|
||||
{
|
||||
var tests = data.tests.Select(remoteTestData => new TestAdaptor(remoteTestData)).ToList();
|
||||
|
||||
for (var index = 0; index < tests.Count; index++)
|
||||
{
|
||||
var test = tests[index];
|
||||
test.ApplyChildren(tests);
|
||||
if (index % 100 == 0)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
yield return tests.First();
|
||||
}
|
||||
|
||||
public void ClearResultsCache()
|
||||
{
|
||||
m_TestResultAdaptorCache.Clear();
|
||||
}
|
||||
|
||||
public void ClearTestsCache()
|
||||
{
|
||||
m_TestAdaptorCache.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0663d520c26b7c48a4135599e66acf8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// A flag indicating whether to run Edit Mode or Play Mode tests.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum TestMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Run EditMode tests.
|
||||
/// </summary>
|
||||
EditMode = 1 << 0,
|
||||
/// <summary>
|
||||
/// Run PlayMode tests.
|
||||
/// </summary>
|
||||
PlayMode = 1 << 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cad095eccea17b741bc4cd264e7441cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class TestResultAdaptor : ITestResultAdaptor
|
||||
{
|
||||
private TNode m_Node;
|
||||
private ITestResult m_Result;
|
||||
|
||||
internal TestResultAdaptor(ITestResult result, ITestAdaptor test, ITestResultAdaptor[] children = null)
|
||||
{
|
||||
Test = test;
|
||||
Name = result.Name;
|
||||
FullName = result.FullName;
|
||||
ResultState = result.ResultState.ToString();
|
||||
TestStatus = ParseTestStatus(result.ResultState.Status);
|
||||
Duration = result.Duration;
|
||||
StartTime = result.StartTime;
|
||||
EndTime = result.EndTime;
|
||||
Message = result.Message;
|
||||
StackTrace = result.StackTrace;
|
||||
AssertCount = result.AssertCount;
|
||||
FailCount = result.FailCount;
|
||||
PassCount = result.PassCount;
|
||||
SkipCount = result.SkipCount;
|
||||
InconclusiveCount = result.InconclusiveCount;
|
||||
HasChildren = result.HasChildren;
|
||||
Output = result.Output;
|
||||
Children = children;
|
||||
m_Result = result;
|
||||
RetryIteration = result.Test.GetRetryIteration();
|
||||
RepeatIteration = result.Test.GetRepeatIteration();
|
||||
}
|
||||
|
||||
internal TestResultAdaptor(RemoteTestResultData result, RemoteTestResultDataWithTestData allData)
|
||||
{
|
||||
Test = new TestAdaptor(allData.tests.First(t => t.id == result.testId));
|
||||
Name = result.name;
|
||||
FullName = result.fullName;
|
||||
ResultState = result.resultState;
|
||||
TestStatus = ParseTestStatus(result.testStatus);
|
||||
Duration = result.duration;
|
||||
StartTime = result.startTime;
|
||||
EndTime = result.endTime;
|
||||
Message = result.message;
|
||||
StackTrace = result.stackTrace;
|
||||
AssertCount = result.assertCount;
|
||||
FailCount = result.failCount;
|
||||
PassCount = result.passCount;
|
||||
SkipCount = result.skipCount;
|
||||
InconclusiveCount = result.inconclusiveCount;
|
||||
HasChildren = result.hasChildren;
|
||||
Output = result.output;
|
||||
Children = result.childrenIds.Select(childId =>
|
||||
new TestResultAdaptor(allData.results.First(r => r.testId == childId), allData)).ToArray();
|
||||
if (!string.IsNullOrEmpty(result.xml))
|
||||
{
|
||||
m_Node = TNode.FromXml(result.xml);
|
||||
}
|
||||
}
|
||||
|
||||
public ITestAdaptor Test { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public string FullName { get; private set; }
|
||||
public string ResultState { get; private set; }
|
||||
public TestStatus TestStatus { get; private set; }
|
||||
public double Duration { get; private set; }
|
||||
public DateTime StartTime { get; private set; }
|
||||
public DateTime EndTime { get; private set; }
|
||||
public string Message { get; private set; }
|
||||
public string StackTrace { get; private set; }
|
||||
public int AssertCount { get; private set; }
|
||||
public int FailCount { get; private set; }
|
||||
public int PassCount { get; private set; }
|
||||
public int SkipCount { get; private set; }
|
||||
public int InconclusiveCount { get; private set; }
|
||||
public bool HasChildren { get; private set; }
|
||||
public IEnumerable<ITestResultAdaptor> Children { get; private set; }
|
||||
public string Output { get; private set; }
|
||||
|
||||
public TNode ToXml()
|
||||
{
|
||||
if (m_Node == null)
|
||||
{
|
||||
m_Node = m_Result.ToXml(true);
|
||||
}
|
||||
|
||||
return m_Node;
|
||||
}
|
||||
|
||||
internal int RetryIteration { get; set; }
|
||||
internal int RepeatIteration { get; set; }
|
||||
|
||||
private static TestStatus ParseTestStatus(NUnit.Framework.Interfaces.TestStatus testStatus)
|
||||
{
|
||||
return (TestStatus)Enum.Parse(typeof(TestStatus), testStatus.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d061ada5d3169454daf54243390b5fdb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
[Serializable]
|
||||
internal class TestRunProgress
|
||||
{
|
||||
[SerializeField]
|
||||
public string RunGuid;
|
||||
[SerializeField]
|
||||
public ExecutionSettings ExecutionSettings;
|
||||
|
||||
[SerializeField]
|
||||
public bool HasFinished;
|
||||
|
||||
[SerializeField]
|
||||
public float Progress;
|
||||
[SerializeField]
|
||||
public string CurrentStepName;
|
||||
[SerializeField]
|
||||
public string CurrentStageName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a06b6fbaa6f4191aa47b7e31242f061
|
||||
timeCreated: 1659007168
|
||||
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor.TestTools.TestRunner.CommandLineTest;
|
||||
using UnityEditor.TestTools.TestRunner.TestRun;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.TestTools.NUnitExtensions;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>The TestRunnerApi retrieves and runs tests programmatically from code inside the project, or inside other packages. TestRunnerApi is a [ScriptableObject](https://docs.unity3d.com/ScriptReference/ScriptableObject.html).
|
||||
/// You can initialize the API like this:</para>
|
||||
/// <code>
|
||||
/// var testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>();
|
||||
/// </code>
|
||||
/// <para>
|
||||
/// Note: You can subscribe and receive test results in one instance of the API, even if the run starts from another instance.
|
||||
/// The TestRunnerApi supports the following workflows:
|
||||
/// - [How to run tests programmatically](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-run-tests.html)
|
||||
/// - [How to get test results](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-get-test-results.html)
|
||||
/// - [How to retrieve the list of tests](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-retrieve-test-list.html)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class TestRunnerApi : ScriptableObject, ITestRunnerApi
|
||||
{
|
||||
internal static ICallbacksHolder callbacksHolder;
|
||||
private static ICallbacksHolder CallbacksHolder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (callbacksHolder == null)
|
||||
{
|
||||
callbacksHolder = Api.CallbacksHolder.instance;
|
||||
}
|
||||
|
||||
return callbacksHolder;
|
||||
}
|
||||
}
|
||||
|
||||
internal static ITestJobDataHolder testJobDataHolder;
|
||||
|
||||
private static ITestJobDataHolder m_testJobDataHolder
|
||||
{
|
||||
get { return testJobDataHolder ?? (testJobDataHolder = TestJobDataHolder.instance); }
|
||||
}
|
||||
|
||||
internal Func<ExecutionSettings,string> ScheduleJob = executionSettings =>
|
||||
{
|
||||
var runner = new TestJobRunner();
|
||||
return runner.RunJob(new TestJobData(executionSettings));
|
||||
};
|
||||
/// <summary>
|
||||
/// Starts a test run with a given set of executionSettings.
|
||||
/// </summary>
|
||||
/// <param name="executionSettings">Set of <see cref="ExecutionSettings"/></param>
|
||||
/// <returns>A GUID that identifies the TestJobData.</returns>
|
||||
public string Execute(ExecutionSettings executionSettings)
|
||||
{
|
||||
if (executionSettings == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(executionSettings));
|
||||
}
|
||||
|
||||
if ((executionSettings.filters == null || executionSettings.filters.Length == 0) && executionSettings.filter != null)
|
||||
{
|
||||
// Map filter (singular) to filters (plural), for backwards compatibility.
|
||||
executionSettings.filters = new[] { executionSettings.filter };
|
||||
}
|
||||
|
||||
if (executionSettings.targetPlatform == null && executionSettings.filters != null &&
|
||||
executionSettings.filters.Length > 0)
|
||||
{
|
||||
executionSettings.targetPlatform = executionSettings.filters[0].targetPlatform;
|
||||
}
|
||||
|
||||
if (executionSettings.featureFlags == null)
|
||||
{
|
||||
executionSettings.featureFlags = new FeatureFlags();
|
||||
}
|
||||
|
||||
return ScheduleJob(executionSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up a given instance of <see cref="ICallbacks"/> to be invoked on test runs.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Generic representing a type of callback.
|
||||
/// </typeparam>
|
||||
/// <param name="testCallbacks">
|
||||
/// The test callbacks to be invoked.
|
||||
/// </param>
|
||||
/// <param name="priority">
|
||||
/// Sets the order in which the callbacks are invoked, starting with the highest value first.
|
||||
/// </param>
|
||||
public void RegisterCallbacks<T>(T testCallbacks, int priority = 0) where T : ICallbacks
|
||||
{
|
||||
RegisterTestCallback(testCallbacks, priority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up a given instance of <see cref="ICallbacks"/> to be invoked on test runs.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Generic representing a type of callback.
|
||||
/// </typeparam>
|
||||
/// <param name="testCallbacks">The test callbacks to be invoked</param>
|
||||
/// <param name="priority">
|
||||
/// Sets the order in which the callbacks are invoked, starting with the highest value first.
|
||||
/// </param>
|
||||
public static void RegisterTestCallback<T>(T testCallbacks, int priority = 0) where T : ICallbacks
|
||||
{
|
||||
if (testCallbacks == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(testCallbacks));
|
||||
}
|
||||
|
||||
CallbacksHolder.Add(testCallbacks, priority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister an instance of <see cref="ICallbacks"/> to no longer receive callbacks from test runs.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Generic representing a type of callback.
|
||||
/// </typeparam>
|
||||
/// <param name="testCallbacks">The test callbacks to unregister.</param>
|
||||
public void UnregisterCallbacks<T>(T testCallbacks) where T : ICallbacks
|
||||
{
|
||||
UnregisterTestCallback(testCallbacks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister an instance of <see cref="ICallbacks"/> to no longer receive callbacks from test runs.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Generic representing a type of callback.
|
||||
/// </typeparam>
|
||||
/// <param name="testCallbacks">The test callbacks to unregister.</param>
|
||||
public static void UnregisterTestCallback<T>(T testCallbacks) where T : ICallbacks
|
||||
{
|
||||
if (testCallbacks == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(testCallbacks));
|
||||
}
|
||||
|
||||
CallbacksHolder.Remove(testCallbacks);
|
||||
}
|
||||
|
||||
internal void RetrieveTestList(ExecutionSettings executionSettings, Action<ITestAdaptor> callback)
|
||||
{
|
||||
if (executionSettings == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(executionSettings));
|
||||
}
|
||||
|
||||
var firstFilter = executionSettings.filters?.FirstOrDefault() ?? executionSettings.filter;
|
||||
RetrieveTestList(firstFilter.testMode, callback);
|
||||
}
|
||||
/// <summary>
|
||||
/// Retrieve the full test tree as ITestAdaptor for a given test mode. This is obsolete. Use TestRunnerApi.RetrieveTestTree instead.
|
||||
/// </summary>
|
||||
/// <param name="testMode">The TestMode to retrieve the test list for.</param>
|
||||
/// <param name="callback">A callback that is invoked when the test tree is retrieved.</param>
|
||||
public void RetrieveTestList(TestMode testMode, Action<ITestAdaptor> callback)
|
||||
{
|
||||
if (callback == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(callback));
|
||||
}
|
||||
|
||||
var platform = ParseTestMode(testMode);
|
||||
var testAssemblyProvider = new EditorLoadedTestAssemblyProvider(new EditorCompilationInterfaceProxy(), new EditorAssembliesProxy());
|
||||
var testAdaptorFactory = new TestAdaptorFactory();
|
||||
var testListCache = new TestListCache(testAdaptorFactory, new RemoteTestResultDataFactory(), TestListCacheData.instance);
|
||||
var testListProvider = new TestListProvider(testAssemblyProvider, new UnityTestAssemblyBuilder(null, 0));
|
||||
var cachedTestListProvider = new CachingTestListProvider(testListProvider, testListCache, testAdaptorFactory);
|
||||
|
||||
var job = new TestListJob(cachedTestListProvider, platform, testRoot =>
|
||||
{
|
||||
callback(testRoot);
|
||||
});
|
||||
job.Start();
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// Save a given set of ITestResultAdaptor in [NUnit XML Format](https://docs.nunit.org/articles/nunit/technical-notes/usage/Test-Result-XML-Format.html) to a file at the provided file path. Any matching existing file is overwritten.
|
||||
/// </summary>
|
||||
/// <param name="results">Test results to write in a file.</param>
|
||||
/// <param name="xmlFilePath">An xml file path relative to the project folder.</param>
|
||||
public static void SaveResultToFile(ITestResultAdaptor results, string xmlFilePath)
|
||||
{
|
||||
var resultsWriter = new ResultsWriter();
|
||||
resultsWriter.WriteResultToFile(results, xmlFilePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel the test run with a given guid string. The guid string can be retrieved when executing the test run. The test run may take multiple frames to finish cleaning up from the test run. Any current active test will be marked as "Canceled" and any other remaining tests marked as "NotRun".
|
||||
/// </summary>
|
||||
/// <param name="guid">Test run guid string.</param>
|
||||
/// <returns>A boolean indicating whether canceling of the given job was successful. Canceling of a job will not be a success if no test job is found matching the guid, if the job is not currently or the job is already canceling.</returns>
|
||||
public static bool CancelTestRun(string guid)
|
||||
{
|
||||
var runner = m_testJobDataHolder.GetRunner(guid);
|
||||
if (runner == null || !runner.IsRunningJob())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return runner.CancelRun();
|
||||
}
|
||||
|
||||
internal static bool IsRunActive()
|
||||
{
|
||||
return m_testJobDataHolder.GetAllRunners().Any(r => r.GetData().isRunning);
|
||||
}
|
||||
|
||||
private static TestPlatform ParseTestMode(TestMode testMode)
|
||||
{
|
||||
return (((testMode & TestMode.EditMode) == TestMode.EditMode) ? TestPlatform.EditMode : 0) | (((testMode & TestMode.PlayMode) == TestMode.PlayMode) ? TestPlatform.PlayMode : 0);
|
||||
}
|
||||
|
||||
internal class RunProgressChangedEvent : UnityEvent<TestRunProgress> {}
|
||||
internal static RunProgressChangedEvent runProgressChanged = new RunProgressChangedEvent();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68993ba529ae04440916cb7c23bf3279
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// The TestStatus enum indicates the test result status.
|
||||
/// </summary>
|
||||
public enum TestStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The test ran with an inconclusive result.
|
||||
/// </summary>
|
||||
Inconclusive,
|
||||
|
||||
/// <summary>
|
||||
/// The test was skipped.
|
||||
/// </summary>
|
||||
Skipped,
|
||||
|
||||
/// <summary>
|
||||
/// The test ran and passed.
|
||||
/// </summary>
|
||||
Passed,
|
||||
|
||||
/// <summary>
|
||||
/// The test ran and failed.
|
||||
/// </summary>
|
||||
Failed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ec94545c5b00344c9bd8e691f15d799
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: AssemblyTitle("UnityEditor.TestRunner")]
|
||||
[assembly: InternalsVisibleTo("Assembly-CSharp-Editor-testable")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("Unity.PerformanceTesting.Editor")]
|
||||
[assembly: InternalsVisibleTo("Unity.IntegrationTests")]
|
||||
[assembly: InternalsVisibleTo("UnityEditor.TestRunner.Tests")]
|
||||
[assembly: InternalsVisibleTo("Unity.PackageManagerUI.Develop.Editor")]
|
||||
[assembly: InternalsVisibleTo("Unity.PackageManagerUI.Develop.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.PackageValidationSuite.Editor")]
|
||||
|
||||
[assembly: AssemblyVersion("1.0.0")]
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9db19a04003fca7439552acd4de9baa1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7602252bdb82b8d45ae3483c3a00d3e1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestRunner.CommandLineParser
|
||||
{
|
||||
internal class CommandLineOption : ICommandLineOption
|
||||
{
|
||||
private 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);
|
||||
}
|
||||
|
||||
private static string[] SplitStringToArray(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3529368f4cd0424a89aa51080a16b06
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestRunner.CommandLineParser
|
||||
{
|
||||
internal class CommandLineOptionSet
|
||||
{
|
||||
private 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 139c5eac101a4dc4fb3098e30c29f15e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestRunner.CommandLineParser
|
||||
{
|
||||
internal interface ICommandLineOption
|
||||
{
|
||||
string ArgName { get; }
|
||||
void ApplyValue(string value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f445ca0c614a846449fcd8ae648c24e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b477d1f29b65a674e9d5cdab4eb72b01
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.TestRunner.TestLaunchers;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class Executer : IExecuter
|
||||
{
|
||||
internal IRunData runData = RunData.instance;
|
||||
|
||||
private ITestRunnerApi m_TestRunnerApi;
|
||||
private ISettingsBuilder m_SettingsBuilder;
|
||||
private Action<string, object[]> m_LogErrorFormat;
|
||||
private Action<Exception> m_LogException;
|
||||
private Action<string> m_LogMessage;
|
||||
private Action<int> m_ExitEditorApplication;
|
||||
private Func<bool> m_ScriptCompilationFailedCheck;
|
||||
private Func<bool> m_IsRunActive;
|
||||
|
||||
public Executer(ITestRunnerApi testRunnerApi, ISettingsBuilder settingsBuilder, Action<string, object[]> logErrorFormat, Action<Exception> logException, Action<string> logMessage, Action<int> exitEditorApplication, Func<bool> scriptCompilationFailedCheck, Func<bool> isRunActive)
|
||||
{
|
||||
m_TestRunnerApi = testRunnerApi;
|
||||
m_SettingsBuilder = settingsBuilder;
|
||||
m_LogErrorFormat = logErrorFormat;
|
||||
m_LogException = logException;
|
||||
m_LogMessage = logMessage;
|
||||
m_ExitEditorApplication = exitEditorApplication;
|
||||
m_ScriptCompilationFailedCheck = scriptCompilationFailedCheck;
|
||||
m_IsRunActive = isRunActive;
|
||||
}
|
||||
|
||||
public string InitializeAndExecuteRun(string[] commandLineArgs)
|
||||
{
|
||||
Api.ExecutionSettings executionSettings;
|
||||
try
|
||||
{
|
||||
executionSettings = m_SettingsBuilder.BuildApiExecutionSettings(commandLineArgs);
|
||||
if (executionSettings.targetPlatform.HasValue)
|
||||
RemotePlayerLogController.instance.SetBuildTarget(executionSettings.targetPlatform.Value);
|
||||
}
|
||||
catch (SetupException exception)
|
||||
{
|
||||
HandleSetupException(exception);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// It is important that the message starts with "Running tests for ", otherwise TestCleanConsole will fail.
|
||||
m_LogMessage($"Running tests for {executionSettings}");
|
||||
return m_TestRunnerApi.Execute(executionSettings);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
m_LogException(exception);
|
||||
ExitApplication(ReturnCodes.RunError, "Exception when starting test run.");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void ExitIfRunIsCompleted()
|
||||
{
|
||||
if (m_IsRunActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var runState = runData.RunState;
|
||||
var returnCode = s_StateReturnCodes[runState];
|
||||
var reason = s_StateMessages[runState] ?? runData.RunErrorMessage;
|
||||
ExitApplication(returnCode, reason);
|
||||
}
|
||||
|
||||
private void ExitApplication(ReturnCodes returnCode, string reason)
|
||||
{
|
||||
var returnCodeInt = (int)returnCode;
|
||||
|
||||
m_LogMessage($"Test run completed. Exiting with code {returnCodeInt} ({returnCode}). {reason}");
|
||||
|
||||
m_ExitEditorApplication(returnCodeInt);
|
||||
}
|
||||
|
||||
public ExecutionSettings BuildExecutionSettings(string[] commandLineArgs)
|
||||
{
|
||||
return m_SettingsBuilder.BuildExecutionSettings(commandLineArgs);
|
||||
}
|
||||
|
||||
internal enum ReturnCodes
|
||||
{
|
||||
Ok = 0,
|
||||
Failed = 2,
|
||||
RunError = 3,
|
||||
PlatformNotFoundReturnCode = 4
|
||||
}
|
||||
|
||||
public void SetUpCallbacks(ExecutionSettings executionSettings)
|
||||
{
|
||||
RemotePlayerLogController.instance.SetLogsDirectory(executionSettings.DeviceLogsDirectory);
|
||||
|
||||
var resultSavingCallback = ScriptableObject.CreateInstance<ResultsSavingCallbacks>();
|
||||
resultSavingCallback.m_ResultFilePath = executionSettings.TestResultsFile;
|
||||
|
||||
var logSavingCallback = ScriptableObject.CreateInstance<LogSavingCallbacks>();
|
||||
|
||||
TestRunnerApi.RegisterTestCallback(resultSavingCallback);
|
||||
TestRunnerApi.RegisterTestCallback(logSavingCallback);
|
||||
TestRunnerApi.RegisterTestCallback(new RunStateCallbacks());
|
||||
}
|
||||
|
||||
public void ExitOnCompileErrors()
|
||||
{
|
||||
if (m_ScriptCompilationFailedCheck())
|
||||
{
|
||||
var handling = s_ExceptionHandlingMapping.First(h => h.m_ExceptionType == SetupException.ExceptionType.ScriptCompilationFailed);
|
||||
m_LogErrorFormat(handling.m_Message, new object[0]);
|
||||
ExitApplication(handling.m_ReturnCode, handling.m_Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSetupException(SetupException exception)
|
||||
{
|
||||
ExceptionHandling handling = s_ExceptionHandlingMapping.FirstOrDefault(h => h.m_ExceptionType == exception.Type) ?? new ExceptionHandling(exception.Type, "Unknown command line test run error. " + exception.Type, ReturnCodes.RunError);
|
||||
m_LogErrorFormat(handling.m_Message, exception.Details);
|
||||
ExitApplication(handling.m_ReturnCode, handling.m_Message);
|
||||
}
|
||||
|
||||
private class ExceptionHandling
|
||||
{
|
||||
internal SetupException.ExceptionType m_ExceptionType;
|
||||
internal string m_Message;
|
||||
internal ReturnCodes m_ReturnCode;
|
||||
public ExceptionHandling(SetupException.ExceptionType exceptionType, string message, ReturnCodes returnCode)
|
||||
{
|
||||
m_ExceptionType = exceptionType;
|
||||
m_Message = message;
|
||||
m_ReturnCode = returnCode;
|
||||
}
|
||||
}
|
||||
|
||||
private static ExceptionHandling[] s_ExceptionHandlingMapping = {
|
||||
new ExceptionHandling(SetupException.ExceptionType.ScriptCompilationFailed, "Scripts had compilation errors.", ReturnCodes.RunError),
|
||||
new ExceptionHandling(SetupException.ExceptionType.PlatformNotFound, "Test platform not found ({0}).", ReturnCodes.PlatformNotFoundReturnCode),
|
||||
new ExceptionHandling(SetupException.ExceptionType.TestSettingsFileNotFound, "Test settings file not found at {0}.", ReturnCodes.RunError),
|
||||
new ExceptionHandling(SetupException.ExceptionType.OrderedTestListFileNotFound, "Ordered test list file not found at {0}.", ReturnCodes.RunError)
|
||||
};
|
||||
|
||||
private static IDictionary<TestRunState, string> s_StateMessages = new Dictionary<TestRunState, string>()
|
||||
{
|
||||
{TestRunState.NoCallbacksReceived, "No callbacks received."},
|
||||
{TestRunState.OneOrMoreTestsExecutedWithNoFailures, "Run completed."},
|
||||
{TestRunState.OneOrMoreTestsExecutedWithOneOrMoreFailed, "One or more tests failed."},
|
||||
{TestRunState.CompletedJobWithoutAnyTestsExecuted, "No tests were executed."},
|
||||
{TestRunState.RunError, null}
|
||||
};
|
||||
|
||||
private static IDictionary<TestRunState, ReturnCodes> s_StateReturnCodes = new Dictionary<TestRunState, ReturnCodes>()
|
||||
{
|
||||
{TestRunState.NoCallbacksReceived, ReturnCodes.RunError},
|
||||
{TestRunState.OneOrMoreTestsExecutedWithNoFailures, ReturnCodes.Ok},
|
||||
{TestRunState.OneOrMoreTestsExecutedWithOneOrMoreFailed, ReturnCodes.Failed},
|
||||
{TestRunState.CompletedJobWithoutAnyTestsExecuted, ReturnCodes.Ok},
|
||||
{TestRunState.RunError, ReturnCodes.RunError}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 083c6a3a5426382449369ddc12b691d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
[Serializable]
|
||||
internal class ExecutionSettings
|
||||
{
|
||||
public string TestResultsFile;
|
||||
public string DeviceLogsDirectory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3a75354f6ceac94ca15ca9d96593290
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal interface IExecuter
|
||||
{
|
||||
string InitializeAndExecuteRun(string[] commandLineArgs);
|
||||
void ExitIfRunIsCompleted();
|
||||
ExecutionSettings BuildExecutionSettings(string[] commandLineArgs);
|
||||
void SetUpCallbacks(ExecutionSettings executionSettings);
|
||||
void ExitOnCompileErrors();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3297738ef11b478a8b7ee802953ae768
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal interface IRunData
|
||||
{
|
||||
bool IsRunning { get; set; }
|
||||
ExecutionSettings ExecutionSettings { get; set; }
|
||||
string RunId { get; set; }
|
||||
TestRunState RunState { get; set; }
|
||||
string RunErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d56e34a052c646e39a3317c404303e5f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal interface ISettingsBuilder
|
||||
{
|
||||
Api.ExecutionSettings BuildApiExecutionSettings(string[] commandLineArgs);
|
||||
ExecutionSettings BuildExecutionSettings(string[] commandLineArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a13cbeb2099aca47bb456f49845f86c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using UnityEditor.TestRunner.TestLaunchers;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
[Serializable]
|
||||
internal class LogSavingCallbacks : ScriptableObject, ICallbacks
|
||||
{
|
||||
public void RunStarted(ITestAdaptor testsToRun)
|
||||
{
|
||||
RemotePlayerLogController.instance.StartLogWriters();
|
||||
}
|
||||
|
||||
public virtual void RunFinished(ITestResultAdaptor testResults)
|
||||
{
|
||||
RemotePlayerLogController.instance.StopLogWriters();
|
||||
}
|
||||
|
||||
public void TestStarted(ITestAdaptor test)
|
||||
{
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResultAdaptor result)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d20eedbe40f0ce41a4c4f633f225de8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor.DeploymentTargets;
|
||||
using UnityEditor.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class LogWriter : IDisposable
|
||||
{
|
||||
private string m_LogsDirectory;
|
||||
private string m_DeviceID;
|
||||
private Dictionary<string, StreamWriter> m_LogStreams;
|
||||
private DeploymentTargetLogger m_Logger;
|
||||
|
||||
internal LogWriter(string logsDirectory, string deviceID, DeploymentTargetLogger logger)
|
||||
{
|
||||
m_LogStreams = new Dictionary<string, StreamWriter>();
|
||||
m_Logger = logger;
|
||||
m_LogsDirectory = logsDirectory;
|
||||
m_DeviceID = deviceID;
|
||||
|
||||
logger.logMessage += WriteLogToFile;
|
||||
}
|
||||
|
||||
private void WriteLogToFile(string id, string logLine)
|
||||
{
|
||||
StreamWriter logStream;
|
||||
var streamExists = m_LogStreams.TryGetValue(id, out logStream);
|
||||
if (!streamExists)
|
||||
{
|
||||
var filePath = GetLogFilePath(m_LogsDirectory, m_DeviceID, id);
|
||||
logStream = CreateLogFile(filePath);
|
||||
|
||||
m_LogStreams.Add(id, logStream);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (logLine != null)
|
||||
logStream.WriteLine(logLine);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Writing {id} log failed.");
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
m_Logger.Stop();
|
||||
foreach (var logStream in m_LogStreams)
|
||||
{
|
||||
logStream.Value.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
private StreamWriter CreateLogFile(string path)
|
||||
{
|
||||
Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "Creating {0} device log: {1}", m_DeviceID, path);
|
||||
StreamWriter streamWriter = null;
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
streamWriter = File.CreateText(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Creating device log {path} file failed.");
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
|
||||
return streamWriter;
|
||||
}
|
||||
|
||||
private string GetLogFilePath(string lgosDirectory, string deviceID, string logID)
|
||||
{
|
||||
var fileName = "Device-" + deviceID + "-" + logID + ".txt";
|
||||
fileName = string.Join("_", fileName.Split(Path.GetInvalidFileNameChars()));
|
||||
return Paths.Combine(lgosDirectory, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05778dd1de4433d418793b6f3d3c18cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEditor.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
[Serializable]
|
||||
internal class ResultsSavingCallbacks : ScriptableObject, ICallbacks
|
||||
{
|
||||
[SerializeField]
|
||||
public string m_ResultFilePath;
|
||||
|
||||
public ResultsSavingCallbacks()
|
||||
{
|
||||
m_ResultFilePath = GetDefaultResultFilePath();
|
||||
}
|
||||
|
||||
public void RunStarted(ITestAdaptor testsToRun)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void RunFinished(ITestResultAdaptor testResults)
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_ResultFilePath))
|
||||
{
|
||||
m_ResultFilePath = GetDefaultResultFilePath();
|
||||
}
|
||||
|
||||
TestRunnerApi.SaveResultToFile(testResults, m_ResultFilePath);
|
||||
}
|
||||
|
||||
public void TestStarted(ITestAdaptor test)
|
||||
{
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResultAdaptor result)
|
||||
{
|
||||
}
|
||||
|
||||
private static string GetDefaultResultFilePath()
|
||||
{
|
||||
var fileName = "TestResults-" + DateTime.Now.Ticks + ".xml";
|
||||
var projectPath = Directory.GetCurrentDirectory();
|
||||
return Paths.Combine(projectPath, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef563c5a6ecf64d4193dc144cb7d472a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class RunData : ScriptableSingleton<RunData>, IRunData
|
||||
{
|
||||
private bool isRunning;
|
||||
private ExecutionSettings executionSettings;
|
||||
private string runId;
|
||||
private TestRunState runState;
|
||||
private string runErrorMessage;
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get { return isRunning; }
|
||||
set { isRunning = value; }
|
||||
}
|
||||
|
||||
public ExecutionSettings ExecutionSettings
|
||||
{
|
||||
get { return executionSettings; }
|
||||
set { executionSettings = value; }
|
||||
}
|
||||
|
||||
public string RunId
|
||||
{
|
||||
get { return runId; }
|
||||
set { runId = value; }
|
||||
}
|
||||
|
||||
public TestRunState RunState
|
||||
{
|
||||
get { return runState; }
|
||||
set { runState = value; }
|
||||
}
|
||||
|
||||
public string RunErrorMessage
|
||||
{
|
||||
get { return runErrorMessage; }
|
||||
set { runErrorMessage = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f8c1075884df0249b80e23a0598f9c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class RunSettings : ITestRunSettings
|
||||
{
|
||||
private ITestSettings m_TestSettings;
|
||||
public RunSettings(ITestSettings testSettings)
|
||||
{
|
||||
m_TestSettings = testSettings;
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (m_TestSettings != null)
|
||||
{
|
||||
m_TestSettings.SetupProjectParameters();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_TestSettings != null)
|
||||
{
|
||||
m_TestSettings.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59d3f5586b341a74c84c8f72144a4568
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class RunStateCallbacks : IErrorCallbacks
|
||||
{
|
||||
internal IRunData runData = RunData.instance;
|
||||
internal static bool preventExit;
|
||||
|
||||
public void RunFinished(ITestResultAdaptor testResults)
|
||||
{
|
||||
if (preventExit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (runData.RunState == TestRunState.NoCallbacksReceived)
|
||||
{
|
||||
runData.RunState = TestRunState.CompletedJobWithoutAnyTestsExecuted;
|
||||
}
|
||||
}
|
||||
|
||||
public void TestStarted(ITestAdaptor test)
|
||||
{
|
||||
if (!test.IsSuite && runData.RunState == TestRunState.NoCallbacksReceived)
|
||||
{
|
||||
runData.RunState = TestRunState.OneOrMoreTestsExecutedWithNoFailures;
|
||||
}
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResultAdaptor result)
|
||||
{
|
||||
if (!result.Test.IsSuite && (result.TestStatus == TestStatus.Failed || result.TestStatus == TestStatus.Inconclusive))
|
||||
{
|
||||
runData.RunState = TestRunState.OneOrMoreTestsExecutedWithOneOrMoreFailed;
|
||||
}
|
||||
}
|
||||
|
||||
public void RunStarted(ITestAdaptor testsToRun)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnError(string message)
|
||||
{
|
||||
runData.RunState = TestRunState.RunError;
|
||||
runData.RunErrorMessage = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43db38ca7188900429bf8be95ebbe197
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor.TestRunner.CommandLineParser;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class SettingsBuilder : ISettingsBuilder
|
||||
{
|
||||
private ITestSettingsDeserializer m_TestSettingsDeserializer;
|
||||
private Action<string> m_LogAction;
|
||||
private Action<string> m_LogWarningAction;
|
||||
internal Func<string, bool> fileExistsCheck = File.Exists;
|
||||
private Func<bool> m_ScriptCompilationFailedCheck;
|
||||
internal Func<string, string[]> readAllLines = filePath => File.ReadAllText(filePath).Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
public SettingsBuilder(ITestSettingsDeserializer testSettingsDeserializer, Action<string> logAction, Action<string> logWarningAction, Func<bool> scriptCompilationFailedCheck)
|
||||
{
|
||||
m_LogAction = logAction;
|
||||
m_LogWarningAction = logWarningAction;
|
||||
m_ScriptCompilationFailedCheck = scriptCompilationFailedCheck;
|
||||
m_TestSettingsDeserializer = testSettingsDeserializer;
|
||||
}
|
||||
|
||||
public Api.ExecutionSettings BuildApiExecutionSettings(string[] commandLineArgs)
|
||||
{
|
||||
var quit = false;
|
||||
string testPlatform = TestMode.EditMode.ToString();
|
||||
string[] testFilters = null;
|
||||
string[] testCategories = null;
|
||||
string testSettingsFilePath = null;
|
||||
int? playerHeartbeatTimeout = null;
|
||||
bool runSynchronously = false;
|
||||
string[] testAssemblyNames = null;
|
||||
string buildPlayerPath = string.Empty;
|
||||
string orderedTestListFilePath = null;
|
||||
int retry = 0;
|
||||
int repeat = 0;
|
||||
int randomOrderSeed = 0;
|
||||
|
||||
|
||||
var optionSet = new CommandLineOptionSet(
|
||||
new CommandLineOption("quit", () => { quit = true; }),
|
||||
new CommandLineOption("testPlatform", platform => { testPlatform = platform; }),
|
||||
new CommandLineOption("editorTestsFilter", filters => { testFilters = filters; }),
|
||||
new CommandLineOption("testFilter", filters => { testFilters = filters; }),
|
||||
new CommandLineOption("editorTestsCategories", catagories => { testCategories = catagories; }),
|
||||
new CommandLineOption("testCategory", catagories => { testCategories = catagories; }),
|
||||
new CommandLineOption("testSettingsFile", settingsFilePath => { testSettingsFilePath = settingsFilePath; }),
|
||||
new CommandLineOption("playerHeartbeatTimeout", timeout => { playerHeartbeatTimeout = int.Parse(timeout); }),
|
||||
new CommandLineOption("runSynchronously", () => { runSynchronously = true; }),
|
||||
new CommandLineOption("assemblyNames", assemblyNames => { testAssemblyNames = assemblyNames; }),
|
||||
new CommandLineOption("buildPlayerPath", buildPath => { buildPlayerPath = buildPath; }),
|
||||
new CommandLineOption("orderedTestListFile", filePath => { orderedTestListFilePath = filePath; }),
|
||||
new CommandLineOption("randomOrderSeed", seed => { randomOrderSeed = int.Parse(seed);}),
|
||||
new CommandLineOption("retry", n => { retry = int.Parse(n); }),
|
||||
new CommandLineOption("repeat", n => { repeat = int.Parse(n); })
|
||||
);
|
||||
optionSet.Parse(commandLineArgs);
|
||||
|
||||
DisplayQuitWarningIfQuitIsGiven(quit);
|
||||
|
||||
CheckForScriptCompilationErrors();
|
||||
|
||||
var testSettings = GetTestSettings(testSettingsFilePath);
|
||||
var filter = new Filter
|
||||
{
|
||||
testMode = testPlatform.ToLower() == "editmode" ? TestMode.EditMode : TestMode.PlayMode,
|
||||
groupNames = testFilters,
|
||||
categoryNames = testCategories,
|
||||
assemblyNames = testAssemblyNames
|
||||
};
|
||||
|
||||
var settings = new Api.ExecutionSettings
|
||||
{
|
||||
filters = new []{ filter },
|
||||
overloadTestRunSettings = new RunSettings(testSettings),
|
||||
ignoreTests = testSettings?.ignoreTests,
|
||||
featureFlags = testSettings?.featureFlags,
|
||||
targetPlatform = GetBuildTarget(testPlatform),
|
||||
runSynchronously = runSynchronously,
|
||||
playerSavePath = buildPlayerPath,
|
||||
orderedTestNames = GetOrderedTestList(orderedTestListFilePath),
|
||||
repeatCount = repeat,
|
||||
retryCount = retry,
|
||||
randomOrderSeed = randomOrderSeed
|
||||
};
|
||||
|
||||
if (playerHeartbeatTimeout != null)
|
||||
{
|
||||
settings.playerHeartbeatTimeout = playerHeartbeatTimeout.Value;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public ExecutionSettings BuildExecutionSettings(string[] commandLineArgs)
|
||||
{
|
||||
string resultFilePath = null;
|
||||
string deviceLogsDirectory = null;
|
||||
|
||||
var optionSet = new CommandLineOptionSet(
|
||||
new CommandLineOption("editorTestsResultFile", filePath => { resultFilePath = filePath; }),
|
||||
new CommandLineOption("testResults", filePath => { resultFilePath = filePath; }),
|
||||
new CommandLineOption("deviceLogs", dirPath => { deviceLogsDirectory = dirPath; })
|
||||
);
|
||||
optionSet.Parse(commandLineArgs);
|
||||
|
||||
return new ExecutionSettings
|
||||
{
|
||||
TestResultsFile = resultFilePath,
|
||||
DeviceLogsDirectory = deviceLogsDirectory
|
||||
};
|
||||
}
|
||||
|
||||
private void DisplayQuitWarningIfQuitIsGiven(bool quitIsGiven)
|
||||
{
|
||||
if (quitIsGiven)
|
||||
{
|
||||
m_LogWarningAction("Running tests from command line arguments will not work when \"quit\" is specified.");
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckForScriptCompilationErrors()
|
||||
{
|
||||
if (m_ScriptCompilationFailedCheck())
|
||||
{
|
||||
throw new SetupException(SetupException.ExceptionType.ScriptCompilationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
private ITestSettings GetTestSettings(string testSettingsFilePath)
|
||||
{
|
||||
ITestSettings testSettings = null;
|
||||
if (!string.IsNullOrEmpty(testSettingsFilePath))
|
||||
{
|
||||
if (!fileExistsCheck(testSettingsFilePath))
|
||||
{
|
||||
throw new SetupException(SetupException.ExceptionType.TestSettingsFileNotFound, testSettingsFilePath);
|
||||
}
|
||||
|
||||
testSettings = m_TestSettingsDeserializer.GetSettingsFromJsonFile(testSettingsFilePath);
|
||||
}
|
||||
return testSettings;
|
||||
}
|
||||
|
||||
private string[] GetOrderedTestList(string orderedTestListFilePath)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(orderedTestListFilePath))
|
||||
{
|
||||
if (!fileExistsCheck(orderedTestListFilePath))
|
||||
{
|
||||
throw new SetupException(SetupException.ExceptionType.OrderedTestListFileNotFound, orderedTestListFilePath);
|
||||
}
|
||||
|
||||
return readAllLines(orderedTestListFilePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static BuildTarget? GetBuildTarget(string testPlatform)
|
||||
{
|
||||
var testPlatformLower = testPlatform.ToLower();
|
||||
if (testPlatformLower == "editmode" || testPlatformLower == "playmode" || testPlatformLower == "editor" ||
|
||||
string.IsNullOrEmpty(testPlatformLower))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return (BuildTarget)Enum.Parse(typeof(BuildTarget), testPlatform, true);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
throw new SetupException(SetupException.ExceptionType.PlatformNotFound, testPlatform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7468a027a77337478e133b40b42b4f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class SetupException : Exception
|
||||
{
|
||||
public ExceptionType Type { get; }
|
||||
public object[] Details { get; }
|
||||
|
||||
public SetupException(ExceptionType type, params object[] details)
|
||||
{
|
||||
Type = type;
|
||||
Details = details;
|
||||
}
|
||||
|
||||
public enum ExceptionType
|
||||
{
|
||||
ScriptCompilationFailed,
|
||||
PlatformNotFound,
|
||||
TestSettingsFileNotFound,
|
||||
OrderedTestListFileNotFound,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63572993f2104574099a48392460b211
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using UnityEditor.TestRunner.CommandLineParser;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class TestStarter
|
||||
{
|
||||
[InitializeOnLoadMethod]
|
||||
internal static void Initialize()
|
||||
{
|
||||
new TestStarter().Init();
|
||||
}
|
||||
|
||||
internal Action<EditorApplication.CallbackFunction> registerEditorUpdateCallback = action =>
|
||||
{
|
||||
EditorApplication.update += action;
|
||||
};
|
||||
internal Action<EditorApplication.CallbackFunction> unregisterEditorUpdateCallback = action =>
|
||||
{
|
||||
EditorApplication.update -= action;
|
||||
};
|
||||
internal Func<bool> isCompiling = () => EditorApplication.isCompiling;
|
||||
internal IRunData runData = RunData.instance;
|
||||
internal Func<string[]> GetCommandLineArgs = Environment.GetCommandLineArgs;
|
||||
|
||||
internal void Init()
|
||||
{
|
||||
if (!ShouldRunTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCompiling())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
executer.ExitOnCompileErrors();
|
||||
|
||||
if (runData.IsRunning)
|
||||
{
|
||||
executer.SetUpCallbacks(runData.ExecutionSettings);
|
||||
registerEditorUpdateCallback(executer.ExitIfRunIsCompleted);
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute the test run on the next editor update to allow other framework components
|
||||
// (the TestJobDataHolder.ResumeRunningJobs method in particular) to register themselves
|
||||
// or modify the test run environment using InitializeOnLoad and InitializeOnLoadMethod calls
|
||||
registerEditorUpdateCallback(InitializeAndExecuteRun);
|
||||
}
|
||||
|
||||
internal void InitializeAndExecuteRun()
|
||||
{
|
||||
unregisterEditorUpdateCallback(InitializeAndExecuteRun);
|
||||
|
||||
runData.IsRunning = true;
|
||||
var commandLineArgs = GetCommandLineArgs();
|
||||
runData.ExecutionSettings = executer.BuildExecutionSettings(commandLineArgs);
|
||||
executer.SetUpCallbacks(runData.ExecutionSettings);
|
||||
runData.RunState = default;
|
||||
runData.RunId = executer.InitializeAndExecuteRun(commandLineArgs);
|
||||
registerEditorUpdateCallback(executer.ExitIfRunIsCompleted);
|
||||
}
|
||||
|
||||
private bool ShouldRunTests()
|
||||
{
|
||||
var shouldRunTests = false;
|
||||
var optionSet = new CommandLineOptionSet(
|
||||
new CommandLineOption("runTests", () => { shouldRunTests = true; }),
|
||||
new CommandLineOption("runEditorTests", () => { shouldRunTests = true; })
|
||||
);
|
||||
optionSet.Parse(GetCommandLineArgs());
|
||||
return shouldRunTests;
|
||||
}
|
||||
|
||||
internal IExecuter m_Executer;
|
||||
private IExecuter executer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Executer == null)
|
||||
{
|
||||
Func<bool> compilationCheck = () => EditorUtility.scriptCompilationFailed;
|
||||
Action<string> actionLogger = msg => { Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, msg); };
|
||||
var apiSettingsBuilder = new SettingsBuilder(new TestSettingsDeserializer(() => new TestSettings()), actionLogger, Debug.LogWarning, compilationCheck);
|
||||
m_Executer = new Executer(ScriptableObject.CreateInstance<TestRunnerApi>(), apiSettingsBuilder, Debug.LogErrorFormat, Debug.LogException, Debug.Log, EditorApplication.Exit, compilationCheck, TestRunnerApi.IsRunActive);
|
||||
}
|
||||
|
||||
return m_Executer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d616d1a494edd144b262cf6cd5e5fda
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal enum TestRunState
|
||||
{
|
||||
/// <summary>
|
||||
/// When the test run has started, but no callbacks from the test runner api have yet been received.
|
||||
/// </summary>
|
||||
NoCallbacksReceived,
|
||||
/// <summary>
|
||||
/// When at least one test started event have been fired for a test node.
|
||||
/// </summary>
|
||||
OneOrMoreTestsExecutedWithNoFailures,
|
||||
/// <summary>
|
||||
/// When at least one test finished event have been fired for a test node and the status is failed.
|
||||
/// </summary>
|
||||
OneOrMoreTestsExecutedWithOneOrMoreFailed,
|
||||
/// <summary>
|
||||
/// When the test job in the test runner api have completed, but no test started events for test nodes has happened. E.g. if there are no valid tests in the project.
|
||||
/// </summary>
|
||||
CompletedJobWithoutAnyTestsExecuted,
|
||||
/// <summary>
|
||||
/// When the test runner api has raised an error during the run.
|
||||
/// </summary>
|
||||
RunError
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 317f06562c344e22991ba4117cf7b71c
|
||||
timeCreated: 1582809279
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e609b27ad2caa14c83dd9951b6c13c6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal class AssetsDatabaseHelper : IAssetsDatabaseHelper
|
||||
{
|
||||
public void OpenAssetInItsDefaultExternalEditor(string assetPath, int line)
|
||||
{
|
||||
var asset = AssetDatabase.LoadMainAssetAtPath(assetPath);
|
||||
AssetDatabase.OpenAsset(asset, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user