Многопоточная реализация CppUnit?

Может ли кто-нибудь указать мне версию CppUnit, которая позволит запускать тесты в отдельных потоках?

Идея состоит в том, что, поскольку многие наши тесты довольно тяжелые (но не многопоточные), и, конечно же, независимы друг от друга), это позволило бы нам быстрее проводить тесты на сегодня многоядерные машины. В настоящее время для проведения всех тестов требуется около 5 минут. Было бы здорово сократить это до 1 или 2 минут...

Ответ 1

Вы думаете, что пять минут - это долго ждать завершения тестов! Попробуйте несколько часов. У меня была мотивация для следующего.

Использование потоков увеличения, потоки CppUnit довольно просты. У CppUnit уже есть некоторые перехватчики для синхронизации, поэтому следующее должно сделать это потокобезопасным:

class Mutex : public CPPUNIT_NS::SynchronizedObject::SynchronizationObject
{
public:
    void lock() { this->mutex->lock(); }
    void unlock() { this->mutex->unlock(); }
private:
    boost::mutex mutex; 
};

С помощью этого вы можете изменить свой тестовый бегун, чтобы сделать поток TestResult безопасным. Просто напишите что-нибудь вроде CPPUNIT_NS::TestResult testResult(new Mutex);. Теперь вот набор тестовых пакетов:

class TestSuiteThreaded : public CPPUNIT_NS::TestSuite
{
public:
    TestSuiteThreaded(std::string name = "", int nThreads = 0)
        : TestSuite(name)
        , nThreads(nThreads ? nThreads : boost::thread::hardware_concurrency())
    {
    }
    void doRunChildTests(CPPUNIT_NS::TestResult *controller)
    {
        ThreadPool pool(this->nThreads);
        for (int i=0; i < getChildTestCount(); ++i)
        {
            pool.add(
                boost::bind(threadFunction, getChildTestAt(i)
                , controller));
        }
    }
private:
    static void threadFunction(
        CPPUNIT_NS::Test *test, 
        CPPUNIT_NS::TestResult *controller)
    {
        test->run(controller);
    }
    const int nThreads;
};

Вам может понадобиться макрос для удобства использования набора для потоковой проверки. Вы должны иметь возможность использовать пакет TestSuiteThreaded либо как набор верхнего уровня, либо набор, содержащий несколько методов одного и того же текстового инструментария. Здесь, как вы делаете последнее, поставьте это вместо CPPUNIT_TEST_SUITE_END. Некоторые из них вставляются из CppUnit, поэтому, пожалуйста, соблюдайте лицензию:

#define CPPUNIT_TEST_SUITE_END_THREADED(n)                                     \
    }                                                                          \
    static CPPUNIT_NS::TestSuite *suite()                                      \
    {                                                                          \
      const CPPUNIT_NS::TestNamer &namer = getTestNamer__();                   \
      std::auto_ptr<CPPUNIT_NS::TestSuite> suite(                              \
         new CPPUNIT_NS::TestSuiteThreaded( namer.getFixtureName(), n));       \
      CPPUNIT_NS::ConcretTestFixtureFactory<TestFixtureType> factory;          \
      CPPUNIT_NS::TestSuiteBuilderContextBase context( *suite.get(),           \
                               namer,                                          \
                               factory );                                      \
      TestFixtureType::addTestsToSuite( context );                             \
      return suite.release();                                                  \
    }                                                                          \
  private: /* dummy typedef so that the macro can still end with ';'*/         \
    typedef int CppUnitDummyTypedefForSemiColonEnding__

Теперь существует небольшое значение a ThreadPool. Я пробовал использовать различные общедоступные, без успеха. У моей компании есть одна, но я не могу ее опубликовать здесь. Итак, сворачивайте свои собственные - пулы потоков довольно легко и забавно сделать с помощью Boost. Вот интерфейс, ожидаемый TestSuiteThreaded:

class ThreadPool
{
public:
    // Create thread pool, launching n worker threads
    ThreadPool(unsigned n); 

    // Join all worker threads and clean up
    ~ThreadPool();

    // You can have add() do one of two things.  Both will work:
    // Either: push a new task to the back of the threadpool work queue
    // Or: block until a worker is free then assign task to that thread
    void add(boost::function0<void> task);
};

Я оставляю это как упражнение для читателя. Получайте удовольствие!

Ответ 2

Учитывая, сколько ответов на этот вопрос вы получили, особенно по сравнению с количеством опросов, я сомневаюсь, что кто-то создал хорошую многопоточную модульную систему тестирования, независимо от того, насколько велика ее идея. Это похоже на отличную возможность для кого-то сделать себе имя, что-то необычайно полезно.