void testSetEndPosition_Next_Reverse(bool unique, bool inclusive) {
    auto harnessHelper = newHarnessHelper();
    auto opCtx = harnessHelper->newOperationContext();
    auto sorted = harnessHelper->newSortedDataInterface(
        unique,
        {
         {key1, loc1}, {key2, loc1}, {key3, loc1}, {key4, loc1}, {key5, loc1},
        });

    // Dup key on end point. Illegal for unique indexes.
    if (!unique)
        insertToIndex(opCtx, sorted, {{key3, loc2}});

    auto cursor = sorted->newCursor(opCtx.get(), false);
    cursor->setEndPosition(key3, inclusive);

    ASSERT_EQ(cursor->seek(key5, true), IndexKeyEntry(key5, loc1));
    ASSERT_EQ(cursor->next(), IndexKeyEntry(key4, loc1));
    if (inclusive) {
        if (!unique)
            ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc2));
        ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc1));
    }
    ASSERT_EQ(cursor->next(), boost::none);
    ASSERT_EQ(cursor->next(), boost::none);  // don't resurrect.
}
void testSetEndPosition_RestoreEndCursor_Reverse(bool unique) {
    auto harnessHelper = newHarnessHelper();
    auto opCtx = harnessHelper->newOperationContext();
    auto sorted = harnessHelper->newSortedDataInterface(unique,
                                                        {
                                                         {key1, loc1}, {key4, loc1},
                                                        });

    auto cursor = sorted->newCursor(opCtx.get(), false);
    cursor->setEndPosition(key3, true);

    ASSERT_EQ(cursor->seek(key4, true), IndexKeyEntry(key4, loc1));

    cursor->saveUnpositioned();
    insertToIndex(opCtx,
                  sorted,
                  {
                   {key2, loc1},  // in range
                   {key3, loc1},  // out of range
                  });
    cursor->restore();  // must restore end cursor even with saveUnpositioned().

    ASSERT_EQ(cursor->seek(key4, true), IndexKeyEntry(key4, loc1));
    ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc1));
    ASSERT_EQ(cursor->next(), boost::none);
}
    // Make sure we restore to a RecordId at or ahead of save point if same key on reverse cursor.
    void testSaveAndRestorePositionConsidersRecordId_Reverse(bool unique) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(unique, {
            {key0, loc1},
            {key1, loc1},
            {key2, loc2},
        });

        auto cursor = sorted->newCursor(opCtx.get(), false);

        ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2));

        cursor->savePositioned();
        removeFromIndex(opCtx, sorted, {{key2, loc2}});
        insertToIndex(opCtx, sorted, {{key2, loc1}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1));

        cursor->savePositioned();
        removeFromIndex(opCtx, sorted, {{key2, loc1}});
        insertToIndex(opCtx, sorted, {{key2, loc2}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));

        cursor->savePositioned();
        removeFromIndex(opCtx, sorted, {{key1, loc1}});
        cursor->restore(opCtx.get());

        cursor->savePositioned();
        insertToIndex(opCtx, sorted, {{key1, loc1}});
        cursor->restore(opCtx.get()); // Lands at same point as initial save.

        // Advances from restore point since restore didn't move position.
        ASSERT_EQ(cursor->next(), IndexKeyEntry(key0, loc1));
    }
    // Ensure that restore lands as close as possible to original position, even if data inserted
    // while saved.
    void testSaveAndRestorePositionSeesNewInserts(bool forward, bool unique) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(unique, {
            {key1, loc1},
            {key3, loc1},
        });

        auto cursor = sorted->newCursor(opCtx.get(), forward);
        const auto seekPoint = forward ? key1 : key3;

        ASSERT_EQ(cursor->seek(seekPoint, true), IndexKeyEntry(seekPoint, loc1));

        cursor->savePositioned();
        insertToIndex(opCtx, sorted, {{key2, loc1}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1));
    }
    // Ensure that repeated restores lands as close as possible to original position, even if data
    // inserted while saved and the current position removed in a way that temporarily makes the
    // cursor EOF.
    void testSaveAndRestorePositionSeesNewInsertsAfterEOF(bool forward, bool unique) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(false, {
            {key1, loc1},
        });

        auto cursor = sorted->newCursor(opCtx.get(), forward);

        ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
        // next() would return EOF now.

        cursor->savePositioned();
        removeFromIndex(opCtx, sorted, {{key1, loc1}});
        cursor->restore(opCtx.get());
        // The restore may have seeked to EOF.

        auto insertPoint = forward ? key2 : key0;
        cursor->savePositioned(); // Should still save key1 as "current position".
        insertToIndex(opCtx, sorted, {{insertPoint, loc1}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->next(), IndexKeyEntry(insertPoint, loc1));
    }
    // Ensure that repeated restores lands as close as possible to original position, even if data
    // inserted while saved and the current position removed.
    void testSaveAndRestorePositionSeesNewInsertsAfterRemove(bool forward, bool unique) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(unique, {
            {key1, loc1},
            {key3, loc1},
        });

        auto cursor = sorted->newCursor(opCtx.get(), forward);
        const auto seekPoint = forward ? key1 : key3;

        ASSERT_EQ(cursor->seek(seekPoint, true), IndexKeyEntry(seekPoint, loc1));

        cursor->savePositioned();
        removeFromIndex(opCtx, sorted, {{key1, loc1}});
        cursor->restore(opCtx.get());
        // The restore may have seeked since it can't return to the saved position.

        cursor->savePositioned(); // Should still save originally saved key as "current position".
        insertToIndex(opCtx, sorted, {{key2, loc1}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1));
    }