Commit 1a8ecf18 by Abseil Team Committed by Derek Mauro

Googletest export

Print std::u8string, std::u16string, and std::u32string as string literals Previously, these types were printed as "{ U+123, U+456, U+789 }". However, printed output in that form is difficult to compare against any literals that might be defined in code. Instead, just treat these types like std::string and std::wstring, escaping non-ASCII characters with a hexadecimal escape sequence. The tests have also been updated to cover the new functionality: as a bonus, the tests now also pass with the MSVC toolchain. Internally, the code has been reorganized to primarily operate in terms of char32_t, under the assumption that char32_t will always be at least as big as wchar_t. While that assumption is currently true, perhaps it won't be in the future... PiperOrigin-RevId: 364033132
parent 3ff1e8b9
......@@ -44,6 +44,14 @@ config_setting(
)
config_setting(
name = "msvc_compiler",
flag_values = {
"@bazel_tools//tools/cpp:compiler": "msvc-cl",
},
visibility = [":__subpackages__"],
)
config_setting(
name = "has_absl",
values = {"define": "absl=1"},
)
......
......@@ -81,6 +81,8 @@ macro(config_compiler_and_linker)
# Suppress "unreachable code" warning
# http://stackoverflow.com/questions/3232669 explains the issue.
set(cxx_base_flags "${cxx_base_flags} -wd4702")
# Ensure MSVC treats source files as UTF-8 encoded.
set(cxx_base_flags "${cxx_base_flags} -utf-8")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(cxx_base_flags "-Wall -Wshadow -Werror -Wconversion")
set(cxx_exception_flags "-fexceptions")
......
......@@ -505,24 +505,21 @@ inline void PrintTo(unsigned char* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
}
#ifdef __cpp_char8_t
inline void PrintTo(const char8_t* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
}
// Overloads for u8 strings.
void PrintTo(const char8_t* s, ::std::ostream* os);
inline void PrintTo(char8_t* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
PrintTo(ImplicitCast_<const char8_t*>(s), os);
}
#endif
inline void PrintTo(const char16_t* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
}
// Overloads for u16 strings.
void PrintTo(const char16_t* s, ::std::ostream* os);
inline void PrintTo(char16_t* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
}
inline void PrintTo(const char32_t* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
PrintTo(ImplicitCast_<const char16_t*>(s), os);
}
// Overloads for u32 strings.
void PrintTo(const char32_t* s, ::std::ostream* os);
inline void PrintTo(char32_t* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
PrintTo(ImplicitCast_<const char32_t*>(s), os);
}
// MSVC can be configured to define wchar_t as a typedef of unsigned
......@@ -558,6 +555,26 @@ inline void PrintTo(const ::std::string& s, ::std::ostream* os) {
PrintStringTo(s, os);
}
// Overloads for ::std::u8string
#ifdef __cpp_char8_t
GTEST_API_ void PrintU8StringTo(const ::std::u8string& s, ::std::ostream* os);
inline void PrintTo(const ::std::u8string& s, ::std::ostream* os) {
PrintU8StringTo(s, os);
}
#endif
// Overloads for ::std::u16string
GTEST_API_ void PrintU16StringTo(const ::std::u16string& s, ::std::ostream* os);
inline void PrintTo(const ::std::u16string& s, ::std::ostream* os) {
PrintU16StringTo(s, os);
}
// Overloads for ::std::u32string
GTEST_API_ void PrintU32StringTo(const ::std::u32string& s, ::std::ostream* os);
inline void PrintTo(const ::std::u32string& s, ::std::ostream* os) {
PrintU32StringTo(s, os);
}
// Overloads for ::std::wstring.
#if GTEST_HAS_STD_WSTRING
GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os);
......@@ -805,6 +822,20 @@ void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
GTEST_API_ void UniversalPrintArray(
const char* begin, size_t len, ::std::ostream* os);
#ifdef __cpp_char8_t
// This overload prints a (const) char8_t array compactly.
GTEST_API_ void UniversalPrintArray(const char8_t* begin, size_t len,
::std::ostream* os);
#endif
// This overload prints a (const) char16_t array compactly.
GTEST_API_ void UniversalPrintArray(const char16_t* begin, size_t len,
::std::ostream* os);
// This overload prints a (const) char32_t array compactly.
GTEST_API_ void UniversalPrintArray(const char32_t* begin, size_t len,
::std::ostream* os);
// This overload prints a (const) wchar_t array compactly.
GTEST_API_ void UniversalPrintArray(
const wchar_t* begin, size_t len, ::std::ostream* os);
......@@ -877,12 +908,55 @@ class UniversalTersePrinter<const char*> {
}
};
template <>
class UniversalTersePrinter<char*> {
class UniversalTersePrinter<char*> : public UniversalTersePrinter<const char*> {
};
#ifdef __cpp_char8_t
template <>
class UniversalTersePrinter<const char8_t*> {
public:
static void Print(const char8_t* str, ::std::ostream* os) {
if (str == nullptr) {
*os << "NULL";
} else {
UniversalPrint(::std::u8string(str), os);
}
}
};
template <>
class UniversalTersePrinter<char8_t*>
: public UniversalTersePrinter<const char8_t*> {};
#endif
template <>
class UniversalTersePrinter<const char16_t*> {
public:
static void Print(const char16_t* str, ::std::ostream* os) {
if (str == nullptr) {
*os << "NULL";
} else {
UniversalPrint(::std::u16string(str), os);
}
}
};
template <>
class UniversalTersePrinter<char16_t*>
: public UniversalTersePrinter<const char16_t*> {};
template <>
class UniversalTersePrinter<const char32_t*> {
public:
static void Print(char* str, ::std::ostream* os) {
UniversalTersePrinter<const char*>::Print(str, os);
static void Print(const char32_t* str, ::std::ostream* os) {
if (str == nullptr) {
*os << "NULL";
} else {
UniversalPrint(::std::u32string(str), os);
}
}
};
template <>
class UniversalTersePrinter<char32_t*>
: public UniversalTersePrinter<const char32_t*> {};
#if GTEST_HAS_STD_WSTRING
template <>
......
......@@ -1936,6 +1936,19 @@ inline bool IsUpper(char ch) {
inline bool IsXDigit(char ch) {
return isxdigit(static_cast<unsigned char>(ch)) != 0;
}
#ifdef __cpp_char8_t
inline bool IsXDigit(char8_t ch) {
return isxdigit(static_cast<unsigned char>(ch)) != 0;
}
#endif
inline bool IsXDigit(char16_t ch) {
const unsigned char low_byte = static_cast<unsigned char>(ch);
return ch == low_byte && isxdigit(low_byte) != 0;
}
inline bool IsXDigit(char32_t ch) {
const unsigned char low_byte = static_cast<unsigned char>(ch);
return ch == low_byte && isxdigit(low_byte) != 0;
}
inline bool IsXDigit(wchar_t ch) {
const unsigned char low_byte = static_cast<unsigned char>(ch);
return ch == low_byte && isxdigit(low_byte) != 0;
......
......@@ -83,6 +83,10 @@ cc_test(
copts = select({
"//:windows": ["-DGTEST_USE_OWN_TR1_TUPLE=0"],
"//conditions:default": ["-DGTEST_USE_OWN_TR1_TUPLE=1"],
}) + select({
# Ensure MSVC treats source files as UTF-8 encoded.
"//:msvc_compiler": ["-utf-8"],
"//conditions:default": [],
}),
includes = [
"googletest",
......
......@@ -493,6 +493,92 @@ TEST(PrintCStringTest, EscapesProperly) {
Print(p));
}
#ifdef __cpp_char8_t
// const char8_t*.
TEST(PrintU8StringTest, Const) {
const char8_t* p = u8"界";
EXPECT_EQ(PrintPointer(p) + " pointing to u8\"\\xE7\\x95\\x8C\"", Print(p));
}
// char8_t*.
TEST(PrintU8StringTest, NonConst) {
char8_t p[] = u8"世";
EXPECT_EQ(PrintPointer(p) + " pointing to u8\"\\xE4\\xB8\\x96\"",
Print(static_cast<char8_t*>(p)));
}
// NULL u8 string.
TEST(PrintU8StringTest, Null) {
const char8_t* p = nullptr;
EXPECT_EQ("NULL", Print(p));
}
// Tests that u8 strings are escaped properly.
TEST(PrintU8StringTest, EscapesProperly) {
const char8_t* p = u8"'\"?\\\a\b\f\n\r\t\v\x7F\xFF hello 世界";
EXPECT_EQ(PrintPointer(p) +
" pointing to u8\"'\\\"?\\\\\\a\\b\\f\\n\\r\\t\\v\\x7F\\xFF "
"hello \\xE4\\xB8\\x96\\xE7\\x95\\x8C\"",
Print(p));
}
#endif
// const char16_t*.
TEST(PrintU16StringTest, Const) {
const char16_t* p = u"界";
EXPECT_EQ(PrintPointer(p) + " pointing to u\"\\x754C\"", Print(p));
}
// char16_t*.
TEST(PrintU16StringTest, NonConst) {
char16_t p[] = u"世";
EXPECT_EQ(PrintPointer(p) + " pointing to u\"\\x4E16\"",
Print(static_cast<char16_t*>(p)));
}
// NULL u16 string.
TEST(PrintU16StringTest, Null) {
const char16_t* p = nullptr;
EXPECT_EQ("NULL", Print(p));
}
// Tests that u16 strings are escaped properly.
TEST(PrintU16StringTest, EscapesProperly) {
const char16_t* p = u"'\"?\\\a\b\f\n\r\t\v\x7F\xFF hello 世界";
EXPECT_EQ(PrintPointer(p) +
" pointing to u\"'\\\"?\\\\\\a\\b\\f\\n\\r\\t\\v\\x7F\\xFF "
"hello \\x4E16\\x754C\"",
Print(p));
}
// const char32_t*.
TEST(PrintU32StringTest, Const) {
const char32_t* p = U"🗺️";
EXPECT_EQ(PrintPointer(p) + " pointing to U\"\\x1F5FA\\xFE0F\"", Print(p));
}
// char32_t*.
TEST(PrintU32StringTest, NonConst) {
char32_t p[] = U"🌌";
EXPECT_EQ(PrintPointer(p) + " pointing to U\"\\x1F30C\"",
Print(static_cast<char32_t*>(p)));
}
// NULL u32 string.
TEST(PrintU32StringTest, Null) {
const char32_t* p = nullptr;
EXPECT_EQ("NULL", Print(p));
}
// Tests that u32 strings are escaped properly.
TEST(PrintU32StringTest, EscapesProperly) {
const char32_t* p = U"'\"?\\\a\b\f\n\r\t\v\x7F\xFF hello 🗺️";
EXPECT_EQ(PrintPointer(p) +
" pointing to U\"'\\\"?\\\\\\a\\b\\f\\n\\r\\t\\v\\x7F\\xFF "
"hello \\x1F5FA\\xFE0F\"",
Print(p));
}
// MSVC compiler can be configured to define whar_t as a typedef
// of unsigned short. Defining an overload for const wchar_t* in that case
// would cause pointers to unsigned shorts be printed as wide strings,
......@@ -564,56 +650,6 @@ TEST(PrintCharPointerTest, ConstUnsignedChar) {
EXPECT_EQ("NULL", Print(p));
}
#ifdef __cpp_char8_t
// char8_t*
TEST(PrintCharPointerTest, Char8) {
char8_t* p = reinterpret_cast<char8_t*>(0x1234);
EXPECT_EQ(PrintPointer(p), Print(p));
p = nullptr;
EXPECT_EQ("NULL", Print(p));
}
// const char8_t*
TEST(PrintCharPointerTest, ConstChar8) {
const char8_t* p = reinterpret_cast<const char8_t*>(0x1234);
EXPECT_EQ(PrintPointer(p), Print(p));
p = nullptr;
EXPECT_EQ("NULL", Print(p));
}
#endif
// char16_t*
TEST(PrintCharPointerTest, Char16) {
char16_t* p = reinterpret_cast<char16_t*>(0x1234);
EXPECT_EQ(PrintPointer(p), Print(p));
p = nullptr;
EXPECT_EQ("NULL", Print(p));
}
// const char16_t*
TEST(PrintCharPointerTest, ConstChar16) {
const char16_t* p = reinterpret_cast<const char16_t*>(0x1234);
EXPECT_EQ(PrintPointer(p), Print(p));
p = nullptr;
EXPECT_EQ("NULL", Print(p));
}
// char32_t*
TEST(PrintCharPointerTest, Char32) {
char32_t* p = reinterpret_cast<char32_t*>(0x1234);
EXPECT_EQ(PrintPointer(p), Print(p));
p = nullptr;
EXPECT_EQ("NULL", Print(p));
}
// const char32_t*
TEST(PrintCharPointerTest, ConstChar32) {
const char32_t* p = reinterpret_cast<const char32_t*>(0x1234);
EXPECT_EQ(PrintPointer(p), Print(p));
p = nullptr;
EXPECT_EQ("NULL", Print(p));
}
// Tests printing pointers to simple, built-in types.
// bool*.
......@@ -753,62 +789,68 @@ TEST(PrintArrayTest, CharArrayWithNoTerminatingNul) {
EXPECT_EQ("\"H\\0i\" (no terminating NUL)", PrintArrayHelper(a));
}
// const char array with terminating NUL.
TEST(PrintArrayTest, ConstCharArrayWithTerminatingNul) {
// char array with terminating NUL.
TEST(PrintArrayTest, CharArrayWithTerminatingNul) {
const char a[] = "\0Hi";
EXPECT_EQ("\"\\0Hi\"", PrintArrayHelper(a));
}
// const wchar_t array without terminating NUL.
TEST(PrintArrayTest, WCharArrayWithNoTerminatingNul) {
#ifdef __cpp_char8_t
// char_t array without terminating NUL.
TEST(PrintArrayTest, Char8ArrayWithNoTerminatingNul) {
// Array a contains '\0' in the middle and doesn't end with '\0'.
const wchar_t a[] = { L'H', L'\0', L'i' };
EXPECT_EQ("L\"H\\0i\" (no terminating NUL)", PrintArrayHelper(a));
const char8_t a[] = {u8'H', u8'\0', u8'i'};
EXPECT_EQ("u8\"H\\0i\" (no terminating NUL)", PrintArrayHelper(a));
}
// wchar_t array with terminating NUL.
TEST(PrintArrayTest, WConstCharArrayWithTerminatingNul) {
const wchar_t a[] = L"\0Hi";
EXPECT_EQ("L\"\\0Hi\"", PrintArrayHelper(a));
}
#ifdef __cpp_char8_t
// char8_t array.
TEST(PrintArrayTest, Char8Array) {
const char8_t a[] = u8"Hello, world!";
// char8_t array with terminating NUL.
TEST(PrintArrayTest, Char8ArrayWithTerminatingNul) {
const char8_t a[] = u8"\0世界";
EXPECT_EQ(
"{ U+0048, U+0065, U+006C, U+006C, U+006F, U+002C, U+0020, U+0077, "
"U+006F, U+0072, U+006C, U+0064, U+0021, U+0000 }",
"u8\"\\0\\xE4\\xB8\\x96\\xE7\\x95\\x8C\"",
PrintArrayHelper(a));
}
#endif
// char16_t array.
#ifdef _MSC_VER
// TODO(b/173029407): Figure out why this doesn't work under MSVC.
TEST(PrintArrayTest, DISABLED_Char16Array) {
#else
TEST(PrintArrayTest, Char16Array) {
#endif
const char16_t a[] = u"Hello, 世界";
EXPECT_EQ(
"{ U+0048, U+0065, U+006C, U+006C, U+006F, U+002C, U+0020, U+4E16, "
"U+754C, U+0000 }",
PrintArrayHelper(a));
// const char16_t array without terminating NUL.
TEST(PrintArrayTest, Char16ArrayWithNoTerminatingNul) {
// Array a contains '\0' in the middle and doesn't end with '\0'.
const char16_t a[] = {u'こ', u'\0', u'ん', u'に', u'ち', u'は'};
EXPECT_EQ("u\"\\x3053\\0\\x3093\\x306B\\x3061\\x306F\" (no terminating NUL)",
PrintArrayHelper(a));
}
// char32_t array.
#ifdef _MSC_VER
// TODO(b/173029407): Figure out why this doesn't work under MSVC.
TEST(PrintArrayTest, DISABLED_Char32Array) {
#else
TEST(PrintArrayTest, Char32Array) {
#endif
const char32_t a[] = U"Hello, 世界";
EXPECT_EQ(
"{ U+0048, U+0065, U+006C, U+006C, U+006F, U+002C, U+0020, U+4E16, "
"U+754C, U+0000 }",
PrintArrayHelper(a));
// char16_t array with terminating NUL.
TEST(PrintArrayTest, Char16ArrayWithTerminatingNul) {
const char16_t a[] = u"\0こんにちは";
EXPECT_EQ("u\"\\0\\x3053\\x3093\\x306B\\x3061\\x306F\"", PrintArrayHelper(a));
}
// char32_t array without terminating NUL.
TEST(PrintArrayTest, Char32ArrayWithNoTerminatingNul) {
// Array a contains '\0' in the middle and doesn't end with '\0'.
const char32_t a[] = {U'👋', U'\0', U'🌌'};
EXPECT_EQ("U\"\\x1F44B\\0\\x1F30C\" (no terminating NUL)",
PrintArrayHelper(a));
}
// char32_t array with terminating NUL.
TEST(PrintArrayTest, Char32ArrayWithTerminatingNul) {
const char32_t a[] = U"\0👋🌌";
EXPECT_EQ("U\"\\0\\x1F44B\\x1F30C\"", PrintArrayHelper(a));
}
// wchar_t array without terminating NUL.
TEST(PrintArrayTest, WCharArrayWithNoTerminatingNul) {
// Array a contains '\0' in the middle and doesn't end with '\0'.
const wchar_t a[] = {L'H', L'\0', L'i'};
EXPECT_EQ("L\"H\\0i\" (no terminating NUL)", PrintArrayHelper(a));
}
// wchar_t array with terminating NUL.
TEST(PrintArrayTest, WCharArrayWithTerminatingNul) {
const wchar_t a[] = L"\0Hi";
EXPECT_EQ("L\"\\0Hi\"", PrintArrayHelper(a));
}
// Array of objects.
......@@ -872,41 +914,22 @@ TEST(PrintWideStringTest, StringAmbiguousHex) {
#ifdef __cpp_char8_t
TEST(PrintStringTest, U8String) {
std::u8string str = u8"Hello, world!";
std::u8string str = u8"Hello, 世界";
EXPECT_EQ(str, str); // Verify EXPECT_EQ compiles with this type.
EXPECT_EQ(
"{ U+0048, U+0065, U+006C, U+006C, U+006F, U+002C, U+0020, U+0077, "
"U+006F, U+0072, U+006C, U+0064, U+0021 }",
Print(str));
EXPECT_EQ("u8\"Hello, \\xE4\\xB8\\x96\\xE7\\x95\\x8C\"", Print(str));
}
#endif
#ifdef _MSC_VER
// TODO(b/173029407): Figure out why this doesn't work under MSVC.
TEST(PrintStringTest, DISABLED_U16String) {
#else
TEST(PrintStringTest, U16String) {
#endif
std::u16string str = u"Hello, 世界";
EXPECT_EQ(str, str); // Verify EXPECT_EQ compiles with this type.
EXPECT_EQ(
"{ U+0048, U+0065, U+006C, U+006C, U+006F, U+002C, U+0020, U+4E16, "
"U+754C }",
Print(str));
EXPECT_EQ("u\"Hello, \\x4E16\\x754C\"", Print(str));
}
#ifdef _MSC_VER
// TODO(b/173029407): Figure out why this doesn't work under MSVC.
TEST(PrintStringTest, DISABLED_U32String) {
#else
TEST(PrintStringTest, U32String) {
#endif
std::u32string str = U"Hello, 世界";
EXPECT_EQ(str, str); // Verify EXPECT_EQ compiles with this type.
EXPECT_EQ(
"{ U+0048, U+0065, U+006C, U+006C, U+006F, U+002C, U+0020, U+4E16, "
"U+754C }",
Print(str));
std::u32string str = U"Hello, 🗺️";
EXPECT_EQ(str, str); // Verify EXPECT_EQ compiles with this type
EXPECT_EQ("U\"Hello, \\x1F5FA\\xFE0F\"", Print(str));
}
// Tests printing types that support generic streaming (i.e. streaming
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment