341 lines
11 KiB
C
341 lines
11 KiB
C
#include <ft/test.h>
|
|
#include <ft/all-tests.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <fxlibc/printf.h>
|
|
|
|
#include <ft/widgets/gtable.h>
|
|
|
|
#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,
|
|
};
|