| 1 | /////////////////////////////////////////////////////////////////////////////// |
| 2 | // Name: tests/thread/queue.cpp |
| 3 | // Purpose: Unit test for wxMessageQueue |
| 4 | // Author: Evgeniy Tarassov |
| 5 | // Created: 31/10/2007 |
| 6 | // RCS-ID: $Id$ |
| 7 | // Copyright: (c) 2007 Evgeniy Tarassov |
| 8 | // Licence: wxWindows licence |
| 9 | /////////////////////////////////////////////////////////////////////////////// |
| 10 | |
| 11 | // ---------------------------------------------------------------------------- |
| 12 | // headers |
| 13 | // ---------------------------------------------------------------------------- |
| 14 | |
| 15 | #include "testprec.h" |
| 16 | |
| 17 | #ifdef __BORLANDC__ |
| 18 | #pragma hdrstop |
| 19 | #endif |
| 20 | |
| 21 | #ifndef WX_PRECOMP |
| 22 | #include "wx/dynarray.h" |
| 23 | #include "wx/thread.h" |
| 24 | #endif // WX_PRECOMP |
| 25 | |
| 26 | #include "wx/msgqueue.h" |
| 27 | |
| 28 | // ---------------------------------------------------------------------------- |
| 29 | // test class |
| 30 | // ---------------------------------------------------------------------------- |
| 31 | |
| 32 | class QueueTestCase : public CppUnit::TestCase |
| 33 | { |
| 34 | public: |
| 35 | QueueTestCase() { } |
| 36 | |
| 37 | enum WaitTestType |
| 38 | { |
| 39 | WaitWithTimeout = 0, |
| 40 | WaitInfinitlyLong |
| 41 | }; |
| 42 | |
| 43 | private: |
| 44 | typedef wxMessageQueue<int> Queue; |
| 45 | |
| 46 | // This class represents a thread that waits (following WaitTestType type) |
| 47 | // for exactly maxMsgCount messages from its message queue and if another |
| 48 | // MyThread is specified, then every message received is posted |
| 49 | // to that next thread. |
| 50 | class MyThread : public wxThread |
| 51 | { |
| 52 | public: |
| 53 | MyThread(WaitTestType type, MyThread *next, int maxMsgCount) |
| 54 | : wxThread(wxTHREAD_JOINABLE), |
| 55 | m_type(type), m_nextThread(next), m_maxMsgCount(maxMsgCount) |
| 56 | {} |
| 57 | |
| 58 | // thread execution starts here |
| 59 | virtual void *Entry(); |
| 60 | |
| 61 | // Thread message queue |
| 62 | Queue& GetQueue() |
| 63 | { |
| 64 | return m_queue; |
| 65 | } |
| 66 | |
| 67 | private: |
| 68 | WaitTestType m_type; |
| 69 | MyThread* m_nextThread; |
| 70 | int m_maxMsgCount; |
| 71 | Queue m_queue; |
| 72 | }; |
| 73 | |
| 74 | WX_DEFINE_ARRAY_PTR(MyThread *, ArrayThread); |
| 75 | |
| 76 | CPPUNIT_TEST_SUITE( QueueTestCase ); |
| 77 | CPPUNIT_TEST( TestReceive ); |
| 78 | CPPUNIT_TEST( TestReceiveTimeout ); |
| 79 | CPPUNIT_TEST_SUITE_END(); |
| 80 | |
| 81 | void TestReceive(); |
| 82 | void TestReceiveTimeout(); |
| 83 | |
| 84 | DECLARE_NO_COPY_CLASS(QueueTestCase) |
| 85 | }; |
| 86 | |
| 87 | // register in the unnamed registry so that these tests are run by default |
| 88 | CPPUNIT_TEST_SUITE_REGISTRATION( QueueTestCase ); |
| 89 | |
| 90 | // also include in its own registry so that these tests can be run alone |
| 91 | CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( QueueTestCase, "QueueTestCase" ); |
| 92 | |
| 93 | // this function creates the given number of threads and posts msgCount |
| 94 | // messages to the last created thread which, in turn, posts all the messages |
| 95 | // it receives to the previously created thread which does the same and so on |
| 96 | // in cascade -- at the end, each thread will have received all msgCount |
| 97 | // messages directly or indirectly |
| 98 | void QueueTestCase::TestReceive() |
| 99 | { |
| 100 | const int msgCount = 100; |
| 101 | const int threadCount = 10; |
| 102 | |
| 103 | ArrayThread threads; |
| 104 | |
| 105 | int i; |
| 106 | for ( i = 0; i < threadCount; ++i ) |
| 107 | { |
| 108 | MyThread *previousThread = i == 0 ? NULL : threads[i-1]; |
| 109 | MyThread *thread = |
| 110 | new MyThread(WaitInfinitlyLong, previousThread, msgCount); |
| 111 | |
| 112 | CPPUNIT_ASSERT_EQUAL ( thread->Create(), wxTHREAD_NO_ERROR ); |
| 113 | threads.Add(thread); |
| 114 | } |
| 115 | |
| 116 | for ( i = 0; i < threadCount; ++i ) |
| 117 | { |
| 118 | threads[i]->Run(); |
| 119 | } |
| 120 | |
| 121 | MyThread* lastThread = threads[threadCount - 1]; |
| 122 | |
| 123 | for ( i = 0; i < msgCount; ++i ) |
| 124 | { |
| 125 | lastThread->GetQueue().Post(i); |
| 126 | } |
| 127 | |
| 128 | for ( i = 0; i < threadCount; ++i ) |
| 129 | { |
| 130 | // each thread should return the number of messages received. |
| 131 | // if it returns a negative, then it detected some problem. |
| 132 | wxThread::ExitCode code = threads[i]->Wait(); |
| 133 | CPPUNIT_ASSERT_EQUAL( code, (wxThread::ExitCode)wxMSGQUEUE_NO_ERROR ); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | // this function creates two threads, each one waiting (with a timeout) for |
| 138 | // exactly two messages. |
| 139 | // Exactly to messages are posted into first thread queue, but |
| 140 | // only one message is posted to the second thread queue. |
| 141 | // Therefore first thread should return with wxMSGQUEUE_NO_ERROR, but the second |
| 142 | // should return wxMSGQUEUUE_TIMEOUT. |
| 143 | void QueueTestCase::TestReceiveTimeout() |
| 144 | { |
| 145 | MyThread* thread1 = new MyThread(WaitWithTimeout, NULL, 2); |
| 146 | MyThread* thread2 = new MyThread(WaitWithTimeout, NULL, 2); |
| 147 | |
| 148 | CPPUNIT_ASSERT_EQUAL ( thread1->Create(), wxTHREAD_NO_ERROR ); |
| 149 | CPPUNIT_ASSERT_EQUAL ( thread2->Create(), wxTHREAD_NO_ERROR ); |
| 150 | |
| 151 | thread1->Run(); |
| 152 | thread2->Run(); |
| 153 | |
| 154 | // Post two messages to the first thread |
| 155 | CPPUNIT_ASSERT_EQUAL( thread1->GetQueue().Post(0), wxMSGQUEUE_NO_ERROR ); |
| 156 | CPPUNIT_ASSERT_EQUAL( thread1->GetQueue().Post(1), wxMSGQUEUE_NO_ERROR ); |
| 157 | |
| 158 | // ...but only one message to the second |
| 159 | CPPUNIT_ASSERT_EQUAL( thread2->GetQueue().Post(0), wxMSGQUEUE_NO_ERROR ); |
| 160 | |
| 161 | wxThread::ExitCode code1 = thread1->Wait(); |
| 162 | wxThread::ExitCode code2 = thread2->Wait(); |
| 163 | |
| 164 | CPPUNIT_ASSERT_EQUAL( code1, (wxThread::ExitCode)wxMSGQUEUE_NO_ERROR ); |
| 165 | CPPUNIT_ASSERT_EQUAL( code2, (wxThread::ExitCode)wxMSGQUEUE_TIMEOUT ); |
| 166 | } |
| 167 | |
| 168 | // every thread tries to read exactly m_maxMsgCount messages from its queue |
| 169 | // following the waiting strategy specified in m_type. If it succeeds then it |
| 170 | // returns 0. Otherwise it returns the error code - one of wxMessageQueueError. |
| 171 | void *QueueTestCase::MyThread::Entry() |
| 172 | { |
| 173 | int messagesReceived = 0; |
| 174 | while ( messagesReceived < m_maxMsgCount ) |
| 175 | { |
| 176 | wxMessageQueueError result; |
| 177 | int msg = -1; // just to suppress "possibly uninitialized" warnings |
| 178 | |
| 179 | if ( m_type == WaitWithTimeout ) |
| 180 | result = m_queue.ReceiveTimeout(1000, msg); |
| 181 | else |
| 182 | result = m_queue.Receive(msg); |
| 183 | |
| 184 | if ( result == wxMSGQUEUE_MISC_ERROR ) |
| 185 | return (wxThread::ExitCode)wxMSGQUEUE_MISC_ERROR; |
| 186 | |
| 187 | if ( result == wxMSGQUEUE_NO_ERROR ) |
| 188 | { |
| 189 | if ( m_nextThread != NULL ) |
| 190 | { |
| 191 | wxMessageQueueError res = m_nextThread->GetQueue().Post(msg); |
| 192 | |
| 193 | if ( res == wxMSGQUEUE_MISC_ERROR ) |
| 194 | return (wxThread::ExitCode)wxMSGQUEUE_MISC_ERROR; |
| 195 | |
| 196 | CPPUNIT_ASSERT_EQUAL( wxMSGQUEUE_NO_ERROR, res ); |
| 197 | } |
| 198 | ++messagesReceived; |
| 199 | continue; |
| 200 | } |
| 201 | |
| 202 | CPPUNIT_ASSERT_EQUAL ( result, wxMSGQUEUE_TIMEOUT ); |
| 203 | |
| 204 | break; |
| 205 | } |
| 206 | |
| 207 | if ( messagesReceived != m_maxMsgCount ) |
| 208 | { |
| 209 | CPPUNIT_ASSERT_EQUAL( m_type, WaitWithTimeout ); |
| 210 | |
| 211 | return (wxThread::ExitCode)wxMSGQUEUE_TIMEOUT; |
| 212 | } |
| 213 | |
| 214 | return (wxThread::ExitCode)wxMSGQUEUE_NO_ERROR; |
| 215 | } |