/* This file is part of GDBM test suite. Copyright (C) 2011, 2017-2018 Free Software Foundation, Inc. GDBM is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GDBM 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 General Public License for more details. You should have received a copy of the GNU General Public License along with GDBM. If not, see . */ #include "autoconf.h" #include #include #include #include #include #include "gdbmdefs.h" #include "progname.h" const char *progname; const char *dbname; int flags = 0; /* gdbm_open flags */ int mode = GDBM_WRCREAT; /* gdbm_open mode */ int block_size = 0; /* block size for the db. 0 means default */ size_t mapped_size_max = 32768; /* size of the memory mapped region */ size_t cache_size = 32; /* cache size */ static size_t get_max_mmap_size (const char *arg) { char *p; size_t size; errno = 0; size = strtoul (arg, &p, 10); if (errno) { fprintf (stderr, "%s: ", progname); perror ("maxmap"); exit (1); } if (*p) { fprintf (stderr, "%s: bad maxmap\n", progname); exit (1); } return size; } /* Test results */ #define RES_PASS 0 #define RES_FAIL 1 #define RES_XFAIL 2 #define RES_SKIP 3 const char *resstr[] = { "PASS", "FAIL", "XFAIL", "SKIP" }; static int _res_max = sizeof(resstr) / sizeof(resstr[0]); /* A single setopt testcase */ struct optest { char *group; /* Group this testcase belongs to */ char *name; /* Testcase name */ /* gdbm_setopt arguments: */ int code; /* option code */ void *valptr; /* points to the value */ int valsize; /* size of the value */ /* end of arguments */ int xfail; /* if !0, expected value of gdbm_errno */ int (*test) (void *valptr); /* Test function (can be NULL) */ void (*init) (void *valptr, int valsize); /* Initialization function (can be NULL) */ }; /* Storage for the test value */ char *string; size_t size; int intval; int retbool; /* Individual test and initialization functions */ int test_getflags (void *valptr) { int expected = mode | flags; #ifndef HAVE_MMAP expected |= GDBM_NOMMAP; #endif return (*(int*) valptr == expected) ? RES_PASS : RES_FAIL; } int test_dbname (void *valptr) { char *s = *(char**)valptr; int rc = strcmp (string, dbname) == 0 ? RES_PASS : RES_FAIL; if (rc != RES_PASS) printf ("[got %s instead of %s] ", s, dbname); free (s); return rc; } void init_cachesize (void *valptr, int valsize) { *(size_t*) valptr = cache_size; } int test_getcachesize (void *valptr) { return *(size_t*) valptr == cache_size ? RES_PASS : RES_FAIL; } void init_true (void *valptr, int valsize) { *(int*) valptr = 1; } void init_false (void *valptr, int valsize) { *(int*) valptr = 0; } void init_negate_bool (void *valptr, int valsize) { *(int*) valptr = !retbool; } int test_true (void *valptr) { return *(int*) valptr == 1 ? RES_PASS : RES_FAIL; } int test_false (void *valptr) { return *(int*) valptr == 0 ? RES_PASS : RES_FAIL; } int test_negate_bool (void *valptr) { return *(int*) valptr == !retbool ? RES_PASS : RES_FAIL; } int test_bool (void *valptr) { return *(int*) valptr == retbool ? RES_PASS : RES_FAIL; } int test_initial_maxmapsize(void *valptr) { return *(size_t*) valptr == SIZE_T_MAX ? RES_PASS : RES_FAIL; } void init_maxmapsize (void *valptr, int valsize) { *(size_t*)valptr = mapped_size_max; } int test_maxmapsize (void *valptr) { size_t page_size = sysconf (_SC_PAGESIZE); size_t expected_size = ((mapped_size_max + page_size - 1) / page_size) * page_size; return (*(size_t*) valptr == expected_size) ? RES_PASS : RES_FAIL; } int test_mmap_group (void *valptr) { #ifdef HAVE_MMAP return RES_PASS; #else return RES_SKIP; #endif } /* Create a group of testcases for testing a boolean option. Arguments: grp - group name set - GDBM_SETxxx option get - GDBM_GETxxx option */ #define TEST_BOOL_OPTION(grp, set,get) \ { #grp, }, \ { #grp, "initial " #get, get, &retbool, sizeof (retbool), \ 0, NULL, NULL }, \ { #grp, #set, set, &intval, sizeof (intval), \ 0, NULL, init_negate_bool }, \ { #grp, #get, get, &intval, sizeof (intval), \ 0, test_negate_bool, NULL }, \ { #grp, #set " true", set, &intval, sizeof (intval), \ 0, NULL, init_true }, \ { #grp, #get, get, &intval, sizeof (intval), \ 0, test_true, NULL }, \ { #grp, #set " false", set, &intval, sizeof (intval), \ 0, NULL, init_false }, \ { #grp, #get, get, &intval, sizeof (intval), \ 0, test_false, NULL } /* Table of testcases: */ struct optest optest_tab[] = { { "GETFLAGS", "GDBM_GETFLAGS", GDBM_GETFLAGS, &intval, sizeof (intval), 0, test_getflags }, { "CACHESIZE" }, { "CACHESIZE", "initial GDBM_SETCACHESIZE", GDBM_SETCACHESIZE, &size, sizeof (size), 0, NULL, init_cachesize }, { "CACHESIZE", "GDBM_GETCACHESIZE", GDBM_GETCACHESIZE, &size, sizeof (size), 0, test_getcachesize }, { "CACHESIZE", "second GDBM_SETCACHESIZE", GDBM_SETCACHESIZE, &size, sizeof (size), GDBM_OPT_ALREADY_SET, NULL, init_cachesize }, TEST_BOOL_OPTION (SYNCMODE, GDBM_SETSYNCMODE, GDBM_GETSYNCMODE), TEST_BOOL_OPTION (CENTFREE, GDBM_SETCENTFREE, GDBM_GETCENTFREE), TEST_BOOL_OPTION (COALESCEBLKS, GDBM_SETCOALESCEBLKS, GDBM_GETCOALESCEBLKS), /* MMAP group */ { "MMAP", NULL, 0, NULL, 0, 0, test_mmap_group }, { "MMAP", "initial GDBM_GETMMAP", GDBM_GETMMAP, &intval, sizeof (intval), 0, test_true }, { "MMAP", "GDBM_SETMMAP false", GDBM_SETMMAP, &intval, sizeof (intval), 0, NULL, init_false }, { "MMAP", "GDBM_GETMMAP", GDBM_GETMMAP, &intval, sizeof (intval), 0, test_false }, { "MMAP", "initial GDBM_GETMAXMAPSIZE", GDBM_GETMAXMAPSIZE, &size, sizeof (size), 0, test_initial_maxmapsize, NULL }, { "MMAP", "GDBM_SETMAXMAPSIZE", GDBM_SETMAXMAPSIZE, &size, sizeof (size), 0, NULL, init_maxmapsize }, { "MMAP", "GDBM_GETMAXMAPSIZE", GDBM_GETMAXMAPSIZE, &size, sizeof (size), 0, test_maxmapsize, NULL }, { "GETDBNAME", "GDBM_GETDBNAME", GDBM_GETDBNAME, &string, sizeof (string), 0, test_dbname, NULL }, { NULL } }; /* Use ARGV to determine whether to run the given GROUP of testcases. ARGV is a NULL-terminated array of allowed group names. A "!" prefix can be used to denote negation. */ int groupok (char **argv, const char *group) { int retval = 1; if (*argv) { char *arg; while ((arg = *argv++)) { if (*arg == '!') { if (strcasecmp (arg + 1, group) == 0) return 0; retval = 1; } else { if (strcasecmp (arg, group) == 0) return 1; retval = 0; } } } return retval; } int main (int argc, char **argv) { GDBM_FILE dbf; struct optest *op; progname = canonical_progname (argv[0]); while (--argc) { char *arg = *++argv; if (strcmp (arg, "-h") == 0) { printf ("usage: %s [-blocksize=N] [-nolock] [-sync] [-maxmap=N] DBFILE [GROUP [GROUP...]\n", progname); exit (0); } else if (strcmp (arg, "-nolock") == 0) flags |= GDBM_NOLOCK; else if (strcmp (arg, "-sync") == 0) flags |= GDBM_SYNC; else if (strncmp (arg, "-blocksize=", 11) == 0) block_size = atoi (arg + 11); else if (strncmp (arg, "-maxmap=", 8) == 0) mapped_size_max = get_max_mmap_size (arg + 8); else if (strcmp (arg, "--") == 0) { --argc; ++argv; break; } else if (arg[0] == '-') { fprintf (stderr, "%s: unknown option %s\n", progname, arg); exit (1); } else break; } if (argc == 0) { fprintf (stderr, "%s: wrong arguments\n", progname); exit (1); } dbname = *argv; ++argv; --argc; dbf = gdbm_open (dbname, block_size, mode|flags, 00664, NULL); if (!dbf) { fprintf (stderr, "gdbm_open failed: %s\n", gdbm_strerror (gdbm_errno)); exit (1); } for (op = optest_tab; op->group; op++) { int rc; if (!groupok (argv, op->group)) continue; if (!op->name) { /* Group header */ const char *grp = op->group; printf ("* %s:", grp); if (op->test && (rc = op->test (NULL)) != RES_PASS) { printf (" %s", resstr[rc]); for (op++; op->name && strcmp (op->group, grp) == 0; op++) ; op--; } putchar ('\n'); continue; } printf ("%s: ", op->name); if (op->init) op->init (op->valptr, op->valsize); rc = gdbm_setopt (dbf, op->code, op->valptr, op->valsize); if (rc) { if (gdbm_errno == op->xfail) puts (resstr[RES_XFAIL]); else printf ("%s: %s\n", resstr[RES_FAIL], gdbm_strerror (gdbm_errno)); } else if (!op->test) puts (resstr[RES_PASS]); else { rc = op->test (op->valptr); assert (rc >= 0 && rc < _res_max); puts (resstr[rc]); } } gdbm_close (dbf); exit (0); }