1#include "RegistrarThreadManagerTest.h"
2
3#include <cppunit/Test.h>
4#include <cppunit/TestCaller.h>
5#include <cppunit/TestSuite.h>
6#include <TestApp.h>
7#include <TestUtils.h>
8
9#ifndef TEST_R5
10#include "RegistrarThread.h"
11#include "RegistrarThreadManager.h"
12#endif	// !TEST_R5
13
14#include <stdio.h>
15
16// Suite
17CppUnit::Test*
18RegistrarThreadManagerTest::Suite() {
19	CppUnit::TestSuite *suite = new CppUnit::TestSuite();
20	typedef CppUnit::TestCaller<RegistrarThreadManagerTest> TC;
21
22	suite->addTest( new TC("RegistrarThreadManager::Shutdown Test",
23						   &RegistrarThreadManagerTest::ShutdownTest) );
24	suite->addTest( new TC("RegistrarThreadManager::Thread Limit Test",
25						   &RegistrarThreadManagerTest::ThreadLimitTest) );
26
27
28	return suite;
29}
30
31#ifndef TEST_R5
32// Base test thread class
33class TestThread : public RegistrarThread {
34public:
35	TestThread(const char *name, int32 priority, BMessenger managerMessenger)
36		: RegistrarThread(name, priority, managerMessenger)
37	{
38	}
39
40	void DoSomethingUseless() {
41		fIntVal++;
42		snooze(1000);
43	}
44
45private:
46	int64 fIntVal;
47};
48
49// Test thread that terminates quickly
50class TerminatingThread : public TestThread {
51public:
52	TerminatingThread(const char *name, int32 priority, BMessenger managerMessenger)
53		: TestThread(name, priority, managerMessenger)
54	{
55	}
56
57protected:
58	virtual status_t ThreadFunction() {
59		DoSomethingUseless();
60		fIsFinished = true;
61		return B_OK;
62	}
63};
64
65// Test thread that never terminates, but pays attention
66// to its fShouldExit member
67class WellBehavedInfiniteThread : public TestThread {
68public:
69	WellBehavedInfiniteThread(const char *name, int32 priority, BMessenger managerMessenger)
70		: TestThread(name, priority, managerMessenger)
71	{
72	}
73
74protected:
75	virtual status_t ThreadFunction() {
76		while (true) {
77			DoSomethingUseless();
78			if (fShouldExit)
79				break;
80		}
81		fIsFinished = true;
82		return B_OK;
83	}
84};
85
86// Test thread that never terminates and completely ignores
87// its fShouldExit member
88class NaughtyInfiniteThread : public TestThread {
89public:
90	NaughtyInfiniteThread(const char *name, int32 priority, BMessenger managerMessenger)
91		: TestThread(name, priority, managerMessenger)
92	{
93	}
94
95protected:
96	virtual status_t ThreadFunction() {
97		while (true) {
98			DoSomethingUseless();
99		}
100		fIsFinished = true;
101		return B_OK;
102	}
103};
104#endif	// !TEST_R5
105
106
107// setUp
108void
109RegistrarThreadManagerTest::setUp()
110{
111	BTestCase::setUp();
112#ifndef TEST_R5
113	// Setup our application
114	fApplication = new BTestApp("application/x-vnd.obos.RegistrarThreadManagerTest");
115	if (fApplication->Init() != B_OK) {
116		fprintf(stderr, "Failed to initialize application (perhaps the Haiku registrar isn't running?).\n");
117		delete fApplication;
118		fApplication = NULL;
119	}
120#endif	// !TEST_R5
121}
122
123// tearDown
124void
125RegistrarThreadManagerTest::tearDown()
126{
127#ifndef TEST_R5
128	// Terminate the Application
129	if (fApplication) {
130		fApplication->Terminate();
131		delete fApplication;
132		fApplication = NULL;
133	}
134#endif	// !TEST_R5
135	BTestCase::tearDown();
136}
137
138void
139RegistrarThreadManagerTest::ShutdownTest()
140{
141#ifdef TEST_R5
142	Outputf("(no tests performed for R5 version)\n");
143#else
144	NextSubTest();
145	status_t err = B_OK;
146	NextSubTest();
147	RegistrarThreadManager manager;
148	NextSubTest();
149	CHK(fApplication && fApplication->InitCheck() == B_OK);
150	NextSubTest();
151//	fApplication->AddHandler(&manager);
152	NextSubTest();
153	BMessenger managerMessenger(NULL, fApplication, &err);
154// TODO: Do something about this...
155if (err != B_OK) {
156fprintf(stderr, "Fails because we try to init an Haiku BMessenger with a "
157"BLooper from R5's libbe (more precisely a BTestApp living in libcppunit, "
158"which is only linked against R5's libbe).\n");
159}
160	NextSubTest();
161	CHK(err == B_OK && managerMessenger.IsValid());
162	NextSubTest();
163
164	// Launch a bunch of threads
165	const uint termThreads = 2;
166	const uint niceThreads = 2;
167	const uint evilThreads = 2;
168
169	for (uint i = 0; i < termThreads; i++) {
170		NextSubTest();
171		char name[1024];
172		sprintf(name, "terminating #%d", i);
173		RegistrarThread *thread = new TerminatingThread(name, B_NORMAL_PRIORITY, managerMessenger);
174		CHK(thread != NULL);
175		CHK(thread->InitCheck() == B_OK);
176		CHK(manager.LaunchThread(thread) == B_OK);
177	}
178
179	for (uint i = 0; i < niceThreads; i++) {
180		NextSubTest();
181		char name[1024];
182		sprintf(name, "nice #%d", i);
183		RegistrarThread *thread = new WellBehavedInfiniteThread(name, B_NORMAL_PRIORITY, managerMessenger);
184		CHK(thread != NULL);
185		CHK(thread->InitCheck() == B_OK);
186		CHK(manager.LaunchThread(thread) == B_OK);
187	}
188
189	for (uint i = 0; i < evilThreads; i++) {
190		NextSubTest();
191		char name[1024];
192		sprintf(name, "evil #%d", i);
193		RegistrarThread *thread = new NaughtyInfiniteThread(name, B_NORMAL_PRIORITY, managerMessenger);
194		CHK(thread != NULL);
195		CHK(thread->InitCheck() == B_OK);
196		CHK(manager.LaunchThread(thread) == B_OK);
197	}
198
199	// Check the number of threads before doing a cleanup
200	NextSubTest();	// <= 13
201	CHK(manager.ThreadCount() == (termThreads + niceThreads + evilThreads));
202
203	// Do the cleanup and check again (the terminating threads
204	// should be gone)
205	NextSubTest();
206	snooze(500000);		// give them time to terminate
207	CHK(manager.CleanupThreads() == B_OK);
208	CHK(manager.ThreadCount() == (niceThreads + evilThreads));
209
210	// Now do a shutdown and check again (the nice infinite threads
211	// should be gone)
212	NextSubTest();
213	CHK(manager.ShutdownThreads() == B_OK);
214	snooze(1000000);	// give them time to quit nicely
215	CHK(manager.CleanupThreads() == B_OK);
216	CHK(manager.ThreadCount() == evilThreads);
217
218
219	// Now finally kill any remaining threads (which should rid us of
220	// the naughty infinite threads)
221	NextSubTest();
222	CHK(manager.KillThreads() == B_OK);
223	CHK(manager.ThreadCount() == 0);
224
225#endif	// !TEST_R5
226}
227
228void
229RegistrarThreadManagerTest::ThreadLimitTest()
230{
231#ifdef TEST_R5
232	Outputf("(no tests performed for R5 version)\n");
233#else
234	NextSubTest();
235	status_t err = B_OK;
236	RegistrarThreadManager manager;
237	CHK(fApplication && fApplication->InitCheck() == B_OK);
238	BMessenger managerMessenger(NULL, fApplication, &err);
239// TODO: Do something about this...
240if (err != B_OK) {
241fprintf(stderr, "Fails because we try to init an Haiku BMessenger with a "
242"BLooper from R5's libbe (more precisely a BTestApp living in libcppunit, "
243"which is only linked against R5's libbe).\n");
244}
245	CHK(err == B_OK && managerMessenger.IsValid());
246
247	const uint termThreads = 2;
248
249	// This test is only useful if the thread limit of the manager
250	// class is > kTermThreads
251	CHK(termThreads < RegistrarThreadManager::kThreadLimit);
252
253	// Launch some terminating threads
254	uint i;
255	for (i = 0; i < termThreads; i++) {
256		NextSubTest();
257		char name[1024];
258		sprintf(name, "terminating #%d", i);
259		RegistrarThread *thread = new TerminatingThread(name, B_NORMAL_PRIORITY, managerMessenger);
260		CHK(thread != NULL);
261		CHK(thread->InitCheck() == B_OK);
262		CHK(manager.LaunchThread(thread) == B_OK);
263	}
264
265	// Now fill up the manager with non-terminating threads
266	for (; i < RegistrarThreadManager::kThreadLimit; i++) {
267		NextSubTest();
268		char name[1024];
269		sprintf(name, "nice #%d", i);
270		RegistrarThread *thread = new WellBehavedInfiniteThread(name, B_NORMAL_PRIORITY, managerMessenger);
271		CHK(thread != NULL);
272		CHK(thread->InitCheck() == B_OK);
273		CHK(manager.LaunchThread(thread) == B_OK);
274	}
275	CHK(manager.ThreadCount() == RegistrarThreadManager::kThreadLimit);
276
277	// Now try to launch just one more...
278	NextSubTest();
279	{
280		char *name = "hopeless thread";
281		RegistrarThread *thread = new WellBehavedInfiniteThread(name, B_NORMAL_PRIORITY, managerMessenger);
282		CHK(thread != NULL);
283		CHK(thread->InitCheck() == B_OK);
284		CHK(manager.LaunchThread(thread) == B_NO_MORE_THREADS);
285		delete thread;
286	}
287
288	// Now wait a little bit for our terminating threads to quit,
289	// cleanup after them, and make sure we can now launch that
290	// many threads again
291	NextSubTest();
292	snooze(500000);
293	manager.CleanupThreads();
294
295	for (i = 0; i < termThreads; i++) {
296		NextSubTest();
297		char name[1024];
298		sprintf(name, "2nd round nice #%d", i);
299		RegistrarThread *thread = new TerminatingThread(name, B_NORMAL_PRIORITY, managerMessenger);
300		CHK(thread != NULL);
301		CHK(thread->InitCheck() == B_OK);
302		CHK(manager.LaunchThread(thread) == B_OK);
303	}
304
305	// Now try once more to launch just one more...
306	NextSubTest();
307	{
308		char *name = "hopeless thread";
309		RegistrarThread *thread = new WellBehavedInfiniteThread(name, B_NORMAL_PRIORITY, managerMessenger);
310		CHK(thread != NULL);
311		CHK(thread->InitCheck() == B_OK);
312		CHK(manager.LaunchThread(thread) == B_NO_MORE_THREADS);
313		delete thread;
314	}
315
316	// Cleanup
317	NextSubTest();
318	manager.ShutdownThreads();
319	snooze(500000);
320
321#endif	// !TEST_R5
322}
323