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 = strrchr(suite_name_full, '\\');
111    }
112    if (suite_name) {
113        suite_name++;
114    } else {
115        suite_name = suite_name_full;
116    }
117    p = strrchr(suite_name, '.');
118    if (p) {
119        subsuite->name = memcpy(calloc(p - suite_name + 1, 1),
120                                suite_name, p - suite_name);
121    }
122    else {
123        subsuite->name = suite_name;
124    }
125
126    if (list_tests) {
127        fprintf(stdout, "%s\n", subsuite->name);
128    }
129
130    subsuite->not_run = 0;
131
132    if (suite == NULL) {
133        suite = malloc(sizeof(*suite));
134        suite->head = subsuite;
135        suite->tail = subsuite;
136    }
137    else {
138        suite->tail->next = subsuite;
139        suite->tail = subsuite;
140    }
141
142    if (!should_test_run(subsuite->name)) {
143        subsuite->not_run = 1;
144        return suite;
145    }
146
147    reset_status();
148    fprintf(stdout, "%-20s:  ", subsuite->name);
149    update_status();
150    fflush(stdout);
151
152    return suite;
153}
154
155void abts_run_test(abts_suite *ts, test_func f, void *value)
156{
157    abts_case *tc;
158    sub_suite *ss;
159
160    if (!should_test_run(ts->tail->name)) {
161        return;
162    }
163    ss = ts->tail;
164
165    tc = malloc(sizeof(*tc));
166    tc->failed = 0;
167    tc->suite = ss;
168
169    ss->num_test++;
170    update_status();
171
172    f(tc, value);
173
174    if (tc->failed) {
175        ss->failed++;
176    }
177    free(tc);
178}
179
180static int report(abts_suite *suite)
181{
182    int count = 0;
183    sub_suite *dptr;
184
185    if (suite && suite->tail &&!suite->tail->not_run) {
186        end_suite(suite);
187    }
188
189    for (dptr = suite->head; dptr; dptr = dptr->next) {
190        count += dptr->failed;
191    }
192
193    if (list_tests) {
194        return 0;
195    }
196
197    if (count == 0) {
198        printf("All tests passed.\n");
199        return 0;
200    }
201
202    dptr = suite->head;
203    fprintf(stdout, "%-15s\t\tTotal\tFail\tFailed %%\n", "Failed Tests");
204    fprintf(stdout, "===================================================\n");
205    while (dptr != NULL) {
206        if (dptr->failed != 0) {
207            float percent = ((float)dptr->failed / (float)dptr->num_test);
208            fprintf(stdout, "%-15s\t\t%5d\t%4d\t%6.2f%%\n", dptr->name,
209                    dptr->num_test, dptr->failed, percent * 100);
210        }
211        dptr = dptr->next;
212    }
213    return 1;
214}
215
216void abts_log_message(const char *fmt, ...)
217{
218    va_list args;
219    update_status();
220
221    if (verbose) {
222        va_start(args, fmt);
223        vfprintf(stderr, fmt, args);
224        va_end(args);
225        fprintf(stderr, "\n");
226        fflush(stderr);
227    }
228}
229
230void abts_int_equal(abts_case *tc, const int expected, const int actual, int lineno)
231{
232    update_status();
233    if (tc->failed) return;
234
235    if (expected == actual) return;
236
237    tc->failed = TRUE;
238    if (verbose) {
239        fprintf(stderr, "Line %d: expected <%d>, but saw <%d>\n", lineno, expected, actual);
240        fflush(stderr);
241    }
242}
243
244void abts_int_nequal(abts_case *tc, const int expected, const int actual, int lineno)
245{
246    update_status();
247    if (tc->failed) return;
248
249    if (expected != actual) return;
250
251    tc->failed = TRUE;
252    if (verbose) {
253        fprintf(stderr, "Line %d: expected something other than <%d>, but saw <%d>\n",
254                lineno, expected, actual);
255        fflush(stderr);
256    }
257}
258
259void abts_str_equal(abts_case *tc, const char *expected, const char *actual, int lineno)
260{
261    update_status();
262    if (tc->failed) return;
263
264    /* If both are NULL, match is good */
265    if (!expected && !actual) return;
266    if (expected && actual)
267        if (!strcmp(expected, actual)) return;
268
269    tc->failed = TRUE;
270    if (verbose) {
271        fprintf(stderr, "Line %d: expected <%s>, but saw <%s>\n", lineno, expected, actual);
272        fflush(stderr);
273    }
274}
275
276void abts_str_nequal(abts_case *tc, const char *expected, const char *actual,
277                       size_t n, int lineno)
278{
279    update_status();
280    if (tc->failed) return;
281
282    if (!strncmp(expected, actual, n)) return;
283
284    tc->failed = TRUE;
285    if (verbose) {
286        fprintf(stderr, "Line %d: expected something other than <%s>, but saw <%s>\n",
287                lineno, expected, actual);
288        fflush(stderr);
289    }
290}
291
292void abts_ptr_notnull(abts_case *tc, const void *ptr, int lineno)
293{
294    update_status();
295    if (tc->failed) return;
296
297    if (ptr != NULL) return;
298
299    tc->failed = TRUE;
300    if (verbose) {
301        fprintf(stderr, "Line %d: expected non-NULL, but saw NULL\n", lineno);
302        fflush(stderr);
303    }
304}
305
306void abts_ptr_equal(abts_case *tc, const void *expected, const void *actual, int lineno)
307{
308    update_status();
309    if (tc->failed) return;
310
311    if (expected == actual) return;
312
313    tc->failed = TRUE;
314    if (verbose) {
315        fprintf(stderr, "Line %d: expected <%p>, but saw <%p>\n", lineno, expected, actual);
316        fflush(stderr);
317    }
318}
319
320void abts_fail(abts_case *tc, const char *message, int lineno)
321{
322    update_status();
323    if (tc->failed) return;
324
325    tc->failed = TRUE;
326    if (verbose) {
327        fprintf(stderr, "Line %d: %s\n", lineno, message);
328        fflush(stderr);
329    }
330}
331
332void abts_assert(abts_case *tc, const char *message, int condition, int lineno)
333{
334    update_status();
335    if (tc->failed) return;
336
337    if (condition) return;
338
339    tc->failed = TRUE;
340    if (verbose) {
341        fprintf(stderr, "Line %d: %s\n", lineno, message);
342        fflush(stderr);
343    }
344}
345
346void abts_true(abts_case *tc, int condition, int lineno)
347{
348    update_status();
349    if (tc->failed) return;
350
351    if (condition) return;
352
353    tc->failed = TRUE;
354    if (verbose) {
355        fprintf(stderr, "Line %d: Condition is false, but expected true\n", lineno);
356        fflush(stderr);
357    }
358}
359
360void abts_not_impl(abts_case *tc, const char *message, int lineno)
361{
362    update_status();
363
364    tc->suite->not_impl++;
365    if (verbose) {
366        fprintf(stderr, "Line %d: %s\n", lineno, message);
367        fflush(stderr);
368    }
369}
370
371int main(int argc, const char *const argv[]) {
372    int i;
373    int rv;
374    int list_provided = 0;
375    abts_suite *suite = NULL;
376
377    initialize();
378
379    quiet = !isatty(STDOUT_FILENO);
380
381    for (i = 1; i < argc; i++) {
382        if (!strcmp(argv[i], "-v")) {
383            verbose = 1;
384            continue;
385        }
386        if (!strcmp(argv[i], "-x")) {
387            exclude = 1;
388            continue;
389        }
390        if (!strcmp(argv[i], "-l")) {
391            list_tests = 1;
392            continue;
393        }
394        if (!strcmp(argv[i], "-q")) {
395            quiet = 1;
396            continue;
397        }
398        if (argv[i][0] == '-') {
399            fprintf(stderr, "Invalid option: `%s'\n", argv[i]);
400            exit(1);
401        }
402        list_provided = 1;
403    }
404
405    if (list_provided) {
406        /* Waste a little space here, because it is easier than counting the
407         * number of tests listed.  Besides it is at most three char *.
408         */
409        testlist = calloc(argc + 1, sizeof(char *));
410        for (i = 1; i < argc; i++) {
411            testlist[i - 1] = argv[i];
412        }
413    }
414
415    for (i = 0; i < (sizeof(alltests) / sizeof(struct testlist *)); i++) {
416        suite = alltests[i].func(suite);
417        apr_pool_clear(p);
418    }
419
420    rv = report(suite);
421    return rv;
422}
423
424