summaryrefslogtreecommitdiff
path: root/dbm/tests/lots.c
diff options
context:
space:
mode:
Diffstat (limited to 'dbm/tests/lots.c')
-rw-r--r--dbm/tests/lots.c639
1 files changed, 639 insertions, 0 deletions
diff --git a/dbm/tests/lots.c b/dbm/tests/lots.c
new file mode 100644
index 000000000..0b3191e74
--- /dev/null
+++ b/dbm/tests/lots.c
@@ -0,0 +1,639 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* use sequental numbers printed to strings
+ * to store lots and lots of entries in the
+ * database.
+ *
+ * Start with 100 entries, put them and then
+ * read them out. Then delete the first
+ * half and verify that all of the first half
+ * is gone and then verify that the second
+ * half is still there.
+ * Then add the first half back and verify
+ * again. Then delete the middle third
+ * and verify again.
+ * Then increase the size by 1000 and do
+ * the whole add delete thing again.
+ *
+ * The data for each object is the number string translated
+ * to hex and replicated a random number of times. The
+ * number of times that the data is replicated is the first
+ * int32 in the data.
+ */
+
+#include <stdio.h>
+
+#include <stdlib.h>
+#ifdef STDC_HEADERS
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+#include <assert.h>
+#include "mcom_db.h"
+
+DB *database=0;
+int MsgPriority=5;
+
+#if defined(_WINDOWS) && !defined(WIN32)
+#define int32 long
+#define uint32 unsigned long
+#else
+#define int32 int
+#define uint32 unsigned int
+#endif
+
+typedef enum {
+USE_LARGE_KEY,
+USE_SMALL_KEY
+} key_type_enum;
+
+#define TraceMe(priority, msg) \
+ do { \
+ if(priority <= MsgPriority) \
+ { \
+ ReportStatus msg; \
+ } \
+ } while(0)
+
+int
+ReportStatus(char *string, ...)
+{
+ va_list args;
+
+#ifdef STDC_HEADERS
+ va_start(args, string);
+#else
+ va_start(args);
+#endif
+ vfprintf(stderr, string, args);
+ va_end(args);
+
+ fprintf (stderr, "\n");
+
+ return(0);
+}
+
+int
+ReportError(char *string, ...)
+{
+ va_list args;
+
+#ifdef STDC_HEADERS
+ va_start(args, string);
+#else
+ va_start(args);
+#endif
+ fprintf (stderr, "\n ");
+ vfprintf(stderr, string, args);
+ fprintf (stderr, "\n");
+ va_end(args);
+
+ return(0);
+}
+
+DBT * MakeLargeKey(int32 num)
+{
+ int32 low_bits;
+ static DBT rv;
+ static char *string_rv=0;
+ int rep_char;
+ size_t size;
+
+ if(string_rv)
+ free(string_rv);
+
+ /* generate a really large text key derived from
+ * an int32
+ */
+ low_bits = (num % 10000) + 1;
+
+ /* get the repeat char from the low 26 */
+ rep_char = (char) ((low_bits % 26) + 'a');
+
+ /* malloc a string low_bits wide */
+ size = low_bits*sizeof(char);
+ string_rv = (char *)malloc(size);
+
+ memset(string_rv, rep_char, size);
+
+ rv.data = string_rv;
+ rv.size = size;
+
+ return(&rv);
+}
+
+DBT * MakeSmallKey(int32 num)
+{
+ static DBT rv;
+ static char data_string[64];
+
+ rv.data = data_string;
+
+ sprintf(data_string, "%ld", (long)num);
+ rv.size = strlen(data_string);
+
+ return(&rv);
+
+}
+
+DBT * GenKey(int32 num, key_type_enum key_type)
+{
+ DBT *key;
+
+ switch(key_type)
+ {
+ case USE_LARGE_KEY:
+ key = MakeLargeKey(num);
+ break;
+ case USE_SMALL_KEY:
+ key = MakeSmallKey(num);
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ return(key);
+}
+
+int
+SeqDatabase()
+{
+ int status;
+ DBT key, data;
+
+ ReportStatus("SEQuencing through database...");
+
+ /* seq throught the whole database */
+ if(!(status = (*database->seq)(database, &key, &data, R_FIRST)))
+ {
+ while(!(status = (database->seq) (database, &key, &data, R_NEXT)));
+ ; /* null body */
+ }
+
+ if(status < 0)
+ ReportError("Error seq'ing database");
+
+ return(status);
+}
+
+int
+VerifyData(DBT *data, int32 num, key_type_enum key_type)
+{
+ int32 count, compare_num;
+ size_t size;
+ int32 *int32_array;
+
+ /* The first int32 is count
+ * The other n entries should
+ * all equal num
+ */
+ if(data->size < sizeof(int32))
+ {
+ ReportError("Data size corrupted");
+ return -1;
+ }
+
+ memcpy(&count, data->data, sizeof(int32));
+
+ size = sizeof(int32)*(count+1);
+
+ if(size != data->size)
+ {
+ ReportError("Data size corrupted");
+ return -1;
+ }
+
+ int32_array = (int32*)data->data;
+
+ for(;count > 0; count--)
+ {
+ memcpy(&compare_num, &int32_array[count], sizeof(int32));
+
+ if(compare_num != num)
+ {
+ ReportError("Data corrupted");
+ return -1;
+ }
+ }
+
+ return(0);
+}
+
+
+/* verify that a range of number strings exist
+ * or don't exist. And that the data is valid
+ */
+#define SHOULD_EXIST 1
+#define SHOULD_NOT_EXIST 0
+int
+VerifyRange(int32 low, int32 high, int32 should_exist, key_type_enum key_type)
+{
+ DBT *key, data;
+ int32 num;
+ int status;
+
+ TraceMe(1, ("Verifying: %ld to %ld, using %s keys",
+ low, high, key_type == USE_SMALL_KEY ? "SMALL" : "LARGE"));
+
+ for(num = low; num <= high; num++)
+ {
+
+ key = GenKey(num, key_type);
+
+ status = (*database->get)(database, key, &data, 0);
+
+ if(status == 0)
+ {
+ /* got the item */
+ if(!should_exist)
+ {
+ ReportError("Item exists but shouldn't: %ld", num);
+ }
+ else
+ {
+ /* else verify the data */
+ VerifyData(&data, num, key_type);
+ }
+ }
+ else if(status > 0)
+ {
+ /* item not found */
+ if(should_exist)
+ {
+ ReportError("Item not found but should be: %ld", num);
+ }
+ }
+ else
+ {
+ /* database error */
+ ReportError("Database error");
+ return(-1);
+ }
+
+ }
+
+ TraceMe(1, ("Correctly verified: %ld to %ld", low, high));
+
+ return(0);
+
+}
+
+DBT *
+GenData(int32 num)
+{
+ int32 n;
+ static DBT *data=0;
+ int32 *int32_array;
+ size_t size;
+
+ if(!data)
+ {
+ data = (DBT*)malloc(sizeof(DBT));
+ data->size = 0;
+ data->data = 0;
+ }
+ else if(data->data)
+ {
+ free(data->data);
+ }
+
+ n = rand();
+
+ n = n % 512; /* bound to a 2K size */
+
+
+ size = sizeof(int32)*(n+1);
+ int32_array = (int32 *) malloc(size);
+
+ memcpy(&int32_array[0], &n, sizeof(int32));
+
+ for(; n > 0; n--)
+ {
+ memcpy(&int32_array[n], &num, sizeof(int32));
+ }
+
+ data->data = (void*)int32_array;
+ data->size = size;
+
+ return(data);
+}
+
+#define ADD_RANGE 1
+#define DELETE_RANGE 2
+
+int
+AddOrDelRange(int32 low, int32 high, int action, key_type_enum key_type)
+{
+ DBT *key, *data;
+#if 0 /* only do this if your really analy checking the puts */
+ DBT tmp_data;
+#endif
+ int32 num;
+ int status;
+
+ if(action != ADD_RANGE && action != DELETE_RANGE)
+ assert(0);
+
+ if(action == ADD_RANGE)
+ {
+ TraceMe(1, ("Adding: %ld to %ld: %s keys", low, high,
+ key_type == USE_SMALL_KEY ? "SMALL" : "LARGE"));
+ }
+ else
+ {
+ TraceMe(1, ("Deleting: %ld to %ld: %s keys", low, high,
+ key_type == USE_SMALL_KEY ? "SMALL" : "LARGE"));
+ }
+
+ for(num = low; num <= high; num++)
+ {
+
+ key = GenKey(num, key_type);
+
+ if(action == ADD_RANGE)
+ {
+ data = GenData(num);
+ status = (*database->put)(database, key, data, 0);
+ }
+ else
+ {
+ status = (*database->del)(database, key, 0);
+ }
+
+ if(status < 0)
+ {
+ ReportError("Database error %s item: %ld",
+ action == ADD_RANGE ? "ADDING" : "DELETING",
+ num);
+ }
+ else if(status > 0)
+ {
+ ReportError("Could not %s item: %ld",
+ action == ADD_RANGE ? "ADD" : "DELETE",
+ num);
+ }
+ else if(action == ADD_RANGE)
+ {
+#define SYNC_EVERY_TIME
+#ifdef SYNC_EVERY_TIME
+ status = (*database->sync)(database, 0);
+ if(status != 0)
+ ReportError("Database error syncing after add");
+#endif
+
+#if 0 /* only do this if your really analy checking the puts */
+
+ /* make sure we can still get it
+ */
+ status = (*database->get)(database, key, &tmp_data, 0);
+
+ if(status != 0)
+ {
+ ReportError("Database error checking item just added: %d",
+ num);
+ }
+ else
+ {
+ /* now verify that none of the ones we already
+ * put in have disappeared
+ */
+ VerifyRange(low, num, SHOULD_EXIST, key_type);
+ }
+#endif
+
+ }
+ }
+
+
+ if(action == ADD_RANGE)
+ {
+ TraceMe(1, ("Successfully added: %ld to %ld", low, high));
+ }
+ else
+ {
+ TraceMe(1, ("Successfully deleted: %ld to %ld", low, high));
+ }
+
+ return(0);
+}
+
+int
+TestRange(int32 low, int32 range, key_type_enum key_type)
+{
+ int status; int32 low_of_range1, high_of_range1; int32 low_of_range2, high_of_range2;
+ int32 low_of_range3, high_of_range3;
+
+ status = AddOrDelRange(low, low+range, ADD_RANGE, key_type);
+ status = VerifyRange(low, low+range, SHOULD_EXIST, key_type);
+
+ TraceMe(1, ("Finished with sub test 1"));
+
+ SeqDatabase();
+
+ low_of_range1 = low;
+ high_of_range1 = low+(range/2);
+ low_of_range2 = high_of_range1+1;
+ high_of_range2 = low+range;
+ status = AddOrDelRange(low_of_range1, high_of_range1, DELETE_RANGE, key_type);
+ status = VerifyRange(low_of_range1, high_of_range1, SHOULD_NOT_EXIST, key_type);
+ status = VerifyRange(low_of_range2, low_of_range2, SHOULD_EXIST, key_type);
+
+ TraceMe(1, ("Finished with sub test 2"));
+
+ SeqDatabase();
+
+ status = AddOrDelRange(low_of_range1, high_of_range1, ADD_RANGE, key_type);
+ /* the whole thing should exist now */
+ status = VerifyRange(low, low+range, SHOULD_EXIST, key_type);
+
+ TraceMe(1, ("Finished with sub test 3"));
+
+ SeqDatabase();
+
+ status = AddOrDelRange(low_of_range2, high_of_range2, DELETE_RANGE, key_type);
+ status = VerifyRange(low_of_range1, high_of_range1, SHOULD_EXIST, key_type);
+ status = VerifyRange(low_of_range2, high_of_range2, SHOULD_NOT_EXIST, key_type);
+
+ TraceMe(1, ("Finished with sub test 4"));
+
+ SeqDatabase();
+
+ status = AddOrDelRange(low_of_range2, high_of_range2, ADD_RANGE, key_type);
+ /* the whole thing should exist now */
+ status = VerifyRange(low, low+range, SHOULD_EXIST, key_type);
+
+ TraceMe(1, ("Finished with sub test 5"));
+
+ SeqDatabase();
+
+ low_of_range1 = low;
+ high_of_range1 = low+(range/3);
+ low_of_range2 = high_of_range1+1;
+ high_of_range2 = high_of_range1+(range/3);
+ low_of_range3 = high_of_range2+1;
+ high_of_range3 = low+range;
+ /* delete range 2 */
+ status = AddOrDelRange(low_of_range2, high_of_range2, DELETE_RANGE, key_type);
+ status = VerifyRange(low_of_range1, high_of_range1, SHOULD_EXIST, key_type);
+ status = VerifyRange(low_of_range2, low_of_range2, SHOULD_NOT_EXIST, key_type);
+ status = VerifyRange(low_of_range3, low_of_range2, SHOULD_EXIST, key_type);
+
+ TraceMe(1, ("Finished with sub test 6"));
+
+ SeqDatabase();
+
+ status = AddOrDelRange(low_of_range2, high_of_range2, ADD_RANGE, key_type);
+ /* the whole thing should exist now */
+ status = VerifyRange(low, low+range, SHOULD_EXIST, key_type);
+
+ TraceMe(1, ("Finished with sub test 7"));
+
+ return(0);
+}
+
+#define START_RANGE 109876
+int
+main(int argc, char **argv)
+{
+ int32 i, j=0;
+ int quick_exit = 0;
+ int large_keys = 0;
+ HASHINFO hash_info = {
+ 16*1024,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0};
+
+
+ if(argc > 1)
+ {
+ while(argc > 1)
+ {
+ if(!strcmp(argv[argc-1], "-quick"))
+ quick_exit = 1;
+ else if(!strcmp(argv[argc-1], "-large"))
+ {
+ large_keys = 1;
+ }
+ argc--;
+ }
+ }
+
+ database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, &hash_info);
+
+ if(!database)
+ {
+ ReportError("Could not open database");
+#ifdef unix
+ perror("");
+#endif
+ exit(1);
+ }
+
+ if(quick_exit)
+ {
+ if(large_keys)
+ TestRange(START_RANGE, 200, USE_LARGE_KEY);
+ else
+ TestRange(START_RANGE, 200, USE_SMALL_KEY);
+
+ (*database->sync)(database, 0);
+ (*database->close)(database);
+ exit(0);
+ }
+
+ for(i=100; i < 10000000; i+=200)
+ {
+ if(1 || j)
+ {
+ TestRange(START_RANGE, i, USE_LARGE_KEY);
+ j = 0;
+ }
+ else
+ {
+ TestRange(START_RANGE, i, USE_SMALL_KEY);
+ j = 1;
+ }
+
+ if(1 == rand() % 3)
+ {
+ (*database->sync)(database, 0);
+ }
+
+ if(1 == rand() % 3)
+ {
+ /* close and reopen */
+ (*database->close)(database);
+ database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, 0);
+ if(!database)
+ {
+ ReportError("Could not reopen database");
+#ifdef unix
+ perror("");
+#endif
+ exit(1);
+ }
+ }
+ else
+ {
+ /* reopen database without closeing the other */
+ database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, 0);
+ if(!database)
+ {
+ ReportError("Could not reopen database "
+ "after not closing the other");
+#ifdef unix
+ perror("");
+#endif
+ exit(1);
+ }
+ }
+ }
+
+ return(0);
+}