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