GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tests.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 119 191 62.3%
Functions: 17 25 68.0%
Branches: 29 70 41.4%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/tests.h"
30 #include "gate/streams.h"
31 #include "gate/strings.h"
32 #include "gate/console.h"
33 #include "gate/arrays.h"
34
35 typedef struct gate_test_session_class
36 {
37 gate_stream_t* stream;
38 gate_test_report_t reports[256];
39 unsigned reports_used;
40 gate_bool_t save_checks;
41 gate_linkedlist_t checks;
42 } gate_test_session_t;
43
44 typedef struct gate_test_history_class
45 {
46 char const* test_name;
47 char message[128];
48 gate_bool_t failed;
49 char const* origin;
50 unsigned line;
51
52 } gate_test_history_t;
53
54 static gate_test_session_t test_session = GATE_INIT_EMPTY;
55
56
57 200 static gate_test_report_t* gate_test_register(char const* unit_name)
58 {
59 200 gate_test_report_t* ptr = NULL;
60 200 gate_size_t unit_name_len = gate_str_length(unit_name);
61 200 gate_size_t const max_reports = sizeof(test_session.reports) / sizeof(test_session.reports[0]);
62
1/2
✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
200 if (test_session.reports_used < max_reports)
63 {
64 200 ptr = &test_session.reports[test_session.reports_used];
65 200 ++test_session.reports_used;
66 200 gate_mem_clear(ptr, sizeof(gate_test_report_t));
67 200 gate_str_print_text(ptr->name, sizeof(ptr->name), unit_name, unit_name_len);
68 200 ptr->test_counter = 0;
69 200 ptr->error_counter = 0;
70 }
71 else
72 {
73 ptr = &test_session.reports[max_reports - 1];
74 }
75 200 return ptr;
76 }
77
78 9125 static gate_test_report_t* gate_test_current_unit()
79 {
80
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9125 times.
9125 if (test_session.reports_used == 0)
81 {
82 return gate_test_register("unnamed_unit_test");
83 }
84 9125 return &test_session.reports[test_session.reports_used - 1];
85 }
86
87 216 static gate_stream_t* gate_test_current_stream()
88 {
89 216 return test_session.stream;
90 }
91
92 200 static void gate_test_print_source(gate_stream_t* stream, char const* filepath, unsigned codeline)
93 {
94
1/2
✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
200 if (filepath)
95 {
96 200 gate_stream_print(stream,
97 GATE_PRINT_CSTR, filepath,
98 GATE_PRINT_CSTR, "(",
99 GATE_PRINT_UI32, (gate_uint32_t)codeline,
100 GATE_PRINT_CSTR, "): ",
101 GATE_PRINT_END
102 );
103 }
104 200 }
105
106 200 static void gate_test_print_message(char const* msg_type, char const* message, char const* filepath, unsigned codeline)
107 {
108 200 gate_stream_t* stream = gate_test_current_stream();
109
110 200 gate_test_print_source(stream, filepath, codeline);
111 200 gate_stream_print(stream,
112 GATE_PRINT_CSTR, msg_type,
113 GATE_PRINT_CSTR, message,
114 GATE_PRINT_NEWLINE,
115 GATE_PRINT_END);
116 200 }
117
118 24 void gate_test_init(gate_stream_t* stream)
119 {
120 gate_console_t* con;
121 24 gate_mem_clear(&test_session, sizeof(test_session));
122 24 test_session.reports_used = 0;
123
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if (stream == NULL)
124 {
125 24 con = gate_console();
126
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if (con)
127 {
128 24 test_session.stream = (gate_stream_t*)con;
129 }
130 else
131 {
132 test_session.stream = gate_nullstream();
133 }
134 }
135 else
136 {
137 test_session.stream = stream;
138 }
139 24 gate_linkedlist_create(&test_session.checks, sizeof(gate_test_history_t), NULL, NULL);
140 24 }
141
142 16 void gate_test_uninit()
143 {
144 16 gate_linkedlist_destroy(&test_session.checks);
145 16 gate_mem_clear(&test_session, sizeof(test_session));
146 16 }
147
148
149 200 void gate_test_unit_begin(char const* unit_name, char const* filepath, unsigned codeline)
150 {
151 200 gate_test_register(unit_name);
152 200 gate_test_print_message("TEST-BEGIN: ", unit_name, filepath, codeline);
153 200 }
154
155 static gate_bool_t volatile global_trace_on;
156 static gate_bool_t volatile global_history_on;
157
158 void gate_test_trace_enable(gate_bool_t on)
159 {
160 global_trace_on = on;
161 }
162
163 void gate_test_history_enable(gate_bool_t on)
164 {
165 global_history_on = on;
166 }
167
168
169 9244 void gate_test_trace_message(char const* message, char const* filepath, unsigned codeline)
170 {
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9244 times.
9244 if (global_trace_on)
172 {
173 gate_test_print_message("TRACE: ", message, filepath, codeline);
174 }
175 9244 }
176
177 4 void gate_test_trace_value(gate_int64_t value, char const* filepath, unsigned codeline)
178 {
179 4 char buffer[128] = GATE_INIT_EMPTY;
180 4 gate_str_print_int64(buffer, sizeof(buffer), value);
181 4 gate_test_trace_message(buffer, filepath, codeline);
182 4 }
183
184 static void add_test_history_entry(gate_bool_t failed, char const* message, char const* filepath, unsigned codeline)
185 {
186 gate_test_report_t* ptr = gate_test_current_unit();
187 gate_test_history_t entry;
188 entry.test_name = ptr->name;
189 gate_str_print_text(entry.message, sizeof(entry.message), message, gate_str_length(message));
190 entry.failed = failed;
191 entry.origin = filepath;
192 entry.line = codeline;
193 gate_linkedlist_add(&test_session.checks, &entry);
194 }
195
196 9125 void gate_test_success_message(char const* message, char const* filepath, unsigned codeline)
197 {
198
2/4
✓ Branch 0 taken 9125 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9125 times.
9125 if (global_trace_on || global_history_on)
199 {
200 gate_test_print_message("SUCCESS: ", message, filepath, codeline);
201 }
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9125 times.
9125 if (global_history_on)
203 {
204 add_test_history_entry(false, message, filepath, codeline);
205 }
206 9125 }
207
208 void gate_test_error_message(char const* message, char const* filepath, unsigned codeline)
209 {
210 gate_test_print_message("ERROR: ", message, filepath, codeline);
211 if (global_history_on)
212 {
213 add_test_history_entry(true, message, filepath, codeline);
214 }
215 }
216
217
218 9125 void gate_test_count_test()
219 {
220 9125 gate_test_report_t* ptr = gate_test_current_unit();
221
1/2
✓ Branch 0 taken 9125 times.
✗ Branch 1 not taken.
9125 if (ptr)
222 {
223 9125 ++ptr->test_counter;
224 }
225 9125 }
226
227 void gate_test_count_error()
228 {
229 gate_test_report_t* ptr = gate_test_current_unit();
230 if (ptr)
231 {
232 ++ptr->error_counter;
233 }
234 }
235
236 unsigned gate_test_get_reports(gate_test_report_t* target_report_array, unsigned max_reports)
237 {
238 unsigned count = (max_reports < test_session.reports_used) ? max_reports : test_session.reports_used;
239 gate_mem_copy(target_report_array, &test_session.reports[0], count * sizeof(gate_test_report_t));
240 return count;
241 }
242
243 static void print_history_junit(gate_stream_t* stream, gate_linkedlist_t* checks)
244 {
245
246 }
247
248 gate_bool_t gate_test_print_history(gate_stream_t* stream, gate_enumint_t flags)
249 {
250 if (GATE_FLAG_ENABLED(flags, GATE_TEST_HISTORY_FORMAT_JUNIT))
251 {
252 print_history_junit(stream, &test_session.checks);
253 return true;
254 }
255 return false;
256 }
257
258
259 16 void gate_test_print_reports()
260 {
261 gate_test_report_t* ptr;
262 16 gate_stream_t* stream = gate_test_current_stream();
263 unsigned ndx;
264 16 gate_uint16_t total_tests = 0;
265 16 gate_uint16_t total_errors = 0;
266
267 16 gate_stream_print(stream,
268 GATE_PRINT_CSTR, "----------------------------------------",
269 GATE_PRINT_NEWLINE,
270 GATE_PRINT_END);
271
272
2/2
✓ Branch 0 taken 200 times.
✓ Branch 1 taken 16 times.
216 for (ndx = 0; ndx != test_session.reports_used; ++ndx)
273 {
274 200 ptr = &test_session.reports[ndx];
275 200 total_tests += ptr->test_counter;
276 200 total_errors += ptr->error_counter;
277
278
1/2
✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
200 if (ptr->error_counter == 0)
279 {
280 200 gate_stream_print_cstr(stream, "UNIT OK: ");
281 }
282 else
283 {
284 gate_stream_print_cstr(stream, "UNIT FAILED: ");
285 }
286
287 400 gate_stream_print(stream,
288 200 GATE_PRINT_CSTR, ptr->name,
289 GATE_PRINT_CSTR, ", ",
290 200 GATE_PRINT_UI16, ptr->test_counter,
291 GATE_PRINT_CSTR, " tests",
292 GATE_PRINT_END);
293
294
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 200 times.
200 if (ptr->error_counter != 0)
295 {
296 gate_stream_print(stream,
297 GATE_PRINT_CSTR, ", ",
298 GATE_PRINT_UI16, ptr->error_counter,
299 GATE_PRINT_CSTR, " failures",
300 GATE_PRINT_END);
301 }
302
303 200 gate_stream_print(stream,
304 GATE_PRINT_NEWLINE,
305 GATE_PRINT_END);
306 }
307
308 16 gate_stream_print(stream,
309 GATE_PRINT_CSTR, "----------------------------------------",
310 GATE_PRINT_NEWLINE,
311 GATE_PRINT_CSTR, "TOTAL: ",
312 GATE_PRINT_UI16, total_tests,
313 GATE_PRINT_CSTR, " tests, ",
314 GATE_PRINT_END);
315
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (total_errors == 0)
316 {
317 16 gate_stream_print(stream,
318 GATE_PRINT_CSTR, "No errors detected",
319 GATE_PRINT_NEWLINE,
320 GATE_PRINT_END);
321 }
322 else
323 {
324 gate_stream_print(stream,
325 GATE_PRINT_UI16, total_errors,
326 GATE_PRINT_CSTR, " errors detected",
327 GATE_PRINT_NEWLINE,
328 GATE_PRINT_END);
329 }
330
331 16 }
332
333 16 gate_bool_t gate_test_failed()
334 {
335 gate_test_report_t* ptr;
336 unsigned ndx;
337
2/2
✓ Branch 0 taken 200 times.
✓ Branch 1 taken 16 times.
216 for (ndx = 0; ndx != test_session.reports_used; ++ndx)
338 {
339 200 ptr = &test_session.reports[ndx];
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 200 times.
200 if (ptr->error_counter != 0)
341 {
342 return true;
343 }
344 }
345 16 return false;
346 }
347
348
349 8 gate_bool_t gate_test_cases_run(gate_test_case_t const* test_cases, gate_size_t test_cases_count)
350 {
351 gate_size_t ndx;
352 8 gate_bool_t succeeded = true;
353 8 gate_test_case_t const* ptr_case = NULL;
354
355 8 GATE_TEST_INIT_DEFAULT();
356
357
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 8 times.
55 for (ndx = 0; ndx != test_cases_count; ++ndx)
358 {
359 47 ptr_case = &test_cases[ndx];
360 47 succeeded = test_cases[ndx].test_function();
361 }
362
363 8 return !gate_test_failed();
364 }
365
366 47 static gate_bool_t is_test_case_selected(char const* tc_name, char const* const* patterns, gate_size_t patterns_count)
367 {
368
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if (patterns_count == 0)
369 {
370 /* no pattern defined -> all TCs are selected */
371 47 return true;
372 }
373 else
374 {
375 gate_size_t const tc_name_len = gate_str_length(tc_name);
376 gate_size_t ndx;
377
378 for (ndx = 0; ndx != patterns_count; ++ndx)
379 {
380 char const* const pattern = patterns[ndx];
381 size_t const pattern_len = gate_str_length(pattern);
382 if (gate_str_like(tc_name, tc_name_len, pattern, pattern_len))
383 {
384 /* matching pattern found */
385 return true;
386 }
387 }
388 return false;
389 }
390
391 }
392
393 8 int gate_test_main(gate_test_case_t const* test_cases, gate_size_t test_cases_count,
394 char const* const* arguments, gate_size_t argcount)
395 {
396 gate_test_case_t selected_test_cases[256];
397 8 gate_size_t selected_test_cases_count = 0;
398 8 const gate_size_t selected_test_cases_max = sizeof(selected_test_cases) / sizeof(selected_test_cases[0]);
399 char const* test_patterns[256];
400 8 gate_size_t test_patterns_count = 0;
401 8 const gate_size_t test_pattern_max = sizeof(test_patterns) / sizeof(test_patterns[0]);
402 gate_bool_t succeeded;
403 gate_size_t ndx;
404 8 gate_bool_t show_help = false;
405 8 gate_bool_t enable_trace = false;
406 8 gate_bool_t list_tests = false;
407 8 gate_stream_t* con = (gate_stream_t*)gate_console();
408
409 8 GATE_TEST_INIT_DEFAULT();
410
411
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 for (ndx = 0; ndx != argcount; ++ndx)
412 {
413 char const* const arg = arguments[ndx];
414 if (gate_str_comp_ic(arg, "--help") == 0)
415 {
416 show_help = true;
417 }
418 else if (gate_str_comp_ic(arg, "--list") == 0)
419 {
420 list_tests = true;
421 }
422 else if (gate_str_comp_ic(arg, "--trace") == 0)
423 {
424 enable_trace = true;
425 }
426 else
427 {
428 if (test_patterns_count < test_pattern_max)
429 {
430 test_patterns[test_patterns_count] = arg;
431 ++test_patterns_count;
432 }
433 }
434 }
435
436
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (show_help)
437 {
438 gate_stream_print(con,
439 GATE_PRINT_CSTR, "Usage:", GATE_PRINT_NEWLINE,
440 GATE_PRINT_CSTR, "testapp --help", GATE_PRINT_NEWLINE,
441 GATE_PRINT_CSTR, "\tPrint test program usage description", GATE_PRINT_NEWLINE,
442 GATE_PRINT_CSTR, "testapp --list", GATE_PRINT_NEWLINE,
443 GATE_PRINT_CSTR, "\tLists all registered test cases by their anmes", GATE_PRINT_NEWLINE,
444 GATE_PRINT_CSTR, "testapp", GATE_PRINT_NEWLINE,
445 GATE_PRINT_CSTR, "\tExecutes all registered test cases", GATE_PRINT_NEWLINE,
446 GATE_PRINT_CSTR, "testapp test_case_name1 test_case_name2", GATE_PRINT_NEWLINE,
447 GATE_PRINT_CSTR, "\tExecutes only test cases named by arguments", GATE_PRINT_NEWLINE,
448 GATE_PRINT_CSTR, "testapp --trace test_case_name1 test_case_name2", GATE_PRINT_NEWLINE,
449 GATE_PRINT_CSTR, "\tExecutes given test cases with trace-mode enabled", GATE_PRINT_NEWLINE,
450 GATE_PRINT_END);
451 return 0;
452 }
453
454
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (list_tests)
455 {
456 for (ndx = 0; ndx != test_cases_count; ++ndx)
457 {
458 gate_test_case_t const* tc = &test_cases[ndx];
459 gate_stream_print(con,
460 GATE_PRINT_CSTR, tc->name, GATE_PRINT_NEWLINE,
461 GATE_PRINT_END);
462 }
463 return 0;
464 }
465
466
467
3/4
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 47 times.
✗ Branch 3 not taken.
55 for (ndx = 0; (ndx != test_cases_count) && (selected_test_cases_count < selected_test_cases_max); ++ndx)
468 {
469 47 gate_test_case_t const* tc = &test_cases[ndx];
470
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 if (is_test_case_selected(tc->name, test_patterns, test_patterns_count))
471 {
472 47 selected_test_cases[selected_test_cases_count] = test_cases[ndx];
473 47 ++selected_test_cases_count;
474 }
475 }
476
477
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (enable_trace)
478 {
479 gate_test_trace_on();
480 }
481
482 8 succeeded = gate_test_cases_run(selected_test_cases, selected_test_cases_count);
483 8 gate_test_print_reports();
484
485 8 gate_test_uninit();
486 8 return succeeded ? 0 : 1;
487 }
488