Многопоточная реализация CppUnit? - PullRequest
13 голосов
/ 08 марта 2011

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

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

Ответы [ 2 ]

6 голосов
/ 06 апреля 2011

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

Используя потоки Boost, многопоточность 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 suite либо как набор верхнего уровня, либо как набор, включающий несколько методов одного и того же текстового фиксатора. Вот как вы делаете последнее - поместите это вместо 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__

Теперь есть маленькая материя 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's work queue
    // Or: block until a worker is free then assign task to that thread
    void add(boost::function0<void> task);
};

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

3 голосов
/ 09 марта 2011

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

...