Blob Blame History Raw
/*
 * test-run.c: test the aug_srun API function
 *
 * Copyright (C) 2009-2016 David Lutterkort
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * Author: David Lutterkort <lutter@redhat.com>
 */

#include <config.h>

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#include "augeas.h"

#include "cutest.h"
#include "internal.h"
#include <memory.h>

#include <unistd.h>

static const char *abs_top_srcdir;
static char *lensdir;

#define KW_TEST "test"
#define KW_PRINTS "prints"
#define KW_SOMETHING "something"
#define KW_USE "use"

/* This array needs to be kept in sync with aug_errcode_t, and
 * the entries need to be in the same order as in that enum; used
 * by errtok
 */
static const char *const errtokens[] = {
    "NOERROR", "ENOMEM", "EINTERNAL", "EPATHX", "ENOMATCH",
    "EMMATCH", "ESYNTAX", "ENOLENS", "EMXFM", "ENOSPAN",
    "EMVDESC", "ECMDRUN", "EBADARG", "ELABEL"
};

struct test {
    struct test *next;
    char *name;
    char *module;
    int  result;
    int  errcode;
    char *cmd;
    char *out;
    bool  out_present;
};

static void free_tests(struct test *test) {
    if (test == NULL)
        return;
    free_tests(test->next);
    free(test->name);
    free(test->module);
    free(test->cmd);
    free(test->out);
    free(test);
}

#define die(msg)                                                    \
    do {                                                            \
        fprintf(stderr, "%d: Fatal error: %s\n", __LINE__, msg);    \
        exit(EXIT_FAILURE);                                         \
    } while(0)

static char *skipws(char *s) {
    while (isspace(*s)) s++;
    return s;
}

static char *findws(char *s) {
    while (*s && ! isspace(*s)) s++;
    return s;
}

static char *token(char *s, char **tok) {
    char *t = skipws(s);
    s = findws(t);
    *tok = strndup(t, s - t);
    return s;
}

static char *inttok(char *s, int *tok) {
    char *t = skipws(s);
    s = findws(t);
    *tok = strtol(t, NULL, 10);
    return s;
}

static char *errtok(char *s, int *err) {
    char *t = skipws(s);
    s = findws(t);
    if (s == t) {
        *err = AUG_NOERROR;
        return s;
    }
    for (*err = 0; *err < ARRAY_CARDINALITY(errtokens); *err += 1) {
        const char *e = errtokens[*err];
        if (strlen(e) == s - t && STREQLEN(e, t, s - t))
            return s;
    }
    fprintf(stderr, "errtok: '%s'\n", t);
    die("unknown error code");
}

static bool looking_at(const char *s, const char *kw) {
    return STREQLEN(s, kw, strlen(kw));
}

static struct test *read_tests(void) {
    char *fname = NULL;
    FILE *fp;
    char line[BUFSIZ];
    struct test *result = NULL, *t = NULL;
    int lc = 0;
    bool append_cmd = true;

    if (asprintf(&fname, "%s/tests/run.tests", abs_top_srcdir) < 0)
        die("asprintf fname");

    if ((fp = fopen(fname, "r")) == NULL)
        die("fopen run.tests");

    while (fgets(line, BUFSIZ, fp) != NULL) {
        lc += 1;
        char *s = skipws(line);
        if (*s == '#' || *s == '\0')
            continue;
        if (*s == ':')
            s += 1;
        char *eos = s + strlen(s) - 2;
        if (eos >= s && *eos == ':') {
            *eos++ = '\n';
            *eos = '\0';
        }

        if (looking_at(s, KW_TEST)) {
            if (ALLOC(t) < 0)
                die_oom();
            list_append(result, t);
            append_cmd = true;
            s = token(s + strlen(KW_TEST), &(t->name));
            s = inttok(s, &t->result);
            s = errtok(s, &t->errcode);
        } else if (looking_at(s, KW_PRINTS)) {
            s = skipws(s + strlen(KW_PRINTS));
            t->out_present = looking_at(s, KW_SOMETHING);
            append_cmd = false;
        } else if (looking_at(s, KW_USE)) {
            if (t->module !=NULL)
                die("Can use at most one module in a test");
            s = token(s + strlen(KW_USE), &(t->module));
        } else {
            char **buf = append_cmd ? &(t->cmd) : &(t->out);
            if (*buf == NULL) {
                *buf = strdup(s);
                if (*buf == NULL)
                    die_oom();
            } else {
                if (REALLOC_N(*buf, strlen(*buf) + strlen(s) + 1) < 0)
                    die_oom();
                strcat(*buf, s);
            }
        }
        if (t->out != NULL)
            t->out_present = true;
    }
    free(fname);
    return result;
}

