1/* Miniature re-implementation of the "check" library.
2
3   This is intended to support just enough of check to run the Expat
4   tests.  This interface is based entirely on the portion of the
5   check library being used.
6                            __  __            _
7                         ___\ \/ /_ __   __ _| |_
8                        / _ \\  /| '_ \ / _` | __|
9                       |  __//  \| |_) | (_| | |_
10                        \___/_/\_\ .__/ \__,_|\__|
11                                 |_| XML parser
12
13   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
14   Copyright (c) 2000-2017 Expat development team
15   Licensed under the MIT license:
16
17   Permission is  hereby granted,  free of charge,  to any  person obtaining
18   a  copy  of  this  software   and  associated  documentation  files  (the
19   "Software"),  to  deal in  the  Software  without restriction,  including
20   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
21   distribute, sublicense, and/or sell copies of the Software, and to permit
22   persons  to whom  the Software  is  furnished to  do so,  subject to  the
23   following conditions:
24
25   The above copyright  notice and this permission notice  shall be included
26   in all copies or substantial portions of the Software.
27
28   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
29   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
30   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
31   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
32   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
33   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
34   USE OR OTHER DEALINGS IN THE SOFTWARE.
35*/
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <setjmp.h>
40#include <assert.h>
41#include <string.h>
42
43#include "internal.h" /* for UNUSED_P only */
44#include "minicheck.h"
45
46Suite *
47suite_create(const char *name) {
48  Suite *suite = (Suite *)calloc(1, sizeof(Suite));
49  if (suite != NULL) {
50    suite->name = name;
51  }
52  return suite;
53}
54
55TCase *
56tcase_create(const char *name) {
57  TCase *tc = (TCase *)calloc(1, sizeof(TCase));
58  if (tc != NULL) {
59    tc->name = name;
60  }
61  return tc;
62}
63
64void
65suite_add_tcase(Suite *suite, TCase *tc) {
66  assert(suite != NULL);
67  assert(tc != NULL);
68  assert(tc->next_tcase == NULL);
69
70  tc->next_tcase = suite->tests;
71  suite->tests = tc;
72}
73
74void
75tcase_add_checked_fixture(TCase *tc, tcase_setup_function setup,
76                          tcase_teardown_function teardown) {
77  assert(tc != NULL);
78  tc->setup = setup;
79  tc->teardown = teardown;
80}
81
82void
83tcase_add_test(TCase *tc, tcase_test_function test) {
84  assert(tc != NULL);
85  if (tc->allocated == tc->ntests) {
86    int nalloc = tc->allocated + 100;
87    size_t new_size = sizeof(tcase_test_function) * nalloc;
88    tcase_test_function *new_tests = realloc(tc->tests, new_size);
89    assert(new_tests != NULL);
90    tc->tests = new_tests;
91    tc->allocated = nalloc;
92  }
93  tc->tests[tc->ntests] = test;
94  tc->ntests++;
95}
96
97static void
98tcase_free(TCase *tc) {
99  if (! tc) {
100    return;
101  }
102
103  free(tc->tests);
104  free(tc);
105}
106
107static void
108suite_free(Suite *suite) {
109  if (! suite) {
110    return;
111  }
112
113  while (suite->tests != NULL) {
114    TCase *next = suite->tests->next_tcase;
115    tcase_free(suite->tests);
116    suite->tests = next;
117  }
118  free(suite);
119}
120
121SRunner *
122srunner_create(Suite *suite) {
123  SRunner *runner = calloc(1, sizeof(SRunner));
124  if (runner != NULL) {
125    runner->suite = suite;
126  }
127  return runner;
128}
129
130static jmp_buf env;
131
132static char const *_check_current_function = NULL;
133static int _check_current_lineno = -1;
134static char const *_check_current_filename = NULL;
135
136void
137_check_set_test_info(char const *function, char const *filename, int lineno) {
138  _check_current_function = function;
139  _check_current_lineno = lineno;
140  _check_current_filename = filename;
141}
142
143static void
144add_failure(SRunner *runner, int verbosity) {
145  runner->nfailures++;
146  if (verbosity >= CK_VERBOSE) {
147    printf("%s:%d: %s\n", _check_current_filename, _check_current_lineno,
148           _check_current_function);
149  }
150}
151
152void
153srunner_run_all(SRunner *runner, int verbosity) {
154  Suite *suite;
155  TCase *volatile tc;
156  assert(runner != NULL);
157  suite = runner->suite;
158  tc = suite->tests;
159  while (tc != NULL) {
160    volatile int i;
161    for (i = 0; i < tc->ntests; ++i) {
162      runner->nchecks++;
163
164      if (tc->setup != NULL) {
165        /* setup */
166        if (setjmp(env)) {
167          add_failure(runner, verbosity);
168          continue;
169        }
170        tc->setup();
171      }
172      /* test */
173      if (setjmp(env)) {
174        add_failure(runner, verbosity);
175        continue;
176      }
177      (tc->tests[i])();
178
179      /* teardown */
180      if (tc->teardown != NULL) {
181        if (setjmp(env)) {
182          add_failure(runner, verbosity);
183          continue;
184        }
185        tc->teardown();
186      }
187    }
188    tc = tc->next_tcase;
189  }
190  if (verbosity) {
191    int passed = runner->nchecks - runner->nfailures;
192    double percentage = ((double)passed) / runner->nchecks;
193    int display = (int)(percentage * 100);
194    printf("%d%%: Checks: %d, Failed: %d\n", display, runner->nchecks,
195           runner->nfailures);
196  }
197}
198
199void
200_fail_unless(int condition, const char *file, int line, const char *msg) {
201  /* Always print the error message so it isn't lost.  In this case,
202     we have a failure, so there's no reason to be quiet about what
203     it is.
204  */
205  UNUSED_P(condition);
206  UNUSED_P(file);
207  UNUSED_P(line);
208  if (msg != NULL) {
209    const int has_newline = (msg[strlen(msg) - 1] == '\n');
210    fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n");
211  }
212  longjmp(env, 1);
213}
214
215int
216srunner_ntests_failed(SRunner *runner) {
217  assert(runner != NULL);
218  return runner->nfailures;
219}
220
221void
222srunner_free(SRunner *runner) {
223  if (! runner) {
224    return;
225  }
226
227  suite_free(runner->suite);
228  free(runner);
229}
230