From e75d0d1af9ecf275db98e9f9f8451854a4053b34 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 24 May 2013 03:23:21 -0700 Subject: [PATCH] Added the beginning of automated tests for the standard C library routines. Implemented more SDL_snprintf format specifiers. --- src/stdlib/SDL_string.c | 313 ++++++++++++++++++++++------------- test/Makefile.in | 15 +- test/testautomation_stdlib.c | 142 ++++++++++++++++ test/testautomation_suites.h | 18 +- 4 files changed, 356 insertions(+), 132 deletions(-) create mode 100644 test/testautomation_stdlib.c diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c index da02d8b09..dd3589e4e 100644 --- a/src/stdlib/SDL_string.c +++ b/src/stdlib/SDL_string.c @@ -1079,8 +1079,7 @@ SDL_sscanf(const char *text, const char *fmt, ...) ++index; } if (text[index] == '0') { - if (SDL_tolower((unsigned char) text[index + 1]) - == 'x') { + if (SDL_tolower((unsigned char) text[index + 1]) == 'x') { radix = 16; } else { radix = 8; @@ -1273,133 +1272,177 @@ SDL_snprintf(char *text, size_t maxlen, const char *fmt, ...) #undef SDL_vsnprintf int SDL_vsnprintf(char *text, size_t maxlen, const char *fmt, va_list ap) { return SDL_vsnprintf_inline(text, maxlen, fmt, ap); } #else + /* FIXME: implement more of the format specifiers */ +typedef struct +{ + SDL_bool left_justify; + SDL_bool force_sign; + SDL_bool force_type; + SDL_bool pad_zeroes; + SDL_bool do_lowercase; + int width; + int radix; + int precision; +} SDL_FormatInfo; + static size_t -SDL_PrintLong(char *text, long value, int radix, size_t maxlen) +SDL_PrintString(char *text, size_t maxlen, SDL_FormatInfo *info, const char *string) { - char num[130]; - size_t size; + return SDL_strlcpy(text, string, maxlen); +} - SDL_ltoa(value, num, radix); - size = SDL_strlen(num); - if (size >= maxlen) { - size = maxlen - 1; - } - SDL_strlcpy(text, num, size + 1); +static size_t +SDL_PrintLong(char *text, size_t maxlen, SDL_FormatInfo *info, long value) +{ + char num[130]; - return size; + SDL_ltoa(value, num, info ? info->radix : 10); + return SDL_PrintString(text, maxlen, info, num); } static size_t -SDL_PrintUnsignedLong(char *text, unsigned long value, int radix, - size_t maxlen) +SDL_PrintUnsignedLong(char *text, size_t maxlen, SDL_FormatInfo *info, unsigned long value) { char num[130]; - size_t size; - SDL_ultoa(value, num, radix); - size = SDL_strlen(num); - if (size >= maxlen) { - size = maxlen - 1; - } - SDL_strlcpy(text, num, size + 1); - - return size; + SDL_ultoa(value, num, info ? info->radix : 10); + return SDL_PrintString(text, maxlen, info, num); } static size_t -SDL_PrintLongLong(char *text, Sint64 value, int radix, size_t maxlen) +SDL_PrintLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, Sint64 value) { char num[130]; - size_t size; - SDL_lltoa(value, num, radix); - size = SDL_strlen(num); - if (size >= maxlen) { - size = maxlen - 1; - } - SDL_strlcpy(text, num, size + 1); - - return size; + SDL_lltoa(value, num, info ? info->radix : 10); + return SDL_PrintString(text, maxlen, info, num); } static size_t -SDL_PrintUnsignedLongLong(char *text, Uint64 value, int radix, size_t maxlen) +SDL_PrintUnsignedLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, Uint64 value) { char num[130]; - size_t size; - - SDL_ulltoa(value, num, radix); - size = SDL_strlen(num); - if (size >= maxlen) { - size = maxlen - 1; - } - SDL_strlcpy(text, num, size + 1); - return size; + SDL_ulltoa(value, num, info ? info->radix : 10); + return SDL_PrintString(text, maxlen, info, num); } static size_t -SDL_PrintFloat(char *text, double arg, size_t maxlen) +SDL_PrintFloat(char *text, size_t maxlen, SDL_FormatInfo *info, double arg) { + int i, width; + size_t len, spot; + size_t left = maxlen; char *textstart = text; + if (arg) { /* This isn't especially accurate, but hey, it's easy. :) */ - const double precision = 0.00000001; - size_t len; + double precision = 1.0; unsigned long value; if (arg < 0) { - *text++ = '-'; - --maxlen; + if (left > 1) { + *text = '-'; + --left; + } + ++text; arg = -arg; + } else if (info->force_sign) { + if (left > 1) { + *text = '+'; + --left; + } + ++text; } value = (unsigned long) arg; - len = SDL_PrintUnsignedLong(text, value, 10, maxlen); + len = SDL_PrintUnsignedLong(text, left, NULL, value); text += len; - maxlen -= len; + if (len >= left) { + left = SDL_min(left, 1); + } else { + left -= len; + } arg -= value; - if (arg > precision && maxlen) { + if (info->precision < 0) { + info->precision = 6; + } + for (i = 0; i < info->precision; ++i) { + precision *= 0.1; + } + if (info->force_type || info->precision > 0) { int mult = 10; - *text++ = '.'; - while ((arg > precision) && maxlen) { + if (left > 1) { + *text = '.'; + --left; + } + ++text; + while (info->precision-- > 0) { value = (unsigned long) (arg * mult); - len = SDL_PrintUnsignedLong(text, value, 10, maxlen); + len = SDL_PrintUnsignedLong(text, left, NULL, value); text += len; - maxlen -= len; + if (len >= left) { + left = SDL_min(left, 1); + } else { + left -= len; + } arg -= (double) value / mult; mult *= 10; } } } else { - *text++ = '0'; + if (left > 1) { + *text = '0'; + --left; + } + ++text; + if (info->force_type) { + if (left > 1) { + *text = '.'; + --left; + } + ++text; + } } - return (text - textstart); -} -static size_t -SDL_PrintString(char *text, const char *string, size_t maxlen) -{ - char *textstart = text; - while (*string && maxlen--) { - *text++ = *string++; + width = info->width - (int)(text - textstart); + if (width > 0) { + char fill = info->pad_zeroes ? '0' : ' '; + char *end = text+left-1; + len = (text - textstart); + for (len = (text - textstart); len--; ) { + if ((textstart+len+width) < end) { + *(textstart+len+width) = *(textstart+len); + } + } + len = (size_t)width; + text += len; + if (len >= left) { + left = SDL_min(left, 1); + } else { + left -= len; + } + while (len--) { + if (textstart+len < end) { + textstart[len] = fill; + } + } } + return (text - textstart); } int SDL_vsnprintf(char *text, size_t maxlen, const char *fmt, va_list ap) { + size_t left = maxlen; char *textstart = text; - if (maxlen <= 0) { - return 0; - } - --maxlen; /* For the trailing '\0' */ - while (*fmt && maxlen) { + + while (*fmt) { if (*fmt == '%') { SDL_bool done = SDL_FALSE; size_t len = 0; - SDL_bool do_lowercase = SDL_FALSE; - int radix = 10; + SDL_bool check_flag; + SDL_FormatInfo info; enum { DO_INT, @@ -1407,21 +1450,59 @@ SDL_vsnprintf(char *text, size_t maxlen, const char *fmt, va_list ap) DO_LONGLONG } inttype = DO_INT; - ++fmt; - /* FIXME: implement more of the format specifiers */ - while (*fmt == '.' || (*fmt >= '0' && *fmt <= '9')) { + SDL_zero(info); + info.radix = 10; + info.precision = -1; + + check_flag = SDL_TRUE; + while (check_flag) { ++fmt; + switch (*fmt) { + case '-': + info.left_justify = SDL_TRUE; + break; + case '+': + info.force_sign = SDL_TRUE; + break; + case '#': + info.force_type = SDL_TRUE; + break; + case '0': + info.pad_zeroes = SDL_TRUE; + break; + default: + check_flag = SDL_FALSE; + break; + } } + + if (*fmt >= '0' && *fmt <= '9') { + info.width = SDL_strtol(fmt, (char **)&fmt, 0); + } + + if (*fmt == '.') { + ++fmt; + if (*fmt >= '0' && *fmt <= '9') { + info.precision = SDL_strtol(fmt, (char **)&fmt, 0); + } else { + info.precision = 0; + } + } + while (!done) { switch (*fmt) { case '%': - *text = '%'; + if (left > 1) { + *text = '%'; + } len = 1; done = SDL_TRUE; break; case 'c': /* char is promoted to int when passed through (...) */ - *text = (char) va_arg(ap, int); + if (left > 1) { + *text = (char) va_arg(ap, int); + } len = 1; done = SDL_TRUE; break; @@ -1443,78 +1524,67 @@ SDL_vsnprintf(char *text, size_t maxlen, const char *fmt, va_list ap) case 'd': switch (inttype) { case DO_INT: - len = - SDL_PrintLong(text, - (long) va_arg(ap, int), - radix, maxlen); + len = SDL_PrintLong(text, left, &info, + (long) va_arg(ap, int)); break; case DO_LONG: - len = - SDL_PrintLong(text, va_arg(ap, long), - radix, maxlen); + len = SDL_PrintLong(text, left, &info, + va_arg(ap, long)); break; case DO_LONGLONG: - len = - SDL_PrintLongLong(text, - va_arg(ap, Sint64), - radix, maxlen); + len = SDL_PrintLongLong(text, left, &info, + va_arg(ap, Sint64)); break; } done = SDL_TRUE; break; case 'p': case 'x': - do_lowercase = SDL_TRUE; + info.do_lowercase = SDL_TRUE; /* Fall through to 'X' handling */ case 'X': - if (radix == 10) { - radix = 16; + if (info.radix == 10) { + info.radix = 16; } if (*fmt == 'p') { inttype = DO_LONG; } /* Fall through to unsigned handling */ case 'o': - if (radix == 10) { - radix = 8; + if (info.radix == 10) { + info.radix = 8; } /* Fall through to unsigned handling */ case 'u': switch (inttype) { case DO_INT: - len = SDL_PrintUnsignedLong(text, (unsigned long) - va_arg(ap, - unsigned - int), - radix, maxlen); + len = SDL_PrintUnsignedLong(text, left, &info, + (unsigned long) + va_arg(ap, unsigned int)); break; case DO_LONG: - len = - SDL_PrintUnsignedLong(text, - va_arg(ap, - unsigned - long), - radix, maxlen); + len = SDL_PrintUnsignedLong(text, left, &info, + va_arg(ap, unsigned long)); break; case DO_LONGLONG: - len = - SDL_PrintUnsignedLongLong(text, - va_arg(ap, - Uint64), - radix, maxlen); + len = SDL_PrintUnsignedLongLong(text, left, &info, + va_arg(ap, Uint64)); break; } - if (do_lowercase) { - SDL_strlwr(text); + if (info.do_lowercase) { + size_t i; + for (i = 0; i < len && i < left; ++i) { + text[i] = SDL_tolower((unsigned char)text[i]); + } } done = SDL_TRUE; break; case 'f': - len = SDL_PrintFloat(text, va_arg(ap, double), maxlen); + len = SDL_PrintFloat(text, left, &info, va_arg(ap, double)); done = SDL_TRUE; break; case 's': - len = SDL_PrintString(text, va_arg(ap, char *), maxlen); + len = SDL_PrintString(text, left, &info, va_arg(ap, char *)); done = SDL_TRUE; break; default: @@ -1524,14 +1594,23 @@ SDL_vsnprintf(char *text, size_t maxlen, const char *fmt, va_list ap) ++fmt; } text += len; - maxlen -= len; + if (len >= left) { + left = SDL_min(left, 1); + } else { + left -= len; + } } else { - *text++ = *fmt++; - --maxlen; + if (left > 1) { + *text = *fmt; + --left; + } + ++fmt; + ++text; } } - *text = '\0'; - + if (left > 0) { + *text = '\0'; + } return (int)(text - textstart); } #endif diff --git a/test/Makefile.in b/test/Makefile.in index 0240a2e60..48f7b1f81 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -69,22 +69,23 @@ testaudioinfo$(EXE): $(srcdir)/testaudioinfo.c $(CC) -o $@ $^ $(CFLAGS) $(LIBS) testautomation$(EXE): $(srcdir)/testautomation.c \ + $(srcdir)/testautomation_audio.c \ $(srcdir)/testautomation_clipboard.c \ + $(srcdir)/testautomation_events.c \ + $(srcdir)/testautomation_keyboard.c \ $(srcdir)/testautomation_main.c \ + $(srcdir)/testautomation_mouse.c \ + $(srcdir)/testautomation_pixels.c \ $(srcdir)/testautomation_platform.c \ $(srcdir)/testautomation_rect.c \ $(srcdir)/testautomation_render.c \ $(srcdir)/testautomation_rwops.c \ - $(srcdir)/testautomation_audio.c \ + $(srcdir)/testautomation_sdltest.c \ + $(srcdir)/testautomation_stdlib.c \ $(srcdir)/testautomation_surface.c \ - $(srcdir)/testautomation_events.c \ - $(srcdir)/testautomation_keyboard.c \ - $(srcdir)/testautomation_video.c \ $(srcdir)/testautomation_syswm.c \ - $(srcdir)/testautomation_sdltest.c \ - $(srcdir)/testautomation_mouse.c \ $(srcdir)/testautomation_timer.c \ - $(srcdir)/testautomation_pixels.c + $(srcdir)/testautomation_video.c $(CC) -o $@ $^ $(CFLAGS) -lSDL2_test $(LIBS) testmultiaudio$(EXE): $(srcdir)/testmultiaudio.c diff --git a/test/testautomation_stdlib.c b/test/testautomation_stdlib.c new file mode 100644 index 000000000..f086c6d44 --- /dev/null +++ b/test/testautomation_stdlib.c @@ -0,0 +1,142 @@ +/** + * Standard C library routine test suite + */ + +#include + +#include "SDL.h" +#include "SDL_test.h" + + +/* Test case functions */ + +/** + * @brief Call to SDL_strlcpy + */ +#undef SDL_strlcpy +int +stdlib_strlcpy(void *arg) +{ + size_t result; + char text[1024]; + const char *expected; + + result = SDL_strlcpy(text, "foo", sizeof(text)); + expected = "foo"; + SDLTest_AssertPass("Call to SDL_strlcpy(\"foo\")"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text); + SDLTest_AssertCheck(result == SDL_strlen(text), "Check result value, expected: %d, got: %d", SDL_strlen(text), result); + + result = SDL_strlcpy(text, "foo", 2); + expected = "f"; + SDLTest_AssertPass("Call to SDL_strlcpy(\"foo\") with buffer size 2"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text); + SDLTest_AssertCheck(result == 3, "Check result value, expected: 3, got: %d", result); + + return TEST_COMPLETED; +} + +/** + * @brief Call to SDL_snprintf + */ +#undef SDL_snprintf +int +stdlib_snprintf(void *arg) +{ + int result; + char text[1024]; + const char *expected; + + result = SDL_snprintf(text, sizeof(text), "%s", "foo"); + expected = "foo"; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%s\", \"foo\")"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text); + SDLTest_AssertCheck(result == SDL_strlen(text), "Check result value, expected: %d, got: %d", SDL_strlen(text), result); + + result = SDL_snprintf(text, 2, "%s", "foo"); + expected = "f"; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%s\", \"foo\") with buffer size 2"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text); + SDLTest_AssertCheck(result == 3, "Check result value, expected: 3, got: %d", result); + + result = SDL_snprintf(NULL, 0, "%s", "foo"); + SDLTest_AssertCheck(result == 3, "Check result value, expected: 3, got: %d", result); + + result = SDL_snprintf(text, sizeof(text), "%f", 1.0); + expected = "1.000000"; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%f\", 1.0)"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text); + SDLTest_AssertCheck(result == SDL_strlen(text), "Check result value, expected: %d, got: %d", SDL_strlen(text), result); + + result = SDL_snprintf(text, sizeof(text), "%.f", 1.0); + expected = "1"; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%.f\", 1.0)"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text); + SDLTest_AssertCheck(result == SDL_strlen(text), "Check result value, expected: %d, got: %d", SDL_strlen(text), result); + + result = SDL_snprintf(text, sizeof(text), "%#.f", 1.0); + expected = "1."; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%#.f\", 1.0)"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text); + SDLTest_AssertCheck(result == SDL_strlen(text), "Check result value, expected: %d, got: %d", SDL_strlen(text), result); + + result = SDL_snprintf(text, sizeof(text), "%f", 1.0 + 1.0 / 3.0); + expected = "1.333333"; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%f\", 1.0 + 1.0 / 3.0)"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text); + SDLTest_AssertCheck(result == SDL_strlen(text), "Check result value, expected: %d, got: %d", SDL_strlen(text), result); + + result = SDL_snprintf(text, sizeof(text), "%+f", 1.0 + 1.0 / 3.0); + expected = "+1.333333"; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%+f\", 1.0 + 1.0 / 3.0)"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text); + SDLTest_AssertCheck(result == SDL_strlen(text), "Check result value, expected: %d, got: %d", SDL_strlen(text), result); + + result = SDL_snprintf(text, sizeof(text), "%.2f", 1.0 + 1.0 / 3.0); + expected = "1.33"; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%.2f\", 1.0 + 1.0 / 3.0)"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text); + SDLTest_AssertCheck(result == SDL_strlen(text), "Check result value, expected: %d, got: %d", SDL_strlen(text), result); + + result = SDL_snprintf(text, sizeof(text), "%6.2f", 1.0 + 1.0 / 3.0); + expected = " 1.33"; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%6.2f\", 1.0 + 1.0 / 3.0)"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: '%s', got: '%s'", expected, text); + SDLTest_AssertCheck(result == SDL_strlen(text), "Check result value, expected: %d, got: %d", SDL_strlen(text), result); + + result = SDL_snprintf(text, sizeof(text), "%06.2f", 1.0 + 1.0 / 3.0); + expected = "001.33"; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%06.2f\", 1.0 + 1.0 / 3.0)"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: '%s', got: '%s'", expected, text); + SDLTest_AssertCheck(result == SDL_strlen(text), "Check result value, expected: %d, got: %d", SDL_strlen(text), result); + + result = SDL_snprintf(text, 5, "%06.2f", 1.0 + 1.0 / 3.0); + expected = "001."; + SDLTest_AssertPass("Call to SDL_snprintf(\"%%06.2f\", 1.0 + 1.0 / 3.0) with buffer size 5"); + SDLTest_AssertCheck(SDL_strcmp(text, expected) == 0, "Check text, expected: '%s', got: '%s'", expected, text); + SDLTest_AssertCheck(result == 6, "Check result value, expected: 6, got: %d", result); + + return TEST_COMPLETED; +} + +/* ================= Test References ================== */ + +/* Standard C routine test cases */ +static const SDLTest_TestCaseReference stdlibTest1 = + { (SDLTest_TestCaseFp)stdlib_strlcpy, "stdlib_strlcpy", "Call to SDL_strlcpy", TEST_ENABLED }; + +static const SDLTest_TestCaseReference stdlibTest2 = + { (SDLTest_TestCaseFp)stdlib_snprintf, "stdlib_snprintf", "Call to SDL_sprintf", TEST_ENABLED }; + +/* Sequence of Standard C routine test cases */ +static const SDLTest_TestCaseReference *stdlibTests[] = { + &stdlibTest1, &stdlibTest2, NULL +}; + +/* Timer test suite (global) */ +SDLTest_TestSuiteReference stdlibTestSuite = { + "Standard C routines", + NULL, + stdlibTests, + NULL +}; diff --git a/test/testautomation_suites.h b/test/testautomation_suites.h index e42cd4266..695681b13 100644 --- a/test/testautomation_suites.h +++ b/test/testautomation_suites.h @@ -14,17 +14,18 @@ extern SDLTest_TestSuiteReference clipboardTestSuite; extern SDLTest_TestSuiteReference eventsTestSuite; extern SDLTest_TestSuiteReference keyboardTestSuite; extern SDLTest_TestSuiteReference mainTestSuite; +extern SDLTest_TestSuiteReference mouseTestSuite; +extern SDLTest_TestSuiteReference pixelsTestSuite; extern SDLTest_TestSuiteReference platformTestSuite; extern SDLTest_TestSuiteReference rectTestSuite; extern SDLTest_TestSuiteReference renderTestSuite; extern SDLTest_TestSuiteReference rwopsTestSuite; +extern SDLTest_TestSuiteReference sdltestTestSuite; +extern SDLTest_TestSuiteReference stdlibTestSuite; extern SDLTest_TestSuiteReference surfaceTestSuite; extern SDLTest_TestSuiteReference syswmTestSuite; -extern SDLTest_TestSuiteReference sdltestTestSuite; -extern SDLTest_TestSuiteReference videoTestSuite; -extern SDLTest_TestSuiteReference mouseTestSuite; extern SDLTest_TestSuiteReference timerTestSuite; -extern SDLTest_TestSuiteReference pixelsTestSuite; +extern SDLTest_TestSuiteReference videoTestSuite; // All test suites SDLTest_TestSuiteReference *testSuites[] = { @@ -33,17 +34,18 @@ SDLTest_TestSuiteReference *testSuites[] = { &eventsTestSuite, &keyboardTestSuite, &mainTestSuite, + &mouseTestSuite, + &pixelsTestSuite, &platformTestSuite, &rectTestSuite, &renderTestSuite, &rwopsTestSuite, + &sdltestTestSuite, + &stdlibTestSuite, &surfaceTestSuite, &syswmTestSuite, - &sdltestTestSuite, - &videoTestSuite, - &mouseTestSuite, &timerTestSuite, - &pixelsTestSuite, + &videoTestSuite, NULL };