609 lines
13 KiB
C++
609 lines
13 KiB
C++
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#ifdef _WIN32
|
|
#include "getopt.h"
|
|
#else
|
|
#include <getopt.h>
|
|
#endif
|
|
#include <assert.h>
|
|
|
|
#include "printf_config.h"
|
|
#include "../src/printf/printf.h"
|
|
|
|
#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES
|
|
#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES 0
|
|
#endif
|
|
|
|
#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES
|
|
# define printf_ printf
|
|
# define sprintf_ sprintf
|
|
# define vsprintf_ vsprintf
|
|
# define snprintf_ snprintf
|
|
# define vsnprintf_ vsnprintf
|
|
# define vprintf_ vprintf
|
|
#endif
|
|
|
|
|
|
//*******************************************************
|
|
// Defines
|
|
//*******************************************************
|
|
|
|
#define min(a,b) ( (a) < (b) ? (a) : (b) )
|
|
#define MAIN_TITLE "autotest"
|
|
|
|
#define BUF_SIZE 100
|
|
#define WIDTH_MAX 25
|
|
|
|
// range for float testing
|
|
#define FLOAT_TST_MIN 1E-5
|
|
#define FLOAT_TST_MAX 1E5
|
|
|
|
#define PRECISION_LIMIT_DEFAULT 17
|
|
|
|
struct cmdopt_struct
|
|
{
|
|
bool i;
|
|
bool x;
|
|
bool o;
|
|
bool u;
|
|
bool f;
|
|
bool e;
|
|
bool g;
|
|
bool left_justify;
|
|
bool zero_pad;
|
|
bool hash;
|
|
bool width;
|
|
bool prec;
|
|
int prec_max;
|
|
};
|
|
|
|
// Valid short options
|
|
#define VALIDOPTS "ixoufeglz#wp::ah"
|
|
|
|
#define MSG_USAGE "\n\
|
|
Usage: " MAIN_TITLE " [OPTION/s]\n\
|
|
Compare randomly formatted strings with the stdio printf()\n\
|
|
Matching strings are output to stdout\n\
|
|
Errors are output to stderr\r\n\
|
|
\n\
|
|
-i test %%i \n\
|
|
-x test %%x\n\
|
|
-o test %%o\n\
|
|
-u test %%u\n\
|
|
-f test %%f\n\
|
|
-e test %%e\n\
|
|
-g test %%g (float falling back to exp)\n\
|
|
-l test left justification\r\n\
|
|
-z test zero padding\n\
|
|
-# test prepending base 0 0x\n\
|
|
-w test width specifier\n\
|
|
-p <limit> test precision specifier, with an optional limit for %%f %%e %%g\n\
|
|
-a test all of the above, with a default precision limit of %i for %%f %%e %%g\n\
|
|
\n\
|
|
-h show these options\n\
|
|
\n\
|
|
Examples:\n\
|
|
" MAIN_TITLE " -a test with all options, showing all passes and failures\n\
|
|
" MAIN_TITLE " -a 1>/dev/null test with all options, showing only errors, with stdout > null\n\
|
|
" MAIN_TITLE " -ap14 1>/dev/null test with all options and precision limit of 14 for %%f %%e %%g, showing only errors, with stdout > null\n\
|
|
" MAIN_TITLE " -ixou 1>/dev/null test only %%i %%x %%o %%u, showing only errors, with stdout > null\n\
|
|
\n\
|
|
"
|
|
|
|
//*******************************************************
|
|
// Variables
|
|
//*******************************************************
|
|
|
|
static struct cmdopt_struct opts;
|
|
|
|
//*******************************************************
|
|
// Prototypes
|
|
//*******************************************************
|
|
|
|
static bool parse_options(int argc, char *argv[]);
|
|
static void run_tests(void);
|
|
|
|
static void test_i(void);
|
|
static void test_x(void);
|
|
static void test_o(void);
|
|
static void test_u(void);
|
|
static void test_f(void);
|
|
static void test_e(void);
|
|
static void test_g(void);
|
|
|
|
float rand_float(float a, float b);
|
|
|
|
//*******************************************************
|
|
// Functions
|
|
//*******************************************************
|
|
|
|
void putchar_(char character)
|
|
{
|
|
(void)character;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
if(!parse_options(argc, argv))
|
|
printf(MSG_USAGE, PRECISION_LIMIT_DEFAULT);
|
|
else
|
|
run_tests();
|
|
return 0;
|
|
}
|
|
|
|
static bool parse_options(int argc, char *argv[])
|
|
{
|
|
char c;
|
|
bool gotopt = false;
|
|
|
|
opts.i = false;
|
|
opts.x = false;
|
|
opts.o = false;
|
|
opts.u = false;
|
|
opts.f = false;
|
|
opts.e = false;
|
|
opts.g = false;
|
|
opts.left_justify = false;
|
|
opts.zero_pad = false;
|
|
opts.hash = false;
|
|
opts.width = false;
|
|
opts.prec = false;
|
|
opts.prec_max = -1;
|
|
|
|
while ((c = getopt (argc, argv, VALIDOPTS)) != -1)
|
|
{
|
|
gotopt = true;
|
|
if(c == 'i')
|
|
opts.i = true;
|
|
else if(c == 'x')
|
|
opts.x = true;
|
|
else if(c == 'o')
|
|
opts.o = true;
|
|
else if(c == 'u')
|
|
opts.u = true;
|
|
else if(c == 'f')
|
|
opts.f = true;
|
|
else if(c == 'e')
|
|
opts.e = true;
|
|
else if(c == 'g')
|
|
opts.g = true;
|
|
else if(c == 'l')
|
|
opts.left_justify = true;
|
|
else if(c == 'z')
|
|
opts.zero_pad = true;
|
|
else if(c == '#')
|
|
opts.hash = true;
|
|
else if(c == 'w')
|
|
opts.width = true;
|
|
else if(c == 'p')
|
|
{
|
|
opts.prec = true;
|
|
if(optarg)
|
|
opts.prec_max = atoi(optarg);
|
|
}
|
|
else if(c == 'a')
|
|
{
|
|
opts.i = true;
|
|
opts.x = true;
|
|
opts.o = true;
|
|
opts.u = true;
|
|
opts.f = true;
|
|
opts.e = true;
|
|
opts.g = true;
|
|
opts.left_justify = true;
|
|
opts.zero_pad = true;
|
|
opts.hash = true;
|
|
opts.width = true;
|
|
opts.prec = true;
|
|
}
|
|
else if(c != 'h')
|
|
{
|
|
fprintf(stderr, "Unknown option\n");
|
|
assert(false);
|
|
};
|
|
};
|
|
|
|
if(opts.prec_max == -1)
|
|
opts.prec_max = PRECISION_LIMIT_DEFAULT;
|
|
|
|
return gotopt;
|
|
}
|
|
|
|
static void run_tests(void)
|
|
{
|
|
while(true)
|
|
{
|
|
if(opts.i)
|
|
test_i();
|
|
if(opts.x)
|
|
test_x();
|
|
if(opts.o)
|
|
test_o();
|
|
if(opts.u)
|
|
test_u();
|
|
if(opts.f)
|
|
test_f();
|
|
if(opts.e)
|
|
test_e();
|
|
if(opts.g)
|
|
test_g();
|
|
};
|
|
}
|
|
|
|
static void test_i(void)
|
|
{
|
|
FILE* dst = stdout;
|
|
char fmt_buf[BUF_SIZE];
|
|
char std_buf[BUF_SIZE];
|
|
char tst_buf[BUF_SIZE];
|
|
bool width_flag;
|
|
int width;
|
|
int value;
|
|
|
|
strcpy(fmt_buf, "%");
|
|
if(rand()&1 && opts.left_justify)
|
|
strcat(fmt_buf, "-");
|
|
if(rand()&1)
|
|
strcat(fmt_buf, "+");
|
|
else if(rand()&1)
|
|
strcat(fmt_buf, " ");
|
|
|
|
width_flag = (rand()&1 && opts.width);
|
|
|
|
width = 1+rand()%WIDTH_MAX;
|
|
if(width_flag)
|
|
{
|
|
if(rand()&1 && opts.zero_pad)
|
|
strcat(fmt_buf, "0");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
|
};
|
|
|
|
if(rand()&1 && opts.prec)
|
|
{
|
|
strcat(fmt_buf, ".");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width);
|
|
};
|
|
|
|
strcat(fmt_buf, "i");
|
|
value = rand();
|
|
if(rand()&1)
|
|
value = 0;
|
|
if(rand()&1)
|
|
value*=-1;
|
|
|
|
sprintf(std_buf, fmt_buf, value);
|
|
sprintf_(tst_buf, fmt_buf, value);
|
|
|
|
if(strcmp(std_buf,tst_buf))
|
|
dst = stderr;
|
|
|
|
fprintf(dst, "\nfmt = \"%s\" value = %i\n", fmt_buf, value);
|
|
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
|
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
|
}
|
|
|
|
static void test_x(void)
|
|
{
|
|
FILE* dst = stdout;
|
|
char fmt_buf[BUF_SIZE];
|
|
char std_buf[BUF_SIZE];
|
|
char tst_buf[BUF_SIZE];
|
|
bool width_flag;
|
|
int width;
|
|
unsigned int value;
|
|
|
|
strcpy(fmt_buf, "%");
|
|
if(rand()&1 && opts.left_justify)
|
|
strcat(fmt_buf, "-");
|
|
if(rand()&1 && opts.hash)
|
|
strcat(fmt_buf, "#");
|
|
|
|
width_flag = (rand()&1 && opts.width);
|
|
|
|
width = 1+rand()%WIDTH_MAX;
|
|
if(width_flag)
|
|
{
|
|
if(rand()&1 && opts.zero_pad)
|
|
strcat(fmt_buf, "0");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
|
};
|
|
|
|
if(rand()&1 && opts.prec)
|
|
{
|
|
strcat(fmt_buf, ".");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width);
|
|
};
|
|
|
|
strcat(fmt_buf, "x");
|
|
value = (unsigned)rand();
|
|
if(rand()&1)
|
|
value = 0;
|
|
|
|
sprintf(std_buf, fmt_buf, value);
|
|
sprintf_(tst_buf, fmt_buf, value);
|
|
|
|
if(strcmp(std_buf,tst_buf))
|
|
dst = stderr;
|
|
|
|
fprintf(dst, "\nfmt = \"%s\" value = %#x\n", fmt_buf, value);
|
|
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
|
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
|
}
|
|
|
|
static void test_o(void)
|
|
{
|
|
FILE* dst = stdout;
|
|
char fmt_buf[BUF_SIZE];
|
|
char std_buf[BUF_SIZE];
|
|
char tst_buf[BUF_SIZE];
|
|
bool width_flag;
|
|
int width;
|
|
unsigned int value;
|
|
|
|
strcpy(fmt_buf, "%");
|
|
if(rand()&1 && opts.left_justify)
|
|
strcat(fmt_buf, "-");
|
|
if(rand()&1 && opts.hash)
|
|
strcat(fmt_buf, "#");
|
|
|
|
width_flag = (rand()&1 && opts.width);
|
|
|
|
width = 1+rand()%WIDTH_MAX;
|
|
if(width_flag)
|
|
{
|
|
if(rand()&1 && opts.zero_pad)
|
|
strcat(fmt_buf, "0");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
|
};
|
|
|
|
if(rand()&1 && opts.prec)
|
|
{
|
|
strcat(fmt_buf, ".");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width);
|
|
};
|
|
|
|
strcat(fmt_buf, "o");
|
|
value = (unsigned)rand();
|
|
if(rand()&1)
|
|
value = 0;
|
|
|
|
sprintf(std_buf, fmt_buf, value);
|
|
sprintf_(tst_buf, fmt_buf, value);
|
|
|
|
if(strcmp(std_buf,tst_buf))
|
|
dst = stderr;
|
|
|
|
fprintf(dst, "\nfmt = \"%s\" value = %o\n", fmt_buf, value);
|
|
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
|
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
|
}
|
|
|
|
static void test_u(void)
|
|
{
|
|
FILE* dst = stdout;
|
|
char fmt_buf[BUF_SIZE];
|
|
char std_buf[BUF_SIZE];
|
|
char tst_buf[BUF_SIZE];
|
|
bool width_flag;
|
|
int width;
|
|
unsigned int value;
|
|
|
|
strcpy(fmt_buf, "%");
|
|
if(rand()&1 && opts.left_justify)
|
|
strcat(fmt_buf, "-");
|
|
|
|
width_flag = (rand()&1 && opts.width);
|
|
|
|
width = 1+rand()%WIDTH_MAX;
|
|
if(width_flag)
|
|
{
|
|
if(rand()&1 && opts.zero_pad)
|
|
strcat(fmt_buf, "0");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
|
};
|
|
|
|
if(rand()&1 && opts.prec)
|
|
{
|
|
strcat(fmt_buf, ".");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%width);
|
|
};
|
|
|
|
strcat(fmt_buf, "u");
|
|
value = (unsigned)rand();
|
|
if(rand()&1)
|
|
value = 0;
|
|
|
|
sprintf(std_buf, fmt_buf, value);
|
|
sprintf_(tst_buf, fmt_buf, value);
|
|
|
|
if(strcmp(std_buf,tst_buf))
|
|
dst = stderr;
|
|
|
|
fprintf(dst, "\nfmt = \"%s\" value = %u\n", fmt_buf, value);
|
|
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
|
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
|
}
|
|
|
|
static void test_f(void)
|
|
{
|
|
FILE* dst = stdout;
|
|
char fmt_buf[BUF_SIZE];
|
|
char std_buf[BUF_SIZE];
|
|
char tst_buf[BUF_SIZE];
|
|
bool width_flag;
|
|
int width;
|
|
double value;
|
|
|
|
strcpy(fmt_buf, "%");
|
|
if(rand()&1 && opts.left_justify)
|
|
strcat(fmt_buf, "-");
|
|
if(rand()&1 && opts.hash)
|
|
strcat(fmt_buf, "#");
|
|
if(rand()&1)
|
|
strcat(fmt_buf, "+");
|
|
else if(rand()&1)
|
|
strcat(fmt_buf, " ");
|
|
|
|
width_flag = (rand()&1 && opts.width);
|
|
|
|
width = 1+rand()%WIDTH_MAX;
|
|
if(width_flag)
|
|
{
|
|
if(rand()&1 && opts.zero_pad)
|
|
strcat(fmt_buf, "0");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
|
};
|
|
|
|
if(rand()&1 && opts.prec)
|
|
{
|
|
strcat(fmt_buf, ".");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%min(width, opts.prec_max));
|
|
};
|
|
|
|
strcat(fmt_buf, "f");
|
|
|
|
if(rand()&1)
|
|
value = 0;
|
|
else
|
|
value = (double) rand_float(FLOAT_TST_MIN, FLOAT_TST_MAX);
|
|
|
|
if(rand()&1)
|
|
value*=-1;
|
|
|
|
sprintf(std_buf, fmt_buf, value);
|
|
sprintf_(tst_buf, fmt_buf, value);
|
|
|
|
if(strcmp(std_buf,tst_buf))
|
|
dst = stderr;
|
|
|
|
fprintf(dst, "\nfmt = \"%s\" value = %.18f\n", fmt_buf, value);
|
|
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
|
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
|
}
|
|
|
|
static void test_e(void)
|
|
{
|
|
FILE* dst = stdout;
|
|
char fmt_buf[BUF_SIZE];
|
|
char std_buf[BUF_SIZE];
|
|
char tst_buf[BUF_SIZE];
|
|
bool width_flag;
|
|
int width;
|
|
double value;
|
|
|
|
strcpy(fmt_buf, "%");
|
|
if(rand()&1 && opts.left_justify)
|
|
strcat(fmt_buf, "-");
|
|
if(rand()&1 && opts.hash)
|
|
strcat(fmt_buf, "#");
|
|
if(rand()&1)
|
|
strcat(fmt_buf, "+");
|
|
else if(rand()&1)
|
|
strcat(fmt_buf, " ");
|
|
|
|
width_flag = (rand()&1 && opts.width);
|
|
|
|
width = 1+rand()%WIDTH_MAX;
|
|
if(width_flag)
|
|
{
|
|
if(rand()&1 && opts.zero_pad)
|
|
strcat(fmt_buf, "0");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
|
};
|
|
|
|
if(rand()&1 && opts.prec)
|
|
{
|
|
strcat(fmt_buf, ".");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%min(width,opts.prec_max));
|
|
};
|
|
|
|
strcat(fmt_buf, "e");
|
|
|
|
if(rand()&1)
|
|
value = 0;
|
|
else
|
|
value = (double) rand_float(FLOAT_TST_MIN, FLOAT_TST_MAX);
|
|
|
|
if(rand()&1)
|
|
value*=-1;
|
|
|
|
sprintf(std_buf, fmt_buf, value);
|
|
sprintf_(tst_buf, fmt_buf, value);
|
|
|
|
if(strcmp(std_buf,tst_buf))
|
|
dst = stderr;
|
|
|
|
fprintf(dst, "\nfmt = \"%s\" value = %.18f\n", fmt_buf, value);
|
|
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
|
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
|
}
|
|
|
|
static void test_g(void)
|
|
{
|
|
FILE* dst = stdout;
|
|
char fmt_buf[BUF_SIZE];
|
|
char std_buf[BUF_SIZE];
|
|
char tst_buf[BUF_SIZE];
|
|
bool width_flag;
|
|
int width;
|
|
double value;
|
|
|
|
strcpy(fmt_buf, "%");
|
|
if(rand()&1 && opts.left_justify)
|
|
strcat(fmt_buf, "-");
|
|
if(rand()&1 && opts.hash)
|
|
strcat(fmt_buf, "#");
|
|
if(rand()&1)
|
|
strcat(fmt_buf, "+");
|
|
else if(rand()&1)
|
|
strcat(fmt_buf, " ");
|
|
|
|
width_flag = (rand()&1 && opts.width);
|
|
|
|
width = 1+rand()%WIDTH_MAX;
|
|
if(width_flag)
|
|
{
|
|
if(rand()&1 && opts.zero_pad)
|
|
strcat(fmt_buf, "0");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", width);
|
|
};
|
|
|
|
if(rand()&1 && opts.prec)
|
|
{
|
|
strcat(fmt_buf, ".");
|
|
sprintf(&fmt_buf[strlen(fmt_buf)], "%i", 1+rand()%min(width,opts.prec_max));
|
|
};
|
|
|
|
strcat(fmt_buf, "g");
|
|
|
|
if(rand()&1)
|
|
value = 0;
|
|
else
|
|
value = (double) rand_float(FLOAT_TST_MIN, FLOAT_TST_MAX);
|
|
|
|
if(rand()&1)
|
|
value*=-1;
|
|
|
|
sprintf(std_buf, fmt_buf, value);
|
|
sprintf_(tst_buf, fmt_buf, value);
|
|
|
|
if(strcmp(std_buf,tst_buf))
|
|
dst = stderr;
|
|
|
|
fprintf(dst, "\nfmt = \"%s\" value = %.18f\n", fmt_buf, value);
|
|
fprintf(dst, "gnu = \"%s\"\n", std_buf);
|
|
fprintf(dst, "mpa = \"%s\"\n", tst_buf);
|
|
}
|
|
|
|
float rand_float(float a, float b)
|
|
{
|
|
float random = ((float) rand()) / (float) RAND_MAX;
|
|
float diff = b - a;
|
|
float r = random * diff;
|
|
return a + r;
|
|
}
|