1178848Scokane/* Miniature re-implementation of the "check" library.
2178848Scokane
3355604Sdelphij   This is intended to support just enough of check to run the Expat
4355604Sdelphij   tests.  This interface is based entirely on the portion of the
5355604Sdelphij   check library being used.
6355604Sdelphij                            __  __            _
7355604Sdelphij                         ___\ \/ /_ __   __ _| |_
8355604Sdelphij                        / _ \\  /| '_ \ / _` | __|
9355604Sdelphij                       |  __//  \| |_) | (_| | |_
10355604Sdelphij                        \___/_/\_\ .__/ \__,_|\__|
11355604Sdelphij                                 |_| XML parser
12355604Sdelphij
13355604Sdelphij   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
14355604Sdelphij   Copyright (c) 2000-2017 Expat development team
15355604Sdelphij   Licensed under the MIT license:
16355604Sdelphij
17355604Sdelphij   Permission is  hereby granted,  free of charge,  to any  person obtaining
18355604Sdelphij   a  copy  of  this  software   and  associated  documentation  files  (the
19355604Sdelphij   "Software"),  to  deal in  the  Software  without restriction,  including
20355604Sdelphij   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
21355604Sdelphij   distribute, sublicense, and/or sell copies of the Software, and to permit
22355604Sdelphij   persons  to whom  the Software  is  furnished to  do so,  subject to  the
23355604Sdelphij   following conditions:
24355604Sdelphij
25355604Sdelphij   The above copyright  notice and this permission notice  shall be included
26355604Sdelphij   in all copies or substantial portions of the Software.
27355604Sdelphij
28355604Sdelphij   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
29355604Sdelphij   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
30355604Sdelphij   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
31355604Sdelphij   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
32355604Sdelphij   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
33355604Sdelphij   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
34355604Sdelphij   USE OR OTHER DEALINGS IN THE SOFTWARE.
35355604Sdelphij*/
36355604Sdelphij
37178848Scokane#include <stdio.h>
38178848Scokane#include <stdlib.h>
39178848Scokane#include <setjmp.h>
40178848Scokane#include <assert.h>
41355604Sdelphij#include <string.h>
42178848Scokane
43355604Sdelphij#include "internal.h" /* for UNUSED_P only */
44178848Scokane#include "minicheck.h"
45178848Scokane
46178848ScokaneSuite *
47355604Sdelphijsuite_create(const char *name) {
48355604Sdelphij  Suite *suite = (Suite *)calloc(1, sizeof(Suite));
49355604Sdelphij  if (suite != NULL) {
50355604Sdelphij    suite->name = name;
51355604Sdelphij  }
52355604Sdelphij  return suite;
53178848Scokane}
54178848Scokane
55178848ScokaneTCase *
56355604Sdelphijtcase_create(const char *name) {
57355604Sdelphij  TCase *tc = (TCase *)calloc(1, sizeof(TCase));
58355604Sdelphij  if (tc != NULL) {
59355604Sdelphij    tc->name = name;
60355604Sdelphij  }
61355604Sdelphij  return tc;
62178848Scokane}
63178848Scokane
64178848Scokanevoid
65355604Sdelphijsuite_add_tcase(Suite *suite, TCase *tc) {
66355604Sdelphij  assert(suite != NULL);
67355604Sdelphij  assert(tc != NULL);
68355604Sdelphij  assert(tc->next_tcase == NULL);
69178848Scokane
70355604Sdelphij  tc->next_tcase = suite->tests;
71355604Sdelphij  suite->tests = tc;
72178848Scokane}
73178848Scokane
74178848Scokanevoid
75355604Sdelphijtcase_add_checked_fixture(TCase *tc, tcase_setup_function setup,
76355604Sdelphij                          tcase_teardown_function teardown) {
77355604Sdelphij  assert(tc != NULL);
78355604Sdelphij  tc->setup = setup;
79355604Sdelphij  tc->teardown = teardown;
80178848Scokane}
81178848Scokane
82178848Scokanevoid
83355604Sdelphijtcase_add_test(TCase *tc, tcase_test_function test) {
84355604Sdelphij  assert(tc != NULL);
85355604Sdelphij  if (tc->allocated == tc->ntests) {
86355604Sdelphij    int nalloc = tc->allocated + 100;
87355604Sdelphij    size_t new_size = sizeof(tcase_test_function) * nalloc;
88355604Sdelphij    tcase_test_function *new_tests = realloc(tc->tests, new_size);
89355604Sdelphij    assert(new_tests != NULL);
90355604Sdelphij    tc->tests = new_tests;
91355604Sdelphij    tc->allocated = nalloc;
92355604Sdelphij  }
93355604Sdelphij  tc->tests[tc->ntests] = test;
94355604Sdelphij  tc->ntests++;
95178848Scokane}
96178848Scokane
97355604Sdelphijstatic void
98355604Sdelphijtcase_free(TCase *tc) {
99355604Sdelphij  if (! tc) {
100355604Sdelphij    return;
101355604Sdelphij  }
102355604Sdelphij
103355604Sdelphij  free(tc->tests);
104355604Sdelphij  free(tc);
105355604Sdelphij}
106355604Sdelphij
107355604Sdelphijstatic void
108355604Sdelphijsuite_free(Suite *suite) {
109355604Sdelphij  if (! suite) {
110355604Sdelphij    return;
111355604Sdelphij  }
112355604Sdelphij
113355604Sdelphij  while (suite->tests != NULL) {
114355604Sdelphij    TCase *next = suite->tests->next_tcase;
115355604Sdelphij    tcase_free(suite->tests);
116355604Sdelphij    suite->tests = next;
117355604Sdelphij  }
118355604Sdelphij  free(suite);
119355604Sdelphij}
120355604Sdelphij
121178848ScokaneSRunner *
122355604Sdelphijsrunner_create(Suite *suite) {
123355604Sdelphij  SRunner *runner = calloc(1, sizeof(SRunner));
124355604Sdelphij  if (runner != NULL) {
125355604Sdelphij    runner->suite = suite;
126355604Sdelphij  }
127355604Sdelphij  return runner;
128178848Scokane}
129178848Scokane
130178848Scokanestatic jmp_buf env;
131178848Scokane
132178848Scokanestatic char const *_check_current_function = NULL;
133178848Scokanestatic int _check_current_lineno = -1;
134178848Scokanestatic char const *_check_current_filename = NULL;
135178848Scokane
136178848Scokanevoid
137355604Sdelphij_check_set_test_info(char const *function, char const *filename, int lineno) {
138355604Sdelphij  _check_current_function = function;
139355604Sdelphij  _check_current_lineno = lineno;
140355604Sdelphij  _check_current_filename = filename;
141178848Scokane}
142178848Scokane
143178848Scokanestatic void
144355604Sdelphijadd_failure(SRunner *runner, int verbosity) {
145355604Sdelphij  runner->nfailures++;
146355604Sdelphij  if (verbosity >= CK_VERBOSE) {
147355604Sdelphij    printf("%s:%d: %s\n", _check_current_filename, _check_current_lineno,
148355604Sdelphij           _check_current_function);
149355604Sdelphij  }
150178848Scokane}
151178848Scokane
152178848Scokanevoid
153355604Sdelphijsrunner_run_all(SRunner *runner, int verbosity) {
154355604Sdelphij  Suite *suite;
155355604Sdelphij  TCase *volatile tc;
156355604Sdelphij  assert(runner != NULL);
157355604Sdelphij  suite = runner->suite;
158355604Sdelphij  tc = suite->tests;
159355604Sdelphij  while (tc != NULL) {
160355604Sdelphij    volatile int i;
161355604Sdelphij    for (i = 0; i < tc->ntests; ++i) {
162355604Sdelphij      runner->nchecks++;
163178848Scokane
164355604Sdelphij      if (tc->setup != NULL) {
165355604Sdelphij        /* setup */
166355604Sdelphij        if (setjmp(env)) {
167355604Sdelphij          add_failure(runner, verbosity);
168355604Sdelphij          continue;
169355604Sdelphij        }
170355604Sdelphij        tc->setup();
171355604Sdelphij      }
172355604Sdelphij      /* test */
173355604Sdelphij      if (setjmp(env)) {
174355604Sdelphij        add_failure(runner, verbosity);
175355604Sdelphij        continue;
176355604Sdelphij      }
177355604Sdelphij      (tc->tests[i])();
178178848Scokane
179355604Sdelphij      /* teardown */
180355604Sdelphij      if (tc->teardown != NULL) {
181355604Sdelphij        if (setjmp(env)) {
182355604Sdelphij          add_failure(runner, verbosity);
183355604Sdelphij          continue;
184178848Scokane        }
185355604Sdelphij        tc->teardown();
186355604Sdelphij      }
187178848Scokane    }
188355604Sdelphij    tc = tc->next_tcase;
189355604Sdelphij  }
190355604Sdelphij  if (verbosity) {
191355604Sdelphij    int passed = runner->nchecks - runner->nfailures;
192355604Sdelphij    double percentage = ((double)passed) / runner->nchecks;
193355604Sdelphij    int display = (int)(percentage * 100);
194355604Sdelphij    printf("%d%%: Checks: %d, Failed: %d\n", display, runner->nchecks,
195355604Sdelphij           runner->nfailures);
196355604Sdelphij  }
197178848Scokane}
198178848Scokane
199178848Scokanevoid
200355604Sdelphij_fail_unless(int condition, const char *file, int line, const char *msg) {
201355604Sdelphij  /* Always print the error message so it isn't lost.  In this case,
202355604Sdelphij     we have a failure, so there's no reason to be quiet about what
203355604Sdelphij     it is.
204355604Sdelphij  */
205355604Sdelphij  UNUSED_P(condition);
206355604Sdelphij  UNUSED_P(file);
207355604Sdelphij  UNUSED_P(line);
208355604Sdelphij  if (msg != NULL) {
209355604Sdelphij    const int has_newline = (msg[strlen(msg) - 1] == '\n');
210355604Sdelphij    fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n");
211355604Sdelphij  }
212355604Sdelphij  longjmp(env, 1);
213178848Scokane}
214178848Scokane
215178848Scokaneint
216355604Sdelphijsrunner_ntests_failed(SRunner *runner) {
217355604Sdelphij  assert(runner != NULL);
218355604Sdelphij  return runner->nfailures;
219178848Scokane}
220178848Scokane
221178848Scokanevoid
222355604Sdelphijsrunner_free(SRunner *runner) {
223355604Sdelphij  if (! runner) {
224355604Sdelphij    return;
225355604Sdelphij  }
226355604Sdelphij
227355604Sdelphij  suite_free(runner->suite);
228355604Sdelphij  free(runner);
229178848Scokane}
230