2011-03-08 97 views
11

有人能指點我的一個版本的CppUnit,允許在單獨的線程中啓動測試嗎?這個想法是因爲我們的許多測試都是CPU負荷過重(但不是多線程,當然,它們是相互獨立的),它可以讓我們更快地運行測試今天的多核機器。目前,運行所有測試大約需要5分鐘。如果能夠將這個問題減少到1或2分鐘,這將是非常好的...CppUnit的多線程實現?

回答

6

你認爲五分鐘是等待測試完成的漫長時間!嘗試幾個小時。我有以下動機。

使用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套件作爲頂層套件或包含相同文本夾具的多種方法的套件。這是你如何做後者 - 把這個代替CPPUNIT_TEST_SUITE_END。一些這是從CppUnit的粘貼所以請尊重license

#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

鑑於你已經得到了多少個這個問題的答案,特別是與upvotes的數量相比,我懷疑有人做了一個好的多線程單元測試框架,不管它有多麼棒。這看起來像是一個很好的機會,讓某個人爲自己開發一個非常有用的名字。

+1

可以考慮一個啓動器,它將單元測試分解爲池,然後用參數指定要執行的池來啓動大量測試運行程序進程。這將允許多線程測試代碼,而不需要代碼是多線程的。 – 2011-03-08 23:10:52

+0

@Rolf Kristensen:儘管如此,仍然存在問題。測試可能取決於外部狀態,文件等。單獨的游泳池中的測試可能會偶然碰撞彼此。 – Omnifarious 2011-03-09 11:57:47