Skip to content

Instantly share code, notes, and snippets.

@lecram
Last active August 14, 2020 15:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lecram/71cf186d02dcde7a49e2809879a2ef4a to your computer and use it in GitHub Desktop.
Save lecram/71cf186d02dcde7a49e2809879a2ef4a to your computer and use it in GitHub Desktop.
Automatic random testing for printf() with regard to %-formatting.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
char rand_chr()
{
char c;
do {
c = rand() % 0x80;
} while (!isalnum(c));
return c;
}
int rand_str(char *buf, int max_len)
{
int len = rand() % (max_len + 1);
char *s = buf;
int i;
for (i = 0; i < len; i++)
*s++ = rand_chr();
return len;
}
int main(int argc, char *argv[])
{
char fmt[16];
char str[16];
char *s = fmt;
char *flags = "-+ #0";
char *specs = "diouxXcsp";
char spec;
int i;
if (argc == 1)
srand(time(NULL));
else
srand(atoi(argv[1]));
*s++ = '%';
for (i = strlen(flags); i; i--)
if (rand() % 2)
*s++ = flags[i-1];
spec = specs[rand() % strlen(specs)];
*s = '\0';
if (rand() % 2)
printf("printf(\"[%s%d", fmt, rand() % 12 + 4);
else
printf("printf(\"[%s", fmt);
#ifdef TEST_LENGTH_MODIFIERS
if (rand() % 2)
printf("%c", (rand() % 2) ? 'h' : 'l');
#endif
printf("%c]\", ", spec);
switch (spec) {
case 'd': case 'i':
switch (rand() % 3) {
case 0:
printf("0");
break;
case 1:
printf("-");
case 2:
printf("%d", rand() / 2);
}
break;
case 'c':
printf("'%c'", rand_chr());
break;
case 's':
rand_str(str, 12);
printf("\"%s\"", str);
break;
default:
if (rand() %2)
printf("0");
else
printf("%d", rand());
}
printf(");\n");
return 0;
}
#include <stdio.h>
int main()
{
int a = -357;
int b = 123;
int n;
/* no formatting */
printf("hello\n");
/* basic formatting */
printf("%d\n", a);
/* multiple */
printf("%d + %d = %d\n", a, b, a+b);
/* %n */
printf("%d + %d =%n %d\n", a, b, &n, a+b);
/* arg as width */
printf("%*d\n", n, a);
/* flag */
printf("%0*d\n", n, a);
/* multiple flags */
printf("%+0*d\n", n, b);
/* unordered flags */
printf("%0+*d\n", n, b);
/* length modifier */
printf("%0+*ld\n", n, b);
/* some complex combination */
printf("%2c, %0+*ld, %#X\n", 'z', n/2, b, 0xBC);
return 0;
}
#!/bin/sh
# requirements:
# * POSIX shell
# * Tiny C Compiler (TCC) with any reference libc (e.g. musl)
# * neatcc driver supporting -r (https://github.com/lecram/neatrun)
# * md5sum(1)
# first do smoke testing
smk=smoke_fmt.c
otcc=$(tcc -run "$smk" | md5sum)
oncc=$(neatcc -r "$smk" | md5sum)
if [ "$otcc" != "$oncc" ]; then
printf "smoke tests failed\n"
exit
fi
printf "smoke tests passed\n"
# do random testing
src=tmpfmt.c
pre="#include <stdio.h>\nint main() {\n\t"
pos="\n\tfflush(stdout);\n\treturn 0;\n}\n"
if [ $# -eq 0 ]; then
seed=$(date '+%s')
else
seed=$1
fi
seed=$((seed%10000))
i=0
while true; do
rndfmt=$(tcc -run rndfmt.c $seed)
printf "$pre%s$pos" "$rndfmt" > "$src"
otcc=$(tcc -run "$src")
oncc=$(neatcc -r "$src")
printf "%4d %s %s %s\n" "$seed" "$rndfmt" "$otcc" "$oncc"
if [ "$otcc" != "$oncc" ]; then
break
fi
seed=$((seed*(seed+i)/10%10000+7))
i=$((i+1))
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment