micro_test.h 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. ==============================================================================*/
  12. // An ultra-lightweight testing framework designed for use with microcontroller
  13. // applications. Its only dependency is on TensorFlow Lite's ErrorReporter
  14. // interface, where log messages are output. This is designed to be usable even
  15. // when no standard C or C++ libraries are available, and without any dynamic
  16. // memory allocation or reliance on global constructors.
  17. //
  18. // To build a test, you use syntax similar to gunit, but with some extra
  19. // decoration to create a hidden 'main' function containing each of the tests to
  20. // be run. Your code should look something like:
  21. // ----------------------------------------------------------------------------
  22. // #include "path/to/this/header"
  23. //
  24. // TF_LITE_MICRO_TESTS_BEGIN
  25. //
  26. // TF_LITE_MICRO_TEST(SomeTest) {
  27. // TF_LITE_LOG_EXPECT_EQ(true, true);
  28. // }
  29. //
  30. // TF_LITE_MICRO_TESTS_END
  31. // ----------------------------------------------------------------------------
  32. // If you compile this for your platform, you'll get a normal binary that you
  33. // should be able to run. Executing it will output logging information like this
  34. // to stderr (or whatever equivalent is available and written to by
  35. // ErrorReporter):
  36. // ----------------------------------------------------------------------------
  37. // Testing SomeTest
  38. // 1/1 tests passed
  39. // ~~~ALL TESTS PASSED~~~
  40. // ----------------------------------------------------------------------------
  41. // This is designed to be human-readable, so you can just run tests manually,
  42. // but the string "~~~ALL TESTS PASSED~~~" should only appear if all of the
  43. // tests do pass. This makes it possible to integrate with automated test
  44. // systems by scanning the output logs and looking for that magic value.
  45. //
  46. // This framework is intended to be a rudimentary alternative to no testing at
  47. // all on systems that struggle to run more conventional approaches, so use with
  48. // caution!
  49. #ifndef TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_
  50. #define TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_
  51. #include "tensorflow/lite/micro/micro_error_reporter.h"
  52. namespace micro_test {
  53. extern int tests_passed;
  54. extern int tests_failed;
  55. extern bool is_test_complete;
  56. extern bool did_test_fail;
  57. extern tflite::ErrorReporter* reporter;
  58. } // namespace micro_test
  59. #define TF_LITE_MICRO_TESTS_BEGIN \
  60. namespace micro_test { \
  61. int tests_passed; \
  62. int tests_failed; \
  63. bool is_test_complete; \
  64. bool did_test_fail; \
  65. tflite::ErrorReporter* reporter; \
  66. } \
  67. \
  68. int main(int argc, char** argv) { \
  69. micro_test::tests_passed = 0; \
  70. micro_test::tests_failed = 0; \
  71. tflite::MicroErrorReporter error_reporter; \
  72. micro_test::reporter = &error_reporter;
  73. #define TF_LITE_MICRO_TESTS_END \
  74. micro_test::reporter->Report( \
  75. "%d/%d tests passed", micro_test::tests_passed, \
  76. (micro_test::tests_failed + micro_test::tests_passed)); \
  77. if (micro_test::tests_failed == 0) { \
  78. micro_test::reporter->Report("~~~ALL TESTS PASSED~~~\n"); \
  79. } else { \
  80. micro_test::reporter->Report("~~~SOME TESTS FAILED~~~\n"); \
  81. } \
  82. }
  83. // TODO(petewarden): I'm going to hell for what I'm doing to this poor for loop.
  84. #define TF_LITE_MICRO_TEST(name) \
  85. micro_test::reporter->Report("Testing " #name); \
  86. for (micro_test::is_test_complete = false, \
  87. micro_test::did_test_fail = false; \
  88. !micro_test::is_test_complete; micro_test::is_test_complete = true, \
  89. micro_test::tests_passed += (micro_test::did_test_fail) ? 0 : 1, \
  90. micro_test::tests_failed += (micro_test::did_test_fail) ? 1 : 0)
  91. #define TF_LITE_MICRO_EXPECT(x) \
  92. do { \
  93. if (!(x)) { \
  94. micro_test::reporter->Report(#x " failed at %s:%d", __FILE__, __LINE__); \
  95. micro_test::did_test_fail = true; \
  96. } \
  97. } while (false)
  98. #define TF_LITE_MICRO_EXPECT_EQ(x, y) \
  99. do { \
  100. auto vx = x; \
  101. auto vy = y; \
  102. if ((vx) != (vy)) { \
  103. micro_test::reporter->Report(#x " == " #y " failed at %s:%d (%d vs %d)", \
  104. __FILE__, __LINE__, (vx), (vy)); \
  105. micro_test::did_test_fail = true; \
  106. } \
  107. } while (false)
  108. #define TF_LITE_MICRO_EXPECT_NE(x, y) \
  109. do { \
  110. if ((x) == (y)) { \
  111. micro_test::reporter->Report(#x " != " #y " failed at %s:%d", __FILE__, \
  112. __LINE__); \
  113. micro_test::did_test_fail = true; \
  114. } \
  115. } while (false)
  116. // TODO(wangtz): Making it more generic once needed.
  117. #define TF_LITE_MICRO_ARRAY_ELEMENT_EXPECT_NEAR(arr1, idx1, arr2, idx2, \
  118. epsilon) \
  119. do { \
  120. auto delta = ((arr1)[(idx1)] > (arr2)[(idx2)]) \
  121. ? ((arr1)[(idx1)] - (arr2)[(idx2)]) \
  122. : ((arr2)[(idx2)] - (arr1)[(idx1)]); \
  123. if (delta > epsilon) { \
  124. micro_test::reporter->Report( \
  125. #arr1 "[%d] (%f) near " #arr2 "[%d] (%f) failed at %s:%d", \
  126. static_cast<int>(idx1), static_cast<float>((arr1)[(idx1)]), \
  127. static_cast<int>(idx2), static_cast<float>((arr2)[(idx2)]), \
  128. __FILE__, __LINE__); \
  129. micro_test::did_test_fail = true; \
  130. } \
  131. } while (false)
  132. #define TF_LITE_MICRO_EXPECT_NEAR(x, y, epsilon) \
  133. do { \
  134. auto vx = (x); \
  135. auto vy = (y); \
  136. auto delta = ((vx) > (vy)) ? ((vx) - (vy)) : ((vy) - (vx)); \
  137. if (delta > epsilon) { \
  138. micro_test::reporter->Report( \
  139. #x " (%f) near " #y " (%f) failed at %s:%d", static_cast<float>(vx), \
  140. static_cast<float>(vy), __FILE__, __LINE__); \
  141. micro_test::did_test_fail = true; \
  142. } \
  143. } while (false)
  144. #define TF_LITE_MICRO_EXPECT_GT(x, y) \
  145. do { \
  146. if ((x) <= (y)) { \
  147. micro_test::reporter->Report(#x " > " #y " failed at %s:%d", __FILE__, \
  148. __LINE__); \
  149. micro_test::did_test_fail = true; \
  150. } \
  151. } while (false)
  152. #define TF_LITE_MICRO_EXPECT_LT(x, y) \
  153. do { \
  154. if ((x) >= (y)) { \
  155. micro_test::reporter->Report(#x " < " #y " failed at %s:%d", __FILE__, \
  156. __LINE__); \
  157. micro_test::did_test_fail = true; \
  158. } \
  159. } while (false)
  160. #define TF_LITE_MICRO_EXPECT_GE(x, y) \
  161. do { \
  162. if ((x) < (y)) { \
  163. micro_test::reporter->Report(#x " >= " #y " failed at %s:%d", __FILE__, \
  164. __LINE__); \
  165. micro_test::did_test_fail = true; \
  166. } \
  167. } while (false)
  168. #define TF_LITE_MICRO_EXPECT_LE(x, y) \
  169. do { \
  170. if ((x) > (y)) { \
  171. micro_test::reporter->Report(#x " <= " #y " failed at %s:%d", __FILE__, \
  172. __LINE__); \
  173. micro_test::did_test_fail = true; \
  174. } \
  175. } while (false)
  176. #define TF_LITE_MICRO_EXPECT_TRUE(x) \
  177. do { \
  178. if (!(x)) { \
  179. micro_test::reporter->Report(#x " was not true failed at %s:%d", \
  180. __FILE__, __LINE__); \
  181. micro_test::did_test_fail = true; \
  182. } \
  183. } while (false)
  184. #define TF_LITE_MICRO_EXPECT_FALSE(x) \
  185. do { \
  186. if (x) { \
  187. micro_test::reporter->Report(#x " was not false failed at %s:%d", \
  188. __FILE__, __LINE__); \
  189. micro_test::did_test_fail = true; \
  190. } \
  191. } while (false)
  192. #define TF_LITE_MICRO_FAIL(msg) \
  193. do { \
  194. micro_test::reporter->Report("FAIL: %s", msg, __FILE__, __LINE__); \
  195. micro_test::did_test_fail = true; \
  196. } while (false)
  197. #define TF_LITE_MICRO_EXPECT_STRING_EQ(string1, string2) \
  198. do { \
  199. for (int i = 0; string1[i] != '\0' && string2[i] != '\0'; i++) { \
  200. if (string1[i] != string2[i]) { \
  201. micro_test::reporter->Report("FAIL: %s did not match %s", string1, \
  202. string2, __FILE__, __LINE__); \
  203. micro_test::did_test_fail = true; \
  204. } \
  205. } \
  206. } while (false)
  207. #endif // TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_
  208. /* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
  209. Licensed under the Apache License, Version 2.0 (the "License");
  210. you may not use this file except in compliance with the License.
  211. You may obtain a copy of the License at
  212. http://www.apache.org/licenses/LICENSE-2.0
  213. Unless required by applicable law or agreed to in writing, software
  214. distributed under the License is distributed on an "AS IS" BASIS,
  215. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  216. See the License for the specific language governing permissions and
  217. limitations under the License.
  218. ==============================================================================*/
  219. // An ultra-lightweight testing framework designed for use with microcontroller
  220. // applications. Its only dependency is on TensorFlow Lite's ErrorReporter
  221. // interface, where log messages are output. This is designed to be usable even
  222. // when no standard C or C++ libraries are available, and without any dynamic
  223. // memory allocation or reliance on global constructors.
  224. //
  225. // To build a test, you use syntax similar to gunit, but with some extra
  226. // decoration to create a hidden 'main' function containing each of the tests to
  227. // be run. Your code should look something like:
  228. // ----------------------------------------------------------------------------
  229. // #include "path/to/this/header"
  230. //
  231. // TF_LITE_MICRO_TESTS_BEGIN
  232. //
  233. // TF_LITE_MICRO_TEST(SomeTest) {
  234. // TF_LITE_LOG_EXPECT_EQ(true, true);
  235. // }
  236. //
  237. // TF_LITE_MICRO_TESTS_END
  238. // ----------------------------------------------------------------------------
  239. // If you compile this for your platform, you'll get a normal binary that you
  240. // should be able to run. Executing it will output logging information like this
  241. // to stderr (or whatever equivalent is available and written to by
  242. // ErrorReporter):
  243. // ----------------------------------------------------------------------------
  244. // Testing SomeTest
  245. // 1/1 tests passed
  246. // ~~~ALL TESTS PASSED~~~
  247. // ----------------------------------------------------------------------------
  248. // This is designed to be human-readable, so you can just run tests manually,
  249. // but the string "~~~ALL TESTS PASSED~~~" should only appear if all of the
  250. // tests do pass. This makes it possible to integrate with automated test
  251. // systems by scanning the output logs and looking for that magic value.
  252. //
  253. // This framework is intended to be a rudimentary alternative to no testing at
  254. // all on systems that struggle to run more conventional approaches, so use with
  255. // caution!
  256. #ifndef TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_
  257. #define TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_
  258. #include "tensorflow/lite/micro/micro_error_reporter.h"
  259. namespace micro_test {
  260. extern int tests_passed;
  261. extern int tests_failed;
  262. extern bool is_test_complete;
  263. extern bool did_test_fail;
  264. extern tflite::ErrorReporter* reporter;
  265. } // namespace micro_test
  266. #define TF_LITE_MICRO_TESTS_BEGIN \
  267. namespace micro_test { \
  268. int tests_passed; \
  269. int tests_failed; \
  270. bool is_test_complete; \
  271. bool did_test_fail; \
  272. tflite::ErrorReporter* reporter; \
  273. } \
  274. \
  275. int main(int argc, char** argv) { \
  276. micro_test::tests_passed = 0; \
  277. micro_test::tests_failed = 0; \
  278. tflite::MicroErrorReporter error_reporter; \
  279. micro_test::reporter = &error_reporter;
  280. #define TF_LITE_MICRO_TESTS_END \
  281. micro_test::reporter->Report( \
  282. "%d/%d tests passed", micro_test::tests_passed, \
  283. (micro_test::tests_failed + micro_test::tests_passed)); \
  284. if (micro_test::tests_failed == 0) { \
  285. micro_test::reporter->Report("~~~ALL TESTS PASSED~~~\n"); \
  286. } else { \
  287. micro_test::reporter->Report("~~~SOME TESTS FAILED~~~\n"); \
  288. } \
  289. }
  290. // TODO(petewarden): I'm going to hell for what I'm doing to this poor for loop.
  291. #define TF_LITE_MICRO_TEST(name) \
  292. micro_test::reporter->Report("Testing " #name); \
  293. for (micro_test::is_test_complete = false, \
  294. micro_test::did_test_fail = false; \
  295. !micro_test::is_test_complete; micro_test::is_test_complete = true, \
  296. micro_test::tests_passed += (micro_test::did_test_fail) ? 0 : 1, \
  297. micro_test::tests_failed += (micro_test::did_test_fail) ? 1 : 0)
  298. #define TF_LITE_MICRO_EXPECT(x) \
  299. do { \
  300. if (!(x)) { \
  301. micro_test::reporter->Report(#x " failed at %s:%d", __FILE__, __LINE__); \
  302. micro_test::did_test_fail = true; \
  303. } \
  304. } while (false)
  305. #define TF_LITE_MICRO_EXPECT_EQ(x, y) \
  306. do { \
  307. auto vx = x; \
  308. auto vy = y; \
  309. if ((vx) != (vy)) { \
  310. micro_test::reporter->Report(#x " == " #y " failed at %s:%d (%d vs %d)", \
  311. __FILE__, __LINE__, (vx), (vy)); \
  312. micro_test::did_test_fail = true; \
  313. } \
  314. } while (false)
  315. #define TF_LITE_MICRO_EXPECT_NE(x, y) \
  316. do { \
  317. if ((x) == (y)) { \
  318. micro_test::reporter->Report(#x " != " #y " failed at %s:%d", __FILE__, \
  319. __LINE__); \
  320. micro_test::did_test_fail = true; \
  321. } \
  322. } while (false)
  323. // TODO(wangtz): Making it more generic once needed.
  324. #define TF_LITE_MICRO_ARRAY_ELEMENT_EXPECT_NEAR(arr1, idx1, arr2, idx2, \
  325. epsilon) \
  326. do { \
  327. auto delta = ((arr1)[(idx1)] > (arr2)[(idx2)]) \
  328. ? ((arr1)[(idx1)] - (arr2)[(idx2)]) \
  329. : ((arr2)[(idx2)] - (arr1)[(idx1)]); \
  330. if (delta > epsilon) { \
  331. micro_test::reporter->Report( \
  332. #arr1 "[%d] (%f) near " #arr2 "[%d] (%f) failed at %s:%d", \
  333. static_cast<int>(idx1), static_cast<float>((arr1)[(idx1)]), \
  334. static_cast<int>(idx2), static_cast<float>((arr2)[(idx2)]), \
  335. __FILE__, __LINE__); \
  336. micro_test::did_test_fail = true; \
  337. } \
  338. } while (false)
  339. #define TF_LITE_MICRO_EXPECT_NEAR(x, y, epsilon) \
  340. do { \
  341. auto vx = (x); \
  342. auto vy = (y); \
  343. auto delta = ((vx) > (vy)) ? ((vx) - (vy)) : ((vy) - (vx)); \
  344. if (delta > epsilon) { \
  345. micro_test::reporter->Report( \
  346. #x " (%f) near " #y " (%f) failed at %s:%d", static_cast<float>(vx), \
  347. static_cast<float>(vy), __FILE__, __LINE__); \
  348. micro_test::did_test_fail = true; \
  349. } \
  350. } while (false)
  351. #define TF_LITE_MICRO_EXPECT_GT(x, y) \
  352. do { \
  353. if ((x) <= (y)) { \
  354. micro_test::reporter->Report(#x " > " #y " failed at %s:%d", __FILE__, \
  355. __LINE__); \
  356. micro_test::did_test_fail = true; \
  357. } \
  358. } while (false)
  359. #define TF_LITE_MICRO_EXPECT_LT(x, y) \
  360. do { \
  361. if ((x) >= (y)) { \
  362. micro_test::reporter->Report(#x " < " #y " failed at %s:%d", __FILE__, \
  363. __LINE__); \
  364. micro_test::did_test_fail = true; \
  365. } \
  366. } while (false)
  367. #define TF_LITE_MICRO_EXPECT_GE(x, y) \
  368. do { \
  369. if ((x) < (y)) { \
  370. micro_test::reporter->Report(#x " >= " #y " failed at %s:%d", __FILE__, \
  371. __LINE__); \
  372. micro_test::did_test_fail = true; \
  373. } \
  374. } while (false)
  375. #define TF_LITE_MICRO_EXPECT_LE(x, y) \
  376. do { \
  377. if ((x) > (y)) { \
  378. micro_test::reporter->Report(#x " <= " #y " failed at %s:%d", __FILE__, \
  379. __LINE__); \
  380. micro_test::did_test_fail = true; \
  381. } \
  382. } while (false)
  383. #define TF_LITE_MICRO_EXPECT_TRUE(x) \
  384. do { \
  385. if (!(x)) { \
  386. micro_test::reporter->Report(#x " was not true failed at %s:%d", \
  387. __FILE__, __LINE__); \
  388. micro_test::did_test_fail = true; \
  389. } \
  390. } while (false)
  391. #define TF_LITE_MICRO_EXPECT_FALSE(x) \
  392. do { \
  393. if (x) { \
  394. micro_test::reporter->Report(#x " was not false failed at %s:%d", \
  395. __FILE__, __LINE__); \
  396. micro_test::did_test_fail = true; \
  397. } \
  398. } while (false)
  399. #define TF_LITE_MICRO_FAIL(msg) \
  400. do { \
  401. micro_test::reporter->Report("FAIL: %s", msg, __FILE__, __LINE__); \
  402. micro_test::did_test_fail = true; \
  403. } while (false)
  404. #define TF_LITE_MICRO_EXPECT_STRING_EQ(string1, string2) \
  405. do { \
  406. for (int i = 0; string1[i] != '\0' && string2[i] != '\0'; i++) { \
  407. if (string1[i] != string2[i]) { \
  408. micro_test::reporter->Report("FAIL: %s did not match %s", string1, \
  409. string2, __FILE__, __LINE__); \
  410. micro_test::did_test_fail = true; \
  411. } \
  412. } \
  413. } while (false)
  414. #endif // TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_