A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using NUnit.Framework;
5using NUnit.Framework.Interfaces;
6using NUnit.Framework.Internal;
7using NUnit.Framework.Internal.Builders;
8
9namespace UnityEngine.TestTools
10{
11 /// <summary>
12 /// `UnityTest` attribute is the main addition to the standard [NUnit](http://www.nunit.org/) library for the Unity Test Framework. This type of unit test allows you to skip a frame from within a test (so background tasks can finish) or give certain commands to the Unity **Editor**, such as performing a domain reload or entering **Play Mode** from an **Edit Mode** test.
13 /// In Play Mode, the `UnityTest` attribute runs as a [coroutine](https://docs.unity3d.com/Manual/Coroutines.html). Whereas Edit Mode tests run in the [EditorApplication.update](https://docs.unity3d.com/ScriptReference/EditorApplication-update.html) callback loop.
14 /// The `UnityTest` attribute is, in fact, an alternative to the `NUnit` [Test attribute](https://github.com/nunit/docs/wiki/Test-Attribute), which allows yielding instructions back to the framework. Once the instruction is complete, the test run continues. If you `yield return null`, you skip a frame. That might be necessary to ensure that some changes do happen on the next iteration of either the `EditorApplication.update` loop or the [game loop](https://docs.unity3d.com/Manual/ExecutionOrder.html).
15 /// <example>
16 /// ## Edit Mode example
17 /// The most simple example of an Edit Mode test could be the one that yields `null` to skip the current frame and then continues to run:
18 /// <code>
19 /// [UnityTest]
20 /// public IEnumerator EditorUtility_WhenExecuted_ReturnsSuccess()
21 /// {
22 /// var utility = RunEditorUtilityInTheBackground();
23 ///
24 /// while (utility.isRunning)
25 /// {
26 /// yield return null;
27 /// }
28 ///
29 /// Assert.IsTrue(utility.isSuccess);
30 /// }
31 /// </code>
32 /// </example>
33 /// <example>
34 /// ## Play Mode example
35 ///
36 /// In Play Mode, a test runs as a coroutine attached to a [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html). So all the yield instructions available in coroutines, are also available in your test.
37 ///
38 /// From a Play Mode test you can use one of Unity’s [Yield Instructions](https://docs.unity3d.com/ScriptReference/YieldInstruction.html):
39 ///
40 /// - [WaitForFixedUpdate](https://docs.unity3d.com/ScriptReference/WaitForFixedUpdate.html): to ensure changes expected within the next cycle of physics calculations.
41 /// - [WaitForSeconds](https://docs.unity3d.com/ScriptReference/WaitForSeconds.html): if you want to pause your test coroutine for a fixed amount of time. Be careful about creating long-running tests.
42 ///
43 /// The simplest example is to yield to `WaitForFixedUpdate`:
44 /// <code>
45 /// [UnityTest]
46 /// public IEnumerator GameObject_WithRigidBody_WillBeAffectedByPhysics()
47 /// {
48 /// var go = new GameObject();
49 /// go.AddComponent<Rigidbody>();
50 /// var originalPosition = go.transform.position.y;
51 ///
52 /// yield return new WaitForFixedUpdate();
53 ///
54 /// Assert.AreNotEqual(originalPosition, go.transform.position.y);
55 /// }
56 /// </code>
57 /// </example>
58 /// </summary>
59 [AttributeUsage(AttributeTargets.Method)]
60 public class UnityTestAttribute : CombiningStrategyAttribute, IImplyFixture, ISimpleTestBuilder, ITestBuilder, IApplyToTest
61 {
62 private const string k_MethodMarkedWithUnitytestMustReturnIenumerator = "Method marked with UnityTest must return IEnumerator.";
63
64 /// <summary>
65 /// Initializes and returns an instance of UnityTestAttribute.
66 /// </summary>
67 public UnityTestAttribute() : base(new UnityCombinatorialStrategy(), new ParameterDataSourceProvider()) {}
68
69 private readonly NUnitTestCaseBuilder _builder = new NUnitTestCaseBuilder();
70
71 /// <summary>
72 /// This method builds the TestMethod from the Test and the method info. In addition it removes the expected result of the test.
73 /// </summary>
74 /// <param name="method">The method info.</param>
75 /// <param name="suite">The test.</param>
76 /// <returns>A TestMethod object</returns>
77 TestMethod ISimpleTestBuilder.BuildFrom(IMethodInfo method, Test suite)
78 {
79 var t = CreateTestMethod(method, suite);
80
81 AdaptToUnityTestMethod(t);
82
83 return t;
84 }
85
86 /// <summary>
87 /// This method hides the base method from CombiningStrategyAttribute.
88 /// It builds a TestMethod from a Parameterized Test and the method info.
89 /// In addition it removes the expected result of the test.
90 /// </summary>
91 /// <param name="method">The method info.</param>
92 /// <param name="suite">The test.</param>
93 /// <returns>A TestMethod object</returns>
94 IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
95 {
96 var testMethods = base.BuildFrom(method, suite);
97
98 foreach (var t in testMethods)
99 {
100 AdaptToUnityTestMethod(t);
101 }
102
103 return testMethods;
104 }
105
106 private TestMethod CreateTestMethod(IMethodInfo method, Test suite)
107 {
108 TestCaseParameters parms = new TestCaseParameters
109 {
110 ExpectedResult = new object(),
111 HasExpectedResult = true
112 };
113
114 var t = _builder.BuildTestMethod(method, suite, parms);
115 return t;
116 }
117
118 private static void AdaptToUnityTestMethod(TestMethod t)
119 {
120 if (t.parms != null)
121 {
122 t.parms.HasExpectedResult = false;
123 }
124 }
125
126 private static bool IsMethodReturnTypeIEnumerator(IMethodInfo method)
127 {
128 return !method.ReturnType.IsType(typeof(IEnumerator));
129 }
130
131 /// <summary>
132 /// This method hides the base method ApplyToTest from CombiningStrategyAttribute.
133 /// In addition it ensures that the test with the `UnityTestAttribute` has an IEnumerator as return type.
134 /// </summary>
135 /// <param name="test">The test.</param>
136 public new void ApplyToTest(Test test)
137 {
138 if (IsMethodReturnTypeIEnumerator(test.Method))
139 {
140 test.RunState = RunState.NotRunnable;
141 test.Properties.Set(PropertyNames.SkipReason, k_MethodMarkedWithUnitytestMustReturnIenumerator);
142 }
143
144 base.ApplyToTest(test);
145 }
146 }
147}