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