From 5f3ed207da5a2e716177e1ee8e6a9d39c628d8c6 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 7 Dec 2012 17:48:10 -0800 Subject: [PATCH] Add more tests. Check if argument index is in range. --- format.cc | 21 ++++++++++--- format.h | 5 ++- format_test.cc | 84 ++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 86 insertions(+), 24 deletions(-) diff --git a/format.cc b/format.cc index dc0a7f72..79c393e4 100644 --- a/format.cc +++ b/format.cc @@ -11,6 +11,13 @@ using std::size_t; +static void CheckClosingBrace(const char *s) { + while (*s && *s != '}') + ++s; + if (!*s) + throw fmt::FormatError("unmatched '{' in format"); +} + template void fmt::Formatter::FormatArg( const char *format, const T &arg, int width, int precision) { @@ -40,21 +47,25 @@ void fmt::Formatter::Format() { buffer_.reserve(500); const char *start = format_; const char *s = start; - for (; *s; ++s) { - if (*s != '{') continue; - buffer_.insert(buffer_.end(), start, s); - ++s; + while (*s) { + if (*s++ != '{') continue; + buffer_.insert(buffer_.end(), start, s - 1); // Parse argument index. unsigned arg_index = 0; if ('0' <= *s && *s <= '9') { + // TODO: check overflow do { arg_index = arg_index * 10 + (*s++ - '0'); } while ('0' <= *s && *s <= '9'); } else { + CheckClosingBrace(s); throw FormatError("missing argument index in format string"); } - // TODO: check if argument index is correct + if (arg_index >= args_.size()) { + CheckClosingBrace(s); + throw std::out_of_range("argument index is out of range in format"); + } char arg_format[8]; // longest format: %+0*.*ld char *arg_format_ptr = arg_format; diff --git a/format.h b/format.h index 2eb78cca..d4841a0e 100644 --- a/format.h +++ b/format.h @@ -28,7 +28,7 @@ class ArgFormatterWithCallback; // fit all the output. class Formatter { private: - std::vector buffer_; + std::vector buffer_; // Output buffer. enum Type { CHAR, INT, UINT, LONG, ULONG, DOUBLE, LONG_DOUBLE, STRING, WSTRING, POINTER @@ -63,8 +63,7 @@ class Formatter { std::vector args_; - // Pointer to the part of the format string that hasn't been written yet. - const char *format_; + const char *format_; // Format string. friend class ArgFormatter; diff --git a/format_test.cc b/format_test.cc index 41866efd..a366e7a1 100644 --- a/format_test.cc +++ b/format_test.cc @@ -14,29 +14,81 @@ 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_) TEST(FormatterTest, FormatNoArgs) { - Formatter format; - format("test"); - EXPECT_STREQ("test", format.c_str()); + EXPECT_EQ("abracadabra", str(Format("{0}{1}{0}") << "abra" << "cad")); + EXPECT_EQ("test", str(Format("test"))); } -TEST(FormatterTest, FormatComplex) { - EXPECT_STREQ("1.2340000000:0042:+3.13:str:0x3e8:X:%", - c_str(Format("{0:0.10f}:{1:04}:{2:+g}:{3}:{4}:{5}:%") - << 1.234 << 42 << 3.13 << "str" << reinterpret_cast(1000) - << 'X')); - printf("%0.*f:%04d:%+g:%s:%p:%c:%%\n", - 10, 1.234, 42, 3.13, "str", (void*)1000, (int)'X'); - printf("%0.10f:%04d:%+g:%s:%p:%c:%%\n", - 1.234, 42, 3.13, "str", (void*)1000, (int)'X'); +TEST(FormatterTest, FormatArgs) { + 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, InvalidFormat) { + //Format("{"); + EXPECT_THROW_MSG(Format("{}"), FormatError, + "missing argument index in format string"); + EXPECT_THROW_MSG(Format("{"), FormatError, "unmatched '{' in format"); + 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"); + // TODO +} + +TEST(FormatterTest, FormatBig) { + 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(1000) << 'X')); } TEST(FormatterTest, FormatInt) { - EXPECT_STREQ("42", c_str(Format("{0}") << 42)); - EXPECT_STREQ("before 42 after", c_str(Format("before {0} after") << 42)); - printf("%0.10f:%04d:%+g:%s:%p:%c:%%\n", - 1.234, 42, 3.13, "str", (void*)1000, (int)'X'); + EXPECT_EQ("42", str(Format("{0}") << 42)); + EXPECT_EQ("before 42 after", str(Format("before {0} after") << 42)); } // TODO