summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/csuite/Makefile.am12
-rw-r--r--test/csuite/wt2909_checkpoint_integrity/main.c666
-rw-r--r--test/csuite/wt3120_filesys/main.c99
-rw-r--r--test/csuite/wt3135_search_near_collator/main.c360
-rw-r--r--test/csuite/wt3184_dup_index_collator/main.c168
-rw-r--r--test/suite/test_cursor_random.py49
-rw-r--r--test/suite/test_reconfig04.py2
-rw-r--r--test/suite/test_sweep01.py7
-rw-r--r--test/utility/misc.c2
-rw-r--r--test/utility/test_util.h2
10 files changed, 1362 insertions, 5 deletions
diff --git a/test/csuite/Makefile.am b/test/csuite/Makefile.am
index a96492c1e71..e2b72532703 100644
--- a/test/csuite/Makefile.am
+++ b/test/csuite/Makefile.am
@@ -37,9 +37,21 @@ noinst_PROGRAMS += test_wt2834_join_bloom_fix
test_wt2853_perf_SOURCES = wt2853_perf/main.c
noinst_PROGRAMS += test_wt2853_perf
+test_wt2909_checkpoint_integrity_SOURCES = wt2909_checkpoint_integrity/main.c
+noinst_PROGRAMS += test_wt2909_checkpoint_integrity
+
test_wt2999_join_extractor_SOURCES = wt2999_join_extractor/main.c
noinst_PROGRAMS += test_wt2999_join_extractor
+test_wt3120_filesys_SOURCES = wt3120_filesys/main.c
+noinst_PROGRAMS += test_wt3120_filesys
+
+test_wt3135_search_near_collator_SOURCES = wt3135_search_near_collator/main.c
+noinst_PROGRAMS += test_wt3135_search_near_collator
+
+test_wt3184_dup_index_collator_SOURCES = wt3184_dup_index_collator/main.c
+noinst_PROGRAMS += test_wt3184_dup_index_collator
+
# Run this during a "make check" smoke test.
TESTS = $(noinst_PROGRAMS)
LOG_COMPILER = $(TEST_WRAPPER)
diff --git a/test/csuite/wt2909_checkpoint_integrity/main.c b/test/csuite/wt2909_checkpoint_integrity/main.c
new file mode 100644
index 00000000000..ddf249fb406
--- /dev/null
+++ b/test/csuite/wt2909_checkpoint_integrity/main.c
@@ -0,0 +1,666 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "test_util.h"
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+/*
+ * JIRA ticket reference: WT-2909
+ * Test case description:
+ *
+ * This test attempts to check the integrity of checkpoints by injecting
+ * failures (by means of a custom file system) and then trying to recover. To
+ * insulate the top level program from various crashes that may occur when
+ * injecting failures, the "populate" code runs in another process, and is
+ * expected to sometimes fail. Then the top level program runs recovery (with
+ * the normal file system) and checks the results. Any failure at the top level
+ * indicates a checkpoint integrity problem.
+ *
+ * Each subtest uses the same kind of schema and data, the only variance is
+ * when the faults are injected. At the moment, this test only injects during
+ * checkpoints, and only injects write failures. It varies in the number of
+ * successful writes that occur before an injected failure (during a checkpoint
+ * operation), this can be indicated with "-o N". When N is not specified, the
+ * test attempts to find the optimal range of N for testing. Clearly when N is
+ * large, then the checkpoint may be successfully written, and the data
+ * represented by the checkpoint will be fully present. When N is small,
+ * nothing of interest is written and no data is present. To find the sweet
+ * spot where interesting failures occur, the test does a binary search to find
+ * the approximate N that divides the "small" and "large" cases. This is not
+ * strictly deterministic, a given N may give different results on different
+ * runs. But approximate optimal N can be determined, allowing a series of
+ * additional tests clustered around this N.
+ *
+ * The data is stored in two tables, one having indices. Both tables have
+ * the same keys and are updated with the same key in a single transaction.
+ *
+ * Failure mode:
+ * If one table is out of step with the other, that is detected as a failure at
+ * the top level. If an index is missing values (or has extra values), that is
+ * likewise a failure at the top level. If the tables or the home directory
+ * cannot be opened, that is a top level error. The tables must be present
+ * as an initial checkpoint is done without any injected fault.
+ */
+
+/*
+ * This program does not run on Windows. The non-portable aspects at minimum
+ * are fork/exec the use of environment variables (used by fail_fs), and file
+ * name and build locations of dynamically loaded libraries.
+ */
+#define BIG_SIZE (1024 * 10)
+#define BIG_CONTENTS "<Big String Contents>"
+#define MAX_ARGS 20
+#define MAX_OP_RANGE 1000
+#define STDERR_FILE "stderr.txt"
+#define STDOUT_FILE "stdout.txt"
+#define TESTS_PER_OP_VALUE 3
+#define VERBOSE_PRINT 10000
+
+static int check_results(TEST_OPTS *, uint64_t *);
+static void check_values(WT_CURSOR *, int, int, int, char *);
+static int create_big_string(char **);
+static void cursor_count_items(WT_CURSOR *, uint64_t *);
+static void disable_failures(void);
+static void enable_failures(uint64_t, uint64_t);
+static void generate_key(uint64_t, int *);
+static void generate_value(uint32_t, uint64_t, char *, int *, int *, int *,
+ char **);
+static void run_check_subtest(TEST_OPTS *, const char *, uint64_t, bool,
+ uint64_t *);
+static void run_check_subtest_range(TEST_OPTS *, const char *, bool);
+static int run_process(TEST_OPTS *, const char *, char *[], int *);
+static int subtest_main(int, char *[], bool);
+static void subtest_populate(TEST_OPTS *, bool);
+int main(int, char *[]);
+
+extern int __wt_optind;
+
+#define WT_FAIL_FS_LIB "../../ext/test/fail_fs/.libs/libwiredtiger_fail_fs.so"
+
+/*
+ * check_results --
+ * Check all the tables and verify the results.
+ */
+static int
+check_results(TEST_OPTS *opts, uint64_t *foundp)
+{
+ WT_CURSOR *maincur, *maincur2, *v0cur, *v1cur, *v2cur;
+ WT_SESSION *session;
+ uint64_t count, idxcount, nrecords;
+ uint32_t rndint;
+ int key, key_got, ret, v0, v1, v2;
+ char *bigref, *big;
+
+ testutil_check(create_big_string(&bigref));
+ nrecords = opts->nrecords;
+ testutil_check(wiredtiger_open(opts->home, NULL,
+ "create,log=(enabled)", &opts->conn));
+ testutil_check(
+ opts->conn->open_session(opts->conn, NULL, NULL, &session));
+
+ testutil_check(session->open_cursor(session, "table:subtest", NULL,
+ NULL, &maincur));
+ testutil_check(session->open_cursor(session, "table:subtest2", NULL,
+ NULL, &maincur2));
+ testutil_check(session->open_cursor(session, "index:subtest:v0", NULL,
+ NULL, &v0cur));
+ testutil_check(session->open_cursor(session, "index:subtest:v1", NULL,
+ NULL, &v1cur));
+ testutil_check(session->open_cursor(session, "index:subtest:v2", NULL,
+ NULL, &v2cur));
+
+ count = 0;
+ while ((ret = maincur->next(maincur)) == 0) {
+ testutil_check(maincur2->next(maincur2));
+ testutil_check(maincur2->get_key(maincur2, &key_got));
+ testutil_check(maincur2->get_value(maincur2, &rndint));
+
+ generate_key(count, &key);
+ generate_value(rndint, count, bigref, &v0, &v1, &v2, &big);
+ testutil_assert(key == key_got);
+
+ /* Check the key/values in main table. */
+ testutil_check(maincur->get_key(maincur, &key_got));
+ testutil_assert(key == key_got);
+ check_values(maincur, v0, v1, v2, big);
+
+ /* Check the values in the indices. */
+ v0cur->set_key(v0cur, v0);
+ testutil_check(v0cur->search(v0cur));
+ check_values(v0cur, v0, v1, v2, big);
+ v1cur->set_key(v1cur, v1);
+ testutil_check(v1cur->search(v1cur));
+ check_values(v1cur, v0, v1, v2, big);
+ v2cur->set_key(v2cur, v2);
+ testutil_check(v2cur->search(v2cur));
+ check_values(v2cur, v0, v1, v2, big);
+
+ count++;
+ if (count % VERBOSE_PRINT == 0 && opts->verbose)
+ printf("checked %" PRIu64 "/%" PRIu64 "\n", count,
+ nrecords);
+ }
+ if (count % VERBOSE_PRINT != 0 && opts->verbose)
+ printf("checked %" PRIu64 "/%" PRIu64 "\n", count, nrecords);
+
+ /*
+ * Always expect at least one entry, as populate does a
+ * checkpoint after the first insert.
+ */
+ testutil_assert(count > 0);
+ testutil_assert(ret == WT_NOTFOUND);
+ testutil_assert(maincur2->next(maincur2) == WT_NOTFOUND);
+ cursor_count_items(v0cur, &idxcount);
+ testutil_assert(count == idxcount);
+ cursor_count_items(v1cur, &idxcount);
+ testutil_assert(count == idxcount);
+ cursor_count_items(v2cur, &idxcount);
+ testutil_assert(count == idxcount);
+
+ testutil_check(opts->conn->close(opts->conn, NULL));
+ opts->conn = NULL;
+
+ free(bigref);
+ *foundp = count;
+ return (0);
+}
+
+/*
+ * check_values --
+ * Check that the values in the cursor match the given values.
+ */
+static void
+check_values(WT_CURSOR *cursor, int v0, int v1, int v2, char *big)
+{
+ int v0_got, v1_got, v2_got;
+ char *big_got;
+
+ testutil_check(cursor->get_value(cursor, &v0_got, &v1_got, &v2_got,
+ &big_got));
+ testutil_assert(v0 == v0_got);
+ testutil_assert(v1 == v1_got);
+ testutil_assert(v2 == v2_got);
+ testutil_assert(strcmp(big, big_got) == 0);
+}
+
+/*
+ * create_big_string --
+ * Create and fill the "reference" big array.
+ */
+static int create_big_string(char **bigp)
+{
+ size_t i, mod;
+ char *big;
+
+ if ((big = malloc(BIG_SIZE + 1)) == NULL)
+ return (ENOMEM);
+ mod = strlen(BIG_CONTENTS);
+ for (i = 0; i < BIG_SIZE; i++) {
+ big[i] = BIG_CONTENTS[i % mod];
+ }
+ big[BIG_SIZE] = '\0';
+ *bigp = big;
+ return (0);
+}
+
+/*
+ * cursor_count_items --
+ * Count the number of items in the table by traversing
+ * through the cursor.
+ */
+static void
+cursor_count_items(WT_CURSOR *cursor, uint64_t *countp)
+{
+ int ret;
+
+ *countp = 0;
+
+ testutil_check(cursor->reset(cursor));
+ while ((ret = cursor->next(cursor)) == 0)
+ (*countp)++;
+ testutil_assert(ret == WT_NOTFOUND);
+}
+
+/*
+ * disable_failures --
+ * Disable failures in the fail file system.
+ */
+static void
+disable_failures(void)
+{
+ testutil_check(setenv("WT_FAIL_FS_ENABLE", "0", 1));
+}
+
+/*
+ * enable_failures --
+ * Enable failures in the fail file system.
+ */
+static void
+enable_failures(uint64_t allow_writes, uint64_t allow_reads)
+{
+ char value[100];
+
+ testutil_check(setenv("WT_FAIL_FS_ENABLE", "1", 1));
+ snprintf(value, sizeof(value), "%" PRIu64, allow_writes);
+ testutil_check(setenv("WT_FAIL_FS_WRITE_ALLOW", value, 1));
+ snprintf(value, sizeof(value), "%" PRIu64, allow_reads);
+ testutil_check(setenv("WT_FAIL_FS_READ_ALLOW", value, 1));
+}
+
+/*
+ * generate_key --
+ * Generate a key used by the "subtest" and "subtest2" tables.
+ */
+static void
+generate_key(uint64_t i, int *keyp)
+{
+ *keyp = (int)i;
+}
+
+/*
+ * generate_value --
+ * Generate values for the "subtest" table.
+ */
+static void
+generate_value(uint32_t rndint, uint64_t i, char *bigref,
+ int *v0p, int *v1p, int *v2p, char **bigp)
+{
+ *v0p = (int)(i * 7);
+ *v1p = (int)(i * 10007);
+ *v2p = (int)(i * 100000007);
+ *bigp = &bigref[rndint % BIG_SIZE];
+}
+
+/*
+ * run_check_subtest --
+ * Run the subtest with the given parameters and check the results.
+ */
+static void
+run_check_subtest(TEST_OPTS *opts, const char *debugger, uint64_t nops,
+ bool close_test, uint64_t *nresultsp)
+{
+ int estatus, narg;
+ char rarg[20], sarg[20], *subtest_args[MAX_ARGS];
+
+ narg = 0;
+ if (debugger != NULL) {
+ subtest_args[narg++] = (char *)debugger;
+ subtest_args[narg++] = (char *)"--";
+ }
+
+ subtest_args[narg++] = (char *)opts->progname;
+ /* "subtest" must appear before arguments */
+ if (close_test)
+ subtest_args[narg++] = (char *)"subtest_close";
+ else
+ subtest_args[narg++] = (char *)"subtest";
+ subtest_args[narg++] = (char *)"-h";
+ subtest_args[narg++] = opts->home;
+ subtest_args[narg++] = (char *)"-v"; /* subtest is always verbose */
+ subtest_args[narg++] = (char *)"-p";
+ subtest_args[narg++] = (char *)"-o";
+ snprintf(sarg, sizeof(sarg), "%" PRIu64, nops);
+ subtest_args[narg++] = sarg; /* number of operations */
+ subtest_args[narg++] = (char *)"-n";
+ snprintf(rarg, sizeof(rarg), "%" PRIu64, opts->nrecords);
+ subtest_args[narg++] = rarg; /* number of records */
+ subtest_args[narg++] = NULL;
+ testutil_assert(narg <= MAX_ARGS);
+ if (opts->verbose)
+ printf("running a separate process with %" PRIu64
+ " operations until fail...\n", nops);
+ testutil_clean_work_dir(opts->home);
+ testutil_check(run_process(
+ opts, debugger != NULL ? debugger : opts->progname,
+ subtest_args, &estatus));
+ if (opts->verbose)
+ printf("process exited %d\n", estatus);
+
+ /*
+ * Verify results in parent process.
+ */
+ testutil_check(check_results(opts, nresultsp));
+}
+
+/*
+ * run_check_subtest_range --
+ *
+ * Run successive tests via binary search that determines the approximate
+ * crossover point between when data is recoverable or not. Once that is
+ * determined, run the subtest in a range near that crossover point.
+ *
+ * The theory is that running at the crossover point will tend to trigger
+ * "interesting" failures at the borderline when the checkpoint is about to,
+ * or has, succeeded. If any of those failures creates a WT home directory
+ * that cannot be recovered, the top level test will fail.
+ */
+static void
+run_check_subtest_range(TEST_OPTS *opts, const char *debugger, bool close_test)
+{
+ uint64_t cutoff, high, low, mid, nops, nresults;
+ int i;
+ bool got_failure, got_success;
+
+ if (opts->verbose)
+ printf("Determining best range of operations until failure, "
+ "with close_test %s.\n",
+ (close_test ? "enabled" : "disabled"));
+
+ run_check_subtest(opts, debugger, 1, close_test, &cutoff);
+ low = 0;
+ high = MAX_OP_RANGE;
+ mid = (low + high) / 2;
+ while (mid != low) {
+ run_check_subtest(opts, debugger, mid, close_test,
+ &nresults);
+ if (nresults > cutoff)
+ high = mid;
+ else
+ low = mid;
+ mid = (low + high) / 2;
+ }
+ /*
+ * mid is the number of ops that is the crossover point.
+ * Run some tests near that point to try to trigger weird
+ * failures. If mid is too low or too high, it indicates
+ * there is a fundamental problem with the test.
+ */
+ testutil_assert(mid > 1 && mid < MAX_OP_RANGE - 1);
+ if (opts->verbose)
+ printf("Retesting around %" PRIu64 " operations.\n",
+ mid);
+
+ got_failure = false;
+ got_success = false;
+ for (nops = mid - 10; nops < mid + 10; nops++) {
+ for (i = 0; i < TESTS_PER_OP_VALUE; i++) {
+ run_check_subtest(opts, debugger, nops,
+ close_test, &nresults);
+ if (nresults > cutoff)
+ got_failure = true;
+ else
+ got_success = true;
+ }
+ }
+ /*
+ * Check that it really ran with a crossover point.
+ */
+ testutil_assert(got_failure);
+ testutil_assert(got_success);
+}
+
+/*
+ * run_process --
+ * Run a program with arguments, wait until it completes.
+ */
+static int
+run_process(TEST_OPTS *opts, const char *prog, char *argv[], int *status)
+{
+ int pid;
+ char **arg;
+
+ if (opts->verbose) {
+ printf("running: ");
+ for (arg = argv; *arg != NULL; arg++)
+ printf("%s ", *arg);
+ printf("\n");
+ }
+ if ((pid = fork()) == 0) {
+ (void)execv(prog, argv);
+ testutil_die(errno, "%s", prog);
+ } else if (pid < 0)
+ return (errno);
+
+ (void)waitpid(pid, status, 0);
+ return (0);
+}
+
+/*
+ * subtest_main --
+ * The main program for the subtest
+ */
+static int
+subtest_main(int argc, char *argv[], bool close_test)
+{
+ TEST_OPTS *opts, _opts;
+ WT_SESSION *session;
+ char config[1024], filename[1024];
+ struct rlimit rlim;
+
+ if (testutil_disable_long_tests())
+ return (0);
+ opts = &_opts;
+ memset(opts, 0, sizeof(*opts));
+ memset(&rlim, 0, sizeof(rlim));
+
+ /* No core files during fault injection tests. */
+ testutil_check(setrlimit(RLIMIT_CORE, &rlim));
+ testutil_check(testutil_parse_opts(argc, argv, opts));
+ testutil_make_work_dir(opts->home);
+
+ /* Redirect stderr, stdout. */
+ sprintf(filename, "%s/%s", opts->home, STDERR_FILE);
+ testutil_assert(freopen(filename, "a", stderr) != NULL);
+ sprintf(filename, "%s/%s", opts->home, STDOUT_FILE);
+ testutil_assert(freopen(filename, "a", stdout) != NULL);
+ snprintf(config, sizeof(config),
+ "create,cache_size=250M,log=(enabled),"
+ "transaction_sync=(enabled,method=none),extensions=("
+ WT_FAIL_FS_LIB
+ "=(early_load,config={environment=true,verbose=true})]");
+
+ testutil_check(wiredtiger_open(opts->home, NULL, config, &opts->conn));
+ testutil_check(
+ opts->conn->open_session(opts->conn, NULL, NULL, &session));
+
+ testutil_check(session->create(session, "table:subtest",
+ "key_format=i,value_format=iiiS,"
+ "columns=(id,v0,v1,v2,big)"));
+
+ testutil_check(session->create(session, "table:subtest2",
+ "key_format=i,value_format=i"));
+
+ testutil_check(session->create(session, "index:subtest:v0",
+ "columns=(v0)"));
+ testutil_check(session->create(session, "index:subtest:v1",
+ "columns=(v1)"));
+ testutil_check(session->create(session, "index:subtest:v2",
+ "columns=(v2)"));
+
+ testutil_check(session->close(session, NULL));
+
+ subtest_populate(opts, close_test);
+
+ testutil_cleanup(opts);
+
+ return (0);
+}
+
+/*
+ * This macro is used as a substitute for testutil_check, except that it is
+ * aware of when a failure may be expected due to the effects of the fail_fs.
+ * This macro is used only in subtest_populate(), it uses local variables.
+ */
+#define CHECK(expr) { \
+ int _ret; \
+ _ret = expr; \
+ if (_ret != 0) { \
+ if (!failmode || \
+ (_ret != WT_RUN_RECOVERY && _ret != EIO)) { \
+ fprintf(stderr, " BAD RETURN %d for \"%s\"\n", \
+ _ret, #expr); \
+ testutil_check(_ret); \
+ } else \
+ failed = true; \
+ } \
+}
+
+/*
+ * subtest_populate --
+ * Populate the tables.
+ */
+static void
+subtest_populate(TEST_OPTS *opts, bool close_test)
+{
+ WT_CURSOR *maincur, *maincur2;
+ WT_RAND_STATE rnd;
+ WT_SESSION *session;
+ uint64_t i, nrecords;
+ uint32_t rndint;
+ int key, v0, v1, v2;
+ char *big, *bigref;
+ bool failed, failmode;
+
+ failmode = failed = false;
+ __wt_random_init_seed(NULL, &rnd);
+ CHECK(create_big_string(&bigref));
+ nrecords = opts->nrecords;
+
+ CHECK(opts->conn->open_session(
+ opts->conn, NULL, NULL, &session));
+
+ CHECK(session->open_cursor(session, "table:subtest", NULL,
+ NULL, &maincur));
+
+ CHECK(session->open_cursor(session, "table:subtest2", NULL,
+ NULL, &maincur2));
+
+ for (i = 0; i < nrecords && !failed; i++) {
+ rndint = __wt_random(&rnd);
+ generate_key(i, &key);
+ generate_value(rndint, i, bigref, &v0, &v1, &v2, &big);
+ CHECK(session->begin_transaction(session, NULL));
+ maincur->set_key(maincur, key);
+ maincur->set_value(maincur, v0, v1, v2, big);
+ CHECK(maincur->insert(maincur));
+
+ maincur2->set_key(maincur2, key);
+ maincur2->set_value(maincur2, rndint);
+ CHECK(maincur2->insert(maincur2));
+ CHECK(session->commit_transaction(session, NULL));
+
+ if (i == 0)
+ /*
+ * Force an initial checkpoint, that helps to
+ * distinguish a clear failure from just not running
+ * long enough.
+ */
+ CHECK(session->checkpoint(session, NULL));
+
+ if ((i + 1) % VERBOSE_PRINT == 0 && opts->verbose)
+ printf(" %" PRIu64 "/%" PRIu64 "\n",
+ (i + 1), nrecords);
+ /* Attempt to isolate the failures to checkpointing. */
+ if (i == (nrecords/100)) {
+ enable_failures(opts->nops, 1000000);
+ failmode = true; /* CHECK should expect failures. */
+ CHECK(session->checkpoint(session, NULL));
+ failmode = false;
+ disable_failures();
+ if (failed && opts->verbose)
+ printf("checkpoint failed (expected).\n");
+ }
+ }
+
+ /*
+ * Closing handles after an extreme fail is likely to cause
+ * cascading failures (or crashes), so recommended practice is
+ * to immediately exit. We're interested in testing both with
+ * and without the recommended practice.
+ */
+ if (failed) {
+ if (!close_test) {
+ fprintf(stderr, "exit early.\n");
+ exit(0);
+ } else
+ fprintf(stderr, "closing after failure.\n");
+ }
+
+ free(bigref);
+ CHECK(maincur->close(maincur));
+ CHECK(maincur2->close(maincur2));
+ CHECK(session->close(session, NULL));
+}
+
+/*
+ * main --
+ * The main program for the test. When invoked with "subtest"
+ * argument, run the subtest. Otherwise, run a separate process
+ * for each needed subtest, and check the results.
+ */
+int
+main(int argc, char *argv[])
+{
+ TEST_OPTS *opts, _opts;
+ uint64_t nresults;
+ const char *debugger;
+
+ if (testutil_disable_long_tests())
+ return (0);
+ opts = &_opts;
+ memset(opts, 0, sizeof(*opts));
+ debugger = NULL;
+
+ testutil_check(testutil_parse_opts(argc, argv, opts));
+ argc -= __wt_optind;
+ argv += __wt_optind;
+ if (opts->nrecords == 0)
+ opts->nrecords = 50000;
+
+ while (argc > 0) {
+ if (strcmp(argv[0], "subtest") == 0)
+ return (subtest_main(argc, argv, false));
+ else if (strcmp(argv[0], "subtest_close") == 0)
+ return (subtest_main(argc, argv, true));
+ else if (strcmp(argv[0], "gdb") == 0)
+ debugger = "/usr/bin/gdb";
+ else
+ testutil_assert(false);
+ argc--;
+ argv++;
+ }
+ if (opts->verbose) {
+ printf("Number of operations until failure: %" PRIu64
+ " (change with -o N)\n", opts->nops);
+ printf("Number of records: %" PRIu64
+ " (change with -n N)\n", opts->nrecords);
+ }
+ if (opts->nops == 0) {
+ run_check_subtest_range(opts, debugger, false);
+ run_check_subtest_range(opts, debugger, true);
+ } else
+ run_check_subtest(opts, debugger, opts->nops,
+ opts->nrecords, &nresults);
+
+ testutil_clean_work_dir(opts->home);
+ testutil_cleanup(opts);
+
+ return (0);
+}
diff --git a/test/csuite/wt3120_filesys/main.c b/test/csuite/wt3120_filesys/main.c
new file mode 100644
index 00000000000..09dce624066
--- /dev/null
+++ b/test/csuite/wt3120_filesys/main.c
@@ -0,0 +1,99 @@
+/*-
+ * Public Domain 2014-2017 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "test_util.h"
+
+/*
+ * JIRA ticket reference: WT-3120
+ * Test case description: A simple file system extension built into
+ * a shared library.
+ * Failure mode: Loading the file system and closing the connection
+ * is enough to evoke the failure. This test does slightly more
+ * than that.
+ */
+
+#define WT_FAIL_FS_LIB "../../ext/test/fail_fs/.libs/libwiredtiger_fail_fs.so"
+
+int
+main(int argc, char *argv[])
+{
+ TEST_OPTS *opts, _opts;
+ WT_CURSOR *cursor;
+ WT_SESSION *session;
+ char *kstr, *vstr;
+ char buf[1024];
+
+ opts = &_opts;
+ memset(opts, 0, sizeof(*opts));
+ testutil_check(testutil_parse_opts(argc, argv, opts));
+ testutil_make_work_dir(opts->home);
+
+ snprintf(buf, sizeof(buf),
+ "create,extensions=(" WT_FAIL_FS_LIB "=(early_load=true))");
+ testutil_check(wiredtiger_open(opts->home, NULL, buf, &opts->conn));
+ testutil_check(
+ opts->conn->open_session(opts->conn, NULL, NULL, &session));
+ testutil_check(session->create(session, opts->uri,
+ "key_format=S,value_format=S"));
+
+ testutil_check(session->open_cursor(session, opts->uri, NULL, NULL,
+ &cursor));
+ cursor->set_key(cursor, "a");
+ cursor->set_value(cursor, "0");
+ testutil_check(cursor->insert(cursor));
+ cursor->set_key(cursor, "b");
+ cursor->set_value(cursor, "1");
+ testutil_check(cursor->insert(cursor));
+ testutil_check(cursor->close(cursor));
+ testutil_check(session->close(session, NULL));
+
+ /* Force to disk and re-open. */
+ testutil_check(opts->conn->close(opts->conn, NULL));
+ testutil_check(wiredtiger_open(opts->home, NULL, NULL, &opts->conn));
+
+ testutil_check(
+ opts->conn->open_session(opts->conn, NULL, NULL, &session));
+ testutil_check(session->open_cursor(session, opts->uri, NULL, NULL,
+ &cursor));
+ testutil_check(cursor->next(cursor));
+ testutil_check(cursor->get_key(cursor, &kstr));
+ testutil_check(cursor->get_value(cursor, &vstr));
+ testutil_assert(strcmp(kstr, "a") == 0);
+ testutil_assert(strcmp(vstr, "0") == 0);
+ testutil_check(cursor->next(cursor));
+ testutil_check(cursor->get_key(cursor, &kstr));
+ testutil_check(cursor->get_value(cursor, &vstr));
+ testutil_assert(strcmp(kstr, "b") == 0);
+ testutil_assert(strcmp(vstr, "1") == 0);
+ testutil_assert(cursor->next(cursor) == WT_NOTFOUND);
+ testutil_check(cursor->close(cursor));
+ testutil_check(session->close(session, NULL));
+ printf("Success\n");
+
+ testutil_cleanup(opts);
+ return (EXIT_SUCCESS);
+}
diff --git a/test/csuite/wt3135_search_near_collator/main.c b/test/csuite/wt3135_search_near_collator/main.c
new file mode 100644
index 00000000000..8783034a7d8
--- /dev/null
+++ b/test/csuite/wt3135_search_near_collator/main.c
@@ -0,0 +1,360 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "test_util.h"
+
+/*
+ * JIRA ticket reference: WT-3135
+ * Test case description: Each set of data is ordered and contains
+ * five elements (0-4). We insert elements 1 and 3, and then do
+ * search_near and search for each element. For each set of data, we perform
+ * these tests first using a custom collator, and second using a custom collator
+ * and extractor. In each case there are index keys having variable length.
+ * Failure mode: In the reported test case, the custom compare routine is
+ * given a truncated key to compare, and the unpack functions return errors
+ * because the truncation appeared in the middle of a key.
+ */
+
+#define TEST_ENTRY_COUNT 5
+typedef const char *TEST_SET[TEST_ENTRY_COUNT];
+static TEST_SET test_sets[] = {
+ { "0", "01", "012", "0123", "01234" },
+ { "A", "B", "C", "D", "E" },
+ { "5", "54", "543", "5432", "54321" },
+ { "54321", "5433", "544", "55", "6" }
+};
+#define TEST_SET_COUNT (sizeof(test_sets) / sizeof(test_sets[0]))
+
+static bool
+item_str_equal(WT_ITEM *item, const char *str)
+{
+ return (item->size == strlen(str) + 1 && strncmp((char *)item->data,
+ str, item->size) == 0);
+}
+
+static int
+compare_int(int64_t a, int64_t b)
+{
+ return (a < b ? -1 : (a > b ? 1 : 0));
+}
+
+static int
+index_compare_primary(WT_PACK_STREAM *s1, WT_PACK_STREAM *s2, int *cmp)
+{
+ int64_t pkey1, pkey2;
+ int rc1, rc2;
+
+ rc1 = wiredtiger_unpack_int(s1, &pkey1);
+ rc2 = wiredtiger_unpack_int(s2, &pkey2);
+
+ if (rc1 == 0 && rc2 == 0)
+ *cmp = compare_int(pkey1, pkey2);
+ else if (rc1 != 0 && rc2 != 0)
+ *cmp = 0;
+ else if (rc1 != 0)
+ *cmp = -1;
+ else
+ *cmp = 1;
+ return (0);
+}
+
+static int
+index_compare_S(WT_COLLATOR *collator, WT_SESSION *session,
+ const WT_ITEM *key1, const WT_ITEM *key2, int *cmp)
+{
+ WT_PACK_STREAM *s1, *s2;
+ const char *skey1, *skey2;
+
+ (void)collator;
+
+ testutil_check(wiredtiger_unpack_start(session, "Si", key1->data,
+ key1->size, &s1));
+ testutil_check(wiredtiger_unpack_start(session, "Si", key2->data,
+ key2->size, &s2));
+
+ testutil_check(wiredtiger_unpack_str(s1, &skey1));
+ testutil_check(wiredtiger_unpack_str(s2, &skey2));
+
+ if ((*cmp = strcmp(skey1, skey2)) == 0)
+ testutil_check(index_compare_primary(s1, s2, cmp));
+
+ testutil_check(wiredtiger_pack_close(s1, NULL));
+ testutil_check(wiredtiger_pack_close(s2, NULL));
+
+ return (0);
+}
+
+static int
+index_compare_u(WT_COLLATOR *collator, WT_SESSION *session,
+ const WT_ITEM *key1, const WT_ITEM *key2, int *cmp)
+{
+ WT_ITEM skey1, skey2;
+ WT_PACK_STREAM *s1, *s2;
+
+ (void)collator;
+
+ testutil_check(wiredtiger_unpack_start(session, "ui", key1->data,
+ key1->size, &s1));
+ testutil_check(wiredtiger_unpack_start(session, "ui", key2->data,
+ key2->size, &s2));
+
+ testutil_check(wiredtiger_unpack_item(s1, &skey1));
+ testutil_check(wiredtiger_unpack_item(s2, &skey2));
+
+ if ((*cmp = strcmp(skey1.data, skey2.data)) == 0)
+ testutil_check(index_compare_primary(s1, s2, cmp));
+
+ testutil_check(wiredtiger_pack_close(s1, NULL));
+ testutil_check(wiredtiger_pack_close(s2, NULL));
+
+ return (0);
+}
+
+static int
+index_extractor_u(WT_EXTRACTOR *extractor, WT_SESSION *session,
+ const WT_ITEM *key, const WT_ITEM *value, WT_CURSOR *result_cursor)
+{
+ (void)extractor;
+ (void)session;
+ (void)key;
+
+ result_cursor->set_key(result_cursor, value);
+ return result_cursor->insert(result_cursor);
+}
+
+static WT_COLLATOR collator_S = { index_compare_S, NULL, NULL };
+static WT_COLLATOR collator_u = { index_compare_u, NULL, NULL };
+static WT_EXTRACTOR extractor_u = { index_extractor_u, NULL, NULL };
+
+/*
+ * Check search() and search_near() using the test string indicated
+ * by test_index.
+ */
+static void
+search_using_str(WT_CURSOR *cursor, TEST_SET test_set, int test_index)
+{
+ int exact, ret;
+ const char *result;
+ const char *str_01, *str_0123, *test_str;
+
+ testutil_assert(test_index >= 0 && test_index <= 4);
+ str_01 = test_set[1];
+ str_0123 = test_set[3];
+ test_str = test_set[test_index];
+
+ cursor->set_key(cursor, test_str);
+ testutil_check(cursor->search_near(cursor, &exact));
+ testutil_check(cursor->get_key(cursor, &result));
+
+ if (test_index == 0)
+ testutil_assert(strcmp(result, str_01) == 0 && exact > 0);
+ else if (test_index == 1)
+ testutil_assert(strcmp(result, str_01) == 0 && exact == 0);
+ else if (test_index == 2)
+ testutil_assert((strcmp(result, str_0123) == 0 && exact > 0) ||
+ (strcmp(result, str_01) == 0 && exact < 0));
+ else if (test_index == 3)
+ testutil_assert(strcmp(result, str_0123) == 0 && exact == 0);
+ else if (test_index == 4)
+ testutil_assert(strcmp(result, str_0123) == 0 && exact < 0);
+
+ cursor->set_key(cursor, test_str);
+ ret = cursor->search(cursor);
+
+ if (test_index == 0 || test_index == 2 || test_index == 4)
+ testutil_assert(ret == WT_NOTFOUND);
+ else if (test_index == 1 || test_index == 3)
+ testutil_assert(ret == 0);
+}
+
+/*
+ * Check search() and search_near() using the test string indicated
+ * by test_index against a table containing a variable sized item.
+ */
+static void
+search_using_item(WT_CURSOR *cursor, TEST_SET test_set, int test_index)
+{
+ WT_ITEM item;
+ size_t testlen;
+ int exact, ret;
+ const char *str_01, *str_0123, *test_str;
+
+ testutil_assert(test_index >= 0 && test_index <= 4);
+ str_01 = test_set[1];
+ str_0123 = test_set[3];
+ test_str = test_set[test_index];
+
+ testlen = strlen(test_str) + 1;
+ item.data = test_str;
+ item.size = testlen;
+ cursor->set_key(cursor, &item);
+ testutil_check(cursor->search_near(cursor, &exact));
+ testutil_check(cursor->get_key(cursor, &item));
+
+ if (test_index == 0)
+ testutil_assert(item_str_equal(&item, str_01) && exact > 0);
+ else if (test_index == 1)
+ testutil_assert(item_str_equal(&item, str_01) && exact == 0);
+ else if (test_index == 2)
+ testutil_assert((item_str_equal(&item, str_0123) && exact > 0)
+ || (item_str_equal(&item, str_01) && exact < 0));
+ else if (test_index == 3)
+ testutil_assert(item_str_equal(&item, str_0123) && exact == 0);
+ else if (test_index == 4)
+ testutil_assert(item_str_equal(&item, str_0123) && exact < 0);
+
+ item.data = test_str;
+ item.size = testlen;
+ cursor->set_key(cursor, &item);
+ ret = cursor->search(cursor);
+
+ if (test_index == 0 || test_index == 2 || test_index == 4)
+ testutil_assert(ret == WT_NOTFOUND);
+ else if (test_index == 1 || test_index == 3)
+ testutil_assert(ret == 0);
+}
+
+/*
+ * For each set of data, perform tests.
+ */
+static void
+test_one_set(WT_SESSION *session, TEST_SET set)
+{
+ WT_CURSOR *cursor;
+ WT_ITEM item;
+ int32_t i;
+
+ /*
+ * Part 1: Using a custom collator, insert some elements
+ * and verify results from search_near.
+ */
+
+ testutil_check(session->create(session,
+ "table:main", "key_format=i,value_format=S,columns=(k,v)"));
+ testutil_check(session->create(session,
+ "index:main:def_collator", "columns=(v)"));
+ testutil_check(session->create(session,
+ "index:main:custom_collator",
+ "columns=(v),collator=collator_S"));
+
+ /* Insert only elements #1 and #3. */
+ testutil_check(session->open_cursor(session,
+ "table:main", NULL, NULL, &cursor));
+ cursor->set_key(cursor, 0);
+ cursor->set_value(cursor, set[1]);
+ testutil_check(cursor->insert(cursor));
+ cursor->set_key(cursor, 1);
+ cursor->set_value(cursor, set[3]);
+ testutil_check(cursor->insert(cursor));
+ testutil_check(cursor->close(cursor));
+
+ /* Check all elements in def_collator index. */
+ testutil_check(session->open_cursor(session,
+ "index:main:def_collator", NULL, NULL, &cursor));
+ for (i = 0; i < (int32_t)TEST_ENTRY_COUNT; i++)
+ search_using_str(cursor, set, i);
+ testutil_check(cursor->close(cursor));
+
+ /* Check all elements in custom_collator index */
+ testutil_check(session->open_cursor(session,
+ "index:main:custom_collator", NULL, NULL, &cursor));
+ for (i = 0; i < (int32_t)TEST_ENTRY_COUNT; i++)
+ search_using_str(cursor, set, i);
+ testutil_check(cursor->close(cursor));
+
+ /*
+ * Part 2: perform the same checks using a custom collator and
+ * extractor.
+ */
+ testutil_check(session->create(session,
+ "table:main2", "key_format=i,value_format=u,columns=(k,v)"));
+
+ testutil_check(session->create(session, "index:main2:idx_w_coll",
+ "key_format=u,collator=collator_u,extractor=extractor_u"));
+
+ testutil_check(session->open_cursor(session,
+ "table:main2", NULL, NULL, &cursor));
+
+ memset(&item, 0, sizeof(item));
+ item.size = strlen(set[1]) + 1;
+ item.data = set[1];
+ cursor->set_key(cursor, 1);
+ cursor->set_value(cursor, &item);
+ testutil_check(cursor->insert(cursor));
+
+ item.size = strlen(set[3]) + 1;
+ item.data = set[3];
+ cursor->set_key(cursor, 3);
+ cursor->set_value(cursor, &item);
+ testutil_check(cursor->insert(cursor));
+
+ testutil_check(cursor->close(cursor));
+
+ testutil_check(session->open_cursor(session,
+ "index:main2:idx_w_coll", NULL, NULL, &cursor));
+ for (i = 0; i < (int32_t)TEST_ENTRY_COUNT; i++)
+ search_using_item(cursor, set, i);
+ testutil_check(cursor->close(cursor));
+
+ testutil_check(session->drop(session, "table:main", NULL));
+ testutil_check(session->drop(session, "table:main2", NULL));
+}
+
+int
+main(int argc, char *argv[])
+{
+ TEST_OPTS *opts, _opts;
+ WT_SESSION *session;
+ size_t i;
+
+ opts = &_opts;
+ memset(opts, 0, sizeof(*opts));
+ testutil_check(testutil_parse_opts(argc, argv, opts));
+ testutil_make_work_dir(opts->home);
+
+ testutil_check(wiredtiger_open(opts->home, NULL, "create",
+ &opts->conn));
+ testutil_check(
+ opts->conn->open_session(opts->conn, NULL, NULL, &session));
+
+ /* Add any collators and extractors used by tests */
+ testutil_check(opts->conn->add_collator(opts->conn, "collator_S",
+ &collator_S, NULL));
+ testutil_check(opts->conn->add_collator(opts->conn, "collator_u",
+ &collator_u, NULL));
+ testutil_check(opts->conn->add_extractor(opts->conn, "extractor_u",
+ &extractor_u, NULL));
+
+ for (i = 0; i < TEST_SET_COUNT; i++) {
+ printf("test set %" WT_SIZET_FMT "\n", i);
+ test_one_set(session, test_sets[i]);
+ }
+
+ testutil_check(session->close(session, NULL));
+ testutil_cleanup(opts);
+ return (EXIT_SUCCESS);
+}
diff --git a/test/csuite/wt3184_dup_index_collator/main.c b/test/csuite/wt3184_dup_index_collator/main.c
new file mode 100644
index 00000000000..bcefd2f1a3b
--- /dev/null
+++ b/test/csuite/wt3184_dup_index_collator/main.c
@@ -0,0 +1,168 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "test_util.h"
+
+/*
+ * JIRA ticket reference: WT-3184
+ * Test case description: Each set of data is ordered and contains
+ * five elements (0-4). We insert elements 1 and 3, and then do
+ * search_near and search for each element. For each set of data, we perform
+ * these tests first using a custom collator, and second using a custom collator
+ * and extractor. In each case there are index keys having variable length.
+ * Failure mode: In the reported test case, the custom compare routine is
+ * given a truncated key to compare, and the unpack functions return errors
+ * because the truncation appeared in the middle of a key.
+ */
+
+static int
+compare_int(int32_t a, int32_t b)
+{
+ return (a < b ? -1 : (a > b ? 1 : 0));
+}
+
+static int32_t
+item_to_int(WT_ITEM *item)
+{
+ testutil_assert(item->size == sizeof(int32_t));
+ return (*(int32_t *)item->data);
+}
+
+static int
+compare_int_items(WT_ITEM *itema, WT_ITEM *itemb)
+{
+ testutil_assert(itema->size == sizeof(int32_t));
+ testutil_assert(itemb->size == sizeof(int32_t));
+ return (compare_int(item_to_int(itema), item_to_int(itemb)));
+}
+
+static void
+print_int_item(const char *str, const WT_ITEM *item)
+{
+ if (item->size > 0) {
+ testutil_assert(item->size == sizeof(int32_t));
+ printf("%s%" PRId32, str, *(int32_t *)item->data);
+ } else
+ printf("%s<empty>", str);
+}
+
+static int
+index_compare(WT_COLLATOR *collator, WT_SESSION *session,
+ const WT_ITEM *key1, const WT_ITEM *key2, int *cmp)
+{
+ WT_ITEM ikey1, pkey1, ikey2, pkey2;
+
+ (void)collator;
+ testutil_check(wiredtiger_struct_unpack(session,
+ key1->data, key1->size, "uu", &ikey1, &pkey1));
+ testutil_check(wiredtiger_struct_unpack(session,
+ key2->data, key2->size, "uu", &ikey2, &pkey2));
+
+ print_int_item("index_compare: index key1 = ", &ikey1);
+ print_int_item(", primary key1 = ", &pkey1);
+ print_int_item(", index key2 = ", &ikey2);
+ print_int_item(", primary key2 = ", &pkey2);
+ printf("\n");
+
+ if ((*cmp = compare_int_items(&ikey1, &ikey2)) != 0)
+ return (0);
+
+ if (pkey1.size != 0 && pkey2.size != 0)
+ *cmp = compare_int_items(&pkey1, &pkey2);
+ else if (pkey1.size != 0)
+ *cmp = 1;
+ else if (pkey2.size != 0)
+ *cmp = -1;
+ else
+ *cmp = 0;
+
+ return (0);
+}
+
+static WT_COLLATOR index_coll = { index_compare, NULL, NULL };
+
+int
+main(int argc, char *argv[])
+{
+ TEST_OPTS *opts, _opts;
+ WT_CURSOR *cursor, *cursor1;
+ WT_ITEM got, k, v;
+ WT_SESSION *session;
+ int32_t ki, vi;
+
+ opts = &_opts;
+ memset(opts, 0, sizeof(*opts));
+ testutil_check(testutil_parse_opts(argc, argv, opts));
+ testutil_make_work_dir(opts->home);
+
+ testutil_check(wiredtiger_open(opts->home, NULL, "create",
+ &opts->conn));
+ testutil_check(
+ opts->conn->open_session(opts->conn, NULL, NULL, &session));
+
+ testutil_check(opts->conn->add_collator(opts->conn, "index_coll",
+ &index_coll, NULL));
+
+ testutil_check(session->create(session,
+ "table:main", "key_format=u,value_format=u,columns=(k,v)"));
+ testutil_check(session->create(session,
+ "index:main:index", "columns=(v),collator=index_coll"));
+
+ printf("adding new record\n");
+ testutil_check(session->open_cursor(session, "table:main", NULL, NULL,
+ &cursor));
+
+ ki = 13;
+ vi = 17;
+
+ k.data = &ki; k.size = sizeof(ki);
+ v.data = &vi; v.size = sizeof(vi);
+
+ cursor->set_key(cursor, &k);
+ cursor->set_value(cursor, &v);
+ testutil_check(cursor->insert(cursor));
+ testutil_check(cursor->close(cursor));
+
+ printf("positioning index cursor\n");
+
+ testutil_check(session->open_cursor(session, "index:main:index", NULL,
+ NULL, &cursor));
+ cursor->set_key(cursor, &v);
+ testutil_check(cursor->search(cursor));
+
+ printf("duplicating cursor\n");
+ testutil_check(session->open_cursor(session, NULL, cursor, NULL,
+ &cursor1));
+ cursor->get_value(cursor, &got);
+ testutil_assert(item_to_int(&got) == 17);
+ cursor1->get_value(cursor1, &got);
+ testutil_assert(item_to_int(&got) == 17);
+
+ testutil_check(session->close(session, NULL));
+ testutil_cleanup(opts);
+ return (EXIT_SUCCESS);
+}
diff --git a/test/suite/test_cursor_random.py b/test/suite/test_cursor_random.py
index 3bda6dc9946..ee0f85a29ee 100644
--- a/test/suite/test_cursor_random.py
+++ b/test/suite/test_cursor_random.py
@@ -71,6 +71,15 @@ class test_cursor_random(wttest.WiredTigerTestCase):
self.assertEquals(cursor.reset(), 0)
cursor.close()
+ # Check that next_random fails with an empty tree, repeatedly.
+ def test_cursor_random_empty(self):
+ uri = self.type
+ self.session.create(uri, 'key_format=S,value_format=S')
+ cursor = self.session.open_cursor(uri, None, self.config)
+ for i in range(1,5):
+ self.assertTrue(cursor.next(), wiredtiger.WT_NOTFOUND)
+ cursor.close
+
# Check that next_random works with a single value, repeatedly.
def test_cursor_random_single_record(self):
uri = self.type
@@ -127,6 +136,46 @@ class test_cursor_random(wttest.WiredTigerTestCase):
def test_cursor_random_multiple_page_records(self):
self.cursor_random_multiple_page_records(0)
+ # Check that next_random fails in the presence of a set of values, some of
+ # which are deleted.
+ def test_cursor_random_deleted_partial(self):
+ uri = self.type
+ ds = self.dataset(self, uri, 10000,
+ config='allocation_size=512,leaf_page_max=512')
+ ds.populate()
+
+ # Close the connection so everything is forced to disk.
+ self.reopen_conn()
+
+ start = self.session.open_cursor(uri, None)
+ start.set_key(ds.key(10))
+ end = self.session.open_cursor(uri, None)
+ end.set_key(ds.key(10000-10))
+ self.session.truncate(None, start, end, None)
+ self.assertEqual(start.close(), 0)
+ self.assertEqual(end.close(), 0)
+
+ cursor = self.session.open_cursor(uri, None, self.config)
+ for i in range(1,10):
+ self.assertEqual(cursor.next(), 0)
+
+ # Check that next_random fails in the presence of a set of values, all of
+ # which are deleted.
+ def test_cursor_random_deleted_all(self):
+ uri = self.type
+ ds = self.dataset(self, uri, 10000,
+ config='allocation_size=512,leaf_page_max=512')
+ ds.populate()
+
+ # Close the connection so everything is forced to disk.
+ self.reopen_conn()
+
+ self.session.truncate(uri, None, None, None)
+
+ cursor = self.session.open_cursor(uri, None, self.config)
+ for i in range(1,10):
+ self.assertTrue(cursor.next(), wiredtiger.WT_NOTFOUND)
+
# Check that opening a random cursor on column-store returns not-supported.
class test_cursor_random_column(wttest.WiredTigerTestCase):
scenarios = make_scenarios([
diff --git a/test/suite/test_reconfig04.py b/test/suite/test_reconfig04.py
index be5e6d3729e..51d9b91c1f4 100644
--- a/test/suite/test_reconfig04.py
+++ b/test/suite/test_reconfig04.py
@@ -26,9 +26,7 @@
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
-import fnmatch, os, time
import wiredtiger, wttest
-from wtdataset import SimpleDataSet
# test_reconfig04.py
# Test WT_SESSION::reconfigure
diff --git a/test/suite/test_sweep01.py b/test/suite/test_sweep01.py
index 71f8fcb180e..5559190caca 100644
--- a/test/suite/test_sweep01.py
+++ b/test/suite/test_sweep01.py
@@ -116,10 +116,15 @@ class test_sweep01(wttest.WiredTigerTestCase, suite_subprocess):
# Give slow machines time to process files.
stat_cursor = self.session.open_cursor('statistics:', None, None)
this_nfile = stat_cursor[stat.conn.file_open][2]
+ removed = stat_cursor[stat.conn.dh_sweep_remove][2]
stat_cursor.close()
self.pr("==== loop " + str(sleep))
self.pr("this_nfile " + str(this_nfile))
- if this_nfile == final_nfile:
+ self.pr("removed " + str(removed))
+ # On slow machines there can be a lag where files get closed but
+ # the sweep server cannot yet remove the handles. So wait for the
+ # removed statistic to indicate forward progress too.
+ if this_nfile == final_nfile and removed != remove1:
break
c.close()
self.pr("Sweep loop took " + str(sleep))
diff --git a/test/utility/misc.c b/test/utility/misc.c
index 1491c9a6938..1ba08ddd77f 100644
--- a/test/utility/misc.c
+++ b/test/utility/misc.c
@@ -78,7 +78,7 @@ testutil_work_dir_from_path(char *buffer, size_t len, const char *dir)
* Remove the work directory.
*/
void
-testutil_clean_work_dir(char *dir)
+testutil_clean_work_dir(const char *dir)
{
size_t len;
int ret;
diff --git a/test/utility/test_util.h b/test/utility/test_util.h
index f6a9cd68e02..489bbe18d87 100644
--- a/test/utility/test_util.h
+++ b/test/utility/test_util.h
@@ -183,7 +183,7 @@ void *dmalloc(size_t);
void *drealloc(void *, size_t);
void *dstrdup(const void *);
void *dstrndup(const char *, size_t);
-void testutil_clean_work_dir(char *);
+void testutil_clean_work_dir(const char *);
void testutil_cleanup(TEST_OPTS *);
bool testutil_disable_long_tests(void);
void testutil_make_work_dir(char *);