zephyr/tests/unit/cbprintf/main.c
Peter Bigot d12a99588b lib: cbprintf: remove cbprintf_arglen
This function was designed to support the logging infrastructure's
need to copy values from va_list structures.  It did not meet that
need, since some values need to be changed based on additional data
that is only available when the complete format specification is
examined.  Remove the function as unnecessary.

Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
2021-01-07 14:02:06 +01:00

1160 lines
26 KiB
C

/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <wctype.h>
#include <stddef.h>
#include <string.h>
#include <sys/cbprintf.h>
#include <sys/util.h>
/* Unit testing doesn't use Kconfig, so if we're not building from
* twister force selection of all features. If we are use flags to
* determine which features are desired. Yes, this is a mess.
*/
#ifndef VIA_TWISTER
/* Set this to truthy to use libc's snprintf as external validation.
* This should be used with all options enabled.
*/
#define USE_LIBC 0
#define CONFIG_CBPRINTF_COMPLETE 1
#define CONFIG_CBPRINTF_FULL_INTEGRAL 1
#define CONFIG_CBPRINTF_FP_SUPPORT 1
#define CONFIG_CBPRINTF_FP_A_SUPPORT 1
#define CONFIG_CBPRINTF_N_SPECIFIER 1
#define CONFIG_CBPRINTF_LIBC_SUBSTS 1
/* compensate for selects */
#if (CONFIG_CBPRINTF_FP_A_SUPPORT - 0)
#undef CONFIG_CBPRINTF_REDUCED_INTEGRAL
#undef CONFIG_CBPRINTF_FULL_INTEGRAL
#undef CONFIG_CBPRINTF_FP_SUPPORT
#define CONFIG_CBPRINTF_FULL_INTEGRAL 1
#define CONFIG_CBPRINTF_FP_SUPPORT 1
#endif
#if !(CONFIG_CBPRINTF_FP_SUPPORT - 0)
#undef CONFIG_CBPRINTF_FP_SUPPORT
#endif
/* Convert choice selections to one defined flag */
#if !(CONFIG_CBPRINTF_COMPLETE - 0)
#ifdef CONFIG_CBPRINTF_FP_SUPPORT
#error Inconsistent configuration
#endif
#undef CONFIG_CBPRINTF_COMPLETE
#define CONFIG_CBPRINTF_NANO 1
#endif
#if !(CONFIG_CBPRINTF_FULL_INTEGRAL - 0)
#undef CONFIG_CBPRINTF_FULL_INTEGRAL
#define CONFIG_CBPRINTF_REDUCED_INTEGRAL 1
#endif
#else /* VIA_TWISTER */
#if (VIA_TWISTER & 0x01) != 0
#define CONFIG_CBPRINTF_FULL_INTEGRAL 1
#else
#define CONFIG_CBPRINTF_REDUCED_INTEGRAL 1
#endif
#if (VIA_TWISTER & 0x02) != 0
#define CONFIG_CBPRINTF_FP_SUPPORT 1
#endif
#if (VIA_TWISTER & 0x04) != 0
#define CONFIG_CBPRINTF_FP_A_SUPPORT 1
#endif
#if (VIA_TWISTER & 0x08) != 0
#define CONFIG_CBPRINTF_N_SPECIFIER 1
#endif
#if (VIA_TWISTER & 0x40) != 0
#define CONFIG_CBPRINTF_FP_ALWAYS_A 1
#endif
#if (VIA_TWISTER & 0x80) != 0
#define CONFIG_CBPRINTF_NANO 1
#else /* 0x80 */
#define CONFIG_CBPRINTF_COMPLETE 1
#endif /* 0x80 */
#if (VIA_TWISTER & 0x100) != 0
#define CONFIG_CBPRINTF_LIBC_SUBSTS 1
#endif
#endif /* VIA_TWISTER */
/* Can't use IS_ENABLED on symbols that don't start with CONFIG_
* without checkpatch complaints, so do something else.
*/
#if USE_LIBC
#define ENABLED_USE_LIBC true
#else
#define ENABLED_USE_LIBC false
#endif
#include "../../../lib/os/cbprintf.c"
#if defined(CONFIG_CBPRINTF_COMPLETE)
#include "../../../lib/os/cbprintf_complete.c"
#elif defined(CONFIG_CBPRINTF_NANO)
#include "../../../lib/os/cbprintf_nano.c"
#endif
/* We can't determine at build-time whether int is 64-bit, so assume
* it is. If not the values are truncated at build time, and the str
* pointers will be updated during test initialization.
*/
static const unsigned int pfx_val = (unsigned int)0x7b6b5b4b3b2b1b0b;
static const char pfx_str64[] = "7b6b5b4b3b2b1b0b";
static const char *pfx_str = pfx_str64;
static const unsigned int sfx_val = (unsigned int)0xe7e6e5e4e3e2e1e0;
static const char sfx_str64[] = "e7e6e5e4e3e2e1e0";
static const char *sfx_str = sfx_str64;
#define WRAP_FMT(_fmt) "%x" _fmt "%x"
#define PASS_ARG(...) pfx_val, __VA_ARGS__, sfx_val
static inline int match_str(const char **strp,
const char *expected,
size_t len)
{
const char *str = *strp;
int rv = strncmp(str, expected, len);
*strp += len;
return rv;
}
static inline int match_pfx(const char **ptr)
{
return match_str(ptr, pfx_str, 2U * sizeof(pfx_val));
}
static inline int match_sfx(const char **ptr)
{
return match_str(ptr, sfx_str, 2U * sizeof(sfx_val));
}
/* This has to be more than 255 so we can test over-sized widths. */
static char buf[512];
static char *bp;
static inline void reset_out(void)
{
bp = buf;
*bp = 0;
}
static int out(int c, void *dest)
{
int rv = EOF;
ARG_UNUSED(dest);
if (bp < (buf + ARRAY_SIZE(buf))) {
*bp++ = (char)(unsigned char)c;
rv = (int)(unsigned char)c;
}
return rv;
}
__printf_like(1, 2)
static int prf(const char *format, ...)
{
va_list ap;
int rv;
reset_out();
va_start(ap, format);
#if USE_LIBC
rv = vsnprintf(buf, sizeof(buf), format, ap);
#else
reset_out();
rv = cbvprintf(out, NULL, format, ap);
if (bp == (buf + ARRAY_SIZE(buf))) {
--bp;
}
*bp = 0;
#endif
va_end(ap);
return rv;
}
static int rawprf(const char *format, ...)
{
va_list ap;
int rv;
va_start(ap, format);
rv = cbvprintf(out, NULL, format, ap);
va_end(ap);
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)
&& !IS_ENABLED(CONFIG_CBPRINTF_LIBC_SUBSTS)) {
zassert_equal(rv, 0, NULL);
rv = bp - buf;
}
return rv;
}
#define TEST_PRF(_fmt, ...) prf(WRAP_FMT(_fmt), PASS_ARG(__VA_ARGS__))
struct context {
const char *expected;
const char *got;
const char *file;
unsigned int line;
};
__printf_like(3, 4)
static bool prf_failed(const struct context *ctx,
const char *cp,
const char *format,
...)
{
va_list ap;
va_start(ap, format);
printf("%s:%u for '%s'\n", ctx->file, ctx->line, ctx->expected);
printf("in: %s\nat: %*c%s\n", ctx->got,
(unsigned int)(cp - ctx->got), '>', ctx->expected);
vprintf(format, ap);
va_end(ap);
return false;
}
static inline bool prf_check(const char *expected,
int rv,
const char *file,
unsigned int line)
{
const struct context ctx = {
.expected = expected,
.got = buf,
.file = file,
.line = line,
};
const char *str = buf;
const char *sp;
int rc;
sp = str;
rc = match_pfx(&str);
if (rc != 0) {
return prf_failed(&ctx, sp, "pfx mismatch %d\n", rc);
}
sp = str;
rc = match_str(&str, expected, strlen(expected));
if (rc != 0) {
return prf_failed(&ctx, sp, "str mismatch %d\n", rc);
}
sp = str;
rc = match_sfx(&str);
if (rc != 0) {
return prf_failed(&ctx, sp, "sfx mismatch, %d\n", rc);
}
rc = (*str != 0);
if (rc != 0) {
return prf_failed(&ctx, str, "no eos %02x\n", *str);
}
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)
&& !IS_ENABLED(CONFIG_CBPRINTF_LIBC_SUBSTS)) {
if (rv != 0) {
return prf_failed(&ctx, str, "nano rv %d != 0\n", rc);
}
} else {
int len = (int)(str - buf);
if (rv != len) {
return prf_failed(&ctx, str, "rv %d != expected %d\n",
rv, len);
}
}
return true;
}
#define PRF_CHECK(expected, rv) \
zassert_true(prf_check(expected, rv, __FILE__, __LINE__), \
NULL)
static void test_pct(void)
{
int rc = TEST_PRF("/%%/%c/", 'a');
PRF_CHECK("/%/a/", rc);
}
static void test_c(void)
{
int rc;
rc = TEST_PRF("%c", 'a');
PRF_CHECK("a", rc);
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("short test for nano\n");
return;
}
rc = TEST_PRF("%lc", (wint_t)'a');
if (ENABLED_USE_LIBC) {
PRF_CHECK("a", rc);
} else {
PRF_CHECK("%lc", rc);
}
rc = prf("/%256c/", 'a');
const char *bp = buf;
size_t spaces = 255;
zassert_equal(rc, 258, "len %d", rc);
zassert_equal(*bp, '/', NULL);
++bp;
while (spaces-- > 0) {
zassert_equal(*bp, ' ', NULL);
++bp;
}
zassert_equal(*bp, 'a', NULL);
++bp;
zassert_equal(*bp, '/', NULL);
}
static void test_s(void)
{
const char *s = "123";
static wchar_t ws[] = L"abc";
int rc;
rc = TEST_PRF("/%s/", s);
PRF_CHECK("/123/", rc);
rc = TEST_PRF("/%6s/%-6s/%2s/", s, s, s);
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
PRF_CHECK("/123/123 /123/", rc);
} else {
PRF_CHECK("/ 123/123 /123/", rc);
}
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("short test for nano\n");
return;
}
rc = TEST_PRF("/%.6s/%.2s/%.s/", s, s, s);
PRF_CHECK("/123/12//", rc);
rc = TEST_PRF("%ls", ws);
if (ENABLED_USE_LIBC) {
PRF_CHECK("abc", rc);
} else {
PRF_CHECK("%ls", rc);
}
}
static void test_v_c(void)
{
int rc;
reset_out();
buf[1] = 'b';
rc = rawprf("%c", 'a');
zassert_equal(rc, 1, NULL);
zassert_equal(buf[0], 'a', NULL);
if (!ENABLED_USE_LIBC) {
zassert_equal(buf[1], 'b', "wth %x", buf[1]);
}
}
static void test_d_length(void)
{
int min = -1234567890;
int max = 1876543210;
long long svll = 123LL << 48;
int rc;
rc = TEST_PRF("%d/%d", min, max);
PRF_CHECK("-1234567890/1876543210", rc);
if (!IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
rc = TEST_PRF("%hd/%hd", min, max);
PRF_CHECK("-722/-14614", rc);
rc = TEST_PRF("%hhd/%hhd", min, max);
PRF_CHECK("46/-22", rc);
}
rc = TEST_PRF("%ld/%ld", (long)min, (long)max);
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)
|| (sizeof(long) <= 4)
|| IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
PRF_CHECK("-1234567890/1876543210", rc);
} else {
PRF_CHECK("%ld/%ld", rc);
}
rc = TEST_PRF("/%lld/%lld/", svll, -svll);
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)) {
PRF_CHECK("/34621422135410688/-34621422135410688/", rc);
} else if (IS_ENABLED(CONFIG_CBPRINTF_COMPLETE)) {
PRF_CHECK("/%lld/%lld/", rc);
} else if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
PRF_CHECK("/ERR/ERR/", rc);
} else {
zassert_true(false, "Missed case!");
}
rc = TEST_PRF("%lld/%lld", (long long)min, (long long)max);
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)) {
PRF_CHECK("-1234567890/1876543210", rc);
} else if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
PRF_CHECK("-1234567890/1876543210", rc);
} else {
PRF_CHECK("%lld/%lld", rc);
}
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("short test for nano\n");
return;
}
rc = TEST_PRF("%jd/%jd", (intmax_t)min, (intmax_t)max);
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)) {
PRF_CHECK("-1234567890/1876543210", rc);
} else {
PRF_CHECK("%jd/%jd", rc);
}
rc = TEST_PRF("%zd/%td/%td", (size_t)min, (ptrdiff_t)min,
(ptrdiff_t)max);
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)
|| (sizeof(size_t) <= 4)) {
PRF_CHECK("-1234567890/-1234567890/1876543210", rc);
} else {
PRF_CHECK("%zd/%td/%td", rc);
}
/* These have to be tested without the format validation
* attribute because they produce diagnostics, but we have
* intended behavior so we have to test them.
*/
reset_out();
rc = rawprf("/%Ld/", max);
zassert_equal(rc, 5, "len %d", rc);
zassert_equal(strncmp("/%Ld/", buf, rc), 0, NULL);
}
static void test_d_flags(void)
{
int sv = 123;
int rc;
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("skipped test for nano\n");
return;
}
/* Stuff related to sign */
rc = TEST_PRF("/%d/%-d/%+d/% d/",
sv, sv, sv, sv);
PRF_CHECK("/123/123/+123/ 123/", rc);
/* Stuff related to width padding */
rc = TEST_PRF("/%1d/%4d/%-4d/%04d/%15d/%-15d/",
sv, sv, sv, sv, sv, sv);
PRF_CHECK("/123/ 123/123 /0123/"
" 123/123 /", rc);
/* Stuff related to precision */
rc = TEST_PRF("/%.6d/%6.4d/", sv, sv);
PRF_CHECK("/000123/ 0123/", rc);
/* Now with negative values */
sv = -sv;
rc = TEST_PRF("/%d/%-d/%+d/% d/",
sv, sv, sv, sv);
PRF_CHECK("/-123/-123/-123/-123/", rc);
rc = TEST_PRF("/%1d/%6d/%-6d/%06d/%13d/%-13d/",
sv, sv, sv, sv, sv, sv);
PRF_CHECK("/-123/ -123/-123 /-00123/"
" -123/-123 /", rc);
rc = TEST_PRF("/%.6d/%6.4d/", sv, sv);
PRF_CHECK("/-000123/ -0123/", rc);
/* These have to be tested without the format validation
* attribute because they produce diagnostics, but the
* standard specifies behavior so we have to test them.
*/
sv = 123;
reset_out();
rc = rawprf("/%#d/% +d/%-04d/%06.4d/", sv, sv, sv, sv);
zassert_equal(rc, 22, "rc %d", rc);
zassert_equal(strncmp("/123/+123/123 / 0123/",
buf, rc), 0, NULL);
}
static void test_x_length(void)
{
unsigned int min = 0x4c3c2c1c;
unsigned int max = 0x4d3d2d1d;
int rc;
rc = TEST_PRF("%x/%X", min, max);
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
PRF_CHECK("4c3c2c1c/4d3d2d1d", rc);
} else {
PRF_CHECK("4c3c2c1c/4D3D2D1D", rc);
}
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("short test for nano\n");
return;
}
rc = TEST_PRF("%hx/%hX", min, max);
PRF_CHECK("2c1c/2D1D", rc);
rc = TEST_PRF("%hhx/%hhX", min, max);
PRF_CHECK("1c/1D", rc);
rc = TEST_PRF("%lx/%lX", (unsigned long)min, (unsigned long)max);
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)
|| (sizeof(long) <= 4)) {
PRF_CHECK("4c3c2c1c/4D3D2D1D", rc);
} else {
PRF_CHECK("%lx/%lX", rc);
}
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)) {
rc = TEST_PRF("%llx/%llX", (unsigned long long)min,
(unsigned long long)max);
PRF_CHECK("4c3c2c1c/4D3D2D1D", rc);
rc = TEST_PRF("%jx/%jX", (uintmax_t)min, (uintmax_t)max);
PRF_CHECK("4c3c2c1c/4D3D2D1D", rc);
}
rc = TEST_PRF("%zx/%zX", (size_t)min, (size_t)max);
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)
|| (sizeof(size_t) <= 4)) {
PRF_CHECK("4c3c2c1c/4D3D2D1D", rc);
} else {
PRF_CHECK("%zx/%zX", rc);
}
rc = TEST_PRF("%tx/%tX", (ptrdiff_t)min, (ptrdiff_t)max);
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)
|| (sizeof(ptrdiff_t) <= 4)) {
PRF_CHECK("4c3c2c1c/4D3D2D1D", rc);
} else {
PRF_CHECK("%tx/%tX", rc);
}
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)
&& (sizeof(long long) > sizeof(int))) {
unsigned long long min = 0x8c7c6c5c4c3c2c1cULL;
unsigned long long max = 0x8d7d6d5d4d3d2d1dULL;
rc = TEST_PRF("%llx/%llX", (unsigned long long)min,
(unsigned long long)max);
PRF_CHECK("8c7c6c5c4c3c2c1c/8D7D6D5D4D3D2D1D", rc);
}
}
static void test_x_flags(void)
{
unsigned int sv = 0x123;
int rc;
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("skipped test for nano\n");
return;
}
/* Stuff related to sign flags, which have no effect on
* unsigned conversions, and alternate form
*/
rc = TEST_PRF("/%x/%-x/%#x/",
sv, sv, sv);
PRF_CHECK("/123/123/0x123/", rc);
/* Stuff related to width and padding */
rc = TEST_PRF("/%1x/%4x/%-4x/%04x/%#15x/%-15x/",
sv, sv, sv, sv, sv, sv);
PRF_CHECK("/123/ 123/123 /0123/"
" 0x123/123 /", rc);
/* These have to be tested without the format validation
* attribute because they produce diagnostics, but the
* standard specifies behavior so we have to test them.
*/
reset_out();
rc = rawprf("/%+x/% x/", sv, sv);
zassert_equal(rc, 9, "rc %d", rc);
zassert_equal(strncmp("/123/123/", buf, rc), 0, NULL);
}
static void test_o(void)
{
unsigned int v = 01234567;
int rc;
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("skipped test for nano\n");
return;
}
rc = TEST_PRF("%o", v);
PRF_CHECK("1234567", rc);
rc = TEST_PRF("%#o", v);
PRF_CHECK("01234567", rc);
}
static void test_fp_value(void)
{
if (!IS_ENABLED(CONFIG_CBPRINTF_FP_SUPPORT)) {
TC_PRINT("skipping unsupported feature\n");
return;
}
double dv = 1234.567;
int rc;
rc = TEST_PRF("/%f/%F/", dv, dv);
PRF_CHECK("/1234.567000/1234.567000/", rc);
rc = TEST_PRF("%g", dv);
PRF_CHECK("1234.57", rc);
rc = TEST_PRF("%e", dv);
PRF_CHECK("1.234567e+03", rc);
rc = TEST_PRF("%E", dv);
PRF_CHECK("1.234567E+03", rc);
rc = TEST_PRF("%a", dv);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT)) {
PRF_CHECK("0x1.34a449ba5e354p+10", rc);
} else {
PRF_CHECK("%a", rc);
}
dv = 1E3;
rc = TEST_PRF("%.2f", dv);
PRF_CHECK("1000.00", rc);
dv = 1E20;
rc = TEST_PRF("%.0f", dv);
PRF_CHECK("100000000000000000000", rc);
rc = TEST_PRF("%.20e", dv);
PRF_CHECK("1.00000000000000000000e+20", rc);
dv = 1E-3;
rc = TEST_PRF("%.3e", dv);
PRF_CHECK("1.000e-03", rc);
dv = 1E-3;
rc = TEST_PRF("%g", dv);
PRF_CHECK("0.001", rc);
dv = 1234567.89;
rc = TEST_PRF("%g", dv);
PRF_CHECK("1.23457e+06", rc);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT)) {
dv = (double)BIT64(40);
rc = TEST_PRF("/%a/%.4a/%.20a/", dv, dv, dv);
PRF_CHECK("/0x1p+40/0x1.0000p+40/"
"0x1.00000000000000000000p+40/", rc);
dv += (double)BIT64(32);
rc = TEST_PRF("%a", dv);
PRF_CHECK("0x1.01p+40", rc);
}
dv = INFINITY;
rc = TEST_PRF("%f.f %F.F %e.e %E.E %g.g %G.g %a.a %A.A",
dv, dv, dv, dv, dv, dv, dv, dv);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT)) {
PRF_CHECK("inf.f INF.F inf.e INF.E "
"inf.g INF.g inf.a INF.A", rc);
} else {
PRF_CHECK("inf.f INF.F inf.e INF.E "
"inf.g INF.g %a.a %A.A", rc);
}
dv = -INFINITY;
rc = TEST_PRF("%f.f %F.F %e.e %E.E %g.g %G.g %a.a %A.A",
dv, dv, dv, dv, dv, dv, dv, dv);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT)) {
PRF_CHECK("-inf.f -INF.F -inf.e -INF.E "
"-inf.g -INF.g -inf.a -INF.A", rc);
} else {
PRF_CHECK("-inf.f -INF.F -inf.e -INF.E "
"-inf.g -INF.g %a.a %A.A", rc);
}
dv = NAN;
rc = TEST_PRF("%f.f %F.F %e.e %E.E %g.g %G.g %a.a %A.A",
dv, dv, dv, dv, dv, dv, dv, dv);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT)) {
PRF_CHECK("nan.f NAN.F nan.e NAN.E "
"nan.g NAN.g nan.a NAN.A", rc);
} else {
PRF_CHECK("nan.f NAN.F nan.e NAN.E "
"nan.g NAN.g %a.a %A.A", rc);
}
dv = DBL_MIN;
rc = TEST_PRF("%a %e", dv, dv);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT)) {
PRF_CHECK("0x1p-1022 2.225074e-308", rc);
} else {
PRF_CHECK("%a 2.225074e-308", rc);
}
dv /= 4;
rc = TEST_PRF("%a %e", dv, dv);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT)) {
PRF_CHECK("0x0.4p-1022 5.562685e-309", rc);
} else {
PRF_CHECK("%a 5.562685e-309", rc);
}
/*
* The following tests are tailored to exercise edge cases in
* lib/os/cbprintf_complete.c:encode_float() and related functions.
*/
dv = 0x1.0p-3;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("0.125", rc);
dv = 0x1.0p-4;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("0.0625", rc);
dv = 0x1.8p-4;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("0.09375", rc);
dv = 0x1.cp-4;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("0.109375", rc);
dv = 0x1.9999999800000p-7;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("0.01249999999708962", rc);
dv = 0x1.9999999ffffffp-8;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("0.006250000005820765", rc);
dv = 0x1.0p+0;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("1", rc);
dv = 0x1.fffffffffffffp-1022;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("4.450147717014402e-308", rc);
dv = 0x1.ffffffffffffep-1022;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("4.450147717014402e-308", rc);
dv = 0x1.ffffffffffffdp-1022;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("4.450147717014401e-308", rc);
dv = 0x1.0000000000001p-1022;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("2.225073858507202e-308", rc);
dv = 0x1p-1022;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("2.225073858507201e-308", rc);
dv = 0x0.fffffffffffffp-1022;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("2.225073858507201e-308", rc);
dv = 0x0.0000000000001p-1022;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("4.940656458412465e-324", rc);
dv = 0x1.1fa182c40c60dp-1019;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("2e-307", rc);
dv = 0x1.fffffffffffffp+1023;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("1.797693134862316e+308", rc);
dv = 0x1.ffffffffffffep+1023;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("1.797693134862316e+308", rc);
dv = 0x1.ffffffffffffdp+1023;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("1.797693134862315e+308", rc);
dv = 0x1.0000000000001p+1023;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("8.988465674311582e+307", rc);
dv = 0x1p+1023;
rc = TEST_PRF("%.16g", dv);
PRF_CHECK("8.98846567431158e+307", rc);
}
static void test_fp_length(void)
{
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("skipped test for nano\n");
return;
}
double dv = 1.2345;
int rc;
rc = TEST_PRF("/%g/", dv);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_SUPPORT)) {
PRF_CHECK("/1.2345/", rc);
} else {
PRF_CHECK("/%g/", rc);
}
rc = TEST_PRF("/%lg/", dv);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_SUPPORT)) {
PRF_CHECK("/1.2345/", rc);
} else {
PRF_CHECK("/%lg/", rc);
}
rc = TEST_PRF("/%Lg/", (long double)dv);
if (ENABLED_USE_LIBC) {
PRF_CHECK("/1.2345/", rc);
} else {
PRF_CHECK("/%Lg/", rc);
}
/* These have to be tested without the format validation
* attribute because they produce diagnostics, but we have
* intended behavior so we have to test them.
*/
reset_out();
rc = rawprf("/%hf/", dv);
zassert_equal(rc, 5, "len %d", rc);
zassert_equal(strncmp("/%hf/", buf, rc), 0, NULL);
}
static void test_fp_flags(void)
{
if (!IS_ENABLED(CONFIG_CBPRINTF_FP_SUPPORT)) {
TC_PRINT("skipping unsupported feature\n");
return;
}
double dv = 1.23;
int rc;
rc = TEST_PRF("/%g/% g/%+g/", dv, dv, dv);
PRF_CHECK("/1.23/ 1.23/+1.23/", rc);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT)) {
rc = TEST_PRF("/%a/%.1a/%.2a/", dv, dv, dv);
PRF_CHECK("/0x1.3ae147ae147aep+0/"
"0x1.4p+0/0x1.3bp+0/", rc);
}
dv = -dv;
rc = TEST_PRF("/%g/% g/%+g/", dv, dv, dv);
PRF_CHECK("/-1.23/-1.23/-1.23/", rc);
dv = 23;
rc = TEST_PRF("/%g/%#g/%.0f/%#.0f/", dv, dv, dv, dv);
PRF_CHECK("/23/23.0000/23/23./", rc);
rc = prf("% .380f", 0x1p-400);
zassert_equal(rc, 383, NULL);
zassert_equal(strncmp(buf, " 0.000", 6), 0, NULL);
zassert_equal(strncmp(&buf[119], "00003872", 8), 0, NULL);
}
static void test_star_width(void)
{
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("skipped test for nano\n");
return;
}
int rc;
rc = TEST_PRF("/%3c/%-3c/", 'a', 'a');
PRF_CHECK("/ a/a /", rc);
rc = TEST_PRF("/%*c/%*c/", 3, 'a', -3, 'a');
PRF_CHECK("/ a/a /", rc);
}
static void test_star_precision(void)
{
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("skipped test for nano\n");
return;
}
int rc;
rc = TEST_PRF("/%.*x/%10.*x/",
5, 0x12, 5, 0x12);
PRF_CHECK("/00012/ 00012/", rc);
if (IS_ENABLED(CONFIG_CBPRINTF_FP_SUPPORT)) {
double dv = 1.2345678;
rc = TEST_PRF("/%.3g/%.5g/%.8g/%g/",
dv, dv, dv, dv);
PRF_CHECK("/1.23/1.2346/1.2345678/1.23457/", rc);
rc = TEST_PRF("/%.*g/%.*g/%.*g/%.*g/",
3, dv,
5, dv,
8, dv,
-3, dv);
PRF_CHECK("/1.23/1.2346/1.2345678/1.23457/", rc);
}
}
static void test_n(void)
{
if (!IS_ENABLED(CONFIG_CBPRINTF_N_SPECIFIER)) {
TC_PRINT("skipping unsupported feature\n");
return;
}
if (IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
TC_PRINT("skipped test for nano\n");
return;
}
char l_hh = 0;
short l_h = 0;
int l = 0;
long l_l = 0;
long long l_ll = 0;
intmax_t l_j = 0;
size_t l_z = 0;
ptrdiff_t l_t = 0;
int rc;
rc = prf("12345%n", &l);
zassert_equal(l, rc, "%d != %d", l, rc);
zassert_equal(rc, 5, NULL);
rc = prf("12345%hn", &l_h);
zassert_equal(l_h, rc, NULL);
rc = prf("12345%hhn", &l_hh);
zassert_equal(l_hh, rc, NULL);
rc = prf("12345%ln", &l_l);
zassert_equal(l_l, rc, NULL);
rc = prf("12345%lln", &l_ll);
zassert_equal(l_ll, rc, NULL);
rc = prf("12345%jn", &l_j);
zassert_equal(l_j, rc, NULL);
rc = prf("12345%zn", &l_z);
zassert_equal(l_z, rc, NULL);
rc = prf("12345%tn", &l_t);
zassert_equal(l_t, rc, NULL);
}
#define EXPECTED_1ARG(_t) (IS_ENABLED(CONFIG_CBPRINTF_NANO) \
? 1U : (sizeof(_t) / sizeof(int)))
static void test_p(void)
{
if (ENABLED_USE_LIBC) {
TC_PRINT("skipping on libc\n");
return;
}
uintptr_t uip = 0xcafe21;
void *ptr = (void *)uip;
int rc;
rc = TEST_PRF("%p", ptr);
PRF_CHECK("0xcafe21", rc);
rc = TEST_PRF("%p", NULL);
PRF_CHECK("(nil)", rc);
/* Nano doesn't support left-justification of pointer
* values.
*/
if (!IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
reset_out();
rc = rawprf("/%12p/", ptr);
zassert_equal(rc, 14, NULL);
zassert_equal(strncmp("/ 0xcafe21/", buf, rc), 0, NULL);
reset_out();
rc = rawprf("/%12p/", NULL);
zassert_equal(rc, 14, NULL);
zassert_equal(strncmp("/ (nil)/", buf, rc), 0, NULL);
}
reset_out();
rc = rawprf("/%-12p/", ptr);
zassert_equal(rc, 14, NULL);
zassert_equal(strncmp("/0xcafe21 /", buf, rc), 0, NULL);
reset_out();
rc = rawprf("/%-12p/", NULL);
zassert_equal(rc, 14, NULL);
zassert_equal(strncmp("/(nil) /", buf, rc), 0, NULL);
/* Nano doesn't support zero-padding of pointer values.
*/
if (!IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
reset_out();
rc = rawprf("/%.8p/", ptr);
zassert_equal(rc, 12, NULL);
zassert_equal(strncmp("/0x00cafe21/", buf, rc), 0, NULL);
}
}
static int out_counter(int c,
void *ctx)
{
size_t *count = ctx;
++*count;
return c;
}
static int out_e42(int c,
void *ctx)
{
return -42;
}
static void test_libc_substs(void)
{
if (!IS_ENABLED(CONFIG_CBPRINTF_LIBC_SUBSTS)) {
TC_PRINT("not enabled\n");
return;
}
char lbuf[8];
char full_flag = 0xbf;
size_t count = 0;
size_t const len = sizeof(lbuf) - 1U;
int rc;
lbuf[len] = full_flag;
rc = snprintfcb(lbuf, len, "%06d", 1);
zassert_equal(rc, 6, NULL);
zassert_equal(strncmp("000001", lbuf, rc), 0, NULL);
zassert_equal(lbuf[7], full_flag, NULL);
rc = snprintfcb(lbuf, len, "%07d", 1);
zassert_equal(rc, 7, NULL);
zassert_equal(strncmp("000000", lbuf, rc), 0, NULL);
zassert_equal(lbuf[7], full_flag, NULL);
rc = snprintfcb(lbuf, len, "%020d", 1);
zassert_equal(rc, 20, "rc %d", rc);
zassert_equal(lbuf[7], full_flag, NULL);
zassert_equal(strncmp("000000", lbuf, rc), 0, NULL);
rc = cbprintf(out_counter, &count, "%020d", 1);
zassert_equal(rc, 20, "rc %d", rc);
zassert_equal(count, 20, NULL);
if (!IS_ENABLED(CONFIG_CBPRINTF_NANO)) {
rc = cbprintf(out_e42, NULL, "%020d", 1);
zassert_equal(rc, -42, "rc %d", rc);
}
}
static void test_nop(void)
{
}
/*test case main entry*/
void test_main(void)
{
if (sizeof(int) == 4) {
pfx_str += 8U;
sfx_str += 8U;
}
TC_PRINT("Opts: " COND_CODE_1(M64_MODE, ("m64"), ("m32")) "\n");
if (ENABLED_USE_LIBC) {
TC_PRINT(" LIBC");
}
if (IS_ENABLED(CONFIG_CBPRINTF_COMPLETE)) {
TC_PRINT(" COMPLETE\n");
} else {
TC_PRINT(" NANO\n");
}
if (IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)) {
TC_PRINT(" FULL_INTEGRAL\n");
} else {
TC_PRINT(" REDUCED_INTEGRAL\n");
}
if (IS_ENABLED(CONFIG_CBPRINTF_FP_SUPPORT)) {
TC_PRINT(" FP_SUPPORT\n");
}
if (IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT)) {
TC_PRINT(" FP_A_SUPPORT\n");
}
if (IS_ENABLED(CONFIG_CBPRINTF_N_SPECIFIER)) {
TC_PRINT(" FP_N_SPECIFIER\n");
}
if (IS_ENABLED(CONFIG_CBPRINTF_LIBC_SUBSTS)) {
TC_PRINT(" LIBC_SUBSTS\n");
}
printf("sizeof: int = %zu ; long = %zu ; ptr = %zu\n",
sizeof(int), sizeof(long), sizeof(void *));
#ifdef CONFIG_CBPRINTF_COMPLETE
printf("sizeof(conversion) = %zu\n", sizeof(struct conversion));
#endif
ztest_test_suite(test_prf,
ztest_unit_test(test_pct),
ztest_unit_test(test_v_c),
ztest_unit_test(test_c),
ztest_unit_test(test_s),
ztest_unit_test(test_d_length),
ztest_unit_test(test_d_flags),
ztest_unit_test(test_x_length),
ztest_unit_test(test_x_flags),
ztest_unit_test(test_o),
ztest_unit_test(test_fp_value),
ztest_unit_test(test_fp_length),
ztest_unit_test(test_fp_flags),
ztest_unit_test(test_star_width),
ztest_unit_test(test_star_precision),
ztest_unit_test(test_n),
ztest_unit_test(test_p),
ztest_unit_test(test_libc_substs),
ztest_unit_test(test_nop)
);
ztest_run_test_suite(test_prf);
}