1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | |
22 | /* |
23 | |
24 | Used by the test framework and test cases. |
25 | |
26 | */ |
27 | |
28 | /* quiet windows compiler warnings */ |
29 | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) |
30 | #define _CRT_SECURE_NO_WARNINGS |
31 | #endif |
32 | #include <SDL3/SDL_test.h> |
33 | |
34 | #include <time.h> /* Needed for localtime() */ |
35 | |
36 | /* work around compiler warning on older GCCs. */ |
37 | #if (defined(__GNUC__) && (__GNUC__ <= 2)) |
38 | static size_t strftime_gcc2_workaround(char *s, size_t max, const char *fmt, const struct tm *tm) |
39 | { |
40 | return strftime(s, max, fmt, tm); |
41 | } |
42 | #ifdef strftime |
43 | #undef strftime |
44 | #endif |
45 | #define strftime strftime_gcc2_workaround |
46 | #endif |
47 | |
48 | /** |
49 | * Converts unix timestamp to its ascii representation in localtime |
50 | * |
51 | * Note: Uses a static buffer internally, so the return value |
52 | * isn't valid after the next call of this function. If you |
53 | * want to retain the return value, make a copy of it. |
54 | * |
55 | * \param timestamp A Timestamp, i.e. time(0) |
56 | * |
57 | * \return Ascii representation of the timestamp in localtime in the format '08/23/01 14:55:02' |
58 | */ |
59 | static const char *SDLTest_TimestampToString(const time_t timestamp) |
60 | { |
61 | time_t copy; |
62 | static char buffer[64]; |
63 | struct tm *local; |
64 | size_t result = 0; |
65 | |
66 | SDL_memset(buffer, 0, sizeof(buffer)); |
67 | copy = timestamp; |
68 | local = localtime(©); |
69 | result = strftime(buffer, sizeof(buffer), "%x %X" , local); |
70 | if (result == 0) { |
71 | return "" ; |
72 | } |
73 | |
74 | return buffer; |
75 | } |
76 | |
77 | /* |
78 | * Prints given message with a timestamp in the TEST category and INFO priority. |
79 | */ |
80 | void SDLTest_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) |
81 | { |
82 | va_list list; |
83 | char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH]; |
84 | |
85 | /* Print log message into a buffer */ |
86 | SDL_memset(logMessage, 0, SDLTEST_MAX_LOGMESSAGE_LENGTH); |
87 | va_start(list, fmt); |
88 | (void)SDL_vsnprintf(logMessage, SDLTEST_MAX_LOGMESSAGE_LENGTH - 1, fmt, list); |
89 | va_end(list); |
90 | |
91 | /* Log with timestamp and newline */ |
92 | SDL_LogMessage(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_INFO, " %s: %s" , SDLTest_TimestampToString(time(0)), logMessage); |
93 | } |
94 | |
95 | /* |
96 | * Prints given message with a timestamp in the TEST category and the ERROR priority. |
97 | */ |
98 | void SDLTest_LogError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) |
99 | { |
100 | va_list list; |
101 | char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH]; |
102 | |
103 | /* Print log message into a buffer */ |
104 | SDL_memset(logMessage, 0, SDLTEST_MAX_LOGMESSAGE_LENGTH); |
105 | va_start(list, fmt); |
106 | (void)SDL_vsnprintf(logMessage, SDLTEST_MAX_LOGMESSAGE_LENGTH - 1, fmt, list); |
107 | va_end(list); |
108 | |
109 | /* Log with timestamp and newline */ |
110 | SDL_LogMessage(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_ERROR, "%s: %s" , SDLTest_TimestampToString(time(0)), logMessage); |
111 | } |
112 | |
113 | static char nibble_to_char(Uint8 nibble) |
114 | { |
115 | if (nibble < 0xa) { |
116 | return '0' + nibble; |
117 | } else { |
118 | return 'a' + nibble - 10; |
119 | } |
120 | } |
121 | |
122 | void SDLTest_LogEscapedString(const char *prefix, const void *buffer, size_t size) |
123 | { |
124 | const Uint8 *data = buffer; |
125 | char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH]; |
126 | |
127 | if (data) { |
128 | size_t i; |
129 | size_t pos = 0; |
130 | #define NEED_X_CHARS(N) \ |
131 | if (pos + (N) > sizeof(logMessage) - 2) { \ |
132 | break; \ |
133 | } |
134 | |
135 | logMessage[pos++] = '"'; |
136 | for (i = 0; i < size; i++) { |
137 | Uint8 c = data[i]; |
138 | size_t pos_start = pos; |
139 | switch (c) { |
140 | case '\0': |
141 | NEED_X_CHARS(2); |
142 | logMessage[pos++] = '\\'; |
143 | logMessage[pos++] = '0'; |
144 | break; |
145 | case '"': |
146 | NEED_X_CHARS(2); |
147 | logMessage[pos++] = '\\'; |
148 | logMessage[pos++] = '"'; |
149 | break; |
150 | case '\n': |
151 | NEED_X_CHARS(2); |
152 | logMessage[pos++] = '\\'; |
153 | logMessage[pos++] = 'n'; |
154 | break; |
155 | case '\r': |
156 | NEED_X_CHARS(2); |
157 | logMessage[pos++] = '\\'; |
158 | logMessage[pos++] = 'r'; |
159 | break; |
160 | case '\t': |
161 | NEED_X_CHARS(2); |
162 | logMessage[pos++] = '\\'; |
163 | logMessage[pos++] = 't'; |
164 | break; |
165 | case '\f': |
166 | NEED_X_CHARS(2); |
167 | logMessage[pos++] = '\\'; |
168 | logMessage[pos++] = 'f'; |
169 | break; |
170 | case '\b': |
171 | NEED_X_CHARS(2); |
172 | logMessage[pos++] = '\\'; |
173 | logMessage[pos++] = 'b'; |
174 | break; |
175 | case '\\': |
176 | NEED_X_CHARS(2); |
177 | logMessage[pos++] = '\\'; |
178 | logMessage[pos++] = '\\'; |
179 | break; |
180 | default: |
181 | if (SDL_isprint(c)) { |
182 | NEED_X_CHARS(1); |
183 | logMessage[pos++] = c; |
184 | } else { |
185 | NEED_X_CHARS(4); |
186 | logMessage[pos++] = '\\'; |
187 | logMessage[pos++] = 'x'; |
188 | logMessage[pos++] = nibble_to_char(c >> 4); |
189 | logMessage[pos++] = nibble_to_char(c & 0xf); |
190 | } |
191 | break; |
192 | } |
193 | if (pos == pos_start) { |
194 | break; |
195 | } |
196 | } |
197 | if (i < size) { |
198 | logMessage[sizeof(logMessage) - 4] = '.'; |
199 | logMessage[sizeof(logMessage) - 3] = '.'; |
200 | logMessage[sizeof(logMessage) - 2] = '.'; |
201 | logMessage[sizeof(logMessage) - 1] = '\0'; |
202 | } else { |
203 | logMessage[pos++] = '"'; |
204 | logMessage[pos] = '\0'; |
205 | } |
206 | } else { |
207 | SDL_strlcpy(logMessage, "(nil)" , sizeof(logMessage)); |
208 | } |
209 | |
210 | SDLTest_Log("%s%s" , prefix, logMessage); |
211 | } |
212 | |