#include "../../unity/unity.h" #include #include #include #include #include #include /* The test file is included into the same translation unit as od.c, so we can use internal types, enums, arrays, and functions directly: - struct tspec, enum output_format, enum size_spec - width_bytes, integral_type_size, fp_type_size - ispec_to_format, print_* functions */ static print_function_type expected_print_fn_integral(enum size_spec size_spec, enum output_format fmt) { /* Mirror the selection logic in decode_one_format for integer formats. */ if (size_spec == INT) return print_int; else if (SHORT < INT && size_spec == SHORT) return (fmt == SIGNED_DECIMAL ? print_s_short : print_short); else if (CHAR < SHORT && size_spec == CHAR) return (fmt == SIGNED_DECIMAL ? print_s_char : print_char); else if (INT < LONG && size_spec == LONG) return print_long; else if (LONG < INTMAX && size_spec == INTMAX) return print_intmax; else if (LONG < LONG_LONG && LONG_LONG < INTMAX && size_spec == LONG_LONG) return print_long_long; /* Should not reach here for valid inputs. */ return (print_function_type)0; } static int fp_size_supported(size_t sz) { /* Match the acceptance check used by decode_one_format. */ if (sz >= countof(fp_type_size)) return 0; if (fp_type_size[sz] == NO_SIZE) return 0; #if !FLOAT16_SUPPORTED && BF16_SUPPORTED /* Special-case rejection per decode_one_format. */ if (sz == sizeof(bfloat16)) return 0; #endif return 1; } void setUp(void) { /* No global state set up is required. */ } void tearDown(void) { /* No teardown required. */ } static void init_tspec_pattern(struct tspec *t, unsigned char pat) { memset(t, pat, sizeof(*t)); } static void assert_tspec_unchanged(struct tspec const *orig, struct tspec const *now) { TEST_ASSERT_EQUAL_UINT8_ARRAY(orig, now, sizeof(*orig)); } void test_decode_one_format_invalid_character(void) { const char *s = "q"; /* invalid leading specifier */ const char *next = (const char*)0xDEADBEEF; /* sentinel */ struct tspec t, before; init_tspec_pattern(&t, 0xA5); before = t; bool ok = decode_one_format(s, s, &next, &t); TEST_ASSERT_FALSE(ok); TEST_ASSERT_EQUAL_PTR((const char*)0xDEADBEEF, next); assert_tspec_unchanged(&before, &t); } void test_decode_one_format_signed_decimal_default_int(void) { const char *s = "d"; const char *next = NULL; struct tspec t; memset(&t, 0, sizeof t); bool ok = decode_one_format(s, s, &next, &t); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(s + 1, next); TEST_ASSERT_EQUAL_INT(SIGNED_DECIMAL, t.fmt); int sz = (int)sizeof(unsigned int); TEST_ASSERT_TRUE(sz < (int)countof(integral_type_size)); enum size_spec ss = integral_type_size[sz]; TEST_ASSERT_EQUAL_INT(ss, t.size); /* Expected print function per selection logic. */ TEST_ASSERT_EQUAL_PTR(expected_print_fn_integral(ss, SIGNED_DECIMAL), t.print_function); int expected_width = INT_BITS_STRLEN_BOUND(CHAR_BIT * sz - 1) + 1; TEST_ASSERT_EQUAL_INT(expected_width, t.field_width); char expected_fmt[FMT_BYTES_ALLOCATED]; sprintf(expected_fmt, "%%*%s", ispec_to_format(ss, "d", "ld", "lld", "jd")); TEST_ASSERT_EQUAL_STRING(expected_fmt, t.fmt_string); TEST_ASSERT_FALSE(t.hexl_mode_trailer); } void test_decode_one_format_octal_size_2(void) { const char *s = "o2"; const char *next = NULL; struct tspec t; memset(&t, 0, sizeof t); bool ok = decode_one_format(s, s, &next, &t); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(s + 2, next); TEST_ASSERT_EQUAL_INT(OCTAL, t.fmt); int sz = 2; TEST_ASSERT_TRUE(sz < (int)countof(integral_type_size)); enum size_spec ss = integral_type_size[sz]; TEST_ASSERT_NOT_EQUAL(NO_SIZE, ss); TEST_ASSERT_EQUAL_INT(ss, t.size); TEST_ASSERT_EQUAL_PTR(expected_print_fn_integral(ss, OCTAL), t.print_function); int expected_width = (CHAR_BIT * sz + 2) / 3; TEST_ASSERT_EQUAL_INT(expected_width, t.field_width); char expected_fmt[FMT_BYTES_ALLOCATED]; sprintf(expected_fmt, "%%*.%d%s", expected_width, ispec_to_format(ss, "o", "lo", "llo", "jo")); TEST_ASSERT_EQUAL_STRING(expected_fmt, t.fmt_string); TEST_ASSERT_FALSE(t.hexl_mode_trailer); } void test_decode_one_format_hex_with_trailer_z(void) { const char *s = "x1zREST"; const char *next = NULL; struct tspec t; memset(&t, 0, sizeof t); bool ok = decode_one_format(s, s, &next, &t); TEST_ASSERT_TRUE(ok); /* Parsed: 'x' + size 1 + 'z' => next should point at 'R' */ TEST_ASSERT_EQUAL_PTR(s + 3, next); TEST_ASSERT_EQUAL_INT(HEXADECIMAL, t.fmt); TEST_ASSERT_TRUE(t.hexl_mode_trailer); } void test_decode_one_format_named_character_a(void) { const char *s = "a"; const char *next = NULL; struct tspec t; memset(&t, 0, sizeof t); bool ok = decode_one_format(s, s, &next, &t); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(s + 1, next); TEST_ASSERT_EQUAL_INT(NAMED_CHARACTER, t.fmt); TEST_ASSERT_EQUAL_INT(CHAR, t.size); TEST_ASSERT_EQUAL_PTR(print_named_ascii, t.print_function); TEST_ASSERT_EQUAL_INT(3, t.field_width); TEST_ASSERT_FALSE(t.hexl_mode_trailer); } void test_decode_one_format_ascii_c(void) { const char *s = "c"; const char *next = NULL; struct tspec t; memset(&t, 0, sizeof t); bool ok = decode_one_format(s, s, &next, &t); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(s + 1, next); TEST_ASSERT_EQUAL_INT(CHARACTER, t.fmt); TEST_ASSERT_EQUAL_INT(CHAR, t.size); TEST_ASSERT_EQUAL_PTR(print_ascii, t.print_function); TEST_ASSERT_EQUAL_INT(3, t.field_width); TEST_ASSERT_FALSE(t.hexl_mode_trailer); } void test_decode_one_format_float_default_double(void) { const char *s = "f"; const char *next = NULL; struct tspec t; memset(&t, 0, sizeof t); bool ok = decode_one_format(s, s, &next, &t); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(s + 1, next); TEST_ASSERT_EQUAL_INT(FLOATING_POINT, t.fmt); size_t sz = sizeof(double); TEST_ASSERT_TRUE(fp_size_supported(sz)); enum size_spec ss = fp_type_size[sz]; TEST_ASSERT_EQUAL_INT(ss, t.size); TEST_ASSERT_EQUAL_PTR(print_double, t.print_function); struct lconv const *lc = localeconv(); size_t dp_len = (lc->decimal_point[0] ? strlen(lc->decimal_point) : 1); int expected_width = DBL_STRLEN_BOUND_L(dp_len); TEST_ASSERT_EQUAL_INT(expected_width, t.field_width); } void test_decode_one_format_float_F_handling(void) { const char *s = "fF"; const char *next = NULL; struct tspec t, before; init_tspec_pattern(&t, 0xCC); before = t; size_t sz = sizeof(float); int supported = fp_size_supported(sz); bool ok = decode_one_format(s, s, &next, &t); if (supported) { TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(s + 2, next); TEST_ASSERT_EQUAL_INT(FLOATING_POINT, t.fmt); TEST_ASSERT_EQUAL_INT(fp_type_size[sz], t.size); TEST_ASSERT_EQUAL_PTR(print_float, t.print_function); struct lconv const *lc = localeconv(); size_t dp_len = (lc->decimal_point[0] ? strlen(lc->decimal_point) : 1); int expected_width = FLT_STRLEN_BOUND_L(dp_len); TEST_ASSERT_EQUAL_INT(expected_width, t.field_width); } else { TEST_ASSERT_FALSE(ok); TEST_ASSERT_EQUAL_PTR(NULL, next); /* decode_one_format does not set on failure */ assert_tspec_unchanged(&before, &t); } } void test_decode_one_format_float_L_handling(void) { const char *s = "fL"; const char *next = NULL; struct tspec t, before; init_tspec_pattern(&t, 0xDD); before = t; size_t sz = sizeof(long double); int supported = fp_size_supported(sz); bool ok = decode_one_format(s, s, &next, &t); if (supported) { TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(s + 2, next); TEST_ASSERT_EQUAL_INT(FLOATING_POINT, t.fmt); TEST_ASSERT_EQUAL_INT(fp_type_size[sz], t.size); TEST_ASSERT_EQUAL_PTR(print_long_double, t.print_function); struct lconv const *lc = localeconv(); size_t dp_len = (lc->decimal_point[0] ? strlen(lc->decimal_point) : 1); int expected_width = LDBL_STRLEN_BOUND_L(dp_len); TEST_ASSERT_EQUAL_INT(expected_width, t.field_width); } else { TEST_ASSERT_FALSE(ok); TEST_ASSERT_EQUAL_PTR(NULL, next); assert_tspec_unchanged(&before, &t); } } void test_decode_one_format_invalid_integral_size_3(void) { const char *s = "d3"; /* no standard 3-byte integral type */ const char *next = (const char*)0xABCD; struct tspec t, before; init_tspec_pattern(&t, 0xEE); before = t; bool ok = decode_one_format(s, s, &next, &t); TEST_ASSERT_FALSE(ok); TEST_ASSERT_EQUAL_PTR((const char*)0xABCD, next); assert_tspec_unchanged(&before, &t); } void test_decode_one_format_integer_size_overflow_digits(void) { /* A number too large to fit into int should be rejected by simple_strtoi. */ const char *s = "d999999999999999999999"; /* many 9s -> overflow */ const char *next = (const char*)0xFEED; struct tspec t, before; init_tspec_pattern(&t, 0x77); before = t; bool ok = decode_one_format(s, s, &next, &t); TEST_ASSERT_FALSE(ok); TEST_ASSERT_EQUAL_PTR((const char*)0xFEED, next); assert_tspec_unchanged(&before, &t); } void test_decode_one_format_integral_letter_sizes(void) { /* Verify C,S,I,L mapping and outputs for several formats. */ struct { const char *spec; enum output_format fmt; char typech; size_t bytes; } cases[] = { { "uC", UNSIGNED_DECIMAL, 'C', sizeof(unsigned char) }, { "xS", HEXADECIMAL, 'S', sizeof(unsigned short) }, { "dI", SIGNED_DECIMAL, 'I', sizeof(unsigned int) }, { "oL", OCTAL, 'L', sizeof(unsigned long) }, }; for (size_t i = 0; i < sizeof(cases)/sizeof(cases[0]); ++i) { const char *s = cases[i].spec; const char *next = NULL; struct tspec t; memset(&t, 0, sizeof t); bool ok = decode_one_format(s, s, &next, &t); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(s + 2, next); TEST_ASSERT_EQUAL_INT(cases[i].fmt, t.fmt); size_t sz = cases[i].bytes; TEST_ASSERT_TRUE(sz < countof(integral_type_size)); enum size_spec ss = integral_type_size[sz]; TEST_ASSERT_NOT_EQUAL(NO_SIZE, ss); TEST_ASSERT_EQUAL_INT(ss, t.size); TEST_ASSERT_EQUAL_PTR(expected_print_fn_integral(ss, cases[i].fmt), t.print_function); int expected_width = 0; char expected_fmt[FMT_BYTES_ALLOCATED]; if (cases[i].fmt == SIGNED_DECIMAL) { expected_width = INT_BITS_STRLEN_BOUND(CHAR_BIT * sz - 1) + 1; sprintf(expected_fmt, "%%*%s", ispec_to_format(ss, "d", "ld", "lld", "jd")); } else if (cases[i].fmt == UNSIGNED_DECIMAL) { expected_width = INT_BITS_STRLEN_BOUND(CHAR_BIT * sz); sprintf(expected_fmt, "%%*%s", ispec_to_format(ss, "u", "lu", "llu", "ju")); } else if (cases[i].fmt == OCTAL) { expected_width = (CHAR_BIT * sz + 2) / 3; sprintf(expected_fmt, "%%*.%d%s", expected_width, ispec_to_format(ss, "o", "lo", "llo", "jo")); } else if (cases[i].fmt == HEXADECIMAL) { expected_width = (CHAR_BIT * sz + 3) / 4; sprintf(expected_fmt, "%%*.%d%s", expected_width, ispec_to_format(ss, "x", "lx", "llx", "jx")); } TEST_ASSERT_EQUAL_INT(expected_width, t.field_width); TEST_ASSERT_EQUAL_STRING(expected_fmt, t.fmt_string); TEST_ASSERT_FALSE(t.hexl_mode_trailer); } } int main(void) { UNITY_BEGIN(); RUN_TEST(test_decode_one_format_invalid_character); RUN_TEST(test_decode_one_format_signed_decimal_default_int); RUN_TEST(test_decode_one_format_octal_size_2); RUN_TEST(test_decode_one_format_hex_with_trailer_z); RUN_TEST(test_decode_one_format_named_character_a); RUN_TEST(test_decode_one_format_ascii_c); RUN_TEST(test_decode_one_format_float_default_double); RUN_TEST(test_decode_one_format_float_F_handling); RUN_TEST(test_decode_one_format_float_L_handling); RUN_TEST(test_decode_one_format_invalid_integral_size_3); RUN_TEST(test_decode_one_format_integer_size_overflow_digits); RUN_TEST(test_decode_one_format_integral_letter_sizes); return UNITY_END(); }