/******************************************************************************
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<etl::istring>;

  template <class... Args>
  etl::istring& test_format(etl::istring& s, etl::format_string<Args...> fmt, Args&&... args)
  {
    (void)etl::format_to(s, fmt, etl::forward<Args>(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_short)
    {
      etl::string<100> s;

      CHECK_EQUAL("1", test_format(s, "{}", static_cast<short>(1)));
      CHECK_EQUAL("123", test_format(s, "{}", static_cast<short>(123)));
      CHECK_EQUAL("4123", test_format(s, "{}", static_cast<short>(4123)));
      CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast<short>(1), static_cast<short>(2)));
      CHECK_EQUAL("-123", test_format(s, "{}", static_cast<short>(-123)));
      CHECK_EQUAL("0", test_format(s, "{}", static_cast<short>(0)));
      CHECK_EQUAL("-1", test_format(s, "{}", static_cast<short>(-1)));
    }

    //*************************************************************************
    TEST(test_format_unsigned_short)
    {
      etl::string<100> s;

      CHECK_EQUAL("1", test_format(s, "{}", static_cast<unsigned short>(1)));
      CHECK_EQUAL("123", test_format(s, "{}", static_cast<unsigned short>(123)));
      CHECK_EQUAL("4123", test_format(s, "{}", static_cast<unsigned short>(4123)));
      CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast<unsigned short>(1), static_cast<unsigned short>(2)));
      CHECK_EQUAL("60123", test_format(s, "{}", static_cast<unsigned short>(60123)));
      CHECK_EQUAL("0", test_format(s, "{}", static_cast<unsigned short>(0)));
      CHECK_EQUAL("65500", test_format(s, "{}", static_cast<unsigned short>(65500)));
    }

    //*************************************************************************
    TEST(test_format_long_int)
    {
      etl::string<100> s;

      CHECK_EQUAL("1", test_format(s, "{}", static_cast<long int>(1)));
      CHECK_EQUAL("123", test_format(s, "{}", static_cast<long int>(123)));
      CHECK_EQUAL("4123", test_format(s, "{}", static_cast<long int>(4123)));
      CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast<long int>(1), static_cast<long int>(2)));
      CHECK_EQUAL("-123", test_format(s, "{}", static_cast<long int>(-123)));
      CHECK_EQUAL("0", test_format(s, "{}", static_cast<long int>(0)));
      CHECK_EQUAL("-1", test_format(s, "{}", static_cast<long int>(-1)));
    }

    //*************************************************************************
    TEST(test_format_unsigned_long_int)
    {
      etl::string<100> s;

      CHECK_EQUAL("1", test_format(s, "{}", static_cast<unsigned long int>(1)));
      CHECK_EQUAL("123", test_format(s, "{}", static_cast<unsigned long int>(123)));
      CHECK_EQUAL("4123", test_format(s, "{}", static_cast<unsigned long int>(4123)));
      CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast<unsigned long int>(1), static_cast<unsigned long int>(2)));
      CHECK_EQUAL("60123", test_format(s, "{}", static_cast<unsigned long int>(60123)));
      CHECK_EQUAL("0", test_format(s, "{}", static_cast<unsigned long int>(0)));
      CHECK_EQUAL("65500", test_format(s, "{}", static_cast<unsigned long int>(65500)));
    }

    //*************************************************************************
    TEST(test_format_unsigned_int)
    {
      etl::string<100> s;

      CHECK_EQUAL("0", test_format(s, "{}", static_cast<unsigned int>(0U)));
      CHECK_EQUAL("1", test_format(s, "{}", static_cast<unsigned int>(1U)));
      CHECK_EQUAL("12345678", test_format(s, "{}", static_cast<unsigned int>(12345678U)));
      CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast<unsigned int>(4123456780U)));
    }

    //*************************************************************************
    TEST(test_format_long_long_int)
    {
      etl::string<100> s;

      CHECK_EQUAL("-1", test_format(s, "{}", static_cast<long long int>(-1LL)));
      CHECK_EQUAL("0", test_format(s, "{}", static_cast<long long int>(0LL)));
      CHECK_EQUAL("1", test_format(s, "{}", static_cast<long long int>(1LL)));
      CHECK_EQUAL("-12345678", test_format(s, "{}", static_cast<long long int>(-12345678LL)));
      CHECK_EQUAL("-4123456780", test_format(s, "{}", static_cast<long long int>(-4123456780LL)));
      CHECK_EQUAL("-123456781234", test_format(s, "{}", static_cast<long long int>(-123456781234LL)));
      CHECK_EQUAL("-412345678012", test_format(s, "{}", static_cast<long long int>(-412345678012LL)));
      CHECK_EQUAL("12345678", test_format(s, "{}", static_cast<long long int>(12345678LL)));
      CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast<long long int>(4123456780LL)));
    }

    //*************************************************************************
    TEST(test_format_unsigned_long_long_int)
    {
      etl::string<100> s;

      CHECK_EQUAL("0", test_format(s, "{}", static_cast<unsigned long long int>(0LL)));
      CHECK_EQUAL("1", test_format(s, "{}", static_cast<unsigned long long int>(1LL)));
      CHECK_EQUAL("12345678", test_format(s, "{}", static_cast<unsigned long long int>(12345678LL)));
      CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast<unsigned long long int>(4123456780LL)));
      CHECK_EQUAL("18446744073709551615", test_format(s, "{}", static_cast<unsigned long long int>(18446744073709551615ULL)));
      CHECK_EQUAL("1311768467463790320", test_format(s, "{}", static_cast<unsigned long long int>(0x123456789ABCDEF0ULL)));
    }

    //*************************************************************************
    TEST(test_format_other_int)
    {
      etl::string<100> s;

      // mapped to unsigned char
      // CHECK_EQUAL("34", test_format(s, "{}", static_cast<uint8_t>(34)));
      // mapped to signed char
      // CHECK_EQUAL("-14", test_format(s, "{}", static_cast<int8_t>(-14)));
      CHECK_EQUAL("6534", test_format(s, "{}", static_cast<uint16_t>(6534)));
      CHECK_EQUAL("-9414", test_format(s, "{}", static_cast<int16_t>(-9414)));
      CHECK_EQUAL("236534", test_format(s, "{}", static_cast<uint32_t>(236534)));
      CHECK_EQUAL("-6759414", test_format(s, "{}", static_cast<int32_t>(-6759414)));
    }

  #if ETL_USING_FORMAT_FLOATING_POINT
    //*************************************************************************
    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));
    }
  #endif

    //*************************************************************************
    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_string)
    {
      etl::string<100> s;
      etl::string<10>  s_arg = "data1";

      CHECK_EQUAL("data1", test_format(s, "{}", s_arg));
      CHECK_EQUAL("data1", test_format(s, "{:s}", s_arg));
      CHECK_THROW(test_format(s, "{:d}", s_arg), etl::bad_format_string_exception);
      CHECK_EQUAL("data1     ", test_format(s, "{:10s}", s_arg));
      CHECK_EQUAL("data1     ", test_format(s, "{:<10s}", s_arg));
      CHECK_EQUAL("     data1", test_format(s, "{:>10s}", s_arg));
      CHECK_EQUAL("  data1   ", test_format(s, "{:^10s}", s_arg));
      CHECK_EQUAL("data1", test_format(s, "{:3}", s_arg));
      CHECK_EQUAL("dat", test_format(s, "{:.3s}", s_arg));
      CHECK_EQUAL("dat", test_format(s, "{:^.3s}", s_arg));
      CHECK_EQUAL(".  dat   !", test_format(s, ".{:^8.3s}!", s_arg));
      CHECK_EQUAL("^dat     $", test_format(s, "^{:8.3s}$", s_arg));
    }

    //*************************************************************************
    // this minimal derived class of etl::string is used to prove that the
    // temporary lifetime is long enough for the format operation
    template <size_t N>
    class clearing_string : public etl::string<N>
    {
    public:

      using etl::string<N>::string;
      ~clearing_string()
      {
        this->clear();
      }
    };
    TEST(test_format_string_temporary)
    {
      etl::string<100> s;
      const char*      data = "data1";
      using string_t        = clearing_string<10>;

      CHECK_EQUAL("data1", test_format(s, "{}", string_t(data)));
      CHECK_EQUAL("data1", test_format(s, "{:s}", string_t(data)));
      CHECK_THROW(test_format(s, "{:d}", string_t(data)), etl::bad_format_string_exception);
      CHECK_EQUAL("data1     ", test_format(s, "{:10s}", string_t(data)));
      CHECK_EQUAL("data1     ", test_format(s, "{:<10s}", string_t(data)));
      CHECK_EQUAL("     data1", test_format(s, "{:>10s}", string_t(data)));
      CHECK_EQUAL("  data1   ", test_format(s, "{:^10s}", string_t(data)));
      CHECK_EQUAL("data1", test_format(s, "{:3}", string_t(data)));
      CHECK_EQUAL("dat", test_format(s, "{:.3s}", string_t(data)));
      CHECK_EQUAL("dat", test_format(s, "{:^.3s}", string_t(data)));
      CHECK_EQUAL(".  dat   !", test_format(s, ".{:^8.3s}!", string_t(data)));
      CHECK_EQUAL("^dat     $", test_format(s, "^{:8.3s}$", string_t(data)));
    }

    //*************************************************************************
    TEST(test_format_string_escaped)
    {
      etl::string<100> s;
      etl::string<10>  s_arg("data1\n");

      CHECK_EQUAL("\"data1\\n\"", test_format(s, "{:?}", s_arg));
    }

    //*************************************************************************
    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<void*>(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<void*>(0x1abcdef0ULL);
        CHECK_EQUAL("0x1abcdef0", test_format(s, "{:p}", p));
        CHECK_EQUAL("0X1ABCDEF0", test_format(s, "{:P}", p));
      }
    }

    //*************************************************************************
    TEST(test_format_size_t)
    {
      etl::string<100> s;

      CHECK_EQUAL("0", test_format(s, "{}", static_cast<size_t>(0LL)));
      CHECK_EQUAL("1", test_format(s, "{}", static_cast<size_t>(1LL)));
      CHECK_EQUAL("12345678", test_format(s, "{}", static_cast<size_t>(12345678LL)));
      CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast<size_t>(4123456780LL)));
  #if ETL_PLATFORM_64BIT
      static_assert(sizeof(size_t) == 8, "size_t is expected to be 64 bit on 64 bit platforms");
      CHECK_EQUAL("18446744073709551615", test_format(s, "{}", static_cast<size_t>(18446744073709551615ULL)));
      CHECK_EQUAL("1311768467463790320", test_format(s, "{}", static_cast<size_t>(0x123456789ABCDEF0ULL)));
  #endif
    }

    //*************************************************************************
    TEST(test_format_unsigned_long)
    {
      etl::string<100> s;

      CHECK_EQUAL("0", test_format(s, "{}", static_cast<unsigned long>(0LL)));
      CHECK_EQUAL("1", test_format(s, "{}", static_cast<unsigned long>(1LL)));
      CHECK_EQUAL("12345678", test_format(s, "{}", static_cast<unsigned long>(12345678LL)));
      CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast<unsigned long>(4123456780LL)));
  #if ETL_PLATFORM_64BIT
      static_assert(sizeof(unsigned long) == 8, "size_t is expected to be 64 bit on 64 bit platforms");
      CHECK_EQUAL("18446744073709551615", test_format(s, "{}", static_cast<unsigned long>(18446744073709551615ULL)));
      CHECK_EQUAL("1311768467463790320", test_format(s, "{}", static_cast<unsigned long>(0x123456789ABCDEF0ULL)));
  #endif
    }

    //*************************************************************************
    TEST(test_format_signed_char)
    {
      etl::string<100> s;

      CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast<signed char>('s')));
      CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast<signed char>('s')));
      CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast<signed char>('s')));
      CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast<signed char>('\t')));
      CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast<signed char>('\t')));
      CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast<signed char>('\n')));
      CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast<signed char>('\r')));
      CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast<signed char>('"')));
      CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast<signed char>('\'')));
      CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<signed char>('\\')));
      CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<signed char>('\\')));
      CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast<signed char>('a')));
      CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast<signed char>('a')));
      CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast<signed char>('a')));
      CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast<signed char>('a')));
    }

    //*************************************************************************
    TEST(test_format_unsigned_char)
    {
      etl::string<100> s;

      CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast<unsigned char>('s')));
      CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast<unsigned char>('s')));
      CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast<unsigned char>('s')));
      CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast<unsigned char>('\t')));
      CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\t')));
      CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\n')));
      CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\r')));
      CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast<unsigned char>('"')));
      CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\'')));
      CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\\')));
      CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\\')));
      CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast<unsigned char>('a')));
      CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast<unsigned char>('a')));
      CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast<unsigned char>('a')));
      CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast<unsigned char>('a')));
    }

    //*************************************************************************
    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(static_cast<size_t>(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<unsigned int>(34)));
      CHECK_EQUAL("a***-341234567890****", test_format(s, "a{:*^20}", static_cast<long long int>(-341234567890)));
      CHECK_EQUAL("a****341234567890****", test_format(s, "a{:*^20}", static_cast<unsigned long long int>(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);
    }
  }
} // namespace

#endif
