Add a PrintTo function for rtc::Optional to aid with testing.
gtest can print objects if they have an operator<< or a PrintTo function in the same namespace as the object's class. Since std::optional does not seem to have an operator<<, it'd be preferable not to rely on rtc::Optional being printable through operator<<. Currently, gtest errors will just dump the raw bytes of rtc::Optionals, which make them really annoying to work with in tests. BUG=webrtc:7196 Review-Url: https://codereview.webrtc.org/2704483002 Cr-Commit-Position: refs/heads/master@{#16717}
This commit is contained in:
parent
6bb8e0efd3
commit
e5c27a5db6
@ -15,6 +15,11 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#include <iomanip>
|
||||
#include <ostream>
|
||||
#endif // UNIT_TEST
|
||||
|
||||
#include "webrtc/base/array_view.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/sanitizer.h"
|
||||
@ -25,7 +30,9 @@ namespace optional_internal {
|
||||
|
||||
#if RTC_HAS_ASAN
|
||||
|
||||
// This is a non-inlined function. The optimizer can't see inside it.
|
||||
// This is a non-inlined function. The optimizer can't see inside it. It
|
||||
// prevents the compiler from generating optimized code that reads value_ even
|
||||
// if it is unset. Although safe, this causes memory sanitizers to complain.
|
||||
void* FunctionThatDoesNothingImpl(void*);
|
||||
|
||||
template <typename T>
|
||||
@ -296,6 +303,98 @@ class Optional final {
|
||||
};
|
||||
};
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
namespace optional_internal {
|
||||
|
||||
// Checks if there's a valid PrintTo(const T&, std::ostream*) call for T.
|
||||
template <typename T>
|
||||
struct HasPrintTo {
|
||||
private:
|
||||
struct No {};
|
||||
|
||||
template <typename T2>
|
||||
static auto Test(const T2& obj)
|
||||
-> decltype(PrintTo(obj, std::declval<std::ostream*>()));
|
||||
|
||||
template <typename>
|
||||
static No Test(...);
|
||||
|
||||
public:
|
||||
static constexpr bool value =
|
||||
!std::is_same<decltype(Test<T>(std::declval<const T&>())), No>::value;
|
||||
};
|
||||
|
||||
// Checks if there's a valid operator<<(std::ostream&, const T&) call for T.
|
||||
template <typename T>
|
||||
struct HasOstreamOperator {
|
||||
private:
|
||||
struct No {};
|
||||
|
||||
template <typename T2>
|
||||
static auto Test(const T2& obj)
|
||||
-> decltype(std::declval<std::ostream&>() << obj);
|
||||
|
||||
template <typename>
|
||||
static No Test(...);
|
||||
|
||||
public:
|
||||
static constexpr bool value =
|
||||
!std::is_same<decltype(Test<T>(std::declval<const T&>())), No>::value;
|
||||
};
|
||||
|
||||
// Prefer using PrintTo to print the object.
|
||||
template <typename T>
|
||||
typename std::enable_if<HasPrintTo<T>::value, void>::type OptionalPrintToHelper(
|
||||
const T& value,
|
||||
std::ostream* os) {
|
||||
PrintTo(value, os);
|
||||
}
|
||||
|
||||
// Fall back to operator<<(std::ostream&, ...) if it exists.
|
||||
template <typename T>
|
||||
typename std::enable_if<HasOstreamOperator<T>::value && !HasPrintTo<T>::value,
|
||||
void>::type
|
||||
OptionalPrintToHelper(const T& value, std::ostream* os) {
|
||||
*os << value;
|
||||
}
|
||||
|
||||
inline void OptionalPrintObjectBytes(const unsigned char* bytes,
|
||||
size_t size,
|
||||
std::ostream* os) {
|
||||
*os << "<optional with " << size << "-byte object [";
|
||||
for (size_t i = 0; i != size; ++i) {
|
||||
*os << (i == 0 ? "" : ((i & 1) ? "-" : " "));
|
||||
*os << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<int>(bytes[i]);
|
||||
}
|
||||
*os << "]>";
|
||||
}
|
||||
|
||||
// As a final back-up, just print the contents of the objcets byte-wise.
|
||||
template <typename T>
|
||||
typename std::enable_if<!HasOstreamOperator<T>::value && !HasPrintTo<T>::value,
|
||||
void>::type
|
||||
OptionalPrintToHelper(const T& value, std::ostream* os) {
|
||||
OptionalPrintObjectBytes(reinterpret_cast<const unsigned char*>(&value),
|
||||
sizeof(value), os);
|
||||
}
|
||||
|
||||
} // namespace optional_internal
|
||||
|
||||
// PrintTo is used by gtest to print out the results of tests. We want to ensure
|
||||
// the object contained in an Optional can be printed out if it's set, while
|
||||
// avoiding touching the object's storage if it is undefined.
|
||||
template <typename T>
|
||||
void PrintTo(const rtc::Optional<T>& opt, std::ostream* os) {
|
||||
if (opt) {
|
||||
optional_internal::OptionalPrintToHelper(*opt, os);
|
||||
} else {
|
||||
*os << "<empty optional>";
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UNIT_TEST
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // WEBRTC_BASE_OPTIONAL_H_
|
||||
|
||||
@ -21,6 +21,34 @@ namespace rtc {
|
||||
|
||||
namespace {
|
||||
|
||||
struct MyUnprintableType {
|
||||
int value;
|
||||
};
|
||||
|
||||
struct MyPrintableType {
|
||||
int value;
|
||||
};
|
||||
|
||||
struct MyOstreamPrintableType {
|
||||
int value;
|
||||
};
|
||||
|
||||
void PrintTo(const MyPrintableType& mpt, std::ostream* os) {
|
||||
*os << "The value is " << mpt.value;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const MyPrintableType& mpt) {
|
||||
os << mpt.value;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const MyOstreamPrintableType& mpt) {
|
||||
os << mpt.value;
|
||||
return os;
|
||||
}
|
||||
|
||||
// Class whose instances logs various method calls (constructor, destructor,
|
||||
// etc.). Each instance has a unique ID (a simple global sequence number) and
|
||||
// an origin ID. When a copy is made, the new object gets a fresh ID but copies
|
||||
@ -740,4 +768,32 @@ TEST(OptionalTest, TestMoveValue) {
|
||||
*log);
|
||||
}
|
||||
|
||||
TEST(OptionalTest, TestPrintTo) {
|
||||
constexpr char kEmptyOptionalMessage[] = "<empty optional>";
|
||||
const Optional<MyUnprintableType> empty_unprintable;
|
||||
const Optional<MyPrintableType> empty_printable;
|
||||
const Optional<MyOstreamPrintableType> empty_ostream_printable;
|
||||
EXPECT_EQ(kEmptyOptionalMessage, ::testing::PrintToString(empty_unprintable));
|
||||
EXPECT_EQ(kEmptyOptionalMessage, ::testing::PrintToString(empty_printable));
|
||||
EXPECT_EQ(kEmptyOptionalMessage,
|
||||
::testing::PrintToString(empty_ostream_printable));
|
||||
EXPECT_NE("1", ::testing::PrintToString(Optional<MyUnprintableType>({1})));
|
||||
EXPECT_NE("1", ::testing::PrintToString(Optional<MyPrintableType>({1})));
|
||||
EXPECT_EQ("The value is 1",
|
||||
::testing::PrintToString(Optional<MyPrintableType>({1})));
|
||||
EXPECT_EQ("1",
|
||||
::testing::PrintToString(Optional<MyOstreamPrintableType>({1})));
|
||||
}
|
||||
|
||||
void UnusedFunctionWorkaround() {
|
||||
// These are here to ensure we don't get warnings about ostream and PrintTo
|
||||
// for MyPrintableType never getting called.
|
||||
const MyPrintableType dont_warn{17};
|
||||
const MyOstreamPrintableType dont_warn2{18};
|
||||
std::stringstream sstr;
|
||||
sstr << dont_warn;
|
||||
PrintTo(dont_warn, &sstr);
|
||||
sstr << dont_warn2;
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user