A game about forced loneliness, made by TACStudios
1using System; 2 3namespace UnityEditor.TestTools 4{ 5 /// <summary> 6 /// You can use the ```TestPlayerBuildModifier``` attribute to accomplish a couple of different scenarios. 7 /// ## Modify the Player build options for Play Mode tests 8 /// 9 /// It is possible to change the [BuildPlayerOptions](https://docs.unity3d.com/ScriptReference/BuildPlayerOptions.html) for the test **Player**, to achieve custom behavior when running **Play Mode** tests. Modifying the build options allows for changing the target location of the build as well as changing [BuildOptions](https://docs.unity3d.com/ScriptReference/BuildOptions.html). 10 /// 11 /// To modify the `BuildPlayerOptions`, do the following: 12 /// 13 /// * Implement the `ITestPlayerBuildModifier` 14 /// * Reference the implementation type in a `TestPlayerBuildModifier` attribute on an assembly level. 15 /// 16 /// <example> 17 /// <code> 18 /// using UnityEditor; 19 /// using UnityEditor.TestTools; 20 /// 21 /// [assembly:TestPlayerBuildModifier(typeof(BuildModifier))] 22 /// public class BuildModifier : ITestPlayerBuildModifier 23 /// { 24 /// public BuildPlayerOptions ModifyOptions(BuildPlayerOptions playerOptions) 25 /// { 26 /// if (playerOptions.target == BuildTarget.iOS) 27 /// { 28 /// playerOptions.options |= BuildOptions.SymlinkLibraries; // Enable symlink libraries when running on iOS 29 /// } 30 /// 31 /// playerOptions.options |= BuildOptions.AllowDebugging; // Enable allow Debugging flag on the test Player. 32 /// return playerOptions; 33 /// } 34 /// } 35 /// </code> 36 /// </example> 37 /// 38 /// 39 /// &gt; **Note:** When building the Player, it includes all `TestPlayerBuildModifier` attributes across all loaded assemblies, independent of the currently used test filter. As the implementation references the `UnityEditor` namespace, the code is typically implemented in an Editor only assembly, as the `UnityEditor` namespace is not available otherwise. 40 /// 41 /// ## Split build and run 42 /// It is possible to use the Unity Editor for building the Player with tests, without [running the tests](./workflow-run-playmode-test-standalone.md). This allows for running the Player on e.g. another machine. In this case, it is necessary to modify the Player to build and implement a custom handling of the test result. 43 /// By using `TestPlayerBuildModifier`, you can alter the `BuildOptions` to not start the Player after the build as well as build the Player at a specific location. Combined with [PostBuildCleanup](./reference-setup-and-cleanup.md#prebuildsetup-and-postbuildcleanup), you can automatically exit the Editor on completion of the build. 44 /// 45 /// 46 /// <example> 47 /// <code> 48 /// using System; 49 /// using System.IO; 50 /// using System.Linq; 51 /// using Tests; 52 /// using UnityEditor; 53 /// using UnityEditor.TestTools; 54 /// using UnityEngine; 55 /// using UnityEngine.TestTools; 56 /// 57 /// [assembly:TestPlayerBuildModifier(typeof(HeadlessPlayModeSetup))] 58 /// [assembly:PostBuildCleanup(typeof(HeadlessPlayModeSetup))] 59 /// 60 /// namespace Tests 61 /// { 62 /// public class HeadlessPlayModeSetup : ITestPlayerBuildModifier, IPostBuildCleanup 63 /// { 64 /// private static bool s_RunningPlayerTests; 65 /// public BuildPlayerOptions ModifyOptions(BuildPlayerOptions playerOptions) 66 /// { 67 /// // Do not launch the player after the build completes. 68 /// playerOptions.options &amp;= ~BuildOptions.AutoRunPlayer; 69 /// 70 /// // Set the headlessBuildLocation to the output directory you desire. It does not need to be inside the project. 71 /// var headlessBuildLocation = Path.GetFullPath(Path.Combine(Application.dataPath, ".//..//PlayModeTestPlayer")); 72 /// var fileName = Path.GetFileName(playerOptions.locationPathName); 73 /// if (!string.IsNullOrEmpty(fileName)) 74 /// { 75 /// headlessBuildLocation = Path.Combine(headlessBuildLocation, fileName); 76 /// } 77 /// playerOptions.locationPathName = headlessBuildLocation; 78 /// 79 /// // Instruct the cleanup to exit the Editor if the run came from the command line. 80 /// // The variable is static because the cleanup is being invoked in a new instance of the class. 81 /// s_RunningPlayerTests = true; 82 /// return playerOptions; 83 /// } 84 /// 85 /// public void Cleanup() 86 /// { 87 /// if (s_RunningPlayerTests &amp;&amp; IsRunningTestsFromCommandLine()) 88 /// { 89 /// // Exit the Editor on the next update, allowing for other PostBuildCleanup steps to run. 90 /// EditorApplication.update += () =&gt; { EditorApplication.Exit(0); }; 91 /// } 92 /// } 93 /// 94 /// private static bool IsRunningTestsFromCommandLine() 95 /// { 96 /// var commandLineArgs = Environment.GetCommandLineArgs(); 97 /// return commandLineArgs.Any(value =&gt; value == "-runTests"); 98 /// } 99 /// } 100 /// } 101 /// </code> 102 /// </example> 103 /// 104 /// If the Editor is still running after the Play Mode tests have run, the Player tries to report the results back, using [PlayerConnection](https://docs.unity3d.com/ScriptReference/Networking.PlayerConnection.PlayerConnection.html), which has a reference to the IP address of the Editor machine, when built. 105 /// 106 /// To implement a custom way of reporting the results of the test run, let one of the assemblies in the Player include a `TestRunCallbackAttribute`. At `RunFinished`, it is possible to get the full test report as XML from the [NUnit](http://www.nunit.org/) test result by calling `result.ToXml(true)`. You can save the result and then save it on the device or send it to another machine as needed. 107 /// 108 /// </summary> 109 [AttributeUsage(AttributeTargets.Assembly)] 110 public class TestPlayerBuildModifierAttribute : Attribute 111 { 112 private Type m_Type; 113 114 /// <summary> 115 /// Initializes and returns an instance of TestPlayerBuildModifierAttribute or throws an <see cref="ArgumentException"/>. 116 /// </summary> 117 /// <param name="type">A target type that implements ITestPlayerBuildModifier.</param> 118 /// <exception cref="ArgumentException">Throws a <see cref="ArgumentException"/> if the type provided does not implemented the `ITestPlayerBuildModifier` interface. </exception> 119 public TestPlayerBuildModifierAttribute(Type type) 120 { 121 var interfaceType = typeof(ITestPlayerBuildModifier); 122 if (!interfaceType.IsAssignableFrom(type)) 123 { 124 throw new ArgumentException(string.Format("Type provided to {0} does not implement {1}", GetType().Name, interfaceType.Name)); 125 } 126 m_Type = type; 127 } 128 129 internal ITestPlayerBuildModifier ConstructModifier() 130 { 131 return Activator.CreateInstance(m_Type) as ITestPlayerBuildModifier; 132 } 133 } 134}