/* Copyright (c) 2001-2018 Dovecot authors, see the included COPYING file */
/* Unit tests for printf-format-fix helper */
#include "test-lib.h"
#include "printf-format-fix.h"
#include <string.h>
struct format_fix_rewrites {
const char *input;
const char *output;
size_t length;
};
static void test_unchanged()
{
static const char *tests[] = {
"Hello world",
"Embedded %%, %u, %f, %s, etc. are OK",
"Allow %#0- +s flags",
"duplicate flags in different args %0-123s %0-123s",
"Minimum length %9999s",
"Minimum length parameter %*s",
"Precision %.9999s",
"Precision %1.9999s",
"Precision parameter %1.*s %.*s",
"Floating precisions such as %.0f %0.4f %-4.0f",
"Length modifiers %hd %hhd %ld %lld %Lg %jd %zd %td",
"Specifiers %s %u %d %c %i %x %X %p %o %e %E %f %F %g %G %a %A",
"%%doesn't cause confusion in %%m and %%n",
};
unsigned int i;
test_begin("printf_format_fix(safe)");
for (i = 0; i < N_ELEMENTS(tests); ++i) {
size_t len;
T_BEGIN {
test_assert_idx(printf_format_fix(tests[i])
== tests[i], i);
test_assert_idx(printf_format_fix_get_len(tests[i], &len)
== tests[i], i);
test_assert_idx(len == strlen(tests[i]), i);
} T_END;
}
test_end();
}
static void test_ok_changes()
{
static const char *tests[] = {
"OK to have a trailing %m",
"%m can appear at the start too",
"Even %m in the middle with a confusing %%m elsewhere is OK",
};
unsigned int i;
const char *needle;
unsigned int needlen;
int old_errno = errno;
test_begin("printf_format_fix(rewrites)");
errno = EINVAL;
needle = strerror(errno);
i_assert(needle != NULL);
needlen = strlen(needle);
for (i = 0; i < N_ELEMENTS(tests); ++i) {
size_t len;
char const *chgd;
char const *insert;
unsigned int offs;
T_BEGIN {
chgd = printf_format_fix(tests[i]);
test_assert_idx(chgd != tests[i], i);
insert = strstr(chgd, needle);
test_assert_idx(insert != NULL, i);
offs = insert - chgd;
test_assert_idx(memcmp(chgd, tests[i], offs) == 0, i);
test_assert_idx(memcmp(chgd+offs, needle, needlen) == 0, i);
test_assert_idx(strcmp(chgd+offs+needlen, tests[i]+offs+2) == 0, i);
chgd = printf_format_fix_get_len(tests[i], &len);
test_assert_idx(chgd != tests[i], i);
test_assert_idx(len == strlen(chgd), i);
insert = strstr(chgd, needle);
test_assert_idx(insert != NULL, i);
offs = insert - chgd;
test_assert_idx(memcmp(chgd, tests[i], offs) == 0, i);
test_assert_idx(memcmp(chgd+offs, needle, needlen) == 0, i);
test_assert_idx(memcmp(chgd+offs+needlen, tests[i]+offs+2, len-needlen-offs) == 0, i);
} T_END;
}
errno = old_errno;
test_end();
}
void test_printf_format_fix()
{
test_unchanged();
test_ok_changes();
}
/* Want to test the panics too? go for it! */
enum fatal_test_state fatal_printf_format_fix(unsigned int stage)
{
static const struct {
const char *format;
const char *expected_fatal;
} fatals[] = {
{ "no no no %n's", "%n modifier used" },
{ "no no no %-1234567890123n's with extra stuff", "Too large minimum field width" },
{ "%m allowed once, but not twice: %m", "%m used twice" },
{ "%m must not obscure a later %n", "%n modifier used" },
{ "definitely can't have a tailing %", "Missing % specifier" },
{ "Evil %**%n", "Unsupported 0x2a specifier" },
{ "Evil %*#%99999$s", "Unsupported 0x23 specifier" },
{ "No weird %% with %0%", "Unsupported 0x25 specifier" },
{ "No duplicate modifiers %00s", "Duplicate % flag '0'" },
{ "Minimum length can't be too long %10000s", "Too large minimum field width" },
{ "Minimum length doesn't support %*1$s", "Unsupported 0x31 specifier" },
{ "Precision can't be too long %.10000s", "Too large precision" },
{ "Precision can't be too long %1.10000s", "Too large precision" },
{ "Precision doesn't support %1.-1s", "Unsupported 0x2d specifier" },
};
if(stage >= N_ELEMENTS(fatals)) {
test_end();
return FATAL_TEST_FINISHED;
}
if(stage == 0)
test_begin("fatal_printf_format_fix");
/* let's crash! */
test_expect_fatal_string(fatals[stage].expected_fatal);
(void)printf_format_fix(fatals[stage].format);
return FATAL_TEST_FAILURE;
}