diff options
Diffstat (limited to 'test/c')
-rw-r--r-- | test/c/README | 2 | ||||
-rw-r--r-- | test/c/chk.ctests | 7 | ||||
-rw-r--r-- | test/c/common/test_util.h | 2 | ||||
-rw-r--r-- | test/c/cutest/CuTest.c | 2 | ||||
-rw-r--r-- | test/c/cutest/CuTest.h | 2 | ||||
-rw-r--r-- | test/c/cutest/CuTests.c | 155 | ||||
-rw-r--r-- | test/c/cutest/Runner.c | 6 | ||||
-rw-r--r-- | test/c/run_failchk.sh | 132 | ||||
-rw-r--r-- | test/c/suites/TestCallbackSetterAndGetter.c | 688 | ||||
-rw-r--r-- | test/c/suites/TestChannel.c | 57 | ||||
-rw-r--r-- | test/c/suites/TestDbHotBackup.c | 989 | ||||
-rw-r--r-- | test/c/suites/TestDbTuner.c | 6 | ||||
-rw-r--r-- | test/c/suites/TestEncryption.c | 2 | ||||
-rw-r--r-- | test/c/suites/TestEnvConfig.c | 90 | ||||
-rw-r--r-- | test/c/suites/TestEnvMethod.c | 2 | ||||
-rw-r--r-- | test/c/suites/TestKeyExistErrorReturn.c | 2 | ||||
-rw-r--r-- | test/c/suites/TestMutexAlignment.c | 70 | ||||
-rw-r--r-- | test/c/suites/TestPartial.c | 2 | ||||
-rw-r--r-- | test/c/suites/TestPartition.c | 508 | ||||
-rw-r--r-- | test/c/suites/TestPreOpenSetterAndGetter.c | 1178 | ||||
-rw-r--r-- | test/c/suites/TestQueue.c | 9 | ||||
-rw-r--r-- | test/c/test_api_methods.c | 2 | ||||
-rw-r--r-- | test/c/test_db185.c | 2 | ||||
-rw-r--r-- | test/c/test_failchk.c | 1078 | ||||
-rw-r--r-- | test/c/test_log_verify.c | 2 |
25 files changed, 4527 insertions, 468 deletions
diff --git a/test/c/README b/test/c/README index 9205cfa2..43f6c58c 100644 --- a/test/c/README +++ b/test/c/README @@ -1,5 +1,5 @@ -The C test cases are currently (loosly) based on the CuTest harness. Loosely +The C test cases are currently (loosely) based on the CuTest harness. Loosely because the harness has been heavily modified from the original version. There are still a few old test cases in the source tree. Those will be diff --git a/test/c/chk.ctests b/test/c/chk.ctests index 1c76e495..3306f8aa 100644 --- a/test/c/chk.ctests +++ b/test/c/chk.ctests @@ -37,7 +37,12 @@ echo "Building DB library, this can take a while." CINC="-I$b -I$s -I$s/dbinc" [ `uname` = "Linux" ] && CINC="$CINC -pthread" -for i in `ls test_*.c`; do +C_TESTS="\ +test_api_methods.c +test_db185.c +test_log_verify.c" + +for i in $C_TESTS; do echo "=== Running $i ===" | tee -a compile.out if cc -g -Wall $CINC $i $b/libdb.a -o t >> compile.out 2>&1; then diff --git a/test/c/common/test_util.h b/test/c/common/test_util.h index 74be1bb8..db1e789d 100644 --- a/test/c/common/test_util.h +++ b/test/c/common/test_util.h @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2012, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ diff --git a/test/c/cutest/CuTest.c b/test/c/cutest/CuTest.c index d7da5b22..68e7b6ac 100644 --- a/test/c/cutest/CuTest.c +++ b/test/c/cutest/CuTest.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ diff --git a/test/c/cutest/CuTest.h b/test/c/cutest/CuTest.h index 76cbceb4..3163fde0 100644 --- a/test/c/cutest/CuTest.h +++ b/test/c/cutest/CuTest.h @@ -1,7 +1,7 @@ /* * See the file LICENSE for redistribution information. * - * Copyright (c) 2012, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ diff --git a/test/c/cutest/CuTests.c b/test/c/cutest/CuTests.c index 430aa7fa..0e73fb96 100644 --- a/test/c/cutest/CuTests.c +++ b/test/c/cutest/CuTests.c @@ -6,6 +6,12 @@ #include "CuTest.h" +extern int TestCallbackSetterAndGetterSuiteSetup(CuSuite *suite); +extern int TestCallbackSetterAndGetterSuiteTeardown(CuSuite *suite); +extern int TestCallbackSetterAndGetterTestSetup(CuTest *ct); +extern int TestCallbackSetterAndGetterTestTeardown(CuTest *ct); +extern int TestEnvCallbacks(CuTest *ct); +extern int TestDbCallbacks(CuTest *ct); extern int TestChannelSuiteSetup(CuSuite *suite); extern int TestChannelSuiteTeardown(CuSuite *suite); extern int TestChannelTestSetup(CuTest *test); @@ -15,11 +21,14 @@ extern int TestDbHotBackupSuiteSetup(CuSuite *suite); extern int TestDbHotBackupSuiteTeardown(CuSuite *suite); extern int TestDbHotBackupTestSetup(CuTest *ct); extern int TestDbHotBackupTestTeardown(CuTest *ct); -extern int TestDbHotBackupSimpleEnv(CuTest *ct); -extern int TestDbHotBackupPartitionDB(CuTest *ct); -extern int TestDbHotBackupMultiDataDir(CuTest *ct); -extern int TestDbHotBackupSetLogDir(CuTest *ct); -extern int TestDbHotBackupQueueDB(CuTest *ct); +extern int TestBackupSimpleEnvNoCallback(CuTest *ct); +extern int TestBackupSimpleEnvWithCallback(CuTest *ct); +extern int TestBackupSimpleEnvWithConfig(CuTest *ct); +extern int TestBackupPartitionDB(CuTest *ct); +extern int TestBackupMultiDataDir(CuTest *ct); +extern int TestBackupSetLogDir(CuTest *ct); +extern int TestBackupQueueDB(CuTest *ct); +extern int TestBackupHeapDB(CuTest *ct); extern int TestDbTuner(CuTest *ct); extern int TestNoEncryptedDb(CuTest *ct); extern int TestEncryptedDbFlag(CuTest *ct); @@ -51,6 +60,7 @@ extern int TestSetTransactionTimeout(CuTest *ct); extern int TestSetCachesize(CuTest *ct); extern int TestSetThreadCount(CuTest *ct); /* SKIP */ extern int TestKeyExistErrorReturn(CuTest *ct); +extern int TestMutexAlignment(CuTest *ct); extern int TestPartialSuiteSetup(CuSuite *ct); extern int TestPartialSuiteTeardown(CuSuite *ct); extern int TestPartialTestSetup(CuTest *ct); @@ -59,8 +69,49 @@ extern int TestDbPartialGet(CuTest *ct); extern int TestDbPartialPGet(CuTest *ct); extern int TestCursorPartialGet(CuTest *ct); extern int TestCursorPartialPGet(CuTest *ct); +extern int TestPartitionSuiteSetup(CuSuite *suite); +extern int TestPartitionSuiteTeardown(CuSuite *suite); +extern int TestPartitionTestSetup(CuTest *ct); +extern int TestPartitionTestTeardown(CuTest *ct); +extern int TestPartOneKeyNoData(CuTest *ct); +extern int TestPartTwoKeyNoData(CuTest *ct); +extern int TestPartDuplicatedKey(CuTest *ct); +extern int TestPartUnsortedKey(CuTest *ct); +extern int TestPartNumber(CuTest *ct); +extern int TestPartKeyCallBothSet(CuTest *ct); +extern int TestPartKeyCallNeitherSet(CuTest *ct); +extern int TestPreOpenSetterAndGetterSuiteSetup(CuSuite *suite); +extern int TestPreOpenSetterAndGetterSuiteTeardown(CuSuite *suite); +extern int TestPreOpenSetterAndGetterTestSetup(CuTest *ct); +extern int TestPreOpenSetterAndGetterTestTeardown(CuTest *ct); +extern int TestEnvPreOpenSetterAndGetter(CuTest *ct); +extern int TestDbPreOpenSetterAndGetter(CuTest *ct); +extern int TestMpoolFilePreOpenSetterAndGetter(CuTest *ct); +extern int TestSequencePreOpenSetterAndGetter(CuTest *ct); extern int TestQueue(CuTest *ct); +int RunCallbackSetterAndGetterTests(CuString *output) +{ + CuSuite *suite = CuSuiteNew("TestCallbackSetterAndGetter", + TestCallbackSetterAndGetterSuiteSetup, + TestCallbackSetterAndGetterSuiteTeardown); + int count; + + SUITE_ADD_TEST(suite, TestEnvCallbacks, + TestCallbackSetterAndGetterTestSetup, + TestCallbackSetterAndGetterTestTeardown); + SUITE_ADD_TEST(suite, TestDbCallbacks, + TestCallbackSetterAndGetterTestSetup, + TestCallbackSetterAndGetterTestTeardown); + + CuSuiteRun(suite); + CuSuiteSummary(suite, output); + CuSuiteDetails(suite, output); + count = suite->failCount; + CuSuiteDelete(suite); + return (count); +} + int RunChannelTests(CuString *output) { CuSuite *suite = CuSuiteNew("TestChannel", @@ -84,15 +135,21 @@ int RunDbHotBackupTests(CuString *output) TestDbHotBackupSuiteSetup, TestDbHotBackupSuiteTeardown); int count; - SUITE_ADD_TEST(suite, TestDbHotBackupSimpleEnv, + SUITE_ADD_TEST(suite, TestBackupSimpleEnvNoCallback, + TestDbHotBackupTestSetup, TestDbHotBackupTestTeardown); + SUITE_ADD_TEST(suite, TestBackupSimpleEnvWithCallback, + TestDbHotBackupTestSetup, TestDbHotBackupTestTeardown); + SUITE_ADD_TEST(suite, TestBackupSimpleEnvWithConfig, TestDbHotBackupTestSetup, TestDbHotBackupTestTeardown); - SUITE_ADD_TEST(suite, TestDbHotBackupPartitionDB, + SUITE_ADD_TEST(suite, TestBackupPartitionDB, TestDbHotBackupTestSetup, TestDbHotBackupTestTeardown); - SUITE_ADD_TEST(suite, TestDbHotBackupMultiDataDir, + SUITE_ADD_TEST(suite, TestBackupMultiDataDir, TestDbHotBackupTestSetup, TestDbHotBackupTestTeardown); - SUITE_ADD_TEST(suite, TestDbHotBackupSetLogDir, + SUITE_ADD_TEST(suite, TestBackupSetLogDir, TestDbHotBackupTestSetup, TestDbHotBackupTestTeardown); - SUITE_ADD_TEST(suite, TestDbHotBackupQueueDB, + SUITE_ADD_TEST(suite, TestBackupQueueDB, + TestDbHotBackupTestSetup, TestDbHotBackupTestTeardown); + SUITE_ADD_TEST(suite, TestBackupHeapDB, TestDbHotBackupTestSetup, TestDbHotBackupTestTeardown); CuSuiteRun(suite); @@ -232,6 +289,23 @@ int RunKeyExistErrorReturnTests(CuString *output) return (count); } +int RunMutexAlignmentTests(CuString *output) +{ + CuSuite *suite = CuSuiteNew("TestMutexAlignment", + NULL, NULL); + int count; + + SUITE_ADD_TEST(suite, TestMutexAlignment, + NULL, NULL); + + CuSuiteRun(suite); + CuSuiteSummary(suite, output); + CuSuiteDetails(suite, output); + count = suite->failCount; + CuSuiteDelete(suite); + return (count); +} + int RunPartialTests(CuString *output) { CuSuite *suite = CuSuiteNew("TestPartial", @@ -255,6 +329,63 @@ int RunPartialTests(CuString *output) return (count); } +int RunPartitionTests(CuString *output) +{ + CuSuite *suite = CuSuiteNew("TestPartition", + TestPartitionSuiteSetup, TestPartitionSuiteTeardown); + int count; + + SUITE_ADD_TEST(suite, TestPartOneKeyNoData, + TestPartitionTestSetup, TestPartitionTestTeardown); + SUITE_ADD_TEST(suite, TestPartTwoKeyNoData, + TestPartitionTestSetup, TestPartitionTestTeardown); + SUITE_ADD_TEST(suite, TestPartDuplicatedKey, + TestPartitionTestSetup, TestPartitionTestTeardown); + SUITE_ADD_TEST(suite, TestPartUnsortedKey, + TestPartitionTestSetup, TestPartitionTestTeardown); + SUITE_ADD_TEST(suite, TestPartNumber, + TestPartitionTestSetup, TestPartitionTestTeardown); + SUITE_ADD_TEST(suite, TestPartKeyCallBothSet, + TestPartitionTestSetup, TestPartitionTestTeardown); + SUITE_ADD_TEST(suite, TestPartKeyCallNeitherSet, + TestPartitionTestSetup, TestPartitionTestTeardown); + + CuSuiteRun(suite); + CuSuiteSummary(suite, output); + CuSuiteDetails(suite, output); + count = suite->failCount; + CuSuiteDelete(suite); + return (count); +} + +int RunPreOpenSetterAndGetterTests(CuString *output) +{ + CuSuite *suite = CuSuiteNew("TestPreOpenSetterAndGetter", + TestPreOpenSetterAndGetterSuiteSetup, + TestPreOpenSetterAndGetterSuiteTeardown); + int count; + + SUITE_ADD_TEST(suite, TestEnvPreOpenSetterAndGetter, + TestPreOpenSetterAndGetterTestSetup, + TestPreOpenSetterAndGetterTestTeardown); + SUITE_ADD_TEST(suite, TestDbPreOpenSetterAndGetter, + TestPreOpenSetterAndGetterTestSetup, + TestPreOpenSetterAndGetterTestTeardown); + SUITE_ADD_TEST(suite, TestMpoolFilePreOpenSetterAndGetter, + TestPreOpenSetterAndGetterTestSetup, + TestPreOpenSetterAndGetterTestTeardown); + SUITE_ADD_TEST(suite, TestSequencePreOpenSetterAndGetter, + TestPreOpenSetterAndGetterTestSetup, + TestPreOpenSetterAndGetterTestTeardown); + + CuSuiteRun(suite); + CuSuiteSummary(suite, output); + CuSuiteDetails(suite, output); + count = suite->failCount; + CuSuiteDelete(suite); + return (count); +} + int RunQueueTests(CuString *output) { CuSuite *suite = CuSuiteNew("TestQueue", @@ -273,6 +404,7 @@ int RunQueueTests(CuString *output) } TestSuite g_suites[] = { + { "TestCallbackSetterAndGetter", RunCallbackSetterAndGetterTests }, { "TestChannel", RunChannelTests }, { "TestDbHotBackup", RunDbHotBackupTests }, { "TestDbTuner", RunDbTunerTests }, @@ -280,7 +412,10 @@ TestSuite g_suites[] = { { "TestEnvConfig", RunEnvConfigTests }, { "TestEnvMethod", RunEnvMethodTests }, { "TestKeyExistErrorReturn", RunKeyExistErrorReturnTests }, + { "TestMutexAlignment", RunMutexAlignmentTests }, { "TestPartial", RunPartialTests }, + { "TestPartition", RunPartitionTests }, + { "TestPreOpenSetterAndGetter", RunPreOpenSetterAndGetterTests }, { "TestQueue", RunQueueTests }, { "", NULL }, }; diff --git a/test/c/cutest/Runner.c b/test/c/cutest/Runner.c index c8812afe..4de4521f 100644 --- a/test/c/cutest/Runner.c +++ b/test/c/cutest/Runner.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ @@ -85,9 +85,9 @@ int main(int argc, char **argv) } } while(num_suites != 0) - free(suites[num_suites--]); + free(suites[--num_suites]); while(num_tests != 0) - free(tests[num_tests--]); + free(tests[--num_tests]); if (failed > 0) return (1); else diff --git a/test/c/run_failchk.sh b/test/c/run_failchk.sh new file mode 100644 index 00000000..1ab8c261 --- /dev/null +++ b/test/c/run_failchk.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +# +# test_failchk -- +# Test failchk in a simple threaded application of some numbers of readers +# and writers competing to read and update a set of words. +# A typical test scenario runs this programs several times concurrently, +# with different options: +# first with the -I option to clear out any home directory +# one or more instances with -f to activate the failchk thread +# one or more instance with neither -I nor -f, as minimally +# involved workers. +# +# If no arguments are given, it selects a default mix of processes: +# run_failchk.sh 100 2 '-r1 -w2' -w2 +# +# This does 100 iterations of this failchk group of applications: +# 2 copies of test_failchk with 1 reader and 2 writer threads +# a solo test_failchk the default # of readers (4) and 2 writers +# a copy of the last test_failchk adding a failchk thread +# wait a few seconds +# randomly kill one of the non-failchk process +# +# This shell script initializes the env with the default number of readers and +# writers, then starts one "worker" process with each listed argument. The last +# worker started also uses -f, to ensure that at least one process will be +# running failchk. It is okay for -f to also be passed to one or more of the +# other processes. One of the processes is selected at random to be killed. + +if test $# -eq 0 ; then + set -- 100 2 '-r1 -w2' -w2 + echo Running $0 $@ +fi +repeat=$1 +dup_procs=$2 +shift; shift +nprocs=0 +victim=-1 + +function timestamp { +perl \ + -e 'use strict;' \ + -e 'use Time::HiRes qw(time);' \ + -e 'use POSIX qw(strftime);' \ + -e 'local $| = 1; # Line buffering on' \ + -e 'while (<>) {' \ + -e ' # HiRes time is a float, often down to nanoseconds.' \ + -e ' my $t = time;' \ + -e ' # Display the time of day, appending microseconds.' \ + -e ' my $date = (strftime "%H:%M:%S", localtime $t ) .' \ + -e ' sprintf ".%06d", ($t-int($t))*1000000;' \ + -e ' printf("%s: %s", $date, $_);' \ + -e '}' +} + +function dofork { + # Keep a slight bit of history -- just the previous iteration + test -f $home/FAILCHK.$nprocs && \ + mv -f $home/FAILCHK.$nprocs $home/FAILCHK.prev.$nprocs + test_failchk $* $arg > $home/FAILCHK.$nprocs 2>&1 & + pids[$nprocs]=$! + printf "Process %d(%s): test_failchk %s\n" $nprocs ${pids[$nprocs]} "$*" + nprocs=$((++nprocs)) +} + +make test_failchk + +home=TESTDIR +rm $home/* +test -d $home || mkdir $home + +initargs=$1 +shift + +function main { + for (( i = 0; $i < $repeat; i=$((++i)) )) ; do + test -f stat && mv stat stat.prev + test -d $home && cp -pr $home $home.prev + nprocs=0 + dofork $initargs + sleep 2 + for arg in "$@" ; do + dofork $arg + done + + # Duplicate the last configuration, then add a for-sure failchk'er. + for (( j = 0; $j < $dup_procs; j=$((++j)) )) ; do + dofork $arg + done + # If the failchk process does real work, it could also trip over. + dofork -f $arg -w1 -r0 + + # $RANDOM is not very random in the lowest bits, div by 23 to scatter a little. + victim=$((($RANDOM / 23) % ($nprocs - 1))) + delay=$((($RANDOM / 37) % 15 + 4)) + echo "$0 #$i: Processes: ${pids[@]}; delaying $delay seconds before killing #$victim" + sleep $delay + echo "$0 #$i: Killing ${pids[$victim]}" + # Stop if a process has exited prematurely + kill -9 ${pids[$victim]} || exit 100 + + for (( j = 0; $j < $nprocs; j=$((++j)) )) ; do + wait ${pids[$j]} + stat=$? + echo "Waited for process #$j ${pids[$j]} returned $stat" + # SIGTERM exits with 2, SIGKILL 137; anything else is bad. + if test $stat -gt 2 -a $stat -ne 137; then + signal=`expr $stat - 128` + test $signal -lt 0 && signal=0 + printf \ + "Unexpected failure for process %d: status %d signal %d\n" \ + ${pids[$j]} $stat $signal + exit $stat + fi + + done + echo "" + sleep 2 + # Saving stats would be nice here; but db_stat can trip over a bad lock + # db_stat -NEh $home > stat || exit 50 + + # If a system might possibly be running multiple instances of this + # script then the follow lines needs to stay a comment. However, + # when running by itself you can notified of non-killed processes + # by enabling the pgrep. + # pgrep test_failchk && echo "PROCESSES REMAIN ACTIVE?!" && exit 101 + + done + echo $i iterations done +} + +main $* 2>&1 | timestamp diff --git a/test/c/suites/TestCallbackSetterAndGetter.c b/test/c/suites/TestCallbackSetterAndGetter.c new file mode 100644 index 00000000..924ab955 --- /dev/null +++ b/test/c/suites/TestCallbackSetterAndGetter.c @@ -0,0 +1,688 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2012, 2015 Oracle and/or its affiliates. All rights reserved. + * + * $Id$ + */ + +/* + * Test setting and getting callbacks on the DB_ENV or DB handle. [#21553] + * + * It tests the callback setters/getters. These setters/getters are + * divided into the following two sets: + * a. The callback setters and getters on DB_ENV handle. + * b. The callback setters and getters on DB handle. + * The general flow for each callback setting/getting test is: + * 1. Create the handle. + * 2. Set the callback on the handle. + * 3. Get the callback and verify it. + * 4. Issue the open call on the handle. + * 5. Get the callback again and verify it. + * 6. Close the handle. + * The callbacks we provide do not guarantee to work, but they guarantee + * the handle can issue a call to open successfully. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "db.h" +#include "CuTest.h" +#include "test_util.h" + +/* + * The callbacks for DB_ENV handle. + * Some of the callbacks are shared by DB handle as well, and there will be + * comments for them. The order follows: + * https://sleepycat.oracle.com/support.web/doc_builds/newdocs.db/api_reference/C/env.html + * so that checking code is easier. + */ +/* For DB_ENV->get_alloc & DB->get_alloc */ +typedef void *(*app_malloc_fcn)(size_t); +typedef void *(*app_realloc_fcn)(void *, size_t); +typedef void (*app_free_fcn)(void *); +/* For DB_ENV->get_app_dispatch */ +typedef int (*tx_recover_fcn)(DB_ENV *dbenv, DBT *log_rec, + DB_LSN *lsn, db_recops op); +/* For DB_ENV->get_backup_callbacks */ +typedef int (*open_func)(DB_ENV *, const char *dbname, + const char *target, void **handle); +typedef int (*write_func)(DB_ENV *, u_int32_t offset_gbytes, + u_int32_t offset_bytes, u_int32_t size, u_int8_t *buf, void *handle); +typedef int (*close_func)(DB_ENV *, const char *dbname, void *handle); +/* For DB_ENV->get_errcall & DB->get_errcall */ +typedef void (*db_errcall_fcn)(const DB_ENV *dbenv, + const char *errpfx, const char *msg); +/* For DB_ENV->get_feedback */ +typedef void (*dbenv_feedback_fcn)(DB_ENV *dbenv, int opcode, int percent); +/* For DB_ENV->get_isalive */ +typedef int (*is_alive_fcn)(DB_ENV *dbenv, pid_t pid, + db_threadid_t tid, u_int32_t flags); +/* For DB_ENV->get_msgcall & DB->get_msgcall */ +typedef void (*db_msgcall_fcn)(const DB_ENV *dbenv, const char *msg); +/* For DB_ENV->get_thread_id_fn */ +typedef void (*thread_id_fcn)(DB_ENV *dbenv, pid_t *pid, db_threadid_t *tid); +/* For DB_ENV->get_thread_id_string_fn */ +typedef char *(*thread_id_string_fcn)(DB_ENV *dbenv, pid_t pid, + db_threadid_t tid, char *buf); + +/* + * The callbacks for DB handle. + * If the DB handle shares a callback with DB_ENV handle, it will not be + * listed here, since it has been listed above. The order follows: + * https://sleepycat.oracle.com/support.web/doc_builds/newdocs.db/api_reference/C/db.html + * so that checking code is easier. + */ +/* For DB->get_dup_compare */ +typedef int (*dup_compare_fcn)(DB *db, + const DBT *dbt1, const DBT *dbt2, size_t *locp); +/* For DB->get_feedback */ +typedef void (*db_feedback_fcn)(DB *dbp, int opcode, int percent); +/* For DB->get_partition_callback */ +typedef u_int32_t (*db_partition_fcn) (DB *db, DBT *key); +/* For DB->get_append_recno */ +typedef int (*db_append_recno_fcn)(DB *dbp, DBT *data, db_recno_t recno); +/* For DB->get_bt_compare */ +typedef int (*bt_compare_fcn)(DB *db, + const DBT *dbt1, const DBT *dbt2, size_t *locp); +/* For DB->get_bt_compress */ +typedef int (*bt_compress_fcn)(DB *db, const DBT *prevKey, + const DBT *prevData, const DBT *key, const DBT *data, DBT *dest); +typedef int (*bt_decompress_fcn)(DB *db, const DBT *prevKey, + const DBT *prevData, DBT *compressed, DBT *destKey, DBT *destData); +/* For DB->get_bt_prefix */ +typedef size_t (*bt_prefix_fcn)(DB *, const DBT *dbt1, const DBT *dbt2); +/* For DB->get_h_compare */ +typedef int (*h_compare_fcn)(DB *db, + const DBT *dbt1, const DBT *dbt2, size_t *locp); +/* For DB->get_h_hash */ +typedef u_int32_t (*h_hash_fcn)(DB *dbp, const void *bytes, u_int32_t length); + +/* + * The order for declarations follows above, so that checking code is easier. + * Their definitions follow the same order, testing order follows it as well. + */ +static void *t_malloc(size_t sz); +static void *t_realloc(void *addr, size_t sz); +static void t_free(void *addr); +static int t_app_dispatch(DB_ENV *dbenv, + DBT *log_rec, DB_LSN *lsn, db_recops op); +static int t_open_func(DB_ENV *, const char *dbname, + const char *target, void **handle); +static int t_write_func(DB_ENV *, u_int32_t offset_gbytes, + u_int32_t offset_bytes, u_int32_t size, u_int8_t *buf, void *handle); +static int t_close_func(DB_ENV *, const char *dbname, void *handle); +static void t_errcall(const DB_ENV *dbenv, + const char *errpfx, const char *msg); +static void t_dbenv_callback(DB_ENV *dbenv, int opcode, int percent); +static int t_is_alive(DB_ENV *dbenv, + pid_t pid, db_threadid_t tid, u_int32_t flags); +static void t_msgcall(const DB_ENV *dbenv, const char *msg); +static void t_thread_id(DB_ENV *dbenv, pid_t *pid, db_threadid_t *tid); +static char *t_thread_id_string(DB_ENV *dbenv, + pid_t pid, db_threadid_t tid, char *buf); +static int t_dup_compare(DB *db, const DBT *dbt1, const DBT *dbt2, size_t *locp); +static void t_db_feedback(DB *dbp, int opcode, int percent); +static u_int32_t t_db_partition(DB *db, DBT *key); +static int t_append_recno(DB *dbp, DBT *data, db_recno_t recno); +static int t_bt_compare(DB *db, const DBT *dbt1, const DBT *dbt2, size_t *locp) ; +static int t_compress(DB *db, const DBT *prevKey, const DBT *prevData, + const DBT *key, const DBT *data, DBT *dest); +static int t_decompress(DB *db, const DBT *prevKey,const DBT *prevData, + DBT *compressed, DBT *destKey, DBT *destData); +static size_t t_bt_prefix(DB *db, const DBT *dbt1, const DBT *dbt2); +static int t_h_compare(DB *db, const DBT *dbt1, const DBT *dbt2, size_t *locp); +static u_int32_t t_h_hash(DB *dbp, const void *bytes, u_int32_t length); + +/* + * Common head routine for functions setting one callback. + */ +#define TEST_FUNCTION_1ARG_HEAD(type) \ + type func_rt = NULL + +/* + * Common pre-open routine for functions setting one callback. + * We get the callback after setting, and check the callback. + */ +#define TEST_FUNCTION_1ARG_PREOPEN(handle, setter, getter, func) \ + CuAssert(ct, #handle"->"#setter, \ + handle->setter(handle, func) == 0); \ + CuAssert(ct, "preopen: "#handle"->"#getter, \ + handle->getter(handle, &func_rt) == 0); \ + CuAssert(ct, "preopen: check "#func, func == func_rt) + +/* + * Common post-open routine for functions setting one callback. + * After object(DB_ENV/DB) open, we check if we still can get the callback + * and check the callback. Also, we close the handle. + */ +#define TEST_FUNCTION_1ARG_POSTOPEN(handle, getter, func) \ + CuAssert(ct, "postopen: "#handle"->"#getter, \ + handle->getter(handle, &func_rt) == 0); \ + CuAssert(ct, "postopen: check "#func, func == func_rt); \ + info.handle = NULL; \ + CuAssert(ct, #handle"->close", handle->close(handle, 0) == 0) + +/* + * Like TEST_FUNCTION_1ARG_PREOPEN, but both setter and getter have no return. + */ +#define TEST_FUNCTION_1ARG_PREOPEN_VOID(handle, setter, getter, func) \ + handle->setter(handle, func); \ + handle->getter(handle, &func_rt); \ + CuAssert(ct, "preopen: check "#func, func == func_rt) + +/* + * Like TEST_FUNCTION_1ARG_POSTOPEN, but both setter and getter have no return. + */ +#define TEST_FUNCTION_1ARG_POSTOPEN_VOID(handle, getter, func) \ + handle->getter(handle, &func_rt); \ + CuAssert(ct, "postopen: check "#func, func == func_rt); \ + info.handle = NULL; \ + CuAssert(ct, #handle"->close", handle->close(handle, 0) == 0) + +/* + * Common head routine for functions setting two callbacks. + */ +#define TEST_FUNCTION_2ARG_HEAD(type1, type2) \ + type1 func_rt1 = NULL; \ + type2 func_rt2 = NULL + +/* + * Common pre-open routine for functions setting two callbacks. + * We get the callbacks after setting, and check the callbacks. + */ +#define TEST_FUNCTION_2ARG_PREOPEN(handle, setter, getter, func1, func2)\ + CuAssert(ct, #handle"->"#setter, \ + handle->setter(handle, func1, func2) == 0); \ + CuAssert(ct, "preopen: "#handle"->"#getter, \ + handle->getter(handle, &func_rt1, &func_rt2) == 0); \ + CuAssert(ct, "preopen: check "#func1, func1 == func_rt1); \ + CuAssert(ct, "preopen: check "#func2, func2 == func_rt2) + +/* + * Common post-open routine for functions setting two callbacks. + * After object(DB_ENV/DB) open, we check if we still can get the callbacks + * and check the callbacks. Also, we close the handle. + */ +#define TEST_FUNCTION_2ARG_POSTOPEN(handle, getter, func1, func2) \ + CuAssert(ct, "postopen: "#handle"->"#getter, \ + handle->getter(handle, &func_rt1, &func_rt2) == 0); \ + CuAssert(ct, "postopen: check "#func1, func1 == func_rt1); \ + CuAssert(ct, "postopen: check "#func2, func2 == func_rt2); \ + info.handle = NULL; \ + CuAssert(ct, #handle"->close", handle->close(handle, 0) == 0) + +/* + * Common head routine for functions setting three callbacks. + */ +#define TEST_FUNCTION_3ARG_HEAD(type1, type2, type3) \ + type1 func_rt1 = NULL; \ + type2 func_rt2 = NULL; \ + type3 func_rt3 = NULL + +/* + * Common pre-open routine for functions setting three callback. + * We get the callbacks after setting, and check the callbacks. + */ +#define TEST_FUNCTION_3ARG_PREOPEN(handle, setter, getter, func1, func2,\ + func3) \ + CuAssert(ct, #handle"->"#setter, \ + handle->setter(handle, func1, func2, func3) == 0); \ + CuAssert(ct, "preopen: "#handle"->"#getter, handle->getter( \ + handle, &func_rt1, &func_rt2, &func_rt3) == 0); \ + CuAssert(ct, "preopen: check "#func1, func1 == func_rt1); \ + CuAssert(ct, "preopen: check "#func2, func2 == func_rt2); \ + CuAssert(ct, "preopen: check "#func3, func3 == func_rt3) + +/* + * Common post-open routine for functions setting three callbacks. + * After object(DB_ENV/DB) open, we check if we still can get the callbacks + * and check the callbacks. Also, we close the handle. + */ +#define TEST_FUNCTION_3ARG_POSTOPEN(handle, getter, func1, func2, func3)\ + CuAssert(ct, "postopen: "#handle"->"#getter, handle->getter( \ + handle, &func_rt1, &func_rt2, &func_rt3) == 0); \ + CuAssert(ct, "postopen: check "#func1, func1 == func_rt1); \ + CuAssert(ct, "postopen: check "#func2, func2 == func_rt2); \ + CuAssert(ct, "postopen: check "#func3, func3 == func_rt3); \ + info.handle = NULL; \ + CuAssert(ct, #handle"->close", handle->close(handle, 0) == 0) + +/* + * Test DB_ENV's functions setting one callback. + */ +#define TEST_ENV_FUNCTIONS_1ARG(setter, getter, type, func) do { \ + DB_ENV *dbenvp; \ + TEST_FUNCTION_1ARG_HEAD(type); \ + CuAssert(ct, "db_env_create", db_env_create(&dbenvp, 0) == 0); \ + info.dbenvp = dbenvp; \ + TEST_FUNCTION_1ARG_PREOPEN(dbenvp, setter, getter, func); \ + CuAssert(ct, "dbenvp->open", dbenvp->open(dbenvp, TEST_ENV, \ + DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | \ + DB_INIT_LOG | DB_INIT_TXN, 0644) == 0); \ + TEST_FUNCTION_1ARG_POSTOPEN(dbenvp, getter, func); \ +} while(0) + +/* + * Test DB_ENV's functions setting one callback, both setter and getter + * have no return. + */ +#define TEST_ENV_FUNCTIONS_1ARG_VOID(setter, getter, type, func) do { \ + DB_ENV *dbenvp; \ + TEST_FUNCTION_1ARG_HEAD(type); \ + CuAssert(ct, "db_env_create", db_env_create(&dbenvp, 0) == 0); \ + info.dbenvp = dbenvp; \ + TEST_FUNCTION_1ARG_PREOPEN_VOID(dbenvp, setter, getter, func); \ + CuAssert(ct, "dbenvp->open", dbenvp->open(dbenvp, TEST_ENV, \ + DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | \ + DB_INIT_LOG | DB_INIT_TXN, 0644) == 0); \ + TEST_FUNCTION_1ARG_POSTOPEN_VOID(dbenvp, getter, func); \ +} while(0) + +/* + * Test DB_ENV's functions setting two callbacks. + */ +#define TEST_ENV_FUNCTIONS_2ARG(setter, getter, type1, func1, type2, func2)\ + do { \ + DB_ENV *dbenvp; \ + TEST_FUNCTION_2ARG_HEAD(type1, type2); \ + CuAssert(ct, "db_env_create", db_env_create(&dbenvp, 0) == 0); \ + info.dbenvp = dbenvp; \ + TEST_FUNCTION_2ARG_PREOPEN(dbenvp, setter, getter, \ + func1, func2); \ + CuAssert(ct, "dbenvp->open", dbenvp->open(dbenvp, TEST_ENV, \ + DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | \ + DB_INIT_LOG | DB_INIT_TXN, 0644) == 0); \ + TEST_FUNCTION_2ARG_POSTOPEN(dbenvp, getter, func1, func2); \ +} while(0) + +/* + * Test DB_ENV's functions setting three callbacks. + */ +#define TEST_ENV_FUNCTIONS_3ARG(setter, getter, type1, func1, type2, \ + func2, type3, func3) do { \ + DB_ENV *dbenvp; \ + TEST_FUNCTION_3ARG_HEAD(type1, type2, type3); \ + CuAssert(ct, "db_env_create", db_env_create(&dbenvp, 0) == 0); \ + info.dbenvp = dbenvp; \ + TEST_FUNCTION_3ARG_PREOPEN(dbenvp, setter, getter, func1, func2,\ + func3); \ + CuAssert(ct, "dbenvp->open", dbenvp->open(dbenvp, TEST_ENV, \ + DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_LOG | \ + DB_INIT_TXN, 0644) == 0); \ + TEST_FUNCTION_3ARG_POSTOPEN(dbenvp, getter, func1, func2, \ + func3); \ +} while(0) + +/* + * Macro for opening database handle. + */ +#define TEST_DB_OPEN(dbtype) if (dbtype == DB_BTREE) { \ + CuAssert(ct, "dbp->set_flags(DB_DUPSORT)", \ + dbp->set_flags(dbp, DB_DUPSORT) == 0); \ + } \ + sprintf(buf, "%s/%d.db", TEST_ENV, indx++); \ + CuAssert(ct, "dbp->open", dbp->open(dbp, NULL, buf, NULL, \ + dbtype, DB_CREATE, 0644) == 0) + +/* + * Test DB's functions setting one callback. + */ +#define TEST_DB_FUNCTIONS_1ARG(setter, getter, dbtype, type, func) do { \ + DB *dbp; \ + char buf[DB_MAXPATHLEN]; \ + TEST_FUNCTION_1ARG_HEAD(type); \ + CuAssert(ct, "db_create", db_create(&dbp, NULL, 0) == 0); \ + info.dbp = dbp; \ + TEST_FUNCTION_1ARG_PREOPEN(dbp, setter, getter, func); \ + TEST_DB_OPEN(dbtype); \ + TEST_FUNCTION_1ARG_POSTOPEN(dbp, getter, func); \ +} while(0) + +/* + * Test DB's functions setting one callback, both setter and getter + * have no return. + */ +#define TEST_DB_FUNCTIONS_1ARG_VOID(setter, getter, dbtype, type, func) \ + do { \ + DB *dbp; \ + char buf[DB_MAXPATHLEN]; \ + TEST_FUNCTION_1ARG_HEAD(type); \ + CuAssert(ct, "db_create", db_create(&dbp, NULL, 0) == 0); \ + info.dbp = dbp; \ + TEST_FUNCTION_1ARG_PREOPEN_VOID(dbp, setter, getter, func); \ + TEST_DB_OPEN(dbtype); \ + TEST_FUNCTION_1ARG_POSTOPEN_VOID(dbp, getter, func); \ +} while(0) + +/* + * Test DB's functions setting two callbacks. + */ +#define TEST_DB_FUNCTIONS_2ARG(setter, getter, dbtype, \ + type1, func1, type2, func2) do { \ + DB *dbp; \ + char buf[DB_MAXPATHLEN]; \ + TEST_FUNCTION_2ARG_HEAD(type1, type2); \ + CuAssert(ct, "db_create", db_create(&dbp, NULL, 0) == 0); \ + info.dbp = dbp; \ + TEST_FUNCTION_2ARG_PREOPEN(dbp, setter, getter, func1, func2); \ + TEST_DB_OPEN(dbtype); \ + TEST_FUNCTION_2ARG_POSTOPEN(dbp, getter, func1, func2); \ +} while(0) + +/* + * Test DB's functions setting three callbacks. + */ +#define TEST_DB_FUNCTIONS_3ARG(setter, getter, dbtype, type1, func1, \ + type2, func2, type3, func3) do { \ + DB *dbp; \ + char buf[DB_MAXPATHLEN]; \ + TEST_FUNCTION_3ARG_HEAD(type1, type2, type3); \ + CuAssert(ct, "db_create", db_create(&dbp, NULL, 0) == 0); \ + info.dbp = dbp; \ + TEST_FUNCTION_3ARG_PREOPEN(dbp, setter, getter, func1, func2, \ + func3); \ + TEST_DB_OPEN(dbtype); \ + TEST_FUNCTION_3ARG_POSTOPEN(dbp, getter, func1, func2, func3); \ +} while(0) + + +struct handlers { + DB_ENV *dbenvp; + DB *dbp; +}; +static struct handlers info; +static u_int32_t nparts = 5; + +int TestCallbackSetterAndGetterSuiteSetup(CuSuite *suite) { + return (0); +} + +int TestCallbackSetterAndGetterSuiteTeardown(CuSuite *suite) { + return (0); +} + +int TestCallbackSetterAndGetterTestSetup(CuTest *ct) { + setup_envdir(TEST_ENV, 1); + info.dbenvp = NULL; + info.dbp = NULL; + return (0); +} + +int TestCallbackSetterAndGetterTestTeardown(CuTest *ct) { + if (info.dbp != NULL) + CuAssert(ct, "dbp->close", + info.dbp->close(info.dbp, 0) == 0); + if (info.dbenvp != NULL) + CuAssert(ct, "dbenvp->close", + info.dbenvp->close(info.dbenvp, 0) == 0); + return (0); +} + + +int TestEnvCallbacks(CuTest *ct) { + + TEST_ENV_FUNCTIONS_3ARG(set_alloc, get_alloc, app_malloc_fcn, + t_malloc, app_realloc_fcn, t_realloc, app_free_fcn, t_free); + TEST_ENV_FUNCTIONS_1ARG(set_app_dispatch, get_app_dispatch, + tx_recover_fcn, t_app_dispatch); + TEST_ENV_FUNCTIONS_3ARG(set_backup_callbacks, get_backup_callbacks, + open_func, t_open_func, write_func, t_write_func, close_func, + t_close_func); + TEST_ENV_FUNCTIONS_1ARG_VOID(set_errcall, get_errcall, + db_errcall_fcn, t_errcall); + TEST_ENV_FUNCTIONS_1ARG(set_feedback, get_feedback, + dbenv_feedback_fcn, t_dbenv_callback); + + /* + * The DB_ENV->set_is_alive requires the thread area be created, + * so we call DB_ENV->set_thread_count to enable the creation + * during environment open. + */ + { + DB_ENV *dbenvp; + TEST_FUNCTION_1ARG_HEAD(is_alive_fcn); + setup_envdir(TEST_ENV, 1); + CuAssert(ct, "db_env_create", db_env_create(&dbenvp, 0) == 0); + info.dbenvp = dbenvp; + TEST_FUNCTION_1ARG_PREOPEN(dbenvp, set_isalive, get_isalive, + t_is_alive); + CuAssert(ct, "dbenvp->set_thread_count", + dbenvp->set_thread_count(dbenvp, 50) == 0); + CuAssert(ct, "dbenvp->open", dbenvp->open(dbenvp, TEST_ENV, + DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | + DB_INIT_LOG | DB_INIT_TXN, 0644) == 0); + TEST_FUNCTION_1ARG_POSTOPEN(dbenvp, get_isalive, t_is_alive); + setup_envdir(TEST_ENV, 1); + } + + TEST_ENV_FUNCTIONS_1ARG_VOID(set_msgcall, get_msgcall, + db_msgcall_fcn, t_msgcall); + TEST_ENV_FUNCTIONS_1ARG(set_thread_id, get_thread_id_fn, + thread_id_fcn, t_thread_id); + TEST_ENV_FUNCTIONS_1ARG(set_thread_id_string, + get_thread_id_string_fn, thread_id_string_fcn, t_thread_id_string); + + return (0); +} + +int TestDbCallbacks(CuTest *ct) { + int indx; + + indx = 0; + TEST_DB_FUNCTIONS_3ARG(set_alloc, get_alloc, DB_BTREE, app_malloc_fcn, + t_malloc, app_realloc_fcn, t_realloc, app_free_fcn, t_free); + TEST_DB_FUNCTIONS_1ARG(set_dup_compare, get_dup_compare, DB_BTREE, + dup_compare_fcn, t_dup_compare); + TEST_DB_FUNCTIONS_1ARG_VOID(set_errcall, get_errcall, DB_BTREE, + db_errcall_fcn, t_errcall); + TEST_DB_FUNCTIONS_1ARG(set_feedback, get_feedback, DB_BTREE, + db_feedback_fcn, t_db_feedback); + TEST_DB_FUNCTIONS_1ARG_VOID(set_msgcall, get_msgcall, DB_BTREE, + db_msgcall_fcn, t_msgcall); + + /* + * Test DB->set_partition and DB->get_partition_callbacks. + * Like others, we do setting before DB->open, and do + * getting before and after DB->open. + */ + { + DB *dbp; + char buf[DB_MAXPATHLEN]; + u_int32_t nparts_rt; + db_partition_fcn func_rt; + + nparts_rt = 0; + func_rt = NULL; + CuAssert(ct, "db_create", db_create(&dbp, NULL, 0) == 0); + info.dbp = dbp; + CuAssert(ct, "dbp->set_partition", dbp->set_partition(dbp, + nparts, NULL, t_db_partition) == 0); + CuAssert(ct, "dbp->get_partition_callbacks", + dbp->get_partition_callback(dbp, + &nparts_rt, &func_rt) == 0); + CuAssert(ct, "check nparts", nparts_rt == nparts); + CuAssert(ct, "check partition callback", + func_rt == t_db_partition); + sprintf(buf, "%s/%d.db", TEST_ENV, indx++); + CuAssert(ct, "dbp->open", dbp->open(dbp, NULL, buf, NULL, + DB_BTREE, DB_CREATE, 0644) == 0); + CuAssert(ct, "dbp->get_partition_callbacks", + dbp->get_partition_callback(dbp, + &nparts_rt, &func_rt) == 0); + CuAssert(ct, "check nparts", nparts_rt == nparts); + CuAssert(ct, "check partition callback", + func_rt == t_db_partition); + info.dbp = NULL; + CuAssert(ct, "dbp->close", dbp->close(dbp, 0) == 0); + } + + TEST_DB_FUNCTIONS_1ARG(set_append_recno, get_append_recno, DB_RECNO, + db_append_recno_fcn, t_append_recno); + TEST_DB_FUNCTIONS_1ARG(set_bt_compare, get_bt_compare, DB_BTREE, + bt_compare_fcn, t_bt_compare); + TEST_DB_FUNCTIONS_2ARG(set_bt_compress, get_bt_compress, DB_BTREE, + bt_compress_fcn, t_compress, bt_decompress_fcn, t_decompress); + + /* + * DB->set_bt_prefix requires DB do not use the default comparision + * function, so we call DB->set_bt_compare to set the comparision + * callback first. + */ + { + DB *dbp; + char buf[DB_MAXPATHLEN]; + TEST_FUNCTION_1ARG_HEAD(bt_prefix_fcn); + CuAssert(ct, "db_create", db_create(&dbp, NULL, 0) == 0); + info.dbp = dbp; + TEST_FUNCTION_1ARG_PREOPEN(dbp, set_bt_prefix, get_bt_prefix, + t_bt_prefix); + CuAssert(ct, "dbp->set_bt_compare", + dbp->set_bt_compare(dbp, t_bt_compare) == 0); + TEST_DB_OPEN(DB_BTREE); + TEST_FUNCTION_1ARG_POSTOPEN(dbp, get_bt_prefix, t_bt_prefix); + } + + TEST_DB_FUNCTIONS_1ARG(set_h_compare, get_h_compare, DB_HASH, + h_compare_fcn, t_h_compare); + TEST_DB_FUNCTIONS_1ARG(set_h_hash, get_h_hash, DB_HASH, + h_hash_fcn, t_h_hash); + + return (0); +} + +static void *t_malloc(size_t sz) { + void *p; + int ret; + + if ((ret = __os_malloc(NULL, sz, &p)) != 0) + p = NULL; + return p; +} + +static void *t_realloc(void *addr, size_t sz) { + void *p; + int ret; + + p = addr; + if ((ret = __os_realloc(NULL, sz, &p)) != 0) + p = NULL; + return p; +} + +static void t_free(void *addr) { + __os_free(NULL, addr); +} + +static int t_app_dispatch(DB_ENV *dbenv, + DBT *log_rec, DB_LSN *lsn, db_recops op) { + return 0; +} + +static int t_open_func(DB_ENV *dbenv, const char *dbname, + const char *target, void **handle) { + return 0; +} + +static int t_write_func(DB_ENV *dbenv, u_int32_t offset_gbytes, + u_int32_t offset_bytes, u_int32_t size, u_int8_t *buf, void *handle) { + return 0; +} + +static int t_close_func(DB_ENV *dbenv, const char *dbname, void *handle) { + return 0; +} + +static void t_errcall(const DB_ENV *dbenv, + const char *errpfx, const char *msg) { + return; +} + +static void t_dbenv_callback(DB_ENV *dbenv, int opcode, int percent) { + return; +} + +static int t_is_alive(DB_ENV *dbenv, + pid_t pid, db_threadid_t tid, u_int32_t flags) { + return 1; +} + +static void t_msgcall(const DB_ENV *dbenv, const char *msg) { + return; +} + +static void t_thread_id(DB_ENV *dbenv, pid_t *pid, db_threadid_t *tid) { + __os_id(dbenv, pid, tid); +} + +static char *t_thread_id_string(DB_ENV *dbenv, + pid_t pid, db_threadid_t tid, char *buf) { + buf[0] = '\0'; + return buf; +} + +static int t_dup_compare(DB *db, + const DBT *dbt1, const DBT *dbt2, size_t *locp) { + return t_bt_compare(db, dbt1, dbt2, locp); +} + +static void t_db_feedback(DB *dbp, int opcode, int percent) { + return; +} + +static u_int32_t t_db_partition(DB *db, DBT *key) { + return (key->size % nparts); +} + +static int t_append_recno(DB *dbp, DBT *data, db_recno_t recno) { + size_t sz; + sz = sizeof(recno) > data->size ? data->size : sizeof(recno); + memcpy(data->data, &recno, sz); + return 0; +} + +static int t_bt_compare(DB *db, + const DBT *dbt1, const DBT *dbt2, size_t *locp) { + u_int32_t len; + int ret; + + locp = NULL; + len = dbt1->size > dbt2->size ? dbt2->size : dbt1->size; + if ((ret = memcmp(dbt1->data, dbt2->data, (size_t)len)) == 0) { + if (dbt1->size != dbt2->size) + ret = dbt1->size > dbt2->size ? 1 : -1; + } + return ret; +} + +static int t_compress(DB *db, const DBT *prevKey, const DBT *prevData, + const DBT *key, const DBT *data, DBT *dest) { + return 0; +} + +static int t_decompress(DB *db, const DBT *prevKey,const DBT *prevData, + DBT *compressed, DBT *destKey, DBT *destData) { + return 0; +} + +static size_t t_bt_prefix(DB *db, const DBT *dbt1, const DBT *dbt2) { + u_int32_t len; + + len = dbt1->size > dbt2->size ? dbt2->size : dbt1->size; + if (dbt1->size != dbt2->size) + len++; + return (size_t)len; +} + +static int t_h_compare(DB *db, + const DBT *dbt1, const DBT *dbt2, size_t *locp) { + return t_bt_compare(db, dbt1, dbt2, locp); +} + +static u_int32_t t_h_hash(DB *dbp, const void *bytes, u_int32_t length) { + return length; +} + diff --git a/test/c/suites/TestChannel.c b/test/c/suites/TestChannel.c index dfbf4e8d..03cebd91 100644 --- a/test/c/suites/TestChannel.c +++ b/test/c/suites/TestChannel.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2011, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved. */ #include <ctype.h> @@ -107,7 +107,6 @@ static void msg_disp2 __P((DB_ENV *, DB_CHANNEL *, DBT *, u_int32_t, u_int32_t)) static void msg_disp3 __P((DB_ENV *, DB_CHANNEL *, DBT *, u_int32_t, u_int32_t)); static void msg_disp4 __P((DB_ENV *, DB_CHANNEL *, DBT *, u_int32_t, u_int32_t)); static void msg_disp5 __P((DB_ENV *, DB_CHANNEL *, DBT *, u_int32_t, u_int32_t)); -static int mystrcmp __P((char *, const char *)); static void notify __P((DB_ENV *, u_int32_t, void *)); static int is_started __P((void *)); static void td __P((DB_ENV *)); @@ -170,9 +169,12 @@ int TestChannelTestTeardown(CuTest *test) { static void myerrcall(const DB_ENV *dbenv, const char *errpfx, const char *msg) { struct report *rpt = get_rpt(dbenv); + char *msgp; assert(rpt->msg_count < MAX_MSGS); - assert((rpt->msg[rpt->msg_count++] = strdup(msg)) != NULL); + msgp = strdup(msg); + assert(msgp != NULL); + rpt->msg[rpt->msg_count++] = msgp; } static int @@ -449,11 +451,10 @@ int TestChannelFeature(CuTest *ct) { /* Wait til dbenv2 has reported 1 msg. */ info.dbenv = dbenv2; info.count = 1; - await_condition(has_msgs, &info, 60); + await_condition(has_msgs, &info, 90); rpt = get_rpt(dbenv2); CuAssertTrue(ct, rpt->msg_count == 1); - CuAssertTrue(ct, mystrcmp(rpt->msg[0], - "No message dispatch call-back function has been configured") == 0); + CuAssertTrue(ct, strncmp(rpt->msg[0], "BDB3670", strlen("BDB3670")) == 0); printf("2. send request with no msg dispatch in place\n"); clear_rpt(dbenv2); @@ -461,10 +462,9 @@ int TestChannelFeature(CuTest *ct) { CuAssertTrue(ct, ret == DB_NOSERVER); if (resp.data != NULL) free(resp.data); - await_condition(has_msgs, &info, 60); + await_condition(has_msgs, &info, 90); CuAssertTrue(ct, rpt->msg_count == 1); - CuAssertTrue(ct, mystrcmp(rpt->msg[0], - "No message dispatch call-back function has been configured") == 0); + CuAssertTrue(ct, strncmp(rpt->msg[0], "BDB3670", strlen("BDB3670")) == 0); CuAssertTrue(ct, (ret = dbenv2->repmgr_msg_dispatch(dbenv2, msg_disp, 0)) == 0); @@ -476,8 +476,7 @@ int TestChannelFeature(CuTest *ct) { free(resp.data); await_done(dbenv2); CuAssertTrue(ct, rpt->msg_count == 1); - CuAssertTrue(ct, mystrcmp(rpt->msg[0], - "Application failed to provide a response") == 0); + CuAssertTrue(ct, strncmp(rpt->msg[0], "BDB3671", strlen("BDB3671")) == 0); printf("4. now with dispatch fn installed, send a simple async msg\n"); clear_rpt(dbenv2); @@ -519,8 +518,7 @@ int TestChannelFeature(CuTest *ct) { CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 3, &resp, 0, 0)) == DB_BUFFER_SMALL); await_done(dbenv2); CuAssertTrue(ct, rpt->msg_count == 1); - CuAssertTrue(ct, mystrcmp(rpt->msg[0], - "originator's USERMEM buffer too small") == 0); + CuAssertTrue(ct, strncmp(rpt->msg[0], "BDB3659", strlen("BDB3659")) == 0); CuAssertTrue(ct, rpt->ret == EINVAL); #define BUFLEN 20000 @@ -536,8 +534,7 @@ int TestChannelFeature(CuTest *ct) { CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 2, &resp, 0, 0)) == DB_BUFFER_SMALL); await_done(dbenv2); CuAssertTrue(ct, rpt->msg_count == 1); - CuAssertTrue(ct, mystrcmp(rpt->msg[0], - "originator does not accept multi-segment response") == 0); + CuAssertTrue(ct, strncmp(rpt->msg[0], "BDB3658", strlen("BDB3658")) == 0); CuAssertTrue(ct, rpt->ret == EINVAL); printf("9. send USERMEM request with DB_MULTIPLE\n"); @@ -776,12 +773,9 @@ int TestChannelFeature(CuTest *ct) { rpt = get_rpt(dbenv3); CuAssertTrue(ct, rpt->ret == EINVAL); CuAssertTrue(ct, rpt->msg_count == 3); - CuAssertTrue(ct, mystrcmp(rpt->msg[0], - "set_timeout() invalid on DB_CHANNEL supplied to msg dispatch function") == 0); - CuAssertTrue(ct, mystrcmp(rpt->msg[1], - "close() invalid on DB_CHANNEL supplied to msg dispatch function") == 0); - CuAssertTrue(ct, mystrcmp(rpt->msg[2], -"send_request() invalid on DB_CHANNEL supplied to msg dispatch function") == 0); + CuAssertTrue(ct, strncmp(rpt->msg[0], "BDB3660", strlen("BDB3660")) == 0); + CuAssertTrue(ct, strncmp(rpt->msg[1], "BDB3660", strlen("BDB3660")) == 0); + CuAssertTrue(ct, strncmp(rpt->msg[2], "BDB3660", strlen("BDB3660")) == 0); ch->close(ch, 0); free(buffer); @@ -1263,23 +1257,6 @@ test_zeroes(ch, dest, ct) free(resp.data); } -/* - * Compare, but skip over BDB error msg number at beginning of `actual'. - */ -static int -mystrcmp(actual, expected) - char *actual; - const char *expected; -{ - char *p; - - for (p = actual; *p != '\0' && !isspace(*p); p++) - ; - for (; *p != '\0' && isspace(*p); p++) - ; - return (strcmp(p, expected)); -} - static int get_avail_ports(ports, count) u_int *ports; int count; @@ -1334,8 +1311,8 @@ static int get_avail_ports(ports, count) i = incr; while (i-- > 0) { - if (ret = __repmgr_getaddr(NULL, "localhost", curport, - AI_PASSIVE, &orig_ai) != 0) + if ((ret = __repmgr_getaddr(NULL, "localhost", curport, + AI_PASSIVE, &orig_ai)) != 0) goto end; for (ai = orig_ai; ai != NULL; ai = ai->ai_next) { diff --git a/test/c/suites/TestDbHotBackup.c b/test/c/suites/TestDbHotBackup.c index b330e734..753b182c 100644 --- a/test/c/suites/TestDbHotBackup.c +++ b/test/c/suites/TestDbHotBackup.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2011, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ @@ -9,12 +9,24 @@ /* * A C Unit test for db_hotbackup APIs [#20451] * - * Different testing environments: - * without any configuration, - * have partitioned databases, - * have multiple add_data_dir configured, - * have set_lg_dir configured, - * with queue extent files. + * Test casess: + * 1. btree database without any environment/database configured, backup only + * the database file without callbacks; + * 2. btree database without any environment/database configured, backup only + * the database file with callbacks; + * 3. btree database without any environment/database configured, backup only + * the database file with backup configurations; + * 4. btree partitioned database, backup the whole environment into a single + * directory; + * 5. btree database with multiple add_data_dir configured, backup the whole + * environment including DB_CONFIG and maintain its directory structure in + * the backup directory; + * 6. btree database with set_lg_dir configured, backup the whole environment + * with callbacks including DB_CONFIG and maintain its directory structure in + * the backup directory; + * 7. queue database having multiple queue extent files, backup only the + * database file without callbacks; + * 8. heap database, backup only the database file without callbacks. * */ @@ -27,38 +39,35 @@ #include "CuTest.h" #include "test_util.h" +/* microseconds in a second */ +#define US_PER_SEC 1000000 + struct handlers { DB_ENV *dbenvp; DB *dbp; }; -typedef enum { - SIMPLE_ENV = 1, - PARTITION_DB = 2, - MULTI_DATA_DIR = 3, - SET_LOG_DIR = 4, - QUEUE_DB = 5 -} ENV_CONF_T; - -static int setup_test(ENV_CONF_T); -static int open_dbp(DB_ENV **, DB **, ENV_CONF_T); -static int store_records(DB *, ENV_CONF_T); -static int cleanup_test(DB_ENV *, DB *); +static int backup_close(DB_ENV *, const char *, void *); static int backup_db(CuTest *, DB_ENV *, const char *, u_int32_t, int); static int backup_env(CuTest *, DB_ENV *, u_int32_t, int); -static int make_dbconfig(ENV_CONF_T); -static int verify_db(ENV_CONF_T); -static int verify_log(ENV_CONF_T); -static int verify_dbconfig(ENV_CONF_T); +static int backup_open(DB_ENV *, const char *, const char *, void **); +static int backup_write(DB_ENV *, u_int32_t, + u_int32_t, u_int32_t, u_int8_t *, void *); +static int cleanup_test(DB_ENV *, DB *); static int cmp_files(const char *, const char *); -int backup_open(DB_ENV *, const char *, const char *, void **); -int backup_write(DB_ENV *, u_int32_t, u_int32_t, u_int32_t, u_int8_t *, void *); -int backup_close(DB_ENV *, const char *, void *); +static int make_dbconfig(const char *); +static int open_dbp(DB_ENV **, DB **, DBTYPE, + u_int32_t, char **, const char *, const char *, u_int32_t, DBT *); +static int setup_dir(u_int32_t, char **); +static int store_records(DB *, u_int32_t); +static int test_backup_onlydbfile(CuTest *, DBTYPE, int); +static int verify_db_log(DBTYPE, u_int32_t, + u_int32_t, const char *, const char *); +static int verify_dbconfig(u_int32_t); #define BACKUP_DIR "BACKUP" #define BACKUP_DB "backup.db" #define LOG_DIR "LOG" -#define NPARTS 3 char *data_dirs[3] = {"DATA1", "DATA2", NULL}; @@ -98,282 +107,394 @@ int TestDbHotBackupTestTeardown(CuTest *ct) { return (0); } -int TestDbHotBackupSimpleEnv(CuTest *ct) { +int TestBackupSimpleEnvNoCallback(CuTest *ct) { + CuAssertTrue(ct, test_backup_onlydbfile(ct, DB_BTREE, 0) == 0); + + return (0); +} + +int TestBackupSimpleEnvWithCallback(CuTest *ct) { + CuAssertTrue(ct, test_backup_onlydbfile(ct, DB_BTREE, 1) == 0); + + return (0); +} + +int TestBackupSimpleEnvWithConfig(CuTest *ct) { DB_ENV *dbenv; DB *dbp; - ENV_CONF_T envconf; + DBTYPE dtype; struct handlers *info; char **names; int cnt, has_callback; - u_int32_t flag; + time_t end_time, secs1, secs2, start_time; + u_int32_t flag, value; - envconf = SIMPLE_ENV; + dtype = DB_BTREE; info = ct->context; has_callback = 0; flag = DB_EXCL; + end_time = secs1 = secs2 = start_time = 0; - /* Step 1: set up test by making relative directories. */ - CuAssert(ct, "setup_test", setup_test(envconf) == 0); + /* Step 1: set up directories. */ + CuAssert(ct, "setup_dir", setup_dir(0, NULL) == 0); /* Step 2: open db handle. */ - CuAssert(ct,"open_dbp", open_dbp(&dbenv, &dbp, envconf) == 0); + CuAssert(ct, "open_dbp", open_dbp(&dbenv, + &dbp, dtype, 0, NULL, NULL, NULL, 0, NULL) == 0); info->dbenvp = dbenv; info->dbp = dbp; - /* Step 3: store records into db. */ - CuAssert(ct,"store_records", store_records(dbp, envconf) == 0); + /* + * Step 3: store records into db so that there is more than + * 1 data page in the db. + */ + CuAssert(ct, "store_records", store_records(dbp, 10) == 0); CuAssert(ct, "DB->sync", dbp->sync(dbp, 0) == 0); - /* Step 4: backup only the db file without callbacks. */ - CuAssert(ct, "backup_env", + /* + * Step 4: verify the backup handle is NULL, + * since we never configure the backup. + */ + CuAssert(ct, "DB_ENV->get_backup_config", + dbenv->get_backup_config(dbenv, + DB_BACKUP_WRITE_DIRECT, &value) == EINVAL); + + /* + * Step 5: backup without any backup configs. + * 5a: backup only the db file without callbacks and record the time. + */ + start_time = time(NULL); + CuAssert(ct, "backup_db", + backup_db(ct, dbenv, BACKUP_DB, flag, has_callback) == 0); + end_time = time(NULL); + secs1 = end_time - start_time; + + /* 5b: verify db file is in BACKUP_DIR. */ + CuAssert(ct, "verify_db_log", + verify_db_log(dtype, 0, 0, NULL, NULL) == 0); + + /* 5c: verify that no other files are in BACKUP_DIR. */ + CuAssert(ct, "__os_dirlist", + __os_dirlist(NULL, BACKUP_DIR, 0, &names, &cnt) == 0); + CuAssert(ct, "too many files in backupdir", cnt == 1); + + /* Clean up the backup directory. */ + setup_envdir(BACKUP_DIR, 1); + + /* + * Step 6: backup with backup configs. + * 6a: configure the backup handle: use direct I/O to write pages to + * the disk, the backup buffer size is 256 bytes (which is smaller + * than the db page size), the number of pages + * to read before pausing is 1, and the number of seconds to sleep + * between batches of reads is 1. + */ + CuAssert(ct, "DB_ENV->set_backup_config", + dbenv->set_backup_config(dbenv, DB_BACKUP_WRITE_DIRECT, 1) == 0); + CuAssert(ct, "DB_ENV->set_backup_config", + dbenv->set_backup_config(dbenv, DB_BACKUP_SIZE, 256) == 0); + CuAssert(ct, "DB_ENV->set_backup_config", + dbenv->set_backup_config(dbenv, DB_BACKUP_READ_COUNT, 1) == 0); + CuAssert(ct, "DB_ENV->set_backup_config", + dbenv->set_backup_config(dbenv, + DB_BACKUP_READ_SLEEP, US_PER_SEC / 2) == 0); + + /* + * 6b: backup only the db file without callbacks and + * record the time. + */ + start_time = time(NULL); + CuAssert(ct, "backup_db", backup_db(ct, dbenv, BACKUP_DB, flag, has_callback) == 0); + end_time = time(NULL); + secs2 = end_time - start_time; - /* Step 5: check backup result. */ - /* 5a: dump the db and verify the content is same. */ - CuAssert(ct, "verify_db", verify_db(envconf) == 0); + /* 6c: verify db file is in BACKUP_DIR. */ + CuAssert(ct, "verify_db_log", + verify_db_log(dtype, 0, 0, NULL, NULL) == 0); - /* 5b: no other files are in backupdir. */ + /* 6d: no other files are in BACKUP_DIR. */ CuAssert(ct, "__os_dirlist", __os_dirlist(NULL, BACKUP_DIR, 0, &names, &cnt) == 0); CuAssert(ct, "too many files in backupdir", cnt == 1); + /* 6e: verify the backup config. */ + CuAssert(ct, "DB_ENV->get_backup_config", + dbenv->get_backup_config(dbenv, + DB_BACKUP_READ_SLEEP, &value) == 0); + CuAssertTrue(ct, value == US_PER_SEC / 2); + /* + * Verify the backup config DB_BACKUP_READ_SLEEP works. That is with + * the configuration, backup pauses for a number of microseconds + * between batches of reads. So for the same backup content, the backup + * time with the configuration should be longer than that without it. + */ + CuAssertTrue(ct, secs2 > secs1); + + CuAssert(ct, "DB_ENV->get_backup_config", + dbenv->get_backup_config(dbenv, + DB_BACKUP_READ_COUNT, &value) == 0); + CuAssertTrue(ct, value == 1); + CuAssert(ct, "DB_ENV->get_backup_config", + dbenv->get_backup_config(dbenv, DB_BACKUP_SIZE, &value) == 0); + CuAssertTrue(ct, value == 256); + CuAssert(ct, "DB_ENV->get_backup_config", + dbenv->get_backup_config(dbenv, + DB_BACKUP_WRITE_DIRECT, &value) == 0); + CuAssertTrue(ct, value == 1); + + /* + * Step 7: re-configure the backup write direct config and + * verify the new config value. + */ + CuAssert(ct, "DB_ENV->set_backup_config", + dbenv->set_backup_config(dbenv, DB_BACKUP_WRITE_DIRECT, 0) == 0); + CuAssert(ct, "DB_ENV->get_backup_config", + dbenv->get_backup_config(dbenv, + DB_BACKUP_WRITE_DIRECT, &value) == 0); + CuAssertTrue(ct, value == 0); + return (0); } -int TestDbHotBackupPartitionDB(CuTest *ct) { +int TestBackupPartitionDB(CuTest *ct) { DB_ENV *dbenv; DB *dbp; - ENV_CONF_T envconf; + DBT key1, key2, keys[2]; + DBTYPE dtype; struct handlers *info; int has_callback; - u_int32_t flag; + u_int32_t flag, value1, value2; - envconf = PARTITION_DB; + dtype = DB_BTREE; info = ct->context; has_callback = 0; flag = DB_BACKUP_CLEAN | DB_CREATE | DB_BACKUP_SINGLE_DIR; - /* Step 1: set up test by making relative directories. */ - CuAssert(ct, "setup_test", setup_test(envconf) == 0); + /* Step 1: set up directories and make DB_CONFIG. */ + CuAssert(ct, "setup_dir", setup_dir(1, data_dirs) == 0); + CuAssert(ct, "make_dbconfig", + make_dbconfig("set_data_dir DATA1") == 0); + + /* Make the partition keys. */ + memset(&key1, 0, sizeof(DBT)); + memset(&key2, 0, sizeof(DBT)); + value1 = 8; + key1.data = &value1; + key1.size = sizeof(value1); + value2 = 16; + key2.data = &value2; + key2.size = sizeof(value2); + keys[0] = key1; + keys[1] = key2; /* Step 2: open db handle. */ - CuAssert(ct,"open_dbp", open_dbp(&dbenv, &dbp, envconf) == 0); + CuAssert(ct,"open_dbp", open_dbp(&dbenv, + &dbp, dtype, 1, data_dirs, data_dirs[0], NULL, 3, keys) == 0); info->dbenvp = dbenv; info->dbp = dbp; /* Step 3: store records into db. */ - CuAssert(ct,"store_records", store_records(dbp, envconf) == 0); + CuAssert(ct, "store_records", store_records(dbp, 1) == 0); CuAssert(ct, "DB->sync", dbp->sync(dbp, 0) == 0); /* Step 4: backup the whole environment into a single directory. */ CuAssert(ct, "backup_env", backup_env(ct, dbenv, flag, has_callback) == 0); - /* Step 5: check backup result. */ - /* 5a: dump the db and verify the content is same. */ - CuAssert(ct, "verify_db", verify_db(envconf) == 0); + /* + * Step 5: check backup result. + * 5a: verify db files are in BACKUP/DATA1. + */ + CuAssert(ct, "verify_db_log", + verify_db_log(dtype, 1, 0, data_dirs[0], NULL) == 0); - /* 5b: verify that creation directory is not in backupdir. */ - CuAssert(ct, "__os_exist", __os_exists(NULL, "BACKUP/DATA1", 0) != 0); + /* 5b: verify that creation directory is not in BACKUPD_DIR. */ + CuAssert(ct, "__os_exist", __os_exists(NULL, "BACKUP/DATA", 0) != 0); - /* 5c: verify that log files are in backupdir. */ - CuAssert(ct, "verify_log", verify_log(envconf) == 0); + /* 5c: verify log files are in BACKUP_DIR. */ + CuAssert(ct, "verify_db_log", + verify_db_log(dtype, 0, 1, NULL, NULL) == 0); - /* 5d: verify that DB_CONFIG is not in backupdir*/ - CuAssert(ct, "verify_dbconfig", verify_dbconfig(envconf) == 0); + /* 5d: verify that DB_CONFIG is not in BACKUP_DIR. */ + CuAssert(ct, "verify_dbconfig", verify_dbconfig(0) == 0); return (0); } -int TestDbHotBackupMultiDataDir(CuTest *ct) { +int TestBackupMultiDataDir(CuTest *ct) { DB_ENV *dbenv; DB *dbp; - ENV_CONF_T envconf; + DBTYPE dtype; struct handlers *info; int has_callback; u_int32_t flag; - envconf = MULTI_DATA_DIR; + dtype = DB_BTREE; info = ct->context; has_callback = 0; flag = DB_BACKUP_CLEAN | DB_CREATE | DB_BACKUP_FILES; - /* Step 1: set up test by making relative directories. */ - CuAssert(ct, "setup_test", setup_test(envconf) == 0); + /* Step 1: set up directories and make DB_CONFIG. */ + CuAssert(ct, "setup_dir", setup_dir(2, data_dirs) == 0); + CuAssert(ct, "make_dbconfig", + make_dbconfig("set_data_dir DATA1") == 0); /* Step 2: open db handle. */ - CuAssert(ct,"open_dbp", open_dbp(&dbenv, &dbp, envconf) == 0); + CuAssert(ct,"open_dbp", open_dbp(&dbenv, &dbp, + dtype, 2, data_dirs, data_dirs[0], NULL, 0, NULL) == 0); info->dbenvp = dbenv; info->dbp = dbp; /* Step 3: store records into db. */ - CuAssert(ct,"store_records", store_records(dbp, envconf) == 0); + CuAssert(ct, "store_records", store_records(dbp, 1) == 0); CuAssert(ct, "DB->sync", dbp->sync(dbp, 0) == 0); /* Step 4: backup the whole environment without callbacks. */ CuAssert(ct, "backup_env", backup_env(ct, dbenv, flag, has_callback) == 0); - /* Step 5: check backup result. */ - /* 5a: dump the db and verify the content is same. */ - CuAssert(ct, "verify_db", verify_db(envconf) == 0); + /* + * Step 5: check backup result. + * 5a: verify db files are in BACKUP/DATA1. + */ + CuAssert(ct, "verify_db_log", + verify_db_log(dtype, 0, 0, data_dirs[0], data_dirs[0]) == 0); /* 5b: verify that data_dirs are in backupdir. */ CuAssert(ct, "__os_exist", __os_exists(NULL, "BACKUP/DATA1", 0) == 0); CuAssert(ct, "__os_exist", __os_exists(NULL, "BACKUP/DATA2", 0) == 0); - /* 5c: verify that log files are in backupdir. */ - CuAssert(ct, "verify_log", verify_log(envconf) == 0); + /* 5c: verify that log files are in BACKUP_DIR. */ + CuAssert(ct, "verify_db_log", + verify_db_log(dtype, 0, 1, NULL, NULL) == 0); - /* 5d: verify that DB_CONFIG is in backupdir. */ - CuAssert(ct, "verify_dbconfig", verify_dbconfig(envconf) == 0); + /* 5d: verify that DB_CONFIG is in BACKUP_DIR. */ + CuAssert(ct, "verify_dbconfig", verify_dbconfig(1) == 0); return (0); } -int TestDbHotBackupSetLogDir(CuTest *ct) { +int TestBackupSetLogDir(CuTest *ct) { DB_ENV *dbenv; DB *dbp; - ENV_CONF_T envconf; + DBTYPE dtype; struct handlers *info; + char *dirs[2]; int has_callback = 1; u_int32_t flag; - envconf = SET_LOG_DIR; + dtype = DB_BTREE; info = ct->context; has_callback = 1; flag = DB_BACKUP_CLEAN | DB_CREATE | DB_BACKUP_FILES; + dirs[0] = LOG_DIR; + dirs[1] = NULL; - /* Step 1: set up test by making relative directories. */ - CuAssert(ct, "setup_test", setup_test(envconf) == 0); + /* Step 1: set up directories and make DB_CONFIG. */ + CuAssert(ct, "setup_dir", setup_dir(1, dirs) == 0); + CuAssert(ct, "make_dbconfig", make_dbconfig("set_lg_dir LOG") == 0); /* Step 2: open db handle. */ - CuAssert(ct,"open_dbp", open_dbp(&dbenv, &dbp, envconf) == 0); + CuAssert(ct,"open_dbp", open_dbp(&dbenv, &dbp, + dtype, 0, NULL, NULL, LOG_DIR, 0, NULL) == 0); info->dbenvp = dbenv; info->dbp = dbp; /* Step 3: store records into db. */ - CuAssert(ct,"store_records", store_records(dbp, envconf) == 0); + CuAssert(ct, "store_records", store_records(dbp, 1) == 0); CuAssert(ct, "DB->sync", dbp->sync(dbp, 0) == 0); - /* Step 4: backup a whole environment with callbacks. */ + /* Step 4: backup the whole environment with callbacks. */ CuAssert(ct, "backup_env", backup_env(ct, dbenv, flag, has_callback) == 0); - /* Step 5: check backup result. */ - /* 5a: dump the db and verify the content is same. */ - CuAssert(ct, "verify_db", verify_db(envconf) == 0); + /* + * Step 5: check backup result. + * 5a: verify the db file is in BACKUP_DIR. + */ + CuAssert(ct, "verify_db_log", + verify_db_log(dtype, 0, 0, NULL, NULL) == 0); - /* 5b: verify that log files are in backupdir/log_dir. */ - CuAssert(ct, "verify_log", verify_log(envconf) == 0); + /* 5b: verify that log files are in BACKUP/LOG. */ + CuAssert(ct, "verify_db_log", + verify_db_log(dtype, 0, 1, LOG_DIR, LOG_DIR) == 0); - /* 5c: verify that DB_CONFIG is in backupdir*/ - CuAssert(ct, "verify_dbconfig", verify_dbconfig(envconf) == 0); + /* 5c: verify that DB_CONFIG is in BACKUP_DIR. */ + CuAssert(ct, "verify_dbconfig", verify_dbconfig(1) == 0); return (0); } -int TestDbHotBackupQueueDB(CuTest *ct) { - DB_ENV *dbenv; - DB *dbp; - ENV_CONF_T envconf; - struct handlers *info; - int has_callback; - u_int32_t flag; - - envconf = QUEUE_DB; - info = ct->context; - has_callback = 0; - flag = DB_BACKUP_CLEAN | DB_CREATE; - - /* Step 1: set up test by making relative directories. */ - CuAssert(ct, "setup_test", setup_test(envconf) == 0); +int TestBackupQueueDB(CuTest *ct) { + CuAssertTrue(ct, test_backup_onlydbfile(ct, DB_QUEUE, 0) == 0); - /* Step 2: open db handle. */ - CuAssert(ct,"open_dbp", open_dbp(&dbenv, &dbp, envconf) == 0); - info->dbenvp = dbenv; - info->dbp = dbp; - - /* Step 3: store records into db. */ - CuAssert(ct,"store_records", store_records(dbp, envconf) == 0); - CuAssert(ct, "DB->sync", dbp->sync(dbp, 0) == 0); - - /* Step 4: backup the whole environment without callbacks. */ - CuAssert(ct, "backup_env", - backup_env(ct, dbenv, flag, has_callback) == 0); - - /* Step 5: check backup result. */ - /* 5a: dump the db and verify the content is same. */ - CuAssert(ct, "verify_db", verify_db(envconf) == 0); - - /* 5b: verify that log files are in backupdir. */ - CuAssert(ct, "verify_log", verify_log(envconf) == 0); + return (0); +} - /* 5c: vertify that DB_CONFIG is not in backupdir. */ - CuAssert(ct, "verify_dbconfig", verify_dbconfig(envconf) == 0); +int TestBackupHeapDB(CuTest *ct) { + CuAssertTrue(ct, test_backup_onlydbfile(ct, DB_HEAP, 0) == 0); return (0); } static int -setup_test(envconf) - ENV_CONF_T envconf; +setup_dir(len, dirs) + u_int32_t len; + char **dirs; { char path[1024]; - int i, ret; - - /* Make directories based on config. */ - switch (envconf) { - case SIMPLE_ENV: - break; - case PARTITION_DB: - snprintf(path, sizeof(path),"%s%c%s", - TEST_ENV, PATH_SEPARATOR[0], data_dirs[0]); - if ((ret = setup_envdir(path, 1)) != 0) - return (ret); - break; - case MULTI_DATA_DIR: - for (i = 0; i < 2; i++) { - snprintf(path, sizeof(path),"%s%c%s", - TEST_ENV, PATH_SEPARATOR[0], data_dirs[i]); + u_int32_t i; + int ret; + + /* Make related directories. */ + if (len > 0) { + for (i = 0; i < len; i++) { + ret = snprintf(path, sizeof(path),"%s%c%s", + TEST_ENV, PATH_SEPARATOR[0], dirs[i]); + if (ret <= 0 || ret >= sizeof(path)) { + ret = EINVAL; + return (ret); + } if ((ret = setup_envdir(path, 1)) != 0) return (ret); } - break; - case SET_LOG_DIR: - snprintf(path, sizeof(path),"%s%c%s", - TEST_ENV, PATH_SEPARATOR[0], LOG_DIR); - if ((ret = setup_envdir(path, 1)) != 0) - return (ret); - break; - case QUEUE_DB: - break; - default: - return (EINVAL); } - /* Make DB_CONFIG for PARTITION_DB, MULT_DATA_DIR and SET_LOG_DIR. */ - if(envconf >= 2 && envconf <= 4) - make_dbconfig(envconf); - return (0); } +/* + * open_dbp: + * DB_ENV **dbenvp. + * DB **dbpp. + * DBTYPE dtype: the database type to create. + * u_int32_t ddir_len: the number of data directories. + * char **data_dir: data directories to add. + * const char *create_dir: database creation diretory. + * const char *lg_dir: log directory. + * u_int32_t nparts: the number of partitions. + * DBT *part_key: the partition keys. + */ static int -open_dbp(dbenvp, dbpp, envconf) +open_dbp(dbenvp, dbpp, dtype, + ddir_len, data_dir, create_dir, lg_dir, nparts, part_key) DB_ENV **dbenvp; DB **dbpp; - ENV_CONF_T envconf; + DBTYPE dtype; + u_int32_t ddir_len, nparts; + char **data_dir; + const char *create_dir, *lg_dir; + DBT *part_key; { DB_ENV *dbenv; DB *dbp; - DBT key1, key2, keys[2]; - DBTYPE dtype; - int i, ret, value1, value2; + const char *part_dir[2]; + u_int32_t i; + int ret; dbenv = NULL; dbp = NULL; - dtype = DB_BTREE; ret = 0; if ((ret = db_env_create(&dbenv, 0)) != 0) { @@ -386,35 +507,23 @@ open_dbp(dbenvp, dbpp, envconf) dbenv->set_errfile(dbenv, stderr); dbenv->set_errpfx(dbenv, "TestDbHotBackup"); - /* Configure the environment. */ - switch (envconf) { - case SIMPLE_ENV: - case PARTITION_DB: - break; /* Add data directories. */ - case MULTI_DATA_DIR: - for (i = 0; i < 2; i++) { + if (ddir_len > 0 && data_dir != NULL) { + for (i = 0; i < ddir_len; i++) { if ((ret = dbenv->add_data_dir(dbenv, - data_dirs[i])) != 0) { + data_dir[i])) != 0) { fprintf(stderr, "DB_ENV->add_data_dir: %s\n", db_strerror(ret)); return (ret); } } - break; + } + /* Set log directory. */ - case SET_LOG_DIR: - if ((ret = dbenv->set_lg_dir(dbenv, LOG_DIR)) != 0) { - fprintf(stderr, "DB_ENV->set_lg_dir: %s\n", - db_strerror(ret)); - return (ret); - } - break; - case QUEUE_DB: - dtype = DB_QUEUE; - break; - default: - return (EINVAL); + if (lg_dir != NULL && (ret = dbenv->set_lg_dir(dbenv, lg_dir)) != 0) { + fprintf(stderr, "DB_ENV->set_lg_dir: %s\n", + db_strerror(ret)); + return (ret); } /* Open the environment. */ @@ -435,33 +544,32 @@ open_dbp(dbenvp, dbpp, envconf) dbp->set_errfile(dbp, stderr); dbp->set_errpfx(dbp, "TestDbHotBackup"); - /* Set db creation directory for PARTTION_DB and MULTI_DATA_DRI. */ - if (envconf == PARTITION_DB || envconf == MULTI_DATA_DIR) { - if ((ret = dbp->set_create_dir(dbp, data_dirs[0])) != 0) { - fprintf(stderr, "DB_ENV->add_data_dir: %s\n", - db_strerror(ret)); - return (ret); - } + /* Set database creation directory. */ + if (create_dir != NULL && + (ret = dbp->set_create_dir(dbp, create_dir)) != 0) { + fprintf(stderr, "DB_ENV->add_data_dir: %s\n", + db_strerror(ret)); + return (ret); } /* Set partition. */ - if (envconf == PARTITION_DB) { - value1 = 8; - key1.data = &value1; - key1.size = sizeof(value1); - value2 = 16; - key2.data = &value2; - key2.size = sizeof(value2); - keys[0] = key1; - keys[1] = key2; - if ((ret = dbp->set_partition(dbp, NPARTS, keys, NULL)) != 0) { + if (dtype == DB_BTREE && nparts > 0 && part_key != NULL) { + if ((ret = dbp->set_partition(dbp, + nparts, part_key, NULL)) != 0) { dbp->err(dbp, ret, "DB->set_partition"); return (ret); } + if (create_dir != NULL) { + part_dir[0]= create_dir; + part_dir[1] = NULL; + if ((ret = + dbp->set_partition_dirs(dbp, part_dir)) != 0) + return (ret); + } } /* Set queue record length and extent size. */ - if (envconf == QUEUE_DB) { + if (dtype == DB_QUEUE) { if ((ret = dbp->set_re_len(dbp, 50)) != 0) { dbp->err(dbp, ret, "DB->set_re_len"); return (ret); @@ -470,13 +578,18 @@ open_dbp(dbenvp, dbpp, envconf) dbp->err(dbp, ret, "DB->set_q_extentsize"); return (ret); } - } - /* Set flag for Btree. */ - else { + } else if (dtype == DB_BTREE) { + /* Set flag for Btree. */ if ((ret = dbp->set_flags(dbp, DB_DUPSORT)) != 0) { dbp->err(dbp, ret, "DB->set_flags"); return (ret); } + } else if (dtype == DB_HEAP) { + /* Set heap region size. */ + if ((ret = dbp->set_heap_regionsize(dbp, 1)) != 0) { + dbp->err(dbp, ret, "DB->set_heap_regionsize"); + return (ret); + } } if ((ret = dbp->set_pagesize(dbp, 512)) != 0) { @@ -494,19 +607,33 @@ open_dbp(dbenvp, dbpp, envconf) return (0); } +/* + * store_records: + * DB **dbpp. + * u_int32_t iter: put 26 * dups key/data pairs into the database. + */ static int -store_records(dbp, envconf) +store_records(dbp, dups) DB *dbp; - ENV_CONF_T envconf; + u_int32_t dups; { DBT key, data; - int i, ret; - size_t num; - u_int32_t flag; + DBTYPE dtype; + u_int32_t flag, i, j, num; + int ret; - char *buf = "abcdefghijefghijklmnopqrstuvwxyz"; - num = strlen(buf); - flag = envconf == QUEUE_DB ? DB_APPEND : 0; + char *buf = "abcdefghijklmnopqrstuvwxyz"; + num = (u_int32_t)strlen(buf); + flag = 0; + + /* Only accepts dups which is between 1 and 26 inclusively. */ + if (dups < 1 || dups > num) + return (EINVAL); + + if ((dbp->get_type(dbp, &dtype)) != 0) + return (EINVAL); + if (dtype == DB_HEAP || dtype == DB_QUEUE || dtype == DB_RECNO) + flag = DB_APPEND; memset(&key, 0, sizeof(DBT)); memset(&data, 0, sizeof(DBT)); @@ -515,12 +642,20 @@ store_records(dbp, envconf) key.data = &i; key.size = sizeof(i); - data.data = &buf[i]; - data.size = sizeof(char); - - if ((ret = dbp->put(dbp, NULL, &key, &data, flag)) != 0) { - dbp->err(dbp, ret, "DB->put"); - return (ret); + /* + * If "dups" > 1, we are putting duplicate records into the db + * and the number of records for each key is "dups". We already + * set DB_DUPSORT when opening the db. + */ + for (j = 1; j <= dups; j++) { + data.data = &buf[0]; + data.size = j * sizeof(char); + + if ((ret = dbp->put(dbp, + NULL, &key, &data, flag)) != 0) { + dbp->err(dbp, ret, "DB->put"); + return (ret); + } } } return (ret); @@ -550,11 +685,11 @@ cleanup_test(dbenv, dbp) /* * backup_env: - * CuTest *ct - * DB_ENV *dbenv: the environment to backup - * u_int32_t flags: hotbackup flags - * int has_callback: 0 if not use callback, 1 otherwise -*/ + * CuTest *ct. + * DB_ENV *dbenv: the environment to backup. + * u_int32_t flags: hotbackup flags. + * int has_callback: 0 if not use callback, 1 otherwise. + */ static int backup_env(ct, dbenv, flags, has_callback) CuTest *ct; @@ -574,13 +709,12 @@ backup_env(ct, dbenv, flags, has_callback) /* * backup_db: - * CuTest *ct - * DB_ENV *dbenv: the environment to backup - * const char *dname: the name of db file to backup - * u_int32_t flags: hot_backup flags - * int has_callback: 0 if not use callback, 1 otherwise -*/ - + * CuTest *ct. + * DB_ENV *dbenv: the environment to backup. + * const char *dname: the name of db file to backup. + * u_int32_t flags: hot_backup flags. + * int has_callback: 0 if not use callback, 1 otherwise. + */ static int backup_db(ct, dbenv, dname, flags, has_callback) CuTest *ct; @@ -599,152 +733,143 @@ backup_db(ct, dbenv, dname, flags, has_callback) return (0); } +/* + * verify_db_log: + * DBTYPE dtype: the database type. + * u_int32_t is_part: 0 if the database is not partitioned, 1 otherwise. + * u_int32_t is_lg: 1 if verifying the log files, 0 otherwise. + * const char *test_cmpdir: the db creation directory or log directory + * under TEST_ENV. + * const char *backup_cmpdir: the db creation directory or log directory + * under BACKUP_DIR. + */ static int -verify_db(envconf) - ENV_CONF_T envconf; +verify_db_log(dtype, is_part, is_lg, test_cmpdir, backup_cmpdir) + DBTYPE dtype; + u_int32_t is_part, is_lg; + const char *test_cmpdir, *backup_cmpdir; { - char buf1[100], buf2[100], path1[100], path2[100], pfx[10]; + char *buf1, *buf2, *path1, *path2, *pfx; char **names1, **names2; int cnt1, cnt2, i, m_cnt, ret, t_cnt1, t_cnt2; + buf1 = buf2 = path1 = path2 = pfx = NULL; names1 = names2 = NULL; cnt1 = cnt2 = i = m_cnt = ret = t_cnt1 = t_cnt2 = 0; - /* Get the data directory paths. */ - if (envconf == PARTITION_DB) { - snprintf(path1, sizeof(path1), "%s%c%s", - TEST_ENV, PATH_SEPARATOR[0], data_dirs[0]); - snprintf(path2, sizeof(path2), "%s", BACKUP_DIR); - } else if (envconf == MULTI_DATA_DIR) { - snprintf(path1, sizeof(path1), "%s%c%s", - TEST_ENV, PATH_SEPARATOR[0], data_dirs[0]); - snprintf(path2, sizeof(path2), "%s%c%s", - BACKUP_DIR, PATH_SEPARATOR[0], data_dirs[0]); - } else { - snprintf(path1, sizeof(path1), "%s", TEST_ENV); - snprintf(path2, sizeof(path2), "%s", BACKUP_DIR); - } + /* Either verify db files or log files. */ + if (is_part != 0 && + (is_lg != 0 || (dtype != DB_BTREE && dtype != DB_HASH))) + return (EINVAL); + + /* Get the data or log directory paths. */ + if ((ret = __os_calloc(NULL, 100, 1, &path1)) != 0) + goto err; + if ((ret = __os_calloc(NULL, 100, 1, &path2)) != 0) + goto err; + + if (test_cmpdir != NULL) { + ret = snprintf(path1, 100, "%s%c%s", + TEST_ENV, PATH_SEPARATOR[0], test_cmpdir); + if (ret <= 0 || ret >= 100) { + ret = EINVAL; + goto err; + } + ret = 0; + } else + snprintf(path1, 100, "%s", TEST_ENV); + + if (backup_cmpdir != NULL) { + ret = snprintf(path2, 100, "%s%c%s", + BACKUP_DIR, PATH_SEPARATOR[0], backup_cmpdir); + if (ret <= 0 || ret >= 100) { + ret = EINVAL; + goto err; + } + ret = 0; + } else + snprintf(path2, 100, "%s", BACKUP_DIR); - /* Define the prefix of partition db and queue extent files. */ - if (envconf == PARTITION_DB) - snprintf(pfx, sizeof(pfx), "%s", "__dbp."); - else if (envconf == QUEUE_DB) - snprintf(pfx, sizeof(pfx), "%s", "__dbq."); + /* Define the prefix of partition db, queue extent or log files. */ + if ((ret = __os_calloc(NULL, 10, 1, &pfx)) != 0) + goto err; + if (is_lg != 0) + snprintf(pfx, 10, "%s", "log."); + else if (is_part != 0) + snprintf(pfx, 10, "%s", "__dbp."); + else if (dtype == DB_QUEUE) + snprintf(pfx, 10, "%s", "__dbq."); else pfx[0] = '\0'; - /* Get the lists of db file, partition db files and queue extent. */ + /* Get the lists of db file, partition files, queue extent or logs. */ if ((ret = __os_dirlist(NULL, path1, 0, &names1, &cnt1)) != 0) return (ret); if ((ret = __os_dirlist(NULL, path2, 0, &names2, &cnt2)) != 0) return (ret); - /* Get the numbers of db files. */ + /* Get the file numbers. */ m_cnt = cnt1 > cnt2 ? cnt1 : cnt2; t_cnt1 = cnt1; t_cnt2 = cnt2; for (i = 0; i < m_cnt; i++) { - if (i < cnt1 && - strncmp(names1[i], BACKUP_DB, strlen(BACKUP_DB)) != 0 && + if (i < cnt1 && ((is_lg != 0 && + strncmp(names1[i], pfx, strlen(pfx)) != 0) || + (strncmp(names1[i], BACKUP_DB, strlen(BACKUP_DB)) != 0 && (strlen(pfx) > 0 ? - strncmp(names1[i], pfx, strlen(pfx)) != 0 : 1)) { - t_cnt1--; - names1[i] = NULL; + strncmp(names1[i], pfx, strlen(pfx)) != 0 : 1)))) { + t_cnt1--; + names1[i] = NULL; } - if (i < cnt2 && - strncmp(names2[i], BACKUP_DB, strlen(BACKUP_DB)) != 0 && + if (i < cnt2 && ((is_lg != 0 && + strncmp(names2[i], pfx, strlen(pfx)) != 0) || + (strncmp(names2[i], BACKUP_DB, strlen(BACKUP_DB)) != 0 && (strlen(pfx) > 0 ? - strncmp(names2[i], pfx, strlen(pfx)) != 0 : 1)) { - t_cnt2--; - names2[i] = NULL; - } + strncmp(names2[i], pfx, strlen(pfx)) != 0 : 1)))) { + t_cnt2--; + names2[i] = NULL; + } } - if ((ret = t_cnt1 == t_cnt2 ? 0 : EINVAL) != 0) - return (ret); + if ((ret = t_cnt1 == t_cnt2 ? 0 : EXIT_FAILURE) != 0) + goto err; - /* Compare each db file. */ + /* Compare each file. */ + if ((ret = __os_calloc(NULL, 100, 1, &buf1)) != 0) + goto err; + if ((ret = __os_calloc(NULL, 100, 1, &buf2)) != 0) + goto err; for (i = 0; i < cnt1; i++) { if (names1[i] == NULL) continue; - snprintf(buf1, sizeof(buf1), "%s%c%s", + snprintf(buf1, 100, "%s%c%s", path1, PATH_SEPARATOR[0], names1[i]); - snprintf(buf2, sizeof(buf2), "%s%c%s", + snprintf(buf2, 100, "%s%c%s", path2, PATH_SEPARATOR[0], names1[i]); if ((ret = cmp_files(buf1, buf2)) != 0) break; } +err: if (buf1 != NULL) + __os_free(NULL, buf1); + if (buf2 != NULL) + __os_free(NULL, buf2); + if (path1 != NULL) + __os_free(NULL, path1); + if (path2 != NULL) + __os_free(NULL, path2); + if (pfx != NULL) + __os_free(NULL, pfx); return (ret); } +/* + * verify_dbconfig: + * u_int32_t is_exist: 1 if DB_CONFIG is expected to exist + * in BACKUP_DIR, 0 otherwise. + */ static int -verify_log(envconf) - ENV_CONF_T envconf; -{ - char buf1[100], buf2[100], lg1[100], lg2[100], pfx[10]; - char **names1, **names2; - int cnt1, cnt2, i, m_cnt, ret, t_cnt1, t_cnt2; - - cnt1 = cnt2 = i = m_cnt = ret = t_cnt1 = t_cnt2 = 0; - - /* Get the log paths. */ - if (envconf == SET_LOG_DIR) { - snprintf(lg1, sizeof(lg1), - "%s%c%s", TEST_ENV, PATH_SEPARATOR[0], LOG_DIR); - snprintf(lg2, sizeof(lg2), - "%s%c%s", BACKUP_DIR, PATH_SEPARATOR[0], LOG_DIR); - } - else { - snprintf(lg1, sizeof(lg1), "%s", TEST_ENV); - snprintf(lg2, sizeof(lg2), "%s", BACKUP_DIR); - } - - /* Define the prefix of log file. */ - snprintf(pfx, sizeof(pfx), "%s", "log."); - - /* Get the lists of log files. */ - if ((ret = __os_dirlist(NULL, lg1, 0, &names1, &cnt1)) != 0) - return (ret); - if ((ret = __os_dirlist(NULL, lg2, 0, &names2, &cnt2)) != 0) - return (ret); - - /* Get the numbers of log files. */ - m_cnt = cnt1 > cnt2 ? cnt1 : cnt2; - t_cnt1 = cnt1; - t_cnt2 = cnt2; - for (i = 0; i < m_cnt; i++) { - if (i < cnt1 && - strncmp(names1[i], pfx, strlen(pfx)) != 0) { - t_cnt1--; - names1[i] = NULL; - } - if (i < cnt2 && - strncmp(names2[i], pfx, strlen(pfx)) != 0) { - t_cnt2--; - names2[i] = NULL; - } - } - if ((ret = t_cnt1 == t_cnt2 ? 0 : EINVAL) != 0) - return (ret); - - /* Compare each log file. */ - for (i = 0; i < cnt1; i++) { - if (names1[i] == NULL) - continue; - snprintf(buf1, sizeof(buf1), "%s%c%s", - lg1, PATH_SEPARATOR[0], names1[i]); - snprintf(buf2, sizeof(buf2), "%s%c%s", - lg2, PATH_SEPARATOR[0], names1[i]); - if ((ret = cmp_files(buf1, buf2)) != 0) - break; - } - - return (ret); -} - -static int -verify_dbconfig(envconf) - ENV_CONF_T envconf; +verify_dbconfig(is_exist) + u_int32_t is_exist; { char *path1, *path2; int ret; @@ -752,42 +877,37 @@ verify_dbconfig(envconf) path1 = path2 = NULL; ret = 0; - if ((ret = __os_calloc(NULL, 1024, 1, &path1)) != 0) + if ((ret = __os_calloc(NULL, 100, 1, &path1)) != 0) goto err; - if ((ret = __os_calloc(NULL, 1024, 1, &path2)) != 0) + if ((ret = __os_calloc(NULL, 100, 1, &path2)) != 0) goto err; - switch(envconf) { - /* DB_CONFIG is not in backupdir for this test cases. */ - case SIMPLE_ENV: - case PARTITION_DB: - case QUEUE_DB: - if((ret = __os_exists(NULL, "BACKUP/DB_CONFIG", 0)) != 0) - return (0); - break; - /* DB_CONFIG is in backupdir for MULTI_DATA_DIR and SET_LOG_DIR. */ - case MULTI_DATA_DIR: - case SET_LOG_DIR: - snprintf(path1, 1024, "%s%c%s", + if (is_exist == 0) { + if ((ret = __os_exists(NULL, "BACKUP/DB_CONFIG", 0)) != 0) { + ret = 0; + goto err; + } else { + ret = EXIT_FAILURE; + goto err; + } + } else { + snprintf(path1, 100, "%s%c%s", TEST_ENV, PATH_SEPARATOR[0], "DB_CONFIG"); - snprintf(path2, 1024, "%s%c%s", + snprintf(path2, 100, "%s%c%s", BACKUP_DIR, PATH_SEPARATOR[0], "DB_CONFIG"); if ((ret = cmp_files(path1, path2)) != 0) goto err; - break; - default: - return (EINVAL); } -err: - if (path1 != NULL) +err: if (path1 != NULL) __os_free(NULL, path1); if (path2 != NULL) __os_free(NULL, path2); return (ret); } -int backup_open(dbenv, dbname, target, handle) +static int +backup_open(dbenv, dbname, target, handle) DB_ENV *dbenv; const char *dbname; const char *target; @@ -817,7 +937,8 @@ int backup_open(dbenv, dbname, target, handle) return (ret); } -int backup_write(dbenv, gigs, offset, size, buf, handle) +static int +backup_write(dbenv, gigs, offset, size, buf, handle) DB_ENV *dbenv; u_int32_t gigs, offset, size; u_int8_t *buf; @@ -841,7 +962,8 @@ int backup_write(dbenv, gigs, offset, size, buf, handle) return (ret); } -int backup_close(dbenv, dbname, handle) +static int +backup_close(dbenv, dbname, handle) DB_ENV *dbenv; const char *dbname; void *handle; @@ -859,34 +981,37 @@ int backup_close(dbenv, dbname, handle) } static int -make_dbconfig(envconf) - ENV_CONF_T envconf; +make_dbconfig(content) + const char * content; { - const char *path = "TESTDIR/DB_CONFIG"; - char str[1024]; FILE *fp; + char *str; + int ret, size; - if (envconf >= PARTITION_DB && envconf <= SET_LOG_DIR) - fp = fopen(path, "w"); - else - return (0); + ret = 0; - switch(envconf) { - case PARTITION_DB: - case MULTI_DATA_DIR: - snprintf(str, 1024, "%s", "set_data_dir DATA1"); - break; - case SET_LOG_DIR: - snprintf(str, 1024, "%s", "set_lg_dir LOG"); - break; - default: + if (content == NULL) + return (0); + if ((fp = fopen("TESTDIR/DB_CONFIG", "w")) == NULL) return (EINVAL); + + if ((ret = __os_calloc(NULL, 1024, 1, &str)) != 0) + goto err; + size = snprintf(str, 1024, "%s", content); + if (size < 0 || size >= 1024) { + ret = EINVAL; + goto err; } - fputs(str, fp); - fclose(fp); + if (fputs(str, fp) == EOF) + ret = EXIT_FAILURE; - return (0); +err: if (fclose(fp) == EOF) + ret = EXIT_FAILURE; + if (str != NULL) + __os_free(NULL, str); + + return (ret); } static int @@ -911,29 +1036,29 @@ cmp_files(name1, name2) goto err; /* Open the input files. */ - if ( (ret = __os_open(NULL, name1, 0, DB_OSO_RDONLY, 0, &fhp1)) != 0 || - (t_ret = __os_open(NULL, name2, 0, DB_OSO_RDONLY, 0, &fhp2)) != 0) { - if (ret == 0) - ret = t_ret; - goto err; + if ((ret = __os_open(NULL, name1, 0, DB_OSO_RDONLY, 0, &fhp1)) != 0 || + (t_ret = __os_open(NULL, name2, 0, + DB_OSO_RDONLY, 0, &fhp2)) != 0) { + if (ret == 0) + ret = t_ret; + goto err; } /* Read and compare the file content. */ while ((ret = __os_read(NULL, fhp1, buf1, MEGABYTE, &nr1)) == 0 && nr1 > 0 && (ret = __os_read(NULL, fhp2, buf2, MEGABYTE, &nr2)) == 0 && nr2 > 0) { - if (nr1 != nr2) { - ret = EINVAL; - break; - } - if ((ret = memcmp(buf1, buf2, nr1)) != 0) - break; + if (nr1 != nr2) { + ret = EXIT_FAILURE; + break; + } + if ((ret = memcmp(buf1, buf2, nr1)) != 0) + break; } if(ret == 0 && nr1 > 0 && nr2 > 0 && nr1 != nr2) - ret = EINVAL; + ret = EXIT_FAILURE; -err: - if (buf1 != NULL) +err: if (buf1 != NULL) __os_free(NULL, buf1); if (buf2 != NULL) __os_free(NULL, buf2); @@ -943,3 +1068,77 @@ err: ret = t_ret; return (ret); } + +static int +test_backup_onlydbfile(ct, dbtype, has_callback) + CuTest *ct; + DBTYPE dbtype; + int has_callback; +{ + DB_ENV *dbenv; + DB *dbp; + struct handlers *info; + char **names; + int (*closep)(DB_ENV *, const char *, void *); + int (*openp)(DB_ENV *, const char *, const char *, void **); + int (*writep)(DB_ENV *,u_int32_t, + u_int32_t, u_int32_t, u_int8_t *, void *); + int cnt, i, t_cnt; + u_int32_t flag; + + info = ct->context; + flag = DB_EXCL; + closep = NULL; + openp = NULL; + writep = NULL; + + /* Step 1: set up directories. */ + CuAssert(ct, "setup_dir", setup_dir(0, NULL) == 0); + + /* Step 2: open db handle. */ + CuAssert(ct,"open_dbp", open_dbp(&dbenv, &dbp, + dbtype, 0, NULL, NULL, NULL, 0, NULL) == 0); + info->dbenvp = dbenv; + info->dbp = dbp; + + /* Step 3: store records into db. */ + CuAssert(ct, "store_records", store_records(dbp, 10) == 0); + CuAssert(ct, "DB->sync", dbp->sync(dbp, 0) == 0); + + /* Step 4: backup only the db file. */ + CuAssert(ct, "backup_db", + backup_db(ct, dbenv, BACKUP_DB, flag, has_callback) == 0); + + /* + * Step 5: check backup result. + * 5a: verify db file is in BACKUP_DIR. + */ + CuAssert(ct, "verify_db_log", + verify_db_log(dbtype, 0, 0, NULL, NULL) == 0); + + /* 5b: verify no other files are in BACKUP_DIR. */ + CuAssert(ct, "__os_dirlist", + __os_dirlist(NULL, BACKUP_DIR, 0, &names, &cnt) == 0); + if (dbtype != DB_QUEUE) + CuAssert(ct, "too many files in backupdir", cnt == 1); + else { + t_cnt = cnt; + for (i = 0; i < t_cnt; i++) { + if (strncmp(names[i], "__dbq.", 6) == 0) + cnt--; + } + CuAssert(ct, "too many files in backupdir", cnt == 1); + } + + /* Step 6: verify the backup callback. */ + CuAssert(ct, "DB_ENV->get_backup_callbacks", + dbenv->get_backup_callbacks(dbenv, + &openp, &writep, &closep) == (has_callback != 0 ? 0 : EINVAL)); + if (has_callback != 0) { + CuAssertTrue(ct, openp == backup_open); + CuAssertTrue(ct, writep == backup_write); + CuAssertTrue(ct, closep == backup_close); + } + + return (0); +} diff --git a/test/c/suites/TestDbTuner.c b/test/c/suites/TestDbTuner.c index 08a16bb3..e59837fe 100644 --- a/test/c/suites/TestDbTuner.c +++ b/test/c/suites/TestDbTuner.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ @@ -33,7 +33,7 @@ int open_db(DB_ENV **, DB **, char *, char *, u_int32_t, int); int run_test(CuTest *, u_int32_t, int, int, int, int, int); int store_db(DB *, int, int, int, int); -const char *progname = "TestDbTuner"; +const char *progname_dbtuner = "TestDbTuner"; int total_cases, success_cases; int TestDbTuner(CuTest *ct) { @@ -201,7 +201,7 @@ open_db(dbenvp, dbpp, dbname, home, pgsize, duptype) *dbenvp = dbenv; dbenv->set_errfile(dbenv, stderr); - dbenv->set_errpfx(dbenv, progname); + dbenv->set_errpfx(dbenv, progname_dbtuner); if ((ret = dbenv->set_cachesize(dbenv, (u_int32_t)0, diff --git a/test/c/suites/TestEncryption.c b/test/c/suites/TestEncryption.c index d0176306..60810034 100644 --- a/test/c/suites/TestEncryption.c +++ b/test/c/suites/TestEncryption.c @@ -1,7 +1,7 @@ /* * See the file LICENSE for redistribution information. * - * Copyright (c) 2011, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ * diff --git a/test/c/suites/TestEnvConfig.c b/test/c/suites/TestEnvConfig.c index f19b7b48..03467dc2 100644 --- a/test/c/suites/TestEnvConfig.c +++ b/test/c/suites/TestEnvConfig.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2002, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. */ #include <sys/types.h> @@ -14,6 +14,11 @@ #include "CuTest.h" #include "test_util.h" +struct context { + FILE *fp; + char *path; +}; + #define ENV { \ if (dbenv != NULL) \ CuAssertTrue(ct, dbenv->close(dbenv, 0) == 0); \ @@ -30,11 +35,30 @@ int TestEnvConfigSuiteTeardown(CuSuite *ct) { } int TestEnvConfigTestSetup(CuTest *ct) { + struct context *info; + + if ((info = calloc(1, sizeof(*info))) == NULL) + return (ENOMEM); + ct->context = info; setup_envdir(TEST_ENV, 1); return (0); } int TestEnvConfigTestTeardown(CuTest *ct) { + struct context *info; + FILE *fp; + char *path; + + info = ct->context; + assert(info != NULL); + fp = info->fp; + path = info->path; + if (fp != NULL) + fclose(fp); + if (path != NULL) + free(path); + free(info); + ct->context = NULL; teardown_envdir(TEST_ENV); return (0); } @@ -61,9 +85,20 @@ int TestSetTxMax(CuTest *ct) { int TestSetLogMax(CuTest *ct) { DB_ENV *dbenv; + struct context *info; + FILE *msgfile; + char *path; u_int32_t v; dbenv = NULL; + if ((path = calloc(100, sizeof(char))) == NULL) + return (ENOMEM); + snprintf(path, 100, "%s%c%s", TEST_ENV, PATH_SEPARATOR[0], "msgfile"); + if ((msgfile = fopen(path, "w")) == NULL) + return (EINVAL); + info = ct->context; + info->fp = msgfile; + info->path = path; /* lg_max: reset at run-time. */ ENV CuAssertTrue(ct, dbenv->set_lg_max(dbenv, 37 * 1024 * 1024) == 0); @@ -72,9 +107,18 @@ int TestSetLogMax(CuTest *ct) { CuAssertTrue(ct, dbenv->get_lg_max(dbenv, &v) == 0); CuAssertTrue(ct, v == 37 * 1024 * 1024); ENV + /* New log maximum size is ignored when joining the environment. */ CuAssertTrue(ct, dbenv->set_lg_max(dbenv, 63 * 1024 * 1024) == 0); + /* Redirect the error message to suppress the warning. */ + dbenv->set_msgfile(dbenv, msgfile); CuAssertTrue(ct, dbenv->open(dbenv, TEST_ENV, DB_JOINENV, 0666) == 0); CuAssertTrue(ct, dbenv->get_lg_max(dbenv, &v) == 0); + CuAssertTrue(ct, v == 37 * 1024 * 1024); + /* Direct the error message back to the standard output. */ + dbenv->set_msgfile(dbenv, NULL); + /* Re-config the log maximum size after opening the environment. */ + CuAssertTrue(ct, dbenv->set_lg_max(dbenv, 63 * 1024 * 1024) == 0); + CuAssertTrue(ct, dbenv->get_lg_max(dbenv, &v) == 0); CuAssertTrue(ct, v == 63 * 1024 * 1024); return (0); } @@ -234,9 +278,20 @@ int TestSetLockMaxObjects(CuTest *ct) { int TestSetLockTimeout(CuTest *ct) { DB_ENV *dbenv; + struct context *info; + FILE *msgfile; + char *path; db_timeout_t timeout; dbenv = NULL; + if ((path = calloc(100, sizeof(char))) == NULL) + return (ENOMEM); + snprintf(path, 100, "%s%c%s", TEST_ENV, PATH_SEPARATOR[0], "msgfile"); + if ((msgfile = fopen(path, "w")) == NULL) + return (EINVAL); + info = ct->context; + info->fp = msgfile; + info->path = path; /* lock timeout: reset at run-time. */ ENV CuAssertTrue(ct, @@ -247,20 +302,42 @@ int TestSetLockTimeout(CuTest *ct) { dbenv->get_timeout(dbenv, &timeout, DB_SET_LOCK_TIMEOUT) == 0); CuAssertTrue(ct, timeout == 37); ENV + /* New lock timeout is ignored when joining the environment. */ CuAssertTrue(ct, dbenv->set_timeout(dbenv, 63, DB_SET_LOCK_TIMEOUT) == 0); + /* Redirect the error message to suppress the warning. */ + dbenv->set_msgfile(dbenv, msgfile); CuAssertTrue(ct, dbenv->open(dbenv, TEST_ENV, DB_JOINENV, 0666) == 0); CuAssertTrue(ct, dbenv->get_timeout(dbenv, &timeout, DB_SET_LOCK_TIMEOUT) == 0); + CuAssertTrue(ct, timeout == 37); + /* Direct the error message back to the standard output. */ + dbenv->set_msgfile(dbenv, NULL); + /* Re-config the lock timeout after opening the environment. */ + CuAssertTrue(ct, + dbenv->set_timeout(dbenv, 63, DB_SET_LOCK_TIMEOUT) == 0); + CuAssertTrue(ct, + dbenv->get_timeout(dbenv, &timeout, DB_SET_LOCK_TIMEOUT) == 0); CuAssertTrue(ct, timeout == 63); return (0); } int TestSetTransactionTimeout(CuTest *ct) { DB_ENV *dbenv; + struct context *info; + FILE *msgfile; + char *path; db_timeout_t timeout; dbenv = NULL; + if ((path = calloc(100, sizeof(char))) == NULL) + return (ENOMEM); + snprintf(path, 100, "%s%c%s", TEST_ENV, PATH_SEPARATOR[0], "msgfile"); + if ((msgfile = fopen(path, "w")) == NULL) + return (EINVAL); + info = ct->context; + info->fp = msgfile; + info->path = path; /* txn timeout: reset at run-time. */ ENV CuAssertTrue(ct, @@ -271,11 +348,22 @@ int TestSetTransactionTimeout(CuTest *ct) { dbenv->get_timeout(dbenv, &timeout, DB_SET_TXN_TIMEOUT) == 0); CuAssertTrue(ct, timeout == 37); ENV + /* New transaction timeout is ignored when joining the environment. */ CuAssertTrue(ct, dbenv->set_timeout(dbenv, 63, DB_SET_TXN_TIMEOUT) == 0); + /* Redirect the error message to suppress the warning. */ + dbenv->set_msgfile(dbenv, msgfile); CuAssertTrue(ct, dbenv->open(dbenv, TEST_ENV, DB_JOINENV, 0666) == 0); CuAssertTrue(ct, dbenv->get_timeout(dbenv, &timeout, DB_SET_TXN_TIMEOUT) == 0); + CuAssertTrue(ct, timeout == 37); + /* Direct the error message back to the standard output. */ + dbenv->set_msgfile(dbenv, NULL); + /* Re-config the transaction timeout after opening the environment. */ + CuAssertTrue(ct, + dbenv->set_timeout(dbenv, 63, DB_SET_TXN_TIMEOUT) == 0); + CuAssertTrue(ct, + dbenv->get_timeout(dbenv, &timeout, DB_SET_TXN_TIMEOUT) == 0); CuAssertTrue(ct, timeout == 63); return (0); } diff --git a/test/c/suites/TestEnvMethod.c b/test/c/suites/TestEnvMethod.c index 7da0f785..febdb272 100644 --- a/test/c/suites/TestEnvMethod.c +++ b/test/c/suites/TestEnvMethod.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ diff --git a/test/c/suites/TestKeyExistErrorReturn.c b/test/c/suites/TestKeyExistErrorReturn.c index 75078bc2..9c870867 100644 --- a/test/c/suites/TestKeyExistErrorReturn.c +++ b/test/c/suites/TestKeyExistErrorReturn.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ diff --git a/test/c/suites/TestMutexAlignment.c b/test/c/suites/TestMutexAlignment.c new file mode 100644 index 00000000..285c59e8 --- /dev/null +++ b/test/c/suites/TestMutexAlignment.c @@ -0,0 +1,70 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2012, 2015 Oracle and/or its affiliates. All rights reserved. + * + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "db.h" +#include "CuTest.h" +#include "test_util.h" + +int TestMutexAlignment(CuTest *ct) { + int procs, threads, alignment, lockloop; + int max_procs, max_threads; + char *bin; + char cmdstr[1000], cmd[1000]; + + /* Step 1: Check required binary file existence, set args */ +#ifdef DB_WIN32 +#ifdef _WIN64 +#ifdef DEBUG + bin = "x64\\Debug\\test_mutex.exe"; +#else + bin = "x64\\Release\\test_mutex.exe"; +#endif +#else +#ifdef DEBUG + bin = "Win32\\Debug\\test_mutex.exe"; +#else + bin = "Win32\\Release\\test_mutex.exe"; +#endif +#endif + sprintf(cmdstr, "%s -p %%d -t %%d -a %%d -n %%d >/nul 2>&1", bin); + lockloop = 100; + max_procs = 2; + max_threads = 2; +#else + bin = "./test_mutex"; + sprintf(cmdstr, "%s -p %%d -t %%d -a %%d -n %%d >/dev/null 2>&1", bin); + lockloop = 2000; + max_procs = 4; + max_threads = 4; +#endif + + if (__os_exists(NULL, bin, NULL) != 0) { + printf("Error! Can not find %s. It need to be built in order to\ + run this test.\n", bin); + CuAssert(ct, bin, 0); + return (EXIT_FAILURE); + } + + /* Step 2: Test with different combinations. */ + for (procs = 1; procs <= max_procs; procs *= 2) { + for (threads= 1; threads <= max_threads; threads *= 2) { + if (procs ==1 && threads == 1) + continue; + for (alignment = 32; alignment <= 128; alignment *= 2) { + sprintf(cmd, cmdstr, procs, threads, alignment, + lockloop); + printf("%s\n", cmd); + CuAssert(ct, cmd, system(cmd) == 0); + } + } + } + return (EXIT_SUCCESS); +} diff --git a/test/c/suites/TestPartial.c b/test/c/suites/TestPartial.c index cacc6d51..d87ce12b 100644 --- a/test/c/suites/TestPartial.c +++ b/test/c/suites/TestPartial.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2011, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ diff --git a/test/c/suites/TestPartition.c b/test/c/suites/TestPartition.c new file mode 100644 index 00000000..7c460a2f --- /dev/null +++ b/test/c/suites/TestPartition.c @@ -0,0 +1,508 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2012, 2015 Oracle and/or its affiliates. All rights reserved. + * + * $Id$ + */ + +/* + * A C Unit test for DB->set_partition API. [#21373] + * + * Test cases: + * One of the key DBTs has no data; + * Two of the key DBTs have no data; + * Two key DBTs have the same non-NULL data; + * The key DBTs are not sorted; + * The partition number is not equal to key array size plus 1; + * Both partition key and callback are set; + * Neither partition key nor callback are set. + * + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "db.h" +#include "CuTest.h" +#include "test_util.h" + +static int close_db(DB_ENV *, DB *, CuTest *); +static int create_db(DB_ENV **, DB **dbpp, int bigcache, CuTest *); +static u_int32_t partitionCallback(DB *, DBT *); +static int put_data(DB *); + +static FILE *errfp; +static char *content; +static u_int32_t nparts; + +int TestPartitionSuiteSetup(CuSuite *suite) { + errfp = NULL; + content = "abcdefghijklmnopqrstuvwxyz"; + nparts = 5; + return (0); +} + +int TestPartitionSuiteTeardown(CuSuite *suite) { + return (0); +} + +int TestPartitionTestSetup(CuTest *ct) { + if (errfp != NULL) + fclose(errfp); + setup_envdir(TEST_ENV, 1); + errfp = fopen("TESTDIR/errfile", "w"); + return (0); +} + +int TestPartitionTestTeardown(CuTest *ct) { + if (errfp != NULL) { + fclose(errfp); + errfp = NULL; + } + return (0); +} + +int TestPartOneKeyNoData(CuTest *ct) { + DB_ENV *dbenv; + DB *dbp; + /* Allocate the memory from stack. */ + DBT keys[4]; + u_int32_t i; + + dbenv = NULL; + dbp = NULL; + nparts = 5; + + /* Do not assign any data to the first DBT. */ + memset(&keys[0], 0, sizeof(DBT)); + for (i = 1 ; i < (nparts - 1); i++) { + memset(&keys[i], 0, sizeof(DBT)); + keys[i].data = &content[(i + 1) * (strlen(content) / nparts)]; + keys[i].size = sizeof(char); + } + + /* Do not set any database flags. */ + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + /* + * Verify that before the database is opened, DB->set_partition can + * be called multiple times regardless of its return code. + */ + keys[0].flags = DB_DBT_MALLOC; + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) != 0); + keys[0].flags = 0; + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) != 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + fclose(errfp); + errfp = NULL; + + /* Set DB_DUPSORT flags. */ + setup_envdir(TEST_ENV, 1); + errfp = fopen("TESTDIR/errfile", "w"); + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->set_flags(dbp, DB_DUPSORT) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) != 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + fclose(errfp); + errfp = NULL; + + /* Set DB_DUP flags. */ + setup_envdir(TEST_ENV, 1); + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->set_flags(dbp, DB_DUP) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) == 0); + CuAssertTrue(ct, put_data(dbp) == 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + return (0); +} + +int TestPartTwoKeyNoData(CuTest *ct) { + DB_ENV *dbenv; + DB *dbp; + DBT *keys; + u_int32_t i; + + dbenv = NULL; + dbp = NULL; + keys = NULL; + nparts = 5; + + CuAssertTrue(ct, (keys = malloc((nparts - 1) * sizeof(DBT))) != NULL); + memset(keys, 0, (nparts - 1) * sizeof(DBT)); + /* Do not assign any data to the first 2 DBTs. */ + keys[0].size = keys[1].size = 0; + for (i = 2 ; i < (nparts - 1); i++) { + keys[i].data = &content[(i + 1) * (strlen(content) / nparts)]; + keys[i].size = sizeof(char); + } + + /* Do not set any database flags. */ + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) != 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + fclose(errfp); + errfp = NULL; + + /* Set DB_DUPSORT flags. */ + setup_envdir(TEST_ENV, 1); + errfp = fopen("TESTDIR/errfile", "w"); + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->set_flags(dbp, DB_DUPSORT) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) != 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + fclose(errfp); + errfp = NULL; + + /* Set DB_DUP flags. */ + setup_envdir(TEST_ENV, 1); + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->set_flags(dbp, DB_DUP) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) == 0); + CuAssertTrue(ct, put_data(dbp) == 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + free(keys); + return (0); +} + +int TestPartDuplicatedKey(CuTest *ct) { + DB_ENV *dbenv; + DB *dbp; + DBT *keys; + u_int32_t i; + + dbenv = NULL; + dbp = NULL; + keys = NULL; + nparts = 5; + + CuAssertTrue(ct, (keys = malloc((nparts - 1) * sizeof(DBT))) != NULL); + memset(keys, 0, (nparts - 1) * sizeof(DBT)); + /* Assign the same data to the first 2 DBTs. */ + for (i = 0 ; i < (nparts - 1); i++) { + if (i < 2) + keys[i].data = &content[strlen(content) / nparts]; + else + keys[i].data = &content[(i + 1) * + (strlen(content) / nparts)]; + keys[i].size = sizeof(char); + } + + /* Do not set any database flags. */ + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) != 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + fclose(errfp); + errfp = NULL; + + /* Set DB_DUPSORT flags. */ + setup_envdir(TEST_ENV, 1); + errfp = fopen("TESTDIR/errfile", "w"); + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->set_flags(dbp, DB_DUPSORT) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) != 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + fclose(errfp); + errfp = NULL; + + /* Set DB_DUP flags. */ + setup_envdir(TEST_ENV, 1); + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->set_flags(dbp, DB_DUP) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) == 0); + CuAssertTrue(ct, put_data(dbp) == 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + free(keys); + return (0); +} + +int TestPartUnsortedKey(CuTest *ct) { + DB_ENV *dbenv; + DB *dbp; + DBT *keys; + u_int32_t i, indx; + + dbenv = NULL; + dbp = NULL; + keys = NULL; + nparts = 6; + + CuAssertTrue(ct, (keys = malloc((nparts - 1) * sizeof(DBT))) != NULL); + memset(keys, 0, (nparts - 1) * sizeof(DBT)); + /* Assign unsorted keys to the array. */ + for (i = 0, indx = 0; i < (nparts - 1); i++) { + if (i == (nparts - 2) && i % 2 == 0) + indx = i; + else if (i % 2 != 0) + indx = i - 1; + else + indx = i + 1; + keys[i].data = + &content[(indx + 1) * (strlen(content) / nparts)]; + keys[i].size = sizeof(char); + } + + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, + dbp->set_partition(dbp, nparts - 1, &keys[1], NULL) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) == 0); + CuAssertTrue(ct, put_data(dbp) == 0); + CuAssertTrue(ct, dbp->close(dbp, 0) == 0); + + /* + * Reconfig with a different partition number and + * re-open the database. + */ + CuAssertTrue(ct, db_create(&dbp, dbenv, 0) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, 0, 0644) != 0); + CuAssertTrue(ct, dbp->close(dbp, 0) == 0); + + /* + * Reconfig with a different set of partition keys and + * re-open the database. + */ + CuAssertTrue(ct, db_create(&dbp, dbenv, 0) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts - 1, keys, NULL) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, 0, 0644) != 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + free(keys); + return (0); +} + +int TestPartNumber(CuTest *ct) { + DB_ENV *dbenv; + DB *dbp; + DBT *keys; + u_int32_t i; + + dbenv = NULL; + dbp = NULL; + keys = NULL; + nparts = 1000000; + + CuAssertTrue(ct, (keys = malloc((nparts - 1) * sizeof(DBT))) != NULL); + memset(keys, 0, (nparts - 1) * sizeof(DBT)); + /* Assign data to the keys. */ + for (i = 0 ; i < (nparts - 1); i++) { + CuAssertTrue(ct, + (keys[i].data = malloc(sizeof(u_int32_t))) != NULL); + memcpy(keys[i].data, &i, sizeof(u_int32_t)); + keys[i].size = sizeof(u_int32_t); + } + + /* Partition number is less than 2. */ + CuAssertTrue(ct, create_db(&dbenv, &dbp, 1, ct) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, 1, keys, NULL) != 0); + + /* Partition number is bigger than the limit 1000000. */ + CuAssertTrue(ct, + dbp->set_partition(dbp, nparts + 1, keys, NULL) == EINVAL); + + /* Partition number is equal to the limit 1000000. */ + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + + /* Partition keys do not fix into a single database page. */ + CuAssertTrue(ct, dbp->set_pagesize(dbp, 512) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, 800, keys, NULL) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) == 0); + CuAssertTrue(ct, put_data(dbp) == 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + + for (i = 0 ; i < (nparts - 1); i++) + free(keys[i].data); + free(keys); + return (0); +} + +int TestPartKeyCallBothSet(CuTest *ct) { + DB_ENV *dbenv; + DB *dbp; + DBT *keys; + u_int32_t i; + + dbenv = NULL; + dbp = NULL; + keys = NULL; + nparts = 5; + + CuAssertTrue(ct, (keys = malloc((nparts - 1) * sizeof(DBT))) != NULL); + memset(keys, 0, (nparts - 1) * sizeof(DBT)); + /* Do not assign any data to the first DBT. */ + for (i = 0 ; i < (nparts - 1); i++) { + keys[i].data = &content[(i + 1) * (strlen(content) / nparts)]; + keys[i].size = sizeof(char); + } + + /* Set both partition key and callback, expect it fails. */ + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, + dbp->set_partition(dbp, nparts, keys, partitionCallback) != 0); + + /* Set partition by key and open the database. */ + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) == 0); + CuAssertTrue(ct, put_data(dbp) == 0); + CuAssertTrue(ct, dbp->close(dbp, 0) == 0); + + /* Reconfig the partition with callback, expect it fails. */ + CuAssertTrue(ct, db_create(&dbp, dbenv, 0) == 0); + CuAssertTrue(ct, + dbp->set_partition(dbp, nparts, NULL, partitionCallback) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, 0, 0644) != 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + fclose(errfp); + errfp = NULL; + + /* Set partition by callback and open the database. */ + setup_envdir(TEST_ENV, 1); + errfp = fopen("TESTDIR/errfile", "w"); + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, + dbp->set_partition(dbp, nparts, NULL, partitionCallback) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, DB_CREATE, 0644) == 0); + CuAssertTrue(ct, put_data(dbp) == 0); + CuAssertTrue(ct, dbp->close(dbp, 0) == 0); + + /* Reconfig the partition with key, expect it fails. */ + CuAssertTrue(ct, db_create(&dbp, dbenv, 0) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, keys, NULL) == 0); + CuAssertTrue(ct, dbp->open(dbp, NULL, + "test.db", NULL, DB_BTREE, 0, 0644) != 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + free(keys); + return (0); +} + +int TestPartKeyCallNeitherSet(CuTest *ct) { + DB_ENV *dbenv; + DB *dbp; + + dbenv = NULL; + dbp = NULL; + + CuAssertTrue(ct, create_db(&dbenv, &dbp, 0, ct) == 0); + CuAssertTrue(ct, dbp->set_partition(dbp, nparts, NULL, NULL) != 0); + CuAssertTrue(ct, close_db(dbenv, dbp, ct) == 0); + + return (0); +} + +static int +create_db(dbenvp, dbpp, bigcache, ct) + DB_ENV **dbenvp; + DB **dbpp; + int bigcache; + CuTest *ct; +{ + DB_ENV *dbenv; + DB *dbp; + + dbenv = NULL; + dbp = NULL; + + CuAssertTrue(ct, db_env_create(&dbenv, 0) == 0); + *dbenvp = dbenv; + /* Big cache size is needed in some test case. */ + if (bigcache != 0 ) + CuAssertTrue(ct, dbenv->set_cachesize(dbenv, + 0, 128 * 1048576, 1) == 0); + CuAssertTrue(ct, dbenv->open(dbenv, TEST_ENV, + DB_CREATE | DB_INIT_LOCK |DB_INIT_LOG | + DB_INIT_MPOOL | DB_INIT_TXN, 0666) == 0); + + CuAssertTrue(ct, db_create(&dbp, dbenv, 0) == 0); + *dbpp = dbp; + dbp->set_errfile(dbp, errfp != NULL ? errfp : stdout); + dbp->set_errpfx(dbp, "TestPartition"); + + return (0); +} + +static int +close_db(dbenv, dbp, ct) + DB_ENV *dbenv; + DB *dbp; + CuTest *ct; +{ + if (dbp != NULL) + CuAssertTrue(ct, dbp->close(dbp, 0) == 0); + if (dbenv != NULL) + CuAssertTrue(ct, dbenv->close(dbenv, 0) == 0); + return (0); +} + +static u_int32_t +partitionCallback(dbp, key) + DB *dbp; + DBT *key; +{ + char *a, *b; + u_int32_t i, len; + + a = (char *)key->data; + b = NULL; + len = nparts % strlen(content); + + for (i = 0; i < len; i++) { + b = &content[(i + 1) * (strlen(content) / len)]; + if ((*a - *b) < 0) + break; + } + + return (i); +} + +static int +put_data(dbp) + DB *dbp; +{ + DBT key, data; + u_int32_t i; + int ret; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + for (i = 0; i < strlen(content); i++) { + key.data = &content[i]; + key.size = sizeof(char); + + data.data = &content[i]; + data.size = sizeof(char); + + if ((ret = dbp->put(dbp, NULL, &key, &data, 0)) != 0) { + dbp->err(dbp, ret, "DB->put"); + return (ret); + } + } + return (ret); +} + diff --git a/test/c/suites/TestPreOpenSetterAndGetter.c b/test/c/suites/TestPreOpenSetterAndGetter.c new file mode 100644 index 00000000..2005307a --- /dev/null +++ b/test/c/suites/TestPreOpenSetterAndGetter.c @@ -0,0 +1,1178 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2012, 2015 Oracle and/or its affiliates. All rights reserved. + * + * $Id$ + */ + +/* + * Test setting and getting the configuration before handle open [#21552] + * + * It tests all the settings on the following handles: + * 1. DB_ENV + * 2. DB + * 3. DB_MPOOLFILE + * 4. DB_SEQUENCE + * These handles have separate steps for 'create' and 'open', so that + * we can do pre-open configuration. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "db.h" +#include "CuTest.h" +#include "test_util.h" + +/* + * Test functions like DB_ENV->set_lk_max_lockers and + * and DB_ENV->get_lk_max_lockers, among which the setter function accepts a + * number as the second argument while the getter function accepts a pointer + * to number. + */ +#define CHECK_1_DIGIT_VALUE(handle, setter, getter, type, v) do { \ + type vs, vg; \ + vs = (type)(v); \ + CuAssert(ct, #handle"->"#setter, \ + (handle)->setter((handle), vs) == 0); \ + CuAssert(ct, #handle"->"#getter, \ + (handle)->getter((handle), &vg) == 0); \ + CuAssert(ct, #getter"=="#setter, vs == vg); \ +} while(0) + +/* + * Test all possible values for functions like DB_ENV->set_lk_detect and + * DB_ENV->get_lk_detect. The values for these functions are in a small set. + */ +#define CHECK_1_DIGIT_VALUES(handle, setter, getter, type, values) do { \ + size_t cnt, i; \ + cnt = sizeof(values) / sizeof(values[0]); \ + for (i = 0; i < cnt; i++) { \ + CHECK_1_DIGIT_VALUE(handle, setter, getter, type, \ + values[i]); \ + } \ +} while(0) + +/* + * Test functions like DB_ENV->set_backup_config and DB_ENV->get_backup_config, + * among which both getter and setter functions accept an option as the second + * argument, and for the third argument the setter accepts a number while + * getter accepts a pointer to number. + */ +#define CHECK_1_DIGIT_CONFIG_VALUE(handle, setter, getter, opt, type, v)\ + do { \ + type vs, vg; \ + vs = (type)(v); \ + CuAssert(ct, #handle"->"#setter, \ + (handle)->setter((handle), (opt), vs) == 0); \ + CuAssert(ct, #handle"->"#getter, \ + (handle)->getter((handle), (opt), &vg) == 0); \ + CuAssert(ct, #getter"=="#setter, vs == vg); \ +} while(0) + +/* + * Test all the options for functions satisfying CHECK_1_DIGIT_CONFIG_VALUE. + * The configuration value has no specific limit. + */ +#define CHECK_1_DIGIT_CONFIG_VALUES(handle, setter, getter, configs, type)\ + do { \ + size_t cnt, i; \ + cnt = sizeof(configs) / sizeof(configs[0]); \ + for (i = 0; i < cnt; i++) { \ + CHECK_1_DIGIT_CONFIG_VALUE(handle, setter, getter, \ + configs[i], type, rand()); \ + } \ +} while(0) + +/* + * Test turning on/off all the options for functions like DB_ENV->set_verbose + * and DB_ENV->get_verbose. + */ +#define CHECK_ONOFF(handle, setter, getter, options, opt_cnt, type) do {\ + size_t i; \ + for (i = 0; i < (opt_cnt); i++) { \ + CHECK_1_DIGIT_CONFIG_VALUE(handle, setter, getter, \ + (options)[i], type, 1); \ + CHECK_1_DIGIT_CONFIG_VALUE(handle, setter, getter, \ + (options)[i], type, 0); \ + } \ +} while(0) + +/* + * Like CHECK_1_DIGIT_CONFIG_VALUE, but the number or pointer to number + * is the second argument while the option is the third argument. + */ +#define CHECK_1_DIGIT_CONFIG_VALUE2(handle, setter, getter, opt, type, v)\ + do { \ + type vs, vg; \ + vs = (type)(v); \ + CuAssert(ct, #handle"->"#setter, \ + (handle)->setter((handle), vs, (opt)) == 0); \ + CuAssert(ct, #handle"->"#getter, \ + (handle)->getter((handle), &vg, (opt)) == 0); \ + CuAssert(ct, #getter"=="#setter, vs == vg); \ +} while(0) + +/* + * Test all the options for functions satisfying CHECK_1_DIGIT_CONFIG_VALUE2. + * The configuration value has no specific limit. + */ +#define CHECK_1_DIGIT_CONFIG_VALUES2(handle, setter, getter, configs, type)\ + do { \ + size_t cnt, i; \ + cnt = sizeof(configs) / sizeof(configs[0]); \ + for (i = 0; i < cnt; i++) { \ + CHECK_1_DIGIT_CONFIG_VALUE2(handle, setter, getter, \ + configs[i], type, rand()); \ + } \ +} while(0) + +/* + * Test functions like DB_ENV->set_create_dir and and DB_ENV->get_create_dir, + * among which the setter function accepts a string(const char *) as the + * second argument, and the getter function accepts a pointer to string. + */ +#define CHECK_1_STR_VALUE(handle, setter, getter, v) do { \ + const char *vs, *vg; \ + vs = (v); \ + CuAssert(ct, #handle"->"#setter, \ + (handle)->setter((handle), vs) == 0); \ + CuAssert(ct, #handle"->"#getter, \ + (handle)->getter((handle), &vg) == 0); \ + CuAssert(ct, #getter"=="#setter, strcmp(vs, vg) == 0); \ +} while(0) + +/* + * Like CHECK_1_STR_VALUE, but both setter and getter functions do + * not return anything. + */ +#define CHECK_1_STR_VALUE_VOID(handle, setter, getter, v) do { \ + const char *vs, *vg; \ + vs = (v); \ + (handle)->setter((handle), vs); \ + (handle)->getter((handle), &vg); \ + CuAssert(ct, #getter"=="#setter, strcmp(vs, vg) == 0); \ +} while(0) + +/* + * Test functions like DB_ENV->set_errfile and and DB_ENV->get_errfile, + * among which the setter function accepts a pointer as the second + * argument, and the getter function accepts a pointer to pointer. + */ +#define CHECK_1_PTR_VALUE(handle, setter, getter, type, v) do { \ + type *vs, *vg; \ + vs = (v); \ + CuAssert(ct, #handle"->"#setter, \ + (handle)->setter((handle), vs) == 0); \ + CuAssert(ct, #handle"->"#getter, \ + (handle)->getter((handle), &vg) == 0); \ + CuAssert(ct, #getter"=="#setter, vs == vg); \ +} while(0) + +/* + * Like CHECK_1_PTR_VALUE, but both setter and getter functions do + * not return anything. + */ +#define CHECK_1_PTR_VALUE_VOID(handle, setter, getter, type, v) do { \ + type *vs, *vg; \ + vs = (v); \ + (handle)->setter((handle), vs); \ + (handle)->getter((handle), &vg); \ + CuAssert(ct, #getter"=="#setter, vs == vg); \ +} while(0) + +/* + * Test functions like DB_ENV->set_memory_max and and DB_ENV->get_memory_max, + * among which the setter function accepts two numbers the second and third + * argument, while the getter function accepts two pointers to number. + */ +#define CHECK_2_DIGIT_VALUES(handle, setter, getter, type1, v1, type2, v2)\ + do { \ + type1 vs1, vg1; \ + type2 vs2, vg2; \ + vs1 = (type1)(v1); \ + vs2 = (type2)(v2); \ + CuAssert(ct, #handle"->"#setter, \ + (handle)->setter((handle), vs1, vs2) == 0); \ + CuAssert(ct, #handle"->"#getter, \ + (handle)->getter((handle), &vg1, &vg2) == 0); \ + CuAssert(ct, #getter"=="#setter, vs1 == vg1); \ + CuAssert(ct, #getter"=="#setter, vs2 == vg2); \ +} while(0) + +/* + * Test functions like DB_ENV->set_cachesize and and DB_ENV->get_cachesize, + * among which the setter function accepts three numbers as the second and + * third and fourth argument, while the getter function accepts three pointers + * to number. + */ +#define CHECK_3_DIGIT_VALUES(handle, setter, getter, type1, v1, type2, \ + v2, type3, v3) do { \ + type1 vs1, vg1; \ + type2 vs2, vg2; \ + type3 vs3, vg3; \ + vs1 = (type1)(v1); \ + vs2 = (type2)(v2); \ + vs3 = (type3)(v3); \ + CuAssert(ct, #handle"->"#setter, \ + (handle)->setter((handle), vs1, vs2, v3) == 0); \ + CuAssert(ct, #handle"->"#getter, \ + (handle)->getter((handle), &vg1, &vg2, &vg3) == 0); \ + CuAssert(ct, #getter"=="#setter, vs1 == vg1); \ + CuAssert(ct, #getter"=="#setter, vs2 == vg2); \ + CuAssert(ct, #getter"=="#setter, vs3 == vg3); \ +} while(0) + +/* + * Test functions like DB->set_flags and DB->get_flags, among which the setter + * function accepts an inclusive'OR of some individual options while the getter + * accepts a pointer to store the options composition. + * In this case, the getter may not return the exact value set by setter. + */ +#define CHECK_FLAG_VALUE(handle, setter, getter, type, v) do { \ + type vs, vg; \ + vs = (type)(v); \ + CuAssert(ct, #handle"->"#setter, \ + (handle)->setter((handle), vs) == 0); \ + CuAssert(ct, #handle"->"#getter, \ + (handle)->getter((handle), &vg) == 0); \ + CuAssert(ct, #getter"=="#setter, (vs & vg) == vs); \ +} while(0) + +/* + * Test functions like DB_ENV->set_flags and DB_ENV->get_flags, among which + * the setter function accepts an individual option and an on/off value(1/0) + * while the getter accepts a pointer to store the options composition. + */ +#define CHECK_FLAG_VALUE_ONOFF(handle, setter, getter, type, v, on) do {\ + type vs, vg; \ + vs = (type)(v); \ + CuAssert(ct, #handle"->"#setter, \ + (handle)->setter((handle), vs, on) == 0); \ + CuAssert(ct, #handle"->"#getter, \ + (handle)->getter((handle), &vg) == 0); \ + if (on) { \ + CuAssert(ct, #getter"=="#setter, (vs & vg) == vs); \ + } else { \ + CuAssert(ct, #getter"=="#setter, (vs & vg) == 0); \ + } \ +} while(0) + +/* + * Test turning on/off all the options for DB_ENV->set_flags. + */ +#define CHECK_ENV_FLAGS(handle, values) do { \ + size_t i, cnt; \ + cnt = sizeof(values) / sizeof(values[0]); \ + for (i = 0; i < cnt; i++) { \ + /* We only set the direct I/O if the os supports it. */ \ + if ((values[i] & DB_DIRECT_DB) != 0 && \ + __os_support_direct_io() == 0) \ + continue; \ + CHECK_FLAG_VALUE_ONOFF(handle, set_flags, \ + get_flags, u_int32_t, values[i], 1); \ + CHECK_FLAG_VALUE_ONOFF(handle, set_flags, \ + get_flags, u_int32_t, values[i], 0); \ + } \ +} while(0) + +struct handlers { + DB_ENV *dbenvp; + DB *dbp; + DB_MPOOLFILE *mp; + DB_SEQUENCE *seqp; +}; +static struct handlers info; +static const char *data_dirs[] = { + "data_dir1", + "data_dir2", + "data_dir3", + "data_dir4", + NULL +}; +const char *passwd = "passwd1"; +static FILE *errfile, *msgfile; + +static int add_dirs_to_dbenv(DB_ENV *dbenv, const char **dirs); +static int close_db_handle(DB *dbp); +static int close_dbenv_handle(DB_ENV *dbenvp); +static int close_mp_handle(DB_MPOOLFILE *mp); +static int close_seq_handle(DB_SEQUENCE *seqp); +static int cmp_dirs(const char **dirs1, const char **dirs2); +static int create_db_handle(DB **dbpp, DB_ENV *dbenv); +static int create_dbenv_handle(DB_ENV **dbenvpp); +static int create_mp_handle(DB_MPOOLFILE **mpp, DB_ENV *dbenv); +static int create_seq_handle(DB_SEQUENCE **seqpp, DB *dbp); + +int TestPreOpenSetterAndGetterSuiteSetup(CuSuite *suite) { + srand((unsigned int)time(NULL)); + return (0); +} + +int TestPreOpenSetterAndGetterSuiteTeardown(CuSuite *suite) { + return (0); +} + +int TestPreOpenSetterAndGetterTestSetup(CuTest *ct) { + char buf[DB_MAXPATHLEN]; + + setup_envdir(TEST_ENV, 1); + sprintf(buf, "%s/%s", TEST_ENV, "errfile"); + errfile = fopen(buf, "w"); + CuAssert(ct, "open errfile", errfile != NULL); + sprintf(buf, "%s/%s", TEST_ENV, "msgfile"); + msgfile = fopen(buf, "w"); + CuAssert(ct, "open msgfile", msgfile != NULL); + + info.dbenvp = NULL; + info.dbp = NULL; + info.mp = NULL; + info.seqp = NULL; + + return (0); +} + +int TestPreOpenSetterAndGetterTestTeardown(CuTest *ct) { + CuAssert(ct, "close errfile", fclose(errfile) == 0); + CuAssert(ct, "close msgfile", fclose(msgfile) == 0); + /* + * Close the handle in case failure happens. + */ + if (info.seqp != NULL) + CuAssert(ct, "seqp->close", + info.seqp->close(info.seqp, 0) == 0); + if (info.mp != NULL) + CuAssert(ct, "mp->close", info.mp->close(info.mp, 0) == 0); + if (info.dbp != NULL) + CuAssert(ct, "dbp->close", + info.dbp->close(info.dbp, 0) == 0); + if (info.dbenvp != NULL) + CuAssert(ct, "dbenvp->close", + info.dbenvp->close(info.dbenvp, 0) == 0); + return (0); +} + +/* + * For most number arguments, if there is no special requirement, we use + * a random value, since most checks are done during open and we do not + * do handle open in all the following tests. + * + * If a configuration has many options, we will cover all options. If + * it accepts inclusive'OR of the options, we will test some OR's as well. + */ + +int TestEnvPreOpenSetterAndGetter(CuTest *ct) { + DB_ENV *dbenv, *repmgr_dbenv; + const char **dirs; + const u_int8_t *lk_get_conflicts; + u_int8_t lk_set_conflicts[] = {1, 0, 0, 0}; + DB_BACKUP_CONFIG backup_configs[] = { + /* + * DB_BACKUP_WRITE_DIRECT is not listed here, since the + * value(only 1/0) for this configuration is different + * from others. So we test it separately. + */ + DB_BACKUP_READ_COUNT, + DB_BACKUP_READ_SLEEP, + DB_BACKUP_SIZE + }; + u_int32_t env_flags[] = { + DB_CDB_ALLDB, + DB_AUTO_COMMIT | DB_MULTIVERSION | DB_REGION_INIT | + DB_TXN_SNAPSHOT, + DB_DSYNC_DB | DB_NOLOCKING | DB_NOMMAP | DB_NOPANIC, + DB_OVERWRITE | DB_TIME_NOTGRANTED | DB_TXN_NOSYNC | + DB_YIELDCPU, + DB_TXN_NOWAIT | DB_TXN_WRITE_NOSYNC, + DB_DIRECT_DB + }; + u_int32_t env_timeouts[] = { + DB_SET_LOCK_TIMEOUT, + DB_SET_REG_TIMEOUT, + DB_SET_TXN_TIMEOUT + }; + u_int32_t lk_detect_values[] = { + DB_LOCK_DEFAULT, + DB_LOCK_EXPIRE, + DB_LOCK_MAXLOCKS, + DB_LOCK_MAXWRITE, + DB_LOCK_MINLOCKS, + DB_LOCK_MINWRITE, + DB_LOCK_OLDEST, + DB_LOCK_RANDOM, + DB_LOCK_YOUNGEST + }; + u_int32_t log_configs[] = { + DB_LOG_DIRECT, + DB_LOG_DSYNC, + DB_LOG_AUTO_REMOVE, + DB_LOG_IN_MEMORY, + DB_LOG_ZERO + }; + DB_MEM_CONFIG mem_configs[] = { + DB_MEM_LOCK, + DB_MEM_LOCKOBJECT, + DB_MEM_LOCKER, + DB_MEM_LOGID, + DB_MEM_TRANSACTION, + DB_MEM_THREAD + }; + u_int32_t rep_configs[] = { + DB_REP_CONF_AUTOINIT, + DB_REP_CONF_AUTOROLLBACK, + DB_REP_CONF_BULK, + DB_REP_CONF_DELAYCLIENT, + DB_REP_CONF_INMEM, + DB_REP_CONF_LEASE, + DB_REP_CONF_NOWAIT, + DB_REPMGR_CONF_ELECTIONS, + DB_REPMGR_CONF_2SITE_STRICT + }; + u_int32_t rep_timeouts[] = { + DB_REP_CHECKPOINT_DELAY, + DB_REP_ELECTION_TIMEOUT, + DB_REP_FULL_ELECTION_TIMEOUT, + DB_REP_LEASE_TIMEOUT + }; + u_int32_t repmgr_timeouts[] = { + DB_REP_ACK_TIMEOUT, + DB_REP_CONNECTION_RETRY, + DB_REP_ELECTION_RETRY, + DB_REP_HEARTBEAT_MONITOR, + DB_REP_HEARTBEAT_SEND + }; + u_int32_t verbose_flags[] = { + DB_VERB_BACKUP, + DB_VERB_DEADLOCK, + DB_VERB_FILEOPS, + DB_VERB_FILEOPS_ALL, + DB_VERB_RECOVERY, + DB_VERB_REGISTER, + DB_VERB_REPLICATION, + DB_VERB_REP_ELECT, + DB_VERB_REP_LEASE, + DB_VERB_REP_MISC, + DB_VERB_REP_MSGS, + DB_VERB_REP_SYNC, + DB_VERB_REP_SYSTEM, + DB_VERB_REPMGR_CONNFAIL, + DB_VERB_REPMGR_MISC, + DB_VERB_WAITSFOR + }; + u_int32_t encrypt_flags; + size_t log_configs_cnt, rep_configs_cnt, verbose_flags_cnt; + int lk_get_nmodes, lk_set_nmodes; + time_t tx_get_timestamp, tx_set_timestamp; + + verbose_flags_cnt = sizeof(verbose_flags) / sizeof(verbose_flags[0]); + log_configs_cnt = sizeof(log_configs) / sizeof(log_configs[0]); + rep_configs_cnt = sizeof(rep_configs) / sizeof(rep_configs[0]); + + CuAssert(ct, "db_env_create", create_dbenv_handle(&dbenv) == 0); + + /* Test the DB_ENV->add_data_dir(), DB_ENV->get_data_dirs(). */ + CuAssert(ct, "add_dirs_to_dbenv", + add_dirs_to_dbenv(dbenv, data_dirs) == 0); + CuAssert(ct, "dbenv->get_data_dirs", + dbenv->get_data_dirs(dbenv, &dirs) == 0); + CuAssert(ct, "cmp_dirs", cmp_dirs(data_dirs, dirs) == 0); + + /* Test DB_ENV->set_backup_config(), DB_ENV->get_backup_config(). */ + CHECK_1_DIGIT_CONFIG_VALUE(dbenv, set_backup_config, + get_backup_config, DB_BACKUP_WRITE_DIRECT, u_int32_t, 1); + CHECK_1_DIGIT_CONFIG_VALUE(dbenv, set_backup_config, + get_backup_config, DB_BACKUP_WRITE_DIRECT, u_int32_t, 0); + CHECK_1_DIGIT_CONFIG_VALUES(dbenv, set_backup_config, get_backup_config, + backup_configs, DB_BACKUP_CONFIG); + + /* Test DB_ENV->set_create_dir(), DB_ENV->get_create_dir(). */ + CHECK_1_STR_VALUE(dbenv, set_create_dir, get_create_dir, data_dirs[1]); + + /* Test DB_ENV->set_encrypt(), DB_ENV->get_encrypt_flags(). */ + CuAssert(ct, "dbenv->set_encrypt", + dbenv->set_encrypt(dbenv, passwd, DB_ENCRYPT_AES) == 0); + CuAssert(ct, "dbenv->get_encrypt_flags", + dbenv->get_encrypt_flags(dbenv, &encrypt_flags) == 0); + CuAssert(ct, "check encrypt flags", encrypt_flags == DB_ENCRYPT_AES); + + /* Test DB_ENV->set_errfile(), DB_ENV->get_errfile(). */ + CHECK_1_PTR_VALUE_VOID(dbenv, set_errfile, get_errfile, FILE, errfile); + + /* Test DB_ENV->set_errpfx(), DB_ENV->get_errpfx(). */ + CHECK_1_STR_VALUE_VOID(dbenv, set_errpfx, get_errpfx, "dbenv0"); + + /* Test DB_ENV->set_flags(), DB_ENV->get_flags(). */ + CHECK_ENV_FLAGS(dbenv, env_flags); + + /* + * Test DB_ENV->set_intermediate_dir_mode(), + * DB_ENV->get_intermediate_dir_mode(). + */ + CHECK_1_STR_VALUE(dbenv, + set_intermediate_dir_mode, get_intermediate_dir_mode, "rwxr-xr--"); + + /* Test DB_ENV->set_memory_init(), DB_ENV->get_memory_init(). */ + CHECK_1_DIGIT_CONFIG_VALUES(dbenv, set_memory_init, get_memory_init, + mem_configs, u_int32_t); + + /* + * Test DB_ENV->set_memory_max(), DB_ENV->get_memory_max(). + * The code will adjust the values if necessary, and using + * random values can not guarantee the returned values + * are exactly what we set. So we will not use random values here. + */ + CHECK_2_DIGIT_VALUES(dbenv, set_memory_max, get_memory_max, + u_int32_t, 2, u_int32_t, 1048576); + + /* Test DB_ENV->set_metadata_dir(), DB_ENV->get_metadata_dir(). */ + CHECK_1_STR_VALUE(dbenv, + set_metadata_dir, get_metadata_dir, data_dirs[2]); + + /* Test DB_ENV->set_msgfile(), DB_ENV->get_msgfile(). */ + CHECK_1_PTR_VALUE_VOID(dbenv, set_msgfile, get_msgfile, FILE, msgfile); + + /* Test DB_ENV->set_shm_key(), DB_ENV->get_shm_key(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_shm_key, get_shm_key, long, rand()); + + /* Test DB_ENV->set_timeout(), DB_ENV->get_timeout(). */ + CHECK_1_DIGIT_CONFIG_VALUES2(dbenv, set_timeout, get_timeout, + env_timeouts, db_timeout_t); + + /* Test DB_ENV->set_tmp_dir(), DB_ENV->get_tmp_dir(). */ + CHECK_1_STR_VALUE(dbenv, set_tmp_dir, get_tmp_dir, "/temp"); + + /* Test DB_ENV->set_verbose(), DB_ENV->get_verbose(). */ + CHECK_ONOFF(dbenv, set_verbose, get_verbose, verbose_flags, + verbose_flags_cnt, int); + + /* ==================== Lock Configuration ===================== */ + + /* Test DB_ENV->set_lk_conflicts(), DB_ENV->get_lk_conflicts(). */ + lk_set_nmodes = 2; + CuAssert(ct, "dbenv->set_lk_conflicts", dbenv->set_lk_conflicts(dbenv, + lk_set_conflicts, lk_set_nmodes) == 0); + CuAssert(ct, "dbenv->get_lk_conflicts", dbenv->get_lk_conflicts(dbenv, + &lk_get_conflicts, &lk_get_nmodes) == 0); + CuAssert(ct, "check lock conflicts", memcmp(lk_set_conflicts, + lk_get_conflicts, sizeof(lk_set_conflicts)) == 0); + CuAssert(ct, "check lock nomdes", lk_set_nmodes == lk_get_nmodes); + + /* DB_ENV->set_lk_detect(), DB_ENV->get_lk_detect(). */ + CHECK_1_DIGIT_VALUES(dbenv, set_lk_detect, get_lk_detect, u_int32_t, + lk_detect_values); + + /* Test DB_ENV->set_lk_max_lockers(), DB_ENV->get_lk_max_lockers(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_lk_max_lockers, get_lk_max_lockers, + u_int32_t, rand()); + + /* Test DB_ENV->set_lk_max_locks(), DB_ENV->get_lk_max_locks(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_lk_max_locks, get_lk_max_locks, + u_int32_t, rand()); + + /* Test DB_ENV->set_lk_max_objects(), DB_ENV->get_lk_max_objects(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_lk_max_objects, get_lk_max_objects, + u_int32_t, rand()); + + /* Test DB_ENV->set_lk_partitions(), DB_ENV->get_lk_partitions(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_lk_partitions, get_lk_partitions, + u_int32_t, rand()); + + /* Test DB_ENV->set_lk_tablesize(), DB_ENV->get_lk_tablesize(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_lk_tablesize, get_lk_tablesize, + u_int32_t, rand()); + + /* ==================== Log Configuration ===================== */ + + /* + * Test DB_ENV->log_set_config(), DB_ENV->log_get_config(). + * Direct I/O setting can only be performed if os supports it. + */ + if (__os_support_direct_io()) { + CHECK_ONOFF(dbenv, log_set_config, log_get_config, + log_configs, log_configs_cnt, int); + } else { + CHECK_ONOFF(dbenv, log_set_config, log_get_config, + &log_configs[1], log_configs_cnt - 1, int); + } + + /* Test DB_ENV->set_lg_bsize(), DB_ENV->get_lg_bsize(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_lg_bsize, get_lg_bsize, + u_int32_t, rand()); + + /* Test DB_ENV->set_lg_dir(), DB_ENV->get_lg_dir(). */ + CHECK_1_STR_VALUE(dbenv, set_lg_dir, get_lg_dir, "/logdir"); + + /* Test DB_ENV->set_lg_filemode(), DB_ENV->get_lg_filemode(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_lg_filemode, get_lg_filemode, + int, 0640); + + /* Test DB_ENV->set_lg_max(), DB_ENV->get_lg_max(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_lg_max, get_lg_max, u_int32_t, rand()); + + /* + * Test DB_ENV->set_lg_regionmax(), DB_ENV->get_lg_regionmax(). + * The value must be bigger than LG_BASE_REGION_SIZE(130000). + */ + CHECK_1_DIGIT_VALUE(dbenv, set_lg_regionmax, get_lg_regionmax, + u_int32_t, 12345678); + + /* ==================== Mpool Configuration ===================== */ + + /* + * Test DB_ENV->set_cache_max(), DB_ENV->get_cache_max(). + * The values could be ajusted, so we use specific values to avoid + * adjustment. + */ + CHECK_2_DIGIT_VALUES(dbenv, set_cache_max, get_cache_max, + u_int32_t, 3, u_int32_t, 131072); + + /* + * Test DB_ENV->set_cachesize() and DB_ENV->get_cachesize(). + * The values could be ajusted, so we use specific values to avoid + * adjustment. + */ + CHECK_3_DIGIT_VALUES(dbenv, set_cachesize, get_cachesize, + u_int32_t, 3, u_int32_t, 1048576, int, 5); + + /* Test DB_ENV->set_mp_max_openfd(), DB_ENV->get_mp_max_openfd(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_mp_max_openfd, get_mp_max_openfd, + int, rand()); + + /* Test DB_ENV->set_mp_max_write(), DB_ENV->get_mp_max_write(). */ + CHECK_2_DIGIT_VALUES(dbenv, set_mp_max_write, get_mp_max_write, + int, rand(), db_timeout_t , rand()); + + /* Test DB_ENV->set_mp_mmapsize(), DB_ENV->get_mp_mmapsize(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_mp_mmapsize, get_mp_mmapsize, + size_t, rand()); + + /* Test DB_ENV->set_mp_mtxcount(), DB_ENV->get_mp_mtxcount(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_mp_mtxcount, get_mp_mtxcount, + u_int32_t, rand()); + + /* + * Test DB_ENV->set_mp_pagesize(), DB_ENV->get_mp_pagesize(). + * The pagesize should be between 512 and 65536 and be power of two. + */ + CHECK_1_DIGIT_VALUE(dbenv, set_mp_pagesize, get_mp_pagesize, + u_int32_t, 65536); + + /* Test DB_ENV->set_mp_tablesize(), DB_ENV->get_mp_tablesize(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_mp_tablesize, get_mp_tablesize, + u_int32_t, rand()); + + /* ==================== Mutex Configuration ===================== */ + + /* + * Test DB_ENV->mutex_set_align(), DB_ENV->mutex_get_align(). + * The mutex align value should be power of two. + */ + CHECK_1_DIGIT_VALUE(dbenv, mutex_set_align, mutex_get_align, + u_int32_t, 32); + + /* Test DB_ENV->mutex_set_increment(), DB_ENV->mutex_get_increment(). */ + CHECK_1_DIGIT_VALUE(dbenv, mutex_set_increment, mutex_get_increment, + u_int32_t, rand()); + + /* + * Test DB_ENV->mutex_set_init(), DB_ENV->mutex_get_init(). + * We should make sure the init value is not bigger than max, otherwise, + * the returned max value will not be correct. + */ + CHECK_1_DIGIT_VALUE(dbenv, mutex_set_init, mutex_get_init, + u_int32_t, 131072); + + /* Test DB_ENV->mutex_set_max(), DB_ENV->mutex_get_max(). */ + CHECK_1_DIGIT_VALUE(dbenv, mutex_set_max, mutex_get_max, + u_int32_t, 503276); + + /* + * Test DB_ENV->mutex_set_tas_spins(), DB_ENV->mutex_get_tas_spins(). + * The value should be between 1 and 1000000 . + */ + CHECK_1_DIGIT_VALUE(dbenv, mutex_set_tas_spins, mutex_get_tas_spins, + u_int32_t, 1234); + + /* =================== Replication Configuration ==================== */ + + /* + * Test DB_ENV->rep_set_clockskew(), DB_ENV->rep_get_clockskew(). + * The fast_clock should be bigger than slow_clock. + */ + CHECK_2_DIGIT_VALUES(dbenv, rep_set_clockskew, rep_get_clockskew, + u_int32_t, 12345, u_int32_t, 11111); + + /* Test DB_ENV->rep_set_config(), DB_ENV->rep_get_config(). */ + CHECK_ONOFF(dbenv, rep_set_config, rep_get_config, rep_configs, + rep_configs_cnt - 2, int); + + /* + * Test DB_ENV->rep_set_limit(), DB_ENV->rep_get_limit(). + * We use specific values to avoid adjustment. + */ + CHECK_2_DIGIT_VALUES(dbenv, rep_set_limit, rep_get_limit, + u_int32_t, 2, u_int32_t, 2345678); + + /* Test DB_ENV->rep_set_nsites(), DB_ENV->rep_get_nsites(). */ + CHECK_1_DIGIT_VALUE(dbenv, rep_set_nsites, rep_get_nsites, + u_int32_t, rand()); + + /* Test DB_ENV->rep_set_priority(), DB_ENV->rep_get_priority(). */ + CHECK_1_DIGIT_VALUE(dbenv, rep_set_priority, rep_get_priority, + u_int32_t, rand()); + + /* + * Test DB_ENV->rep_set_request(), DB_ENV->rep_get_request(). + * The max should be bigger than min. + */ + CHECK_2_DIGIT_VALUES(dbenv, rep_set_request, rep_get_request, + u_int32_t, 100001, u_int32_t, 1234567); + + /* Test DB_ENV->rep_set_timeout(), DB_ENV->rep_get_timeout(). */ + CHECK_1_DIGIT_CONFIG_VALUES(dbenv, rep_set_timeout, rep_get_timeout, + rep_timeouts, u_int32_t); + + /* Test DB_ENV->set_tx_max(), DB_ENV->get_tx_max(). */ + CHECK_1_DIGIT_VALUE(dbenv, set_tx_max, get_tx_max, u_int32_t, rand()); + + /* + * Test DB_ENV->set_tx_timestamp(), DB_ENV->get_tx_timestamp(). + * We specify the timestamp to be one hour ago. + */ + tx_set_timestamp = time(NULL); + tx_set_timestamp -= 3600; + CuAssert(ct, "dbenv->set_tx_timestamp", + dbenv->set_tx_timestamp(dbenv, &tx_set_timestamp) == 0); + CuAssert(ct, "dbenv->get_tx_timestamp", + dbenv->get_tx_timestamp(dbenv, &tx_get_timestamp) == 0); + CuAssert(ct, "check tx timestamp", + tx_set_timestamp == tx_get_timestamp); + + CuAssert(ct, "dbenv->close", close_dbenv_handle(dbenv) == 0); + + /* + * The follwoing configurations are only valid for environment + * using replication manager API. + */ + CuAssert(ct, "db_env_create", create_dbenv_handle(&repmgr_dbenv) == 0); + + /* Test DB_ENV->rep_set_config(), DB_ENV->rep_get_config() */ + CHECK_ONOFF(repmgr_dbenv, rep_set_config, rep_get_config, + rep_configs + rep_configs_cnt - 3, 2, int); + + /* Test DB_ENV->rep_set_timeout(), DB_ENV->rep_get_timeout() */ + CHECK_1_DIGIT_CONFIG_VALUES(repmgr_dbenv, rep_set_timeout, + rep_get_timeout, repmgr_timeouts, u_int32_t); + + CuAssert(ct, "repmgr_dbenv->close", + close_dbenv_handle(repmgr_dbenv) == 0); + + return (0); +} + +int TestDbPreOpenSetterAndGetter(CuTest *ct) { + DB_ENV *dbenv; + DB *db, *env_db, *hash_db, *heap_db, *queue_db, *recno_db; + const char **part_dirs; + u_int32_t encrypt_flags, heap_set_bytes, heap_set_gbytes, + heap_get_bytes, heap_get_gbytes; + int nowait, onoff; + + /* ================ General and Btree Configuration =============== */ + + CuAssert(ct, "db_create", create_db_handle(&db, NULL) == 0); + + /* + * Test DB->set_cachesize(), DB->get_cachesize(). + * We use specific values to avoid adjustment. + */ + CHECK_3_DIGIT_VALUES(db, set_cachesize, get_cachesize, + u_int32_t, 3, u_int32_t, 1048576, int, 5); + + /* Test DB->set_encrypt(), DB->get_encrypt_flags(). */ + CuAssert(ct, "db->set_encrypt", + db->set_encrypt(db, passwd, DB_ENCRYPT_AES) == 0); + CuAssert(ct, "db->get_encrypt_flags", + db->get_encrypt_flags(db, &encrypt_flags) == 0); + CuAssert(ct, "check encrypt flags", encrypt_flags == DB_ENCRYPT_AES); + + /* Test DB->set_errfile(), DB->get_errfile(). */ + CHECK_1_PTR_VALUE_VOID(db, set_errfile, get_errfile, FILE, errfile); + + /* Test DB->set_errpfx(), DB->get_errpfx().*/ + CHECK_1_STR_VALUE_VOID(db, set_errpfx, get_errpfx, "dbp1"); + + /* Test DB->set_flags(), DB->get_flags(). */ + CHECK_FLAG_VALUE(db, set_flags, get_flags, + u_int32_t, DB_CHKSUM | DB_RECNUM | DB_REVSPLITOFF); + + /* Test DB->set_lk_exclusive(), DB->get_lk_exclusive(). */ + CuAssert(ct, "db->set_lk_exclusive", db->set_lk_exclusive(db, 1) == 0); + CuAssert(ct, "db->get_lk_exclusive", + db->get_lk_exclusive(db, &onoff, &nowait) == 0); + CuAssert(ct, "check lk_exclusive onoff", onoff == 1); + CuAssert(ct, "check lk_exclusive nowait", nowait == 1); + + /* + * Test DB->set_lorder(), DB->get_lorder(). + * The only acceptable values are 1234 and 4321. + */ + CHECK_1_DIGIT_VALUE(db, set_lorder, get_lorder, int, 1234); + CHECK_1_DIGIT_VALUE(db, set_lorder, get_lorder, int, 4321); + + /* Test DB->set_msgfile(), DB->get_msgfile(). */ + CHECK_1_PTR_VALUE_VOID(db, set_msgfile, get_msgfile, FILE, msgfile); + + /* + * Test DB->set_pagesize(), DB->get_pagesize(). + * The pagesize should be 512-55536, and be power of two. + */ + CHECK_1_DIGIT_VALUE(db, set_pagesize, get_pagesize, u_int32_t, 512); + CHECK_1_DIGIT_VALUE(db, set_pagesize, get_pagesize, u_int32_t, 65536); + + /* + * Test DB->set_bt_minkey(), DB->get_bt_minkey(). + * The minkey value should be 2 at least. + */ + CHECK_1_DIGIT_VALUE(db, set_bt_minkey, get_bt_minkey, u_int32_t, 17); + + CuAssert(ct, "db->close", close_db_handle(db) == 0); + + /* =================== Recno-only Configuration ===================== */ + + CuAssert(ct, "db_create", create_db_handle(&recno_db, NULL) == 0); + + /* Test DB->set_flags(), DB->get_flags(). */ + CHECK_FLAG_VALUE(recno_db, set_flags, get_flags, + u_int32_t, DB_RENUMBER | DB_SNAPSHOT); + + /* Test DB->set_re_delim(), DB->get_re_delim(). */ + CHECK_1_DIGIT_VALUE(recno_db, set_re_delim, get_re_delim, + int, rand()); + + /* Test DB->set_re_len(), DB->get_re_len(). */ + CHECK_1_DIGIT_VALUE(recno_db, set_re_len, get_re_len, + u_int32_t, rand()); + + /* Test DB->set_re_pad(), DB->get_re_pad(). */ + CHECK_1_DIGIT_VALUE(recno_db, set_re_pad, get_re_pad, int, rand()); + + /* Test DB->set_re_source(), DB->get_re_source(). */ + CHECK_1_STR_VALUE(recno_db, set_re_source, get_re_source, "re_source1"); + + CuAssert(ct, "recno_db->close", close_db_handle(recno_db) == 0); + + /* ==================== Hash-only Configuration ===================== */ + + CuAssert(ct, "db_create", create_db_handle(&hash_db, NULL) == 0); + + /* Test DB->set_flags(), DB->get_flags(). */ + CHECK_FLAG_VALUE(hash_db, set_flags, get_flags, + u_int32_t, DB_DUP | DB_DUPSORT | DB_REVSPLITOFF); + + /* Test DB->set_h_ffactor(), DB->get_h_ffactor(). */ + CHECK_1_DIGIT_VALUE(hash_db, set_h_ffactor, get_h_ffactor, + u_int32_t, rand()); + + /* Test DB->set_h_nelem(), DB->get_h_nelem(). */ + CHECK_1_DIGIT_VALUE(hash_db, set_h_nelem, get_h_nelem, + u_int32_t, rand()); + + CuAssert(ct, "hash_db->close", close_db_handle(hash_db) == 0); + + /* =================== Queue-only Configuration ===================== */ + + CuAssert(ct, "db_create", create_db_handle(&queue_db, NULL) == 0); + + /* Test DB->set_flags(), DB->get_flags(). */ + CHECK_FLAG_VALUE(queue_db, set_flags, get_flags, u_int32_t, DB_INORDER); + + /* Test DB->set_q_extentsize(), DB->get_q_extentsize(). */ + CHECK_1_DIGIT_VALUE(queue_db, set_q_extentsize, get_q_extentsize, + u_int32_t, rand()); + + CuAssert(ct, "queue_db->close", close_db_handle(queue_db) == 0); + + /* ==================== Heap-only Configuration ===================== */ + CuAssert(ct, "db_create", create_db_handle(&heap_db, NULL) == 0); + + /* Test DB->set_heapsize(), DB->get_heapsize(). */ + heap_set_gbytes = 3; + heap_set_bytes = 1048576; + heap_get_gbytes = heap_get_bytes = 0; + CuAssert(ct, "DB->set_heapsize", heap_db->set_heapsize(heap_db, + heap_set_gbytes, heap_set_bytes, 0) == 0); + CuAssert(ct, "DB->get_heapsize", heap_db->get_heapsize(heap_db, + &heap_get_gbytes, &heap_get_bytes) == 0); + CuAssert(ct, "Check heap gbytes", heap_set_gbytes == heap_get_gbytes); + CuAssert(ct, "Check heap bytes", heap_set_bytes == heap_get_bytes); + + /* Test DB->set_heap_regionsize(), DB->get_heap_regionsize(). */ + CHECK_1_DIGIT_VALUE(heap_db, set_heap_regionsize, get_heap_regionsize, + u_int32_t, rand()); + + CuAssert(ct, "heap_db->close", close_db_handle(heap_db) == 0); + + /* + * The following configurations require the database + * be opened in an environment. + */ + CuAssert(ct, "db_env_create", create_dbenv_handle(&dbenv) == 0); + CuAssert(ct, "dbenv->set_flags(DB_ENCRYPT)", dbenv->set_encrypt(dbenv, + passwd, DB_ENCRYPT_AES) == 0); + CuAssert(ct, "add_dirs_to_dbenv", + add_dirs_to_dbenv(dbenv, data_dirs) == 0); + CuAssert(ct, "dbenv->open", dbenv->open(dbenv, TEST_ENV, + DB_CREATE | DB_INIT_MPOOL | DB_INIT_TXN, 0644) == 0); + CuAssert(ct, "db_create", create_db_handle(&env_db, dbenv) == 0); + + /* Test DB->set_flags(), DB->get_flags(). */ + CHECK_FLAG_VALUE(env_db, set_flags, get_flags, + u_int32_t, DB_ENCRYPT | DB_TXN_NOT_DURABLE); + + /* Test DB->set_create_dir(), DB->get_create_dir(). */ + CHECK_1_STR_VALUE(env_db, set_create_dir, get_create_dir, data_dirs[0]); + + /* Test DB->set_partition_dirs(), DB->get_partition_dirs(). */ + CuAssert(ct, "env_db->set_partition_dirs", + env_db->set_partition_dirs(env_db, &data_dirs[1]) == 0); + CuAssert(ct, "env_db->get_partition_dirs", + env_db->get_partition_dirs(env_db, &part_dirs) == 0); + CuAssert(ct, "cmp_dirs", cmp_dirs(&data_dirs[1], part_dirs) == 0); + + CuAssert(ct, "env_db->close", close_db_handle(env_db) == 0); + CuAssert(ct, "dbenv->close", close_dbenv_handle(dbenv) == 0); + + return (0); +} + +int TestMpoolFilePreOpenSetterAndGetter(CuTest *ct) { + DB_ENV *dbenv; + DB_MPOOLFILE *mpf; + u_int8_t get_fileid[DB_FILE_ID_LEN], set_fileid[DB_FILE_ID_LEN]; + DB_CACHE_PRIORITY cache_priorities[] = { + DB_PRIORITY_VERY_LOW, + DB_PRIORITY_LOW, + DB_PRIORITY_DEFAULT, + DB_PRIORITY_HIGH, + DB_PRIORITY_VERY_HIGH + }; + u_int32_t mpool_flags; + size_t len; + DBT pgcookie_get, pgcookie_set; + + CuAssert(ct, "db_env_create", create_dbenv_handle(&dbenv) == 0); + CuAssert(ct, "dbenv->open", dbenv->open(dbenv, TEST_ENV, + DB_CREATE | DB_INIT_MPOOL, 0644) == 0); + CuAssert(ct, "dbenv->memp_fcreate", create_mp_handle(&mpf, dbenv) == 0); + + /* Test DB_MPOOLFILE->set_clear_len(), DB_MPOOLFILE->get_clear_len(). */ + CHECK_1_DIGIT_VALUE(mpf, set_clear_len, get_clear_len, + u_int32_t, rand()); + + /* Test DB_MPOOLFILE->set_fileid(), DB_MPOOLFILE->get_fileid(). */ + len = sizeof(DB_ENV) > DB_FILE_ID_LEN ? DB_FILE_ID_LEN : sizeof(DB_ENV); + memset(get_fileid, 0, DB_FILE_ID_LEN); + memcpy(set_fileid, dbenv, len); + CuAssert(ct, "mpf->set_fileid", mpf->set_fileid(mpf, set_fileid) == 0); + CuAssert(ct, "mpf->get_fileid", mpf->get_fileid(mpf, get_fileid) == 0); + CuAssert(ct, "check fileid", memcmp(set_fileid, get_fileid, len) == 0); + + /* Test DB_MPOOLFILE->set_flags(), DB_MPOOLFILE->get_flags(). */ + mpool_flags = 0; + CuAssert(ct, "mpf->set_flags", + mpf->set_flags(mpf, DB_MPOOL_NOFILE, 1) == 0); + CuAssert(ct, "mpf->set_flags", + mpf->set_flags(mpf, DB_MPOOL_UNLINK, 1) == 0); + CuAssert(ct, "mpf->get_flags", + mpf->get_flags(mpf, &mpool_flags) == 0); + CuAssert(ct, "check flags", + mpool_flags == (DB_MPOOL_NOFILE | DB_MPOOL_UNLINK)); + CuAssert(ct, "mpf->set_flags", + mpf->set_flags(mpf, DB_MPOOL_NOFILE, 0) == 0); + CuAssert(ct, "mpf->set_flags", + mpf->set_flags(mpf, DB_MPOOL_UNLINK, 0) == 0); + CuAssert(ct, "mpf->get_flags", mpf->get_flags(mpf, &mpool_flags) == 0); + CuAssert(ct, "check flags", mpool_flags == 0); + + /* Test DB_MPOOLFILE->set_ftype(), DB_MPOOLFILE->get_ftype(). */ + CHECK_1_DIGIT_VALUE(mpf, set_ftype, get_ftype, int, rand()); + + /* + * Test DB_MPOOLFILE->set_lsn_offset(), + * DB_MPOOLFILE->get_lsn_offset(). + */ + CHECK_1_DIGIT_VALUE(mpf, set_lsn_offset, get_lsn_offset, + int32_t, rand()); + + /* + * Test DB_MPOOLFILE->set_maxsize(), DB_MPOOLFILE->get_maxsize(). + * We use specific values to avoid adjustment. + */ + CHECK_2_DIGIT_VALUES(mpf, set_maxsize, get_maxsize, + u_int32_t, 2, u_int32_t, 1048576); + + /* Test DB_MPOOLFILE->set_pgcookie(), DB_MPOOLFILE->get_pgcookie(). */ + memset(&pgcookie_set, 0, sizeof(DBT)); + memset(&pgcookie_get, 0, sizeof(DBT)); + pgcookie_set.data = set_fileid; + pgcookie_set.size = DB_FILE_ID_LEN; + CuAssert(ct, "mpf->set_pgcookie", + mpf->set_pgcookie(mpf, &pgcookie_set) == 0); + CuAssert(ct, "mpf->get_pgcookie", + mpf->get_pgcookie(mpf, &pgcookie_get) == 0); + CuAssert(ct, "check pgcookie size", + pgcookie_get.size == pgcookie_set.size); + CuAssert(ct, "check pgcookie data", memcmp(pgcookie_get.data, + pgcookie_set.data, pgcookie_set.size) == 0); + + /* Test DB_MPOOLFILE->set_priority(), DB_MPOOLFILE->get_priority(). */ + CHECK_1_DIGIT_VALUES(mpf, set_priority, get_priority, DB_CACHE_PRIORITY, + cache_priorities); + + CuAssert(ct, "mpf->close", close_mp_handle(mpf) == 0); + CuAssert(ct, "dbenv->close", close_dbenv_handle(dbenv) == 0); + return (0); +} + +int TestSequencePreOpenSetterAndGetter(CuTest *ct) { + DB_ENV *dbenv; + DB *dbp; + DB_SEQUENCE *seq; + u_int32_t seq_flags; + + CuAssert(ct, "db_env_create", create_dbenv_handle(&dbenv) == 0); + CuAssert(ct, "dbenv->open", dbenv->open(dbenv, + TEST_ENV, DB_CREATE | DB_INIT_MPOOL, 0644) == 0); + CuAssert(ct, "db_create", create_db_handle(&dbp, dbenv) == 0); + CuAssert(ct, "dbp->open", dbp->open(dbp, + NULL, "seq.db", NULL, DB_BTREE, DB_CREATE, 0644) == 0); + CuAssert(ct, "db_sequence_create", + create_seq_handle(&seq, dbp) == 0); + + /* Test DB_SEQUENCE->set_cachesize(), DB_SEQUENCE->get_cachesize(). */ + CHECK_1_DIGIT_VALUE(seq, set_cachesize, get_cachesize, + u_int32_t, rand()); + + /* Test DB_SEQUENCE->set_flags(), DB_SEQUENCE->get_flags(). */ + seq_flags = 0; + CHECK_1_DIGIT_VALUE(seq, set_flags, get_flags, + u_int32_t, DB_SEQ_DEC | DB_SEQ_WRAP); + /* We make sure the DB_SEQ_DEC is cleared if we set DB_SEQ_INC. */ + CuAssert(ct, "seq->set_flags", seq->set_flags(seq, DB_SEQ_INC) == 0); + CuAssert(ct, "seq->get_flags", seq->get_flags(seq, &seq_flags) == 0); + CuAssert(ct, "check seq flags", + seq_flags == (DB_SEQ_INC | DB_SEQ_WRAP)); + + /* + * Test DB_SEQUENCE->set_range(), DB_SEQUENCE->get_range(). + * The max should be bigger than min. + */ + CHECK_2_DIGIT_VALUES(seq, set_range, get_range, + db_seq_t, 2, db_seq_t, 1048576); + + CuAssert(ct, "seq->close", close_seq_handle(seq) == 0); + CuAssert(ct, "dbp->close", close_db_handle(dbp) == 0); + CuAssert(ct, "dbenv->close", close_dbenv_handle(dbenv) == 0); + + return (0); +} + +static int create_dbenv_handle(DB_ENV **dbenvpp) { + int ret; + if ((ret = db_env_create(dbenvpp, 0)) == 0) + info.dbenvp = *dbenvpp; + return ret; +} + +static int close_dbenv_handle(DB_ENV *dbenvp) { + info.dbenvp = NULL; + return dbenvp->close(dbenvp, 0); +} + +static int create_db_handle(DB **dbpp, DB_ENV *dbenvp) { + int ret; + if ((ret = db_create(dbpp, dbenvp, 0)) == 0) + info.dbp = *dbpp; + return ret; +} + +static int close_db_handle(DB *dbp) { + info.dbp = NULL; + return dbp->close(dbp, 0); +} + +static int create_mp_handle(DB_MPOOLFILE **mpp, DB_ENV *dbenv) { + int ret; + if ((ret = dbenv->memp_fcreate(dbenv, mpp, 0)) == 0) + info.mp = *mpp; + return ret; +} + +static int close_mp_handle(DB_MPOOLFILE *mp) { + info.mp = NULL; + return mp->close(mp, 0); +} + +static int create_seq_handle(DB_SEQUENCE **seqpp, DB *dbp) { + int ret; + if ((ret = db_sequence_create(seqpp, dbp, 0)) == 0) + info.seqp = *seqpp; + return ret; +} + +static int close_seq_handle(DB_SEQUENCE *seqp) { + info.seqp = NULL; + return seqp->close(seqp, 0); +} + +static int add_dirs_to_dbenv(DB_ENV *dbenv, const char **dirs) { + int ret; + const char *dir; + + if (dirs == NULL) + return (0); + + ret = 0; + while (ret == 0 && (dir = *dirs++) != NULL) + ret = dbenv->add_data_dir(dbenv, dir); + return ret; +} + +/* + * Compare the directory list reprensented by dirs1 and dirs2. + * Both dirs1 and dirs2 use NULL pointer as terminator. + */ +static int cmp_dirs(const char **dirs1, const char **dirs2) { + int ret; + const char *dir1, *dir2; + + if (dirs1 == NULL || *dirs1 == NULL) { + if (dirs2 == NULL || *dirs2 == NULL) + return (0); + else + return (-1); + } else if (dirs2 == NULL || *dirs2 == NULL) + return (1); + + ret = 0; + while (ret == 0) { + dir1 = *dirs1++; + dir2 = *dirs2++; + if (dir1 == NULL || dir2 == NULL) + break; + ret = strcmp(dir1, dir2); + } + if (ret == 0) { + if (dir1 != NULL) + ret = 1; + else if (dir2 != NULL) + ret = -1; + } + + return ret; +} + diff --git a/test/c/suites/TestQueue.c b/test/c/suites/TestQueue.c index 70f0209b..7343d751 100644 --- a/test/c/suites/TestQueue.c +++ b/test/c/suites/TestQueue.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2002, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. * * A C test for the queue access method. * TODO: Make this more consistent with the CuTest harness. @@ -778,7 +778,8 @@ int TestQueue(CuTest *ct) { } if (!strcmp("sh_tailq", qfns[t].name)) { result = - sh_t_verify_TAILQ_LAST(list, ops[i].init); + sh_t_verify_TAILQ_LAST( + (struct sh_tq *)list, ops[i].init); } #ifdef VERBOSE printf("\ncase %d %s in %s init: \"%s\" desired: \"%s\" elem: \"%s\" insert: \"%s\"\n", @@ -814,8 +815,8 @@ int TestQueue(CuTest *ct) { break; } if (!strcmp("sh_tailq", op_names[ops[i].op])) { - result = sh_t_verify_TAILQ_LAST(list, - ops[i].final); + result = sh_t_verify_TAILQ_LAST( + (struct sh_tq *)list, ops[i].final); } if (result == 0) result = qfns[t].f_verify(list, ops[i].final); diff --git a/test/c/test_api_methods.c b/test/c/test_api_methods.c index 5998f556..8de7903e 100644 --- a/test/c/test_api_methods.c +++ b/test/c/test_api_methods.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2002, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. */ #include <sys/types.h> diff --git a/test/c/test_db185.c b/test/c/test_db185.c index 8670028b..accf0b89 100644 --- a/test/c/test_db185.c +++ b/test/c/test_db185.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2002, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. */ #include <sys/types.h> diff --git a/test/c/test_failchk.c b/test/c/test_failchk.c new file mode 100644 index 00000000..45f03e03 --- /dev/null +++ b/test/c/test_failchk.c @@ -0,0 +1,1078 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2014, 2015 Oracle and/or its affiliates. All rights reserved. + * + * $Id$ + */ + +#include <db_config.h> +#include <db.h> + +#include <sys/types.h> +#include <sys/time.h> + +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#ifdef _WIN32 +extern int getopt(int, char * const *, const char *); +#else +#include <unistd.h> +#endif + +/* This BDB internal routine calls gettimeofday() or clock_gettime() or ... */ +void __os_gettime __P((const ENV *, struct timespec *, int)); + +/* This exit status says "stop, look at the last run. something didn't work." */ +#define EXIT_TEST_ABORTED 101 + +/* + * "Shut that bloody compiler up!" + * + * Unused, or not-used-yet variable. We need to write and then read the + * variable, some compilers are too bloody clever by half. + */ +#define COMPQUIET(n, v) do { \ + (n) = (v); \ + (n) = (n); \ +} while (0) +#define UTIL_ANNOTATE_STRLEN 64 /* Length of argument to util_annotate(). */ + +/* + * NB: This application is written using POSIX 1003.1b-1993 pthreads + * interfaces, which may not be portable to your system. + */ +extern int sched_yield __P((void)); /* Pthread yield function. */ + +extern pthread_key_t TxnCommitMutex; + +int db_init __P((DB_ENV **, u_int32_t)); +void *deadlock __P((void *)); +void fatal __P((const char *, int)); +void onint __P((int)); +int main __P((int, char *[])); +void notice_event __P((DB_ENV *, u_int32_t, void *)); +int reader __P((int)); +void stats __P((void)); +void *failchk __P((void *)); +int say_is_alive __P((DB_ENV *, pid_t, db_threadid_t, unsigned)); +void *trickle __P((void *)); +void *tstart __P((void *)); +int usage __P((const char *)); +const char *util_annotate __P((const DB_ENV *, char *, size_t)); +void util_errcall __P((const DB_ENV *, const char *, const char *)); +void util_msgcall __P((const DB_ENV *, const char *)); +void word __P((void)); +int writer __P((int)); + +struct _statistics { + int aborted; /* Write. */ + int aborts; /* Read/write. */ + int adds; /* Write. */ + int deletes; /* Write. */ + int txns; /* Write. */ + int found; /* Read. */ + int notfound; /* Read. */ +} *perf; + +const char *Progname = "test_failchk"; /* Program name. */ + +#define DATABASE "access.db" /* Database name. */ +#define WORDLIST "../test/tcl/wordlist" /* Dictionary. */ + +/* + * We can seriously increase the number of collisions and transaction + * aborts by yielding the scheduler after every DB call. Specify the + * -p option to do this. + */ +time_t EndTime; +int Duration = 60; /* -d <#seconds to run> */ +char *Home = "TESTDIR"; /* -h */ +int Punish; /* -p */ +int Nlist = 1000; /* -n */ +int Nreaders = 4; /* -r */ +int StatsInterval = 60; /* -s #seconds between printout of statistics */ +int TxnInterval = 1000; /* -t #txns between printout of txn progress */ +int Verbose; /* -v */ +int Nwriters = 4; /* -w */ + + +int ActiveThreads = 0; +DB *Dbp; /* Database handle. */ +DB_ENV *DbEnv; /* Database environment. */ +int EnvOpenFlags = DB_CREATE | DB_THREAD | DB_REGISTER; +int Failchk = 0; /* Failchk found a dead process. */ +char **List; /* Word list. */ +int MutexDied = 0; /* #threads that tripped on a dead mutex */ +int Nthreads; /* Total number of non-failchk threads. */ +int Quit = 0; /* Interrupt handling flag. */ +int PrintStats = 0; /* -S print all stats before exit. */ + +/* + * test_failchk -- + * Test failchk in a simple threaded application of some numbers of readers + * and writers competing to read and update a set of words. + * A typical test scenario runs this programs several times concurrently, + * with different options: + * first with the -I option to clear out any home directory + * one or more instances with -f to activate the failchk thread + * one or more instance with neither -I nor -f, as minimally + * involved workers. + * + * + * + * + * Example UNIX shell script to run this program: + * test_failchk -I & # recreates the home TESTDIR directory + * test_failchk & # read & write in testdir, expecting w + * test_failchk -f & # read & write & call faichk to notice crashes + * randomly kill a process, leaving at least one other to discover the crash + * with a DB_ENV->failchk() call, which then allows the other processes + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int errno, optind; + DB_TXN *txnp; + pthread_t *tids; + sig_t sig; + int ch, do_failchk, i, init, pid, ret; + char syscmd[100]; + void *retp; + + setlinebuf(stdout); + setlinebuf(stderr); + txnp = NULL; + do_failchk = init = 0; + pid = getpid(); + while ((ch = getopt(argc, argv, "d:fh:Ipn:Rr:Ss:t:vw:x")) != EOF) + switch (ch) { + case 'd': + if ((Duration = atoi(optarg)) <= 0) + return (usage("-d <duration> must be >= 0")); + break; + case 'f': + do_failchk = 1; + break; + case 'h': + Home = optarg; + break; + case 'I': + init = 1; + break; + case 'n': + Nlist = atoi(optarg); + break; + case 'p': + Punish = 1; + break; + case 'R': + EnvOpenFlags &= ~DB_REGISTER; + break; + case 'r': + if ((Nreaders = atoi(optarg)) < 0) + return (usage("-r <readers> may not be <0")); + break; + case 'S': + PrintStats = 1; + break; + case 's': + if ((StatsInterval = atoi(optarg)) <= 0) + return (usage("-s <seconds> must be positive")); + break; + case 't': + if ((TxnInterval = atoi(optarg)) <= 0) + return (usage("-t <#txn> must be positive")); + break; + case 'v': + Verbose = 1; + break; + case 'w': + if ((Nwriters = atoi(optarg)) < 0) + return (usage("-r <writers> may not be <0")); + break; + case 'x': + EnvOpenFlags |= DB_RECOVER; + break; + case '?': + default: + return (usage("unknown option")); + } + printf("Running %d: %s ", pid, argv[0]); + for (i = 1; i != argc; i++) + printf("%s ", argv[i]); + printf("\n"); + argc -= optind; + argv += optind; + + if (init) { + /* Prevent accidentally rm -rf of a full path, etc. */ + if (Home[0] == '/' || Home[0] == '.') + return (usage("-I accepts only local path names (prevents rm -r /...)")); + snprintf(syscmd, sizeof(syscmd), + "rm -rf %s ; mkdir %s", Home, Home); + printf("Clearing out env with \"%s\"\n", syscmd); + if ((ret = system(syscmd)) != 0) { + fatal(syscmd, errno); + /* NOTREACHED */ + return (EXIT_TEST_ABORTED); + } + } + if (Nreaders + Nwriters == 0 && !do_failchk) + usage("Nothing specified to do?"); + + srand(pid | time(NULL)); + + /* + * Close down the env cleanly on an interrupt, except when running in + * the background. Catch SIGTERM to exit with a distinctive status. + */ + if ((sig = signal(SIGINT, onint)) != SIG_DFL) + (void)signal(SIGINT, sig); + (void)signal(SIGTERM, onint); + + /* Build the key list. */ + word(); + + /* Set when this run will end, if not interrupted. */ + if (StatsInterval > Duration) + StatsInterval = Duration; + time(&EndTime); + EndTime += Duration; + + /* Initialize the database environment. */ + if ((ret = db_init(&DbEnv, EnvOpenFlags)) != 0) + return (ret); + EnvOpenFlags &= ~DB_RECOVER; + + /* + * Create thread ID structures. It starts with the readers and writers, + * then the trickle, deadlock and possibly failchk threads. + */ + Nthreads = Nreaders + Nwriters + 2; + if ((tids = malloc((Nthreads + do_failchk) * sizeof(pthread_t))) == NULL) + fatal("malloc threads", errno); + + /* + * Create failchk thread first; it might be needed during db_create. + * Put it at the end of the threads array, so that in doesn't get in + * the way of the worker threads. + */ + if (do_failchk && + (ret = pthread_create(&tids[Nthreads], NULL, failchk, &i)) != 0) + fatal("pthread_create failchk", errno); + + /* Initialize the database. */ + if ((ret = db_create(&Dbp, DbEnv, 0)) != 0) { + DbEnv->err(DbEnv, ret, "db_create"); + (void)DbEnv->close(DbEnv, 0); + return (EXIT_TEST_ABORTED); + } + if ((ret = Dbp->set_pagesize(Dbp, 1024)) != 0) { + Dbp->err(Dbp, ret, "set_pagesize"); + goto err; + } + + if ((ret = DbEnv->txn_begin(DbEnv, NULL, &txnp, 0)) != 0) + fatal("txn_begin", ret); + if ((ret = Dbp->open(Dbp, txnp, + DATABASE, NULL, DB_BTREE, DB_CREATE | DB_THREAD, 0664)) != 0) { + Dbp->err(Dbp, ret, "%s: open", DATABASE); + goto err; + } else { + ret = txnp->commit(txnp, 0); + txnp = NULL; + if (ret != 0) + goto err; + } + + ActiveThreads = Nthreads; + + /* Create statistics structures, offset by 1. */ + if ((perf = calloc(Nreaders + Nwriters + 1, sizeof(*perf))) == NULL) + fatal("calloc statistics", errno); + + /* Create reader/writer threads. */ + for (i = 0; i < Nreaders + Nwriters; ++i) + if ((ret = pthread_create( + &tids[i], NULL, tstart, (void *)(uintptr_t)i)) != 0) + fatal("pthread_create", ret > 0 ? ret : errno); + + /* Create buffer pool trickle thread. */ + if (pthread_create(&tids[i], NULL, trickle, &i)) + fatal("pthread_create trickle thread", errno); + ++i; + + /* Create deadlock detector thread. */ + if (pthread_create(&tids[i], NULL, deadlock, &i)) + fatal("pthread_create deadlock thread", errno); + ++i; + + /* Wait for the worker, trickle and deadlock threads. */ + for (i = 0; i < Nthreads; ++i) { + printf("joining thread %d...\n", i); + if ((ret = pthread_join(tids[i], &retp)) != 0) + fatal("pthread_join", ret); + ActiveThreads--; + printf("join thread %d done, %d left\n", i, ActiveThreads); + } + + printf("Exiting\n"); + stats(); + + if (!Failchk) { +err: if (txnp != NULL) + ret = txnp->abort(txnp); + if (ret == 0 && Dbp != NULL) + ret = Dbp->close(Dbp, 0); + if (PrintStats) + DbEnv->stat_print(DbEnv, + DB_STAT_SUBSYSTEM | DB_STAT_ALL); + if (ret == 0 && DbEnv != NULL) + ret = DbEnv->close(DbEnv, 0); + } + + return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +} + +int +reader(id) + int id; +{ + DBT key, data; + int n, ret; + char buf[100]; + + /* + * DBT's must use local memory or malloc'd memory if the DB handle + * is accessed in a threaded fashion. + */ + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + + /* + * Read-only threads do not require transaction protection, unless + * there's a need for repeatable reads. + */ + while (!Quit) { + /* Pick a key at random, and look it up. */ + n = rand() % Nlist; + key.data = List[n]; + key.size = strlen(key.data); + + if (Verbose) + DbEnv->errx(DbEnv, "reader: %d: list entry %d", id, n); + + switch (ret = Dbp->get(Dbp, NULL, &key, &data, 0)) { + case DB_LOCK_DEADLOCK: /* Deadlock. */ + ++perf[id].aborts; + break; + case 0: /* Success. */ + ++perf[id].found; + free(data.data); + break; + case DB_NOTFOUND: /* Not found. */ + ++perf[id].notfound; + break; + default: + sprintf(buf, "reader %d: dbp->get of %s", + id, (char *)key.data); + fatal(buf, ret); + } + } + return (0); +} + +int +writer(id) + int id; +{ + DBT key, data; + DB_TXN *tid; + int n, ret; + char buf[100], dbuf[10000]; + + + /* + * DBT's must use local memory or malloc'd memory if the DB handle + * is accessed in a threaded fashion. + */ + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + data.data = dbuf; + data.ulen = sizeof(dbuf); + data.flags = DB_DBT_USERMEM; + + while (!Quit) { + /* Pick a random key. */ + n = rand() % Nlist; + key.data = List[n]; + key.size = strlen(key.data); + + if (Verbose) + DbEnv->errx(DbEnv, "writer: %d: list entry %d", id, n); + + /* Abort and retry. */ + if (0) { +retry: if ((ret = tid->abort(tid)) != 0) + fatal("DB_TXN->abort", ret); + ++perf[id].aborts; + ++perf[id].aborted; + } + + /* Begin the transaction. */ + if ((ret = DbEnv->txn_begin(DbEnv, NULL, &tid, 0)) != 0) + fatal("txn_begin", ret); + + /* + * Get the key. If it doesn't exist, add it. If it does + * exist, delete it. + */ + switch (ret = Dbp->get(Dbp, tid, &key, &data, 0)) { + case DB_LOCK_DEADLOCK: + goto retry; + case 0: + goto delete; + case DB_NOTFOUND: + goto add; + default: + snprintf(buf, sizeof(buf), + "writer %d: put %s", id, (char *)key.data); + fatal(buf, ret); + /* NOTREACHED */ + } + +delete: /* Delete the key. */ + switch (ret = Dbp->del(Dbp, tid, &key, 0)) { + case DB_LOCK_DEADLOCK: + goto retry; + case 0: + ++perf[id].deletes; + goto commit; + } + + snprintf(buf, sizeof(buf), "writer: %d: dbp->del", id); + fatal(buf, ret); + /* NOTREACHED */ + +add: /* Add the key. 1 data item in 30 is an overflow item. */ + data.size = 20 + rand() % 128; + if (rand() % 30 == 0) + data.size += 8192; + + switch (ret = Dbp->put(Dbp, tid, &key, &data, 0)) { + case DB_LOCK_DEADLOCK: + goto retry; + case 0: + ++perf[id].adds; + goto commit; + default: + snprintf(buf, sizeof(buf), "writer: %d: dbp->put", id); + fatal(buf, ret); + } + +commit: /* The transaction finished, commit it. */ + if ((ret = tid->commit(tid, 0)) != 0) + fatal("DB_TXN->commit", ret); + + /* + * Every time the thread completes many transactions, show + * our progress. + */ + if (++perf[id].txns % TxnInterval == 0) { + DbEnv->errx(DbEnv, +"writer: %2d: adds: %4d: deletes: %4d: aborts: %4d: txns: %4d", + id, perf[id].adds, perf[id].deletes, + perf[id].aborts, perf[id].txns); + } + + /* + * If this thread was aborted more than 5 times before + * the transaction finished, complain. + */ + if (perf[id].aborted > 5) { + DbEnv->errx(DbEnv, +"writer: %2d: adds: %4d: deletes: %4d: aborts: %4d: txns: %4d: ABORTED: %2d", + id, perf[id].adds, perf[id].deletes, + perf[id].aborts, perf[id].txns, perf[id].aborted); + } + perf[id].aborted = 0; + } + return (0); +} + +/* + * stats -- + * Display reader/writer thread statistics. To display the statistics + * for the mpool trickle or deadlock threads, use db_stat(1). + */ +void +stats() +{ + int id; + char *p, buf[8192]; + + p = buf + sprintf(buf, "-------------\n"); + for (id = 0; id < Nreaders + Nwriters;) + if (id++ < Nwriters) + p += sprintf(p, + "writer: %2d: adds: %4d: deletes: %4d: aborts: %4d: txns: %4d\n", + id, perf[id].adds, + perf[id].deletes, perf[id].aborts, perf[id].txns); + else + p += sprintf(p, + "reader: %2d: found: %5d: notfound: %5d: aborts: %4d\n", + id, perf[id].found, + perf[id].notfound, perf[id].aborts); + p += sprintf(p, "-------------\n"); + + printf("%s", buf); +} + +/* + * util_annotate - + * Obtain timestamp, thread id, etc; prepend to messages. + */ +const char * +util_annotate(dbenv, header, header_size) + const DB_ENV *dbenv; + char *header; + size_t header_size; +{ + struct timespec now; + db_threadid_t tid; + pid_t pid; +#ifdef HAVE_STRFTIME + struct tm *tm_p; +#ifdef HAVE_LOCALTIME_R + struct tm tm; +#endif +#endif + char idstr[DB_THREADID_STRLEN], tmstr[20]; + + if (dbenv == NULL) { + snprintf(idstr, sizeof(idstr), + "Pid/tid %d:%p", getpid(), (void *)pthread_self()); + } + else { + dbenv->thread_id((DB_ENV *)dbenv, &pid, &tid); + (void)dbenv->thread_id_string((DB_ENV *)dbenv, pid, tid, idstr); + } + + __os_gettime(dbenv == NULL ? NULL : dbenv->env, &now, 0); + /* Print the time readably if possible; else print seconds. */ +#ifdef HAVE_STRFTIME +#ifdef HAVE_LOCALTIME_R + tm_p = localtime_r(&now.tv_sec, &tm); +#else + tm_p = localtime(&now.tv_sec); +#endif + if (tm_p != NULL) + (void)strftime(tmstr, sizeof(tmstr), "%H:%M:%S", tm_p); + else +#endif + (void)snprintf(tmstr, sizeof(tmstr), "%lu", (u_long)now.tv_sec); + (void)snprintf(header, header_size, "%s.%06lu[%s]: ", + tmstr, (u_long)(now.tv_nsec / 1000), idstr); + + return (header); +} + +/* + * util_errcall - + * Annotate error messages with timestamp and thread id, + ??? + */ +void +util_errcall(dbenv, errpfx, msg) + const DB_ENV *dbenv; + const char *errpfx; + const char *msg; +{ + char header[UTIL_ANNOTATE_STRLEN]; + + util_annotate(dbenv, header, sizeof(header)); + if (errpfx == NULL) + errpfx = ""; + fprintf(stderr, "%s%s%s\n", header, errpfx, msg); + fflush(stderr); +} + +/* + * util_msgcall - + * Annotate messages with timestamp and thread id, + ??? + */ +void +util_msgcall(dbenv, msg) + const DB_ENV *dbenv; + const char *msg; +{ + char header[UTIL_ANNOTATE_STRLEN]; + + util_annotate(dbenv, header, sizeof(header)); + fprintf(stderr, "%s%s\n", header, msg); + fflush(stderr); +} + +/* + * db_init -- + * Initialize a TDS environment with failchk, running recovery if needed. + * The caller specifies additional flags such as: + * DB_THREAD | DB_CREATE | DB_REGISTER + */ +int +db_init(dbenvp, flags) + DB_ENV **dbenvp; + u_int32_t flags; +{ + DB_ENV *dbenv; + int ret; + + dbenv = *dbenvp; +retry: + if (dbenv != NULL) { + dbenv->errx(dbenv, "Closing existing environment"); + *dbenvp = NULL; + (void)dbenv->close(dbenv, 0); + } + if ((ret = db_env_create(&dbenv, 0)) != 0) { + fprintf(stderr, + "%s: db_env_create: %s\n", Progname, db_strerror(ret)); + return (EXIT_TEST_ABORTED); + } + (void)dbenv->set_event_notify(dbenv, notice_event); + if (Punish) + (void)dbenv->set_flags(dbenv, DB_YIELDCPU, 1); + + /* Use errcall and msgcall functions to include threadid, timestamp. */ + (void)dbenv->set_errcall(dbenv, util_errcall); + (void)dbenv->set_msgcall(dbenv, util_msgcall); + + /* Set a tiny cache. */ + (void)dbenv->set_cachesize(dbenv, 0, 100 * 1024, 0); + (void)dbenv->set_lg_max(dbenv, 200000); + (void)dbenv->set_verbose(dbenv, DB_VERB_RECOVERY, 1); + (void)dbenv->set_verbose(dbenv, DB_VERB_REGISTER, 1); + (void)dbenv->set_verbose(dbenv, DB_VERB_FILEOPS, 1); + (void)dbenv->set_verbose(dbenv, DB_VERB_FILEOPS_ALL, 1); + (void)dbenv->set_isalive(dbenv, say_is_alive); + (void)dbenv->set_thread_count(dbenv, 100); + + flags |= DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | + DB_FAILCHK; + if ((ret = dbenv->open(dbenv, Home, flags, 0)) == DB_RUNRECOVERY && + !(flags & DB_RECOVER)) { + dbenv->errx(dbenv, "About to run recovery in %s", Home); + flags |= DB_RECOVER; + goto retry; + } + if (ret != 0) { + dbenv->err(dbenv, ret, "Could not open environment in %s", Home); + *dbenvp = NULL; + (void)dbenv->close(dbenv, 0); + return (EXIT_TEST_ABORTED); + } + if (flags & DB_RECOVER && !(flags & DB_REGISTER)) { + DB_TXN_STAT *txn_stat; + if ((ret = dbenv->txn_stat(dbenv, &txn_stat, 0)) != 0) + fatal("txn_stat after recovery failed", ret); + if (txn_stat->st_nbegins != 0) + fatal("txn_stat found txns, did recovery run?", 0); + free(txn_stat); + } + *dbenvp = dbenv; + dbenv->errx(dbenv, "Opened environment in %s", Home); + if (!Verbose) { + (void)dbenv->set_verbose(dbenv, DB_VERB_RECOVERY, 0); + (void)dbenv->set_verbose(dbenv, DB_VERB_REGISTER, 0); + (void)dbenv->set_verbose(dbenv, DB_VERB_FILEOPS, 0); + (void)dbenv->set_verbose(dbenv, DB_VERB_FILEOPS_ALL, 0); + } + return (0); +} + +/* + * tstart -- + * Thread start function for readers and writers. + */ +void * +tstart(arg) + void *arg; +{ + pthread_t tid; + u_int id; + + id = (uintptr_t)arg + 1; + + tid = pthread_self(); + + if (id <= (u_int)Nwriters) { + printf("write thread %d starting: tid: %lx\n", id, (u_long)tid); + fflush(stdout); + writer(id); + } else { + printf("read thread %d starting: tid: %lx\n", id, (u_long)tid); + fflush(stdout); + reader(id); + } + + /* NOTREACHED */ + return (NULL); +} + +/* + * deadlock -- + * Thread start function for DB_ENV->lock_detect. + */ +void * +deadlock(arg) + void *arg; +{ + struct timeval t; + pthread_t tid; + int err; + + tid = pthread_self(); + + printf("deadlock thread starting: tid: %lx\n", (u_long)tid); + fflush(stdout); + + t.tv_sec = 0; + t.tv_usec = 100000; + while (!Quit) { + err = DbEnv->lock_detect(DbEnv, 0, DB_LOCK_YOUNGEST, NULL); + if (err != 0) { + DbEnv->err(DbEnv, err, "lock_detect failed"); + break; + } + + /* Check every 100ms. */ + (void)select(0, NULL, NULL, NULL, &t); + } + + printf("%d deadlock thread exiting\n", getpid()); + COMPQUIET(arg, NULL); + return (NULL); +} + +/* + * trickle -- + * Thread start function for memp_trickle. + */ +void * +trickle(arg) + void *arg; +{ + pthread_t tid; + time_t now, then; + int err, wrote; + + time(&now); + then = now; + tid = pthread_self(); + + printf("trickle thread starting: tid: %lx\n", (u_long)tid); + fflush(stdout); + + while (!Quit) { + err = DbEnv->memp_trickle(DbEnv, 10, &wrote); + if (err != 0) { + DbEnv->err(DbEnv, err, "trickle failed"); + break; + } + if (Verbose) + fprintf(stderr, "trickle: wrote %d\n", wrote); + + /* + * The trickle thread prints statistics every few seconds. + * It also checks whether it is time to quit. + */ + time(&now); + if (now - then >= StatsInterval) { + stats(); + then = now; + if (now > EndTime) { + printf("trickle: ending time reached @ %s", + ctime(&now)); + Quit = 1; + } + } + if (wrote == 0) { + sleep(1); + sched_yield(); + } + } + printf("%d trickle thread exiting\n", getpid()); + + COMPQUIET(arg, NULL); + return (NULL); +} + +/* + * failchk -- + * Thread start function for failchk. + */ +void * +failchk(arg) + void *arg; +{ + DB_ENV *failenv; + pthread_t tid; + time_t now; + int err; + + tid = pthread_self(); + failenv = NULL; + + if (db_init(&failenv, 0) != 0) { + fprintf(stderr, "failchk: environment open failed!\n"); + exit(EXIT_TEST_ABORTED); + } + (void)failenv->set_errpfx(failenv, "(failchk) "); + + failenv->errx(failenv, "starting tid: %lx\n", (u_long)tid); + + while (!Quit) { + if ((err = failenv->failchk(failenv, 0)) != 0) { + Failchk = 1; + failenv->err(failenv, err, "failchk() returned"); + system("db_stat -Neh TESTDIR|egrep 'Creation|Failure'"); + /* + * Tell all threads to quit, then check that + * the environment can be reopened. + */ + Quit = 1; + if (0) { + do { + sleep(10); + if ((err = failenv->failchk(failenv, 0)) != 0) + failenv->err(failenv, err, + "redo failchk with %d left returns", + ActiveThreads); + } while (ActiveThreads > 0); + fprintf(stderr, + "failchk: reopening %s with recovery\n", Home); + (void)db_init(&failenv, DB_RECOVER); + fprintf(stderr, "failchk: reopened %s\n", Home); + } + system("db_stat -eh TESTDIR | egrep 'Creat|Failure'"); + fprintf(stderr, "failchk thread exiting\n"); + exit(0); + } + sleep(1); + } + + (void)failenv->close(failenv, 0); + now = time(NULL); + printf("failchk() thread returning @ %s", ctime(&now)); + + COMPQUIET(arg, NULL); + return (NULL); +} + +/* + * word -- + * Build the dictionary word list + */ +void +word() +{ + FILE *fp; + int cnt; + char buf[256], *nl; + + if ((fp = fopen(WORDLIST, "r")) == NULL) + fatal(WORDLIST, errno); + + if ((List = malloc(Nlist * sizeof(char *))) == NULL) + fatal("malloc word list", errno); + + for (cnt = 0; cnt < Nlist; ++cnt) { + if (fgets(buf, sizeof(buf), fp) == NULL) + break; + /* Newlines in the data make for confusing messages. */ + if ((nl = strrchr(buf, '\n')) != NULL) + *nl = '\0'; + if ((List[cnt] = strdup(buf)) == NULL) + fatal("strdup word", errno); + } + Nlist = cnt; /* In case nlist was larger than the word list. */ +} + +/* + * fatal -- + * Report a fatal error and quit. + */ +void +fatal(msg, err) + const char *msg; + int err; +{ + char buf[1000]; + char header[UTIL_ANNOTATE_STRLEN]; + int ret; + + snprintf(buf, sizeof(buf), "pid %d %s: %s", getpid(), Progname, msg); + if (err != 0) + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ": %s%s", + db_strerror(err), MutexDied > 0 ? + " after seeing DB_EVENT_MUTEX_DIED" : + (Failchk > 0 ? "after seeing a failchk panic" : "")); + /* fatal errors are 'ok' if a failchk-detected panic has occurred. */ + ret = (Failchk == 0 && MutexDied == 0 ? EXIT_TEST_ABORTED : EXIT_FAILURE); + util_annotate(NULL, header, sizeof(header)); + fprintf(stderr, "%s%s\n", header, buf); + + exit(ret); + + /* NOTREACHED */ +} + +/* + * usage -- + * Usage message. + */ +int +usage(msg) + const char *msg; +{ + (void)fprintf(stderr, + "usage: %s " + "[-p<Punish>] [-v<Verbose>] [-R<avoid DB_REGISTER>] [-f<run a failchk thread>] [-I <initialize>] [-x <always recover>]\n\t" + "[-d <duration(%d seconds)]\n\t" + "[-h <home(%s)>]\n\t" + "[-n <words(%d)>]\n\t" + "[-r <#readers %d>]\n\t" + "[-s <statistics interval>]\n\t" + "[-t <txn progress interval>]\n\t" + "[-w <#writers %d>]\n\t%s\n", + Progname, Duration, Home, Nlist, Nreaders, Nwriters, msg); + return (EXIT_TEST_ABORTED); +} + +/* + * onint -- + * Interrupt signal handler. + */ +void +onint(signo) + int signo; +{ + Quit = 1; + fflush(stdout); + printf("pid %d sees signal %d\n", getpid(), signo); + if (signo == SIGTERM) { + printf("pid %d exiting due to SIGTERM\n", getpid()); + exit(EXIT_FAILURE + 1); + } +} + +/* + * notice_event -- + * Display the details of events. + */ +void notice_event(dbenv, event, info) + DB_ENV *dbenv; + u_int32_t event; + void *info; +{ +#ifdef DB_EVENT_MUTEX_DIED + DB_EVENT_MUTEX_DIED_INFO *mtxdied; +#endif +#ifdef DB_EVENT_FAILCHK_PANIC + DB_EVENT_FAILCHK_INFO *crashed; +#endif + switch (event) { + case DB_EVENT_PANIC: + dbenv->err(dbenv, *(int *)info, "Notification: panic"); + break; + case DB_EVENT_REG_ALIVE: + dbenv->errx(dbenv, "DB_EVENT_REG_ALIVE pid %lu is still alive.", + (u_long)(*(pid_t *)info)); + break; + case DB_EVENT_REG_PANIC: + dbenv->err(dbenv, *(int *)info, "Notification: register panic"); + break; +#ifdef DB_EVENT_MUTEX_DIED + case DB_EVENT_MUTEX_DIED: + mtxdied = info; + dbenv->errx(dbenv, "Notification: dead mutex: %.*s", + sizeof(mtxdied->desc), mtxdied->desc); + MutexDied++; + break; +#endif +#ifdef DB_EVENT_FAILCHK_PANIC + case DB_EVENT_FAILCHK_PANIC: + crashed = info; + dbenv->errx(dbenv, "Notification: panic \"%s\" after: %s", + db_strerror(crashed->error), crashed->symptom); + Failchk++; + break; +#endif + default: + dbenv->errx(dbenv, "Event %u info %p", event, info); + break; + } +} + +/* + * say_is_alive - failchk is_alive function + * + * Return 1 if the pid is alive, else 0 (dead). + * + * We are alive, so is our parent and any other process to which we can + * send a null signal (*IX) or get info about (Win32). Posix doesn't provide + * a true way to detect whether another process' threads are active. + */ +int +say_is_alive(dbenv, pid, tid, flags) + DB_ENV *dbenv; + pid_t pid; + db_threadid_t tid; + u_int32_t flags; +{ +#ifdef DB_WIN32 + HANDLE proc; + LONG exitCode; + int still_active; +#else + int ret; +#endif + +#ifdef DB_WIN32 + /* OpenProcess() may return a handle to a dead process, so check + * whether the process exists as well as whether it has just + * recently exited. This fails to detect processes that + * explicitly return STILL_ACTIVE as its exit status. + */ + if ((proc = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid)) != 0) { + still_active = GetExitCodeProcess(proc, &exitCode) != 0 && + exitCode == STILL_ACTIVE; + CloseHandle(proc); + if (still_active) + return (1); + } +#else + /* Self, parent, and processes findable by kill are alive. */ + if (pid == getpid() || pid == getppid() || + kill(pid, 0) == 0 || (ret = errno) != ESRCH) + return (1); + ret = errno; +#endif + dbenv->err(dbenv, ret, "is-alive probe for pid %d", pid); + COMPQUIET(dbenv, NULL); + COMPQUIET(tid, 0); + COMPQUIET(flags, 0); + + return (0); +} + diff --git a/test/c/test_log_verify.c b/test/c/test_log_verify.c index 2fac4ee5..e9338d42 100644 --- a/test/c/test_log_verify.c +++ b/test/c/test_log_verify.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2009, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ |