これまでのテストは、 ASSERT のみで記述されていました。
しかし、それではログから必要な情報を十分に得ることができなかったため、
比較マクロを追加することにしました。
まず、書いたのが以下のようなコード
#define COMPARE_HELPER(op_name, op) \
template<typename T1, typename T2> \
static AssertionResult CmpHelper##op_name( \
const char* expr1, const char* expr2 \
, const T1& val1, const T2& val2) { \
if( val1 op val2 ) return AssertionResult::Success(); \
else { \
return AssertionResult::Failure() \
<< "error: Expected: " << expr1 << " " #op " " << expr2 \
<< "\n Actual: " << val1 << " vs " << val2; \
} \
}
COMPARE_HELPER(EQ, ==)
COMPARE_HELPER(NE, !=)
COMPARE_HELPER(LE, <=)
COMPARE_HELPER(LT, < )
COMPARE_HELPER(GE, >=)
COMPARE_HELPER(GT, > )
ASSERT_EQ(expected, actual) を書くと、CmpHelperEQ 関数が呼ばれるようになってます。
簡単簡単と意気揚々とテストを書くわけですが、これだけではダメです。
TEST(Hoge, foge)
{
int* p = NULL;
// error C2040: '==' : 'const int' は 'int *const ' と間接操作のレベルが異なります。
ASSERT_EQ(NULL, p);
}
※gtest ではエラーになりません。
では、なぜ gtest ではエラーにならないのか、調べてみましょう。
(※アバウトなことしか書いていないです。ご了承ください。)
NULL リテラルか判断する
gtest の ASSERT_EQ マクロの定義を見てみると以下のようになっています。#if !GTEST_DONT_DEFINE_ASSERT_EQ
# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2)
#endif
#define GTEST_ASSERT_EQ(expected, actual) \
ASSERT_PRED_FORMAT2(::testing::internal:: \
EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
expected, actual)
まず、 EqHelper と GTEST_IS_NULL_LITERAL_ が目に入ります。
GTEST_IS_NULL_LITERAL_
見るからに NULL と関係がありそうです。
class Secret;
char IsNullLiteralHelper(Secret* p); // A
char (&IsNullLiteralHelper(...))[2]; // B
# define GTEST_IS_NULL_LITERAL_(x) \
(sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1)
Secret* に変換できるならば A が
それ以外は、B として解釈されます。
すると、
GTEST_IS_NULL_LITERAL_(NULL) は、true です。
スバラシイ!!でも、まだです。
以下は、EqHelper の GTEST_IS_NULL_LITERAL_ が true の場合の実装です。
template <>
class EqHelper<true> {
public:
template <typename T1, typename T2>
static AssertionResult Compare(
const char* expected_expression,
const char* actual_expression,
const T1& expected,
const T2& actual,
typename EnableIf<!is_pointer<T2>::value>::type* = 0) {
// 第二引数がポインター型でない
// 略
}
template <typename T>
static AssertionResult Compare(
const char* expected_expression,
const char* actual_expression,
Secret* /* expected (NULL) */,
T* actual) {
return CmpHelperEQ(expected_expression, actual_expression,
static_cast<T*>(NULL), actual);
}
};
ポインターであれば、2番目の関数が呼ばれます。
2番目の関数では、比較対象のポインター型にキャストしてますので、エラーにならないという寸法です。
いやー、非常に勉強になりますね。
0 件のコメント:
コメントを投稿