blob: ac029ec8e13b58e7b223803434feb75461bfebe2 [file] [log] [blame]
#include "unit_nanoprintf.h"
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS
#pragma GCC diagnostic push
#if NANOPRINTF_CLANG
#pragma GCC diagnostic ignored "-Wformat-pedantic"
#pragma GCC diagnostic ignored "-Wold-style-cast"
#else
#pragma GCC diagnostic ignored "-Wformat-overflow"
#endif
#pragma GCC diagnostic ignored "-Wformat"
#pragma GCC diagnostic ignored "-Wformat-zero-length"
#pragma GCC diagnostic ignored "-Wformat-security"
#endif
struct Recorder {
static void PutC(int c, void *ctx) {
static_cast<Recorder*>(ctx)->calls.push_back((char)c);
}
std::string String() const {
return calls.empty() ? std::string() : std::string(calls.begin(), calls.end());
}
std::vector<char> calls;
};
TEST_CASE("npf_vpprintf") {
Recorder r;
SUBCASE("empty string never calls callback") {
REQUIRE(npf_pprintf(r.PutC, &r, "") == 0);
REQUIRE(r.calls.empty());
}
SUBCASE("single character string") {
REQUIRE(npf_pprintf(r.PutC, &r, "A") == 1);
REQUIRE(r.calls.size() == 1);
REQUIRE(r.calls[0] == 'A');
}
SUBCASE("string without format specifiers") {
std::string const s{"Hello from nanoprintf!"};
REQUIRE(npf_pprintf(r.PutC, &r, s.c_str()) == (int)s.size());
REQUIRE(s == r.String());
}
SUBCASE("percent literal") {
REQUIRE(npf_pprintf(r.PutC, &r, "%%") == 1);
REQUIRE(r.String() == std::string{"%"});
}
SUBCASE("character single") {
REQUIRE(npf_pprintf(r.PutC, &r, "%c", 'A') == 1);
REQUIRE(r.String() == std::string{"A"});
}
SUBCASE("character multiple") {
REQUIRE(npf_pprintf(r.PutC, &r, "%c%c%c%c", 'A', 'B', 'C', 'D') == 4);
REQUIRE(r.String() == std::string{"ABCD"});
}
SUBCASE("string single") {
REQUIRE(npf_pprintf(r.PutC, &r, "%s", "abcd") == 4);
REQUIRE(r.String() == std::string{"abcd"});
}
SUBCASE("string empty") {
REQUIRE(npf_pprintf(r.PutC, &r, "%s", "") == 0);
REQUIRE(r.String().empty());
}
SUBCASE("string multiple") {
REQUIRE(npf_pprintf(r.PutC, &r, "%s%s%s", "abcd", "e", "fgh") == 8);
REQUIRE(r.String() == std::string{"abcdefgh"});
}
SUBCASE("string precision zero null pointer") {
REQUIRE(npf_pprintf(r.PutC, &r, "%.0s", nullptr) == 0);
REQUIRE(r.String() == std::string{""});
}
SUBCASE("signed int zero") {
REQUIRE(npf_pprintf(r.PutC, &r, "%i", 0) == 1);
REQUIRE(r.String() == std::string{"0"});
}
SUBCASE("signed int positive") {
REQUIRE(npf_pprintf(r.PutC, &r, "%i", 123) == 3);
REQUIRE(r.String() == std::string{"123"});
}
SUBCASE("signed int negative") {
REQUIRE(npf_pprintf(r.PutC, &r, "%i", -456) == 4);
REQUIRE(r.String() == std::string{"-456"});
}
SUBCASE("signed int max") {
REQUIRE(npf_pprintf(r.PutC, &r, "%d", INT_MAX) == 10);
REQUIRE(r.String() == std::string{"2147483647"});
}
SUBCASE("signed int min") {
REQUIRE(npf_pprintf(r.PutC, &r, "%d", INT_MIN) == 11);
REQUIRE(r.String() == std::string{"-2147483648"});
}
SUBCASE("unsigned int zero") {
REQUIRE(npf_pprintf(r.PutC, &r, "%u", 0) == 1);
REQUIRE(r.String() == std::string{"0"});
}
SUBCASE("unsigned int positive") {
REQUIRE(npf_pprintf(r.PutC, &r, "%u", 45678) == 5);
REQUIRE(r.String() == std::string{"45678"});
}
SUBCASE("unsigned int max u32") {
REQUIRE(npf_pprintf(r.PutC, &r, "%u", 4294967295u) == 10);
REQUIRE(r.String() == std::string{"4294967295"});
}
SUBCASE("octal zero") {
REQUIRE(npf_pprintf(r.PutC, &r, "%o", 0) == 1);
REQUIRE(r.String() == std::string{"0"});
}
SUBCASE("octal positive") {
REQUIRE(npf_pprintf(r.PutC, &r, "%o", 1234) == 4);
REQUIRE(r.String() == std::string{"2322"});
}
SUBCASE("octal max u32") {
REQUIRE(npf_pprintf(r.PutC, &r, "%o", 4294967295u) == 11);
REQUIRE(r.String() == std::string{"37777777777"});
}
SUBCASE("hex zero") {
REQUIRE(npf_pprintf(r.PutC, &r, "%x", 0) == 1);
REQUIRE(r.String() == std::string{"0"});
}
SUBCASE("hex single digit numeric") {
REQUIRE(npf_pprintf(r.PutC, &r, "%x", 8) == 1);
REQUIRE(r.String() == std::string{"8"});
}
SUBCASE("hex single digit alpha") {
REQUIRE(npf_pprintf(r.PutC, &r, "%x", 0xc) == 1);
REQUIRE(r.String() == std::string{"c"});
}
SUBCASE("hex large") {
REQUIRE(npf_pprintf(r.PutC, &r, "%x", 0x9ABCDEF0) == 8);
REQUIRE(r.String() == std::string{"9abcdef0"});
}
SUBCASE("hex max u32") {
REQUIRE(npf_pprintf(r.PutC, &r, "%x", 0xFFFFFFFF) == 8);
REQUIRE(r.String() == std::string{"ffffffff"});
}
SUBCASE("hex uppercase") {
REQUIRE(npf_pprintf(r.PutC, &r, "%X", 0xabcdefab) == 8);
REQUIRE(r.String() == std::string{"ABCDEFAB"});
}
SUBCASE("pointer null") {
REQUIRE(npf_pprintf(r.PutC, &r, "%p", nullptr) == 3);
REQUIRE(r.String() == std::string{"0x0"});
}
SUBCASE("pointer") {
void *p;
uintptr_t const u = 1234;
memcpy(&p, &u, sizeof(p));
int const n = npf_pprintf(r.PutC, &r, "%p", p);
std::string const s = r.String();
char const *sb = s.c_str();
REQUIRE(n > 2);
REQUIRE(*sb == '0');
++sb;
REQUIRE(*sb == 'x');
++sb;
for (int i = 2; i < n - 1; ++i) {
char const c = *sb++;
REQUIRE([c]{ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); }());
}
}
SUBCASE("float zero") {
REQUIRE(npf_pprintf(r.PutC, &r, "%f", (double)0.f) == 8);
REQUIRE(r.String() == std::string{"0.000000"});
}
SUBCASE("float one") {
REQUIRE(npf_pprintf(r.PutC, &r, "%f", 1.0) == 8);
REQUIRE(r.String() == std::string{"1.000000"});
}
SUBCASE("field width 1 has no effect") {
REQUIRE(npf_pprintf(r.PutC, &r, "%1c", 'A') == 1);
REQUIRE(r.String() == std::string{"A"});
}
SUBCASE("field width right justified by default") {
REQUIRE(npf_pprintf(r.PutC, &r, "%2c", 'A') == 2);
REQUIRE(r.String() == std::string{" A"});
}
SUBCASE("field width left justify flag") {
REQUIRE(npf_pprintf(r.PutC, &r, "%-2c", 'A') == 2);
REQUIRE(r.String() == std::string{"A "});
}
SUBCASE("prepend sign flag negative") {
REQUIRE(npf_pprintf(r.PutC, &r, "%+d", -2) == 2);
REQUIRE(r.String() == std::string{"-2"});
}
SUBCASE("prepend sign flag positive for signed conversion") {
REQUIRE(npf_pprintf(r.PutC, &r, "%+d", 2) == 2);
REQUIRE(r.String() == std::string{"+2"});
}
SUBCASE("prepend sign flag zero") {
REQUIRE(npf_pprintf(r.PutC, &r, "%+d", 0) == 2);
REQUIRE(r.String() == std::string{"+0"});
}
SUBCASE("prepend sign flag does nothing for unsigned conversion") {
REQUIRE(npf_pprintf(r.PutC, &r, "%+u", 1) == 1);
REQUIRE(r.String() == std::string{"1"});
}
SUBCASE("prepend space emits space instead of sign when positive") {
REQUIRE(npf_pprintf(r.PutC, &r, "% d", 1) == 2);
REQUIRE(r.String() == std::string{" 1"});
}
SUBCASE("prepend space emits minus sign when negative") {
REQUIRE(npf_pprintf(r.PutC, &r, "% d", -1) == 2);
REQUIRE(r.String() == std::string{"-1"});
}
SUBCASE("leading zero-pad flag does nothing on char (undefined)") {
REQUIRE(npf_pprintf(r.PutC, &r, "%010c", 'A') == 1);
REQUIRE(r.String() == std::string{"A"});
}
SUBCASE("leading zero-pad flag does nothing on string (undefined)") {
REQUIRE(npf_pprintf(r.PutC, &r, "%0s", "ABCD") == 4);
REQUIRE(r.String() == std::string{"ABCD"});
}
SUBCASE("alternative flag: hex doesn't prepend 0x if value is 0") {
REQUIRE(npf_pprintf(r.PutC, &r, "%#x", 0) == 1);
REQUIRE(r.String() == std::string{"0"});
}
SUBCASE("alternative flag: hex uppercase 0X") {
REQUIRE(npf_pprintf(r.PutC, &r, "%#X", 1) == 3);
REQUIRE(r.String() == std::string{"0X1"});
}
SUBCASE("alternative flag: hex lowercase 0x") {
REQUIRE(npf_pprintf(r.PutC, &r, "%#x", 1) == 3);
REQUIRE(r.String() == std::string{"0x1"});
}
SUBCASE("alternative flag: octal doesn't prepend 0 if value is 0") {
REQUIRE(npf_pprintf(r.PutC, &r, "%#o", 0) == 1);
REQUIRE(r.String() == std::string{"0"});
}
SUBCASE("alterinative flag: octal non-zero value") {
REQUIRE(npf_pprintf(r.PutC, &r, "%#o", 2) == 2);
REQUIRE(r.String() == std::string{"02"});
}
}