test_cxx.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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 int start_slow_init_task(int id, int affinity)
  94. {
  95. return xTaskCreatePinnedToCore(&SlowInit<obj>::task, "slow_init", 2048,
  96. reinterpret_cast<void*>(id), 3, NULL, affinity) ? 1 : 0;
  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. int task_count = 0;
  103. // four tasks competing for static initialization of one object
  104. task_count += start_slow_init_task<1>(0, PRO_CPU_NUM);
  105. #if portNUM_PROCESSORS == 2
  106. task_count += start_slow_init_task<1>(1, APP_CPU_NUM);
  107. #endif
  108. task_count += start_slow_init_task<1>(2, PRO_CPU_NUM);
  109. task_count += start_slow_init_task<1>(3, tskNO_AFFINITY);
  110. // four tasks competing for static initialization of another object
  111. task_count += start_slow_init_task<2>(0, PRO_CPU_NUM);
  112. #if portNUM_PROCESSORS == 2
  113. task_count += start_slow_init_task<2>(1, APP_CPU_NUM);
  114. #endif
  115. task_count += start_slow_init_task<2>(2, PRO_CPU_NUM);
  116. task_count += start_slow_init_task<2>(3, tskNO_AFFINITY);
  117. // All tasks should
  118. for (int i = 0; i < task_count; ++i) {
  119. TEST_ASSERT_TRUE(xSemaphoreTake(s_slow_init_sem, 500/portTICK_PERIOD_MS));
  120. }
  121. vSemaphoreDelete(s_slow_init_sem);
  122. vTaskDelay(10); // Allow tasks to clean up, avoids race with leak detector
  123. }
  124. struct GlobalInitTest
  125. {
  126. GlobalInitTest() : index(order++) {
  127. }
  128. int index;
  129. static int order;
  130. };
  131. int GlobalInitTest::order = 0;
  132. GlobalInitTest g_init_test1;
  133. GlobalInitTest g_init_test2;
  134. GlobalInitTest g_init_test3;
  135. TEST_CASE("global initializers run in the correct order", "[cxx]")
  136. {
  137. TEST_ASSERT_EQUAL(0, g_init_test1.index);
  138. TEST_ASSERT_EQUAL(1, g_init_test2.index);
  139. TEST_ASSERT_EQUAL(2, g_init_test3.index);
  140. }
  141. struct StaticInitTestBeforeScheduler
  142. {
  143. StaticInitTestBeforeScheduler()
  144. {
  145. static int first_init_order = getOrder();
  146. index = first_init_order;
  147. }
  148. int getOrder()
  149. {
  150. return order++;
  151. }
  152. int index;
  153. static int order;
  154. };
  155. int StaticInitTestBeforeScheduler::order = 1;
  156. StaticInitTestBeforeScheduler g_static_init_test1;
  157. StaticInitTestBeforeScheduler g_static_init_test2;
  158. StaticInitTestBeforeScheduler g_static_init_test3;
  159. TEST_CASE("before scheduler has started, static initializers work correctly", "[cxx]")
  160. {
  161. TEST_ASSERT_EQUAL(1, g_static_init_test1.index);
  162. TEST_ASSERT_EQUAL(1, g_static_init_test2.index);
  163. TEST_ASSERT_EQUAL(1, g_static_init_test3.index);
  164. TEST_ASSERT_EQUAL(2, StaticInitTestBeforeScheduler::order);
  165. }
  166. #ifdef CONFIG_CXX_EXCEPTIONS
  167. TEST_CASE("c++ exceptions work", "[cxx]")
  168. {
  169. /* Note: When first exception (in system) is thrown this test produces memory leaks report (~500 bytes):
  170. - 392 bytes (can vary) as libunwind allocates memory to keep stack frames info to handle exceptions.
  171. This info is kept until global destructors are called by __do_global_dtors_aux()
  172. - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals
  173. - 16 bytes are allocated by pthread_setspecific() which is called by __cxa_get_globals() to init TLS var for __cxa_eh_globals
  174. - 88 bytes are allocated by pthread_setspecific() to init internal lock
  175. */
  176. int thrown_value;
  177. try
  178. {
  179. throw 20;
  180. }
  181. catch (int e)
  182. {
  183. thrown_value = e;
  184. }
  185. TEST_ASSERT_EQUAL(20, thrown_value);
  186. printf("OK?\n");
  187. }
  188. TEST_CASE("c++ exceptions emergency pool", "[cxx] [ignore]")
  189. {
  190. /* Note: When first exception (in system) is thrown this test produces memory leaks report (~500 bytes):
  191. - 392 bytes (can vary) as libunwind allocates memory to keep stack frames info to handle exceptions.
  192. This info is kept until global destructors are called by __do_global_dtors_aux()
  193. - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals
  194. - 16 bytes are allocated by pthread_setspecific() which is called by __cxa_get_globals() to init TLS var for __cxa_eh_globals
  195. - 88 bytes are allocated by pthread_setspecific() to init internal lock
  196. */
  197. void **p, **pprev = NULL;
  198. int thrown_value = 0;
  199. // throw first exception to ensure that all initial allocations are made
  200. try
  201. {
  202. throw 33;
  203. }
  204. catch (int e)
  205. {
  206. thrown_value = e;
  207. }
  208. TEST_ASSERT_EQUAL(33, thrown_value);
  209. // consume all dynamic memory
  210. while ((p = (void **)malloc(sizeof(void *)))) {
  211. if (pprev) {
  212. *p = pprev;
  213. } else {
  214. *p = NULL;
  215. }
  216. pprev = p;
  217. }
  218. try
  219. {
  220. throw 20;
  221. }
  222. catch (int e)
  223. {
  224. thrown_value = e;
  225. printf("Got exception %d\n", thrown_value);
  226. }
  227. #if CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE > 0
  228. // free all memory
  229. while (pprev) {
  230. p = (void **)(*pprev);
  231. free(pprev);
  232. pprev = p;
  233. }
  234. TEST_ASSERT_EQUAL(20, thrown_value);
  235. #else
  236. // if emergency pool is disabled we should never get here,
  237. // expect abort() due to lack of memory for new exception
  238. TEST_ASSERT_TRUE(0 == 1);
  239. #endif
  240. }
  241. #endif
  242. /* These test cases pull a lot of code from libstdc++ and are disabled for now
  243. */
  244. #if 0
  245. #include <iostream>
  246. #include <functional>
  247. TEST_CASE("can use iostreams", "[cxx]")
  248. {
  249. std::cout << "hello world";
  250. }
  251. TEST_CASE("can call std::function and bind", "[cxx]")
  252. {
  253. int outer = 1;
  254. std::function<int(int)> fn = [&outer](int x) -> int {
  255. return x + outer;
  256. };
  257. outer = 5;
  258. TEST_ASSERT_EQUAL(6, fn(1));
  259. auto bound = std::bind(fn, outer);
  260. outer = 10;
  261. TEST_ASSERT_EQUAL(15, bound());
  262. }
  263. #endif