/****************************************************************************** The MIT License(MIT) Embedded Template Library. https://github.com/ETLCPP/etl https://www.etlcpp.com Copyright(c) 2025 BMW AG Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ #include "unit_test_framework.h" #include "etl/format.h" #include "etl/iterator.h" #if ETL_USING_CPP11 namespace { using iterator = etl::back_insert_iterator; template etl::istring& test_format(etl::istring& s, etl::format_string fmt, Args&&... args) { (void) etl::format_to(s, fmt, etl::forward(args)...); return s; } SUITE(test_format) { //************************************************************************* TEST(test_empty) { etl::string<100> s; const char* result = etl::format_to(s, ""); CHECK_EQUAL(s.cbegin(), result); CHECK_EQUAL("", s); } //************************************************************************* TEST(test_format) { etl::string<100> s; const char* result = etl::format_to(s, "abc"); CHECK_EQUAL(s.cbegin() + 3, result); CHECK_EQUAL("abc", s); } //************************************************************************* TEST(test_format_int) { etl::string<100> s; CHECK_EQUAL("1", test_format(s, "{}", 1)); CHECK_EQUAL("123", test_format(s, "{}", 123)); CHECK_EQUAL("4123", test_format(s, "{}", 4123)); CHECK_EQUAL("1048962123", test_format(s, "{}", 1048962123)); CHECK_EQUAL("1 2", test_format(s, "{} {}", 1, 2)); CHECK_EQUAL("-123", test_format(s, "{}", -123)); CHECK_EQUAL("-314748364", test_format(s, "{}", (int)-314748364)); CHECK_EQUAL("2147483647", test_format(s, "{}", INT32_MAX)); CHECK_EQUAL("-2147483648", test_format(s, "{}", INT32_MIN)); CHECK_EQUAL("0", test_format(s, "{}", 0)); CHECK_EQUAL("-1", test_format(s, "{}", -1)); } //************************************************************************* TEST(test_format_unsigned_int) { etl::string<100> s; CHECK_EQUAL("0", test_format(s, "{}", static_cast(0U))); CHECK_EQUAL("1", test_format(s, "{}", static_cast(1U))); CHECK_EQUAL("12345678", test_format(s, "{}", static_cast(12345678U))); CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast(4123456780U))); } //************************************************************************* TEST(test_format_long_long_int) { etl::string<100> s; CHECK_EQUAL("-1", test_format(s, "{}", static_cast(-1LL))); CHECK_EQUAL("0", test_format(s, "{}", static_cast(0LL))); CHECK_EQUAL("1", test_format(s, "{}", static_cast(1LL))); CHECK_EQUAL("-12345678", test_format(s, "{}", static_cast(-12345678LL))); CHECK_EQUAL("-4123456780", test_format(s, "{}", static_cast(-4123456780LL))); CHECK_EQUAL("-123456781234", test_format(s, "{}", static_cast(-123456781234LL))); CHECK_EQUAL("-412345678012", test_format(s, "{}", static_cast(-412345678012LL))); CHECK_EQUAL("12345678", test_format(s, "{}", static_cast(12345678LL))); CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast(4123456780LL))); } //************************************************************************* TEST(test_format_unsigned_long_long_int) { etl::string<100> s; CHECK_EQUAL("0", test_format(s, "{}", static_cast(0LL))); CHECK_EQUAL("1", test_format(s, "{}", static_cast(1LL))); CHECK_EQUAL("12345678", test_format(s, "{}", static_cast(12345678LL))); CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast(4123456780LL))); CHECK_EQUAL("18446744073709551615", test_format(s, "{}", static_cast(18446744073709551615ULL))); CHECK_EQUAL("1311768467463790320", test_format(s, "{}", static_cast(0x123456789ABCDEF0ULL))); } //************************************************************************* TEST(test_format_other_int) { etl::string<100> s; CHECK_EQUAL("34", test_format(s, "{}", static_cast(34))); CHECK_EQUAL("-14", test_format(s, "{}", static_cast(-14))); CHECK_EQUAL("6534", test_format(s, "{}", static_cast(6534))); CHECK_EQUAL("-9414", test_format(s, "{}", static_cast(-9414))); CHECK_EQUAL("236534", test_format(s, "{}", static_cast(236534))); CHECK_EQUAL("-6759414", test_format(s, "{}", static_cast(-6759414))); } //************************************************************************* TEST(test_format_float) { etl::string<100> s; CHECK_EQUAL("1.0", test_format(s, "{}", 1.0f)); CHECK_EQUAL("1.234567", test_format(s, "{}", 1.234567f)); CHECK_EQUAL("1.234567", test_format(s, "{}", 1.2345678f)); CHECK_EQUAL("1.125", test_format(s, "{}", 1.125f)); } //************************************************************************* TEST(test_format_double) { etl::string<100> s; CHECK_EQUAL("1.0", test_format(s, "{}", 1.0)); CHECK_EQUAL("1.234564", test_format(s, "{}", 1.234564)); CHECK_EQUAL("1.234567", test_format(s, "{}", 1.2345678)); CHECK_EQUAL("1.5", test_format(s, "{}", 1.5)); } //************************************************************************* TEST(test_format_long_double) { etl::string<100> s; CHECK_EQUAL("1.0", test_format(s, "{}", 1.0l)); auto& result = test_format(s, "{}", 1.234567l); CHECK("1.234567" == result || "1.234566" == result); CHECK_EQUAL("1.234567", test_format(s, "{}", 1.2345678l)); CHECK_EQUAL("1.25", test_format(s, "{}", 1.25l)); } //************************************************************************* TEST(test_format_float_presentation) { etl::string<100> s; CHECK_EQUAL("1.000000e+00", test_format(s, "{:e}", 1.0f)); CHECK_EQUAL("1.125000E+00", test_format(s, "{:E}", 1.125f)); CHECK_EQUAL("-2.533324e-05", test_format(s, "{:e}", -0.00002533324f)); CHECK_EQUAL("-2.500000e+11", test_format(s, "{:e}", -250000000000.0f)); CHECK_EQUAL("1.000000", test_format(s, "{:f}", 1.0f)); CHECK_EQUAL("1.125000", test_format(s, "{:F}", 1.125f)); CHECK_EQUAL("1.000000", test_format(s, "{:g}", 1.0f)); CHECK_EQUAL("1.125000", test_format(s, "{:G}", 1.125f)); CHECK_EQUAL("1.000000e+10", test_format(s, "{:g}", 10000000000.0f)); CHECK_EQUAL("1.000000E+10", test_format(s, "{:G}", 10000000000.0f)); CHECK_EQUAL("nan", test_format(s, "{}", NAN)); CHECK_EQUAL("nan", test_format(s, "{:e}", NAN)); CHECK_EQUAL("NAN", test_format(s, "{:E}", NAN)); CHECK_EQUAL("nan", test_format(s, "{:f}", NAN)); CHECK_EQUAL("NAN", test_format(s, "{:F}", NAN)); CHECK_EQUAL("nan", test_format(s, "{:g}", NAN)); CHECK_EQUAL("NAN", test_format(s, "{:0.3G}", NAN)); CHECK_EQUAL("inf", test_format(s, "{}", INFINITY)); CHECK_EQUAL("inf", test_format(s, "{:e}", INFINITY)); CHECK_EQUAL("INF", test_format(s, "{:E}", INFINITY)); CHECK_EQUAL("inf", test_format(s, "{:f}", INFINITY)); CHECK_EQUAL("INF", test_format(s, "{:F}", INFINITY)); CHECK_EQUAL("inf", test_format(s, "{:g}", INFINITY)); CHECK_EQUAL("INF", test_format(s, "{:0.3G}", INFINITY)); CHECK_EQUAL("0x1.8p+0", test_format(s, "{:a}", 1.5f)); CHECK_EQUAL("0X1.4CCCCCCCCCP+0", test_format(s, "{:A}", 1.3l)); CHECK_EQUAL("0x2.49fp+4", test_format(s, "{:a}", 150000.0)); CHECK_EQUAL("0x1.92a738p-5", test_format(s, "{:a}", 0.0000015f)); CHECK_EQUAL("0x1.6345785d8ap+e", test_format(s, "{:a}", 100000000000000000.l)); } //************************************************************************* TEST(test_format_char_array) { etl::string<100> s; CHECK_EQUAL("s", test_format(s, "{}", "s")); CHECK_EQUAL("abcd", test_format(s, "{}", "abcd")); } //************************************************************************* TEST(test_format_char) { etl::string<100> s; CHECK_EQUAL("a s b", test_format(s, "a {} b", 's')); CHECK_EQUAL("a s b", test_format(s, "a {:c} b", 's')); CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", 's')); CHECK_EQUAL("a \t b", test_format(s, "a {} b", '\t')); CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", '\t')); CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", '\n')); CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", '\r')); CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", '"')); CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", '\'')); CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", '\\')); CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", '\\')); CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", 'a')); CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", 'a')); CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", 'a')); CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", 'a')); } //************************************************************************* TEST(test_format_bool) { etl::string<100> s; CHECK_EQUAL("1false2true3", test_format(s, "1{}2{}3", false, true)); CHECK_EQUAL("true", test_format(s, "{:s}", true)); CHECK_EQUAL("1", test_format(s, "{:d}", true)); CHECK_EQUAL("1", test_format(s, "{:X}", true)); CHECK_EQUAL("01", test_format(s, "{:#o}", true)); CHECK_EQUAL(" true", test_format(s, "{:10}", true)); } //************************************************************************* TEST(test_format_string_view) { etl::string<100> s; etl::string_view sv("data1"); CHECK_EQUAL("data1", test_format(s, "{}", sv)); CHECK_EQUAL("data1", test_format(s, "{:s}", sv)); CHECK_THROW(test_format(s, "{:d}", sv), etl::bad_format_string_exception); CHECK_EQUAL("data1 ", test_format(s, "{:10s}", sv)); CHECK_EQUAL("data1 ", test_format(s, "{:<10s}", sv)); CHECK_EQUAL(" data1", test_format(s, "{:>10s}", sv)); CHECK_EQUAL(" data1 ", test_format(s, "{:^10s}", sv)); CHECK_EQUAL("data1", test_format(s, "{:3}", sv)); CHECK_EQUAL("dat", test_format(s, "{:.3s}", sv)); CHECK_EQUAL("dat", test_format(s, "{:^.3s}", sv)); CHECK_EQUAL(". dat !", test_format(s, ".{:^8.3s}!", sv)); CHECK_EQUAL("^dat $", test_format(s, "^{:8.3s}$", sv)); } //************************************************************************* TEST(test_format_string_view_escaped) { etl::string<100> s; etl::string_view sv("data1\n"); CHECK_EQUAL("\"data1\\n\"", test_format(s, "{:?}", sv)); } //************************************************************************* TEST(test_format_chars) { etl::string<100> s; const char* chars = "data1"; CHECK_EQUAL("data1", test_format(s, "{}", chars)); CHECK_EQUAL("data1", test_format(s, "{:s}", chars)); CHECK_THROW(test_format(s, "{:d}", chars), etl::bad_format_string_exception); CHECK_EQUAL("data1 ", test_format(s, "{:10s}", chars)); CHECK_EQUAL("data1 ", test_format(s, "{:<10s}", chars)); CHECK_EQUAL(" data1", test_format(s, "{:>10s}", chars)); CHECK_EQUAL(" data1 ", test_format(s, "{:^10s}", chars)); CHECK_EQUAL("data1", test_format(s, "{:3}", chars)); CHECK_EQUAL("dat", test_format(s, "{:.3s}", chars)); CHECK_EQUAL("dat", test_format(s, "{:^.3s}", chars)); CHECK_EQUAL(". dat !", test_format(s, ".{:^8.3s}!", chars)); CHECK_EQUAL("^dat $", test_format(s, "^{:8.3s}$", chars)); } //************************************************************************* TEST(test_format_chars_escaped) { etl::string<100> s; const char* chars = "data2\n"; CHECK_EQUAL("\"data2\\n\"", test_format(s, "{:?}", chars)); } //************************************************************************* TEST(test_format_pointer) { etl::string<100> s; void* p = nullptr; CHECK_EQUAL("0x0", test_format(s, "{}", p)); CHECK_EQUAL("0x0", test_format(s, "{:p}", p)); CHECK_EQUAL("0X0", test_format(s, "{:P}", p)); if (sizeof(uintptr_t) == 8) { p = reinterpret_cast(0x123456789abcdef0ULL); CHECK_EQUAL("0x123456789abcdef0", test_format(s, "{:p}", p)); CHECK_EQUAL("0X123456789ABCDEF0", test_format(s, "{:P}", p)); } else if (sizeof(uintptr_t) == 4) { p = reinterpret_cast(0x1abcdef0ULL); CHECK_EQUAL("0x1abcdef0", test_format(s, "{:p}", p)); CHECK_EQUAL("0X1ABCDEF0", test_format(s, "{:P}", p)); } } //************************************************************************* TEST(test_format_limit) { etl::string<10> s; CHECK_EQUAL("abcdefghij", test_format(s, "abcdefghijkl")); CHECK_EQUAL("abcdefgh12", test_format(s, "abcdefgh{}", 123)); } //************************************************************************* TEST(test_format_escape) { etl::string<100> s; CHECK_EQUAL("abc{def", test_format(s, "abc{{def")); CHECK_EQUAL("}abc", test_format(s, "}}abc")); } //************************************************************************* TEST(test_format_invalid) { etl::string<100> s; CHECK_THROW(test_format(s, "a{b}", 1), etl::bad_format_string_exception); // bad format index spec // goal: rejected at compile time on C++20, error on <= C++17 CHECK_THROW(test_format(s, "a{b"), etl::bad_format_string_exception); // closing brace missing // goal: rejected at compile time on C++20, error on <= C++17 CHECK_THROW(test_format(s, "a{b}"), etl::bad_format_string_exception); // arg missing // goal: rejected at compile time on C++20, error on <= C++17 CHECK_THROW(test_format(s, "a}b"), etl::bad_format_string_exception); // bad format: only escaped }} allowed // goal: rejected at compile time on C++20, error on <= C++17 CHECK_EQUAL("123", test_format(s, "{:}", 123)); // valid CHECK_THROW(test_format(s, "{::}", 123), etl::bad_format_string_exception); // bad format spec CHECK_THROW(test_format(s, "{1}", 123), etl::bad_format_string_exception); // bad index } //************************************************************************* TEST(test_format_indexed) { etl::string<100> s; CHECK_EQUAL("34 56", test_format(s, "{0} {1}", 34, 56)); CHECK_EQUAL("56 34", test_format(s, "{1} {0}", 34, 56)); CHECK_EQUAL("134 134", test_format(s, "{0} {0}", 134)); CHECK_EQUAL("a b c d e f g h i j k l m n", test_format(s, "{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13}", 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n')); } //************************************************************************* TEST(test_formatted_size) { CHECK_EQUAL(0, etl::formatted_size("")); CHECK_EQUAL(0, etl::formatted_size("{}", "")); CHECK_EQUAL(5, etl::formatted_size("xyz{}", 12)); CHECK_EQUAL(3, etl::formatted_size("{}", "abc")); } //************************************************************************* TEST(test_iterator) { etl::string<100> s; etl::istring::iterator result = etl::format_to(s.begin(), "{0} {1}", 34, 56); s.uninitialized_resize(result - s.begin()); CHECK_EQUAL("34 56", s); s.clear(); etl::format_to(iterator(s), ""); CHECK_EQUAL("", s); s.clear(); etl::format_to(iterator(s), "{0} {1}", 65, 34); CHECK_EQUAL("65 34", s); s = "abcdefghij"; etl::format_to_n(s.begin(), 3, "xy{}", 123); CHECK_EQUAL("xy1defghij", s); } //************************************************************************* TEST(test_format_spec_fill_and_align) { etl::string<100> s; CHECK_EQUAL("a 34", test_format(s, "a{:5}", 34)); CHECK_EQUAL("a34", test_format(s, "a{:1}", 34)); CHECK_EQUAL("a34", test_format(s, "a{:2}", 34)); CHECK_EQUAL(" 34", test_format(s, "{:>4}", 34)); CHECK_EQUAL(" 34 ", test_format(s, "{:^4}", 34)); CHECK_EQUAL(" 34 ", test_format(s, "{:^5}", 34)); CHECK_EQUAL(" -65 ", test_format(s, "{:^5}", -65)); CHECK_EQUAL("34 ", test_format(s, "{:<4}", 34)); CHECK_THROW(test_format(s, "a{:*5}", 34), etl::bad_format_string_exception); CHECK_EQUAL("a*34**", test_format(s, "a{:*^5}", 34)); CHECK_EQUAL("a*34**", test_format(s, "a{:*^5}", static_cast(34))); CHECK_EQUAL("a***-341234567890****", test_format(s, "a{:*^20}", static_cast(-341234567890))); CHECK_EQUAL("a****341234567890****", test_format(s, "a{:*^20}", static_cast(341234567890))); CHECK_EQUAL(" x ", test_format(s, "{: ^20}", 'x')); CHECK_EQUAL("x ", test_format(s, "{:20}", 'x')); CHECK_EQUAL(" x", test_format(s, "{:>20}", 'x')); CHECK_EQUAL("x ", test_format(s, "{:<20}", 'x')); } //************************************************************************* TEST(test_format_spec_sign) { etl::string<100> s; CHECK_EQUAL("34", test_format(s, "{:-}", 34)); CHECK_EQUAL("-34", test_format(s, "{:-}", -34)); CHECK_EQUAL("-34", test_format(s, "{:+}", -34)); CHECK_EQUAL("+34", test_format(s, "{:+}", 34)); CHECK_EQUAL("+0", test_format(s, "{:+}", 0)); CHECK_EQUAL("0", test_format(s, "{:-}", 0)); CHECK_EQUAL("210", test_format(s, "{:-}", 210)); CHECK_EQUAL("-210", test_format(s, "{:-}", -210)); CHECK_EQUAL(" 0", test_format(s, "{: }", 0)); CHECK_EQUAL(" 546", test_format(s, "{: }", 546)); CHECK_EQUAL("-546", test_format(s, "{: }", -546)); } //************************************************************************* TEST(test_format_int_presentation) { etl::string<100> s; CHECK_EQUAL("134", test_format(s, "{:d}", 134)); CHECK_EQUAL("3f4", test_format(s, "{:x}", 0x3f4)); CHECK_EQUAL("123456789abcdef0", test_format(s, "{:x}", 0x123456789abcdef0ULL)); CHECK_EQUAL("0x3f4", test_format(s, "{:#x}", 0x3f4)); CHECK_EQUAL("3F4", test_format(s, "{:X}", 0x3f4)); CHECK_EQUAL("0X3F4", test_format(s, "{:#X}", 0x3f4)); CHECK_EQUAL("34", test_format(s, "{:o}", 034)); CHECK_EQUAL("034", test_format(s, "{:#o}", 034)); #if ETL_USING_CPP14 CHECK_EQUAL("1010", test_format(s, "{:b}", 0b1010)); CHECK_EQUAL("0b1010", test_format(s, "{:#b}", 0b1010)); CHECK_EQUAL("1010", test_format(s, "{:B}", 0b1010)); CHECK_EQUAL("0B1010", test_format(s, "{:#B}", 0b1010)); CHECK_EQUAL("-0B1010", test_format(s, "{:#B}", -0b1010)); #endif CHECK_EQUAL("C", test_format(s, "{:c}", 67)); CHECK_EQUAL("00067", test_format(s, "{:05d}", 67)); CHECK_EQUAL("+00067", test_format(s, "{:+05d}", 67)); CHECK_EQUAL("+0X00EF1", test_format(s, "{:+#05X}", 0xEF1)); CHECK_THROW(test_format(s, "{:+#05.5X}", 0xEF1), etl::bad_format_string_exception); } } } #endif