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.

241 lines
7.0KB

  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. {
  76. // pass
  77. Log(PASS + success);
  78. TestRoot.TestsPassed = true;
  79. return true;
  80. }
  81. else if (!(obj1 == null && obj2 == null) && obj1.Equals(obj2) && obj2.Equals(obj1))
  82. {
  83. // pass
  84. Log(PASS + success);
  85. TestRoot.TestsPassed = true;
  86. return true;
  87. }
  88. else if (obj1 != null && (obj1 != null && !obj1.Equals(obj2)))
  89. {
  90. // pass
  91. Log(PASS + success);
  92. TestRoot.TestsPassed = true;
  93. return true;
  94. }
  95. else if (obj2 != null && !obj2.Equals(obj1))
  96. {
  97. // pass
  98. Log(PASS + success);
  99. TestRoot.TestsPassed = true;
  100. return true;
  101. }
  102. else
  103. {
  104. // fail
  105. Log(FAIL + err);
  106. TestRoot.TestsPassed = false;
  107. return false;
  108. }
  109. }
  110. public static bool Errorless(Action tryThis, string err = null, string success = null)
  111. {
  112. if (err == null) err = $"{tryThis} raised an exception: ";
  113. if (success == null) success = $"{tryThis} completed without raising an exception.";
  114. try
  115. {
  116. tryThis();
  117. }
  118. catch (Exception e)
  119. {
  120. Log(FAIL + err + e);
  121. TestRoot.TestsPassed = false;
  122. return false;
  123. }
  124. TestRoot.TestsPassed = true;
  125. Log(PASS + success);
  126. return true;
  127. }
  128. public static bool CloseTo(float a, float b, string err = null, string success = null, float delta = float.Epsilon)
  129. {
  130. if (err == null) err = $"{a} is not within {delta} of {b}.";
  131. if (success == null) success = $"{a} is close enough to {b}.";
  132. if (Math.Abs(a - b) > delta)
  133. {
  134. Log(FAIL + err);
  135. TestRoot.TestsPassed = false;
  136. return false;
  137. }
  138. else
  139. {
  140. TestRoot.TestsPassed = true;
  141. Log(PASS + success);
  142. return true;
  143. }
  144. }
  145. public static bool CloseTo(double a, double b, string err = null, string success = null, double delta = double.Epsilon)
  146. {
  147. if (err == null) err = $"{a} is not within {delta} of {b}.";
  148. if (success == null) success = $"{a} is close enough to {b}.";
  149. if (Math.Abs(a - b) > delta)
  150. {
  151. Log(FAIL + err);
  152. TestRoot.TestsPassed = false;
  153. return false;
  154. }
  155. else
  156. {
  157. TestRoot.TestsPassed = true;
  158. Log(PASS + success);
  159. return true;
  160. }
  161. }
  162. public static bool CloseTo(float3 a, float3 b, string err = null, string success = null, float delta = float.Epsilon)
  163. {
  164. if (err == null) err = $"{a} is not within {delta} of {b} in every direction.";
  165. if (success == null) success = $"{a} is close enough to {b}.";
  166. bool xClose = CloseTo(a.x, b.x, err, success, delta);
  167. bool yClose = CloseTo(a.y, b.y, err, success, delta);
  168. bool zClose = CloseTo(a.z, b.z, err, success, delta);
  169. if (xClose && yClose && zClose)
  170. {
  171. //TestRoot.TestsPassed = true;
  172. //Log(PASS + success);
  173. return true;
  174. }
  175. else
  176. {
  177. //Log(FAIL + err);
  178. //TestRoot.TestsPassed = false;
  179. return false;
  180. }
  181. }
  182. public static void Fail(string msg = null)
  183. {
  184. if (msg == null) msg = $"Manual test failure with no message provided.";
  185. Log(FAIL + msg);
  186. TestRoot.TestsPassed = false;
  187. }
  188. public static void Pass(string msg = null)
  189. {
  190. if (msg == null) msg = $"Manual test pass with no message provided.";
  191. Log(PASS + msg);
  192. TestRoot.TestsPassed = true;
  193. }
  194. public static void Warn(string msg = null)
  195. {
  196. if (msg == null) msg = $"Manual test warning with no message provided.";
  197. Log(WARN + msg);
  198. TestRoot.TestsPassed = true;
  199. }
  200. internal static void CallsComplete()
  201. {
  202. foreach(string key in callbacks.Keys)
  203. {
  204. Log(FAIL + callbacks[key]);
  205. TestRoot.TestsPassed = false;
  206. }
  207. }
  208. internal static void CloseLog()
  209. {
  210. if (logFile != null) logFile.Close();
  211. }
  212. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  213. private static void openTestLog()
  214. {
  215. logFile = File.CreateText(TestRoot.ReportFile);
  216. }
  217. }
  218. }