1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "abts.h"
18#include "abts_tests.h"
19#include "testutil.h"
20
21#define ABTS_STAT_SIZE 6
22static char status[ABTS_STAT_SIZE] = {'|', '/', '-', '|', '\\', '-'};
23static int curr_char;
24static int verbose = 0;
25static int exclude = 0;
26static int quiet = 0;
27static int list_tests = 0;
28
29const char **testlist = NULL;
30
31static int find_test_name(const char *testname) {
32    int i;
33    for (i = 0; testlist[i] != NULL; i++) {
34        if (!strcmp(testlist[i], testname)) {
35            return 1;
36        }
37    }
38    return 0;
39}
40
41/* Determine if the test should be run at all */
42static int should_test_run(const char *testname) {
43    int found = 0;
44    if (list_tests == 1) {
45        return 0;
46    }
47    if (testlist == NULL) {
48        return 1;
49    }
50    found = find_test_name(testname);
51    if ((found && !exclude) || (!found && exclude)) {
52        return 1;
53    }
54    return 0;
55}
56
57static void reset_status(void)
58{
59    curr_char = 0;
60}
61
62static void update_status(void)
63{
64    if (!quiet) {
65        curr_char = (curr_char + 1) % ABTS_STAT_SIZE;
66        fprintf(stdout, "\b%c", status[curr_char]);
67        fflush(stdout);
68    }
69}
70
71static void end_suite(abts_suite *suite)
72{
73    if (suite != NULL) {
74        sub_suite *last = suite->tail;
75        if (!quiet) {
76            fprintf(stdout, "\b");
77            fflush(stdout);
78        }
79        if (last->failed == 0) {
80            fprintf(stdout, "SUCCESS\n");
81            fflush(stdout);
82        }
83        else {
84            fprintf(stdout, "FAILED %d of %d\n", last->failed, last->num_test);
85            fflush(stdout);
86        }
87    }
88}
89
90abts_suite *abts_add_suite(abts_suite *suite, const char *suite_name_full)
91{
92    sub_suite *subsuite;
93    char *p;
94    const char *suite_name;
95    curr_char = 0;
96
97    /* Only end the suite if we actually ran it */
98    if (suite && suite->tail &&!suite->tail->not_run) {
99        end_suite(suite);
100    }
101
102    subsuite = malloc(sizeof(*subsuite));
103    subsuite->num_test = 0;
104    subsuite->failed = 0;
105    subsuite->next = NULL;
106    /* suite_name_full may be an absolute path depending on __FILE__
107     * expansion */
108    suite_name = strrchr(suite_name_full, '/');
109    if (suite_name) {
110        suite_name++;
111    } else {
112        suite_name = suite_name_full;
113    }
114    p = strrchr(suite_name, '.');
115    if (p) {
116        subsuite->name = memcpy(calloc(p - suite_name + 1, 1),
117                                suite_name, p - suite_name);
118    }
119    else {
120        subsuite->name = suite_name;
121    }
122
123    if (list_tests) {
124        fprintf(stdout, "%s\n", subsuite->name);
125    }
126
127    subsuite->not_run = 0;
128
129    if (suite == NULL) {
130        suite = malloc(sizeof(*suite));
131        suite->head = subsuite;
132        suite->tail = subsuite;
133    }
134    else {
135        suite->tail->next = subsuite;
136        suite->tail = subsuite;
137    }
138
139    if (!should_test_run(subsuite->name)) {
140        subsuite->not_run = 1;
141        return suite;
142    }
143
144    reset_status();
145    fprintf(stdout, "%-20s:  ", subsuite->name);
146    update_status();
147    fflush(stdout);
148
149    return suite;
150}
151
152void abts_run_test(abts_suite *ts, test_func f, void *value)
153{
154    abts_case *tc;
155    sub_suite *ss;
156
157    if (!should_test_run(ts->tail->name)) {
158        return;
159    }
160    ss = ts->tail;
161
162    tc = malloc(sizeof(*tc));
163    tc->failed = 0;
164    tc->suite = ss;
165
166    ss->num_test++;
167    update_status();
168
169    f(tc, value);
170
171    if (tc->failed) {
172        ss->failed++;
173    }
174    free(tc);
175}
176
177static int report(abts_suite *suite)
178{
179    int count = 0;
180    sub_suite *dptr;
181
182    if (suite && suite->tail &&!suite->tail->not_run) {
183        end_suite(suite);
184    }
185
186    for (dptr = suite->head; dptr; dptr = dptr->next) {
187        count += dptr->failed;
188    }
189
190    if (list_tests) {
191        return 0;
192    }
193
194    if (count == 0) {
195        printf("All tests passed.\n");
196        return 0;
197    }
198
199    dptr = suite->head;
200    fprintf(stdout, "%-15s\t\tTotal\tFail\tFailed %%\n", "Failed Tests");
201    fprintf(stdout, "===================================================\n");
202    while (dptr != NULL) {
203        if (dptr->failed != 0) {
204            float percent = ((float)dptr->failed / (float)dptr->num_test);
205            fprintf(stdout, "%-15s\t\t%5d\t%4d\t%6.2f%%\n", dptr->name,
206                    dptr->num_test, dptr->failed, percent * 100);
207        }
208        dptr = dptr->next;
209    }
210    return 1;
211}
212
213void abts_log_message(const char *fmt, ...)
214{
215    va_list args;
216    update_status();
217
218    if (verbose) {
219        va_start(args, fmt);
220        vfprintf(stderr, fmt, args);
221        va_end(args);
222        fprintf(stderr, "\n");
223        fflush(stderr);
224    }
225}
226
227void abts_int_equal(abts_case *tc, const int expected, const int actual, int lineno)
228{
229    update_status();
230    if (tc->failed) return;
231
232    if (expected == actual) return;
233
234    tc->failed = TRUE;
235    if (verbose) {
236        fprintf(stderr, "Line %d: expected <%d>, but saw <%d>\n", lineno, expected, actual);
237        fflush(stderr);
238    }
239}
240
241void abts_int_nequal(abts_case *tc, const int expected, const int actual, int lineno)
242{
243    update_status();
244    if (tc->failed) return;
245
246    if (expected != actual) return;
247
248    tc->failed = TRUE;
249    if (verbose) {
250        fprintf(stderr, "Line %d: expected <%d>, but saw <%d>\n", lineno, expected, actual);
251        fflush(stderr);
252    }
253}
254
255void abts_str_equal(abts_case *tc, const char *expected, const char *actual, int lineno)
256{
257    update_status();
258    if (tc->failed) return;
259
260    /* If both are NULL, match is good */
261    if (!expected && !actual) return;
262    if (expected && actual)
263        if (!strcmp(expected, actual)) return;
264
265    tc->failed = TRUE;
266    if (verbose) {
267        fprintf(stderr, "Line %d: expected <%s>, but saw <%s>\n", lineno, expected, actual);
268        fflush(stderr);
269    }
270}
271
272void abts_str_nequal(abts_case *tc, const char *expected, const char *actual,
273                       size_t n, int lineno)
274{
275    update_status();
276    if (tc->failed) return;
277
278    if (!strncmp(expected, actual, n)) return;
279
280    tc->failed = TRUE;
281    if (verbose) {
282        fprintf(stderr, "Line %d: expected <%s>, but saw <%s>\n", lineno, expected, actual);
283        fflush(stderr);
284    }
285}
286
287void abts_ptr_notnull(abts_case *tc, const void *ptr, int lineno)
288{
289    update_status();
290    if (tc->failed) return;
291
292    if (ptr != NULL) return;
293
294    tc->failed = TRUE;
295    if (verbose) {
296        fprintf(stderr, "Line %d: expected non-NULL, but saw NULL\n", lineno);
297        fflush(stderr);
298    }
299}
300
301void abts_ptr_equal(abts_case *tc, const void *expected, const void *actual, int lineno)
302{
303    update_status();
304    if (tc->failed) return;
305
306    if (expected == actual) return;
307
308    tc->failed = TRUE;
309    if (verbose) {
310        fprintf(stderr, "Line %d: expected <%p>, but saw <%p>\n", lineno, expected, actual);
311        fflush(stderr);
312    }
313}
314
315void abts_fail(abts_case *tc, const char *message, int lineno)
316{
317    update_status();
318    if (tc->failed) return;
319
320    tc->failed = TRUE;
321    if (verbose) {
322        fprintf(stderr, "Line %d: %s\n", lineno, message);
323        fflush(stderr);
324    }
325}
326
327void abts_assert(abts_case *tc, const char *message, int condition, int lineno)
328{
329    update_status();
330    if (tc->failed) return;
331
332    if (condition) return;
333
334    tc->failed = TRUE;
335    if (verbose) {
336        fprintf(stderr, "Line %d: %s\n", lineno, message);
337        fflush(stderr);
338    }
339}
340
341void abts_true(abts_case *tc, int condition, int lineno)
342{
343    update_status();
344    if (tc->failed) return;
345
346    if (condition) return;
347
348    tc->failed = TRUE;
349    if (verbose) {
350        fprintf(stderr, "Line %d: Condition is false, but expected true\n", lineno);
351        fflush(stderr);
352    }
353}
354
355void abts_not_impl(abts_case *tc, const char *message, int lineno)
356{
357    update_status();
358
359    tc->suite->not_impl++;
360    if (verbose) {
361        fprintf(stderr, "Line %d: %s\n", lineno, message);
362        fflush(stderr);
363    }
364}
365
366int main(int argc, const char *const argv[]) {
367    int i;
368    int rv;
369    int list_provided = 0;
370    abts_suite *suite = NULL;
371
372    initialize();
373
374    quiet = !isatty(STDOUT_FILENO);
375
376    for (i = 1; i < argc; i++) {
377        if (!strcmp(argv[i], "-v")) {
378            verbose = 1;
379            continue;
380        }
381        if (!strcmp(argv[i], "-x")) {
382            exclude = 1;
383            continue;
384        }
385        if (!strcmp(argv[i], "-l")) {
386            list_tests = 1;
387            continue;
388        }
389        if (!strcmp(argv[i], "-q")) {
390            quiet = 1;
391            continue;
392        }
393        if (argv[i][0] == '-') {
394            fprintf(stderr, "Invalid option: `%s'\n", argv[i]);
395            exit(1);
396        }
397        list_provided = 1;
398    }
399
400    if (list_provided) {
401        /* Waste a little space here, because it is easier than counting the
402         * number of tests listed.  Besides it is at most three char *.
403         */
404        testlist = calloc(argc + 1, sizeof(char *));
405        for (i = 1; i < argc; i++) {
406            testlist[i - 1] = argv[i];
407        }
408    }
409
410    for (i = 0; i < (sizeof(alltests) / sizeof(struct testlist *)); i++) {
411        suite = alltests[i].func(suite);
412        apr_pool_clear(p);
413    }
414
415    rv = report(suite);
416    return rv;
417}
418
419