test_cxx.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. #include <vector>
  2. #include <numeric>
  3. #include <stdexcept>
  4. #include <string>
  5. #include "unity.h"
  6. #include "esp_log.h"
  7. #include "freertos/FreeRTOS.h"
  8. #include "freertos/task.h"
  9. #include "freertos/semphr.h"
  10. #include "soc/soc.h"
  11. TEST_CASE("can use new and delete", "[cxx]")
  12. {
  13. int* int_p = new int(10);
  14. delete int_p;
  15. int* int_array = new int[10];
  16. delete[] int_array;
  17. }
  18. class Base
  19. {
  20. public:
  21. virtual ~Base() {}
  22. virtual void foo() = 0;
  23. };
  24. class Derived : public Base
  25. {
  26. public:
  27. virtual void foo() { }
  28. };
  29. TEST_CASE("can call virtual functions", "[cxx]")
  30. {
  31. Derived d;
  32. Base& b = static_cast<Base&>(d);
  33. b.foo();
  34. }
  35. TEST_CASE("can use std::vector", "[cxx]")
  36. {
  37. std::vector<int> v(10, 1);
  38. v[0] = 42;
  39. TEST_ASSERT_EQUAL(51, std::accumulate(std::begin(v), std::end(v), 0));
  40. }
  41. /* Note: When first exception (in system) is thrown this test produces memory leaks report (~300 bytes):
  42. - 392 bytes (can vary) as libunwind allocates memory to keep stack frames info to handle exceptions.
  43. This info is kept until global destructors are called by __do_global_dtors_aux()
  44. - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals
  45. - 16 bytes are allocated by pthread_setspecific() which is called by __cxa_get_globals() to init TLS var for __cxa_eh_globals
  46. - 88 bytes are allocated by pthread_setspecific() to init internal lock
  47. - some more memory...
  48. */
  49. #ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
  50. TEST_CASE("c++ exceptions work", "[cxx] [exceptions] [leaks=300]")
  51. {
  52. int thrown_value;
  53. try {
  54. throw 20;
  55. } catch (int e) {
  56. thrown_value = e;
  57. }
  58. TEST_ASSERT_EQUAL(20, thrown_value);
  59. printf("OK?\n");
  60. }
  61. TEST_CASE("c++ bool exception", "[cxx] [exceptions] [leaks=300]")
  62. {
  63. bool thrown_value = false;
  64. try {
  65. throw true;
  66. } catch (bool e) {
  67. thrown_value = e;
  68. }
  69. TEST_ASSERT_EQUAL(true, thrown_value);
  70. printf("OK?\n");
  71. }
  72. TEST_CASE("c++ void exception", "[cxx] [exceptions] [leaks=300]")
  73. {
  74. void* thrown_value = 0;
  75. try {
  76. throw (void*) 47;
  77. } catch (void* e) {
  78. thrown_value = e;
  79. }
  80. TEST_ASSERT_EQUAL(47, thrown_value);
  81. printf("OK?\n");
  82. }
  83. TEST_CASE("c++ uint64_t exception", "[cxx] [exceptions] [leaks=300]")
  84. {
  85. uint64_t thrown_value = 0;
  86. try {
  87. throw (uint64_t) 47;
  88. } catch (uint64_t e) {
  89. thrown_value = e;
  90. }
  91. TEST_ASSERT_EQUAL(47, thrown_value);
  92. printf("OK?\n");
  93. }
  94. TEST_CASE("c++ char exception", "[cxx] [exceptions] [leaks=300]")
  95. {
  96. char thrown_value = '0';
  97. try {
  98. throw '/';
  99. } catch (char e) {
  100. thrown_value = e;
  101. }
  102. TEST_ASSERT_EQUAL('/', thrown_value);
  103. printf("OK?\n");
  104. }
  105. TEST_CASE("c++ wchar exception", "[cxx] [exceptions] [leaks=300]")
  106. {
  107. wchar_t thrown_value = 0;
  108. try {
  109. throw (wchar_t) 47;
  110. } catch (wchar_t e) {
  111. thrown_value = e;
  112. }
  113. TEST_ASSERT_EQUAL((wchar_t) 47, thrown_value);
  114. printf("OK?\n");
  115. }
  116. TEST_CASE("c++ float exception", "[cxx] [exceptions] [leaks=300]")
  117. {
  118. float thrown_value = 0;
  119. try {
  120. throw 23.5f;
  121. } catch (float e) {
  122. thrown_value = e;
  123. }
  124. TEST_ASSERT_EQUAL(23.5, thrown_value);
  125. printf("OK?\n");
  126. }
  127. TEST_CASE("c++ double exception", "[cxx] [exceptions] [leaks=300]")
  128. {
  129. double thrown_value = 0;
  130. try {
  131. throw 23.5d;
  132. } catch (double e) {
  133. thrown_value = e;
  134. }
  135. TEST_ASSERT_EQUAL(23.5d, thrown_value);
  136. printf("OK?\n");
  137. }
  138. TEST_CASE("c++ const char* exception", "[cxx] [exceptions] [leaks=300]")
  139. {
  140. const char *thrown_value = 0;
  141. try {
  142. throw "Hi :)";
  143. } catch (const char *e) {
  144. thrown_value = e;
  145. }
  146. TEST_ASSERT_EQUAL_STRING("Hi :)", thrown_value);
  147. printf("OK?\n");
  148. }
  149. struct NonExcTypeThrowee {
  150. int value;
  151. public:
  152. NonExcTypeThrowee(int value) : value(value) { }
  153. };
  154. TEST_CASE("c++ any class exception", "[cxx] [exceptions] [leaks=300]")
  155. {
  156. int thrown_value = 0;
  157. try {
  158. throw NonExcTypeThrowee(47);
  159. } catch (NonExcTypeThrowee &e) {
  160. thrown_value = e.value;
  161. }
  162. TEST_ASSERT_EQUAL(47, thrown_value);
  163. printf("OK?\n");
  164. }
  165. struct ExcTypeThrowee : public std::exception {
  166. int value;
  167. public:
  168. ExcTypeThrowee(int value) : value(value) { }
  169. };
  170. TEST_CASE("c++ std::exception child", "[cxx] [exceptions] [leaks=300]")
  171. {
  172. int thrown_value = 0;
  173. try {
  174. throw ExcTypeThrowee(47);
  175. } catch (ExcTypeThrowee &e) {
  176. thrown_value = e.value;
  177. }
  178. TEST_ASSERT_EQUAL(47, thrown_value);
  179. printf("OK?\n");
  180. }
  181. TEST_CASE("c++ exceptions emergency pool", "[cxx] [exceptions] [ignore] [leaks=300]")
  182. {
  183. void **p, **pprev = NULL;
  184. int thrown_value = 0;
  185. // throw first exception to ensure that all initial allocations are made
  186. try
  187. {
  188. throw 33;
  189. }
  190. catch (int e)
  191. {
  192. thrown_value = e;
  193. }
  194. TEST_ASSERT_EQUAL(33, thrown_value);
  195. // consume all dynamic memory
  196. while ((p = (void **)malloc(sizeof(void *)))) {
  197. if (pprev) {
  198. *p = pprev;
  199. } else {
  200. *p = NULL;
  201. }
  202. pprev = p;
  203. }
  204. try
  205. {
  206. throw 20;
  207. }
  208. catch (int e)
  209. {
  210. thrown_value = e;
  211. printf("Got exception %d\n", thrown_value);
  212. }
  213. #if CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE > 0
  214. // free all memory
  215. while (pprev) {
  216. p = (void **)(*pprev);
  217. free(pprev);
  218. pprev = p;
  219. }
  220. TEST_ASSERT_EQUAL(20, thrown_value);
  221. #else
  222. // if emergency pool is disabled we should never get here,
  223. // expect abort() due to lack of memory for new exception
  224. TEST_ASSERT_TRUE(0 == 1);
  225. #endif
  226. }
  227. #define TIMEOUT 19
  228. #define RECURSION 19
  229. static esp_timer_handle_t crash_timer;
  230. static uint32_t result = 0;
  231. uint32_t calc_fac(uint32_t n) {
  232. if (n == 1 || n == 0) {
  233. return 1;
  234. } else {
  235. return n * calc_fac(n - 1);
  236. }
  237. }
  238. static void timer_cb(void *arg) {
  239. result = calc_fac(RECURSION);
  240. }
  241. // TODO: Not a unit test, refactor to integration test/system test, etc.
  242. TEST_CASE("frequent interrupts don't interfere with c++ exceptions", "[cxx] [exceptions] [leaks=800]")
  243. {// if exception workaround is disabled, this is almost guaranteed to fail
  244. const esp_timer_create_args_t timer_args {
  245. timer_cb,
  246. NULL,
  247. ESP_TIMER_TASK,
  248. "crash_timer"
  249. };
  250. TEST_ESP_OK(esp_timer_create(&timer_args, &crash_timer));
  251. TEST_ESP_OK(esp_timer_start_periodic(crash_timer, TIMEOUT));
  252. for (int i = 0; i < 500; i++) {
  253. bool thrown_value = false;
  254. try {
  255. throw true;
  256. } catch (bool e) {
  257. thrown_value = e;
  258. }
  259. if (thrown_value) {
  260. printf("ex thrown %d\n", i);
  261. } else {
  262. printf("ex not thrown\n");
  263. TEST_ASSERT(false);
  264. }
  265. }
  266. TEST_ESP_OK(esp_timer_stop(crash_timer));
  267. TEST_ESP_OK(esp_timer_delete(crash_timer));
  268. }
  269. #else // !CONFIG_COMPILER_CXX_EXCEPTIONS
  270. TEST_CASE("std::out_of_range exception when -fno-exceptions", "[cxx][reset=abort,SW_CPU_RESET]")
  271. {
  272. std::vector<int> v(10);
  273. v.at(20) = 42;
  274. TEST_FAIL_MESSAGE("Unreachable because we are aborted on the line above");
  275. }
  276. TEST_CASE("std::bad_alloc exception when -fno-exceptions", "[cxx][reset=abort,SW_CPU_RESET]")
  277. {
  278. std::string s = std::string(2000000000, 'a');
  279. (void)s;
  280. TEST_FAIL_MESSAGE("Unreachable because we are aborted on the line above");
  281. }
  282. #endif
  283. /* These test cases pull a lot of code from libstdc++ and are disabled for now
  284. */
  285. #if 0
  286. #include <iostream>
  287. #include <functional>
  288. TEST_CASE("can use iostreams", "[cxx]")
  289. {
  290. std::cout << "hello world";
  291. }
  292. TEST_CASE("can call std::function and bind", "[cxx]")
  293. {
  294. int outer = 1;
  295. std::function<int(int)> fn = [&outer](int x) -> int {
  296. return x + outer;
  297. };
  298. outer = 5;
  299. TEST_ASSERT_EQUAL(6, fn(1));
  300. auto bound = std::bind(fn, outer);
  301. outer = 10;
  302. TEST_ASSERT_EQUAL(15, bound());
  303. }
  304. #endif
  305. /* Tests below are done in the compile time, don't actually get run. */
  306. /* Check whether a enumerator flag can be used in C++ */
  307. template<typename T> __attribute__((unused)) static void test_binary_operators()
  308. {
  309. T flag1 = (T)0;
  310. T flag2 = (T)0;
  311. flag1 = ~flag1;
  312. flag1 = flag1 | flag2;
  313. flag1 = flag1 & flag2;
  314. flag1 = flag1 ^ flag2;
  315. flag1 = flag1 >> 2;
  316. flag1 = flag1 << 2;
  317. flag1 |= flag2;
  318. flag1 &= flag2;
  319. flag1 ^= flag2;
  320. flag1 >>= 2;
  321. flag1 <<= 2;
  322. }
  323. //Add more types here. If any flags cannot pass the build, use FLAG_ATTR in esp_attr.h
  324. #include "hal/timer_types.h"
  325. template void test_binary_operators<timer_intr_t>();