mirror of
				https://github.com/fmtlib/fmt.git
				synced 2025-10-31 06:01:41 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			298 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			298 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  Formatting library tests.
 | |
|  Author: Victor Zverovich
 | |
|  */
 | |
| 
 | |
| #include <cfloat>
 | |
| #include <climits>
 | |
| #include <cstring>
 | |
| #include <gtest/gtest.h>
 | |
| #include "format.h"
 | |
| 
 | |
| using std::size_t;
 | |
| using std::sprintf;
 | |
| 
 | |
| using fmt::Formatter;
 | |
| using fmt::Format;
 | |
| using fmt::FormatError;
 | |
| 
 | |
| #define FORMAT_TEST_THROW_(statement, expected_exception, message, fail) \
 | |
|   GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
 | |
|   if (::testing::internal::ConstCharPtr gtest_msg = "") { \
 | |
|     bool gtest_caught_expected = false; \
 | |
|     std::string actual_message; \
 | |
|     try { \
 | |
|       GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
 | |
|     } \
 | |
|     catch (expected_exception const& e) { \
 | |
|       gtest_caught_expected = true; \
 | |
|       actual_message = e.what(); \
 | |
|     } \
 | |
|     catch (...) { \
 | |
|       gtest_msg.value = \
 | |
|           "Expected: " #statement " throws an exception of type " \
 | |
|           #expected_exception ".\n  Actual: it throws a different type."; \
 | |
|       goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
 | |
|     } \
 | |
|     if (!gtest_caught_expected) { \
 | |
|       gtest_msg.value = \
 | |
|           "Expected: " #statement " throws an exception of type " \
 | |
|           #expected_exception ".\n  Actual: it throws nothing."; \
 | |
|       goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
 | |
|     } else if (actual_message != message) {\
 | |
|       gtest_msg.value = \
 | |
|           "Expected: " #statement " throws an exception of type " \
 | |
|           #expected_exception " with message \"" message "\"."; \
 | |
|       goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
 | |
|     } \
 | |
|   } else \
 | |
|     GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
 | |
|       fail(gtest_msg.value)
 | |
| 
 | |
| #define EXPECT_THROW_MSG(statement, expected_exception, expected_message) \
 | |
|   FORMAT_TEST_THROW_(statement, expected_exception, expected_message, \
 | |
|       GTEST_NONFATAL_FAILURE_)
 | |
| 
 | |
| class TestString {
 | |
|  private:
 | |
|   std::string value_;
 | |
| 
 | |
|  public:
 | |
|   explicit TestString(const char *value = "") : value_(value) {}
 | |
| 
 | |
|   friend std::ostream &operator<<(std::ostream &os, const TestString &s) {
 | |
|     os << s.value_;
 | |
|     return os;
 | |
|   }
 | |
| };
 | |
| 
 | |
