1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2007-2009  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 "cut-test-result.h"
29 #include "cut-enum-types.h"
30 #include "cut-test.h"
31 #include "cut-test-iterator.h"
32 #include "cut-test-case.h"
33 #include "cut-test-suite.h"
34 #include "cut-stream-parser.h"
35 #include "cut-backtrace-entry.h"
36 #include "cut-utils.h"
37 #include "cut-readable-differ.h"
38 
39 #define MAX_DIFF_TARGET_SIZE 8092
40 
41 #define CUT_TEST_RESULT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CUT_TYPE_TEST_RESULT, CutTestResultPrivate))
42 
43 typedef struct _CutTestResultPrivate	CutTestResultPrivate;
44 struct _CutTestResultPrivate
45 {
46     CutTestResultStatus status;
47     CutTest *test;
48     CutTestIterator *test_iterator;
49     CutTestCase *test_case;
50     CutTestSuite *test_suite;
51     CutTestData *test_data;
52     gchar *message;
53     gboolean user_set_message;
54     gchar *user_message;
55     gchar *system_message;
56     GList *backtrace;
57     GTimeVal start_time;
58     gdouble elapsed;
59     gchar *expected;
60     gchar *actual;
61     gchar *diff;
62     gchar *folded_diff;
63     gboolean user_set_diff;
64     gboolean user_set_folded_diff;
65 };
66 
67 enum
68 {
69     PROP_0,
70     PROP_STATUS,
71     PROP_TEST,
72     PROP_TEST_ITERATOR,
73     PROP_TEST_CASE,
74     PROP_TEST_SUITE,
75     PROP_TEST_DATA,
76     PROP_USER_MESSAGE,
77     PROP_SYSTEM_MESSAGE,
78     PROP_BACKTRACE,
79     PROP_ELAPSED,
80     PROP_EXPECTED,
81     PROP_ACTUAL,
82     PROP_DIFF,
83     PROP_FOLDED_DIFF,
84 };
85 
86 
87 G_DEFINE_TYPE (CutTestResult, cut_test_result, 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
cut_test_result_class_init(CutTestResultClass * klass)100 cut_test_result_class_init (CutTestResultClass *klass)
101 {
102     GObjectClass *gobject_class;
103     GParamSpec *spec;
104 
105     gobject_class = G_OBJECT_CLASS(klass);
106 
107     gobject_class->dispose      = dispose;
108     gobject_class->set_property = set_property;
109     gobject_class->get_property = get_property;
110 
111     spec = g_param_spec_enum("status",
112                              "Status",
113                              "The status of the result",
114                              CUT_TYPE_TEST_RESULT_STATUS,
115                              CUT_TEST_RESULT_SUCCESS,
116                              G_PARAM_READWRITE);
117     g_object_class_install_property(gobject_class, PROP_STATUS, spec);
118 
119     spec = g_param_spec_object("test",
120                                "CutTest object",
121                                "A CutTest object",
122                                CUT_TYPE_TEST,
123                                G_PARAM_READWRITE);
124     g_object_class_install_property(gobject_class, PROP_TEST, spec);
125 
126     spec = g_param_spec_object("test-iterator",
127                                "CutTestIterator object",
128                                "A CutTestIterator object",
129                                CUT_TYPE_TEST_ITERATOR,
130                                G_PARAM_READWRITE);
131     g_object_class_install_property(gobject_class, PROP_TEST_ITERATOR, spec);
132 
133     spec = g_param_spec_object("test-case",
134                                "CutTestCase object",
135                                "A CutTestCase object",
136                                CUT_TYPE_TEST_CASE,
137                                G_PARAM_READWRITE);
138     g_object_class_install_property(gobject_class, PROP_TEST_CASE, spec);
139 
140     spec = g_param_spec_object("test-suite",
141                                "CutTestSuite object",
142                                "A CutTestSuite object",
143                                CUT_TYPE_TEST_SUITE,
144                                G_PARAM_READWRITE);
145     g_object_class_install_property(gobject_class, PROP_TEST_SUITE, spec);
146 
147     spec = g_param_spec_object("test-data",
148                                "CutTestData object",
149                                "A CutTestData object",
150                                CUT_TYPE_TEST_DATA,
151                                G_PARAM_READWRITE);
152     g_object_class_install_property(gobject_class, PROP_TEST_DATA, spec);
153 
154     spec = g_param_spec_string("user-message",
155                                "User Message",
156                                "The message from user of the result",
157                                NULL,
158                                G_PARAM_READWRITE);
159     g_object_class_install_property(gobject_class, PROP_USER_MESSAGE, spec);
160 
161     spec = g_param_spec_string("system-message",
162                                "System Message",
163                                "The message from system of the result",
164                                NULL,
165                                G_PARAM_READWRITE);
166     g_object_class_install_property(gobject_class, PROP_SYSTEM_MESSAGE, spec);
167 
168     spec = g_param_spec_pointer("backtrace",
169                                 "backtrace",
170                                 "The backtrace of the result",
171                                 G_PARAM_READWRITE);
172     g_object_class_install_property(gobject_class, PROP_BACKTRACE, spec);
173 
174     spec = g_param_spec_double("elapsed",
175                                "Elapsed time",
176                                "The time of the result",
177                                0, G_MAXDOUBLE, 0,
178                                G_PARAM_READWRITE);
179     g_object_class_install_property(gobject_class, PROP_ELAPSED, spec);
180 
181     spec = g_param_spec_string("expected",
182                                "Expected object",
183                                "The inspected string of expected object",
184                                NULL,
185                                G_PARAM_READWRITE);
186     g_object_class_install_property(gobject_class, PROP_EXPECTED, spec);
187 
188     spec = g_param_spec_string("actual",
189                                "Actual object",
190                                "The inspected string of actual object",
191                                NULL,
192                                G_PARAM_READWRITE);
193     g_object_class_install_property(gobject_class, PROP_ACTUAL, spec);
194 
195     spec = g_param_spec_string("diff",
196                                "Difference",
197                                "The difference between expected object "
198                                "and actual object",
199                                NULL,
200                                G_PARAM_READWRITE);
201     g_object_class_install_property(gobject_class, PROP_DIFF, spec);
202 
203     spec = g_param_spec_string("folded-diff",
204                                "Folded difference",
205                                "The difference between folded expected object "
206                                "and folded actual object",
207                                NULL,
208                                G_PARAM_READWRITE);
209     g_object_class_install_property(gobject_class, PROP_FOLDED_DIFF, spec);
210 
211     g_type_class_add_private(gobject_class, sizeof(CutTestResultPrivate));
212 }
213 
214 static void
cut_test_result_init(CutTestResult * result)215 cut_test_result_init (CutTestResult *result)
216 {
217     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
218 
219     priv->status = CUT_TEST_RESULT_SUCCESS;
220     priv->test = NULL;
221     priv->test_iterator = NULL;
222     priv->test_case = NULL;
223     priv->test_suite = NULL;
224     priv->test_data = NULL;
225     priv->message = NULL;
226     priv->user_set_message = FALSE;
227     priv->user_message = NULL;
228     priv->system_message = NULL;
229     priv->backtrace = NULL;
230     priv->start_time.tv_sec = 0;
231     priv->start_time.tv_usec = 0;
232     priv->elapsed = 0.0;
233     priv->expected = NULL;
234     priv->actual = NULL;
235     priv->diff = NULL;
236     priv->folded_diff= NULL;
237     priv->user_set_diff = FALSE;
238     priv->user_set_folded_diff = FALSE;
239 }
240 
241 static void
dispose(GObject * object)242 dispose (GObject *object)
243 {
244     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(object);
245 
246     if (priv->test) {
247         g_object_unref(priv->test);
248         priv->test = NULL;
249     }
250 
251     if (priv->test_iterator) {
252         g_object_unref(priv->test_iterator);
253         priv->test_iterator = NULL;
254     }
255 
256     if (priv->test_case) {
257         g_object_unref(priv->test_case);
258         priv->test_case = NULL;
259     }
260 
261     if (priv->test_suite) {
262         g_object_unref(priv->test_suite);
263         priv->test_suite = NULL;
264     }
265 
266     if (priv->test_data) {
267         g_object_unref(priv->test_data);
268         priv->test_data = NULL;
269     }
270 
271     if (priv->message) {
272         g_free(priv->message);
273         priv->message = NULL;
274     }
275 
276     if (priv->user_message) {
277         g_free(priv->user_message);
278         priv->user_message = NULL;
279     }
280 
281     if (priv->system_message) {
282         g_free(priv->system_message);
283         priv->system_message = NULL;
284     }
285 
286     if (priv->backtrace) {
287         g_list_foreach(priv->backtrace, (GFunc)g_object_unref, NULL);
288         g_list_free(priv->backtrace);
289         priv->backtrace = NULL;
290     }
291 
292     if (priv->expected) {
293         g_free(priv->expected);
294         priv->expected = NULL;
295     }
296 
297     if (priv->actual) {
298         g_free(priv->actual);
299         priv->actual = NULL;
300     }
301 
302     if (priv->diff) {
303         g_free(priv->diff);
304         priv->diff = NULL;
305     }
306 
307     if (priv->folded_diff) {
308         g_free(priv->folded_diff);
309         priv->folded_diff = NULL;
310     }
311 
312     G_OBJECT_CLASS(cut_test_result_parent_class)->dispose(object);
313 }
314 
315 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)316 set_property (GObject      *object,
317               guint         prop_id,
318               const GValue *value,
319               GParamSpec   *pspec)
320 {
321     CutTestResult *result;
322     CutTestResultPrivate *priv;
323 
324     result = CUT_TEST_RESULT(object);
325     priv = CUT_TEST_RESULT_GET_PRIVATE(result);
326 
327     switch (prop_id) {
328     case PROP_STATUS:
329         priv->status = g_value_get_enum(value);
330         break;
331     case PROP_TEST:
332         cut_test_result_set_test(result, g_value_get_object(value));
333         break;
334     case PROP_TEST_ITERATOR:
335         cut_test_result_set_test_iterator(result,
336                                           g_value_get_object(value));
337         break;
338     case PROP_TEST_CASE:
339         cut_test_result_set_test_case(result, g_value_get_object(value));
340         break;
341     case PROP_TEST_SUITE:
342         cut_test_result_set_test_suite(result, g_value_get_object(value));
343         break;
344     case PROP_TEST_DATA:
345         cut_test_result_set_test_data(result, g_value_get_object(value));
346         break;
347     case PROP_USER_MESSAGE:
348         cut_test_result_set_user_message(result, g_value_get_string(value));
349         break;
350     case PROP_SYSTEM_MESSAGE:
351         cut_test_result_set_system_message(result, g_value_get_string(value));
352         break;
353     case PROP_BACKTRACE:
354         cut_test_result_set_backtrace(result, g_value_get_pointer(value));
355         break;
356     case PROP_ELAPSED:
357         priv->elapsed = g_value_get_double(value);
358         break;
359     case PROP_EXPECTED:
360         cut_test_result_set_expected(result, g_value_get_string(value));
361         break;
362     case PROP_ACTUAL:
363         cut_test_result_set_actual(result, g_value_get_string(value));
364         break;
365     case PROP_DIFF:
366         cut_test_result_set_diff(result, g_value_get_string(value));
367         break;
368     case PROP_FOLDED_DIFF:
369         cut_test_result_set_folded_diff(result, g_value_get_string(value));
370         break;
371     default:
372         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
373         break;
374     }
375 }
376 
377 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)378 get_property (GObject    *object,
379               guint       prop_id,
380               GValue     *value,
381               GParamSpec *pspec)
382 {
383     CutTestResult *result;
384     CutTestResultPrivate *priv;
385 
386     result = CUT_TEST_RESULT(object);
387     priv = CUT_TEST_RESULT_GET_PRIVATE(result);
388 
389     switch (prop_id) {
390     case PROP_STATUS:
391         g_value_set_enum(value, priv->status);
392         break;
393     case PROP_TEST:
394         g_value_set_object(value, G_OBJECT(priv->test));
395         break;
396     case PROP_TEST_ITERATOR:
397         g_value_set_object(value, G_OBJECT(priv->test_iterator));
398         break;
399     case PROP_TEST_CASE:
400         g_value_set_object(value, G_OBJECT(priv->test_case));
401         break;
402     case PROP_TEST_SUITE:
403         g_value_set_object(value, G_OBJECT(priv->test_suite));
404         break;
405     case PROP_TEST_DATA:
406         g_value_set_object(value, G_OBJECT(priv->test_data));
407         break;
408     case PROP_USER_MESSAGE:
409         g_value_set_string(value, priv->user_message);
410         break;
411     case PROP_SYSTEM_MESSAGE:
412         g_value_set_string(value, priv->system_message);
413         break;
414     case PROP_BACKTRACE:
415         g_value_set_pointer(value, priv->backtrace);
416         break;
417     case PROP_ELAPSED:
418         g_value_set_double(value, priv->elapsed);
419         break;
420     case PROP_EXPECTED:
421         g_value_set_string(value, priv->expected);
422         break;
423     case PROP_ACTUAL:
424         g_value_set_string(value, priv->actual);
425         break;
426     case PROP_DIFF:
427         g_value_set_string(value, cut_test_result_get_diff(result));
428         break;
429     case PROP_FOLDED_DIFF:
430         g_value_set_string(value, cut_test_result_get_folded_diff(result));
431         break;
432     default:
433         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
434         break;
435     }
436 }
437 
438 CutTestResult *
cut_test_result_new(CutTestResultStatus status,CutTest * test,CutTestIterator * test_iterator,CutTestCase * test_case,CutTestSuite * test_suite,CutTestData * test_data,const gchar * user_message,const gchar * system_message,const GList * backtrace)439 cut_test_result_new (CutTestResultStatus  status,
440                      CutTest             *test,
441                      CutTestIterator     *test_iterator,
442                      CutTestCase         *test_case,
443                      CutTestSuite        *test_suite,
444                      CutTestData         *test_data,
445                      const gchar         *user_message,
446                      const gchar         *system_message,
447                      const GList         *backtrace)
448 {
449     return g_object_new(CUT_TYPE_TEST_RESULT,
450                         "status", status,
451                         "test", test,
452                         "test-iterator", test_iterator,
453                         "test-case", test_case,
454                         "test-suite", test_suite,
455                         "test-data", test_data,
456                         "user-message", user_message,
457                         "system-message", system_message,
458                         "backtrace", backtrace,
459                         NULL);
460 }
461 
462 CutTestResult *
cut_test_result_new_empty(void)463 cut_test_result_new_empty (void)
464 {
465     return cut_test_result_new(CUT_TEST_RESULT_SUCCESS,
466                                NULL, NULL, NULL, NULL, NULL,
467                                NULL, NULL, NULL);
468 }
469 
470 static void
collect_result(CutStreamParser * parser,CutTestResult * result,gpointer user_data)471 collect_result (CutStreamParser *parser, CutTestResult *result,
472                 gpointer user_data)
473 {
474     CutTestResult **result_store = (CutTestResult **)user_data;
475 
476     *result_store = result;
477     g_object_ref(*result_store);
478 }
479 
480 CutTestResult *
cut_test_result_new_from_xml(const gchar * xml,gssize length,GError ** error)481 cut_test_result_new_from_xml (const gchar *xml, gssize length, GError **error)
482 {
483     CutStreamParser *parser;
484     CutTestResult *result = NULL;
485 
486     if (!xml)
487         return NULL;
488 
489     parser = cut_test_result_parser_new();
490     g_signal_connect(parser, "result",
491                      G_CALLBACK(collect_result), (gpointer)(&result));
492 
493     cut_stream_parser_parse(parser, xml, length, error);
494 
495     g_signal_handlers_disconnect_by_func(parser,
496                                          collect_result,
497                                          (gpointer)&result);
498     g_object_unref(parser);
499 
500     return result;
501 }
502 
503 CutTestResultStatus
cut_test_result_get_status(CutTestResult * result)504 cut_test_result_get_status (CutTestResult *result)
505 {
506     return CUT_TEST_RESULT_GET_PRIVATE(result)->status;
507 }
508 
509 static void
reset_message(CutTestResultPrivate * priv)510 reset_message (CutTestResultPrivate *priv)
511 {
512     if (priv->user_set_message)
513         return;
514 
515     if (priv->message) {
516         g_free(priv->message);
517         priv->message = NULL;
518     }
519 }
520 
521 const gchar *
cut_test_result_get_message(CutTestResult * result)522 cut_test_result_get_message (CutTestResult *result)
523 {
524     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
525 
526     if (!priv->message) {
527         GString *message;
528         const gchar *diff, *folded_diff;
529 
530         message = g_string_new(NULL);
531         if (priv->user_message)
532             g_string_append(message, priv->user_message);
533 
534         if (priv->system_message) {
535             if (message->len > 0)
536                 g_string_append(message, "\n");
537             g_string_append(message, priv->system_message);
538         }
539 
540         if (priv->expected) {
541             if (message->len > 0)
542                 g_string_append(message, "\n");
543             g_string_append_printf(message, "expected: <%s>", priv->expected);
544         }
545 
546         if (priv->actual) {
547             if (message->len > 0)
548                 g_string_append(message, "\n");
549             g_string_append_printf(message, "  actual: <%s>", priv->actual);
550         }
551 
552         diff = cut_test_result_get_diff(result);
553         if (diff) {
554             if (message->len > 0)
555                 g_string_append(message, "\n\n");
556             g_string_append(message, "diff:\n");
557             g_string_append(message, diff);
558         }
559 
560         folded_diff = cut_test_result_get_folded_diff(result);
561         if (folded_diff) {
562             if (message->len > 0)
563                 g_string_append(message, "\n\n");
564             g_string_append(message, "folded diff:\n");
565             g_string_append(message, folded_diff);
566         }
567 
568         if (message->len > 0) {
569             priv->message = g_string_free(message, FALSE);
570         } else {
571             g_string_free(message, TRUE);
572         }
573     }
574 
575     return priv->message;
576 }
577 
578 CutTest *
cut_test_result_get_test(CutTestResult * result)579 cut_test_result_get_test (CutTestResult *result)
580 {
581     return CUT_TEST_RESULT_GET_PRIVATE(result)->test;
582 }
583 
584 CutTestIterator *
cut_test_result_get_test_iterator(CutTestResult * result)585 cut_test_result_get_test_iterator (CutTestResult *result)
586 {
587     return CUT_TEST_RESULT_GET_PRIVATE(result)->test_iterator;
588 }
589 
590 CutTestCase *
cut_test_result_get_test_case(CutTestResult * result)591 cut_test_result_get_test_case (CutTestResult *result)
592 {
593     return CUT_TEST_RESULT_GET_PRIVATE(result)->test_case;
594 }
595 
596 const gchar *
cut_test_result_get_test_name(CutTestResult * result)597 cut_test_result_get_test_name (CutTestResult *result)
598 {
599     CutTestResultPrivate *priv;
600 
601     priv = CUT_TEST_RESULT_GET_PRIVATE(result);
602 
603     if (priv->test)
604         return cut_test_get_full_name(priv->test);
605 
606     return NULL;
607 }
608 
609 const gchar *
cut_test_result_get_test_iterator_name(CutTestResult * result)610 cut_test_result_get_test_iterator_name (CutTestResult *result)
611 {
612     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
613 
614     if (priv->test_iterator)
615         return cut_test_get_name(CUT_TEST(priv->test_iterator));
616     return NULL;
617 }
618 
619 const gchar *
cut_test_result_get_test_case_name(CutTestResult * result)620 cut_test_result_get_test_case_name (CutTestResult *result)
621 {
622     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
623 
624     if (priv->test_case)
625         return cut_test_get_name(CUT_TEST(priv->test_case));
626     return NULL;
627 }
628 
629 CutTestSuite *
cut_test_result_get_test_suite(CutTestResult * result)630 cut_test_result_get_test_suite (CutTestResult *result)
631 {
632     return CUT_TEST_RESULT_GET_PRIVATE(result)->test_suite;
633 }
634 
635 const gchar *
cut_test_result_get_test_suite_name(CutTestResult * result)636 cut_test_result_get_test_suite_name (CutTestResult *result)
637 {
638     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
639 
640     if (priv->test_suite)
641         return cut_test_get_name(CUT_TEST(priv->test_suite));
642     return NULL;
643 }
644 
645 CutTestData *
cut_test_result_get_test_data(CutTestResult * result)646 cut_test_result_get_test_data (CutTestResult *result)
647 {
648     return CUT_TEST_RESULT_GET_PRIVATE(result)->test_data;
649 }
650 
651 const gchar *
cut_test_result_get_user_message(CutTestResult * result)652 cut_test_result_get_user_message (CutTestResult *result)
653 {
654     return CUT_TEST_RESULT_GET_PRIVATE(result)->user_message;
655 }
656 
657 const gchar *
cut_test_result_get_system_message(CutTestResult * result)658 cut_test_result_get_system_message (CutTestResult *result)
659 {
660     return CUT_TEST_RESULT_GET_PRIVATE(result)->system_message;
661 }
662 
663 const GList *
cut_test_result_get_backtrace(CutTestResult * result)664 cut_test_result_get_backtrace (CutTestResult *result)
665 {
666     return CUT_TEST_RESULT_GET_PRIVATE(result)->backtrace;
667 }
668 
669 void
cut_test_result_get_start_time(CutTestResult * result,GTimeVal * start_time)670 cut_test_result_get_start_time (CutTestResult *result, GTimeVal *start_time)
671 {
672     memcpy(start_time, &(CUT_TEST_RESULT_GET_PRIVATE(result)->start_time),
673            sizeof(GTimeVal));
674 }
675 
676 gdouble
cut_test_result_get_elapsed(CutTestResult * result)677 cut_test_result_get_elapsed (CutTestResult *result)
678 {
679     return CUT_TEST_RESULT_GET_PRIVATE(result)->elapsed;
680 }
681 
682 const gchar *
cut_test_result_get_expected(CutTestResult * result)683 cut_test_result_get_expected (CutTestResult *result)
684 {
685     return CUT_TEST_RESULT_GET_PRIVATE(result)->expected;
686 }
687 
688 const gchar *
cut_test_result_get_actual(CutTestResult * result)689 cut_test_result_get_actual (CutTestResult *result)
690 {
691     return CUT_TEST_RESULT_GET_PRIVATE(result)->actual;
692 }
693 
694 const gchar *
cut_test_result_get_diff(CutTestResult * result)695 cut_test_result_get_diff (CutTestResult *result)
696 {
697     CutTestResultPrivate *priv;
698 
699     priv = CUT_TEST_RESULT_GET_PRIVATE(result);
700 
701     if (priv->diff)
702         return priv->diff;
703 
704     if (priv->expected && priv->actual &&
705         strlen(priv->expected) < MAX_DIFF_TARGET_SIZE &&
706         strlen(priv->actual) < MAX_DIFF_TARGET_SIZE) {
707         priv->diff = cut_diff_readable(priv->expected, priv->actual);
708         if (!cut_diff_readable_is_interested(priv->diff)) {
709             g_free(priv->diff);
710             priv->diff = NULL;
711         }
712     }
713 
714     return priv->diff;
715 }
716 
717 const gchar *
cut_test_result_get_folded_diff(CutTestResult * result)718 cut_test_result_get_folded_diff (CutTestResult *result)
719 {
720     CutTestResultPrivate *priv;
721     const gchar *diff;
722 
723     priv = CUT_TEST_RESULT_GET_PRIVATE(result);
724 
725     if (priv->folded_diff)
726         return priv->folded_diff;
727 
728     diff = cut_test_result_get_diff(result);
729     if (cut_diff_readable_need_fold(diff)) {
730         priv->folded_diff =
731             cut_diff_readable_folded(priv->expected, priv->actual);
732     }
733 
734     return priv->folded_diff;
735 }
736 
737 gchar *
cut_test_result_to_xml(CutTestResult * result)738 cut_test_result_to_xml (CutTestResult *result)
739 {
740     GString *string;
741 
742     string = g_string_new(NULL);
743     cut_test_result_to_xml_string(result, string, 0);
744     return g_string_free(string, FALSE);
745 }
746 
747 static const gchar *
result_status_to_name(CutTestResultStatus status)748 result_status_to_name (CutTestResultStatus status)
749 {
750     switch (status) {
751       case CUT_TEST_RESULT_SUCCESS:
752         return "success";
753         break;
754       case CUT_TEST_RESULT_NOTIFICATION:
755         return "notification";
756         break;
757       case CUT_TEST_RESULT_OMISSION:
758         return "omission";
759         break;
760       case CUT_TEST_RESULT_PENDING:
761         return "pending";
762         break;
763       case CUT_TEST_RESULT_FAILURE:
764         return "failure";
765         break;
766       case CUT_TEST_RESULT_ERROR:
767         return "error";
768         break;
769       case CUT_TEST_RESULT_CRASH:
770         return "crash";
771         break;
772       default:
773         return "unknown status";
774         break;
775     }
776 }
777 
778 static void
append_backtrace_to_string(GString * string,CutTestResult * result,guint indent)779 append_backtrace_to_string (GString *string, CutTestResult *result, guint indent)
780 {
781     CutTestResultPrivate *priv;
782     GList *node;
783 
784     priv = CUT_TEST_RESULT_GET_PRIVATE(result);
785     if (priv->backtrace == NULL)
786         return;
787 
788     cut_utils_append_indent(string, indent);
789     g_string_append(string, "<backtrace>\n");
790     for (node = priv->backtrace; node; node = g_list_next(node)) {
791         CutBacktraceEntry *entry = node->data;
792 
793         cut_backtrace_entry_to_xml_string(entry, string, indent + 2);
794     }
795     cut_utils_append_indent(string, indent);
796     g_string_append(string, "</backtrace>\n");
797 }
798 
799 static void
append_test_result_to_string(GString * string,CutTestResult * result,guint indent)800 append_test_result_to_string (GString *string, CutTestResult *result,
801                               guint indent)
802 {
803     CutTestResultStatus status;
804     GTimeVal start_time;
805     gchar *elapsed_string, *start_time_string;
806     const gchar *message, *expected, *actual, *diff, *folded_diff;
807 
808     status = cut_test_result_get_status(result);
809     message = cut_test_result_get_message(result);
810     expected = cut_test_result_get_expected(result);
811     actual = cut_test_result_get_actual(result);
812     diff = cut_test_result_get_diff(result);
813     folded_diff = cut_test_result_get_folded_diff(result);
814 
815     cut_utils_append_xml_element_with_value(string, indent, "status",
816                                             result_status_to_name(status));
817     if (message)
818         cut_utils_append_xml_element_with_value(string, indent,
819                                                 "detail", message);
820     if (status != CUT_TEST_RESULT_SUCCESS)
821         append_backtrace_to_string(string, result, indent);
822 
823     cut_test_result_get_start_time(result, &start_time);
824     start_time_string = g_time_val_to_iso8601(&start_time);
825     cut_utils_append_xml_element_with_value(string, indent, "start-time",
826                                             start_time_string);
827     g_free(start_time_string);
828 
829     elapsed_string =
830         cut_utils_double_to_string(cut_test_result_get_elapsed(result));
831     cut_utils_append_xml_element_with_value(string, indent, "elapsed",
832                                             elapsed_string);
833     g_free(elapsed_string);
834 
835     if (expected)
836         cut_utils_append_xml_element_with_value(string, indent,
837                                                 "expected", expected);
838     if (actual)
839         cut_utils_append_xml_element_with_value(string, indent,
840                                                 "actual", actual);
841     if (diff)
842         cut_utils_append_xml_element_with_value(string, indent,
843                                                 "diff", diff);
844     if (folded_diff)
845         cut_utils_append_xml_element_with_value(string, indent,
846                                                 "folded-diff", folded_diff);
847 }
848 
849 void
cut_test_result_to_xml_string(CutTestResult * result,GString * string,guint indent)850 cut_test_result_to_xml_string (CutTestResult *result, GString *string,
851                                guint indent)
852 {
853     CutTestCase *test_case;
854     CutTestIterator *test_iterator;
855     CutTest *test;
856     CutTestData *test_data;
857 
858     cut_utils_append_indent(string, indent);
859     g_string_append(string, "<result>\n");
860 
861     test_case = cut_test_result_get_test_case(result);
862     if (test_case)
863         cut_test_to_xml_string(CUT_TEST(test_case), string, indent + 2);
864     test_iterator = cut_test_result_get_test_iterator(result);
865     if (test_iterator)
866         cut_test_to_xml_string(CUT_TEST(test_iterator), string, indent + 2);
867     test = cut_test_result_get_test(result);
868     if (test)
869         cut_test_to_xml_string(test, string, indent + 2);
870     test_data = cut_test_result_get_test_data(result);
871     if (test_data)
872         cut_test_data_to_xml_string(test_data, string, indent + 2);
873     append_test_result_to_string(string, result, indent + 2);
874 
875     cut_utils_append_indent(string, indent);
876     g_string_append(string, "</result>\n");
877 }
878 
879 const gchar *
cut_test_result_status_to_signal_name(CutTestResultStatus status)880 cut_test_result_status_to_signal_name (CutTestResultStatus status)
881 {
882     const gchar *signal_name = NULL;
883 
884     switch (status) {
885       case CUT_TEST_RESULT_SUCCESS:
886         signal_name = "success";
887         break;
888       case CUT_TEST_RESULT_NOTIFICATION:
889         signal_name = "notification";
890         break;
891       case CUT_TEST_RESULT_OMISSION:
892         signal_name = "omission";
893         break;
894       case CUT_TEST_RESULT_PENDING:
895         signal_name = "pending";
896         break;
897       case CUT_TEST_RESULT_FAILURE:
898         signal_name = "failure";
899         break;
900       case CUT_TEST_RESULT_ERROR:
901         signal_name = "error";
902         break;
903       case CUT_TEST_RESULT_CRASH:
904         signal_name = "crash";
905         break;
906       default:
907         signal_name = "invalid status";
908         break;
909     }
910 
911     return signal_name;
912 }
913 
914 gboolean
cut_test_result_status_is_critical(CutTestResultStatus status)915 cut_test_result_status_is_critical (CutTestResultStatus status)
916 {
917     return status > CUT_TEST_RESULT_OMISSION;
918 }
919 
920 void
cut_test_result_set_status(CutTestResult * result,CutTestResultStatus status)921 cut_test_result_set_status (CutTestResult *result, CutTestResultStatus status)
922 {
923     CUT_TEST_RESULT_GET_PRIVATE(result)->status = status;
924 }
925 
926 void
cut_test_result_set_test(CutTestResult * result,CutTest * test)927 cut_test_result_set_test (CutTestResult *result, CutTest *test)
928 {
929     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
930 
931     if (priv->test) {
932         g_object_unref(priv->test);
933         priv->test = NULL;
934     }
935     if (test)
936         priv->test = g_object_ref(test);
937 }
938 
939 void
cut_test_result_set_test_iterator(CutTestResult * result,CutTestIterator * test_iterator)940 cut_test_result_set_test_iterator (CutTestResult *result,
941                                    CutTestIterator *test_iterator)
942 {
943     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
944 
945     if (priv->test_iterator) {
946         g_object_unref(priv->test_iterator);
947         priv->test_iterator = NULL;
948     }
949     if (test_iterator)
950         priv->test_iterator = g_object_ref(test_iterator);
951 }
952 
953 void
cut_test_result_set_test_case(CutTestResult * result,CutTestCase * test_case)954 cut_test_result_set_test_case (CutTestResult *result, CutTestCase *test_case)
955 {
956     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
957 
958     if (priv->test_case) {
959         g_object_unref(priv->test_case);
960         priv->test_case = NULL;
961     }
962     if (test_case)
963         priv->test_case = g_object_ref(test_case);
964 }
965 
966 void
cut_test_result_set_test_suite(CutTestResult * result,CutTestSuite * test_suite)967 cut_test_result_set_test_suite (CutTestResult *result, CutTestSuite *test_suite)
968 {
969     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
970 
971     if (priv->test_suite) {
972         g_object_unref(priv->test_suite);
973         priv->test_suite = NULL;
974     }
975     if (test_suite)
976         priv->test_suite = g_object_ref(test_suite);
977 }
978 
979 void
cut_test_result_set_test_data(CutTestResult * result,CutTestData * test_data)980 cut_test_result_set_test_data (CutTestResult *result, CutTestData *test_data)
981 {
982     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
983 
984     if (priv->test_data) {
985         g_object_unref(priv->test_data);
986         priv->test_data = NULL;
987     }
988     if (test_data)
989         priv->test_data = g_object_ref(test_data);
990 }
991 
992 void
cut_test_result_set_user_message(CutTestResult * result,const gchar * user_message)993 cut_test_result_set_user_message (CutTestResult *result,
994                                   const gchar *user_message)
995 {
996     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
997 
998     if (priv->user_message) {
999         g_free(priv->user_message);
1000         priv->user_message = NULL;
1001     }
1002     if (user_message && user_message[0])
1003         priv->user_message = g_strdup(user_message);
1004 
1005     reset_message(priv);
1006 }
1007 
1008 void
cut_test_result_set_message(CutTestResult * result,const gchar * message)1009 cut_test_result_set_message (CutTestResult *result,
1010                              const gchar *message)
1011 {
1012     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
1013 
1014     if (priv->message) {
1015         g_free(priv->message);
1016         priv->message = NULL;
1017     }
1018 
1019     if (message) {
1020         priv->message = g_strdup(message);
1021         priv->user_set_message = TRUE;
1022     } else {
1023         priv->user_set_message = FALSE;
1024     }
1025 }
1026 
1027 void
cut_test_result_set_system_message(CutTestResult * result,const gchar * system_message)1028 cut_test_result_set_system_message (CutTestResult *result,
1029                                     const gchar *system_message)
1030 {
1031     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
1032 
1033     if (priv->system_message) {
1034         g_free(priv->system_message);
1035         priv->system_message = NULL;
1036     }
1037     if (system_message && system_message[0])
1038         priv->system_message = g_strdup(system_message);
1039 
1040     reset_message(priv);
1041 }
1042 
1043 void
cut_test_result_set_backtrace(CutTestResult * result,const GList * backtrace)1044 cut_test_result_set_backtrace (CutTestResult *result, const GList *backtrace)
1045 {
1046     CutTestResultPrivate *priv = CUT_TEST_RESULT_GET_PRIVATE(result);
1047 
1048     if (priv->backtrace) {
1049         g_list_foreach(priv->backtrace, (GFunc)g_object_unref, NULL);
1050         g_list_free(priv->backtrace);
1051         priv->backtrace = NULL;
1052     }
1053     priv->backtrace = g_list_copy((GList *)backtrace);
1054     g_list_foreach(priv->backtrace, (GFunc)g_object_ref, NULL);
1055 }
1056 
1057 void
cut_test_result_set_start_time(CutTestResult * result,GTimeVal * start_time)1058 cut_test_result_set_start_time (CutTestResult *result, GTimeVal *start_time)
1059 {
1060     memcpy(&(CUT_TEST_RESULT_GET_PRIVATE(result)->start_time), start_time,
1061            sizeof(GTimeVal));
1062 }
1063 
1064 void
cut_test_result_set_elapsed(CutTestResult * result,gdouble elapsed)1065 cut_test_result_set_elapsed (CutTestResult *result,
1066                              gdouble elapsed)
1067 {
1068     CUT_TEST_RESULT_GET_PRIVATE(result)->elapsed = elapsed;
1069 }
1070 
1071 static void
reset_diff(CutTestResultPrivate * priv)1072 reset_diff (CutTestResultPrivate *priv)
1073 {
1074     gboolean need_message_regeneration = FALSE;
1075 
1076     if (!priv->user_set_diff) {
1077         if (priv->diff)
1078             g_free(priv->diff);
1079         priv->diff = NULL;
1080         need_message_regeneration = TRUE;
1081     }
1082 
1083     if (!priv->user_set_folded_diff) {
1084         if (priv->folded_diff)
1085             g_free(priv->folded_diff);
1086         priv->folded_diff = NULL;
1087         need_message_regeneration = TRUE;
1088     }
1089 
1090     if (need_message_regeneration)
1091         reset_message(priv);
1092 }
1093 
1094 void
cut_test_result_set_expected(CutTestResult * result,const gchar * expected)1095 cut_test_result_set_expected (CutTestResult *result, const gchar *expected)
1096 {
1097     CutTestResultPrivate *priv;
1098 
1099     priv = CUT_TEST_RESULT_GET_PRIVATE(result);
1100 
1101     if (priv->expected) {
1102         g_free(priv->expected);
1103         priv->expected = NULL;
1104     }
1105 
1106     if (expected)
1107         priv->expected = g_strdup(expected);
1108 
1109     if (priv->expected)
1110         reset_diff(priv);
1111 }
1112 
1113 void
cut_test_result_set_actual(CutTestResult * result,const gchar * actual)1114 cut_test_result_set_actual (CutTestResult *result, const gchar *actual)
1115 {
1116     CutTestResultPrivate *priv;
1117 
1118     priv = CUT_TEST_RESULT_GET_PRIVATE(result);
1119 
1120     if (priv->actual) {
1121         g_free(priv->actual);
1122         priv->actual = NULL;
1123     }
1124 
1125     if (actual)
1126         priv->actual = g_strdup(actual);
1127 
1128     if (priv->actual)
1129         reset_diff(priv);
1130 }
1131 
1132 void
cut_test_result_set_diff(CutTestResult * result,const gchar * diff)1133 cut_test_result_set_diff (CutTestResult *result, const gchar *diff)
1134 {
1135     CutTestResultPrivate *priv;
1136 
1137     priv = CUT_TEST_RESULT_GET_PRIVATE(result);
1138 
1139     if (priv->diff) {
1140         g_free(priv->diff);
1141         priv->diff = NULL;
1142     }
1143 
1144     if (diff && diff[0]) {
1145         priv->diff = g_strdup(diff);
1146         priv->user_set_diff = TRUE;
1147     } else {
1148         priv->user_set_diff = FALSE;
1149     }
1150 }
1151 
1152 void
cut_test_result_set_folded_diff(CutTestResult * result,const gchar * folded_diff)1153 cut_test_result_set_folded_diff (CutTestResult *result, const gchar *folded_diff)
1154 {
1155     CutTestResultPrivate *priv;
1156 
1157     priv = CUT_TEST_RESULT_GET_PRIVATE(result);
1158 
1159     if (priv->folded_diff) {
1160         g_free(priv->folded_diff);
1161         priv->folded_diff = NULL;
1162     }
1163 
1164     if (folded_diff && folded_diff[0]) {
1165         priv->folded_diff = g_strdup(folded_diff);
1166         priv->user_set_folded_diff = TRUE;
1167     } else {
1168         priv->user_set_folded_diff = FALSE;
1169     }
1170 }
1171 
1172 /*
1173 vi:ts=4:nowrap:ai:expandtab:sw=4
1174 */
1175