1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2008-2014 Kouhei Sutou <kou@clear-code.com>
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif /* HAVE_CONFIG_H */
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <glib.h>
27
28 #include <signal.h>
29
30 #include "cut-test-iterator.h"
31
32 #include "cut-run-context.h"
33
34 #include "cut-test-result.h"
35 #include "cut-utils.h"
36 #include "cut-crash-backtrace.h"
37
38 #include "../gcutter/gcut-error.h"
39 #include "../gcutter/gcut-marshalers.h"
40
41 #define CUT_TEST_ITERATOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CUT_TYPE_TEST_ITERATOR, CutTestIteratorPrivate))
42
43 typedef struct _CutTestIteratorPrivate CutTestIteratorPrivate;
44 struct _CutTestIteratorPrivate
45 {
46 CutIteratedTestFunction iterated_test_function;
47 CutDataSetupFunction data_setup_function;
48 };
49
50 enum
51 {
52 PROP_0,
53 PROP_ITERATED_TEST_FUNCTION,
54 PROP_DATA_SETUP_FUNCTION
55 };
56
57 enum
58 {
59 READY,
60 START_TEST,
61 COMPLETE_TEST,
62 LAST_SIGNAL
63 };
64
65 static gint signals[LAST_SIGNAL] = {0};
66
67 G_DEFINE_TYPE(CutTestIterator, cut_test_iterator, CUT_TYPE_TEST_CONTAINER)
68
69 static void dispose (GObject *object);
70 static void set_property (GObject *object,
71 guint prop_id,
72 const GValue *value,
73 GParamSpec *pspec);
74 static void get_property (GObject *object,
75 guint prop_id,
76 GValue *value,
77 GParamSpec *pspec);
78
79 static gboolean run (CutTest *test,
80 CutTestContext *test_context,
81 CutRunContext *run_context);
82
83 static void
cut_test_iterator_class_init(CutTestIteratorClass * klass)84 cut_test_iterator_class_init (CutTestIteratorClass *klass)
85 {
86 GObjectClass *gobject_class;
87 CutTestClass *test_class;
88 GParamSpec *spec;
89
90 gobject_class = G_OBJECT_CLASS(klass);
91 test_class = CUT_TEST_CLASS(klass);
92
93 gobject_class->dispose = dispose;
94 gobject_class->set_property = set_property;
95 gobject_class->get_property = get_property;
96
97 test_class->run = run;
98
99 spec = g_param_spec_pointer("iterated-test-function",
100 "Iterated Test Function",
101 "The function for iterated test",
102 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
103 g_object_class_install_property(gobject_class, PROP_ITERATED_TEST_FUNCTION,
104 spec);
105
106 spec = g_param_spec_pointer("data-setup-function",
107 "Data Setup Function",
108 "The function for data setup",
109 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
110 g_object_class_install_property(gobject_class, PROP_DATA_SETUP_FUNCTION,
111 spec);
112
113 signals[READY]
114 = g_signal_new("ready",
115 G_TYPE_FROM_CLASS(klass),
116 G_SIGNAL_RUN_LAST,
117 G_STRUCT_OFFSET(CutTestIteratorClass, ready),
118 NULL, NULL,
119 g_cclosure_marshal_VOID__UINT,
120 G_TYPE_NONE, 1, G_TYPE_UINT);
121
122 signals[START_TEST]
123 = g_signal_new("start-iterated-test",
124 G_TYPE_FROM_CLASS(klass),
125 G_SIGNAL_RUN_LAST,
126 G_STRUCT_OFFSET(CutTestIteratorClass,
127 start_iterated_test),
128 NULL, NULL,
129 _gcut_marshal_VOID__OBJECT_OBJECT,
130 G_TYPE_NONE, 2, CUT_TYPE_TEST, CUT_TYPE_TEST_CONTEXT);
131
132 signals[COMPLETE_TEST]
133 = g_signal_new("complete-iterated-test",
134 G_TYPE_FROM_CLASS(klass),
135 G_SIGNAL_RUN_LAST,
136 G_STRUCT_OFFSET(CutTestIteratorClass,
137 complete_iterated_test),
138 NULL, NULL,
139 _gcut_marshal_VOID__OBJECT_OBJECT_BOOLEAN,
140 G_TYPE_NONE, 3,
141 CUT_TYPE_TEST, CUT_TYPE_TEST_CONTEXT, G_TYPE_BOOLEAN);
142
143 g_type_class_add_private(gobject_class, sizeof(CutTestIteratorPrivate));
144 }
145
146 static void
cut_test_iterator_init(CutTestIterator * test_iterator)147 cut_test_iterator_init (CutTestIterator *test_iterator)
148 {
149 CutTestIteratorPrivate *priv = CUT_TEST_ITERATOR_GET_PRIVATE(test_iterator);
150
151 priv->iterated_test_function = NULL;
152 priv->data_setup_function = NULL;
153 }
154
155 static void
dispose(GObject * object)156 dispose (GObject *object)
157 {
158 G_OBJECT_CLASS(cut_test_iterator_parent_class)->dispose(object);
159 }
160
161 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)162 set_property (GObject *object,
163 guint prop_id,
164 const GValue *value,
165 GParamSpec *pspec)
166 {
167 CutTestIteratorPrivate *priv = CUT_TEST_ITERATOR_GET_PRIVATE(object);
168
169 switch (prop_id) {
170 case PROP_ITERATED_TEST_FUNCTION:
171 priv->iterated_test_function = g_value_get_pointer(value);
172 break;
173 case PROP_DATA_SETUP_FUNCTION:
174 priv->data_setup_function = g_value_get_pointer(value);
175 break;
176 default:
177 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
178 break;
179 }
180 }
181
182 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)183 get_property (GObject *object,
184 guint prop_id,
185 GValue *value,
186 GParamSpec *pspec)
187 {
188 CutTestIteratorPrivate *priv = CUT_TEST_ITERATOR_GET_PRIVATE(object);
189
190 switch (prop_id) {
191 case PROP_ITERATED_TEST_FUNCTION:
192 g_value_set_pointer(value, priv->iterated_test_function);
193 break;
194 case PROP_DATA_SETUP_FUNCTION:
195 g_value_set_pointer(value, priv->data_setup_function);
196 break;
197 default:
198 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
199 break;
200 }
201 }
202
203 CutTestIterator *
cut_test_iterator_new(const gchar * name,CutIteratedTestFunction test_function,CutDataSetupFunction data_setup_function)204 cut_test_iterator_new (const gchar *name,
205 CutIteratedTestFunction test_function,
206 CutDataSetupFunction data_setup_function)
207 {
208 return g_object_new(CUT_TYPE_TEST_ITERATOR,
209 "element-name", "test-iterator",
210 "name", name,
211 "iterated-test-function", test_function,
212 "data-setup-function", data_setup_function,
213 NULL);
214 }
215
216 CutTestIterator *
cut_test_iterator_new_empty(void)217 cut_test_iterator_new_empty (void)
218 {
219 return cut_test_iterator_new(NULL, NULL, NULL);
220 }
221
222 CutIteratedTest *
cut_test_iterator_create_iterated_test(CutTestIterator * test_iterator,const gchar * name,CutIteratedTestFunction iterated_test_function,CutTestData * test_data)223 cut_test_iterator_create_iterated_test (CutTestIterator *test_iterator,
224 const gchar *name,
225 CutIteratedTestFunction iterated_test_function,
226 CutTestData *test_data)
227 {
228 CutTestIteratorClass *klass;
229
230 klass = CUT_TEST_ITERATOR_GET_CLASS(test_iterator);
231 if (klass->create_iterated_test) {
232 return klass->create_iterated_test(test_iterator,
233 name,
234 iterated_test_function,
235 test_data);
236 } else {
237 return cut_iterated_test_new(name,
238 iterated_test_function,
239 test_data);
240 }
241 }
242
243 void
cut_test_iterator_add_test(CutTestIterator * test_iterator,CutIteratedTest * test)244 cut_test_iterator_add_test (CutTestIterator *test_iterator,
245 CutIteratedTest *test)
246 {
247 cut_test_container_add_test(CUT_TEST_CONTAINER(test_iterator),
248 CUT_TEST(test));
249 }
250
251 typedef struct _RunTestInfo
252 {
253 CutRunContext *run_context;
254 CutTestCase *test_case;
255 CutTestIterator *test_iterator;
256 CutIteratedTest *iterated_test;
257 CutTestContext *test_context;
258 CutTestContext *parent_test_context;
259 } RunTestInfo;
260
261 static void
run_test_without_thread(gpointer data,gpointer user_data)262 run_test_without_thread (gpointer data, gpointer user_data)
263 {
264 RunTestInfo *info = data;
265 CutRunContext *run_context;
266 CutTestCase *test_case;
267 CutTestIterator *test_iterator;
268 CutIteratedTest *iterated_test;
269 CutTestContext *test_context, *parent_test_context;
270 gint signum;
271 jmp_buf crash_jump_buffer;
272 CutCrashBacktrace *crash_backtrace = NULL;
273 gboolean *success = user_data;
274
275 run_context = info->run_context;
276 test_case = info->test_case;
277 test_iterator = info->test_iterator;
278 iterated_test = info->iterated_test;
279 test_context = info->test_context;
280 parent_test_context = info->parent_test_context;
281
282 if (cut_run_context_is_canceled(run_context))
283 return;
284
285 cut_test_context_current_push(test_context);
286
287 if (cut_run_context_is_multi_thread(run_context) ||
288 !cut_run_context_get_handle_signals(run_context)) {
289 signum = 0;
290 } else {
291 crash_backtrace = cut_crash_backtrace_new(&crash_jump_buffer);
292 signum = setjmp(crash_jump_buffer);
293 }
294 switch (signum) {
295 case 0:
296 g_signal_emit_by_name(test_iterator, "start-iterated-test",
297 iterated_test, test_context);
298
299 cut_test_case_run_setup(test_case, test_context);
300 if (cut_test_context_is_failed(test_context)) {
301 *success = FALSE;
302 } else {
303 if (!cut_test_run(CUT_TEST(iterated_test),
304 test_context, run_context))
305 *success = FALSE;
306 }
307
308 if (crash_backtrace)
309 cut_crash_backtrace_free(crash_backtrace);
310
311 break;
312 #ifndef G_OS_WIN32
313 case SIGSEGV:
314 case SIGABRT:
315 case SIGTERM:
316 case SIGBUS:
317 success = FALSE;
318 cut_crash_backtrace_emit(cut_run_context_get_test_suite(run_context),
319 test_case, CUT_TEST(iterated_test),
320 test_iterator,
321 cut_iterated_test_get_data(iterated_test),
322 test_context);
323 break;
324 case SIGINT:
325 cut_run_context_cancel(run_context);
326 break;
327 #endif
328 default:
329 break;
330 }
331
332 cut_test_case_run_teardown(test_case, test_context);
333
334 cut_test_context_set_failed(parent_test_context,
335 cut_test_context_is_failed(test_context));
336
337 g_signal_emit_by_name(test_iterator, "complete-iterated-test",
338 iterated_test, test_context, *success);
339
340 cut_test_context_set_test(test_context, NULL);
341 cut_test_context_set_test(parent_test_context, NULL);
342 cut_test_context_current_pop();
343
344 g_object_unref(run_context);
345 g_object_unref(test_case);
346 g_object_unref(test_iterator);
347 g_object_unref(iterated_test);
348 g_object_unref(test_context);
349 g_object_unref(parent_test_context);
350 g_free(info);
351 }
352
353 static void
run_test_with_thread_support(CutTestIterator * test_iterator,CutIteratedTest * iterated_test,CutTestContext * test_context,CutRunContext * run_context,GThreadPool * thread_pool,gboolean * success)354 run_test_with_thread_support (CutTestIterator *test_iterator,
355 CutIteratedTest *iterated_test,
356 CutTestContext *test_context,
357 CutRunContext *run_context,
358 GThreadPool *thread_pool,
359 gboolean *success)
360 {
361 RunTestInfo *info;
362 CutTest *test;
363 CutTestCase *test_case;
364 CutTestContext *local_test_context;
365 gboolean is_multi_thread;
366 gboolean need_no_thread_run = TRUE;
367 const gchar *multi_thread_attribute;
368
369 if (cut_run_context_is_canceled(run_context))
370 return;
371
372 test = CUT_TEST(iterated_test);
373 test_case = cut_test_context_get_test_case(test_context);
374 local_test_context = cut_test_context_new(run_context,
375 NULL, test_case, test_iterator,
376 test);
377 is_multi_thread = cut_run_context_is_multi_thread(run_context);
378 multi_thread_attribute = cut_test_get_attribute(CUT_TEST(test_iterator),
379 "multi-thread");
380 if (multi_thread_attribute &&
381 g_str_equal(multi_thread_attribute, "false"))
382 is_multi_thread = FALSE;
383 cut_test_context_set_multi_thread(local_test_context, is_multi_thread);
384 cut_test_context_set_data(local_test_context,
385 cut_iterated_test_get_data(iterated_test));
386
387 cut_test_context_set_test(test_context, test);
388 cut_test_context_set_test(local_test_context, test);
389
390 info = g_new0(RunTestInfo, 1);
391 info->run_context = g_object_ref(run_context);
392 info->test_case = g_object_ref(test_case);
393 info->test_iterator = g_object_ref(test_iterator);
394 info->iterated_test = g_object_ref(iterated_test);
395 info->test_context = local_test_context;
396 info->parent_test_context = g_object_ref(test_context);
397 if (is_multi_thread && thread_pool) {
398 GError *error = NULL;
399
400 g_thread_pool_push(thread_pool, info, &error);
401 if (error) {
402 cut_utils_report_error(error);
403 } else {
404 need_no_thread_run = FALSE;
405 }
406 }
407
408 if (need_no_thread_run)
409 run_test_without_thread(info, success);
410 }
411
412 static void
cb_test_status(CutTest * test,CutTestContext * context,CutTestResult * result,gpointer data)413 cb_test_status (CutTest *test, CutTestContext *context, CutTestResult *result,
414 gpointer data)
415 {
416 CutTestResultStatus *status = data;
417
418 *status = MAX(*status, cut_test_result_get_status(result));
419 }
420
421 static void
run_iterated_tests(CutTest * test,CutTestContext * test_context,CutRunContext * run_context,CutTestResultStatus * status,gboolean * all_success)422 run_iterated_tests (CutTest *test, CutTestContext *test_context,
423 CutRunContext *run_context,
424 CutTestResultStatus *status, gboolean *all_success)
425 {
426 CutTestIteratorPrivate *priv;
427 CutTestIterator *test_iterator;
428 CutTestContainer *test_container;
429 GError *error = NULL;
430 GList *node, *iterated_tests = NULL, *filtered_tests = NULL;
431 const gchar **test_names;
432 guint n_tests;
433 GThreadPool *thread_pool = NULL;
434
435 thread_pool = g_thread_pool_new(run_test_without_thread,
436 all_success,
437 cut_run_context_get_max_threads(run_context),
438 FALSE,
439 &error);
440 if (error) {
441 cut_utils_report_error(error);
442 return;
443 }
444
445 priv = CUT_TEST_ITERATOR_GET_PRIVATE(test);
446 test_iterator = CUT_TEST_ITERATOR(test);
447 while (cut_test_context_have_data(test_context)) {
448 CutIteratedTest *iterated_test;
449 CutTestData *test_data;
450
451 test_data = cut_test_context_get_current_data(test_context);
452 iterated_test =
453 cut_test_iterator_create_iterated_test(test_iterator,
454 cut_test_get_name(test),
455 priv->iterated_test_function,
456 test_data);
457 cut_test_iterator_add_test(test_iterator, iterated_test);
458 g_object_unref(iterated_test);
459
460 iterated_tests = g_list_prepend(iterated_tests, iterated_test);
461
462 g_signal_connect(iterated_test, "success",
463 G_CALLBACK(cb_test_status), status);
464 g_signal_connect(iterated_test, "failure",
465 G_CALLBACK(cb_test_status), status);
466 g_signal_connect(iterated_test, "error",
467 G_CALLBACK(cb_test_status), status);
468 g_signal_connect(iterated_test, "pending",
469 G_CALLBACK(cb_test_status), status);
470 g_signal_connect(iterated_test, "notification",
471 G_CALLBACK(cb_test_status), status);
472 g_signal_connect(iterated_test, "omission",
473 G_CALLBACK(cb_test_status), status);
474
475 cut_test_context_shift_data(test_context);
476 }
477
478 test_container = CUT_TEST_CONTAINER(test);
479 test_names = cut_run_context_get_target_test_names(run_context);
480 filtered_tests = cut_test_container_filter_children(test_container,
481 test_names);
482
483 n_tests = cut_test_container_get_n_tests(test_container, run_context);
484 g_signal_emit_by_name(test_iterator, "ready", n_tests);
485 g_signal_emit_by_name(test, "start", NULL);
486
487 for (node = filtered_tests; node; node = g_list_next(node)) {
488 CutIteratedTest *iterated_test = node->data;
489
490 run_test_with_thread_support(test_iterator, iterated_test,
491 test_context, run_context,
492 thread_pool, all_success);
493 }
494 g_list_free(filtered_tests);
495
496 if (thread_pool)
497 g_thread_pool_free(thread_pool, FALSE, TRUE);
498
499 for (node = iterated_tests; node; node = g_list_next(node)) {
500 CutIteratedTest *iterated_test = node->data;
501
502 g_signal_handlers_disconnect_by_func(iterated_test,
503 G_CALLBACK(cb_test_status),
504 status);
505 cut_iterated_test_clear_data(iterated_test);
506 }
507 g_list_free(iterated_tests);
508 }
509
510 static gboolean
run(CutTest * test,CutTestContext * test_context,CutRunContext * run_context)511 run (CutTest *test, CutTestContext *test_context, CutRunContext *run_context)
512 {
513 CutTestIterator *test_iterator;
514 CutTestIteratorPrivate *priv;
515 CutTestResult *result;
516 CutTestCase *test_case;
517 CutTestResultStatus status = CUT_TEST_RESULT_SUCCESS;
518 gboolean all_success = TRUE;
519 jmp_buf jump_buffer;
520
521 test_iterator = CUT_TEST_ITERATOR(test);
522 g_return_val_if_fail(CUT_IS_TEST_ITERATOR(test_iterator), FALSE);
523
524 priv = CUT_TEST_ITERATOR_GET_PRIVATE(test_iterator);
525 g_return_val_if_fail(priv->data_setup_function != NULL, FALSE);
526
527 cut_test_context_set_test_iterator(test_context, test_iterator);
528 if (priv->data_setup_function) {
529 cut_test_context_set_jump_buffer(test_context, &jump_buffer);
530 if (setjmp(jump_buffer) == 0) {
531 priv->data_setup_function();
532 }
533 }
534
535 if (cut_test_context_is_failed(test_context)) {
536 cut_test_context_set_test_iterator(test_context, NULL);
537 return FALSE;
538 }
539
540 run_iterated_tests(test, test_context, run_context,
541 &status, &all_success);
542
543 test_case = cut_test_context_get_test_case(test_context);
544 result = cut_test_result_new(status,
545 NULL, test_iterator, test_case, NULL, NULL,
546 NULL, NULL, NULL);
547 cut_test_emit_result_signal(CUT_TEST(test_iterator), test_context, result);
548 g_object_unref(result);
549 g_signal_emit_by_name(test, "complete", NULL, all_success);
550
551 cut_test_context_set_test_iterator(test_context, NULL);
552
553 return all_success;
554 }
555
556
557 /*
558 vi:nowrap:ai:expandtab:sw=4
559 */
560