A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEditor.TestRunner.TestLaunchers;
5using UnityEditor.TestTools.TestRunner.Api;
6using UnityEngine;
7
8namespace UnityEditor.TestTools.TestRunner.CommandLineTest
9{
10 internal class Executer : IExecuter
11 {
12 internal IRunData runData = RunData.instance;
13
14 private ITestRunnerApi m_TestRunnerApi;
15 private ISettingsBuilder m_SettingsBuilder;
16 private Action<string, object[]> m_LogErrorFormat;
17 private Action<Exception> m_LogException;
18 private Action<string> m_LogMessage;
19 private Action<int> m_ExitEditorApplication;
20 private Func<bool> m_ScriptCompilationFailedCheck;
21 private Func<bool> m_IsRunActive;
22
23 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)
24 {
25 m_TestRunnerApi = testRunnerApi;
26 m_SettingsBuilder = settingsBuilder;
27 m_LogErrorFormat = logErrorFormat;
28 m_LogException = logException;
29 m_LogMessage = logMessage;
30 m_ExitEditorApplication = exitEditorApplication;
31 m_ScriptCompilationFailedCheck = scriptCompilationFailedCheck;
32 m_IsRunActive = isRunActive;
33 }
34
35 public string InitializeAndExecuteRun(string[] commandLineArgs)
36 {
37 Api.ExecutionSettings executionSettings;
38 try
39 {
40 executionSettings = m_SettingsBuilder.BuildApiExecutionSettings(commandLineArgs);
41 if (executionSettings.targetPlatform.HasValue)
42 RemotePlayerLogController.instance.SetBuildTarget(executionSettings.targetPlatform.Value);
43 }
44 catch (SetupException exception)
45 {
46 HandleSetupException(exception);
47 return string.Empty;
48 }
49
50 try
51 {
52 // It is important that the message starts with "Running tests for ", otherwise TestCleanConsole will fail.
53 m_LogMessage($"Running tests for {executionSettings}");
54 return m_TestRunnerApi.Execute(executionSettings);
55 }
56 catch (Exception exception)
57 {
58 m_LogException(exception);
59 ExitApplication(ReturnCodes.RunError, "Exception when starting test run.");
60 return string.Empty;
61 }
62 }
63
64 public void ExitIfRunIsCompleted()
65 {
66 if (m_IsRunActive())
67 {
68 return;
69 }
70
71 var runState = runData.RunState;
72 var returnCode = s_StateReturnCodes[runState];
73 var reason = s_StateMessages[runState] ?? runData.RunErrorMessage;
74 ExitApplication(returnCode, reason);
75 }
76
77 private void ExitApplication(ReturnCodes returnCode, string reason)
78 {
79 var returnCodeInt = (int)returnCode;
80
81 m_LogMessage($"Test run completed. Exiting with code {returnCodeInt} ({returnCode}). {reason}");
82
83 m_ExitEditorApplication(returnCodeInt);
84 }
85
86 public ExecutionSettings BuildExecutionSettings(string[] commandLineArgs)
87 {
88 return m_SettingsBuilder.BuildExecutionSettings(commandLineArgs);
89 }
90
91 internal enum ReturnCodes
92 {
93 Ok = 0,
94 Failed = 2,
95 RunError = 3,
96 PlatformNotFoundReturnCode = 4
97 }
98
99 public void SetUpCallbacks(ExecutionSettings executionSettings)
100 {
101 RemotePlayerLogController.instance.SetLogsDirectory(executionSettings.DeviceLogsDirectory);
102
103 var resultSavingCallback = ScriptableObject.CreateInstance<ResultsSavingCallbacks>();
104 resultSavingCallback.m_ResultFilePath = executionSettings.TestResultsFile;
105
106 var logSavingCallback = ScriptableObject.CreateInstance<LogSavingCallbacks>();
107
108 TestRunnerApi.RegisterTestCallback(resultSavingCallback);
109 TestRunnerApi.RegisterTestCallback(logSavingCallback);
110 TestRunnerApi.RegisterTestCallback(new RunStateCallbacks());
111 }
112
113 public void ExitOnCompileErrors()
114 {
115 if (m_ScriptCompilationFailedCheck())
116 {
117 var handling = s_ExceptionHandlingMapping.First(h => h.m_ExceptionType == SetupException.ExceptionType.ScriptCompilationFailed);
118 m_LogErrorFormat(handling.m_Message, new object[0]);
119 ExitApplication(handling.m_ReturnCode, handling.m_Message);
120 }
121 }
122
123 private void HandleSetupException(SetupException exception)
124 {
125 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);
126 m_LogErrorFormat(handling.m_Message, exception.Details);
127 ExitApplication(handling.m_ReturnCode, handling.m_Message);
128 }
129
130 private class ExceptionHandling
131 {
132 internal SetupException.ExceptionType m_ExceptionType;
133 internal string m_Message;
134 internal ReturnCodes m_ReturnCode;
135 public ExceptionHandling(SetupException.ExceptionType exceptionType, string message, ReturnCodes returnCode)
136 {
137 m_ExceptionType = exceptionType;
138 m_Message = message;
139 m_ReturnCode = returnCode;
140 }
141 }
142
143 private static ExceptionHandling[] s_ExceptionHandlingMapping = {
144 new ExceptionHandling(SetupException.ExceptionType.ScriptCompilationFailed, "Scripts had compilation errors.", ReturnCodes.RunError),
145 new ExceptionHandling(SetupException.ExceptionType.PlatformNotFound, "Test platform not found ({0}).", ReturnCodes.PlatformNotFoundReturnCode),
146 new ExceptionHandling(SetupException.ExceptionType.TestSettingsFileNotFound, "Test settings file not found at {0}.", ReturnCodes.RunError),
147 new ExceptionHandling(SetupException.ExceptionType.OrderedTestListFileNotFound, "Ordered test list file not found at {0}.", ReturnCodes.RunError)
148 };
149
150 private static IDictionary<TestRunState, string> s_StateMessages = new Dictionary<TestRunState, string>()
151 {
152 {TestRunState.NoCallbacksReceived, "No callbacks received."},
153 {TestRunState.OneOrMoreTestsExecutedWithNoFailures, "Run completed."},
154 {TestRunState.OneOrMoreTestsExecutedWithOneOrMoreFailed, "One or more tests failed."},
155 {TestRunState.CompletedJobWithoutAnyTestsExecuted, "No tests were executed."},
156 {TestRunState.RunError, null}
157 };
158
159 private static IDictionary<TestRunState, ReturnCodes> s_StateReturnCodes = new Dictionary<TestRunState, ReturnCodes>()
160 {
161 {TestRunState.NoCallbacksReceived, ReturnCodes.RunError},
162 {TestRunState.OneOrMoreTestsExecutedWithNoFailures, ReturnCodes.Ok},
163 {TestRunState.OneOrMoreTestsExecutedWithOneOrMoreFailed, ReturnCodes.Failed},
164 {TestRunState.CompletedJobWithoutAnyTestsExecuted, ReturnCodes.Ok},
165 {TestRunState.RunError, ReturnCodes.RunError}
166 };
167 }
168}