xref: /haiku/src/tests/system/kernel/unit/TestContext.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "TestContext.h"
8 
9 #include <util/AutoLock.h>
10 
11 #include "Test.h"
12 #include "TestError.h"
13 
14 
15 static spinlock sLock = B_SPINLOCK_INITIALIZER;
16 
17 
18 // #pragma mark - TestOptions
19 
20 
21 TestOptions::TestOptions()
22 	:
23 	panicOnFailure(false),
24 	quitAfterFailure(false)
25 {
26 }
27 
28 
29 // #pragma mark - GlobalTestContext
30 
31 
32 struct GlobalTestContext::ThreadCookie {
33 	GlobalTestContext*	context;
34 	thread_func			function;
35 	void*				arg;
36 
37 	ThreadCookie(GlobalTestContext* context, thread_func function, void* arg)
38 		:
39 		context(context),
40 		function(function),
41 		arg(arg)
42 	{
43 	}
44 };
45 
46 
47 /*static*/ GlobalTestContext::ThreadEntry* GlobalTestContext::sGlobalThreads
48 	= NULL;
49 
50 
51 GlobalTestContext::GlobalTestContext(TestOutput& output, TestOptions& options)
52 	:
53 	fThreads(NULL),
54 	fThreadEntry(this),
55 	fOutput(output),
56 	fOptions(options),
57 	fCurrentContext(NULL),
58 	fTotalTests(0),
59 	fFailedTests(0)
60 {
61 	_SetCurrent(&fThreadEntry);
62 }
63 
64 
65 GlobalTestContext::~GlobalTestContext()
66 {
67 	InterruptsSpinLocker locker(sLock);
68 
69 	// remove all of our entries from the global list
70 	ThreadEntry** entry = &sGlobalThreads;
71 	while (*entry != NULL) {
72 		if ((*entry)->context == this)
73 			*entry = (*entry)->globalNext;
74 		else
75 			entry = &(*entry)->globalNext;
76 	}
77 }
78 
79 
80 /*static*/ GlobalTestContext*
81 GlobalTestContext::Current()
82 {
83 	thread_id thread = find_thread(NULL);
84 
85 	InterruptsSpinLocker locker(sLock);
86 
87 	ThreadEntry* entry = sGlobalThreads;
88 	while (entry != NULL) {
89 		if (entry->thread == thread)
90 			return entry->context;
91 		entry = entry->globalNext;
92 	}
93 
94 	return NULL;
95 }
96 
97 
98 void
99 GlobalTestContext::SetCurrentContext(TestContext* context)
100 {
101 	fCurrentContext = context;
102 }
103 
104 
105 void
106 GlobalTestContext::TestDone(bool success)
107 {
108 	fTotalTests++;
109 	if (!success)
110 		fFailedTests++;
111 }
112 
113 
114 thread_id
115 GlobalTestContext::SpawnThread(thread_func function, const char* name,
116 	int32 priority, void* arg)
117 {
118 	ThreadCookie* cookie = new(std::nothrow) ThreadCookie(this, function, arg);
119 	if (cookie == NULL)
120 		return B_NO_MEMORY;
121 
122 	thread_id thread = spawn_kernel_thread(_ThreadEntry, name, priority,
123 		cookie);
124 	if (thread < 0) {
125 		delete cookie;
126 		return thread;
127 	}
128 
129 	return thread;
130 }
131 
132 
133 /*static*/ void
134 GlobalTestContext::_SetCurrent(ThreadEntry* entry)
135 {
136 	InterruptsSpinLocker locker(sLock);
137 
138 	entry->contextNext = entry->context->fThreads;
139 	entry->context->fThreads = entry;
140 
141 	entry->globalNext = sGlobalThreads;
142 	sGlobalThreads = entry;
143 }
144 
145 
146 /*static*/ void
147 GlobalTestContext::_UnsetCurrent(ThreadEntry* entryToRemove)
148 {
149 	InterruptsSpinLocker locker(sLock);
150 
151 	// remove from the global list
152 	ThreadEntry** entry = &sGlobalThreads;
153 	while (*entry != NULL) {
154 		if (*entry == entryToRemove) {
155 			*entry = (*entry)->globalNext;
156 			break;
157 		}
158 
159 		entry = &(*entry)->globalNext;
160 	}
161 
162 	// remove from the context's list
163 	entry = &entryToRemove->context->fThreads;
164 	while (*entry != NULL) {
165 		if (*entry == entryToRemove) {
166 			*entry = (*entry)->contextNext;
167 			break;
168 		}
169 
170 		entry = &(*entry)->contextNext;
171 	}
172 }
173 
174 
175 /*static*/ status_t
176 GlobalTestContext::_ThreadEntry(void* data)
177 {
178 	ThreadCookie* cookie = (ThreadCookie*)data;
179 
180 	ThreadEntry entry(cookie->context);
181 	_SetCurrent(&entry);
182 
183 	thread_func function = cookie->function;
184 	void* arg = cookie->arg;
185 	delete cookie;
186 
187 	status_t result = function(arg);
188 
189 	_UnsetCurrent(&entry);
190 
191 	return result;
192 }
193 
194 
195 // #pragma mark - TestContext
196 
197 
198 TestContext::TestContext(GlobalTestContext* globalContext)
199 	:
200 	fGlobalContext(globalContext),
201 	fParent(NULL),
202 	fTest(NULL),
203 	fErrors(NULL),
204 	fLevel(0)
205 {
206 	fGlobalContext->SetCurrentContext(this);
207 }
208 
209 
210 TestContext::TestContext(TestContext& parent, Test* test)
211 	:
212 	fGlobalContext(parent.GlobalContext()),
213 	fParent(&parent),
214 	fTest(test),
215 	fErrors(NULL),
216 	fLevel(parent.Level() + 1)
217 {
218 	fGlobalContext->SetCurrentContext(this);
219 
220 	if (fTest != NULL) {
221 		if (fTest->IsLeafTest())
222 			Print("%*s%s...", fLevel * 2, "", test->Name());
223 		else
224 			Print("%*s%s:\n", fLevel * 2, "", test->Name());
225 	}
226 }
227 
228 
229 TestContext::~TestContext()
230 {
231 	fGlobalContext->SetCurrentContext(fParent);
232 
233 	while (fErrors != NULL) {
234 		TestError* error = fErrors;
235 		fErrors = error->ListLink();
236 		delete error;
237 	}
238 }
239 
240 
241 void
242 TestContext::TestDone(bool success)
243 {
244 	if (fTest != NULL && fTest->IsLeafTest()) {
245 		if (success) {
246 			Print(" ok\n");
247 		} else {
248 			Print(" FAILED\n");
249 			TestError* error = fErrors;
250 			while (error != NULL) {
251 				Print("%s", error->Message());
252 				error = error->ListLink();
253 			}
254 		}
255 
256 		fGlobalContext->TestDone(success);
257 	}
258 }
259 
260 
261 void
262 TestContext::ErrorArgs(const char* format, va_list args)
263 {
264 	int size = vsnprintf(NULL, 0, format, args) + 1;
265 	char* buffer = (char*)malloc(size);
266 	if (buffer == NULL)
267 		return;
268 
269 	vsnprintf(buffer, size, format, args);
270 
271 	TestError* error = new(std::nothrow) TestError(fTest, buffer);
272 	if (error == NULL) {
273 		free(buffer);
274 		return;
275 	}
276 
277 	InterruptsSpinLocker locker(sLock);
278 	error->ListLink() = fErrors;
279 	fErrors = error;
280 
281 	if (Options().panicOnFailure)
282 		panic("Test check failed: %s", error->Message());
283 }
284