| TEST(FormatterTest, NoArgs) {
 | |
|   EXPECT_EQ("abracadabra", str(Format("{0}{1}{0}") << "abra" << "cad"));
 | |
|   EXPECT_EQ("test", str(Format("test")));
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, Args) {
 | |
|   EXPECT_EQ("42", str(Format("{0}") << 42));
 | |
|   EXPECT_EQ("before 42", str(Format("before {0}") << 42));
 | |
|   EXPECT_EQ("42 after", str(Format("{0} after") << 42));
 | |
|   EXPECT_EQ("before 42 after", str(Format("before {0} after") << 42));
 | |
|   EXPECT_EQ("answer = 42", str(Format("{0} = {1}") << "answer" << 42));
 | |
|   EXPECT_EQ("42 is the answer", str(Format("{1} is the {0}") << "answer" << 42));
 | |
|   EXPECT_EQ("abracadabra", str(Format("{0}{1}{0}") << "abra" << "cad"));
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, ArgErrors) {
 | |
|   EXPECT_THROW_MSG(Format("{"), FormatError, "unmatched '{' in format");
 | |
|   EXPECT_THROW_MSG(Format("{}"), FormatError,
 | |
|       "missing argument index in format string");
 | |
|   EXPECT_THROW_MSG(Format("{0"), FormatError, "unmatched '{' in format");
 | |
|   EXPECT_THROW_MSG(Format("{0}"), std::out_of_range,
 | |
|       "argument index is out of range in format");
 | |
|   char format[256];
 | |
|   std::sprintf(format, "{%u", UINT_MAX);
 | |
|   EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
 | |
|   std::sprintf(format, "{%u}", UINT_MAX);
 | |
|   EXPECT_THROW_MSG(Format(format), std::out_of_range,
 | |
|       "argument index is out of range in format");
 | |
|   if (ULONG_MAX > UINT_MAX) {
 | |
|     std::sprintf(format, "{%lu", UINT_MAX + 1l);
 | |
|     EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
 | |
|     std::sprintf(format, "{%lu}", UINT_MAX + 1l);
 | |
|     EXPECT_THROW_MSG(Format(format),
 | |
|         FormatError, "number is too big in format");
 | |
|   } else {
 | |
|     std::sprintf(format, "{%u0", UINT_MAX);
 | |
|     EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
 | |
|     std::sprintf(format, "{%u0}", UINT_MAX);
 | |
|     EXPECT_THROW_MSG(Format(format),
 | |
|         FormatError, "number is too big in format");
 | |
|   }
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, EmptySpecs) {
 | |
|   EXPECT_EQ("42", str(Format("{0:}") << 42));
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, PlusFlag) {
 | |
|   EXPECT_EQ("+42", str(Format("{0:+}") << 42));
 | |
|   EXPECT_EQ("-42", str(Format("{0:+}") << -42));
 | |
|   EXPECT_EQ("+42", str(Format("{0:+}") << 42));
 | |
|   EXPECT_THROW_MSG(Format("{0:+}") << 42u,
 | |
|       FormatError, "format specifier '+' requires signed argument");
 | |
|   EXPECT_EQ("+42", str(Format("{0:+}") << 42l));
 | |
|   EXPECT_THROW_MSG(Format("{0:+}") << 42ul,
 | |
|       FormatError, "format specifier '+' requires signed argument");
 | |
|   EXPECT_EQ("+42", str(Format("{0:+}") << 42.0));
 | |
|   EXPECT_EQ("+42", str(Format("{0:+}") << 42.0l));
 | |
|   EXPECT_THROW_MSG(Format("{0:+") << 'c',
 | |
|       FormatError, "unmatched '{' in format");
 | |
|   EXPECT_THROW_MSG(Format("{0:+}") << 'c',
 | |
|       FormatError, "format specifier '+' requires numeric argument");
 | |
|   EXPECT_THROW_MSG(Format("{0:+}") << "abc",
 | |
|       FormatError, "format specifier '+' requires numeric argument");
 | |
|   EXPECT_THROW_MSG(Format("{0:+}") << L"abc",
 | |
|       FormatError, "format specifier '+' requires numeric argument");
 | |
|   EXPECT_THROW_MSG(Format("{0:+}") << reinterpret_cast<void*>(0x42),
 | |
|       FormatError, "format specifier '+' requires numeric argument");
 | |
|   EXPECT_THROW_MSG(Format("{0:+}") << TestString(),
 | |
|       FormatError, "format specifier '+' requires numeric argument");
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, ZeroFlag) {
 | |
|   EXPECT_EQ("42", str(Format("{0:0}") << 42));
 | |
|   EXPECT_EQ("-0042", str(Format("{0:05}") << -42));
 | |
|   EXPECT_EQ("00042", str(Format("{0:05}") << 42u));
 | |
|   EXPECT_EQ("-0042", str(Format("{0:05}") << -42l));
 | |
|   EXPECT_EQ("00042", str(Format("{0:05}") << 42ul));
 | |
|   EXPECT_EQ("-0042", str(Format("{0:05}") << -42.0));
 | |
|   EXPECT_EQ("-0042", str(Format("{0:05}") << -42.0l));
 | |
|   EXPECT_THROW_MSG(Format("{0:0") << 'c',
 | |
|       FormatError, "unmatched '{' in format");
 | |
|   EXPECT_THROW_MSG(Format("{0:05}") << 'c',
 | |
|       FormatError, "format specifier '0' requires numeric argument");
 | |
|   EXPECT_THROW_MSG(Format("{0:05}") << "abc",
 | |
|       FormatError, "format specifier '0' requires numeric argument");
 | |
|   EXPECT_THROW_MSG(Format("{0:05}") << L"abc",
 | |
|       FormatError, "format specifier '0' requires numeric argument");
 | |
|   EXPECT_THROW_MSG(Format("{0:05}") << reinterpret_cast<void*>(0x42),
 | |
|       FormatError, "format specifier '0' requires numeric argument");
 | |
|   EXPECT_THROW_MSG(Format("{0:05}") << TestString(),
 | |
|       FormatError, "format specifier '0' requires numeric argument");
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, Width) {
 | |
|   char format[256];
 | |
|   if (ULONG_MAX > UINT_MAX) {
 | |
|     std::sprintf(format, "{0:%lu", INT_MAX + 1l);
 | |
|     EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
 | |
|     std::sprintf(format, "{0:%lu}", UINT_MAX + 1l);
 | |
|     EXPECT_THROW_MSG(Format(format) << 0,
 | |
|         FormatError, "number is too big in format");
 | |
|   } else {
 | |
|     std::sprintf(format, "{0:%u0", UINT_MAX);
 | |
|     EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
 | |
|     std::sprintf(format, "{0:%u0}", UINT_MAX);
 | |
|     EXPECT_THROW_MSG(Format(format) << 0,
 | |
|         FormatError, "number is too big in format");
 | |
|   }
 | |
|   std::sprintf(format, "{0:%u", INT_MAX + 1u);
 | |
|   EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
 | |
|   std::sprintf(format, "{0:%u}", INT_MAX + 1u);
 | |
|   EXPECT_THROW_MSG(Format(format) << 0,
 | |
|       FormatError, "number is too big in format");
 | |
|   EXPECT_EQ(" -42", str(Format("{0:4}") << -42));
 | |
|   EXPECT_EQ("   42", str(Format("{0:5}") << 42u));
 | |
|   EXPECT_EQ("   -42", str(Format("{0:6}") << -42l));
 | |
|   EXPECT_EQ("     42", str(Format("{0:7}") << 42ul));
 | |
|   EXPECT_EQ("   -1.23", str(Format("{0:8}") << -1.23));
 | |
|   EXPECT_EQ("    -1.23", str(Format("{0:9}") << -1.23l));
 | |
|   EXPECT_EQ("    0xcafe",
 | |
|       str(Format("{0:10}") << reinterpret_cast<void*>(0xcafe)));
 | |
|   EXPECT_EQ("x          ", str(Format("{0:11}") << 'x'));
 | |
|   EXPECT_EQ("narrow      ", str(Format("{0:12}") << "narrow"));
 | |
|   EXPECT_EQ("wide         ", str(Format("{0:13}") << L"wide"));
 | |
|   EXPECT_EQ("test          ", str(Format("{0:14}") << TestString("test")));
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, Precision) {
 | |
|   char format[256];
 | |
|   if (ULONG_MAX > UINT_MAX) {
 | |
|     std::sprintf(format, "{0:.%lu", INT_MAX + 1l);
 | |
|     EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
 | |
|     std::sprintf(format, "{0:.%lu}", UINT_MAX + 1l);
 | |
|     EXPECT_THROW_MSG(Format(format) << 0,
 | |
|         FormatError, "number is too big in format");
 | |
|   } else {
 | |
|     std::sprintf(format, "{0:.%u0", UINT_MAX);
 | |
|     EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
 | |
|     std::sprintf(format, "{0:.%u0}", UINT_MAX);
 | |
|     EXPECT_THROW_MSG(Format(format) << 0,
 | |
|         FormatError, "number is too big in format");
 | |
|   }
 | |
| 
 | |
|   std::sprintf(format, "{0:.%u", INT_MAX + 1u);
 | |
|   EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
 | |
|   std::sprintf(format, "{0:.%u}", INT_MAX + 1u);
 | |
|   EXPECT_THROW_MSG(Format(format) << 0,
 | |
|       FormatError, "number is too big in format");
 | |
| 
 | |
|   EXPECT_THROW_MSG(Format("{0:.") << 0,
 | |
|       FormatError, "unmatched '{' in format");
 | |
|   EXPECT_THROW_MSG(Format("{0:.}") << 0,
 | |
|       FormatError, "missing precision in format");
 | |
|   EXPECT_THROW_MSG(Format("{0:.2") << 0,
 | |
|       FormatError, "unmatched '{' in format");
 | |
| 
 | |
|   EXPECT_THROW_MSG(Format("{0:.2}") << 42,
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
|   EXPECT_EQ("42.00", str(Format("{0:.2f}") << 42));
 | |
|   EXPECT_THROW_MSG(Format("{0:.2}") << 42u,
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
|   EXPECT_EQ("42.00", str(Format("{0:.2f}") << 42u));
 | |
|   EXPECT_THROW_MSG(Format("{0:.2}") << 42l,
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
|   EXPECT_EQ("42.00", str(Format("{0:.2f}") << 42l));
 | |
|   EXPECT_THROW_MSG(Format("{0:.2}") << 42ul,
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
|   EXPECT_EQ("42.00", str(Format("{0:.2f}") << 42ul));
 | |
|   EXPECT_EQ("1.2", str(Format("{0:.2}") << 1.2345));
 | |
|   EXPECT_EQ("1.2", str(Format("{0:.2}") << 1.2345l));
 | |
| 
 | |
|   EXPECT_THROW_MSG(Format("{0:.2}") << reinterpret_cast<void*>(0xcafe),
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
|   EXPECT_THROW_MSG(Format("{0:.2f}") << reinterpret_cast<void*>(0xcafe),
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
| 
 | |
|   EXPECT_THROW_MSG(Format("{0:.2}") << 'x',
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
|   EXPECT_THROW_MSG(Format("{0:.2f}") << 'x',
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
| 
 | |
|   EXPECT_THROW_MSG(Format("{0:.2}") << "str",
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
|   EXPECT_THROW_MSG(Format("{0:.2f}") << "str",
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
| 
 | |
|   EXPECT_THROW_MSG(Format("{0:.2}") << L"str",
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
|   EXPECT_THROW_MSG(Format("{0:.2f}") << L"str",
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
| 
 | |
|   EXPECT_THROW_MSG(Format("{0:.2}") << TestString(),
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
|   EXPECT_THROW_MSG(Format("{0:.2f}") << TestString(),
 | |
|       FormatError, "precision specifier requires floating-point type");
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, FormatInt) {
 | |
|   EXPECT_EQ("42", str(Format("{0}") << 42));
 | |
|   EXPECT_EQ("-1234", str(Format("{0}") << -1234));
 | |
|   std::ostringstream os;
 | |
|   os << INT_MIN;
 | |
|   EXPECT_EQ(os.str(), str(Format("{0}") << INT_MIN));
 | |
|   os.str(std::string());
 | |
|   os << INT_MAX;
 | |
|   EXPECT_EQ(os.str(), str(Format("{0}") << INT_MAX));
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, FormatChar) {
 | |
|   EXPECT_EQ("a*b", str(Format("{0}{1}{2}") << 'a' << '*' << 'b'));
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, FormatString) {
 | |
|   EXPECT_EQ("test", str(Format("{0}") << std::string("test")));
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, FormatCustomArg) {
 | |
|   EXPECT_EQ("a string", str(Format("{0}") << TestString("a string")));
 | |
| }
 | |
| 
 | |
| TEST(FormatterTest, FormatStringFromSpeedTest) {
 | |
|   EXPECT_EQ("1.2340000000:0042:+3.13:str:0x3e8:X:%",
 | |
|       str(Format("{0:0.10f}:{1:04}:{2:+g}:{3}:{4}:{5}:%")
 | |
|           << 1.234 << 42 << 3.13 << "str"
 | |
|           << reinterpret_cast<void*>(1000) << 'X'));
 | |
| }
 | |
| 
 | |
| // TODO: more tests
 |