1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2007-2014 Kouhei Sutou <kou@clear-code.com>
4 * Copyright (C) 2014 Kazuhiro Yamato <kz0817@gmail.com>
5 *
6 * This library is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif /* HAVE_CONFIG_H */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <signal.h>
28
29 #include <glib.h>
30
31 #include "cut-test.h"
32 #include "cut-test-container.h"
33 #include "cut-run-context.h"
34 #include "cut-test-result.h"
35 #include "cut-utils.h"
36 #include "cut-crash-backtrace.h"
37
38 #include <gcutter/gcut-marshalers.h>
39
40 #ifdef ERROR
41 # undef ERROR /* for Windows */
42 #endif
43
44 #define CUT_TEST_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CUT_TYPE_TEST, CutTestPrivate))
45
46 typedef struct _CutTestPrivate CutTestPrivate;
47 struct _CutTestPrivate
48 {
49 gchar *name;
50 gchar *full_name;
51 gchar *element_name;
52 CutTestFunction test_function;
53 GTimer *timer;
54 GTimeVal start_time;
55 gdouble elapsed;
56 GHashTable *attributes;
57 gchar *base_directory;
58 jmp_buf *jump_buffer;
59 };
60
61 enum
62 {
63 PROP_0,
64 PROP_NAME,
65 PROP_ELEMENT_NAME,
66 PROP_TEST_FUNCTION,
67 PROP_BASE_DIRECTORY
68 };
69
70 enum
71 {
72 START,
73 PASS_ASSERTION,
74 SUCCESS,
75 FAILURE,
76 ERROR,
77 PENDING,
78 NOTIFICATION,
79 OMISSION,
80 CRASH,
81 COMPLETE,
82 LAST_SIGNAL
83 };
84
85 static gint cut_test_signals[LAST_SIGNAL] = {0};
86
87 G_DEFINE_TYPE (CutTest, cut_test, G_TYPE_OBJECT)
88
89 static void dispose (GObject *object);
90 static void set_property (GObject *object,
91 guint prop_id,
92 const GValue *value,
93 GParamSpec *pspec);
94 static void get_property (GObject *object,
95 guint prop_id,
96 GValue *value,
97 GParamSpec *pspec);
98
99 static void start (CutTest *test,
100 CutTestContext *test_context);
101 static gdouble get_elapsed (CutTest *test);
102 static void set_elapsed (CutTest *test, gdouble elapsed);
103 static gboolean run (CutTest *test,
104 CutTestContext *test_context,
105 CutRunContext *run_context);
106 static void long_jump (CutTest *test,
107 jmp_buf *jump_buffer,
108 gint value);
109 static gboolean is_available (CutTest *test,
110 CutTestContext *test_context,
111 CutRunContext *run_context);
112 static void invoke (CutTest *test,
113 CutTestContext *test_context,
114 CutRunContext *run_context);
115 static void emit_result_signal
116 (CutTest *test,
117 CutTestContext *test_context,
118 CutTestResult *result);
119
120 static void
cut_test_class_init(CutTestClass * klass)121 cut_test_class_init (CutTestClass *klass)
122 {
123 GObjectClass *gobject_class;
124 GParamSpec *spec;
125
126 gobject_class = G_OBJECT_CLASS(klass);
127
128 gobject_class->dispose = dispose;
129 gobject_class->set_property = set_property;
130 gobject_class->get_property = get_property;
131
132 klass->start = start;
133 klass->get_elapsed = get_elapsed;
134 klass->set_elapsed = set_elapsed;
135 klass->run = run;
136 klass->long_jump = long_jump;
137 klass->is_available = is_available;
138 klass->invoke = invoke;
139 klass->emit_result_signal = emit_result_signal;
140
141 spec = g_param_spec_string("name",
142 "Test name",
143 "The name of the test",
144 NULL,
145 G_PARAM_READWRITE);
146 g_object_class_install_property(gobject_class, PROP_NAME, spec);
147
148 spec = g_param_spec_string("element-name",
149 "Element name",
150 "The element name of the test",
151 NULL,
152 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
153 g_object_class_install_property(gobject_class, PROP_ELEMENT_NAME, spec);
154
155 spec = g_param_spec_pointer("test-function",
156 "Test Function",
157 "The function for test",
158 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
159 g_object_class_install_property(gobject_class, PROP_TEST_FUNCTION, spec);
160
161 spec = g_param_spec_string("base-directory",
162 "Base directory",
163 "The base directory of the test",
164 NULL,
165 G_PARAM_READWRITE);
166 g_object_class_install_property(gobject_class, PROP_BASE_DIRECTORY, spec);
167
168 cut_test_signals[START]
169 = g_signal_new ("start",
170 G_TYPE_FROM_CLASS (klass),
171 G_SIGNAL_RUN_FIRST,
172 G_STRUCT_OFFSET (CutTestClass, start),
173 NULL, NULL,
174 g_cclosure_marshal_VOID__OBJECT,
175 G_TYPE_NONE, 1, CUT_TYPE_TEST_CONTEXT);
176
177 cut_test_signals[PASS_ASSERTION]
178 = g_signal_new ("pass-assertion",
179 G_TYPE_FROM_CLASS (klass),
180 G_SIGNAL_RUN_LAST,
181 G_STRUCT_OFFSET (CutTestClass, pass_assertion),
182 NULL, NULL,
183 g_cclosure_marshal_VOID__OBJECT,
184 G_TYPE_NONE, 1, CUT_TYPE_TEST_CONTEXT);
185
186 cut_test_signals[SUCCESS]
187 = g_signal_new ("success",
188 G_TYPE_FROM_CLASS (klass),
189 G_SIGNAL_RUN_LAST,
190 G_STRUCT_OFFSET (CutTestClass, success),
191 NULL, NULL,
192 _gcut_marshal_VOID__OBJECT_OBJECT,
193 G_TYPE_NONE, 2,
194 CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
195
196 cut_test_signals[FAILURE]
197 = g_signal_new ("failure",
198 G_TYPE_FROM_CLASS (klass),
199 G_SIGNAL_RUN_LAST,
200 G_STRUCT_OFFSET (CutTestClass, failure),
201 NULL, NULL,
202 _gcut_marshal_VOID__OBJECT_OBJECT,
203 G_TYPE_NONE, 2,
204 CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
205
206 cut_test_signals[ERROR]
207 = g_signal_new ("error",
208 G_TYPE_FROM_CLASS (klass),
209 G_SIGNAL_RUN_LAST,
210 G_STRUCT_OFFSET (CutTestClass, error),
211 NULL, NULL,
212 _gcut_marshal_VOID__OBJECT_OBJECT,
213 G_TYPE_NONE, 2,
214 CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
215
216 cut_test_signals[PENDING]
217 = g_signal_new ("pending",
218 G_TYPE_FROM_CLASS (klass),
219 G_SIGNAL_RUN_LAST,
220 G_STRUCT_OFFSET (CutTestClass, pending),
221 NULL, NULL,
222 _gcut_marshal_VOID__OBJECT_OBJECT,
223 G_TYPE_NONE, 2,
224 CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
225
226 cut_test_signals[NOTIFICATION]
227 = g_signal_new ("notification",
228 G_TYPE_FROM_CLASS (klass),
229 G_SIGNAL_RUN_LAST,
230 G_STRUCT_OFFSET (CutTestClass, notification),
231 NULL, NULL,
232 _gcut_marshal_VOID__OBJECT_OBJECT,
233 G_TYPE_NONE, 2,
234 CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
235
236 cut_test_signals[OMISSION]
237 = g_signal_new("omission",
238 G_TYPE_FROM_CLASS(klass),
239 G_SIGNAL_RUN_LAST,
240 G_STRUCT_OFFSET(CutTestClass, omission),
241 NULL, NULL,
242 _gcut_marshal_VOID__OBJECT_OBJECT,
243 G_TYPE_NONE, 2,
244 CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
245
246 cut_test_signals[CRASH]
247 = g_signal_new("crash",
248 G_TYPE_FROM_CLASS(klass),
249 G_SIGNAL_RUN_LAST,
250 G_STRUCT_OFFSET(CutTestClass, crash),
251 NULL, NULL,
252 _gcut_marshal_VOID__OBJECT_OBJECT,
253 G_TYPE_NONE, 2,
254 CUT_TYPE_TEST_CONTEXT, CUT_TYPE_TEST_RESULT);
255
256 cut_test_signals[COMPLETE]
257 = g_signal_new("complete",
258 G_TYPE_FROM_CLASS (klass),
259 G_SIGNAL_RUN_LAST,
260 G_STRUCT_OFFSET (CutTestClass, complete),
261 NULL, NULL,
262 _gcut_marshal_VOID__OBJECT_BOOLEAN,
263 G_TYPE_NONE, 2, CUT_TYPE_TEST_CONTEXT, G_TYPE_BOOLEAN);
264
265
266 g_type_class_add_private(gobject_class, sizeof(CutTestPrivate));
267 }
268
269 static void
cut_test_init(CutTest * test)270 cut_test_init (CutTest *test)
271 {
272 CutTestPrivate *priv = CUT_TEST_GET_PRIVATE(test);
273
274 priv->full_name = NULL;
275
276 priv->test_function = NULL;
277 priv->timer = NULL;
278 priv->start_time.tv_sec = 0;
279 priv->start_time.tv_usec = 0;
280 priv->elapsed = -1.0;
281 priv->attributes = g_hash_table_new_full(g_str_hash, g_str_equal,
282 g_free, g_free);
283 priv->jump_buffer = NULL;
284 }
285
286 static void
free_full_name(CutTestPrivate * priv)287 free_full_name (CutTestPrivate *priv)
288 {
289 if (priv->full_name) {
290 g_free(priv->full_name);
291 priv->full_name = NULL;
292 }
293 }
294
295 static void
dispose(GObject * object)296 dispose (GObject *object)
297 {
298 CutTestPrivate *priv = CUT_TEST_GET_PRIVATE(object);
299
300 if (priv->name) {
301 g_free(priv->name);
302 priv->name = NULL;
303 }
304
305 if (priv->element_name) {
306 g_free(priv->element_name);
307 priv->element_name = NULL;
308 }
309
310 priv->test_function = NULL;
311
312 if (priv->timer) {
313 g_timer_destroy(priv->timer);
314 priv->timer = NULL;
315 }
316
317 if (priv->attributes) {
318 g_hash_table_unref(priv->attributes);
319 priv->attributes = NULL;
320 }
321
322 free_full_name(priv);
323
324 if (priv->base_directory) {
325 g_free(priv->base_directory);
326 priv->base_directory = NULL;
327 }
328
329 G_OBJECT_CLASS(cut_test_parent_class)->dispose(object);
330 }
331
332 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)333 set_property (GObject *object,
334 guint prop_id,
335 const GValue *value,
336 GParamSpec *pspec)
337 {
338 CutTestPrivate *priv = CUT_TEST_GET_PRIVATE(object);
339
340 switch (prop_id) {
341 case PROP_NAME:
342 cut_test_set_name(CUT_TEST(object), g_value_get_string(value));
343 break;
344 case PROP_ELEMENT_NAME:
345 if (priv->element_name) {
346 g_free(priv->element_name);
347 priv->element_name = NULL;
348 }
349 priv->element_name = g_value_dup_string(value);
350 break;
351 case PROP_TEST_FUNCTION:
352 priv->test_function = g_value_get_pointer(value);
353 break;
354 case PROP_BASE_DIRECTORY:
355 cut_test_set_base_directory(CUT_TEST(object), g_value_get_string(value));
356 break;
357 default:
358 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
359 break;
360 }
361 }
362
363 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)364 get_property (GObject *object,
365 guint prop_id,
366 GValue *value,
367 GParamSpec *pspec)
368 {
369 CutTestPrivate *priv = CUT_TEST_GET_PRIVATE(object);
370
371 switch (prop_id) {
372 case PROP_NAME:
373 g_value_set_string(value, priv->name);
374 break;
375 case PROP_ELEMENT_NAME:
376 g_value_set_string(value, priv->element_name);
377 break;
378 case PROP_TEST_FUNCTION:
379 g_value_set_pointer(value, priv->test_function);
380 break;
381 case PROP_BASE_DIRECTORY:
382 g_value_set_string(value, priv->base_directory);
383 break;
384 default:
385 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
386 break;
387 }
388 }
389
390 CutTest *
cut_test_new(const gchar * name,CutTestFunction function)391 cut_test_new (const gchar *name, CutTestFunction function)
392 {
393 return g_object_new(CUT_TYPE_TEST,
394 "element-name", "test",
395 "name", name,
396 "test-function", function,
397 NULL);
398 }
399
400 CutTest *
cut_test_new_empty(void)401 cut_test_new_empty (void)
402 {
403 return cut_test_new(NULL, NULL);
404 }
405
406 static void
start(CutTest * test,CutTestContext * test_context)407 start (CutTest *test, CutTestContext *test_context)
408 {
409 GTimeVal value;
410 g_get_current_time(&value);
411 cut_test_set_start_time(test, &value);
412 }
413
414 static gboolean
is_available(CutTest * test,CutTestContext * test_context,CutRunContext * run_context)415 is_available (CutTest *test, CutTestContext *test_context,
416 CutRunContext *run_context)
417 {
418 return CUT_TEST_GET_PRIVATE(test)->test_function != NULL;
419 }
420
421 static void
invoke(CutTest * test,CutTestContext * test_context,CutRunContext * run_context)422 invoke (CutTest *test, CutTestContext *test_context, CutRunContext *run_context)
423 {
424 CutTestPrivate *priv;
425
426 priv = CUT_TEST_GET_PRIVATE(test);
427 if (cut_run_context_get_stop_before_test(run_context))
428 G_BREAKPOINT();
429 priv->test_function();
430 }
431
432 static gboolean
run(CutTest * test,CutTestContext * test_context,CutRunContext * run_context)433 run (CutTest *test, CutTestContext *test_context, CutRunContext *run_context)
434 {
435 CutTestClass *klass;
436 CutTestPrivate *priv;
437 gboolean success = TRUE;
438 jmp_buf jump_buffer;
439 CutTestCase *test_case;
440 CutTestIterator *test_iterator;
441 CutTestResult *result;
442 CutTestData *data = NULL;
443 gint signum;
444 jmp_buf crash_jump_buffer;
445 CutCrashBacktrace *crash_backtrace = NULL;
446
447 priv = CUT_TEST_GET_PRIVATE(test);
448 klass = CUT_TEST_GET_CLASS(test);
449
450 priv->jump_buffer = &jump_buffer;
451
452 if (!klass->is_available(test, test_context, run_context)) {
453 priv->jump_buffer = NULL;
454 return FALSE;
455 }
456
457 test_case = cut_test_context_get_test_case(test_context);
458 test_iterator = cut_test_context_get_test_iterator(test_context);
459 if (CUT_IS_ITERATED_TEST(test))
460 data = cut_iterated_test_get_data(CUT_ITERATED_TEST(test));
461
462 if (cut_run_context_is_multi_thread(run_context) ||
463 !cut_run_context_get_handle_signals(run_context)) {
464 signum = 0;
465 } else {
466 crash_backtrace = cut_crash_backtrace_new(&crash_jump_buffer);
467 signum = setjmp(crash_jump_buffer);
468 }
469 switch (signum) {
470 case 0:
471 g_signal_emit_by_name(test, "start", test_context);
472
473 cut_test_context_set_jump_buffer(test_context, &jump_buffer);
474 if (setjmp(jump_buffer) == 0) {
475 if (priv->timer) {
476 g_timer_start(priv->timer);
477 } else {
478 priv->timer = g_timer_new();
479 }
480 klass->invoke(test, test_context, run_context);
481 }
482 g_timer_stop(priv->timer);
483
484 success = !cut_test_context_is_failed(test_context);
485
486 if (crash_backtrace)
487 cut_crash_backtrace_free(crash_backtrace);
488 break;
489 #ifndef G_OS_WIN32
490 case SIGSEGV:
491 case SIGABRT:
492 case SIGTERM:
493 case SIGBUS:
494 success = FALSE;
495 {
496 CutTestSuite *test_suite;
497 test_suite = cut_test_context_get_test_suite(test_context);
498 cut_crash_backtrace_emit(test_suite, test_case,
499 test, test_iterator, data,
500 test_context);
501 }
502 break;
503 case SIGINT:
504 cut_run_context_cancel(run_context);
505 break;
506 #endif
507 default:
508 break;
509 }
510
511 if (success) {
512 result = cut_test_result_new(CUT_TEST_RESULT_SUCCESS,
513 test, test_iterator, test_case,
514 NULL, data,
515 NULL, NULL, NULL);
516 cut_test_emit_result_signal(test, test_context, result);
517 g_object_unref(result);
518 }
519
520 g_signal_emit_by_name(test, "complete", test_context, success);
521
522 priv->jump_buffer = NULL;
523
524 return success;
525 }
526
527 static void
long_jump(CutTest * test,jmp_buf * jump_buffer,gint value)528 long_jump (CutTest *test, jmp_buf *jump_buffer, gint value)
529 {
530 longjmp(*jump_buffer, value);
531 }
532
533 gboolean
cut_test_run(CutTest * test,CutTestContext * test_context,CutRunContext * run_context)534 cut_test_run (CutTest *test, CutTestContext *test_context,
535 CutRunContext *run_context)
536 {
537 return CUT_TEST_GET_CLASS(test)->run(test, test_context, run_context);
538 }
539
540 void
cut_test_long_jump(CutTest * test,jmp_buf * jump_buffer,gint value)541 cut_test_long_jump (CutTest *test, jmp_buf *jump_buffer, gint value)
542 {
543 return CUT_TEST_GET_CLASS(test)->long_jump(test, jump_buffer, value);
544 }
545
546 gboolean
cut_test_is_own_jump_buffer(CutTest * test,jmp_buf * jump_buffer)547 cut_test_is_own_jump_buffer (CutTest *test, jmp_buf *jump_buffer)
548 {
549 CutTestPrivate *priv = CUT_TEST_GET_PRIVATE(test);
550
551 return priv->jump_buffer == jump_buffer;
552 }
553
554 const gchar *
cut_test_get_name(CutTest * test)555 cut_test_get_name (CutTest *test)
556 {
557 return CUT_TEST_GET_PRIVATE(test)->name;
558 }
559
560 void
cut_test_set_name(CutTest * test,const gchar * name)561 cut_test_set_name (CutTest *test, const gchar *name)
562 {
563 CutTestPrivate *priv = CUT_TEST_GET_PRIVATE(test);
564
565 if (priv->name) {
566 g_free(priv->name);
567 priv->name = NULL;
568 }
569
570 free_full_name(priv);
571
572 if (name)
573 priv->name = g_strdup(name);
574 }
575
576 const gchar *
cut_test_get_full_name(CutTest * test)577 cut_test_get_full_name (CutTest *test)
578 {
579 CutTestPrivate *priv;
580 CutTestData *data = NULL;
581
582 priv = CUT_TEST_GET_PRIVATE(test);
583 if (priv->full_name)
584 return priv->full_name;
585
586 if (CUT_IS_ITERATED_TEST(test))
587 data = cut_iterated_test_get_data(CUT_ITERATED_TEST(test));
588
589 if (priv->name && data) {
590 priv->full_name = g_strconcat(priv->name,
591 " (",
592 cut_test_data_get_name(data),
593 ")",
594 NULL);
595 } else if (priv->name) {
596 priv->full_name = g_strdup(priv->name);
597 } else if (data) {
598 priv->full_name = g_strconcat(" (",
599 cut_test_data_get_name(data),
600 ")",
601 NULL);
602 }
603
604 return priv->full_name;
605 }
606
607 const gchar *
cut_test_get_description(CutTest * test)608 cut_test_get_description (CutTest *test)
609 {
610 return cut_test_get_attribute(test, "description");
611 }
612
613 void
cut_test_get_start_time(CutTest * test,GTimeVal * start_time)614 cut_test_get_start_time (CutTest *test, GTimeVal *start_time)
615 {
616 memcpy(start_time, &(CUT_TEST_GET_PRIVATE(test)->start_time),
617 sizeof(GTimeVal));
618 }
619
620 void
cut_test_set_start_time(CutTest * test,GTimeVal * start_time)621 cut_test_set_start_time (CutTest *test, GTimeVal *start_time)
622 {
623 memcpy(&(CUT_TEST_GET_PRIVATE(test)->start_time), start_time,
624 sizeof(GTimeVal));
625 }
626
627 static gdouble
get_elapsed(CutTest * test)628 get_elapsed (CutTest *test)
629 {
630 CutTestPrivate *priv = CUT_TEST_GET_PRIVATE(test);
631
632 if (!(priv->elapsed < 0.0))
633 return priv->elapsed;
634
635 if (priv->timer)
636 return g_timer_elapsed(priv->timer, NULL);
637 else
638 return 0.0;
639 }
640
641 gdouble
cut_test_get_elapsed(CutTest * test)642 cut_test_get_elapsed (CutTest *test)
643 {
644 return CUT_TEST_GET_CLASS(test)->get_elapsed(test);
645 }
646
647 static void
set_elapsed(CutTest * test,gdouble elapsed)648 set_elapsed (CutTest *test, gdouble elapsed)
649 {
650 CUT_TEST_GET_PRIVATE(test)->elapsed = elapsed;
651 }
652
653 void
cut_test_set_elapsed(CutTest * test,gdouble elapsed)654 cut_test_set_elapsed (CutTest *test, gdouble elapsed)
655 {
656 CUT_TEST_GET_CLASS(test)->set_elapsed(test, elapsed);
657 }
658
659 const gchar *
cut_test_get_attribute(CutTest * test,const gchar * name)660 cut_test_get_attribute (CutTest *test, const gchar *name)
661 {
662 return g_hash_table_lookup(CUT_TEST_GET_PRIVATE(test)->attributes, name);
663 }
664
665 void
cut_test_set_attribute(CutTest * test,const gchar * name,const gchar * value)666 cut_test_set_attribute (CutTest *test, const gchar *name, const gchar *value)
667 {
668 g_hash_table_replace(CUT_TEST_GET_PRIVATE(test)->attributes,
669 g_strdup(name),
670 g_strdup(value));
671 }
672
673 GHashTable *
cut_test_get_attributes(CutTest * test)674 cut_test_get_attributes (CutTest *test)
675 {
676 return CUT_TEST_GET_PRIVATE(test)->attributes;
677 }
678
679 const gchar *
cut_test_get_base_directory(CutTest * test)680 cut_test_get_base_directory (CutTest *test)
681 {
682 return CUT_TEST_GET_PRIVATE(test)->base_directory;
683 }
684
685 void
cut_test_set_base_directory(CutTest * test,const gchar * base_directory)686 cut_test_set_base_directory (CutTest *test, const gchar *base_directory)
687 {
688 CutTestPrivate *priv = CUT_TEST_GET_PRIVATE(test);
689
690 if (priv->base_directory) {
691 g_free(priv->base_directory);
692 priv->base_directory = NULL;
693 }
694 priv->base_directory = g_strdup(base_directory);
695 }
696
697 gchar *
cut_test_to_xml(CutTest * test)698 cut_test_to_xml (CutTest *test)
699 {
700 GString *string;
701
702 string = g_string_new(NULL);
703 cut_test_to_xml_string(test, string, 0);
704 return g_string_free(string, FALSE);
705 }
706
707 typedef struct _AppendAttributeInfo {
708 GString *string;
709 guint indent;
710 } AppendAttributeInfo;
711
712 static void
append_attribute(const gchar * key,const gchar * value,AppendAttributeInfo * info)713 append_attribute (const gchar *key, const gchar *value,
714 AppendAttributeInfo *info)
715 {
716 if (strcmp(key, "description") == 0)
717 return;
718
719 cut_utils_append_indent(info->string, info->indent);
720 g_string_append(info->string, "<option>\n");
721
722 cut_utils_append_xml_element_with_value(info->string, info->indent + 2,
723 "name", key);
724 cut_utils_append_xml_element_with_value(info->string, info->indent + 2,
725 "value", value);
726
727 cut_utils_append_indent(info->string, info->indent);
728 g_string_append(info->string, "</option>\n");
729 }
730
731 void
cut_test_to_xml_string(CutTest * test,GString * string,guint indent)732 cut_test_to_xml_string (CutTest *test, GString *string, guint indent)
733 {
734 CutTestPrivate *priv;
735 gchar *escaped, *start_time, *elapsed;
736 const gchar *description, *name;
737 GHashTable *attributes;
738
739 priv = CUT_TEST_GET_PRIVATE(test);
740
741 escaped = g_markup_escape_text(priv->element_name, -1);
742 cut_utils_append_indent(string, indent);
743 g_string_append_printf(string, "<%s>\n", escaped);
744
745 name = cut_test_get_name(test);
746 if (name)
747 cut_utils_append_xml_element_with_value(string, indent + 2,
748 "name", name);
749
750 description = cut_test_get_description(test);
751 if (description)
752 cut_utils_append_xml_element_with_value(string, indent + 2,
753 "description", description);
754
755 start_time = g_time_val_to_iso8601(&(priv->start_time));
756 cut_utils_append_xml_element_with_value(string, indent + 2,
757 "start-time", start_time);
758 g_free(start_time);
759
760 elapsed = cut_utils_double_to_string(cut_test_get_elapsed(test));
761 cut_utils_append_xml_element_with_value(string, indent + 2,
762 "elapsed", elapsed);
763 g_free(elapsed);
764
765 attributes = cut_test_get_attributes(test);
766 if (attributes) {
767 AppendAttributeInfo info;
768
769 info.string = string;
770 info.indent = indent + 2;
771 g_hash_table_foreach(attributes, (GHFunc)append_attribute, &info);
772 }
773
774 cut_utils_append_indent(string, indent);
775 g_string_append_printf(string, "</%s>\n", escaped);
776 g_free(escaped);
777 }
778
779 void
cut_test_set_result_elapsed(CutTest * test,CutTestResult * result)780 cut_test_set_result_elapsed (CutTest *test, CutTestResult *result)
781 {
782 CutTestPrivate *priv;
783
784 priv = CUT_TEST_GET_PRIVATE(test);
785 cut_test_result_set_start_time(result, &(priv->start_time));
786 cut_test_result_set_elapsed(result, cut_test_get_elapsed(test));
787 }
788
789 static void
emit_result_signal(CutTest * test,CutTestContext * test_context,CutTestResult * result)790 emit_result_signal (CutTest *test,
791 CutTestContext *test_context,
792 CutTestResult *result)
793 {
794 const gchar *status_signal_name = NULL;
795 CutTestResultStatus status;
796
797 status = cut_test_result_get_status(result);
798 status_signal_name = cut_test_result_status_to_signal_name(status);
799 g_signal_emit_by_name(test, status_signal_name, test_context, result);
800 }
801
802 void
cut_test_emit_result_signal(CutTest * test,CutTestContext * test_context,CutTestResult * result)803 cut_test_emit_result_signal (CutTest *test,
804 CutTestContext *test_context,
805 CutTestResult *result)
806 {
807 cut_test_set_result_elapsed(test, result);
808
809 CUT_TEST_GET_CLASS(test)->emit_result_signal(test, test_context, result);
810 }
811
812 /*
813 vi:ts=4:nowrap:ai:expandtab:sw=4
814 */
815