| /* Copyright 2019 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| |
| #include "common.h" |
| #include "printf.h" |
| #include "test_util.h" |
| #include "util.h" |
| |
| #define INIT_VALUE 0x5E |
| #define NO_BYTES_TOUCHED NULL |
| |
| static const char err_str[] = "ERROR"; |
| static char output[1024]; |
| |
| int run(int expect_ret, const char *expect, |
| bool output_null, size_t size_limit, |
| const char *format, va_list args) |
| { |
| size_t expect_size = expect ? strlen(expect) + 1 : 0; |
| int rv; |
| |
| ccprintf("\n"); |
| ccprintf("size_limit=%-4zd | format='%s'\n", size_limit, format); |
| ccprintf("expect ='%s' | expect_status=%d\n", |
| expect ? expect : "NO_BYTES_TOUCHED", expect_ret); |
| |
| TEST_ASSERT(expect_size <= sizeof(output)); |
| TEST_ASSERT(expect_size <= size_limit); |
| memset(output, INIT_VALUE, sizeof(output)); |
| |
| rv = vsnprintf(output_null ? NULL : output, size_limit, |
| format, args); |
| ccprintf("received='%.*s' | ret =%d\n", |
| 30, output, rv); |
| |
| TEST_ASSERT_ARRAY_EQ(output, expect, expect_size); |
| TEST_ASSERT_MEMSET(&output[expect_size], INIT_VALUE, |
| sizeof(output) - expect_size); |
| |
| if (rv >= 0) { |
| TEST_ASSERT(rv == expect_size - 1); |
| TEST_ASSERT(EC_SUCCESS == expect_ret); |
| } else { |
| TEST_ASSERT(rv == -expect_ret); |
| } |
| |
| return EC_SUCCESS; |
| } |
| |
| int expect_success(const char *expect, const char *format, ...) |
| { |
| va_list args; |
| int rv; |
| |
| va_start(args, format); |
| rv = run(EC_SUCCESS, expect, |
| false, sizeof(output), |
| format, args); |
| va_end(args); |
| |
| return rv; |
| } |
| |
| int expect(int expect_ret, const char *expect, |
| bool output_null, size_t size_limit, |
| const char *format, ...) |
| { |
| va_list args; |
| int rv; |
| |
| va_start(args, format); |
| rv = run(expect_ret, expect, |
| output_null, size_limit, |
| format, args); |
| va_end(args); |
| |
| return rv; |
| } |
| |
| #define T(n) \ |
| do { \ |
| int rv = (n); \ |
| if (rv != EC_SUCCESS) \ |
| return rv; \ |
| } while (0) |
| |
| test_static int test_vsnprintf_args(void) |
| { |
| T(expect_success("", "")); |
| T(expect_success("a", "a")); |
| |
| T(expect(/* expect an invalid args error */ |
| EC_ERROR_INVAL, NO_BYTES_TOUCHED, |
| /* given -1 as output size limit */ |
| false, -1, "")); |
| T(expect(/* expect an invalid args error */ |
| EC_ERROR_INVAL, NO_BYTES_TOUCHED, |
| /* given 0 as output size limit */ |
| false, 0, "")); |
| T(expect(/* expect SUCCESS */ |
| EC_SUCCESS, "", |
| /* given 1 as output size limit and a blank format */ |
| false, 1, "")); |
| T(expect(/* expect an overflow error */ |
| EC_ERROR_OVERFLOW, "", |
| /* given 1 as output size limit with a non-blank format */ |
| false, 1, "a")); |
| |
| T(expect(/* expect an invalid args error */ |
| EC_ERROR_INVAL, NO_BYTES_TOUCHED, |
| /* given NULL as the output buffer */ |
| true, sizeof(output), "")); |
| T(expect(/* expect an invalid args error */ |
| EC_ERROR_INVAL, NO_BYTES_TOUCHED, |
| /* given a NULL format string */ |
| false, sizeof(output), NULL)); |
| |
| return EC_SUCCESS; |
| } |
| |
| test_static int test_vsnprintf_int(void) |
| { |
| T(expect_success("123", "%d", 123)); |
| T(expect_success("-123", "%d", -123)); |
| T(expect_success("+123", "%+d", 123)); |
| T(expect_success("-123", "%+d", -123)); |
| T(expect_success("123", "%-d", 123)); |
| T(expect_success("-123", "%-d", -123)); |
| |
| T(expect_success(" 123", "%5d", 123)); |
| T(expect_success(" +123", "%+5d", 123)); |
| T(expect_success("00123", "%05d", 123)); |
| T(expect_success("00123", "%005d", 123)); |
| /* |
| * TODO(crbug.com/974084): This odd behavior should be fixed. |
| * T(expect_success("+0123", "%+05d", 123)); |
| * Actual: "0+123" |
| * T(expect_success("+0123", "%+005d", 123)); |
| * Actual: "0+123" |
| */ |
| |
| T(expect_success(" 123", "%*d", 5, 123)); |
| T(expect_success(" +123", "%+*d", 5, 123)); |
| T(expect_success("00123", "%0*d", 5, 123)); |
| /* |
| * TODO(crbug.com/974084): This odd behavior should be fixed. |
| * T(expect_success("00123", "%00*d", 5, 123)); |
| * Actual: "ERROR" |
| */ |
| T(expect_success("0+123", "%+0*d", 5, 123)); |
| /* |
| * TODO(crbug.com/974084): This odd behavior should be fixed. |
| * T(expect_success("0+123", "%+00*d", 5, 123)); |
| * Actual: "ERROR" |
| */ |
| |
| T(expect_success("123 ", "%-5d", 123)); |
| T(expect_success("+123 ", "%-+5d", 123)); |
| T(expect_success(err_str, "%+-5d", 123)); |
| T(expect_success("123 ", "%-05d", 123)); |
| T(expect_success("123 ", "%-005d", 123)); |
| T(expect_success("+123 ", "%-+05d", 123)); |
| T(expect_success("+123 ", "%-+005d", 123)); |
| |
| T(expect_success("0.00123", "%.5d", 123)); |
| T(expect_success("+0.00123", "%+.5d", 123)); |
| T(expect_success("0.00123", "%7.5d", 123)); |
| T(expect_success(" 0.00123", "%9.5d", 123)); |
| T(expect_success(" +0.00123", "%+9.5d", 123)); |
| |
| T(expect_success("123", "%u", 123)); |
| T(expect_success("4294967295", "%u", -1)); |
| T(expect_success("18446744073709551615", "%llu", (uint64_t)-1)); |
| |
| T(expect_success("0", "%x", 0)); |
| T(expect_success("0", "%X", 0)); |
| T(expect_success("5e", "%x", 0X5E)); |
| T(expect_success("5E", "%X", 0X5E)); |
| |
| /* |
| * %l is deprecated on 32-bit systems (see crbug.com/984041), but is |
| * is still functional on 64-bit systems. |
| */ |
| if (sizeof(long) == sizeof(uint32_t)) { |
| T(expect_success(err_str, "%lx", 0x7b)); |
| T(expect_success(err_str, "%08lu", 0x7b)); |
| T(expect_success("13ERROR", "%d%lu", 13, 14)); |
| } else { |
| T(expect_success("7b", "%lx", 0x7b)); |
| T(expect_success("00000123", "%08lu", 123)); |
| T(expect_success("131415", "%d%lu%d", 13, 14L, 15)); |
| } |
| |
| return EC_SUCCESS; |
| } |
| |
| test_static int test_vsnprintf_pointers(void) |
| { |
| void *ptr = (void *)0x55005E00; |
| unsigned int val = 0; |
| |
| T(expect_success("55005e00", "%pP", ptr)); |
| T(expect_success(err_str, "%P", ptr)); |
| /* %p by itself is invalid */ |
| T(expect(EC_ERROR_INVAL, NO_BYTES_TOUCHED, |
| false, -1, "%p")); |
| /* %p with an unknown suffix is invalid */ |
| T(expect(EC_ERROR_INVAL, NO_BYTES_TOUCHED, |
| false, -1, "%p ")); |
| /* %p with an unknown suffix is invalid */ |
| T(expect(EC_ERROR_INVAL, NO_BYTES_TOUCHED, |
| false, -1, "%pQ")); |
| |
| /* Test %pb, binary format */ |
| T(expect_success("0", "%pb", BINARY_VALUE(val, 0))); |
| val = 0x5E; |
| T(expect_success("1011110", "%pb", BINARY_VALUE(val, 0))); |
| T(expect_success("0000000001011110", "%pb", BINARY_VALUE(val, 16))); |
| val = 0x12345678; |
| T(expect_success("10010001101000101011001111000", "%pb", |
| BINARY_VALUE(val, 0))); |
| val = 0xFEDCBA90; |
| /* Test a number that makes the longest string possible */ |
| T(expect_success("11111110110111001011101010010000", "%pb", |
| BINARY_VALUE(val, 0))); |
| return EC_SUCCESS; |
| } |
| |
| test_static int test_vsnprintf_chars(void) |
| { |
| T(expect_success("a", "%c", 'a')); |
| T(expect_success("*", "%c", '*')); |
| return EC_SUCCESS; |
| } |
| |
| test_static int test_vsnprintf_strings(void) |
| { |
| T(expect_success("abc", "%s", "abc")); |
| T(expect_success(" abc", "%5s", "abc")); |
| T(expect_success("abc", "%0s", "abc")); |
| T(expect_success("abc ", "%-5s", "abc")); |
| T(expect_success("abc", "%*s", 0, "abc")); |
| T(expect_success("a", "%.1s", "abc")); |
| T(expect_success("a", "%.*s", 1, "abc")); |
| T(expect_success("", "%.0s", "abc")); |
| T(expect_success("", "%.*s", 0, "abc")); |
| /* |
| * TODO(crbug.com/974084): |
| * Ignoring the padding parameter is slightly |
| * odd behavior and could use a review. |
| */ |
| T(expect_success("ab", "%5.2s", "abc")); |
| T(expect_success("abc", "%.4s", "abc")); |
| |
| /* |
| * Given a malformed string (address 0x1 is a good example), |
| * if we ask for zero precision, expect no bytes to be read |
| * from the malformed address and a blank output string. |
| */ |
| T(expect_success("", "%.0s", (char *)1)); |
| |
| return EC_SUCCESS; |
| } |
| |
| test_static int test_vsnprintf_timestamps(void) |
| { |
| uint64_t ts = 0; |
| |
| T(expect_success("0.000000", "%pT", &ts)); |
| ts = 123456; |
| T(expect_success("0.123456", "%pT", &ts)); |
| ts = 9999999000000; |
| T(expect_success("9999999.000000", "%pT", &ts)); |
| return EC_SUCCESS; |
| } |
| |
| test_static int test_vsnprintf_hexdump(void) |
| { |
| const char bytes[] = {0x00, 0x5E}; |
| |
| T(expect_success("005e", "%ph", HEX_BUF(bytes, 2))); |
| T(expect_success("", "%ph", HEX_BUF(bytes, 0))); |
| T(expect_success("00", "%ph", HEX_BUF(bytes, 1))); |
| return EC_SUCCESS; |
| } |
| |
| test_static int test_vsnprintf_combined(void) |
| { |
| T(expect_success("abc", "%c%s", 'a', "bc")); |
| T(expect_success("12\tbc", "%d\t%s", 12, "bc")); |
| return EC_SUCCESS; |
| } |
| |
| void run_test(int argc, char **argv) |
| { |
| test_reset(); |
| |
| RUN_TEST(test_vsnprintf_args); |
| RUN_TEST(test_vsnprintf_int); |
| RUN_TEST(test_vsnprintf_pointers); |
| RUN_TEST(test_vsnprintf_chars); |
| RUN_TEST(test_vsnprintf_strings); |
| RUN_TEST(test_vsnprintf_timestamps); |
| RUN_TEST(test_vsnprintf_hexdump); |
| RUN_TEST(test_vsnprintf_combined); |
| |
| test_print_result(); |
| } |