#define fail(cond, msg ...)                     \
    if (cond) {                                 \
        printf("FAIL (");                       \
        fprintf(stdout, ## msg);                \
        printf(")\n");                          \
        goto error;                             \
    }

static int load_module(struct augeas *aug, struct test *test) {
    char *fname, *fpath;
    int r, result = -1;

    if (test->module == NULL)
        return 0;

    if (asprintf(&fname, "%s.aug", test->module) == -1)
       fail(true, "asprintf test->module");

    for (int i=0; i < strlen(fname); i++)
        fname[i] = tolower(fname[i]);

    if (asprintf(&fpath, "%s/%s", lensdir, fname) == -1)
        fail(true, "asprintf lensdir");

    r = __aug_load_module_file(aug, fpath);
    fail(r < 0, "Could not load %s", fpath);
    result = 0;
 error:
    free(fname);
    free(fpath);
    return result;
}

static int run_one_test(struct test *test) {
    int r;
    struct augeas *aug = NULL;
    struct memstream ms;
    int result = 0;

    MEMZERO(&ms, 1);

    aug = aug_init("/dev/null", lensdir,
                   AUG_NO_STDINC|AUG_NO_MODL_AUTOLOAD|AUG_ENABLE_SPAN);
    fail(aug == NULL, "aug_init");
    fail(aug_error(aug) != AUG_NOERROR, "aug_init: errcode was %d",
         aug_error(aug));

    printf("%-30s ... ", test->name);

    r = load_module(aug, test);
    if (r < 0)
        goto error;

    r = init_memstream(&ms);
    fail(r < 0, "init_memstream");

    r = aug_srun(aug, ms.stream, test->cmd);
    fail(r != test->result, "return value: expected %d, actual %d",
         test->result, r);
    fail(aug_error(aug) != test->errcode, "errcode: expected %s, actual %s",
         errtokens[test->errcode], errtokens[aug_error(aug)]);

    r = close_memstream(&ms);
    fail(r < 0, "close_memstream");
    fail(ms.buf == NULL, "close_memstream left buf NULL");

    if (test->out != NULL) {
        fail(STRNEQ(ms.buf, test->out), "output: expected '%s', actual '%s'",
             test->out, ms.buf);
    } else if (test->out_present) {
        fail(strlen(ms.buf) == 0,
             "output: expected some output");
    } else {
        fail(strlen(ms.buf) > 0,
             "output: expected nothing, actual '%s'", ms.buf);
    }
    printf("PASS\n");

 done:
    free(ms.buf);
    aug_close(aug);
    return result;
 error:
    result = -1;
    goto done;
}

static int run_tests(struct test *tests, int argc, char **argv) {
    int result = EXIT_SUCCESS;

    list_for_each(t, tests) {
        if (! should_run(t->name, argc, argv))
            continue;
        if (run_one_test(t) < 0)
            result = EXIT_FAILURE;
    }
    return result;
}

int main(int argc, char **argv) {
    struct test *tests;

    abs_top_srcdir = getenv("abs_top_srcdir");
    if (abs_top_srcdir == NULL)
        die("env var abs_top_srcdir must be set");

    if (asprintf(&lensdir, "%s/lenses", abs_top_srcdir) < 0)
        die("out of memory setting lensdir");

    tests = read_tests();
    int result = run_tests(tests, argc - 1, argv + 1);
    free_tests(tests);
    return result;
}

/*
 * Local variables:
 *  indent-tabs-mode: nil
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 * End:
 */