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
TestOptions()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
ThreadCookieGlobalTestContext::ThreadCookie37 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
GlobalTestContext(TestOutput & output,TestOptions & options)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
~GlobalTestContext()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*
Current()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
SetCurrentContext(TestContext * context)99 GlobalTestContext::SetCurrentContext(TestContext* context)
100 {
101 fCurrentContext = context;
102 }
103
104
105 void
TestDone(bool success)106 GlobalTestContext::TestDone(bool success)
107 {
108 fTotalTests++;
109 if (!success)
110 fFailedTests++;
111 }
112
113
114 thread_id
SpawnThread(thread_func function,const char * name,int32 priority,void * arg)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
_SetCurrent(ThreadEntry * entry)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
_UnsetCurrent(ThreadEntry * entryToRemove)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
_ThreadEntry(void * data)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
TestContext(GlobalTestContext * globalContext)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
TestContext(TestContext & parent,Test * test)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
~TestContext()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
TestDone(bool success)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
ErrorArgs(const char * format,va_list args)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