1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2007-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-case.h"
31 #include "cut-test.h"
32 #include "cut-run-context.h"
33 #include "cut-test-result.h"
34 #include "cut-crash-backtrace.h"
35 
36 #include <gcutter/gcut-marshalers.h>
37 
38 #define CUT_TEST_CASE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CUT_TYPE_TEST_CASE, CutTestCasePrivate))
39 
40 typedef struct _CutTestCasePrivate	CutTestCasePrivate;
41 struct _CutTestCasePrivate
42 {
43     CutSetupFunction setup;
44     CutTeardownFunction teardown;
45     CutStartupFunction startup;
46     CutShutdownFunction shutdown;
47 };
48 
49 enum
50 {
51     PROP_0,
52     PROP_SETUP_FUNCTION,
53     PROP_TEARDOWN_FUNCTION,
54     PROP_STARTUP_FUNCTION,
55     PROP_SHUTDOWN_FUNCTION
56 };
57 
58 enum
59 {
60     READY,
61     START_TEST,
62     COMPLETE_TEST,
63     START_TEST_ITERATOR,
64     COMPLETE_TEST_ITERATOR,
65 
66     SUCCESS_IN,
67     FAILURE_IN,
68     ERROR_IN,
69     PENDING_IN,
70     NOTIFICATION_IN,
71     OMISSION_IN,
72     CRASH_IN,
73 
74     LAST_SIGNAL
75 };
76 
77 static gint cut_test_case_signals[LAST_SIGNAL] = {0};
78 
79 G_DEFINE_TYPE (CutTestCase, cut_test_case, CUT_TYPE_TEST_CONTAINER)
80 
81 static void dispose        (GObject         *object);
82 static void set_property   (GObject         *object,
83                             guint            prop_id,
84                             const GValue    *value,
85                             GParamSpec      *pspec);
86 static void get_property   (GObject         *object,
87                             guint            prop_id,
88                             GValue          *value,
89                             GParamSpec      *pspec);
90 static void emit_result_signal
91                            (CutTest        *test,
92                             CutTestContext *test_context,
93                             CutTestResult  *result);
94 
95 static void
cut_test_case_class_init(CutTestCaseClass * klass)96 cut_test_case_class_init (CutTestCaseClass *klass)
97 {
98     GObjectClass *gobject_class;
99     CutTestClass *test_class;
100     GParamSpec *spec;
101 
102     gobject_class = G_OBJECT_CLASS(klass);
103     test_class = CUT_TEST_CLASS(klass);
104 
105     gobject_class->dispose      = dispose;
106     gobject_class->set_property = set_property;
107     gobject_class->get_property = get_property;
108 
109     test_class->emit_result_signal = emit_result_signal;
110 
111     spec = g_param_spec_pointer("setup-function",
112                                 "Setup Function",
113                                 "The function for setup",
114                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
115     g_object_class_install_property(gobject_class, PROP_SETUP_FUNCTION, spec);
116 
117     spec = g_param_spec_pointer("teardown-function",
118                                 "Teardown Function",
119                                 "The function for teardown",
120                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
121     g_object_class_install_property(gobject_class, PROP_TEARDOWN_FUNCTION, spec);
122 
123     spec = g_param_spec_pointer("startup-function",
124                                 "Startup Function",
125                                 "The function for initialization of TestCase",
126                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
127     g_object_class_install_property(gobject_class,
128                                     PROP_STARTUP_FUNCTION,
129                                     spec);
130 
131     spec = g_param_spec_pointer("shutdown-function",
132                                 "Shutdown Function",
133                                 "The function for finalization of TestCase",
134                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
135     g_object_class_install_property(gobject_class,
136                                     PROP_SHUTDOWN_FUNCTION,
137                                     spec);
138 
139 
140     cut_test_case_signals[READY]
141         = g_signal_new("ready",
142                        G_TYPE_FROM_CLASS(klass),
143                        G_SIGNAL_RUN_LAST,
144                        G_STRUCT_OFFSET(CutTestCaseClass, ready),
145                        NULL, NULL,
146                        g_cclosure_marshal_VOID__UINT,
147                        G_TYPE_NONE, 1, G_TYPE_UINT);
148 
149     cut_test_case_signals[START_TEST]
150         = g_signal_new("start-test",
151                        G_TYPE_FROM_CLASS(klass),
152                        G_SIGNAL_RUN_LAST,
153                        G_STRUCT_OFFSET(CutTestCaseClass, start_test),
154                        NULL, NULL,
155                        _gcut_marshal_VOID__OBJECT_OBJECT,
156                        G_TYPE_NONE, 2, CUT_TYPE_TEST, CUT_TYPE_TEST_CONTEXT);
157 
158     cut_test_case_signals[COMPLETE_TEST]
159         = g_signal_new("complete-test",
160                        G_TYPE_FROM_CLASS(klass),
161                        G_SIGNAL_RUN_LAST,
162                        G_STRUCT_OFFSET(CutTestCaseClass, complete_test),
163                        NULL, NULL,
164                        _gcut_marshal_VOID__OBJECT_OBJECT_BOOLEAN,
165                        G_TYPE_NONE, 3,
166                        CUT_TYPE_TEST, CUT_TYPE_TEST_CONTEXT, G_TYPE_BOOLEAN);
167 
168     cut_test_case_signals[START_TEST_ITERATOR]
169         = g_signal_new("start-test-iterator",
170                        G_TYPE_FROM_CLASS(klass),
171                        G_SIGNAL_RUN_LAST,
172                        G_STRUCT_OFFSET(CutTestCaseClass, start_test_iterator),
173                        NULL, NULL,
174                        _gcut_marshal_VOID__OBJECT_OBJECT,
175                        G_TYPE_NONE,
176                        2, CUT_TYPE_TEST_ITERATOR, CUT_TYPE_TEST_CONTEXT);
177 
178     cut_test_case_signals[COMPLETE_TEST_ITERATOR]
179         = g_signal_new("complete-test-iterator",
180                        G_TYPE_FROM_CLASS(klass),
181                        G_SIGNAL_RUN_LAST,
182                        G_STRUCT_OFFSET(CutTestCaseClass, complete_test_iterator),
183                        NULL, NULL,
184                        _gcut_marshal_VOID__OBJECT_OBJECT_BOOLEAN,
185                        G_TYPE_NONE,
186                        3, CUT_TYPE_TEST_ITERATOR, CUT_TYPE_TEST_CONTEXT,
187                        G_TYPE_BOOLEAN);
188 
189     cut_test_case_signals[FAILURE_IN]
190         = g_signal_new("failure-in",
191                        G_TYPE_FROM_CLASS(klass),
192                        G_SIGNAL_RUN_LAST,
193                        G_STRUCT_OFFSET(CutTestCaseClass, failure_in),
194                        NULL, NULL,
195                        _gcut_marshal_VOID__OBJECT_OBJECT,
196                        G_TYPE_NONE, 2,
197                        CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
198 
199     cut_test_case_signals[ERROR_IN]
200         = g_signal_new("error-in",
201                        G_TYPE_FROM_CLASS(klass),
202                        G_SIGNAL_RUN_LAST,
203                        G_STRUCT_OFFSET(CutTestCaseClass, error_in),
204                        NULL, NULL,
205                        _gcut_marshal_VOID__OBJECT_OBJECT,
206                        G_TYPE_NONE, 2,
207                        CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
208 
209     cut_test_case_signals[PENDING_IN]
210         = g_signal_new("pending-in",
211                        G_TYPE_FROM_CLASS(klass),
212                        G_SIGNAL_RUN_LAST,
213                        G_STRUCT_OFFSET(CutTestCaseClass, pending_in),
214                        NULL, NULL,
215                        _gcut_marshal_VOID__OBJECT_OBJECT,
216                        G_TYPE_NONE, 2,
217                        CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
218 
219     cut_test_case_signals[NOTIFICATION_IN]
220         = g_signal_new("notification-in",
221                        G_TYPE_FROM_CLASS(klass),
222                        G_SIGNAL_RUN_LAST,
223                        G_STRUCT_OFFSET(CutTestCaseClass, notification_in),
224                        NULL, NULL,
225                        _gcut_marshal_VOID__OBJECT_OBJECT,
226                        G_TYPE_NONE, 2,
227                        CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
228 
229     cut_test_case_signals[OMISSION_IN]
230         = g_signal_new("omission-in",
231                        G_TYPE_FROM_CLASS(klass),
232                        G_SIGNAL_RUN_LAST,
233                        G_STRUCT_OFFSET(CutTestCaseClass, omission_in),
234                        NULL, NULL,
235                        _gcut_marshal_VOID__OBJECT_OBJECT,
236                        G_TYPE_NONE, 2,
237                        CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
238 
239     cut_test_case_signals[CRASH_IN]
240         = g_signal_new("crash-in",
241                        G_TYPE_FROM_CLASS(klass),
242                        G_SIGNAL_RUN_LAST,
243                        G_STRUCT_OFFSET(CutTestCaseClass, crash_in),
244                        NULL, NULL,
245                        _gcut_marshal_VOID__OBJECT_OBJECT,
246                        G_TYPE_NONE, 2,
247                        CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
248 
249     g_type_class_add_private(gobject_class, sizeof(CutTestCasePrivate));
250 }
251 
252 static void
cut_test_case_init(CutTestCase * test_case)253 cut_test_case_init (CutTestCase *test_case)
254 {
255     CutTestCasePrivate *priv = CUT_TEST_CASE_GET_PRIVATE(test_case);
256 
257     priv->setup = NULL;
258     priv->teardown = NULL;
259     priv->startup = NULL;
260     priv->shutdown = NULL;
261 }
262 
263 static void
dispose(GObject * object)264 dispose (GObject *object)
265 {
266     G_OBJECT_CLASS(cut_test_case_parent_class)->dispose(object);
267 }
268 
269 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)270 set_property (GObject      *object,
271               guint         prop_id,
272               const GValue *value,
273               GParamSpec   *pspec)
274 {
275     CutTestCasePrivate *priv = CUT_TEST_CASE_GET_PRIVATE(object);
276 
277     switch (prop_id) {
278       case PROP_SETUP_FUNCTION:
279         priv->setup = g_value_get_pointer(value);
280         break;
281       case PROP_TEARDOWN_FUNCTION:
282         priv->teardown = g_value_get_pointer(value);
283         break;
284       case PROP_STARTUP_FUNCTION:
285         priv->startup = g_value_get_pointer(value);
286         break;
287       case PROP_SHUTDOWN_FUNCTION:
288         priv->shutdown = g_value_get_pointer(value);
289         break;
290       default:
291         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
292         break;
293     }
294 }
295 
296 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)297 get_property (GObject    *object,
298               guint       prop_id,
299               GValue     *value,
300               GParamSpec *pspec)
301 {
302     CutTestCasePrivate *priv = CUT_TEST_CASE_GET_PRIVATE(object);
303 
304     switch (prop_id) {
305       case PROP_SETUP_FUNCTION:
306         g_value_set_pointer(value, priv->setup);
307         break;
308       case PROP_TEARDOWN_FUNCTION:
309         g_value_set_pointer(value, priv->teardown);
310         break;
311       case PROP_STARTUP_FUNCTION:
312         g_value_set_pointer(value, priv->startup);
313         break;
314       case PROP_SHUTDOWN_FUNCTION:
315         g_value_set_pointer(value, priv->shutdown);
316         break;
317       default:
318         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
319         break;
320     }
321 }
322 
323 CutTestCase *
cut_test_case_new(const gchar * name,CutSetupFunction setup,CutTeardownFunction teardown,CutStartupFunction startup,CutShutdownFunction shutdown)324 cut_test_case_new (const gchar *name,
325                    CutSetupFunction setup,
326                    CutTeardownFunction teardown,
327                    CutStartupFunction startup,
328                    CutShutdownFunction shutdown)
329 {
330     return g_object_new(CUT_TYPE_TEST_CASE,
331                         "name", name,
332                         "element-name", "test-case",
333                         "setup-function", setup,
334                         "teardown-function", teardown,
335                         "startup-function", startup,
336                         "shutdown-function", shutdown,
337                         NULL);
338 }
339 
340 CutTestCase *
cut_test_case_new_empty(void)341 cut_test_case_new_empty (void)
342 {
343     return cut_test_case_new(NULL,
344                              NULL, NULL,
345                              NULL, NULL);
346 }
347 
348 static GList *
get_filtered_tests(CutTestCase * test_case,const gchar ** test_names)349 get_filtered_tests (CutTestCase *test_case, const gchar **test_names)
350 {
351     CutTestContainer *container;
352 
353     container = CUT_TEST_CONTAINER(test_case);
354     if (test_names && *test_names) {
355         return cut_test_container_filter_children(container, test_names);
356     } else {
357         return g_list_copy(cut_test_container_get_children(container));
358     }
359 }
360 
361 void
cut_test_case_add_test(CutTestCase * test_case,CutTest * test)362 cut_test_case_add_test (CutTestCase *test_case, CutTest *test)
363 {
364     cut_test_container_add_test(CUT_TEST_CONTAINER(test_case), test);
365 }
366 
367 void
cut_test_case_run_setup(CutTestCase * test_case,CutTestContext * test_context)368 cut_test_case_run_setup (CutTestCase *test_case, CutTestContext *test_context)
369 {
370     CutTestCasePrivate *priv;
371 
372     priv = CUT_TEST_CASE_GET_PRIVATE(test_case);
373     if (priv->setup) {
374         jmp_buf jump_buffer;
375 
376         cut_test_context_set_jump_buffer(test_context, &jump_buffer);
377         if (setjmp(jump_buffer) == 0) {
378             priv->setup();
379         }
380     }
381 }
382 
383 void
cut_test_case_run_teardown(CutTestCase * test_case,CutTestContext * test_context)384 cut_test_case_run_teardown (CutTestCase *test_case, CutTestContext *test_context)
385 {
386     CutTestCasePrivate *priv;
387 
388     priv = CUT_TEST_CASE_GET_PRIVATE(test_case);
389     if (priv->teardown) {
390         jmp_buf jump_buffer;
391 
392         cut_test_context_set_jump_buffer(test_context, &jump_buffer);
393         if (setjmp(jump_buffer) == 0) {
394             priv->teardown();
395         }
396     }
397 }
398 
399 static gboolean
run_test(CutTestCase * test_case,CutTest * test,CutTestContext * test_context,CutRunContext * run_context)400 run_test (CutTestCase *test_case, CutTest *test,
401           CutTestContext *test_context, CutRunContext *run_context)
402 {
403     gboolean success = TRUE;
404 
405     if (CUT_IS_TEST_ITERATOR(test)) {
406         g_signal_emit_by_name(test_case, "start-test-iterator",
407                               test, test_context);
408     } else {
409         g_signal_emit_by_name(test_case, "start-test", test, test_context);
410         cut_test_case_run_setup(test_case, test_context);
411     }
412 
413     if (cut_test_context_is_failed(test_context)) {
414         success = FALSE;
415     } else if (cut_test_context_need_test_run(test_context)) {
416         success = cut_test_run(test, test_context, run_context);
417     }
418 
419     if (CUT_IS_TEST_ITERATOR(test)) {
420         g_signal_emit_by_name(test_case, "complete-test-iterator",
421                               test, test_context, success);
422     } else {
423         cut_test_case_run_teardown(test_case, test_context);
424         g_signal_emit_by_name(test_case, "complete-test",
425                               test, test_context, success);
426     }
427 
428     return success;
429 }
430 
431 static gboolean
run(CutTestCase * test_case,CutTest * test,CutRunContext * run_context)432 run (CutTestCase *test_case, CutTest *test, CutRunContext *run_context)
433 {
434     CutTestContext *test_context;
435     gboolean success = TRUE;
436     gboolean is_multi_thread;
437     CutTestSuite *test_suite;
438 
439     if (cut_run_context_is_canceled(run_context))
440         return TRUE;
441 
442     test_suite = cut_run_context_get_test_suite(run_context);
443     is_multi_thread = cut_run_context_is_multi_thread(run_context);
444 
445     test_context = cut_test_context_new(run_context,
446                                         test_suite, test_case, NULL, NULL);
447     cut_test_context_set_multi_thread(test_context, is_multi_thread);
448 
449     cut_test_context_current_push(test_context);
450     cut_test_context_set_test(test_context, test);
451     success = run_test(test_case, test, test_context, run_context);
452     cut_test_context_set_test(test_context, NULL);
453 
454     g_object_unref(test_context);
455     cut_test_context_current_pop();
456     /* FIXME: We want to use the code:
457        g_object_unref(cut_test_context_current_pop());
458        We need to hide cut_set_current_test_context() from user.
459     */
460 
461     return success;
462 }
463 
464 static void
cb_test_status(CutTest * test,CutTestContext * context,CutTestResult * result,gpointer data)465 cb_test_status (CutTest *test, CutTestContext *context, CutTestResult *result,
466                 gpointer data)
467 {
468     CutTestResultStatus *status = data;
469 
470     *status = MAX(*status, cut_test_result_get_status(result));
471 }
472 
473 static void
cut_test_case_run_startup(CutTestCase * test_case,CutTestContext * test_context)474 cut_test_case_run_startup (CutTestCase *test_case, CutTestContext *test_context)
475 {
476     CutTestCasePrivate *priv;
477 
478     priv = CUT_TEST_CASE_GET_PRIVATE(test_case);
479     if (priv->startup) {
480         jmp_buf jump_buffer;
481 
482         cut_test_context_set_jump_buffer(test_context, &jump_buffer);
483         if (setjmp(jump_buffer) == 0) {
484             priv->startup();
485         }
486     }
487 }
488 
489 static void
cut_test_case_run_shutdown(CutTestCase * test_case,CutTestContext * test_context)490 cut_test_case_run_shutdown (CutTestCase *test_case, CutTestContext *test_context)
491 {
492     CutTestCasePrivate *priv;
493 
494     priv = CUT_TEST_CASE_GET_PRIVATE(test_case);
495     if (priv->shutdown) {
496         jmp_buf jump_buffer;
497 
498         cut_test_context_set_jump_buffer(test_context, &jump_buffer);
499         if (setjmp(jump_buffer) == 0) {
500             priv->shutdown();
501         }
502     }
503 }
504 
505 static void
emit_result_signal(CutTest * test,CutTestContext * test_context,CutTestResult * result)506 emit_result_signal (CutTest *test, CutTestContext *test_context,
507                     CutTestResult *result)
508 {
509     const gchar *status_signal_name = NULL;
510     gchar *signal_name;
511     CutTestResultStatus status;
512 
513     status = cut_test_result_get_status(result);
514     status_signal_name = cut_test_result_status_to_signal_name(status);
515     signal_name = g_strdup_printf("%s-in", status_signal_name);
516     g_signal_emit_by_name(test, signal_name, test_context, result);
517     g_free(signal_name);
518 }
519 
520 static void
cut_test_case_emit_result_signal(CutTestCase * test_case,CutTestResult * result)521 cut_test_case_emit_result_signal (CutTestCase *test_case, CutTestResult *result)
522 {
523     const gchar *status_signal_name = NULL;
524     CutTestResultStatus status;
525 
526     cut_test_set_result_elapsed(CUT_TEST(test_case), result);
527 
528     status = cut_test_result_get_status(result);
529     status_signal_name = cut_test_result_status_to_signal_name(status);
530     g_signal_emit_by_name(test_case, status_signal_name, NULL, result);
531 }
532 
533 static gboolean
run_tests(CutTestCase * test_case,CutRunContext * run_context,const GList * tests,CutTestResultStatus * status)534 run_tests (CutTestCase *test_case, CutRunContext *run_context,
535            const GList *tests, CutTestResultStatus *status)
536 {
537     const GList *list;
538     gboolean all_success = TRUE;
539 
540     for (list = tests; list; list = g_list_next(list)) {
541         CutTest *test = list->data;
542 
543         if (!test)
544             continue;
545 
546         if (CUT_IS_TEST(test)) {
547             g_signal_connect(test, "success", G_CALLBACK(cb_test_status),
548                              status);
549             g_signal_connect(test, "failure", G_CALLBACK(cb_test_status),
550                              status);
551             g_signal_connect(test, "error", G_CALLBACK(cb_test_status),
552                              status);
553             g_signal_connect(test, "pending", G_CALLBACK(cb_test_status),
554                              status);
555             g_signal_connect(test, "notification", G_CALLBACK(cb_test_status),
556                              status);
557             g_signal_connect(test, "omission", G_CALLBACK(cb_test_status),
558                              status);
559             g_signal_connect(test, "crash", G_CALLBACK(cb_test_status),
560                              status);
561             if (!run(test_case, test, run_context))
562                 all_success = FALSE;
563             g_signal_handlers_disconnect_by_func(test,
564                                                  G_CALLBACK(cb_test_status),
565                                                  status);
566         } else {
567             g_warning("This object is not CutTest object");
568         }
569     }
570 
571     return all_success;
572 }
573 
574 static void
cb_test_case_status_in(CutTestCase * test_case,CutTestContext * test_context,CutTestResult * result,gpointer data)575 cb_test_case_status_in (CutTestCase *test_case, CutTestContext *test_context,
576                         CutTestResult *result, gpointer data)
577 {
578     CutTestResultStatus *status = data;
579 
580     *status = MAX(*status, cut_test_result_get_status(result));
581 }
582 
583 static gboolean
cut_test_case_run_tests(CutTestCase * test_case,CutRunContext * run_context,const GList * tests)584 cut_test_case_run_tests (CutTestCase *test_case, CutRunContext *run_context,
585                          const GList *tests)
586 {
587     CutTestContext *test_context;
588     gboolean all_success = TRUE;
589     CutTestResult *result;
590     CutTestResultStatus status = CUT_TEST_RESULT_SUCCESS;
591     gint signum;
592     jmp_buf jump_buffer;
593     CutTestSuite *test_suite;
594     CutCrashBacktrace *crash_backtrace = NULL;
595 
596     g_signal_emit_by_name(test_case, "ready", g_list_length((GList *)tests));
597     g_signal_emit_by_name(CUT_TEST(test_case), "start", NULL);
598 
599     test_suite = cut_run_context_get_test_suite(run_context);
600     test_context = cut_test_context_new(run_context,
601                                         test_suite, test_case, NULL, NULL);
602     cut_test_context_current_push(test_context);
603 
604 #define CONNECT(event)                                          \
605     g_signal_connect(test_case, #event "-in",                   \
606                      G_CALLBACK(cb_test_case_status_in),        \
607                      &status)
608 
609     CONNECT(failure);
610     CONNECT(error);
611     CONNECT(pending);
612     CONNECT(notification);
613     CONNECT(omission);
614     CONNECT(crash);
615 
616 #undef CONNECT
617 
618     if (cut_run_context_is_multi_thread(run_context) ||
619         !cut_run_context_get_handle_signals(run_context)) {
620         signum = 0;
621     } else {
622         crash_backtrace = cut_crash_backtrace_new(&jump_buffer);
623         signum = setjmp(jump_buffer);
624     }
625     switch (signum) {
626     case 0:
627         cut_test_case_run_startup(test_case, test_context);
628         if (cut_test_context_is_failed(test_context)) {
629             all_success = FALSE;
630         } else {
631             if (status != CUT_TEST_RESULT_OMISSION)
632                 all_success = run_tests(test_case, run_context, tests, &status);
633         }
634 
635         if (crash_backtrace)
636             cut_crash_backtrace_free(crash_backtrace);
637 
638         break;
639 #ifndef G_OS_WIN32
640     case SIGSEGV:
641     case SIGABRT:
642     case SIGTERM:
643     case SIGBUS:
644         all_success = FALSE;
645         cut_crash_backtrace_emit(test_suite, test_case, NULL, NULL, NULL,
646                                  test_context);
647         break;
648     case SIGINT:
649         cut_run_context_cancel(run_context);
650         break;
651 #endif
652     default:
653         break;
654     }
655 
656     cut_test_case_run_shutdown(test_case, test_context);
657 
658     g_signal_handlers_disconnect_by_func(test_case,
659                                          G_CALLBACK(cb_test_case_status_in),
660                                          &status);
661     result = cut_test_result_new(status,
662                                  NULL, NULL, test_case, NULL, NULL,
663                                  NULL, NULL, NULL);
664     cut_test_case_emit_result_signal(test_case, result);
665     g_object_unref(result);
666 
667     g_signal_emit_by_name(CUT_TEST(test_case), "complete", NULL, all_success);
668 
669     g_object_unref(test_context);
670     cut_test_context_current_pop();
671     /* FIXME: We want to use the code:
672          g_object_unref(cut_test_context_current_pop());
673        We need to hide cut_set_current_test_context() from user.
674     */
675 
676     return all_success;
677 }
678 
679 gboolean
cut_test_case_run_test(CutTestCase * test_case,CutRunContext * run_context,const gchar * name)680 cut_test_case_run_test (CutTestCase *test_case, CutRunContext *run_context, const gchar *name)
681 {
682     const gchar *test_names[] = {NULL, NULL};
683 
684     g_return_val_if_fail(CUT_IS_TEST_CASE(test_case), FALSE);
685 
686     test_names[0] = name;
687     return cut_test_case_run_with_filter(test_case, run_context, test_names);
688 }
689 
690 gboolean
cut_test_case_run_with_filter(CutTestCase * test_case,CutRunContext * run_context,const gchar ** test_names)691 cut_test_case_run_with_filter (CutTestCase *test_case,
692                                CutRunContext *run_context,
693                                const gchar **test_names)
694 {
695     GList *filtered_tests;
696     gboolean success = TRUE;
697 
698     filtered_tests = get_filtered_tests(test_case, test_names);
699     if (!filtered_tests)
700         return TRUE;
701 
702     success = cut_test_case_run_tests(test_case, run_context, filtered_tests);
703 
704     g_list_free(filtered_tests);
705 
706     return success;
707 }
708 
709 gboolean
cut_test_case_run(CutTestCase * test_case,CutRunContext * run_context)710 cut_test_case_run (CutTestCase *test_case, CutRunContext *run_context)
711 {
712     const gchar **test_names;
713 
714     test_names = cut_run_context_get_target_test_names(run_context);
715     return cut_test_case_run_with_filter(test_case, run_context, test_names);
716 }
717 
718 /*
719 vi:nowrap:ai:expandtab:sw=4
720 */
721