testingframework.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*!
  2. \defgroup ScriptTestingFramework Script Testing Framework
  3. \addtogroup ScriptTestingFramework
  4. @{
  5. \page Page_System_ScriptTestingFramework Script Testing Framework
  6. \tableofcontents
  7. \section Introduction Introduction
  8. - Provides a unified and simple interface that emphasizes the smallest amount
  9. of boiler plate code possible.
  10. - The collection and instantiation of its primitives is performed right after
  11. the script compilation.
  12. - Within the framework a SINGLE test harness derived class can exist. The
  13. harness runs the tests and contains API to access them.
  14. - The test units are compiled to Suites. These provide additional API for
  15. environmental control.
  16. \section SimpleTests Simple tests
  17. Can be perfomed in form of annotated <b>free functions</b>.
  18. <em>Note: Notice the Suite name in the attribute.</em>
  19. @code
  20. [Test("MyFirstTestSuite")]
  21. TestResultBase MyFooBarTest() { return TestBoolResult(5 > 3); }
  22. @endcode
  23. \section StatefulTests Stateful tests
  24. More elaborate tests that need some state and will run for several ticks have
  25. to be defined as TestBase derived classes. Your logic has to be ran through
  26. __step methods__.
  27. \subsection StepMethods Step methods
  28. - You can name your step methods however you like.
  29. - They have to be annotated by the [Step(Stage)] attribute which pairs the step
  30. with a stage.
  31. \subsection Stages Stages
  32. - They divide the steps into groups that express the initialization and
  33. finalization process.
  34. - Stages are executed in order Setup -> Main -> TearDown.
  35. - Methods in stages are executed in order of definition.
  36. \subsection ReturnValues Return values
  37. - void -> Will get executed only once.
  38. - bool -> Will get executed every tick until true is returned.
  39. \subsection Result Result
  40. - Result is set via API member method of TestBase `SetResult(TestResultBase)`.
  41. - Setting a result which evaluates to failure will terminate the stage.
  42. \subsection FailureUnwind Failure unwind
  43. - If the Setup stage fails the test only terminates and TearDown is not called.
  44. - Main stage failure will trigger the TearDown.
  45. - TearDown failure will do nothing.
  46. \subsection Timeout Timeout
  47. - The tests won't timeout by default. The value may be specified via Test
  48. attribute.
  49. - The timeout counter resets for every stage method.
  50. - If the stage method times out the TimeoutResult is set (it evaluates to
  51. failure and the failure unwind process starts).
  52. @code
  53. [Test("MyFirstTestSuite", timeoutS: 2, timeoutMs: 250)]
  54. class MyAsyncTest : TestBase
  55. {
  56. // Simple blocking initialization.
  57. [Step(EStage.Setup)]
  58. void Initialize() { ... }
  59. // Async test which is waiting for result for several frames.
  60. [Step(EStage.Main)]
  61. bool Pool() { ... }
  62. // Finalization process waiting for result for several frames.
  63. [Step(EStage.TearDown)]
  64. bool FinalizeA() { ... }
  65. // Simple blocking finalization call.
  66. [Step(EStage.TearDown)]
  67. void FinalizeB() { ... }
  68. }
  69. @endcode
  70. */
  71. //-----------------------------------------------------------------------------
  72. //! Attribute used for tests annotation and assignment to Suites.
  73. class Test
  74. {
  75. string Suite;
  76. int TimeoutS;
  77. int TimeoutMs;
  78. int SortOrder;
  79. //! Defines a suite the test belongs to, its timeout value and order within the suite.
  80. void Test(string suite, int timeoutS = 0, int timeoutMs = 0, int sortOrder = 0)
  81. {
  82. Suite = suite;
  83. TimeoutS = timeoutS;
  84. TimeoutMs = timeoutMs;
  85. SortOrder = sortOrder;
  86. }
  87. }
  88. //-----------------------------------------------------------------------------
  89. //! Stage definition used in conjunction with Step attribute.
  90. enum EStage
  91. {
  92. Setup,
  93. Main,
  94. TearDown
  95. }
  96. //-----------------------------------------------------------------------------
  97. //! Attribute which marks a method as part of the testing process.
  98. class Step
  99. {
  100. EStage Stage;
  101. void Step(EStage stage = EStage.Main)
  102. {
  103. Stage = stage;
  104. }
  105. }
  106. //-----------------------------------------------------------------------------
  107. //! Collection and main interface of the Testing framework.
  108. class TestHarness : Managed
  109. {
  110. //! Starts the testing process. Returns true when all tests have finished. If
  111. //! some of them are still in progress false is reported.
  112. proto native static bool Run();
  113. //! Generates a xml report.
  114. proto static string Report();
  115. //! Returns number of test suites.
  116. proto native static int GetNSuites();
  117. //! Returns a test suite.
  118. proto native static TestSuite GetSuite(int handle);
  119. //! Returns currently active TestSuite or null when none is active.
  120. proto native static TestSuite ActiveSuite();
  121. //! Returns true when all tests and suites finished.
  122. proto native static bool Finished();
  123. //! Starts up the testing process and initializes the structures.
  124. proto native static void Begin();
  125. //! Finalizes the testing process.
  126. proto native static void End();
  127. }
  128. //-----------------------------------------------------------------------------
  129. //! Collection of tests.
  130. class TestSuite : Managed
  131. {
  132. //! Sets the suite result. Failure can result in specialized behavior described
  133. //! in TestResultBase.
  134. proto native void SetResult(TestResultBase res);
  135. //! Returns the number for tests within this suite.
  136. proto native int GetNTests();
  137. //! Returns a test.
  138. proto native TestBase GetTest(int handle);
  139. //! Enables/Disables the suites. Disabled suites won't run at all.
  140. proto native void SetEnabled(bool val);
  141. //! Enabled flag getter.
  142. proto native bool IsEnabled();
  143. //! Suite class name getter. Strictly for UI porposes!
  144. proto string GetName();
  145. //! Callback for user defined initialization. Called for all suites during TestHarness.Begin().
  146. protected void OnInit();
  147. }
  148. //-----------------------------------------------------------------------------
  149. //! Test base class.
  150. class TestBase : Managed
  151. {
  152. //! Sets the test result. Failure can result in specialized behavior described
  153. //! in TestResultBase.
  154. proto native void SetResult(TestResultBase res);
  155. //! Result getter.
  156. proto native TestResultBase GetResult();
  157. //! Enables/Disables the test. Disabled tests won't run at all.
  158. proto native void SetEnabled(bool val);
  159. //! Enabled flag getter.
  160. proto native bool IsEnabled();
  161. //! Test name getter. Strictly for UI porposes!
  162. proto string GetName();
  163. }
  164. //-----------------------------------------------------------------------------
  165. //! Base class for test results. This way you report back to the system.
  166. //! More complex failure types with descriptions can be reported by
  167. //! implementation of FailureText in format of junit
  168. //! [https://llg.cubic.org/docs/junit/].
  169. class TestResultBase : Managed
  170. {
  171. //! Return true of the result means failure.
  172. bool Failure() { return NativeFailure(); }
  173. //! Text used for xml report output.
  174. string FailureText() { return NativeFailureText(); }
  175. // Script forwarding to cpp. Otherwise the script overloading wouldn't be able
  176. // to call the native base implementation.
  177. // ----------------- vvv -----------------
  178. proto native bool NativeFailure();
  179. proto native string NativeFailureText();
  180. // ----------------- ^^^ -----------------
  181. }
  182. /*!
  183. * @}
  184. */
  185. //-----------------------------------------------------------------------------
  186. // EXAMPLES
  187. //-----------------------------------------------------------------------------
  188. /*
  189. //-----------------------------------------------------------------------------
  190. //! Basic test result.
  191. class TestBoolResult : TestResultBase
  192. {
  193. bool Value;
  194. void TestBoolResult(bool val) { Value = val; }
  195. override bool Failure() { return !Value; }
  196. override string FailureText()
  197. {
  198. // junit kind of error report. (simple)
  199. return "<failure type=\"BoolResult\">Failed</failure>";
  200. }
  201. }
  202. //-----------------------------------------------------------------------------
  203. class MyHarness : TestHarness
  204. {
  205. }
  206. //-----------------------------------------------------------------------------
  207. class MyTestSuite : TestSuite
  208. {
  209. int cnt;
  210. [Step(EStage.Setup)]
  211. void Prep()
  212. {
  213. Print("MyTestSuite::Prep");
  214. cnt = 3;
  215. }
  216. [Step(EStage.Setup)]
  217. bool Count()
  218. {
  219. --cnt;
  220. Print("MyTestSuite::Count: cnt=" + cnt);
  221. return cnt == 0;
  222. }
  223. [Step(EStage.TearDown)]
  224. bool CountUp()
  225. {
  226. ++cnt;
  227. Print("MyTestSuite::CountUp: cnt=" + cnt);
  228. return cnt == 10;
  229. }
  230. }
  231. //-----------------------------------------------------------------------------
  232. [Test("MyTestSuite")]
  233. TestResultBase MyTest()
  234. {
  235. Print("MyFuncTest");
  236. return new TestBoolResult(true);
  237. }
  238. //-----------------------------------------------------------------------------
  239. [Test("MyTestSuite")]
  240. class MyAsyncTest : TestBase
  241. {
  242. int counter;
  243. [Step(EStage.Main)]
  244. void Set()
  245. {
  246. counter = 10;
  247. }
  248. [Step(EStage.Main)]
  249. bool Pool()
  250. {
  251. Print("AsyncTest::Pool::counter=" + counter);
  252. if(counter == 0)
  253. {
  254. Print("AsyncTest::Pool::Result");
  255. SetResult(new TestBoolResult(false));
  256. return true;
  257. }
  258. Print("AsyncTest::Pool::Progress");
  259. counter--;
  260. return false;
  261. }
  262. }
  263. */