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