#include #include #include #include #include #include #include #define NAN __builtin_nan("") #define INFINITY __builtin_inf() struct printf_test { char const *format; /* Argument information is set by macros below */ enum { TYPE_I32, TYPE_U32, TYPE_STR, TYPE_PTR, TYPE_U64, TYPE_DBL } type; union { int i32; uint32_t u32; char const *str; void *ptr; uint64_t u64; double dbl; }; char const *argument_as_string; char const *solution; }; struct printf_result { char answer[15]; uint8_t passed; }; #define I32(i) TYPE_I32, { .i32 = i }, #i #define U32(u) TYPE_U32, { .u32 = u }, #u #define STR(s) TYPE_STR, { .str = s }, #s #define PTR(p) TYPE_PTR, { .ptr = (void *)p }, #p #define U64(u) TYPE_U64, { .u64 = u }, #u #define DBL(d) TYPE_DBL, { .dbl = d }, #d static void run_tests(ft_test *t0, struct printf_test const *tests, struct printf_result *results) { for(int i = 0; tests[i].format; i++) { struct printf_test const *t = &tests[i]; struct printf_result *r = &results[i]; int c = -1; #define run(TYPE, field) \ case TYPE: \ c = snprintf(r->answer, 16, t->format, t->field); \ break; switch(t->type) { run(TYPE_I32, i32) run(TYPE_U32, u32) run(TYPE_STR, str) run(TYPE_PTR, ptr) run(TYPE_U64, u64) run(TYPE_DBL, dbl) } r->passed = (!strcmp(r->answer, t->solution) && c == (int)strlen(t->solution)); ft_assert(t0, r->passed == 1); } } struct table_params { struct printf_test const *test; struct printf_result const *result; }; static void table_gen(gtable *t0, int row, void *params0) { struct table_params *params = params0; struct printf_test const *t = params->test; struct printf_result const *r = params->result; char c1[16]; sprintf(c1, "%d", row+1); gtable_provide(t0, c1, t[row].format, t[row].solution, r[row].answer); } static jwidget *widget_gen(struct printf_test const *tests, struct printf_result const *results) { if(!results) return NULL; static struct table_params p; p.test = tests; p.result = results; int test_count = 0; while(tests[test_count].format) test_count++; gtable *t = gtable_create(4, table_gen, &p, NULL); gtable_set_rows(t, test_count); gtable_set_column_titles(t, "ID", "Format", "Expected", "Got"); gtable_set_column_sizes(t, 3, 8, 16, 16); gtable_set_row_spacing(t, 2); jwidget_set_stretch(t, 1, 1, false); return (void *)t; } //--- // General features //--- static void _ft_stdio_printf_basics(ft_test *t) { char str[32]; int n; /* Basic output */ n = snprintf(str, 32, "test"); ft_assert(t, n == 4 && !strcmp(str, "test")); /* Truncated output */ n = snprintf(str, 8, "12345678901234"); ft_assert(t, n == 14 && !strcmp(str, "1234567")); /* Parameter size */ n = snprintf(str, 32, "%hhx", 0x123); ft_assert(t, n == 2 && !strcmp(str, "23")); /* Truncated output in the middle of a format */ n = snprintf(str, 5, "%hd", 100000); ft_assert(t, n == 6 && !strcmp(str, "-310")); /* Percent format */ n = snprintf(str, 32, "%%d"); ft_assert(t, n == 2 && !strcmp(str, "%d")); /* Nonexistent formats */ n = snprintf(str, 32, "a%.7Z"); ft_assert(t, n == 1 && !strcmp(str, "a")); /* Extension */ n = snprintf(str, 32, "%.4D", 1234567); ft_assert(t, n == 8 && !strcmp(str, "123.4567")); } ft_test ft_stdio_printf_basics = { .name = "printf: Basics options", .function = _ft_stdio_printf_basics, }; //--- // Usual formats //--- static struct printf_test const usual_tests[] = { /* Base cases with length and precision */ { "%d", I32(-849), "-849" }, { "%7i", I32(78372), " 78372" }, { "%3d", I32(65536), "65536" }, { "%6.4d", I32(17), " 0017" }, { "%6.3d", I32(-1876), " -1876" }, { "%.0d", I32(0), "" }, { "%.d", I32(0), "" }, /* Sign */ { "%+i", I32(15), "+15" }, { "% 7i", I32(78372), " 78372" }, { "% d", I32(65536), " 65536" }, /* Alignment */ { "%08d", I32(-839), "-0000839" }, { "%-6.4d", I32(17), "0017 " }, { "%-+6.4i", I32(17), "+0017 " }, /* Bases */ { "%d", I32(0xcb7), "3255" }, { "%x", U32(0xcb7), "cb7" }, { "%X", U32(0xcb7), "CB7" }, { "%o", U32(0xcb7), "6267" }, /* Argument size */ { "%zu", U32(1000000000), "1000000000" }, { "%td", I32(99999), "99999" }, { "%lld", U64(10000000000ll), "10000000000" }, { "%llx", U64(0x123456789abull), "123456789ab" }, { "%jx", U64(0x123456789abull), "123456789ab" }, /* Alternative prefixes */ { "%#x", U32(0x7b), "0x7b" }, { "%#X", U32(0x7b), "0X7B" }, { "%#o", U32(255), "0377" }, /* Pointers */ { "%p", PTR(0xa44b0000), "0xa44b0000" }, /* Characters and strings */ { "%s", STR("HellWrld!"), "HellWrld!" }, { "%-8.5s", STR("Hello, World!"), "Hello " }, { "%c", I32(100), "d" }, { "%6c", I32('#'), " #", }, /* NULL terminator */ { NULL } }; static struct printf_result *usual_results = NULL; static void _ft_stdio_printf_formats(ft_test *t) { int test_count = sizeof usual_tests / sizeof usual_tests[0] - 1; if(usual_results) free(usual_results); usual_results = malloc(test_count * sizeof *usual_results); if(!ft_assert(t, usual_results != NULL)) return; run_tests(t, usual_tests, usual_results); } static jwidget *_ft_stdio_printf_formats_widget(ft_test *test) { (void)test; return widget_gen(usual_tests, usual_results); } ft_test ft_stdio_printf_formats = { .name = "printf: Usual formats", .function = _ft_stdio_printf_formats, .widget = _ft_stdio_printf_formats_widget, }; //--- // Floating-point formats //--- static struct printf_test const fp_tests[] = { /* Floating-point special values */ { "%f", DBL(NAN), "nan" }, { "%3.5F", DBL(NAN), "NAN" }, { "%+2F", DBL(-INFINITY), "-INF" }, { "%10G", DBL(INFINITY), "INF" }, { "%+g", DBL(INFINITY), "inf" }, { "%7.3e", DBL(-INFINITY), "-inf" }, { "%8E", DBL(NAN), "NAN" }, /* Simple floating-point cases */ { "%f", DBL(13e3), "13000.000000" }, { "%.3f", DBL(-13e3), "-13000.000" }, { "%.F", DBL(13e3), "13000" }, { "%f", DBL(-12.42), "-12.420000" }, { "%.0f", DBL(-12.42), "-12", }, { "%.1f", DBL(12.42), "12.4", }, { "%.8F", DBL(12.42), "12.42000000" }, { "%F", DBL(0.0312), "0.031200" }, { "%.4f", DBL(0.0312), "0.0312" }, { "%.2f", DBL(-0.0312), "-0.03", }, { "%.0f", DBL(0.0312), "0", }, /* Floating-point rounding */ { "%.f", DBL(1.75), "2", }, { "%.1f", DBL(1.75), "1.8", }, { "%.3F", DBL(0.0625), "0.063", }, { "%.1F", DBL(-99.99), "-100.0" }, { "%.2F", DBL(12999.992), "12999.99" }, { "%.2f", DBL(12999.995), "13000.00" }, /* General options with floating-point */ { "%09.3F", DBL(123.4567), "00123.457" }, { "%05.0f", DBL(99.99), "00100" }, { "%+11f", DBL(0.0035678), " +0.003568" }, { "%- 11F", DBL(0.0035678), " 0.003568 " }, /* Simple exponent cases */ { "%e", DBL(3.876), "3.876000e+00" }, { "%E", DBL(-38473.34254), "-3.847334E+04" }, { "%.2e", DBL(187.2), "1.87e+02" }, { "%.1e", DBL(-18.27), "-1.8e+01" }, { "%e", DBL(1e-10), "1.000000e-10" }, { "%E", DBL(3.873e180), "3.873000E+180" }, { "%.e", DBL(0.0005), "5e-04" }, { "%.E", DBL(128.37), "1E+02" }, { "%.5e", DBL(912.3), "9.12300e+02" }, /* Exponent with rounding and general options */ { "%11.3e", DBL(12.499), " 1.250e+01" }, { "% -11.E", DBL(358.7), " 4E+02 " }, { "%+e", DBL(14.99999), "+1.499999e+01" }, { "%+e", DBL(14.999999), "+1.500000e+01" }, /* Exponent sizes */ { "%.2e", DBL(1e10), "1.00e+10" }, { "%.2e", DBL(1e100), "1.00e+100" }, { "%.2e", DBL(1e-10), "1.00e-10" }, { "%.2e", DBL(1e-100), "1.00e-100" }, /* Format selection and trailing zero elimination in %g */ { "%g", DBL(124), "124" }, { "%g", DBL(2.3), "2.3" }, { "%g", DBL(0.0001), "0.0001" }, { "%G", DBL(0.00001), "1E-05" }, { "%g", DBL(834e-93), "8.34e-91" }, { "%g", DBL(32842914732), "3.28429e+10" }, { "%g", DBL(123456), "123456" }, { "%G", DBL(1234567), "1.23457E+06" }, { "%G", DBL(123000), "123000" }, /* Rounding and general options in %g */ { "%.3g", DBL(1278), "1.28e+03" }, { "%+.8g", DBL(1.23e5), "+123000" }, { "%- 12.8g", DBL(123000.01), " 123000.01 " }, { "%0.8g", DBL(123000.001), "123000" }, { "%08.8g", DBL(123000.001), "00123000" }, { "%g", DBL(1.234567), "1.23457" }, { "%.1g", DBL(1.8), "2" }, /* Edge cases of significant digit count in %g */ { "%.4g", DBL(999.93), "999.9" }, { "%.4g", DBL(999.97), "1000" }, { "%.5g", DBL(999.97), "999.97" }, { "%.8G", DBL(999.97), "999.97" }, { "%.4g", DBL(1.0001), "1", }, /* Elimination of trailing zeros in %g */ { "%.3g", DBL(3002), "3e+03" }, { "%.5g", DBL(0.00000034), "3.4e-07" }, { "%.6g", DBL(999.9997), "1000" }, /* NULL terminator */ { NULL } }; static struct printf_result *fp_results = NULL; static void _ft_stdio_printf_fp(ft_test *t) { int test_count = sizeof fp_tests / sizeof fp_tests[0] - 1; if(fp_results) free(fp_results); fp_results = malloc(test_count * sizeof *fp_results); if(!ft_assert(t, fp_results != NULL)) return; run_tests(t, fp_tests, fp_results); } static jwidget *_ft_stdio_printf_fp_widget(ft_test *test) { (void)test; return widget_gen(fp_tests, fp_results); } ft_test ft_stdio_printf_fp = { .name = "printf: Floating-point formats", .function = _ft_stdio_printf_fp, .widget = _ft_stdio_printf_fp_widget, };