Add initial support for printf format specifications.

This commit is contained in:
Victor Zverovich
2014-06-06 06:38:37 -07:00
parent 7042d14341
commit bf8b29fbe7
8 changed files with 958 additions and 371 deletions

View File

@@ -49,6 +49,7 @@ FILE *FOpen(const char *filename, const char *mode) {
#endif
#include "format.h"
#include "util.h"
#include "gtest-extra.h"
#include <stdint.h>
@@ -110,19 +111,6 @@ struct WriteChecker {
#define CHECK_WRITE_WCHAR(value) \
EXPECT_PRED_FORMAT1(WriteChecker<wchar_t>(), value)
// Increment a number in a string.
void Increment(char *s) {
for (int i = static_cast<int>(std::strlen(s)) - 1; i >= 0; --i) {
if (s[i] != '9') {
++s[i];
break;
}
s[i] = '0';
}
}
enum {BUFFER_SIZE = 256};
#ifdef _MSC_VER
# define vsnprintf vsprintf_s
#endif
@@ -134,234 +122,13 @@ void SPrintf(char *buffer, const char *format, ...) {
va_end(args);
}
std::string ReadFile(fmt::StringRef filename) {
std::string ReadFile(StringRef filename) {
std::ifstream out(filename.c_str());
std::stringstream content;
content << out.rdbuf();
return content.str();
}
std::string GetSystemErrorMessage(int error_code) {
#ifndef _WIN32
return strerror(error_code);
#else
enum { BUFFER_SIZE = 200 };
char buffer[BUFFER_SIZE];
EXPECT_EQ(0, strerror_s(buffer, BUFFER_SIZE, error_code));
std::size_t max_len = BUFFER_SIZE - 1;
EXPECT_LT(std::strlen(buffer), max_len);
return buffer;
#endif
}
}
TEST(UtilTest, Increment) {
char s[10] = "123";
Increment(s);
EXPECT_STREQ("124", s);
s[2] = '8';
Increment(s);
EXPECT_STREQ("129", s);
Increment(s);
EXPECT_STREQ("130", s);
s[1] = s[2] = '9';
Increment(s);
EXPECT_STREQ("200", s);
}
// Tests fmt::internal::CountDigits for integer type Int.
template <typename Int>
void TestCountDigits(Int) {
for (Int i = 0; i < 10; ++i)
EXPECT_EQ(1u, fmt::internal::CountDigits(i));
for (Int i = 1, n = 1,
end = std::numeric_limits<Int>::max() / 10; n <= end; ++i) {
n *= 10;
EXPECT_EQ(i, fmt::internal::CountDigits(n - 1));
EXPECT_EQ(i + 1, fmt::internal::CountDigits(n));
}
}
TEST(UtilTest, CountDigits) {
TestCountDigits(uint32_t());
TestCountDigits(uint64_t());
}
#ifdef _WIN32
TEST(UtilTest, UTF16ToUTF8) {
std::string s = "ёжик";
fmt::internal::UTF16ToUTF8 u(L"\x0451\x0436\x0438\x043A");
EXPECT_EQ(s, fmt::str(u));
EXPECT_EQ(s.size(), u.size());
}
TEST(UtilTest, UTF8ToUTF16) {
std::string s = "лошадка";
fmt::internal::UTF8ToUTF16 u(s.c_str());
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", fmt::str(u));
EXPECT_EQ(7, u.size());
}
template <typename Converter>
void CheckUTFConversionError(const char *message) {
fmt::Writer out;
fmt::internal::FormatWinErrorMessage(out, ERROR_INVALID_PARAMETER, message);
fmt::SystemError error("", 0);
try {
Converter(0);
} catch (const fmt::SystemError &e) {
error = e;
}
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.error_code());
EXPECT_EQ(out.str(), error.what());
}
TEST(UtilTest, UTF16ToUTF8Error) {
CheckUTFConversionError<fmt::internal::UTF16ToUTF8>(
"cannot convert string from UTF-16 to UTF-8");
}
TEST(UtilTest, UTF8ToUTF16Error) {
CheckUTFConversionError<fmt::internal::UTF8ToUTF16>(
"cannot convert string from UTF-8 to UTF-16");
}
TEST(UtilTest, UTF16ToUTF8Convert) {
fmt::internal::UTF16ToUTF8 u;
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.Convert(0));
}
#endif // _WIN32
TEST(UtilTest, StrError) {
using fmt::internal::StrError;
char *message = 0;
char buffer[BUFFER_SIZE];
#ifndef NDEBUG
EXPECT_DEBUG_DEATH(StrError(EDOM, message = 0, 0), "Assertion");
EXPECT_DEBUG_DEATH(StrError(EDOM, message = buffer, 0), "Assertion");
#endif
buffer[0] = 'x';
#ifdef _GNU_SOURCE
// Use invalid error code to make sure that StrError returns an error
// message in the buffer rather than a pointer to a static string.
int error_code = -1;
#else
int error_code = EDOM;
#endif
int result = StrError(error_code, message = buffer, 1);
EXPECT_EQ(buffer, message); // Message should point to buffer.
EXPECT_EQ(ERANGE, result);
EXPECT_STREQ("", message);
result = StrError(error_code, message = buffer, BUFFER_SIZE);
EXPECT_EQ(0, result);
std::size_t message_size = std::strlen(message);
EXPECT_GE(BUFFER_SIZE - 1u, message_size);
EXPECT_EQ(GetSystemErrorMessage(error_code), message);
result = StrError(error_code, message = buffer, message_size);
EXPECT_EQ(ERANGE, result);
}
TEST(UtilTest, SystemError) {
fmt::SystemError e(fmt::StringRef("test"), 42);
EXPECT_STREQ("test", e.what());
EXPECT_EQ(42, e.error_code());
}
typedef void (*FormatErrorMessage)(
fmt::Writer &out, int error_code, fmt::StringRef message);
template <typename Sink>
void CheckErrorSink(int error_code, FormatErrorMessage format) {
fmt::SystemError error("", 0);
Sink sink(error_code);
fmt::Writer w;
w << "test";
try {
sink(w);
} catch (const fmt::SystemError &e) {
error = e;
}
fmt::Writer message;
format(message, error_code, "test");
EXPECT_EQ(str(message), error.what());
EXPECT_EQ(error_code, error.error_code());
}
template <typename Sink>
void CheckThrowError(int error_code, FormatErrorMessage format,
fmt::Formatter<Sink> (*throw_error)(int error_code, StringRef format)) {
fmt::SystemError error("", 0);
try {
throw_error(error_code, "test {}") << "error";
} catch (const fmt::SystemError &e) {
error = e;
}
fmt::Writer message;
format(message, error_code, "test error");
EXPECT_EQ(str(message), error.what());
EXPECT_EQ(error_code, error.error_code());
}
TEST(UtilTest, FormatSystemErrorMessage) {
fmt::Writer message;
fmt::internal::FormatSystemErrorMessage(message, EDOM, "test");
EXPECT_EQ(str(fmt::Format("test: {}")
<< GetSystemErrorMessage(EDOM)), fmt::str(message));
}
TEST(UtilTest, SystemErrorSink) {
CheckErrorSink<fmt::SystemErrorSink>(
EDOM, fmt::internal::FormatSystemErrorMessage);
}
TEST(UtilTest, ThrowSystemError) {
CheckThrowError(EDOM,
fmt::internal::FormatSystemErrorMessage, fmt::ThrowSystemError);
}
TEST(ErrorTest, ReportSystemError) {
fmt::Writer out;
fmt::internal::FormatSystemErrorMessage(out, EDOM, "test error");
out << '\n';
EXPECT_WRITE(stderr, fmt::ReportSystemError(EDOM, "test error"), out.str());
}
#ifdef _WIN32
TEST(UtilTest, FormatWinErrorMessage) {
LPWSTR message = 0;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0);
fmt::internal::UTF16ToUTF8 utf8_message(message);
LocalFree(message);
fmt::Writer actual_message;
fmt::internal::FormatWinErrorMessage(
actual_message, ERROR_FILE_EXISTS, "test");
EXPECT_EQ(str(fmt::Format("test: {}") << fmt::str(utf8_message)),
fmt::str(actual_message));
}
TEST(UtilTest, WinErrorSink) {
CheckErrorSink<fmt::WinErrorSink>(
ERROR_FILE_EXISTS, fmt::internal::FormatWinErrorMessage);
}
TEST(UtilTest, ThrowWinError) {
CheckThrowError(ERROR_FILE_EXISTS,
fmt::internal::FormatWinErrorMessage, fmt::ThrowWinError);
}
TEST(ErrorTest, ReportWinError) {
fmt::Writer out;
fmt::internal::FormatWinErrorMessage(out, ERROR_FILE_EXISTS, "test error");
out << '\n';
EXPECT_WRITE(stderr,
fmt::ReportWinError(ERROR_FILE_EXISTS, "test error"), out.str());
}
#endif // _WIN32
class TestString {
private:
@@ -849,18 +616,15 @@ TEST(FormatterTest, ArgErrors) {
"argument index is out of range in format");
char format[BUFFER_SIZE];
SPrintf(format, "{%u", UINT_MAX);
SPrintf(format, "{%u", INT_MAX);
EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
SPrintf(format, "{%u}", UINT_MAX);
SPrintf(format, "{%u}", INT_MAX);
EXPECT_THROW_MSG(Format(format), FormatError,
"argument index is out of range in format");
SPrintf(format, "{%u", UINT_MAX);
Increment(format + 1);
SPrintf(format, "{%u", INT_MAX + 1u);
EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format");
std::size_t size = std::strlen(format);
format[size] = '}';
format[size + 1] = 0;
SPrintf(format, "{%u}", INT_MAX + 1u);
EXPECT_THROW_MSG(Format(format), FormatError, "number is too big in format");
}