Пришлось немного повозиться, но я разобрался. Хитрость в том, что статические инициализаторы функции будут работать, например,
int someFunc() {
return 42;
}
int someVal= someFunc();
пока они не вызывают никаких конструкторов, не используют new/malloc или не используют printf. (Мне потребовалось некоторое время, чтобы понять, что Gunslinger47 был прав насчет того, что printfs все портит.)
Тот факт, что статические функции инициализации работают, достаточен, чтобы заставить работать UnitTest++. Что мы делаем, так это используем вариант обходного пути «Указатели», описанный здесь:
- Вместо статического распределения каждый тестовый класс имеет функцию распределения.
- Указатель на каждую функцию-распределитель добавляется в список указателей на функции.
- В main этот список указателей функций затем повторяется и вызывается каждая функция.
Подробности ниже:
(1) В TestMacros.h измените макрос TEST_EX, чтобы использовать статическую функцию инициализации, а не конструктор:
#define TEST_EX(Name, List) \
class Test##Name : public UnitTest::Test \
{ \
public: \
Test##Name() : Test(#Name, UnitTestSuite::GetSuiteName(), __FILE__, __LINE__) {} \
private: \
virtual void RunImpl() const; \
}; \
\
void create_test##Name##Instance() { \
Test##Name *test##Name##Instance= new Test##Name(); \
UnitTest::ListAdder adder##Name (List(), test##Name##Instance); \
} \
\
UnitTest::test_creator_func_t fp_create_test##Name##Instance= \
UnitTest::addTestCreator(create_test##Name##Instance); \
\
void Test##Name::RunImpl() const
#define TEST(Name) TEST_EX(Name, UnitTest::Test::GetTestList)
(2) Измените TEST_FIXTURE_EX аналогично TEST_EX. Я избавлю вас от многословия.
(3) Внизу TestList.cpp добавьте функции, которые вызывают макросы TEST_EX/TEST_FIXTURE_EX:
#if !defined(MAX_TEST_CREATORS)
#define MAX_TEST_CREATORS 1024
#endif
const size_t max_test_creators= MAX_TEST_CREATORS;
size_t num_test_creators= 0;
// This list unfortunately must be static-- if we were to
// dynamically allocate it, then alchemy would break.
// If it winds up not being big enough, then just inject
// a bigger definition for MAX_TEST_CREATORS
test_creator_func_t test_creator_list[max_test_creators]= {NULL};
test_creator_func_t addTestCreator(test_creator_func_t fp) {
int idx= num_test_creators;
num_test_creators++;
if (num_test_creators > max_test_creators) {
throw "test creator overflow";
}
test_creator_list[idx]= fp;
return fp;
}
void initializeAllTests() {
for (size_t idx= 0; idx < num_test_creators; idx++) {
test_creator_list[idx]();
}
}
и конечно добавить их прототипы в TestList.h:
typedef void (*test_creator_func_t)();
test_creator_func_t addTestCreator(test_creator_func_t fp);
void initializeAllTests();
(4) Наконец, в вашем модуле запуска модульных тестов вы должны вызвать initializeAllTests:
UnitTest::initializeAllTests();
return UnitTest::RunAllTests();
Но это не все! Есть несколько других лакомых кусочков, которые необходимо сделать, прежде чем он заработает:
(1) Убедитесь, что UNITTEST_USE_CUSTOM_STREAMS определен в Config.h:
// by default, MemoryOutStream is implemented in terms of std::ostringstream, which can be expensive.
// uncomment this line to use the custom MemoryOutStream (no deps on std::ostringstream).
#define UNITTEST_USE_CUSTOM_STREAMS
Причина этого в том, что если он не определен, MemoryOutStream.h будет #include <sstream>
, что нарушит статическую инициализацию (я подозреваю, что он выполняет какой-то глобальный конструктор или что-то в этом роде).
(2) В SignalTranslator.h убедитесь, что макрос UNITTEST_THROW_SIGNALS является нулевым. Я делаю это, вводя -D__ALCHEMY__
в свои сборки и проверяя его:
#if defined(__ALCHEMY__)
#define UNITTEST_THROW_SIGNALS
#else
#define UNITTEST_THROW_SIGNALS \
UnitTest::SignalTranslator sig; \
if (UNITTEST_EXTENSION sigsetjmp(*UnitTest::SignalTranslator::s_jumpTarget, 1) != 0) \
throw ("Unhandled system exception");
#endif
Если этого не сделать, вызов sigsetjmp завершится ошибкой во время выполнения.
person
paleozogt
schedule
30.11.2010