From 7f34e02b5df3d68b090d3d29a5952beec0c1c1a2 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sun, 3 Jul 2022 15:51:46 +0300 Subject: Test database file dumping and loading --- tests/.gitignore | 1 + tests/Makefile.am | 2 + tests/dumpload.at | 5 + tests/t_dumpload.c | 505 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/testsuite.at | 2 + 5 files changed, 515 insertions(+) create mode 100644 tests/dumpload.at create mode 100644 tests/t_dumpload.c diff --git a/tests/.gitignore b/tests/.gitignore index f424120..729988a 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -31,4 +31,5 @@ testsuite.dir *.sum *.bak site.exp +t_dumpload t_wordwrap diff --git a/tests/Makefile.am b/tests/Makefile.am index fac6826..246fd88 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -65,6 +65,7 @@ TESTSUITE_AT = \ dbmfetch01.at\ dbmfetch02.at\ dbmfetch03.at\ + dumpload.at\ create00.at\ delete00.at\ delete01.at\ @@ -126,6 +127,7 @@ check_PROGRAMS = \ gtrecover\ gtver\ num2word\ + t_dumpload\ t_wordwrap\ $(DBMPROGS) diff --git a/tests/dumpload.at b/tests/dumpload.at new file mode 100644 index 0000000..7e39079 --- /dev/null +++ b/tests/dumpload.at @@ -0,0 +1,5 @@ +# This file is part of GDBM testsuite. -*- autoconf -*- +AT_SETUP([dump/load]) +AT_KEYWORDS([dump load import export]) +AT_CHECK(t_dumpload) +AT_CLEANUP diff --git a/tests/t_dumpload.c b/tests/t_dumpload.c new file mode 100644 index 0000000..92f20ca --- /dev/null +++ b/tests/t_dumpload.c @@ -0,0 +1,505 @@ +/* This file is part of GDBM test suite. + Copyright (C) 2022 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 +#include +#include +#include "gdbm.h" + +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#endif + +char a_name[] = "a.gdbm"; +char b_name[] = "b.gdbm"; +char orig_name[] = "orig.gdbm"; +char bin_dumpname[] = "a.bin.dump"; +char ascii_dumpname[] = "a.ascii.dump"; + +void +db_perror (char const *fmt, ...) +{ + int en = errno; + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fprintf (stderr, ": %s", gdbm_strerror (gdbm_errno)); + if (gdbm_check_syserr (gdbm_errno)) + fprintf (stderr, ": %s", strerror (en)); + fputc ('\n', stderr); +} + +GDBM_FILE +createdb (char const *name) +{ + int rc; + char *cmdstub = "num2word 1:2000 | gtload -clear "; + char *cmd; + size_t cmdlen; + GDBM_FILE db; + + cmdlen = strlen (cmdstub) + strlen (name); + cmd = malloc (cmdlen + 1); + assert (cmd != NULL); + strcat (strcpy (cmd, cmdstub), name); + + rc = system (cmd); + if (rc == -1) + { + perror (cmd); + exit (1); + } + else if (WIFEXITED (rc)) + { + rc = WEXITSTATUS (rc); + if (rc) + { + fprintf (stderr, "%s: terminated with code %d\n", cmd, rc); + exit (1); + } + } + else if (WIFSIGNALED (rc)) + { + fprintf (stderr, "%s: terminated on signal %d", cmd, WTERMSIG (rc)); + if (WCOREDUMP (rc)) + fprintf (stderr, " (core dumped)"); + fputc ('\n', stderr); + exit (1); + } + else + { + fprintf (stderr, "%s: terminated with unrecognized status: %d", cmd, rc); + exit (1); + } + + db = gdbm_open (name, 0, GDBM_READER, 0, NULL); + if (db == NULL) + { + db_perror ("can't open %s", name); + exit (1); + } + + return db; +} + +int +db_cmp (GDBM_FILE a, GDBM_FILE b) +{ + datum key, adata, bdata; + gdbm_count_t an, bn; + + if (gdbm_count (a, &an)) + { + fprintf (stderr, "a: gdbm_count: %s\n", gdbm_db_strerror (a)); + return -1; + } + if (gdbm_count (b, &bn)) + { + fprintf (stderr, "b: gdbm_count: %s\n", gdbm_db_strerror (b)); + return -1; + } + + if (an != bn) + { + fprintf (stderr, "key counts differ: a=%lu, b=%lu\n", + (unsigned long)an, (unsigned long)bn); + return -1; + } + + key = gdbm_firstkey (a); + while (key.dptr) + { + datum nextkey; + + adata = gdbm_fetch (a, key); + if (adata.dptr == NULL) + { + fprintf (stderr, "a: can't get %*.*s: %s\n", + key.dsize, key.dsize, key.dptr, + gdbm_db_strerror (a)); + free (key.dptr); + return -1; + } + + bdata = gdbm_fetch (b, key); + if (bdata.dptr == NULL) + { + fprintf (stderr, "b: can't get %*.*s: %s\n", + key.dsize, key.dsize, key.dptr, + gdbm_db_strerror (b)); + free (key.dptr); + free (adata.dptr); + return -1; + } + + if (adata.dsize != bdata.dsize || + memcmp (adata.dptr, bdata.dptr, adata.dsize)) + { + fprintf (stderr, "data differ for %*.*s\n", + key.dsize, key.dsize, key.dptr); + free (key.dptr); + free (adata.dptr); + free (bdata.dptr); + return -1; + } + + free (adata.dptr); + free (bdata.dptr); + + nextkey = gdbm_nextkey (a, key); + free (key.dptr); + key = nextkey; + } + + return 0; +} + +/* + * Load binary dump into a non-existing (NULL) database. This should + * fail with GDBM_NO_DBNAME. + */ +void +test_bindump_0 (GDBM_FILE dbf) +{ + GDBM_FILE b = NULL; + + if (gdbm_load (&b, bin_dumpname, GDBM_INSERT, 0, NULL)) + { + if (gdbm_errno != GDBM_NO_DBNAME) + { + db_perror ("%s: loading binary dump to non-existing database failed with unexpected error", __func__); + exit (1); + } + } + else + { + fprintf (stderr, "%s: loading binary dump to non-existing database succeeded when it should not\n", __func__); + exit (1); + } +} + +/* + * Load binary dump into an existing empty database. + */ +void +test_bindump_1 (GDBM_FILE dbf) +{ + GDBM_FILE b; + + /* + * Create an empty database and retry loading into it. + */ + b = gdbm_open (b_name, 0, GDBM_NEWDB, 0644, NULL); + if (b == NULL) + { + db_perror ("%s: can't open %s", __func__, b_name); + exit (1); + } + + if (gdbm_load (&b, bin_dumpname, GDBM_INSERT, 0, NULL)) + { + db_perror ("%s: failed to load database from binary dump", __func__); + exit (1); + } + + /* Compare two databases. */ + if (db_cmp (dbf, b)) + { + fprintf (stderr, "%s: databases differ\n", __func__); + exit (1); + } + + gdbm_close (b); +} + +/* + * Load binary dump to a non-empty database. + * When tried with GDBM_INSERT replace parameter, it is expected to + * fail with GDBM_CANNOT_REPLACE. + * When run with GDBM_REPLACE, it should succeed. + */ +void +test_bindump_2 (GDBM_FILE dbf) +{ + GDBM_FILE b; + datum key, dat; + + b = gdbm_open (b_name, 0, GDBM_NEWDB, 0644, NULL); + if (b == NULL) + { + db_perror ("%s: can't open %s", __func__, b_name); + exit (1); + } + + key.dptr = "99"; + key.dsize = 2; + dat.dptr = "99"; + dat.dsize = 2; + if (gdbm_store (b, key, dat, 0)) + { + db_perror ("%s: can't store datum", __func__); + exit (1); + } + gdbm_sync (b); + + if (gdbm_load (&b, bin_dumpname, GDBM_INSERT, 0, NULL)) + { + if (gdbm_errno != GDBM_CANNOT_REPLACE) + { + db_perror ("%s: expected GDBM_CANNOT_REPLACE, but got", __func__); + exit (1); + } + } + + if (gdbm_load (&b, bin_dumpname, GDBM_REPLACE, 0, NULL)) + { + db_perror ("%s: failed to load from ASCII dump", __func__); + exit (1); + } + + if (db_cmp (dbf, b)) + { + fprintf (stderr, "%s: databases differ\n", __func__); + exit (1); + } + + gdbm_close (b); +} + +/* + * Test dumping to and restoring from binary dump format. + */ +void +test_bindump (GDBM_FILE dbf) +{ + /* + * Create a binary dump. + */ + if (gdbm_dump (dbf, bin_dumpname, GDBM_DUMP_FMT_BINARY, GDBM_NEWDB, 0600)) + { + fprintf (stderr, "%s: failed to dump: %s\n", __func__, + gdbm_db_strerror (dbf)); + exit (1); + } + + test_bindump_0 (dbf); + test_bindump_1 (dbf); + test_bindump_2 (dbf); +} + +/* + * Load ASCII dump into a non-existing (NULL) database. + */ +void +test_asciidump_0 (GDBM_FILE dbf) +{ + GDBM_FILE b = NULL; + + /* + * Loading into a non-existing database should create it. + */ + if (gdbm_load (&b, ascii_dumpname, GDBM_INSERT, + GDBM_META_MASK_MODE|GDBM_META_MASK_OWNER, NULL)) + { + db_perror ("%s: can't load from ascii dump", __func__); + exit (1); + } + + /* + * The loaded database should have the same name as the original. + */ + if (access (a_name, F_OK)) + { + fprintf (stderr, "%s: %s: %s\n", __func__, a_name, strerror (errno)); + exit (1); + } + + /* Compare the two databases. */ + if (db_cmp (dbf, b)) + { + fprintf (stderr, "%s: databases differ\n", __func__); + exit (1); + } + + gdbm_close (b); + + /* Cleanup */ + unlink (a_name); +} + +/* + * Load ASCII dump into an existing empty database. + */ +void +test_asciidump_1 (GDBM_FILE dbf) +{ + GDBM_FILE b; + + /* + * Now try loading into an existing database. + */ + b = gdbm_open (b_name, 0, GDBM_NEWDB, 0644, NULL); + if (b == NULL) + { + db_perror ("%s: can't open %s", __func__, b_name); + exit (1); + } + + if (gdbm_load (&b, ascii_dumpname, GDBM_INSERT, + GDBM_META_MASK_MODE|GDBM_META_MASK_OWNER, NULL)) + { + db_perror ("%s: can't load from ascii dump", __func__); + exit (1); + } + + /* + * Just in case: check if a_name was not created on disk. + */ + if (access (a_name, F_OK) == 0) + { + fprintf (stderr, "%s: %s exists when it should not\n", __func__, a_name); + exit (1); + } + + if (db_cmp (dbf, b)) + { + fprintf (stderr, "%s: databases differ\n", __func__); + exit (1); + } + + gdbm_close (b); +} + +/* + * Load ASCII dump to a non-empty database. + * When tried with GDBM_INSERT replace parameter, it is expected to + * fail with GDBM_CANNOT_REPLACE. + * When run with GDBM_REPLACE, it should succeed. + */ +void +test_asciidump_2 (GDBM_FILE dbf) +{ + GDBM_FILE b; + datum key, dat; + + b = gdbm_open (b_name, 0, GDBM_NEWDB, 0644, NULL); + if (b == NULL) + { + db_perror ("%s: can't open %s", __func__, b_name); + exit (1); + } + + key.dptr = "99"; + key.dsize = 2; + dat.dptr = "99"; + dat.dsize = 2; + if (gdbm_store (b, key, dat, 0)) + { + db_perror ("%s: can't store datum", __func__); + exit (1); + } + gdbm_sync (b); + + if (gdbm_load (&b, ascii_dumpname, GDBM_INSERT, + GDBM_META_MASK_MODE|GDBM_META_MASK_OWNER, NULL)) + { + if (gdbm_errno != GDBM_CANNOT_REPLACE) + { + db_perror ("%s: expected GDBM_CANNOT_REPLACE, but got", __func__); + exit (1); + } + } + + if (gdbm_load (&b, ascii_dumpname, GDBM_REPLACE, + GDBM_META_MASK_MODE|GDBM_META_MASK_OWNER, NULL)) + { + db_perror ("%s: failed to load from ASCII dump", __func__); + exit (1); + } + + if (db_cmp (dbf, b)) + { + fprintf (stderr, "%s: databases differ\n", __func__); + exit (1); + } + + gdbm_close (b); +} + +/* + * Test dumping to and restoring from ASCII dump format. + */ +void +test_asciidump (GDBM_FILE dbf) +{ + /* + * Create a dump in ASCII format. + */ + if (gdbm_dump (dbf, ascii_dumpname, GDBM_DUMP_FMT_ASCII, GDBM_NEWDB, 0600)) + { + fprintf (stderr, "%s: failed to dump: %s\n", __func__, + gdbm_db_strerror (dbf)); + exit (1); + } + + /* + * Rename the original database. + */ + if (unlink (orig_name) && errno != ENOENT) + { + fprintf (stderr, "%s: failed to remove %s: %s\n", __func__, + orig_name, strerror (errno)); + exit (1); + } + + if (rename (a_name, orig_name)) + { + fprintf (stderr, "%s: can't rename %s to %s: %s\n", __func__, + a_name, orig_name, strerror (errno)); + exit (1); + } + + test_asciidump_0 (dbf); + test_asciidump_1 (dbf); + test_asciidump_2 (dbf); +} + +int +main (int argc, char **argv) +{ + GDBM_FILE db; + + db = createdb (a_name); + test_bindump (db); + test_asciidump (db); + unlink (a_name); + unlink (b_name); + unlink (orig_name); + unlink (bin_dumpname); + unlink (ascii_dumpname); + return 0; +} diff --git a/tests/testsuite.at b/tests/testsuite.at index 5eba99f..a914a7f 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -45,6 +45,8 @@ m4_include([delete02.at]) m4_include([closerr.at]) +m4_include([dumpload.at]) + AT_BANNER([Block size selection]) m4_include([blocksize00.at]) m4_include([blocksize01.at]) -- cgit v1.2.1