最近在研究WebKit,它是一个开源的网页浏览引擎,它主要用于Mac的Safari浏览器和Google的Chrome浏览器,最近RIM公司也在他们最新的blackberry OS6上用了WebKit引擎,它最初的HTML和javascript代码来自于KDE库的KHTML和KJS的一个分支。
最近在下载了它最新的源代码包以后编译了gtk的WebKit版本了,看了一下它的WebKitTools/WebKitAPITest中的代码,发现了一个好玩的东东,其中有一个Test.h,代码如下
呵呵,自动生成TESTCase的一段宏代码,很有意思,你可以像如有如下方式进行调用
#include <iostream> #include <string> #include "Test.h" using namespace std; using namespace WebKitAPITest; TEST(TestCase,printTest) // create a print test case object { cout << "This is a Test " << endl; // implement it's run() method } int main() { TEST_CLASS_NAME(TestCase,printTest) tt; // create a object cout << "Name:" << tt.name() << endl; tt.run(); // execute the run() method }
//////////////////////////////////////////////////////
这样你就可以自动分成很多不同的测试有例,生成的时候会统一的注册到一个叫TestController的类中,其中有一个容器来收集不同的测试用便,这个是一个单例,继承自Noncopyable类。在它的main.cpp中只要一句话就可以调用所有的test用例
#include "TestsController.h" using namespace WebKitAPITest; int main(int, char*[]) { // FIXME: Remove this line once <http://webkit.org/b/32867> is fixed. OleInitialize(0); return !TestsController::shared().runAllTests(); // run all test cases }
这边用到了宏定义来自动生成代码,这东东和OOP中的抽象类很相似,不过区别是宏定义增加了代码的灵活性。google也在gtest和protobuf中使用了这种方式。如下
*** gtest/include/gtest/gtest-param-test.h: <unknown>[1373] gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } / <unknown>[1374] int gtest_##prefix##test_case_name##_dummy_ = / <unknown>[1379] >est_##prefix##test_case_name##_EvalGenerator_, / *** gtest/include/gtest/gtest-typed-test.h: <unknown>[160] #define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ <unknown>[175] bool gtest_##CaseName##_##TestName##_registered_ = / <unknown>[197] gtest_case_##TestCaseName##_ <unknown>[204] gtest_typed_test_case_p_state_##TestCaseName##_ <unknown>[211] gtest_registered_test_names_##TestCaseName##_ <unknown>[229] static bool gtest_##TestName##_defined_ = / <unknown>[245] bool gtest_##Prefix##_##CaseName = / *** gtest/include/gtest/gtest.h: <unknown>[1284] AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, / <unknown>[1296] AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, / *** gtest/include/gtest/gtest_prod.h: <unknown>[56] friend class test_case_name##_##test_name##_Test *** gtest/include/gtest/internal/gtest-internal.h: <unknown>[62] // foo ## __LINE__ <unknown>[68] #define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar <unknown>[885] test_case_name##_##test_name##_Test *** src/google/protobuf/descriptor.cc: <unknown>[2671] OUTPUT->NAME##_count_ = INPUT.NAME##_size(); / <unknown>[2672] AllocateArray(INPUT.NAME##_size(), &OUTPUT->NAME##s_); / <unknown>[2673] for (int i = 0; i < INPUT.NAME##_size(); i++) { / <unknown>[2674] METHOD(INPUT.NAME(i), PARENT, OUTPUT->NAME##s_ + i); / <unknown>[3619] for (int i = 0; i < descriptor->array_name##_count(); ++i) { / <unknown>[3620] Validate##type##Options(descriptor->array_name##s_ + i, / *** src/google/protobuf/descriptor.h: <unknown>[1174] inline TYPE CLASS::FIELD() const { return FIELD##_; } <unknown>[1178] inline const string& CLASS::FIELD() const { return *FIELD##_; } <unknown>[1182] inline TYPE CLASS::FIELD(int index) const { return FIELD##s_ + index; } *** src/google/protobuf/dynamic_message.cc: <unknown>[247] case FieldDescriptor::CPPTYPE_##CPPTYPE: / <unknown>[249] new(field_ptr) TYPE(field->default_value_##TYPE()); / <unknown>[329] case FieldDescriptor::CPPTYPE_##UPPERCASE : /
gtest.h
--------
// A macro for implementing the helper functions needed to implement // ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste // of similar code. // // For each templatized helper function, we also define an overloaded // version for BiggestInt in order to reduce code bloat and allow // anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled // with gcc 4. // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. #define GTEST_IMPL_CMP_HELPER_(op_name, op)/ template <typename T1, typename T2>/ AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, / const T1& val1, const T2& val2) {/ if (val1 op val2) {/ return AssertionSuccess();/ } else {/ Message msg;/ msg << "Expected: (" << expr1 << ") " #op " (" << expr2/ << "), actual: " << FormatForComparisonFailureMessage(val1, val2)/ << " vs " << FormatForComparisonFailureMessage(val2, val1);/ return AssertionFailure(msg);/ }/ }/ AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, / BiggestInt val1, BiggestInt val2);
宏定义中的井号
1)# 只在有参数的定义时用到,为把参数产开并转化成字符串即用""引起来。里面的空格分割全部改为一个,并且会自动添加转义字符'/'
2)##可以用于有无参数都行。它作用就是把##前后连个东西连起来形成新的标示符。
参考资料:
1. http://www.uml.org.cn/c%2B%2B/200902104.asp
2. http://webkit.org/