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.

240 lines
7.1KB

  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.IO;
  4. using System.Runtime.CompilerServices;
  5. using Unity.Mathematics;
  6. namespace GamecraftModdingAPI.Tests
  7. {
  8. /// <summary>
  9. /// API test system assertion utilities.
  10. /// </summary>
  11. public static class Assert
  12. {
  13. private static StreamWriter logFile = null;
  14. private static ConcurrentDictionary<string, string> callbacks = new ConcurrentDictionary<string, string>();
  15. private const string PASS = "SUCCESS: ";
  16. private const string FAIL = "FAILURE: ";
  17. private const string WARN = "WARNING: ";
  18. private const string INFO = "DEBUG: ";
  19. /// <summary>
  20. /// Log a message to the test log.
  21. /// </summary>
  22. /// <param name="msg">Message.</param>
  23. /// <param name="end">Message ending.</param>
  24. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  25. public static void Log(string msg, string end = "\n")
  26. {
  27. if (logFile == null) openTestLog();
  28. logFile.Write(msg + end);
  29. logFile.Flush();
  30. }
  31. /// <summary>
  32. /// Asserts that the event receives a callback... eventually.
  33. /// Add the eventhandler returned by this method to the relevant event.
  34. /// This does not assert that the callback happens under that event's intended circumstances.
  35. /// Add another event handler to assert specific circumstance requirements.
  36. /// </summary>
  37. /// <returns>The callback event handler.</returns>
  38. /// <param name="eventName">Event name.</param>
  39. /// <param name="eventMsg">Event error message.</param>
  40. /// <typeparam name="T">The event handler callback argument object.</typeparam>
  41. public static EventHandler<T> CallsBack<T>(string eventName, string eventMsg = null)
  42. {
  43. if (eventMsg == null) eventMsg = $"expected callback to {eventName} but it never occurred...";
  44. callbacks[eventName] = eventMsg;
  45. return (sender, args) =>
  46. {
  47. string value = null;
  48. if (!callbacks.TryRemove(eventName, out value)) { Log(WARN + $"callback to {eventName} occurred again or a related error occurred... (Received '{args.ToString()}' from '{(sender == null ? (string)sender : sender.ToString())}')"); }
  49. Log(PASS + $"callback to {eventName} occurred... (Received '{args.ToString()}' from '{(sender == null ? (string)sender : sender.ToString())}')");
  50. TestRoot.TestsPassed = true;
  51. };
  52. }
  53. public static bool NotNull<T>(T obj, string err = null, string success = null)
  54. {
  55. if (err == null) err = $"{nameof(T)} object was null.";
  56. if (success == null) success = $"{nameof(T)} '{obj}' not null";
  57. if (obj == null)
  58. {
  59. Log(FAIL + err);
  60. TestRoot.TestsPassed = false;
  61. return false;
  62. }
  63. else
  64. {
  65. Log(PASS + success);
  66. TestRoot.TestsPassed = true;
  67. return true;
  68. }
  69. }
  70. public static bool Equal<T>(T obj1, T obj2, string err = null, string success = null)
  71. {
  72. if (err == null) err = $"{nameof(T)} '{obj1}' is not equal to '{obj2}'.";
  73. if (success == null) success = $"{nameof(T)} '{obj1}' is equal to '{obj2}'.";
  74. if ((obj1 == null && obj2 == null)
  75. || (obj1 != null && obj2 != null && obj1.Equals(obj2) && obj2.Equals(obj1)))
  76. {
  77. // pass
  78. Log(PASS + success);
  79. TestRoot.TestsPassed = true;
  80. return true;
  81. }
  82. else
  83. {
  84. // fail
  85. Log(FAIL + err);
  86. TestRoot.TestsPassed = false;
  87. return false;
  88. }
  89. }
  90. public static bool Errorless(Action tryThis, string err = null, string success = null)
  91. {
  92. if (err == null) err = $"{tryThis} raised an exception: ";
  93. if (success == null) success = $"{tryThis} completed without raising an exception.";
  94. try
  95. {
  96. tryThis();
  97. }
  98. catch (Exception e)
  99. {
  100. Log(FAIL + err + e);
  101. TestRoot.TestsPassed = false;
  102. return false;
  103. }
  104. TestRoot.TestsPassed = true;
  105. Log(PASS + success);
  106. return true;
  107. }
  108. public static bool Errorful<T>(Action tryThis, string err = null, string success = null) where T : Exception
  109. {
  110. if (err == null) err = $"{tryThis} was expected to error but completed without errors.";
  111. if (success == null) success = $"{tryThis} completed with an expected error.";
  112. try
  113. {
  114. tryThis();
  115. }
  116. catch (T e)
  117. {
  118. TestRoot.TestsPassed = true;
  119. Log(PASS + success + " " + e.GetType().Name + ": " + e.Message);
  120. return true;
  121. }
  122. Log(FAIL + err);
  123. TestRoot.TestsPassed = false;
  124. return false;
  125. }
  126. public static bool CloseTo(float a, float b, string err = null, string success = null, float delta = float.Epsilon)
  127. {
  128. if (err == null) err = $"{a} is not within {delta} of {b}.";
  129. if (success == null) success = $"{a} is close enough to {b}.";
  130. if (Math.Abs(a - b) > delta)
  131. {
  132. Log(FAIL + err);
  133. TestRoot.TestsPassed = false;
  134. return false;
  135. }
  136. else
  137. {
  138. TestRoot.TestsPassed = true;
  139. Log(PASS + success);
  140. return true;
  141. }
  142. }
  143. public static bool CloseTo(double a, double b, string err = null, string success = null, double delta = double.Epsilon)
  144. {
  145. if (err == null) err = $"{a} is not within {delta} of {b}.";
  146. if (success == null) success = $"{a} is close enough to {b}.";
  147. if (Math.Abs(a - b) > delta)
  148. {
  149. Log(FAIL + err);
  150. TestRoot.TestsPassed = false;
  151. return false;
  152. }
  153. else
  154. {
  155. TestRoot.TestsPassed = true;
  156. Log(PASS + success);
  157. return true;
  158. }
  159. }
  160. public static bool CloseTo(float3 a, float3 b, string err = null, string success = null, float delta = float.Epsilon)
  161. {
  162. if (err == null) err = $"{a} is not within {delta} of {b} in every direction.";
  163. if (success == null) success = $"{a} is close enough to {b}.";
  164. bool xClose = CloseTo(a.x, b.x, err, success, delta);
  165. bool yClose = CloseTo(a.y, b.y, err, success, delta);
  166. bool zClose = CloseTo(a.z, b.z, err, success, delta);
  167. if (xClose && yClose && zClose)
  168. {
  169. //TestRoot.TestsPassed = true;
  170. //Log(PASS + success);
  171. return true;
  172. }
  173. else
  174. {
  175. //Log(FAIL + err);
  176. //TestRoot.TestsPassed = false;
  177. return false;
  178. }
  179. }
  180. public static void Fail(string msg = null)
  181. {
  182. if (msg == null) msg = $"Manual test failure with no message provided.";
  183. Log(FAIL + msg);
  184. TestRoot.TestsPassed = false;
  185. }
  186. public static void Pass(string msg = null)
  187. {
  188. if (msg == null) msg = $"Manual test pass with no message provided.";
  189. Log(PASS + msg);
  190. TestRoot.TestsPassed = true;
  191. }
  192. public static void Warn(string msg = null)
  193. {
  194. if (msg == null) msg = $"Manual test warning with no message provided.";
  195. Log(WARN + msg);
  196. TestRoot.TestsPassed = true;
  197. }
  198. internal static void CallsComplete()
  199. {
  200. foreach(string key in callbacks.Keys)
  201. {
  202. Log(FAIL + callbacks[key]);
  203. TestRoot.TestsPassed = false;
  204. }
  205. }
  206. internal static void CloseLog()
  207. {
  208. if (logFile != null) logFile.Close();
  209. }
  210. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  211. private static void openTestLog()
  212. {
  213. logFile = File.CreateText(TestRoot.ReportFile);
  214. }
  215. }
  216. }