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

TestRunnerApi.cs 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. using System;
  2. using System.Linq;
  3. using UnityEditor.TestTools.TestRunner.CommandLineTest;
  4. using UnityEditor.TestTools.TestRunner.TestRun;
  5. using UnityEngine;
  6. using UnityEngine.Events;
  7. using UnityEngine.TestRunner.NUnitExtensions.Runner;
  8. using UnityEngine.TestRunner.TestLaunchers;
  9. using UnityEngine.TestTools;
  10. using UnityEngine.TestTools.NUnitExtensions;
  11. namespace UnityEditor.TestTools.TestRunner.Api
  12. {
  13. /// <summary>
  14. /// 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).
  15. /// You can initialize the API like this:
  16. /// <code>
  17. /// var testRunnerApi = ScriptableObject.CreateInstance&lt;TestRunnerApi&gt;();
  18. /// </code>
  19. /// Note: You can subscribe and receive test results in one instance of the API, even if the run starts from another instance.
  20. /// The TestRunnerApi supports the following workflows:
  21. /// - [How to run tests programmatically](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-run-tests.html)
  22. /// - [How to get test results](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-get-test-results.html)
  23. /// - [How to retrieve the list of tests](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-retrieve-test-list.html)
  24. /// </summary>
  25. public class TestRunnerApi : ScriptableObject, ITestRunnerApi
  26. {
  27. internal static ICallbacksHolder callbacksHolder;
  28. private static ICallbacksHolder CallbacksHolder
  29. {
  30. get
  31. {
  32. if (callbacksHolder == null)
  33. {
  34. callbacksHolder = Api.CallbacksHolder.instance;
  35. }
  36. return callbacksHolder;
  37. }
  38. }
  39. internal static ITestJobDataHolder testJobDataHolder;
  40. private static ITestJobDataHolder m_testJobDataHolder
  41. {
  42. get { return testJobDataHolder ?? (testJobDataHolder = TestJobDataHolder.instance); }
  43. }
  44. internal Func<ExecutionSettings,string> ScheduleJob = executionSettings =>
  45. {
  46. var runner = new TestJobRunner();
  47. return runner.RunJob(new TestJobData(executionSettings));
  48. };
  49. /// <summary>
  50. /// Starts a test run with a given set of executionSettings.
  51. /// </summary>
  52. /// <param name="executionSettings">Set of <see cref="ExecutionSettings"/></param>
  53. /// <returns>A GUID that identifies the TestJobData.</returns>
  54. public string Execute(ExecutionSettings executionSettings)
  55. {
  56. if (executionSettings == null)
  57. {
  58. throw new ArgumentNullException(nameof(executionSettings));
  59. }
  60. if ((executionSettings.filters == null || executionSettings.filters.Length == 0) && executionSettings.filter != null)
  61. {
  62. // Map filter (singular) to filters (plural), for backwards compatibility.
  63. executionSettings.filters = new[] { executionSettings.filter };
  64. }
  65. if (executionSettings.targetPlatform == null && executionSettings.filters != null &&
  66. executionSettings.filters.Length > 0)
  67. {
  68. executionSettings.targetPlatform = executionSettings.filters[0].targetPlatform;
  69. }
  70. if (executionSettings.featureFlags == null)
  71. {
  72. executionSettings.featureFlags = new FeatureFlags();
  73. }
  74. return ScheduleJob(executionSettings);
  75. }
  76. /// <summary>
  77. /// Sets up a given instance of <see cref="ICallbacks"/> to be invoked on test runs.
  78. /// </summary>
  79. /// <typeparam name="T">
  80. /// Generic representing a type of callback.
  81. /// </typeparam>
  82. /// <param name="testCallbacks">
  83. /// The test callbacks to be invoked.
  84. /// </param>
  85. /// <param name="priority">
  86. /// Sets the order in which the callbacks are invoked, starting with the highest value first.
  87. /// </param>
  88. public void RegisterCallbacks<T>(T testCallbacks, int priority = 0) where T : ICallbacks
  89. {
  90. RegisterTestCallback(testCallbacks, priority);
  91. }
  92. /// <summary>
  93. /// Sets up a given instance of <see cref="ICallbacks"/> to be invoked on test runs.
  94. /// </summary>
  95. /// <typeparam name="T">
  96. /// Generic representing a type of callback.
  97. /// </typeparam>
  98. /// <param name="testCallbacks">The test callbacks to be invoked</param>
  99. /// <param name="priority">
  100. /// Sets the order in which the callbacks are invoked, starting with the highest value first.
  101. /// </param>
  102. public static void RegisterTestCallback<T>(T testCallbacks, int priority = 0) where T : ICallbacks
  103. {
  104. if (testCallbacks == null)
  105. {
  106. throw new ArgumentNullException(nameof(testCallbacks));
  107. }
  108. CallbacksHolder.Add(testCallbacks, priority);
  109. }
  110. /// <summary>
  111. /// Unregister an instance of <see cref="ICallbacks"/> to no longer receive callbacks from test runs.
  112. /// </summary>
  113. /// <typeparam name="T">
  114. /// Generic representing a type of callback.
  115. /// </typeparam>
  116. /// <param name="testCallbacks">The test callbacks to unregister.</param>
  117. public void UnregisterCallbacks<T>(T testCallbacks) where T : ICallbacks
  118. {
  119. UnregisterTestCallback(testCallbacks);
  120. }
  121. /// <summary>
  122. /// Unregister an instance of <see cref="ICallbacks"/> to no longer receive callbacks from test runs.
  123. /// </summary>
  124. /// <typeparam name="T">
  125. /// Generic representing a type of callback.
  126. /// </typeparam>
  127. /// <param name="testCallbacks">The test callbacks to unregister.</param>
  128. public static void UnregisterTestCallback<T>(T testCallbacks) where T : ICallbacks
  129. {
  130. if (testCallbacks == null)
  131. {
  132. throw new ArgumentNullException(nameof(testCallbacks));
  133. }
  134. CallbacksHolder.Remove(testCallbacks);
  135. }
  136. internal void RetrieveTestList(ExecutionSettings executionSettings, Action<ITestAdaptor> callback)
  137. {
  138. if (executionSettings == null)
  139. {
  140. throw new ArgumentNullException(nameof(executionSettings));
  141. }
  142. var firstFilter = executionSettings.filters?.FirstOrDefault() ?? executionSettings.filter;
  143. RetrieveTestList(firstFilter.testMode, callback);
  144. }
  145. /// <summary>
  146. /// Retrieve the full test tree as ITestAdaptor for a given test mode. This is obsolete. Use TestRunnerApi.RetrieveTestTree instead.
  147. /// </summary>
  148. /// <param name="testMode"></param>
  149. /// <param name="callback"></param>
  150. public void RetrieveTestList(TestMode testMode, Action<ITestAdaptor> callback)
  151. {
  152. if (callback == null)
  153. {
  154. throw new ArgumentNullException(nameof(callback));
  155. }
  156. var platform = ParseTestMode(testMode);
  157. var testAssemblyProvider = new EditorLoadedTestAssemblyProvider(new EditorCompilationInterfaceProxy(), new EditorAssembliesProxy());
  158. var testAdaptorFactory = new TestAdaptorFactory();
  159. var testListCache = new TestListCache(testAdaptorFactory, new RemoteTestResultDataFactory(), TestListCacheData.instance);
  160. var testListProvider = new TestListProvider(testAssemblyProvider, new UnityTestAssemblyBuilder(null, 0));
  161. var cachedTestListProvider = new CachingTestListProvider(testListProvider, testListCache, testAdaptorFactory);
  162. var job = new TestListJob(cachedTestListProvider, platform, testRoot =>
  163. {
  164. callback(testRoot);
  165. });
  166. job.Start();
  167. }
  168. ///<summary>
  169. /// 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.
  170. /// </summary>
  171. /// <param name="results">Test results to write in a file.</param>
  172. /// <param name="xmlFilePath">An xml file path relative to the project folder.</param>
  173. public static void SaveResultToFile(ITestResultAdaptor results, string xmlFilePath)
  174. {
  175. var resultsWriter = new ResultsWriter();
  176. resultsWriter.WriteResultToFile(results, xmlFilePath);
  177. }
  178. /// <summary>
  179. /// 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".
  180. /// </summary>
  181. /// <param name="guid">Test run guid string.</param>
  182. /// <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>
  183. public static bool CancelTestRun(string guid)
  184. {
  185. var runner = m_testJobDataHolder.GetRunner(guid);
  186. if (runner == null || !runner.IsRunningJob())
  187. {
  188. return false;
  189. }
  190. return runner.CancelRun();
  191. }
  192. internal static bool IsRunActive()
  193. {
  194. return m_testJobDataHolder.GetAllRunners().Any(r => r.GetData().isRunning);
  195. }
  196. private static TestPlatform ParseTestMode(TestMode testMode)
  197. {
  198. return (((testMode & TestMode.EditMode) == TestMode.EditMode) ? TestPlatform.EditMode : 0) | (((testMode & TestMode.PlayMode) == TestMode.PlayMode) ? TestPlatform.PlayMode : 0);
  199. }
  200. internal class RunProgressChangedEvent : UnityEvent<TestRunProgress> {}
  201. internal static RunProgressChangedEvent runProgressChanged = new RunProgressChangedEvent();
  202. }
  203. }