summaryrefslogtreecommitdiffstats
path: root/ppapi/tests/test_case.h
diff options
context:
space:
mode:
authordmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-24 01:10:23 +0000
committerdmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-24 01:10:23 +0000
commit6d8528630b4841afa5a9b0c09be17d9f74d779db (patch)
treea7d2a4d2eec7887003e1eed884ff6e760aa4d0a1 /ppapi/tests/test_case.h
parent71c10c5ae000e72154770f4e467928a15a05297a (diff)
downloadchromium_src-6d8528630b4841afa5a9b0c09be17d9f74d779db.zip
chromium_src-6d8528630b4841afa5a9b0c09be17d9f74d779db.tar.gz
chromium_src-6d8528630b4841afa5a9b0c09be17d9f74d779db.tar.bz2
PPAPI: Make ASSERT macros print values of operands
With this patch, if one of our ASSERT macros fails, the error string will contain the expected and actual values. I made a browser test for it. I would prefer a unit test, but I didn't want to have to mix test_case.h with our unit tests. BUG=None Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=246129 Review URL: https://codereview.chromium.org/123463007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@246757 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi/tests/test_case.h')
-rw-r--r--ppapi/tests/test_case.h369
1 files changed, 358 insertions, 11 deletions
diff --git a/ppapi/tests/test_case.h b/ppapi/tests/test_case.h
index 0a96c9b..948d2b7 100644
--- a/ppapi/tests/test_case.h
+++ b/ppapi/tests/test_case.h
@@ -9,6 +9,7 @@
#include <limits>
#include <map>
#include <set>
+#include <sstream>
#include <string>
#include "ppapi/c/pp_resource.h"
@@ -273,6 +274,326 @@ class TestCaseFactory {
static TestCaseFactory* head_;
};
+namespace internal {
+
+// The internal namespace contains implementation details that are used by
+// the ASSERT macros.
+
+// This base class provides a ToString that works for classes that can be
+// converted to a string using std::stringstream. Later, we'll do
+// specializations for types that we know will work with this approach.
+template <class T>
+struct StringinatorBase {
+ static std::string ToString(const T& value) {
+ std::stringstream stream;
+ stream << value;
+ return stream.str();
+ }
+ protected:
+ // Not implemented, do not use.
+ // Note, these are protected because Windows complains if I make these private
+ // and then inherit StringinatorBase (even though they're never used).
+ StringinatorBase();
+ ~StringinatorBase();
+};
+
+// This default class template is for types that we don't recognize as
+// something we can convert into a string using stringstream. Types that we
+// know *can* be turned to a string should have specializations below.
+template <class T>
+struct Stringinator {
+ static std::string ToString(const T& value) {
+ return std::string();
+ }
+ private:
+ // Not implemented, do not use.
+ Stringinator();
+ ~Stringinator();
+};
+
+// Define some full specializations for types that can just use stringstream.
+#define DEFINE_STRINGINATOR_FOR_TYPE(type) \
+template <> \
+struct Stringinator<type> : public StringinatorBase<type> {};
+DEFINE_STRINGINATOR_FOR_TYPE(int32_t);
+DEFINE_STRINGINATOR_FOR_TYPE(uint32_t);
+DEFINE_STRINGINATOR_FOR_TYPE(int64_t);
+DEFINE_STRINGINATOR_FOR_TYPE(uint64_t);
+DEFINE_STRINGINATOR_FOR_TYPE(float);
+DEFINE_STRINGINATOR_FOR_TYPE(double);
+DEFINE_STRINGINATOR_FOR_TYPE(bool);
+DEFINE_STRINGINATOR_FOR_TYPE(std::string);
+#undef DEFINE_STRINGINATOR_FOR_TYPE
+
+template <class T>
+std::string ToString(const T& param) {
+ return Stringinator<T>::ToString(param);
+}
+
+// This overload is necessary to allow enum values (such as those from
+// pp_errors.h, including PP_OK) to work. They won't automatically convert to
+// an integral type to instantiate the above function template.
+inline std::string ToString(int32_t param) {
+ return Stringinator<int32_t>::ToString(param);
+}
+
+inline std::string ToString(const char* c_string) {
+ return std::string(c_string);
+}
+
+// This overload deals with pointers.
+template <class T>
+std::string ToString(const T* ptr) {
+ uintptr_t ptr_val = reinterpret_cast<uintptr_t>(ptr);
+ std::stringstream stream;
+ stream << ptr_val;
+ return stream.str();
+}
+
+// ComparisonHelper classes wrap the left-hand parameter of a binary comparison
+// ASSERT. The correct class gets chosen based on whether or not it's a NULL or
+// 0 literal. If it is a NULL/0 literal, we use NullLiteralComparisonHelper.
+// For all other parameters, we use ComparisonHelper. There's also a
+// specialization of ComparisonHelper for int below (see below for why
+// that is.)
+//
+// ComparisonHelper does two things for the left param:
+// 1) Provides all the appropriate CompareXX functions (CompareEQ, etc).
+// 2) Provides ToString.
+template <class T>
+struct ComparisonHelper {
+ explicit ComparisonHelper(const T& param) : value(param) {}
+ template <class U>
+ bool CompareEQ(const U& right) const {
+ return value == right;
+ }
+ template <class U>
+ bool CompareNE(const U& right) const {
+ return value != right;
+ }
+ template <class U>
+ bool CompareLT(const U& right) const {
+ return value < right;
+ }
+ template <class U>
+ bool CompareGT(const U& right) const {
+ return value > right;
+ }
+ template <class U>
+ bool CompareLE(const U& right) const {
+ return value <= right;
+ }
+ template <class U>
+ bool CompareGE(const U& right) const {
+ return value >= right;
+ }
+ std::string ToString() const {
+ return internal::ToString(value);
+ }
+ const T& value;
+};
+
+// Used for NULL or 0.
+struct NullLiteralComparisonHelper {
+ NullLiteralComparisonHelper() : value(0) {}
+ template <class U>
+ bool CompareEQ(const U& right) const {
+ return 0 == right;
+ }
+ template <class U>
+ bool CompareNE(const U& right) const {
+ return 0 != right;
+ }
+ template <class U>
+ bool CompareLT(const U& right) const {
+ return 0 < right;
+ }
+ template <class U>
+ bool CompareGT(const U& right) const {
+ return 0 > right;
+ }
+ template <class U>
+ bool CompareLE(const U& right) const {
+ return 0 <= right;
+ }
+ template <class U>
+ bool CompareGE(const U& right) const {
+ return 0 >= right;
+ }
+ std::string ToString() const {
+ return std::string("0");
+ }
+ const int value;
+};
+
+// This class makes it safe to use an integer literal (like 5, or 123) when
+// comparing with an unsigned. For example:
+// ASSERT_EQ(1, some_vector.size());
+// We do a lot of those comparisons, so this makes it easy to get it right
+// (rather than forcing assertions to use unsigned literals like 5u or 123u).
+//
+// This is slightly risky; we're static_casting an int to whatever's on the
+// right. If the left value is negative and the right hand side is a large
+// unsigned value, it's possible that the comparison will succeed when maybe
+// it shouldn't have.
+// TODO(dmichael): It should be possible to fix this and upgrade int32_t and
+// uint32_t to int64_t for the comparison, and make any unsafe
+// comparisons into compile errors.
+template <>
+struct ComparisonHelper<int> {
+ explicit ComparisonHelper(int param) : value(param) {}
+ template <class U>
+ bool CompareEQ(const U& right) const {
+ return static_cast<U>(value) == right;
+ }
+ template <class U>
+ bool CompareNE(const U& right) const {
+ return static_cast<U>(value) != right;
+ }
+ template <class U>
+ bool CompareLT(const U& right) const {
+ return static_cast<U>(value) < right;
+ }
+ template <class U>
+ bool CompareGT(const U& right) const {
+ return static_cast<U>(value) > right;
+ }
+ template <class U>
+ bool CompareLE(const U& right) const {
+ return static_cast<U>(value) <= right;
+ }
+ template <class U>
+ bool CompareGE(const U& right) const {
+ return static_cast<U>(value) >= right;
+ }
+ std::string ToString() const {
+ return internal::ToString(value);
+ }
+ const int value;
+ private:
+};
+
+// The default is for the case there the parameter is *not* a NULL or 0 literal.
+template <bool is_null_literal>
+struct ParameterWrapper {
+ template <class T>
+ static ComparisonHelper<T> WrapValue(const T& value) {
+ return ComparisonHelper<T>(value);
+ }
+ // This overload is so that we can deal with values from anonymous enums,
+ // like the one in pp_errors.h. The function template above won't be
+ // considered a match by the compiler.
+ static ComparisonHelper<int> WrapValue(int value) {
+ return ComparisonHelper<int>(value);
+ }
+};
+
+// The parameter to WrapValue *is* a NULL or 0 literal.
+template <>
+struct ParameterWrapper<true> {
+ // We just use "..." and ignore the parameter. This sidesteps some problems we
+ // would run in to (not all compilers have the same set of constraints).
+ // - We can't use a pointer type, because int and enums won't convert.
+ // - We can't use an integral type, because pointers won't convert.
+ // - We can't overload, because it will sometimes be ambiguous.
+ // - We can't templatize and deduce the parameter. Some compilers will deduce
+ // int for NULL, and then refuse to convert NULL to an int.
+ //
+ // We know in this case that the value is 0, so there's no need to capture the
+ // value. We also know it's a fundamental type, so it's safe to pass to "...".
+ // (It's illegal to pass non-POD types to ...).
+ static NullLiteralComparisonHelper WrapValue(...) {
+ return NullLiteralComparisonHelper();
+ }
+};
+
+// IS_NULL_LITERAL(type) is a little template metaprogramming for determining
+// if a type is a null or zero literal (NULL or 0 or a constant that evaluates
+// to one of those).
+// The idea is that for NULL or 0, any pointer type is always a better match
+// than "...". But no other pointer types or literals should convert
+// automatically to InternalDummyClass.
+struct InternalDummyClass {};
+char TestNullLiteral(const InternalDummyClass*);
+struct BiggerThanChar { char dummy[2]; };
+BiggerThanChar TestNullLiteral(...);
+// If the compiler chooses the overload of TestNullLiteral which returns char,
+// then we know the value converts automatically to InternalDummyClass*, which
+// should only be true of NULL and 0 constants.
+#define IS_NULL_LITERAL(a) sizeof(internal::TestNullLiteral(a)) == sizeof(char)
+
+template <class T, class U>
+static std::string MakeBinaryComparisonFailureMessage(
+ const char* comparator,
+ const T& left,
+ const U& right,
+ const char* left_precompiler_string,
+ const char* right_precompiler_string,
+ const char* file_name,
+ int line_number) {
+ std::string error_msg =
+ std::string("Failed ASSERT_") + comparator + "(" +
+ left_precompiler_string + ", " + right_precompiler_string + ")";
+ std::string left_string(left.ToString());
+ std::string right_string(ToString(right));
+ if (!left_string.empty())
+ error_msg += " Left: (" + left_string + ")";
+
+ if (!right_string.empty())
+ error_msg += " Right: (" + right_string + ")";
+
+ return TestCase::MakeFailureMessage(file_name, line_number,
+ error_msg.c_str());
+}
+
+// The Comparison function templates allow us to pass the parameter for
+// ASSERT macros below and have them be evaluated only once. This is important
+// for cases where the parameter might be an expression with side-effects, like
+// a function call.
+#define DEFINE_COMPARE_FUNCTION(comparator_name) \
+template <class T, class U> \
+std::string Compare ## comparator_name ( \
+ const T& left, \
+ const U& right, \
+ const char* left_precompiler_string, \
+ const char* right_precompiler_string, \
+ const char* file_name, \
+ int line_num) { \
+ if (!(left.Compare##comparator_name(right))) { \
+ return MakeBinaryComparisonFailureMessage(#comparator_name, \
+ left, \
+ right, \
+ left_precompiler_string, \
+ right_precompiler_string, \
+ file_name, \
+ line_num); \
+ } \
+ return std::string(); \
+}
+DEFINE_COMPARE_FUNCTION(EQ)
+DEFINE_COMPARE_FUNCTION(NE)
+DEFINE_COMPARE_FUNCTION(LT)
+DEFINE_COMPARE_FUNCTION(LE)
+DEFINE_COMPARE_FUNCTION(GT)
+DEFINE_COMPARE_FUNCTION(GE)
+#undef DEFINE_COMPARE_FUNCTION
+inline std::string CompareDoubleEq(ComparisonHelper<double> left,
+ double right,
+ const char* left_precompiler_string,
+ const char* right_precompiler_string,
+ const char* file_name,
+ int linu_num) {
+ if (!(std::fabs(left.value - right) <=
+ std::numeric_limits<double>::epsilon())) {
+ return MakeBinaryComparisonFailureMessage(
+ "~=", left, right, left_precompiler_string, right_precompiler_string,
+ __FILE__, __LINE__);
+ }
+ return std::string();
+}
+
+} // namespace internal
+
// Use the REGISTER_TEST_CASE macro in your TestCase implementation file to
// register your TestCase. If your test is named TestFoo, then add the
// following to test_foo.cc:
@@ -373,22 +694,48 @@ class TestCaseFactory {
return MakeFailureMessage(__FILE__, __LINE__, #cmd); \
} while (false)
#define ASSERT_FALSE(cmd) ASSERT_TRUE(!(cmd))
-#define ASSERT_EQ(a, b) ASSERT_TRUE((a) == (b))
-#define ASSERT_NE(a, b) ASSERT_TRUE((a) != (b))
-#define ASSERT_LT(a, b) ASSERT_TRUE((a) < (b))
-#define ASSERT_LE(a, b) ASSERT_TRUE((a) <= (b))
-#define ASSERT_GT(a, b) ASSERT_TRUE((a) > (b))
-#define ASSERT_GE(a, b) ASSERT_TRUE((a) >= (b))
-
-#define ASSERT_DOUBLE_EQ(a, b) ASSERT_TRUE( \
- std::fabs((a)-(b)) <= std::numeric_limits<double>::epsilon())
-
+#define COMPARE_BINARY_INTERNAL(comparison_type, a, b) \
+ internal::Compare##comparison_type( \
+ internal::ParameterWrapper<IS_NULL_LITERAL(a)>::WrapValue(a), \
+ (b), \
+ #a, \
+ #b, \
+ __FILE__, \
+ __LINE__)
+#define ASSERT_BINARY_INTERNAL(comparison_type, a, b) \
+do { \
+ std::string internal_assert_result_string = \
+ COMPARE_BINARY_INTERNAL(comparison_type, a, b); \
+ if (!internal_assert_result_string.empty()) { \
+ return internal_assert_result_string; \
+ } \
+} while(false)
+#define ASSERT_EQ(a, b) ASSERT_BINARY_INTERNAL(EQ, a, b)
+#define ASSERT_NE(a, b) ASSERT_BINARY_INTERNAL(NE, a, b)
+#define ASSERT_LT(a, b) ASSERT_BINARY_INTERNAL(LT, a, b)
+#define ASSERT_LE(a, b) ASSERT_BINARY_INTERNAL(LE, a, b)
+#define ASSERT_GT(a, b) ASSERT_BINARY_INTERNAL(GT, a, b)
+#define ASSERT_GE(a, b) ASSERT_BINARY_INTERNAL(GE, a, b)
+#define ASSERT_DOUBLE_EQ(a, b) \
+do { \
+ std::string internal_assert_result_string = \
+ internal::CompareDoubleEq( \
+ internal::ParameterWrapper<IS_NULL_LITERAL(a)>::WrapValue(a), \
+ (b), \
+ #a, \
+ #b, \
+ __FILE__, \
+ __LINE__); \
+ if (!internal_assert_result_string.empty()) { \
+ return internal_assert_result_string; \
+ } \
+} while(false)
// Runs |function| as a subtest and asserts that it has passed.
#define ASSERT_SUBTEST_SUCCESS(function) \
do { \
std::string result = (function); \
if (!result.empty()) \
- return result; \
+ return TestCase::MakeFailureMessage(__FILE__, __LINE__, result.c_str()); \
} while (false)
#define PASS() return std::string()