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)を用意しています。
/*--------------------------------------------------------------------------------*
Project: CrossRoad
Copyright (C)Nintendo All rights reserved.
These coded instructions, statements, and computer programs contain proprietary
information of Nintendo and/or its licensed developers and are protected by
national and international copyright laws. They may not be disclosed to third
parties or copied or duplicated in any form, in whole or in part, without the
prior written consent of Nintendo.
The content herein is highly confidential and should be handled accordingly.
*--------------------------------------------------------------------------------*/
// 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 {
kOne = 0,
kI = 1,
kJ = 2,
kK = 3,
kMinusOne = 4,
kMinusI = 5,
kMinusJ = 6,
kMinusK = 7
};
Q4() : value_(kOne) {}
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] = {{kOne, kI, kJ, kK, kMinusOne, kMinusI, kMinusJ, kMinusK},
{kI, kMinusOne, kK, kMinusJ, kMinusI, kOne, kMinusK, kJ},
{kJ, kMinusK, kMinusOne, kI, kMinusJ, kK, kOne, kMinusI},
{kK, kJ, kMinusI, kMinusOne, kMinusK, kMinusJ, kI, kOne},
{kMinusOne, kMinusI, kMinusJ, kMinusK, kOne, kI, kJ, kK},
{kMinusI, kOne, kMinusK, kJ, kI, kMinusOne, kK, kMinusJ},
{kMinusJ, kK, kOne, kMinusI, kJ, kMinusK, kMinusOne, kI},
{kMinusK, kMinusJ, kI, kOne, kK, kJ, kMinusI, kMinusOne}};
value_ = table[value_][rhs.value_];
return *this;
}
NLIB_TESTING_OSTREAM& operator<<(NLIB_TESTING_OSTREAM& str, const Q4& data) {
switch (data.value()) {
case Q4::kOne:
str << "1";
break;
case Q4::kI:
str << "i";
break;
case Q4::kJ:
str << "j";
break;
case Q4::kK:
str << "k";
break;
case Q4::kMinusOne:
str << "-1";
break;
case Q4::kMinusI:
str << "-i";
break;
case Q4::kMinusJ:
str << "-j";
break;
case Q4::kMinusK:
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