nlib
testing/param_type/param_type.cpp
型つけテストのサンプルです。 TYPED_TEST_CASE()マクロとTYPED_TEST()マクロの利用法を解説しています。
サンプルのテストコードは、テスト対象のクラスが群の定義を満たしているかどうかをテストしています。 すなわち、クラスに属する全てのオブジェクトa, b, cに対して以下が成立しているかどうかです。
  • a + (b + c) == (a + b) + c (結合法則)
  • 0 + a == a + 0 (単位元(0)の存在)
  • 0 == a + (-a) (逆元(-a)の存在)
それぞれ、テストコードとしては以下が対応しています。
テスト対象のクラス(型)として、整数4による剰余群(Z4)、4元数群(Q4)、整数256による剰余群(unsigned char)を用意しています。
// define NLIB_USE_GTEST to use googletest
// #define NLIB_USE_GTEST
// Z4: factor group of 'x mod 4'
class Z4 {
public:
Z4() : value_(0) {}
explicit Z4(int x) : value_(x > 0 ? (x & 3) : ((-x) & 3)) {}
bool operator==(const Z4& rhs) const { return value_ == rhs.value_; }
bool operator!=(const Z4& rhs) const { return value_ != rhs.value_; }
Z4& operator+=(const Z4& rhs) {
value_ = (value_ + rhs.value_) & 3;
return *this;
}
int value() const { return value_; }
Z4& operator++() {
value_ = (value_ + 1) & 3;
return *this;
}
private:
int value_;
};
NLIB_TESTING_OSTREAM& operator<<(NLIB_TESTING_OSTREAM& str, const Z4& data) {
str << "Z4(" << data.value() << ")";
return str;
}
Z4 operator+(const Z4& lhs, const Z4& rhs) {
Z4 result(lhs);
result += rhs;
return result;
}
// quaternion group
class Q4 {
public:
enum ValueType {
ONE = 0,
I = 1,
J = 2,
K = 3,
MINUS_ONE = 4,
MINUS_I = 5,
MINUS_J = 6,
MINUS_K = 7
};
Q4() : value_(ONE) {}
explicit Q4(int x) : value_(static_cast<ValueType>(x > 0 ? x % 8 : -x % 8)) {}
bool operator==(const Q4& rhs) const { return value_ == rhs.value_; }
bool operator!=(const Q4& rhs) const { return value_ != rhs.value_; }
Q4& operator+=(const Q4& rhs);
ValueType value() const { return value_; }
Q4& operator++() {
value_ = static_cast<ValueType>((value_ + 1) % 8);
return *this;
}
private:
ValueType value_;
};
Q4& Q4::operator+=(const Q4& rhs) {
// ii = jj == kk = ijk = -1
// ij = -ij = k
// jk = -kj = i
// ki = -ik = j
// This sample uses operator+=() for binary operation.
static const ValueType table[8][8] = {{ONE, I, J, K, MINUS_ONE, MINUS_I, MINUS_J, MINUS_K},
{I, MINUS_ONE, K, MINUS_J, MINUS_I, ONE, MINUS_K, J},
{J, MINUS_K, MINUS_ONE, I, MINUS_J, K, ONE, MINUS_I},
{K, J, MINUS_I, MINUS_ONE, MINUS_K, MINUS_J, I, ONE},
{MINUS_ONE, MINUS_I, MINUS_J, MINUS_K, ONE, I, J, K},
{MINUS_I, ONE, MINUS_K, J, I, MINUS_ONE, K, MINUS_J},
{MINUS_J, K, ONE, MINUS_I, J, MINUS_K, MINUS_ONE, I},
{MINUS_K, MINUS_J, I, ONE, K, J, MINUS_I, MINUS_ONE}};
value_ = table[value_][rhs.value_];
return *this;
}
NLIB_TESTING_OSTREAM& operator<<(NLIB_TESTING_OSTREAM& str, const Q4& data) {
switch (data.value()) {
case Q4::ONE:
str << "1";
break;
case Q4::I:
str << "i";
break;
case Q4::J:
str << "j";
break;
case Q4::K:
str << "k";
break;
case Q4::MINUS_ONE:
str << "-1";
break;
case Q4::MINUS_I:
str << "-i";
break;
case Q4::MINUS_J:
str << "-j";
break;
case Q4::MINUS_K:
str << "-k";
break;
default:
str << "ERROR";
break;
}
return str;
}
Q4 operator+(const Q4& lhs, const Q4& rhs) {
Q4 result(lhs);
result += rhs;
return result;
}
template <class T>
class GroupTest : public ::testing::Test {
public:
// you can define text fixtures here
};
// check if Z4, Q4, and unsigned char satisfy the definition of group
typedef ::testing::Types<Z4, Q4, unsigned char> MyTypes;
TYPED_TEST_CASE(GroupTest, MyTypes);
TYPED_TEST(GroupTest, BasicInterface) {
TypeParam x = TypeParam(); // check if TypeParam has a default constructor
TypeParam y = TypeParam(x); // check if TypeParam has a copy constructor
x = y; // check if TypeParam has an assignment operator
TypeParam z = x;
++z; // check if operator++() is defined.
// it is used for convenience, to get the next element.
EXPECT_EQ(x, y); // check if operator==() is defined, and x == y
EXPECT_EQ(TypeParam(), TypeParam(0)); // default value is equal to an identity element
}
TYPED_TEST(GroupTest, AssociativeLaw) {
// Associative law:
// a + (b + c) == (a + b) + c
// for all elements
TypeParam e = TypeParam();
TypeParam a = TypeParam();
TypeParam b = TypeParam();
TypeParam c = TypeParam();
do {
do {
do {
ASSERT_EQ(a + (b + c), (a + b) + c) << "a = " << a << ", "
<< "b = " << b << ", "
<< "c = " << c;
++c;
} while (e != c);
++b;
} while (e != b);
++a;
} while (e != a);
}
TYPED_TEST(GroupTest, IdentityElement) {
// existence of an identity element:
// 0 + a == a + 0
// for all elements
TypeParam e = TypeParam();
TypeParam a = TypeParam();
ASSERT_EQ(e, TypeParam());
do {
ASSERT_EQ(e + a, a + e) << a;
++a;
} while (e != a);
}
TYPED_TEST(GroupTest, InverseElement) {
// existence of inverse elements:
// there is an inverse element 'b' for all 'a' such that
// 0 == a + b
TypeParam e = TypeParam();
TypeParam a = TypeParam();
do {
bool success = false;
TypeParam b = TypeParam();
do {
if (e == a + b) {
success = true;
break;
}
} while (e != b);
ASSERT_TRUE(success) << a;
} while (e != a);
}
NLIB_PATHMAPPER_FORSAMPLE
bool SampleMain(int argc, char** argv) {
InitPathMapperForSample();
char path[512];
char buf[512];
size_t count;
g_PathMapper.ResolvePath(&count, path, "nlibpath:///readwrite/param_type.xml");
nlib_snprintf(&count, buf, "xml:%s", path);
::testing::GTEST_FLAG(output) = buf;
// RUN_ALL_TESTS returns 0 if all the tests are successful.
// You can use the return value of main() function.
return RUN_ALL_TESTS() == 0;
}
NLIB_MAINFUNC