diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2021-07-29 13:47:25 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2021-07-29 14:21:06 +0300 |
commit | 080a8db469f55d18a2d51e9f957bab344b5aa06e (patch) | |
tree | 112b468516b0f1a103f8c3b8137acea064bf4d98 /tests | |
parent | 827ef17081ff2b53ba6daa86d5ea55982e7e8d5a (diff) | |
download | gdbm-080a8db469f55d18a2d51e9f957bab344b5aa06e.tar.gz gdbm-080a8db469f55d18a2d51e9f957bab344b5aa06e.tar.bz2 |
Test conversion to extended format.
* src/gdbmdefs.h (gdbm_ext_header): Add new field: version.
Pad to 8 integers.
* tests/gtconv.c: New auxiliary program.
* tests/conv.at: New test.
* tests/Makefile.am: Add new tests.
* tests/testsuite.at: Likewise.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/.gitignore | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/conv.at | 21 | ||||
-rw-r--r-- | tests/gtconv.c | 307 | ||||
-rw-r--r-- | tests/testsuite.at | 3 |
5 files changed, 334 insertions, 0 deletions
diff --git a/tests/.gitignore b/tests/.gitignore index 4db3640..558b2ee 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -13,6 +13,7 @@ dtload fdop g_open_ce g_reorg_ce +gtconv gtdel gtdump gtfetch diff --git a/tests/Makefile.am b/tests/Makefile.am index 77f0b15..1caa76e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -55,6 +55,7 @@ TESTSUITE_AT = \ cloexec02.at\ cloexec03.at\ closerr.at\ + conv.at\ dbmcreate00.at\ dbmdel00.at\ dbmdel01.at\ @@ -113,6 +114,7 @@ check_PROGRAMS = \ fdop\ g_open_ce\ g_reorg_ce\ + gtconv\ gtdel\ gtdump\ gtfetch\ diff --git a/tests/conv.at b/tests/conv.at new file mode 100644 index 0000000..cc1cd3e --- /dev/null +++ b/tests/conv.at @@ -0,0 +1,21 @@ +# This file is part of GDBM. -*- autoconf -*- +# Copyright (C) 2018-2021 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 <http://www.gnu.org/licenses/>. */ + +AT_SETUP([Database format conversion]) +AT_KEYWORDS([conv]) +AT_CHECK([gtconv]) +AT_CLEANUP + diff --git a/tests/gtconv.c b/tests/gtconv.c new file mode 100644 index 0000000..fc9b9ab --- /dev/null +++ b/tests/gtconv.c @@ -0,0 +1,307 @@ +/* + NAME + gtconv - test GDBM database conversion to extended format. + + SYNOPSIS + gtconv [-v] + + DESCRIPTION + When converting a traditional GDBM database to extended (numsync) + format, the size of the master av_table shrinks. Consequently, + if it is full or nearly full, the entries near its end that + don't fit into the new size are returned to the per-bucket + available pools using _gdbm_free (see _gdbm_convert_to_numsync). + + This test program verifies that all main av_table entries are + preserved during format upgrade. + + Operation: + + 1) Create a database with the minimal possible block size, to ensure + the mimimal size of the av_table array + 2) Set the GDBM_SETCENTFREE option, so all released entries are + returned to the main av_table. + 3) Populate the database with a sufficient number of entries. + 4) Keep deleting entries until main av_table becomes full. + 5) Save a copy of all avail_elems (both master and per-block), + sorted by av_adr. + 6) Convert the database to the GDBM_NUMSYNC format. + 7) Get a copy of all avail_elems similar to (5) + 8) Compare arrays obtained in 5 and 7. + + The array obtained in step 7 is normally one entry longer than the + one from step 5. The comparison in 8 ignores such extra entries. + + OPTIONS + -v Verbosely print what's being done. + Repeated twice, dumps listings of av_table arrays received in + steps 5 and 7. + + EXIT CODE + 0 success + 1 failure + 2 usage error + 77 unable to fill the av_table in step 4. + + LICENSE + This file is part of GDBM test suite. + Copyright (C) 2021 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 <http://www.gnu.org/licenses/>. +*/ +#include "autoconf.h" +#include "gdbmdefs.h" +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> + +char dbname[] = "a.db"; + +#define DATASIZE (4*IGNORE_SIZE) + +static int +avail_counter (avail_block *blk, off_t off, void *closure) +{ + int *np = closure; + *np += blk->count; + return 0; +} + +static int +avail_saver (avail_block *blk, off_t off, void *closure) +{ + avail_block *ab = closure; + memcpy (ab->av_table + ab->count, blk->av_table, + blk->count * sizeof (blk->av_table[0])); + ab->count += blk->count; + return 0; +} + +static int +av_table_cmp (const void *a, const void *b) +{ + const avail_elem *ea = a; + const avail_elem *eb = b; + if (ea->av_adr < eb->av_adr) + return -1; + else if (ea->av_adr > eb->av_adr) + return 1; + return 0; +} + +static avail_block * +collect_avail (GDBM_FILE dbf) +{ + int av_count; + avail_block *ab; + + /* Compute the number of entries in avail block */ + av_count = 0; + gdbm_avail_traverse (dbf, avail_counter, &av_count); + + /* Allocate temporary storage */ + ab = malloc (sizeof (ab[0]) + (av_count - 1) * sizeof (ab->av_table[0])); + assert (ab != NULL); + + /* Save the avail table */ + ab->size = av_count; + ab->count = 0; + gdbm_avail_traverse (dbf, avail_saver, ab); + + /* Sort it */ + qsort (ab->av_table, ab->count, sizeof (ab->av_table[0]), av_table_cmp); + + return ab; +} + +static void +dump_avail (avail_block *ab, char const *title, FILE *fp) +{ + int i; + unsigned long total = 0; + + fprintf (fp, "%s\n", title); + for (i = 0; i < ab->count; i++) + { + total += ab->av_table[i].av_size; + fprintf (fp, "% 4d %6lu\n", ab->av_table[i].av_size, + (unsigned long) ab->av_table[i].av_adr); + } + fprintf (fp, "total = %lu\n", total); +} + +int +main (int argc, char **argv) +{ + int avcount; + GDBM_FILE dbf; + datum key, content; + char data[DATASIZE]; + + int nkeys; + int *keys; + + int i, n; + + avail_block *av_saved, *av_new; + + int verbose = 0; + int rc; + + while ((i = getopt (argc, argv, "v")) != EOF) + { + switch (i) + { + case 'v': + verbose++; + break; + + default: + return 2; + } + } + + /* Make sure we create new database */ + unlink (dbname); + + /* Create the database */ + if (verbose) + printf ("creating database\n"); + dbf = gdbm_open (dbname, GDBM_MIN_BLOCK_SIZE, GDBM_NEWDB, 0644, NULL); + if (!dbf) + { + fprintf (stderr, "gdbm_open: %s\n", gdbm_strerror (gdbm_errno)); + return 1; + } + + int t = 1; + if (gdbm_setopt (dbf, GDBM_SETCENTFREE, &t, sizeof (t)) == -1) + { + fprintf (stderr, "gdbm_setopt: %s\n", gdbm_strerror (gdbm_errno)); + return 1; + } + + avcount = dbf->avail->size; + if (verbose) + printf ("main av_table capacity: %d\n", avcount); + + + /* Initialize keys */ + nkeys = 2*avcount; + keys = calloc (nkeys, sizeof (keys)); + assert (keys != NULL); + for (i = 0; i < nkeys; i++) + { + keys[i] = i+1; + } + + /* Initialize content */ + for (i = 0; i < DATASIZE; i++) + data[i] = i+1; + content.dsize = DATASIZE; + content.dptr = data; + + /* Populate the database */ + if (verbose) + printf ("populating database (%d keys)\n", nkeys); + key.dsize = sizeof (keys[0]); + for (i = 0; i < nkeys; i++) + { + key.dptr = (char*) &keys[i]; + if (gdbm_store (dbf, key, content, 0) != 0) + { + fprintf (stderr, "%d: item not inserted: %s\n", + i, gdbm_db_strerror (dbf)); + gdbm_close (dbf); + return 1; + } + } + + /* Delete all keys */ + if (verbose) + printf ("deleting keys\n"); + i = 0; + while (dbf->avail->count < dbf->avail->size) + { + if (i == nkeys) + { + if (verbose) + printf ("failed to fill av_table\n"); + gdbm_close (dbf); + return 77; + } + key.dptr = (char*) &keys[i]; + if (gdbm_delete (dbf, key)) + { + fprintf (stderr, "%d: gdbm_delete: %s\n", + i, gdbm_db_strerror (dbf)); + gdbm_close (dbf); + return 1; + } + i++; + } + + if (verbose) + printf ("main av_table elements: %d\n", dbf->avail->count); + + av_saved = collect_avail (dbf); + if (verbose) + printf ("total number of avail_elem entries used: %d\n", av_saved->count); + if (verbose > 1) + dump_avail (av_saved, "av_saved", stdout); + + /* Upgrade the database */ + if (verbose) + printf ("converting database\n"); + + if (gdbm_convert (dbf, GDBM_NUMSYNC)) + { + fprintf (stderr, "gdbm_convert: %s\n", gdbm_db_strerror (dbf)); + gdbm_close (dbf); + return 1; + } + + if (verbose) + printf ("main av_table elements: %d / %d\n", dbf->avail->count, dbf->avail->size); + + av_new = collect_avail (dbf); + if (verbose) + printf ("total number of avail_elem entries used: %d\n", av_new->count); + + if (verbose > 1) + dump_avail (av_new, "av_new", stdout); + + n = (av_saved->count < av_new->count) ? av_saved->count : av_new->count; + rc = 0; + for (i = 0; i < n; i++) + { + if (!(av_saved->av_table[i].av_adr == av_new->av_table[i].av_adr && + av_saved->av_table[i].av_size == av_new->av_table[i].av_size)) + { + fprintf (stderr, "element %d differs\n", i); + rc = 1; + break; + } + } + + if (rc) + { + dump_avail (av_saved, "av_saved", stderr); + dump_avail (av_new, "av_new", stderr); + } + + gdbm_close (dbf); + return rc; +} + diff --git a/tests/testsuite.at b/tests/testsuite.at index 29b1067..8437851 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -83,4 +83,7 @@ m4_include([gdbmtool01.at]) m4_include([gdbmtool02.at]) m4_include([gdbmtool03.at]) +AT_BANNER([Database formats]) +m4_include([conv.at]) + # End of testsuite.at |