| 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 | |