1/*
2	$Id: ConcurrencyTest1.cpp 383 2002-07-22 09:28:00Z tylerdauwalder $
3
4	This file implements a test class for testing BMessageQueue functionality.
5	It tests use cases Destruction, Add Message 1, Add Message 3, Remove Message 1,
6	Remove Message 2, Count Messages, Find Message 1, Next Message 1 and Next Message
7	2.
8
9	The test works like the following:
10		- It does the test two times, one using an internal BList to keep track of
11		  what should be in the queue and once without.  It does it without because
12		  using the internal BList isn't a good test of mutual exclusion because an
13		  explicit lock is used.  However, it is worth testing with the BList to show
14		  that the result is what was expected.
15	    - It starts three threads.
16	    - In one thread, numAddMessages are added to the queue
17	    - In the second thread, numNextMessages are removed from the queue using
18	      NextMessage().
19	    - In the third thread, numAddMessages are added to the queue however every
20	      numRemovedMessagesPerAdd messages is removed using RemoveMessage().
21	    - Once all three threads complete, the number of elements on the queue is
22	      compared to what it expects.
23	    - If the BList is being used internally, the queue is checked against the list.
24	    - It checks that no BMessages have been deleted.
25	    - It deletes the message queue.
26	    - It checks that the expected number of BMessages have been deleted.
27	    - It creates a new message queue and restarts the test again if necessary
28	      (ie perform it without the internal list).
29
30	*/
31
32
33#include "ThreadedTestCaller.h"
34#include "ConcurrencyTest1.h"
35#include "ThreadedTestCaller.h"
36#include "TestSuite.h"
37#include <MessageQueue.h>
38#include <Autolock.h>
39
40
41// This constant indicates how many messages to add to the queue.
42const int numAddMessages = 5000;
43
44// This constant indicates how many times to call NextMessage() on the queue.
45const int numNextMessages = 100;
46
47// This constant says how many adds to perform before doing a remove.
48const int numAddMessagesPerRemove = 5;
49
50
51/*
52 *  Method:  ConcurrencyTest1::ConcurrencyTest1()
53 *   Descr:  This is the constructor for this test.
54 */
55
56
57	ConcurrencyTest1::ConcurrencyTest1(std::string name, bool listFlag) :
58		MessageQueueTestCase(name), useList(listFlag)
59{
60	}
61
62
63/*
64 *  Method:  ConcurrencyTest1::~ConcurrencyTest1()
65 *   Descr:  This is the destructor for this test.
66 */
67
68
69	ConcurrencyTest1::~ConcurrencyTest1()
70{
71	}
72
73
74/*
75 *  Method:  ConcurrencyTest1::setUp()
76 *   Descr:  This member function prepares the enviroment for test execution.
77 *           It resets the message destructor count and puts "numAddMessages"
78 *           messages on the queue.
79 */
80
81 void ConcurrencyTest1::setUp(void)
82{
83	testMessageClass::messageDestructorCount = 0;
84
85	int i;
86	for (i=0; i < numAddMessages; i++) {
87		BMessage *theMessage = new testMessageClass(i);
88		if (useList) {
89			AddMessage(theMessage);
90		} else {
91			theMessageQueue->AddMessage(theMessage);
92		}
93	}
94}
95
96
97/*
98 *  Method:  ConcurrencyTest1::TestThread1()
99 *   Descr:  This member function is one of the three threads for performing
100 *           this test.  It waits until the other two threads complete and
101 *           then checks the state of the message queue.  If the list is
102 *           being used, the queue is compared to the list.  It checks that
103 *           the expected number of messages are on the queue and that they
104 *           are all deleted when the message queue is destructed.
105 */
106
107 void ConcurrencyTest1::TestThread1(void)
108{
109	NextSubTest();
110	snooze(1000000);
111	thread2Lock.Lock();
112	thread3Lock.Lock();
113
114	if (useList) {
115		CheckQueueAgainstList();
116	}
117
118	int numMessages = (2*numAddMessages) -
119						(numAddMessages / numAddMessagesPerRemove) -
120						numNextMessages;
121	CPPUNIT_ASSERT(numMessages == theMessageQueue->CountMessages());
122	CPPUNIT_ASSERT(testMessageClass::messageDestructorCount == 0);
123
124	delete theMessageQueue;
125	theMessageQueue = NULL;
126	CPPUNIT_ASSERT(testMessageClass::messageDestructorCount == numMessages);
127}
128
129
130/*
131 *  Method:  ConcurrencyTest1::TestThread2()
132 *   Descr:  This member function is one of the three threads for performing
133 *           this test.  It performs NextMessage() numNextMessages times on
134 *           the queue.  It confirms that it always receives a message from
135 *           the queue.
136 */
137
138 void ConcurrencyTest1::TestThread2(void)
139{
140	BAutolock mySafetyLock(&thread2Lock);
141
142	int i;
143	for(i = 0; i < numNextMessages; i++) {
144		if (i % (numNextMessages / 10) == 0)
145			NextSubTest();
146
147		snooze(5000);
148		BMessage *theMessage;
149		if (useList) {
150			theMessage = NextMessage();
151		} else {
152			theMessage = theMessageQueue->NextMessage();
153		}
154		CPPUNIT_ASSERT(theMessage != NULL);
155	}
156}
157
158
159/*
160 *  Method:  ConcurrencyTest1::TestThread3()
161 *   Descr:  This member function is one of the three threads for performing
162 *           this test.  It adds numAddMessages messages to the queue.  For
163 *           every numAddMessagesPerRemove messages it adds, one message
164 *           is removed.
165 */
166
167 void ConcurrencyTest1::TestThread3(void)
168{
169	BAutolock mySafetyLock(&thread3Lock);
170
171	int i;
172	BList messagesToRemove;
173
174	for (i=0; i < numAddMessages; i++) {
175		if (i % (numAddMessages / 10) == 0)
176			NextSubTest();
177
178		BMessage *theMessage = new testMessageClass(i);
179		if (useList) {
180			AddMessage(theMessage);
181		} else {
182			theMessageQueue->AddMessage(theMessage);
183		}
184		if ((i % numAddMessagesPerRemove) == numAddMessagesPerRemove - 1) {
185			snooze(500);
186			if (useList) {
187				RemoveMessage(theMessage);
188			} else {
189				theMessageQueue->RemoveMessage(theMessage);
190			}
191		}
192	}
193}
194
195
196/*
197 *  Method:  ConcurrencyTest1::suite()
198 *   Descr:  This static member function returns a test suite for performing
199 *           all combinations of "ConcurrencyTest1".  The test suite contains
200 *           two instances of the test.  One is performed with a list,
201 *           the other without.  Each individual test
202 *           is created as a ThreadedTestCase (typedef'd as
203 *           ConcurrencyTest1Caller) with three independent threads.
204 */
205
206 Test *ConcurrencyTest1::suite(void)
207{
208	typedef BThreadedTestCaller<ConcurrencyTest1>
209		ConcurrencyTest1Caller;
210	TestSuite *testSuite = new TestSuite("ConcurrencyTest1");
211
212	ConcurrencyTest1 *theTest = new ConcurrencyTest1("WithList", true);
213	ConcurrencyTest1Caller *threadedTest1 = new ConcurrencyTest1Caller("BMessageQueue::Concurrency Test #1 (with list)", theTest);
214	threadedTest1->addThread("A", &ConcurrencyTest1::TestThread1);
215	threadedTest1->addThread("B", &ConcurrencyTest1::TestThread2);
216	threadedTest1->addThread("C", &ConcurrencyTest1::TestThread3);
217
218	theTest = new ConcurrencyTest1("WithoutList", false);
219	ConcurrencyTest1Caller *threadedTest2 = new ConcurrencyTest1Caller("BMessageQueue::Concurrency Test #1 (without list)", theTest);
220	threadedTest2->addThread("A", &ConcurrencyTest1::TestThread1);
221	threadedTest2->addThread("B", &ConcurrencyTest1::TestThread2);
222	threadedTest2->addThread("C", &ConcurrencyTest1::TestThread3);
223
224	testSuite->addTest(threadedTest1);
225	testSuite->addTest(threadedTest2);
226	return(testSuite);
227	}
228
229
230
231