test_cxx.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #include <vector>
  2. #include <algorithm>
  3. #include "unity.h"
  4. #include "esp_log.h"
  5. #include "freertos/FreeRTOS.h"
  6. #include "freertos/task.h"
  7. #include "freertos/semphr.h"
  8. static const char* TAG = "cxx";
  9. TEST_CASE("can use new and delete", "[cxx]")
  10. {
  11. int* int_p = new int(10);
  12. delete int_p;
  13. int* int_array = new int[10];
  14. delete[] int_array;
  15. }
  16. class Base
  17. {
  18. public:
  19. virtual ~Base() {}
  20. virtual void foo() = 0;
  21. };
  22. class Derived : public Base
  23. {
  24. public:
  25. virtual void foo() { }
  26. };
  27. TEST_CASE("can call virtual functions", "[cxx]")
  28. {
  29. Derived d;
  30. Base& b = static_cast<Base&>(d);
  31. b.foo();
  32. }
  33. class NonPOD
  34. {
  35. public:
  36. NonPOD(int a_) : a(a_) { }
  37. int a;
  38. };
  39. static int non_pod_test_helper(int new_val)
  40. {
  41. static NonPOD non_pod(42);
  42. int ret = non_pod.a;
  43. non_pod.a = new_val;
  44. return ret;
  45. }
  46. TEST_CASE("can use static initializers for non-POD types", "[cxx]")
  47. {
  48. TEST_ASSERT_EQUAL(42, non_pod_test_helper(1));
  49. TEST_ASSERT_EQUAL(1, non_pod_test_helper(0));
  50. }
  51. TEST_CASE("can use std::vector", "[cxx]")
  52. {
  53. std::vector<int> v(10, 1);
  54. v[0] = 42;
  55. TEST_ASSERT_EQUAL(51, std::accumulate(std::begin(v), std::end(v), 0));
  56. }
  57. /*
  58. * This test exercises static initialization guards for two objects.
  59. * For each object, 4 tasks are created which attempt to perform static initialization.
  60. * We check that constructor runs only once for each object.
  61. */
  62. static SemaphoreHandle_t s_slow_init_sem = NULL;
  63. template<int obj>
  64. class SlowInit
  65. {
  66. public:
  67. SlowInit(int arg) {
  68. ESP_LOGD(TAG, "init obj=%d start, arg=%d\n", obj, arg);
  69. vTaskDelay(300/portTICK_PERIOD_MS);
  70. TEST_ASSERT_EQUAL(-1, mInitBy);
  71. TEST_ASSERT_EQUAL(0, mInitCount);
  72. mInitBy = arg;
  73. ++mInitCount;
  74. ESP_LOGD(TAG, "init obj=%d done\n", obj);
  75. }
  76. static void task(void* arg) {
  77. int taskId = reinterpret_cast<int>(arg);
  78. ESP_LOGD(TAG, "obj=%d before static init, task=%d\n", obj, taskId);
  79. static SlowInit slowinit(taskId);
  80. ESP_LOGD(TAG, "obj=%d after static init, task=%d\n", obj, taskId);
  81. xSemaphoreGive(s_slow_init_sem);
  82. vTaskDelete(NULL);
  83. }
  84. private:
  85. static int mInitBy;
  86. static int mInitCount;
  87. };
  88. template<> int SlowInit<1>::mInitBy = -1;
  89. template<> int SlowInit<1>::mInitCount = 0;
  90. template<> int SlowInit<2>::mInitBy = -1;
  91. template<> int SlowInit<2>::mInitCount = 0;
  92. template<int obj>
  93. static void start_slow_init_task(int id, int affinity)
  94. {
  95. xTaskCreatePinnedToCore(&SlowInit<obj>::task, "slow_init", 2048,
  96. reinterpret_cast<void*>(id), 3, NULL, affinity);
  97. }
  98. TEST_CASE("static initialization guards work as expected", "[cxx]")
  99. {
  100. s_slow_init_sem = xSemaphoreCreateCounting(10, 0);
  101. TEST_ASSERT_NOT_NULL(s_slow_init_sem);
  102. // four tasks competing for static initialization of one object
  103. start_slow_init_task<1>(0, PRO_CPU_NUM);
  104. start_slow_init_task<1>(1, APP_CPU_NUM);
  105. start_slow_init_task<1>(2, PRO_CPU_NUM);
  106. start_slow_init_task<1>(3, tskNO_AFFINITY);
  107. // four tasks competing for static initialization of another object
  108. start_slow_init_task<2>(0, PRO_CPU_NUM);
  109. start_slow_init_task<2>(1, APP_CPU_NUM);
  110. start_slow_init_task<2>(2, PRO_CPU_NUM);
  111. start_slow_init_task<2>(3, tskNO_AFFINITY);
  112. // All tasks should
  113. for (int i = 0; i < 8; ++i) {
  114. TEST_ASSERT_TRUE(xSemaphoreTake(s_slow_init_sem, 500/portTICK_PERIOD_MS));
  115. }
  116. vSemaphoreDelete(s_slow_init_sem);
  117. vTaskDelay(10); // Allow tasks to clean up, avoids race with leak detector
  118. }
  119. struct GlobalInitTest
  120. {
  121. GlobalInitTest() : index(order++) {
  122. }
  123. int index;
  124. static int order;
  125. };
  126. int GlobalInitTest::order = 0;
  127. GlobalInitTest g_init_test1;
  128. GlobalInitTest g_init_test2;
  129. GlobalInitTest g_init_test3;
  130. TEST_CASE("global initializers run in the correct order", "[cxx]")
  131. {
  132. TEST_ASSERT_EQUAL(0, g_init_test1.index);
  133. TEST_ASSERT_EQUAL(1, g_init_test2.index);
  134. TEST_ASSERT_EQUAL(2, g_init_test3.index);
  135. }
  136. struct StaticInitTestBeforeScheduler
  137. {
  138. StaticInitTestBeforeScheduler()
  139. {
  140. static int first_init_order = getOrder();
  141. index = first_init_order;
  142. }
  143. int getOrder()
  144. {
  145. return order++;
  146. }
  147. int index;
  148. static int order;
  149. };
  150. int StaticInitTestBeforeScheduler::order = 1;
  151. StaticInitTestBeforeScheduler g_static_init_test1;
  152. StaticInitTestBeforeScheduler g_static_init_test2;
  153. StaticInitTestBeforeScheduler g_static_init_test3;
  154. TEST_CASE("before scheduler has started, static initializers work correctly", "[cxx]")
  155. {
  156. TEST_ASSERT_EQUAL(1, g_static_init_test1.index);
  157. TEST_ASSERT_EQUAL(1, g_static_init_test2.index);
  158. TEST_ASSERT_EQUAL(1, g_static_init_test3.index);
  159. TEST_ASSERT_EQUAL(2, StaticInitTestBeforeScheduler::order);
  160. }
  161. #ifdef CONFIG_CXX_EXCEPTIONS
  162. TEST_CASE("c++ exceptions work", "[cxx]")
  163. {
  164. /* Note: When first exception (in system) is thrown this test produces memory leaks report (~500 bytes):
  165. - 392 bytes (can vary) as libunwind allocates memory to keep stack frames info to handle exceptions.
  166. This info is kept until global destructors are called by __do_global_dtors_aux()
  167. - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals
  168. - 16 bytes are allocated by pthread_setspecific() which is called by __cxa_get_globals() to init TLS var for __cxa_eh_globals
  169. - 88 bytes are allocated by pthread_setspecific() to init internal lock
  170. */
  171. int thrown_value;
  172. try
  173. {
  174. throw 20;
  175. }
  176. catch (int e)
  177. {
  178. thrown_value = e;
  179. }
  180. TEST_ASSERT_EQUAL(20, thrown_value);
  181. printf("OK?\n");
  182. }
  183. TEST_CASE("c++ exceptions emergency pool", "[cxx] [ignore]")
  184. {
  185. /* Note: When first exception (in system) is thrown this test produces memory leaks report (~500 bytes):
  186. - 392 bytes (can vary) as libunwind allocates memory to keep stack frames info to handle exceptions.
  187. This info is kept until global destructors are called by __do_global_dtors_aux()
  188. - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals
  189. - 16 bytes are allocated by pthread_setspecific() which is called by __cxa_get_globals() to init TLS var for __cxa_eh_globals
  190. - 88 bytes are allocated by pthread_setspecific() to init internal lock
  191. */
  192. void **p, **pprev = NULL;
  193. int thrown_value = 0;
  194. // throw first exception to ensure that all initial allocations are made
  195. try
  196. {
  197. throw 33;
  198. }
  199. catch (int e)
  200. {
  201. thrown_value = e;
  202. }
  203. TEST_ASSERT_EQUAL(33, thrown_value);
  204. // consume all dynamic memory
  205. while ((p = (void **)malloc(sizeof(void *)))) {
  206. if (pprev) {
  207. *p = pprev;
  208. } else {
  209. *p = NULL;
  210. }
  211. pprev = p;
  212. }
  213. try
  214. {
  215. throw 20;
  216. }
  217. catch (int e)
  218. {
  219. thrown_value = e;
  220. printf("Got exception %d\n", thrown_value);
  221. }
  222. #if CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE > 0
  223. // free all memory
  224. while (pprev) {
  225. p = (void **)(*pprev);
  226. free(pprev);
  227. pprev = p;
  228. }
  229. TEST_ASSERT_EQUAL(20, thrown_value);
  230. #else
  231. // if emergency pool is disabled we should never get here,
  232. // expect abort() due to lack of memory for new exception
  233. TEST_ASSERT_TRUE(0 == 1);
  234. #endif
  235. }
  236. #endif
  237. /* These test cases pull a lot of code from libstdc++ and are disabled for now
  238. */
  239. #if 0
  240. #include <iostream>
  241. #include <functional>
  242. TEST_CASE("can use iostreams", "[cxx]")
  243. {
  244. std::cout << "hello world";
  245. }
  246. TEST_CASE("can call std::function and bind", "[cxx]")
  247. {
  248. int outer = 1;
  249. std::function<int(int)> fn = [&outer](int x) -> int {
  250. return x + outer;
  251. };
  252. outer = 5;
  253. TEST_ASSERT_EQUAL(6, fn(1));
  254. auto bound = std::bind(fn, outer);
  255. outer = 10;
  256. TEST_ASSERT_EQUAL(15, bound());
  257. }
  258. #endif