CLibs
Loading...
Searching...
No Matches
unit_tests.h
Go to the documentation of this file.
1/*
2 * Header for unit-testing.
3 *
4 * For more info see `docs/unit-tests.md`.
5 *
6 *
7 * Created by MacBook on 03.01.2025.
8 */
9
10#ifndef CLIBS_UNIT_TESTS_H
11#define CLIBS_UNIT_TESTS_H
12
13#include "filenames.h" /* PATH_MAX */
14#include "misc.h" /* STRLEN */
15#include "terminal_colors.h" /* colors */
16
17#include <stdbool.h> /* * */
18#include <stdio.h> /* printf */
19#include <stdlib.h> /* exit */
20
21
22#if !defined( LINE_PREF_WIDTH )
24#define LINE_PREF_WIDTH 100
25#endif
26
27#if LINE_PREF_WIDTH < 32
28static const size_t TESTS_LINE_WIDTH = ( 32 + 1 );
29#else
30static const size_t TESTS_LINE_WIDTH = LINE_PREF_WIDTH;
31#endif
32
33
34#ifndef COLOR_FAIL
35#define COLOR_FAIL FOREGROUND_RED
36#endif //COLOR_FAIL
37
38#ifndef COLOR_SUCC
39#define COLOR_SUCC FOREGROUND_GREEN
40#endif //COLOR_SUCC
41
42#ifndef COLOR_NOTE
43#define COLOR_NOTE FOREGROUND_CYAN
44#endif //COLOR_NOTE
45
46#ifndef COLOR_TEST_TAG
47#define COLOR_TEST_TAG FOREGROUND_YELLOW
48#endif //COLOR_TEST_TAG
49
50
52#define TEST_NAME_CREATOR( TOK ) CLIBS_UNIT_TESTS_## TOK
53
54
55static int TEST_NAME_CREATOR( TOTAL_FAILED ) = 0;
56static int TEST_NAME_CREATOR( TOTAL_RAN ) = 0;
57
58static int TEST_NAME_CREATOR( TOTAL_FAILED_UNIT ) = 0;
59static int TEST_NAME_CREATOR( TOTAL_RAN_UNIT ) = 0;
60
61static bool TEST_NAME_CREATOR( YAP ) = true;
62
63
64#ifndef UNIT_TESTS_SILENT
76#define TEST( HANDLE ) \
77 int TEST_NAME_CREATOR( HANDLE )( void ) \
78 { \
79 int TEST_NAME_CREATOR( failed_total ) = 0; \
80 int TEST_NAME_CREATOR( ran_total ) = 0; \
81 const char *TEST_NAME_CREATOR( test_handle ) = #HANDLE; \
82 printf( COLOR_TEST_TAG "[TEST]" COLOR_DEFAULT " " #HANDLE "\n" );
83#else
84#define TEST( HANDLE ) \
85 int TEST_NAME_CREATOR( HANDLE )( void ) \
86 { \
87 int TEST_NAME_CREATOR( failed_total ) = 0; \
88 int TEST_NAME_CREATOR( ran_total ) = 0; \
89 const char *TEST_NAME_CREATOR( test_handle ) = #HANDLE;
90#endif //UNIT_TESTS_SILENT
91
97#define END_TEST \
98 printf( COLOR_TEST_TAG "[TEST]" COLOR_DEFAULT " %s: ran %i tests, " PRINT_COLOR \
99 "%i successful" COLOR_DEFAULT ", " PRINT_COLOR \
100 "%i failed" COLOR_DEFAULT "\n", \
101 TEST_NAME_CREATOR( test_handle ), \
102 TEST_NAME_CREATOR( ran_total ), \
103 TEST_NAME_CREATOR( ran_total ) - TEST_NAME_CREATOR( failed_total ) == 0 \
104 ? COLOR_FAIL \
105 : COLOR_SUCC, \
106 TEST_NAME_CREATOR( ran_total ) - TEST_NAME_CREATOR( failed_total ), \
107 TEST_NAME_CREATOR( failed_total ) == 0 ? COLOR_SUCC : COLOR_FAIL, \
108 TEST_NAME_CREATOR( failed_total ) ); \
109 TEST_NAME_CREATOR( TOTAL_FAILED_UNIT ) += TEST_NAME_CREATOR( failed_total ); \
110 TEST_NAME_CREATOR( TOTAL_RAN_UNIT ) += TEST_NAME_CREATOR( ran_total ); \
111 return TEST_NAME_CREATOR( failed_total ) == 0 ? 0 : 1; \
112 }
113
114
120#define RUN_TEST( HANDLE ) \
121 do \
122 { \
123 TEST_NAME_CREATOR( TOTAL_FAILED ) += TEST_NAME_CREATOR( HANDLE )(); \
124 ++TEST_NAME_CREATOR( TOTAL_RAN ); \
125 } \
126 while ( 0 )
127
128
135{
136 printf( "\n" COLOR_NOTE "[SUMMARY]" COLOR_DEFAULT " total unit tests ran: " COLOR_NOTE
137 "%i" COLOR_DEFAULT ", total unit tests failed: " PRINT_COLOR
138 "%i" COLOR_DEFAULT "\n",
139 TEST_NAME_CREATOR( TOTAL_RAN_UNIT ),
140 TEST_NAME_CREATOR( TOTAL_FAILED_UNIT ) == 0 ? COLOR_SUCC : COLOR_FAIL,
141 TEST_NAME_CREATOR( TOTAL_FAILED_UNIT ) );
142
143 printf( COLOR_NOTE "[SUMMARY]" COLOR_DEFAULT " total tests ran: " COLOR_NOTE
144 "%i" COLOR_DEFAULT ", total tests failed: " PRINT_COLOR
145 "%i" COLOR_DEFAULT "\n",
146 TEST_NAME_CREATOR( TOTAL_RAN ),
147 TEST_NAME_CREATOR( TOTAL_FAILED ) == 0 ? COLOR_SUCC : COLOR_FAIL,
148 TEST_NAME_CREATOR( TOTAL_FAILED ) );
149
150 exit( TEST_NAME_CREATOR( TOTAL_FAILED ) == 0 ? EXIT_SUCCESS : EXIT_FAILURE );
151}
152
153
155LibraryDefined bool UNIT_TEST_( const char *cond_str,
156 const bool passed,
157 const char *filename,
158 const int lineno,
159 const bool isCritical )
160{
161#ifndef UNIT_TESTS_SILENT
162 if ( TEST_NAME_CREATOR( YAP ) || !passed || isCritical )
163 {
164 printf( " " );
165 static const size_t msg_indent = STRLEN( " " );
166 ssize_t ln = TESTS_LINE_WIDTH - msg_indent;
167
168 SetTerminalColor( stdout,
169 !isCritical ? COLOR_TEST_TAG
170 : passed ? COLOR_SUCC
171 : COLOR_FAIL );
172
173 ln -= printf( "[%s", isCritical ? "CRITICAL" : "UNIT TEST" );
174
175 if ( !passed )
176 {
177 static char buffer[ sizeof( __FILE_NAME__ ) + 20 + 7 + 1 ];
178 snprintf( buffer, sizeof buffer, " // %s @ %d", filename, lineno );
179 ln -= printf( "%s", buffer );
180 }
181 ln -= printf( "]" );
183 ln -= printf( " %s ", cond_str );
184
185 static const size_t MSG_END_PART_LEN = STRLEN( " SUCCESS" ) /* or failure */;
186
187 ln -= MSG_END_PART_LEN;
188
189 if ( ln < 0 )
190 {
191 printf( "\n" );
192 for ( size_t i = 0; i < msg_indent; ++i )
193 printf( " " );
194 }
195
196#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L
197 _Static_assert( TESTS_LINE_WIDTH > ( MSG_END_PART_LEN + msg_indent ),
198 "TESTS_LINE_WIDTH must be greater than the const part" );
199#endif
200
201 const size_t ndots =
202 ln > 0 ? ( size_t ) ln : TESTS_LINE_WIDTH - MSG_END_PART_LEN - msg_indent;
203
204 for ( size_t i = 0; i < ndots; ++i )
205 printf( "." );
206
207 printf( " " PRINT_COLOR "%s" COLOR_DEFAULT "\n",
208 passed ? COLOR_SUCC : COLOR_FAIL,
209 passed ? "SUCCESS" : "FAILURE" );
210 }
211#else
212 ( void ) cond_str;
213 ( void ) filename;
214 ( void ) lineno;
215 ( void ) isCritical;
216#endif //UNIT_TESTS_SILENT
217
218 return passed;
219}
221
222
230#define UNIT_TEST( CONDITION ) \
231 do \
232 { \
233 if ( !UNIT_TEST_( #CONDITION, CONDITION, __FILE_NAME__, __LINE__, false ) ) \
234 ++TEST_NAME_CREATOR( failed_total ); \
235 ++TEST_NAME_CREATOR( ran_total ); \
236 } \
237 while ( 0 )
238
239
240// clang-format off
249#define CRITICAL_TEST( CONDITION ) \
250 do \
251 { \
252 ++TEST_NAME_CREATOR( ran_total ); \
253 if ( !UNIT_TEST_( #CONDITION, CONDITION, __FILE_NAME__, __LINE__, true ) ) \
254 { \
255 ++TEST_NAME_CREATOR( failed_total ); \
256 { END_TEST /* the END_TEST macro has a terminating bracket `}' */ \
257 } \
258 } \
259 while ( 0 )
260// clang-format on
261
262
271LibraryDefined void SET_UNIT_TEST_VERBOSITY( const bool verbose )
272{
273 TEST_NAME_CREATOR( YAP ) = verbose;
274}
275
280{
281 return TEST_NAME_CREATOR( YAP );
282}
283
284#endif //CLIBS_UNIT_TESTS_H
#define LibraryDefined
Definition attributes.h:107
#define NoReturn
Function always exits.
Definition attributes.h:83
#define STRLEN(STRING)
For string literals.
Definition misc.h:19
#define COLOR_DEFAULT
Definition terminal_colors.h:33
#define PRINT_COLOR
Definition terminal_colors.h:18
LibraryDefined bool SetTerminalColor(FILE *stream, const char *Color)
Definition terminal_colors.h:79
LibraryDefined void SET_UNIT_TEST_VERBOSITY(const bool verbose)
Definition unit_tests.h:271
#define COLOR_TEST_TAG
Definition unit_tests.h:47
LibraryDefined bool GET_UNIT_TEST_VERBOSITY(void)
Definition unit_tests.h:279
#define COLOR_NOTE
Definition unit_tests.h:43
#define TEST_NAME_CREATOR(TOK)
To avoid namespace collisions.
Definition unit_tests.h:52
#define COLOR_SUCC
Definition unit_tests.h:39
#define COLOR_FAIL
Definition unit_tests.h:35
LibraryDefined NoReturn void FINISH_TESTING(void)
Definition unit_tests.h:134