chromium/content/test/data/indexeddb/corrupted_open_db_detection.html

<!DOCTYPE html>
<html>
<!--
Copyright 2014 The Chromium Authors
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<head>
<title>IDB test that db's corrupted while open are properly handled Part 1 / 2</title>
<script type="text/javascript" src="common.js"></script>
<script>

var testType = 'get';

function test() {
  testType = location.hash.substring(1);
  if (testType == 'testCommon') {
    fail('"testCommon" is a reserved test name');
    return;
  }
  indexedDBTest(upgradeCallback, openCallback);
}

var numObjectsWrittenToDb = 5;
var numTransactions = 0;
var numTransactionErrors = 0;
var numTransactionAborts = 0;
var numKeys = 0;
var transaction;
var request;
var db;
var objectStore;
var transactionErrorsExpected = true;

function upgradeCallback() {
  db = event.target.result;
  deleteAllObjectStores(db);
  objectStore = db.createObjectStore('storeName', { autoIncrement : true });

  var i;
  var len = 80;
  var data = Array(len);
  for (i = 0; i < len; ++i) {
    data[i] = i;
  }

  for (i = 0; i < numObjectsWrittenToDb; ++i) {
    var key = 'key-' + i;
    request = objectStore.add(data, key);
    request.onerror = unexpectedErrorCallback;
    request.onsuccess = upgradeTransactionComplete;
  }
}

function upgradeTransactionComplete() {
  ++numTransactions;
  if (numTransactions === numObjectsWrittenToDb) {
    debug("All transactions written");
  }
}

function transactionError(event) {
  if (event.target.error) {
    numTransactionErrors += 1;
  } else {
    fail("Transaction onerror had no error");
  }
}

function transactionAbort(event) {
  if (event.target.error) {
    numTransactionAborts += 1;
  } else {
    fail("Transaction onabort had no error");
  }
}

function requestError(event) {
  if (!event.target.error) {
    fail("get request had no/invalid error");
  }
}

function databaseClosed(event) {
  if (transactionErrorsExpected) {
    shouldBe("numTransactionErrors", "1");
    shouldBe("numTransactionAborts", "1");
  }
  done("Closed as expected");
}

function testXhr(url, onSuccess) {
  var xmlhttp = new window.XMLHttpRequest();
  xmlhttp.open("GET", url, /*async=*/false);
  xmlhttp.onreadystatechange = function() {
      if (xmlhttp.readyState === 4) {
        if (xmlhttp.status === 200) {
          onSuccess()
        }
      }
    };
  xmlhttp.send();
}

var tests = {
  // Common setup tasks for the other tests in this object
  testCommon: function(mode) {
    transaction = db.transaction('storeName', mode);
    db.onclose = databaseClosed;
    transaction.onabort = transactionAbort;
    transaction.onerror = transactionError;
    objectStore = transaction.objectStore('storeName');
  },
  get: function() {
    testXhr("/corrupt/test/fail?class=LevelDBTransaction&method=Get&instNum=1",
            function() {
      tests.testCommon('readonly');
      request = objectStore.get('key-0');
      request.onsuccess = unexpectedSuccessCallback;
      request.onerror = requestError;
    });
  },
  getAll: function() {
    testXhr("/corrupt/test/fail?class=LevelDBTransaction&method=Get&instNum=1",
            function() {
      tests.testCommon('readonly');
      request = objectStore.getAll('key-0');
      request.onsuccess = unexpectedSuccessCallback;
      request.onerror = requestError;
    });
  },
  failTransactionCommit: function() {
    tests.testCommon('readwrite');
    db.onclose = function(event) {
      shouldBe("numTransactionErrors", "0");
      shouldBe("numTransactionAborts", "1");

      done("Closed as expected");
    };
    testXhr("/corrupt/test/fail?class=LevelDBTransaction&method=Commit",
            function() {
      request = objectStore.put("Any Value", 'key-0');
      request.onerror = requestError;
    });
  },
  iterate: function() {
    testXhr("/corrupt/test/corruptdb?storeName", function() {
      tests.testCommon('readonly');
      request = objectStore.openCursor();
      request.onerror = requestError;
      request.onsuccess = function (event){
        var cursor = request.result;
        if (cursor) {
          // Get an object. Probably shouldn't get this far, but won't call this
          // an error.
          cursor.continue();
        } else {
          // Got the last object. We shouldn't get this far.
          fail("Should *not* have been able to iterate over database.");
        }
      };
    });
  },
  failGetBlobJournal: function() {
    // Get() #1 is the blob key generator (GetBlobKeyGeneratorCurrentNumber)
    // Get() #2 is the journal (GetPrimaryBlobJournal)
    testXhr(
        "/corrupt/test/fail?class=LevelDBDirectTransaction&method=Get&instNum=2",
        function() {
      tests.testCommon('readwrite');
      request = objectStore.put({blob: new Blob(['abc'])}, 'key-0');
      request.onerror = requestError;
      db.onclose = function databaseClosed(event) {
        shouldBe("numTransactionErrors", "0");
        shouldBe("numTransactionAborts", "1");

        done("Closed as expected");
      };
    });
  },
  clearObjectStore: function() {
    // Since the error will occur during 'cleanup' of the leveldb scope, there
    // might not be any transactions that explicitly fail. Instead, the database
    // will just close.
    testXhr("/corrupt/test/corruptdb?storeName", function() {
      transactionErrorsExpected = false;
      tests.testCommon('readwrite');
      request = objectStore.clear();
      request.onerror = requestError;
    });
  }
};

function openCallback() {
  if (testType in tests)
    tests[testType]();
  else
    fail('Unknown test: "' + testType + '"');
}

</script>
</head>
<body onLoad="test()">
<div id="status">Starting...</div>
</body>
</html>