chromium/third_party/blink/web_tests/storage/indexeddb/resources/cursor-advance.js

if (this.importScripts) {
    importScripts('../../../resources/js-test.js');
    importScripts('shared.js');
}

description("Test IndexedDB cursor.advance().");

var objectStoreData = [
    { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
    { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
    { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
    { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
    { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
    { key: "237-23-7737", value: { name: "Pat", height: 65, weight: 100 } },
    { key: "237-23-7738", value: { name: "Leo", height: 65, weight: 180 } },
    { key: "237-23-7739", value: { name: "Jef", height: 65, weight: 120 } },
    { key: "237-23-7740", value: { name: "Sam", height: 71, weight: 110 } },
    { key: "237-23-7741", value: { name: "Bug", height: 63, weight: 100 } },
    { key: "237-23-7742", value: { name: "Tub", height: 69, weight: 180 } },
    { key: "237-23-7743", value: { name: "Rug", height: 77, weight: 120 } },
    { key: "237-23-7744", value: { name: "Pug", height: 66, weight: 110 } },
];

var indexData = [
    { name: "name", keyPath: "name", options: { unique: true } },
    { name: "height", keyPath: "height", options: { } },
    { name: "weight", keyPath: "weight", options: { unique: false } }
];

indexedDBTest(prepareDatabase, onOpen);
function prepareDatabase(evt)
{
    preamble(evt);
    db = event.target.result;
    objectStoreName = "People";
    objectStore = evalAndLog("objectStore = db.createObjectStore(objectStoreName);");
    createIndexes();
}

function onOpen()
{
    evalAndLog("trans = db.transaction(objectStoreName, 'readwrite')");
    evalAndLog("objectStore = trans.objectStore(objectStoreName)");
    populateObjectStore();
}

function populateObjectStore()
{
    debug("First, add all our data to the object store.");
    addedData = 0;
    for (i in objectStoreData) {
        request = evalAndLog("request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);");
        request.onerror = unexpectedErrorCallback;
    }
    trans.oncomplete = testSimple;
}

function createIndexes()
{
    debug("Now create the indexes.");
    for (i in indexData) {
      evalAndLog("objectStore.createIndex(indexData[i].name, indexData[i].keyPath, indexData[i].options);");
    }
}

// Make a test that uses continue() to get to startPos, then passes
// 'count' to get to a position.
function makeAdvanceTest(startPos, direction, count, expectedValue, indexName)
{
    result = {};
    evalAndLog("trans = db.transaction(objectStoreName)");
    store = evalAndLog("store = trans.objectStore(objectStoreName)");
    if (indexName)
        store = store.index(indexName);

    if (direction)
        evalAndLog("request = store.openCursor(null, " + direction + ")");
    else
        evalAndLog("request = store.openCursor()");
    var currentPos = 0;
    function continueToTest(event)
    {
        cursor = event.target.result;
        if (currentPos == startPos) {
            evalAndLog("cursor.advance(" + count + ")");
        } else if (currentPos == startPos + 1) {
            runTest(cursor, expectedValue);
            result.onsuccess();
        } else {
            if (cursor == null)
                result.onsuccess();
            else {
                evalAndLog("cursor.continue();");
            }
        }
        currentPos++;
    }

    request.onsuccess = continueToTest;
    request.onerror = function(e)
    {
        result.onerror(e);
    };

    return result;
}

function simplifyCursor(cursor)
{
    var obj = {};
    if (cursor === null) {
        return null;
    }
    if ('key' in cursor) {
        obj.key = cursor.key;
    }

    if ('value' in cursor) {
        obj.value = cursor.value;
    }

    if ('primaryKey' in cursor) {
        obj.primaryKey = cursor.primaryKey;
    }
    return obj;
}

function runTest(cursor, expectedValue)
{
    expected = JSON.stringify(expectedValue);
    shouldBeEqualToString("expected", JSON.stringify(simplifyCursor(cursor)));
}


function testSimple()
{
    debug("testSimple()");
    makeAdvanceTest(0, null, 1,
                    {key: objectStoreData[1].key,
                     value: objectStoreData[1].value,
                     primaryKey: objectStoreData[1].key})
                         .onsuccess= testContinueThenAdvance;
}

function testContinueThenAdvance()
{
    debug("testContinueThenAdvance()");
    makeAdvanceTest(3, null, 1,
                    // Joe
                    {key: objectStoreData[4].key,
                     value: objectStoreData[4].value,
                     primaryKey: objectStoreData[4].key})
                         .onsuccess= testAdvanceMultiple;
}

function testAdvanceMultiple()
{
    debug("testAdvanceMultiple()");
    makeAdvanceTest(0, null, 3,
                    {key: objectStoreData[3].key,
                     value: objectStoreData[3].value,
                     primaryKey: objectStoreData[3].key})
                         .onsuccess = testAdvanceIndex;
}

function testAdvanceIndex()
{
    debug("testAdvanceIndex()");
    makeAdvanceTest(0, null, 3,
                    // Jef
                    {key: objectStoreData[7].value.name,
                     value: objectStoreData[7].value,
                     primaryKey: objectStoreData[7].key},
                    "name").onsuccess = testAdvanceIndexNoDupe;
}

function testAdvanceIndexNoDupe()
{
    debug("testAdvanceIndexNoDupe()");
    makeAdvanceTest(0, "'nextunique'", 3,
                    // Sue (weight 130 - skipping weight 100, 110, 120)
                    {key: objectStoreData[3].value.weight,
                     value: objectStoreData[3].value,
                     primaryKey: objectStoreData[3].key},
                    "weight").onsuccess = testAdvanceIndexPrev;
}

function testAdvanceIndexPrev()
{
    debug("testAdvanceIndexPrev()");
    makeAdvanceTest(0, "'prev'", 3,
                    // Joe (weight 150 - skipping 180, 180, 180)
                    {key: objectStoreData[4].value.weight,
                     value: objectStoreData[4].value,
                     primaryKey: objectStoreData[4].key},
                    "weight").onsuccess = testAdvanceIndexPrevNoDupe;
}

function testAdvanceIndexPrevNoDupe()
{
    debug("testAdvanceIndexPrevNoDupe()");
    makeAdvanceTest(0, "'prevunique'", 3,
                    // Bob (weight 120 - skipping weights 180, 150, 130)
                    {key: objectStoreData[0].value.weight,
                     value: objectStoreData[0].value,
                     primaryKey: objectStoreData[0].key},
                    "weight").onsuccess = testAdvanceToEnd;
}

function testAdvanceToEnd()
{
    debug("testAdvanceToEnd()");
    makeAdvanceTest(0, null, 100, null)
        .onsuccess = testPrefetchInRange;
}

// Make sure the prefetching that exists on some platforms (chromium)
// doesn't mess with advance(), or vice versa.
function testPrefetchInRange()
{
    debug("testPrefetchInRange()");
    var kPrefetchContinueThreshold = 2;
    evalAndLog("trans = db.transaction(objectStoreName)");
    objectStore = evalAndLog("objectStore = trans.objectStore(objectStoreName)");

    evalAndLog("request = objectStore.openCursor()");

    var currentPos = 0;
    function prefetch(evt)
    {
        preamble(evt);
        cursor = event.target.result;

        if (!cursor) {
            testPrefetchOutOfRange();
            return;
        }

        runTest(cursor, {key: objectStoreData[currentPos].key,
                         value: objectStoreData[currentPos].value,
                         primaryKey: objectStoreData[currentPos].key});

        // force some prefetching,
        if (currentPos < (kPrefetchContinueThreshold+1)) {
            evalAndLog("cursor.continue()");
            currentPos++;
        } else if (currentPos == (kPrefetchContinueThreshold+1)) {
            // stay within the prefetch range
            evalAndLog("cursor.advance(2)");
            currentPos += 2;
        } else {
            // we're just past the threshold
            evalAndLog("cursor.continue()");
            currentPos++;
        }
    }
    request.onsuccess = prefetch;
    request.onerror = unexpectedErrorCallback;
}

// Make sure the prefetching that exists on some platforms (chromium)
// doesn't mess with advance(), or vice versa.
function testPrefetchOutOfRange()
{
    debug("testPrefetchOutOfRange()");
    var kPrefetchContinueThreshold = 2;
    evalAndLog("trans = db.transaction(objectStoreName)");
    objectStore = evalAndLog("objectStore = trans.objectStore(objectStoreName)");

    evalAndLog("request = objectStore.openCursor()");

    var currentPos = 0;
    function prefetch(evt)
    {
        preamble(evt);
        cursor = event.target.result;

        if (!cursor) {
            testBadAdvance();
            return;
        }

        runTest(cursor, {key: objectStoreData[currentPos].key,
                         value: objectStoreData[currentPos].value,
                         primaryKey: objectStoreData[currentPos].key});

        // force some prefetching,
        if (currentPos < (kPrefetchContinueThreshold+1)) {
            evalAndLog("cursor.continue()");
            currentPos++;
        } else if (currentPos == (kPrefetchContinueThreshold+1)) {
            // advance past the prefetch range
            evalAndLog("cursor.advance(7)");
            currentPos += 7;
        } else {
            // we're past the threshold
            evalAndLog("cursor.continue()");
            currentPos++;
        }
    }
    request.onsuccess = prefetch;
    request.onerror = unexpectedErrorCallback;
}

function testBadAdvance()
{
    debug("testBadAdvance()");
    evalAndLog("trans = db.transaction(objectStoreName, 'readwrite')");
    objectStore = evalAndLog("objectStore = trans.objectStore(objectStoreName)");

    evalAndLog("request = objectStore.openCursor()");

    function advanceBadly(evt)
    {
        preamble(evt);
        cursor = event.target.result;

        evalAndExpectExceptionClass("cursor.advance(0)", "TypeError");
        evalAndExpectExceptionClass("cursor.advance(-1)", "TypeError");
        evalAndExpectExceptionClass("cursor.advance(0x100000000)", "TypeError");
        evalAndExpectExceptionClass("cursor.advance(0x20000000000000)", "TypeError");
        testEdges();
    }
    request.onsuccess = advanceBadly;
    request.onerror = unexpectedErrorCallback;
}

function testEdges()
{
    preamble();
    evalAndLog("trans = db.transaction(objectStoreName, 'readonly')");
    trans.onabort = unexpectedAbortCallback;

    objectStore = evalAndLog("objectStore = trans.objectStore(objectStoreName)");
    evalAndLog("request = objectStore.openCursor()");
    request.onerror = unexpectedErrorCallback;

    firstSuccess = true;
    request.onsuccess = function onSuccess(evt) {
        preamble(evt);
        evalAndLog("cursor = event.target.result");
        if (firstSuccess) {
            shouldBeNonNull("cursor");
            firstSuccess = false;
            evalAndLog("cursor.advance(0xffffffff)");
        } else {
            shouldBeNull("cursor");
        }
    };

    trans.oncomplete = testDelete;
}

function testDelete()
{
    debug("testDelete()");
    evalAndLog("trans = db.transaction(objectStoreName, 'readwrite')");
    objectStore = evalAndLog("objectStore = trans.objectStore(objectStoreName)");

    evalAndLog("request = objectStore.openCursor()");

    var currentPos = 0;
    function deleteSecond(evt)
    {
        preamble(evt);
        cursor = event.target.result;

        if (!cursor) {
            finishJSTest();
            return;
        }

        runTest(cursor, {key: objectStoreData[currentPos].key,
                         value: objectStoreData[currentPos].value,
                         primaryKey: objectStoreData[currentPos].key});


        // this is in the middle of the data, so it will test validity
        if (currentPos == 2) {
            evalAndLog("cursor.delete()");
            cursor.advance(4);
            currentPos += 4;
        } else {
            evalAndLog("cursor.advance(1)");
            currentPos++;
        }

    }
    request.onsuccess = deleteSecond;
    request.onerror = unexpectedErrorCallback;
}