A stable modding interface between Techblox and mods https://mod.exmods.org/
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.

248 lines
7.2KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Reflection;
  5. using System.Linq; // welcome to the dark side
  6. using Svelto.Tasks;
  7. using Svelto.Tasks.Lean;
  8. using Svelto.Tasks.Enumerators;
  9. using UnityEngine;
  10. using GamecraftModdingAPI.App;
  11. using GamecraftModdingAPI.Tasks;
  12. using GamecraftModdingAPI.Utility;
  13. namespace GamecraftModdingAPI.Tests
  14. {
  15. public static class TestRoot
  16. {
  17. public static bool AutoShutdown = true;
  18. public const string ReportFile = "GamecraftModdingAPI_tests.log";
  19. private static bool _testsPassed = false;
  20. private static uint _testsCount = 0;
  21. private static uint _testsCountPassed = 0;
  22. private static uint _testsCountFailed = 0;
  23. private static string state = "StartingUp";
  24. private static Stopwatch timer;
  25. private static List<Type> testTypes = null;
  26. public static bool TestsPassed
  27. {
  28. get => _testsPassed;
  29. set
  30. {
  31. _testsPassed = _testsPassed && value;
  32. _testsCount++;
  33. if (value)
  34. {
  35. _testsCountPassed++;
  36. }
  37. else
  38. {
  39. _testsCountFailed++;
  40. }
  41. }
  42. }
  43. private static void StartUp()
  44. {
  45. // init
  46. timer = Stopwatch.StartNew();
  47. _testsPassed = true;
  48. _testsCount = 0;
  49. _testsCountPassed = 0;
  50. _testsCountFailed = 0;
  51. // flow control
  52. Game.Enter += (sender, args) => { GameTests().RunOn(RobocraftX.Schedulers.Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING); };
  53. Game.Exit += (s, a) => state = "ReturningFromGame";
  54. Client.EnterMenu += (sender, args) =>
  55. {
  56. if (state == "EnteringMenu")
  57. {
  58. MenuTests().RunOn(Scheduler.leanRunner);
  59. state = "EnteringGame";
  60. }
  61. if (state == "ReturningFromGame")
  62. {
  63. TearDown().RunOn(Scheduler.leanRunner);
  64. state = "ShuttingDown";
  65. }
  66. };
  67. // init tests here
  68. foreach (Type t in testTypes)
  69. {
  70. foreach (MethodBase m in t.GetMethods())
  71. {
  72. if (m.GetCustomAttribute<APITestStartUpAttribute>() != null)
  73. {
  74. m.Invoke(null, new object[0]);
  75. }
  76. }
  77. }
  78. state = "EnteringMenu";
  79. }
  80. private static IEnumerator<TaskContract> MenuTests()
  81. {
  82. yield return Yield.It;
  83. // menu tests
  84. foreach (Type t in testTypes)
  85. {
  86. foreach (MethodBase m in t.GetMethods())
  87. {
  88. APITestCaseAttribute a = m.GetCustomAttribute<APITestCaseAttribute>();
  89. if (a != null && a.TestType == TestType.Menu)
  90. {
  91. m.Invoke(null, new object[0]);
  92. yield return Yield.It;
  93. }
  94. }
  95. }
  96. // load game
  97. yield return GoToGameTests().Continue();
  98. }
  99. private static IEnumerator<TaskContract> GoToGameTests()
  100. {
  101. Client app = new Client();
  102. int oldLength = 0;
  103. while (app.MyGames.Length == 0 || oldLength != app.MyGames.Length)
  104. {
  105. oldLength = app.MyGames.Length;
  106. yield return new WaitForSecondsEnumerator(1).Continue();
  107. }
  108. yield return Yield.It;
  109. app.MyGames[0].EnterGame();
  110. // returning from a new game without saving will hard lock GC (it's an invalid state)
  111. //Game newGame = Game.NewGame();
  112. //yield return new WaitForSecondsEnumerator(5).Continue(); // wait for sync
  113. //newGame.EnterGame();
  114. }
  115. private static IEnumerator<TaskContract> GameTests()
  116. {
  117. yield return Yield.It;
  118. Game currentGame = Game.CurrentGame();
  119. // in-game tests
  120. yield return new WaitForSecondsEnumerator(5).Continue(); // wait for game to finish loading
  121. foreach (Type t in testTypes)
  122. {
  123. foreach (MethodBase m in t.GetMethods())
  124. {
  125. APITestCaseAttribute a = m.GetCustomAttribute<APITestCaseAttribute>();
  126. if (a != null && a.TestType == TestType.Game)
  127. {
  128. m.Invoke(null, new object[0]);
  129. yield return Yield.It;
  130. }
  131. }
  132. }
  133. currentGame.ToggleTimeMode();
  134. yield return new WaitForSecondsEnumerator(5).Continue();
  135. // simulation tests
  136. foreach (Type t in testTypes)
  137. {
  138. foreach (MethodBase m in t.GetMethods())
  139. {
  140. APITestCaseAttribute a = m.GetCustomAttribute<APITestCaseAttribute>();
  141. if (a != null && a.TestType == TestType.SimulationMode)
  142. {
  143. m.Invoke(null, new object[0]);
  144. yield return Yield.It;
  145. }
  146. }
  147. }
  148. currentGame.ToggleTimeMode();
  149. yield return new WaitForSecondsEnumerator(5).Continue();
  150. // build tests
  151. foreach (Type t in testTypes)
  152. {
  153. foreach (MethodBase m in t.GetMethods())
  154. {
  155. APITestCaseAttribute a = m.GetCustomAttribute<APITestCaseAttribute>();
  156. if (a != null && a.TestType == TestType.EditMode)
  157. {
  158. m.Invoke(null, new object[0]);
  159. yield return Yield.It;
  160. }
  161. }
  162. }
  163. // exit game
  164. yield return new WaitForSecondsEnumerator(5).Continue();
  165. yield return ReturnToMenu().Continue();
  166. }
  167. private static IEnumerator<TaskContract> ReturnToMenu()
  168. {
  169. Logging.MetaLog("Returning to main menu");
  170. yield return Yield.It;
  171. Game.CurrentGame().ExitGame();
  172. }
  173. private static IEnumerator<TaskContract> TearDown()
  174. {
  175. yield return new WaitForSecondsEnumerator(5).Continue();
  176. Logging.MetaLog("Tearing down test run");
  177. // dispose tests here
  178. foreach (Type t in testTypes)
  179. {
  180. foreach (MethodBase m in t.GetMethods())
  181. {
  182. if (m.GetCustomAttribute<APITestTearDownAttribute>() != null)
  183. {
  184. m.Invoke(null, new object[0]);
  185. yield return Yield.It;
  186. }
  187. }
  188. }
  189. // finish up
  190. Assert.CallsComplete();
  191. timer.Stop();
  192. string verdict = _testsPassed ? "--- PASSED :) ---" : "--- FAILED :( ---";
  193. Assert.Log($"VERDICT: {verdict} ({_testsCountPassed}/{_testsCountFailed}/{_testsCount} P/F/T in {timer.ElapsedMilliseconds}ms)");
  194. yield return Yield.It;
  195. // end game
  196. Logging.MetaLog("Completed test run: " + verdict);
  197. yield return Yield.It;
  198. Assert.CloseLog();
  199. if (AutoShutdown) Application.Quit();
  200. }
  201. private static void FindTests(Assembly asm)
  202. {
  203. testTypes = new List<Type>();
  204. foreach (Type t in asm.GetTypes())
  205. {
  206. if (t.GetCustomAttribute<APITestClassAttribute>() != null)
  207. {
  208. testTypes.Add(t);
  209. }
  210. }
  211. }
  212. public static void RunTests(Assembly asm = null)
  213. {
  214. if (asm == null) asm = Assembly.GetExecutingAssembly();
  215. FindTests(asm);
  216. Logging.MetaLog("Starting test run");
  217. // log metadata
  218. Assert.Log($"Unity {Application.unityVersion}");
  219. Assert.Log($"Gamecraft {Application.version}");
  220. Assert.Log($"GamecraftModdingAPI {Assembly.GetExecutingAssembly().GetName().Version}");
  221. Assert.Log($"Testing {asm.GetName().Name} {asm.GetName().Version}");
  222. Assert.Log($"START: --- {DateTime.Now.ToString()} --- ({testTypes.Count} tests classes detected)");
  223. StartUp();
  224. Logging.MetaLog("Test StartUp complete");
  225. }
  226. }
  227. }