summaryrefslogtreecommitdiffstats
path: root/toolkit/components/sqlite
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/sqlite')
-rw-r--r--toolkit/components/sqlite/moz.build11
-rw-r--r--toolkit/components/sqlite/sqlite_internal.js323
-rw-r--r--toolkit/components/sqlite/tests/xpcshell/.eslintrc.js7
-rw-r--r--toolkit/components/sqlite/tests/xpcshell/data/chrome.manifest1
-rw-r--r--toolkit/components/sqlite/tests/xpcshell/data/worker_sqlite_internal.js279
-rw-r--r--toolkit/components/sqlite/tests/xpcshell/data/worker_sqlite_shared.js39
-rw-r--r--toolkit/components/sqlite/tests/xpcshell/test_sqlite_internal.js43
-rw-r--r--toolkit/components/sqlite/tests/xpcshell/xpcshell.ini10
8 files changed, 713 insertions, 0 deletions
diff --git a/toolkit/components/sqlite/moz.build b/toolkit/components/sqlite/moz.build
new file mode 100644
index 000000000..bbe5b8b96
--- /dev/null
+++ b/toolkit/components/sqlite/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
+
+EXTRA_JS_MODULES.sqlite += [
+ 'sqlite_internal.js',
+]
diff --git a/toolkit/components/sqlite/sqlite_internal.js b/toolkit/components/sqlite/sqlite_internal.js
new file mode 100644
index 000000000..18b07ff50
--- /dev/null
+++ b/toolkit/components/sqlite/sqlite_internal.js
@@ -0,0 +1,323 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This file defines an Sqlite object containing js-ctypes bindings for
+ * sqlite3. It should be included from a worker thread using require.
+ *
+ * It serves the following purposes:
+ * - opens libxul;
+ * - defines sqlite3 API functions;
+ * - defines the necessary sqlite3 types.
+ */
+
+"use strict";
+
+importScripts("resource://gre/modules/workers/require.js");
+
+var SharedAll = require(
+ "resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
+
+// Open the sqlite3 library.
+var path;
+if (SharedAll.Constants.Sys.Name === "Android") {
+ path = ctypes.libraryName("sqlite3");
+} else if (SharedAll.Constants.Win) {
+ path = ctypes.libraryName("nss3");
+} else {
+ path = SharedAll.Constants.Path.libxul;
+}
+
+var lib;
+try {
+ lib = ctypes.open(path);
+} catch (ex) {
+ throw new Error("Could not open system library: " + ex.message);
+}
+
+var declareLazyFFI = SharedAll.declareLazyFFI;
+
+var Type = Object.create(SharedAll.Type);
+
+/**
+ * Opaque Structure |sqlite3_ptr|.
+ * |sqlite3_ptr| is equivalent to a void*.
+ */
+Type.sqlite3_ptr = Type.voidptr_t.withName("sqlite3_ptr");
+
+/**
+ * |sqlite3_stmt_ptr| an instance of an object representing a single SQL
+ * statement.
+ * |sqlite3_stmt_ptr| is equivalent to a void*.
+ */
+Type.sqlite3_stmt_ptr = Type.voidptr_t.withName("sqlite3_stmt_ptr");
+
+/**
+ * |sqlite3_destructor_ptr| a constant defining a special destructor behaviour.
+ * |sqlite3_destructor_ptr| is equivalent to a void*.
+ */
+Type.sqlite3_destructor_ptr = Type.voidptr_t.withName(
+ "sqlite3_destructor_ptr");
+
+/**
+ * A C double.
+ */
+Type.double = new SharedAll.Type("double", ctypes.double);
+
+/**
+ * |sqlite3_int64| typedef for 64-bit integer.
+ */
+Type.sqlite3_int64 = Type.int64_t.withName("sqlite3_int64");
+
+/**
+ * Sqlite3 constants.
+ */
+var Constants = {};
+
+/**
+ * |SQLITE_STATIC| a special value for the destructor that is passed as an
+ * argument to routines like bind_blob, bind_text and bind_text16. It means that
+ * the content pointer is constant and will never change and does need to be
+ * destroyed.
+ */
+Constants.SQLITE_STATIC = Type.sqlite3_destructor_ptr.implementation(0);
+
+/**
+ * |SQLITE_TRANSIENT| a special value for the destructor that is passed as an
+ * argument to routines like bind_blob, bind_text and bind_text16. It means that
+ * the content will likely change in the near future and that SQLite should make
+ * its own private copy of the content before returning.
+ */
+Constants.SQLITE_TRANSIENT = Type.sqlite3_destructor_ptr.implementation(-1);
+
+/**
+ * |SQLITE_OK|
+ * Successful result.
+ */
+Constants.SQLITE_OK = 0;
+
+/**
+ * |SQLITE_ROW|
+ * sqlite3_step() has another row ready.
+ */
+Constants.SQLITE_ROW = 100;
+
+/**
+ * |SQLITE_DONE|
+ * sqlite3_step() has finished executing.
+ */
+Constants.SQLITE_DONE = 101;
+
+var Sqlite3 = {
+ Constants: Constants,
+ Type: Type
+};
+
+declareLazyFFI(Sqlite3, "open", lib, "sqlite3_open", null,
+ /* return*/ Type.int,
+ /* path*/ Type.char.in_ptr,
+ /* db handle*/ Type.sqlite3_ptr.out_ptr);
+
+declareLazyFFI(Sqlite3, "open_v2", lib, "sqlite3_open_v2", null,
+ /* return*/ Type.int,
+ /* path*/ Type.char.in_ptr,
+ /* db handle*/ Type.sqlite3_ptr.out_ptr,
+ /* flags*/ Type.int,
+ /* VFS*/ Type.char.in_ptr);
+
+declareLazyFFI(Sqlite3, "close", lib, "sqlite3_close", null,
+ /* return*/ Type.int,
+ /* db handle*/ Type.sqlite3_ptr);
+
+declareLazyFFI(Sqlite3, "prepare_v2", lib, "sqlite3_prepare_v2", null,
+ /* return*/ Type.int,
+ /* db handle*/ Type.sqlite3_ptr,
+ /* zSql*/ Type.char.in_ptr,
+ /* nByte*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr.out_ptr,
+ /* unused*/ Type.cstring.out_ptr);
+
+declareLazyFFI(Sqlite3, "step", lib, "sqlite3_step", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr);
+
+declareLazyFFI(Sqlite3, "finalize", lib, "sqlite3_finalize", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr);
+
+declareLazyFFI(Sqlite3, "reset", lib, "sqlite3_reset", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr);
+
+declareLazyFFI(Sqlite3, "column_int", lib, "sqlite3_column_int", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* col*/ Type.int);
+
+declareLazyFFI(Sqlite3, "column_blob", lib, "sqlite3_column_blob", null,
+ /* return*/ Type.voidptr_t,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* col*/ Type.int);
+
+declareLazyFFI(Sqlite3, "column_bytes", lib, "sqlite3_column_bytes", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* col*/ Type.int);
+
+declareLazyFFI(Sqlite3, "column_bytes16", lib, "sqlite3_column_bytes16",
+ null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* col*/ Type.int);
+
+declareLazyFFI(Sqlite3, "column_double", lib, "sqlite3_column_double", null,
+ /* return*/ Type.double,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* col*/ Type.int);
+
+declareLazyFFI(Sqlite3, "column_int64", lib, "sqlite3_column_int64", null,
+ /* return*/ Type.sqlite3_int64,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* col*/ Type.int);
+
+declareLazyFFI(Sqlite3, "column_text", lib, "sqlite3_column_text", null,
+ /* return*/ Type.cstring,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* col*/ Type.int);
+
+declareLazyFFI(Sqlite3, "column_text16", lib, "sqlite3_column_text16", null,
+ /* return*/ Type.wstring,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* col*/ Type.int);
+
+declareLazyFFI(Sqlite3, "bind_int", lib, "sqlite3_bind_int", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* index*/ Type.int,
+ /* value*/ Type.int);
+
+declareLazyFFI(Sqlite3, "bind_int64", lib, "sqlite3_bind_int64", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* index*/ Type.int,
+ /* value*/ Type.sqlite3_int64);
+
+declareLazyFFI(Sqlite3, "bind_double", lib, "sqlite3_bind_double", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* index*/ Type.int,
+ /* value*/ Type.double);
+
+declareLazyFFI(Sqlite3, "bind_null", lib, "sqlite3_bind_null", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* index*/ Type.int);
+
+declareLazyFFI(Sqlite3, "bind_zeroblob", lib, "sqlite3_bind_zeroblob", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* index*/ Type.int,
+ /* nBytes*/ Type.int);
+
+declareLazyFFI(Sqlite3, "bind_text", lib, "sqlite3_bind_text", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* index*/ Type.int,
+ /* value*/ Type.cstring,
+ /* nBytes*/ Type.int,
+ /* destructor*/ Type.sqlite3_destructor_ptr);
+
+declareLazyFFI(Sqlite3, "bind_text16", lib, "sqlite3_bind_text16", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* index*/ Type.int,
+ /* value*/ Type.wstring,
+ /* nBytes*/ Type.int,
+ /* destructor*/ Type.sqlite3_destructor_ptr);
+
+declareLazyFFI(Sqlite3, "bind_blob", lib, "sqlite3_bind_blob", null,
+ /* return*/ Type.int,
+ /* statement*/ Type.sqlite3_stmt_ptr,
+ /* index*/ Type.int,
+ /* value*/ Type.voidptr_t,
+ /* nBytes*/ Type.int,
+ /* destructor*/ Type.sqlite3_destructor_ptr);
+
+module.exports = {
+ get Constants() {
+ return Sqlite3.Constants;
+ },
+ get Type() {
+ return Sqlite3.Type;
+ },
+ get open() {
+ return Sqlite3.open;
+ },
+ get open_v2() {
+ return Sqlite3.open_v2;
+ },
+ get close() {
+ return Sqlite3.close;
+ },
+ get prepare_v2() {
+ return Sqlite3.prepare_v2;
+ },
+ get step() {
+ return Sqlite3.step;
+ },
+ get finalize() {
+ return Sqlite3.finalize;
+ },
+ get reset() {
+ return Sqlite3.reset;
+ },
+ get column_int() {
+ return Sqlite3.column_int;
+ },
+ get column_blob() {
+ return Sqlite3.column_blob;
+ },
+ get column_bytes() {
+ return Sqlite3.column_bytes;
+ },
+ get column_bytes16() {
+ return Sqlite3.column_bytes16;
+ },
+ get column_double() {
+ return Sqlite3.column_double;
+ },
+ get column_int64() {
+ return Sqlite3.column_int64;
+ },
+ get column_text() {
+ return Sqlite3.column_text;
+ },
+ get column_text16() {
+ return Sqlite3.column_text16;
+ },
+ get bind_int() {
+ return Sqlite3.bind_int;
+ },
+ get bind_int64() {
+ return Sqlite3.bind_int64;
+ },
+ get bind_double() {
+ return Sqlite3.bind_double;
+ },
+ get bind_null() {
+ return Sqlite3.bind_null;
+ },
+ get bind_zeroblob() {
+ return Sqlite3.bind_zeroblob;
+ },
+ get bind_text() {
+ return Sqlite3.bind_text;
+ },
+ get bind_text16() {
+ return Sqlite3.bind_text16;
+ },
+ get bind_blob() {
+ return Sqlite3.bind_blob;
+ }
+};
diff --git a/toolkit/components/sqlite/tests/xpcshell/.eslintrc.js b/toolkit/components/sqlite/tests/xpcshell/.eslintrc.js
new file mode 100644
index 000000000..d35787cd2
--- /dev/null
+++ b/toolkit/components/sqlite/tests/xpcshell/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
diff --git a/toolkit/components/sqlite/tests/xpcshell/data/chrome.manifest b/toolkit/components/sqlite/tests/xpcshell/data/chrome.manifest
new file mode 100644
index 000000000..92b9cf60b
--- /dev/null
+++ b/toolkit/components/sqlite/tests/xpcshell/data/chrome.manifest
@@ -0,0 +1 @@
+content test_sqlite_internal ./
diff --git a/toolkit/components/sqlite/tests/xpcshell/data/worker_sqlite_internal.js b/toolkit/components/sqlite/tests/xpcshell/data/worker_sqlite_internal.js
new file mode 100644
index 000000000..7f0b3af03
--- /dev/null
+++ b/toolkit/components/sqlite/tests/xpcshell/data/worker_sqlite_internal.js
@@ -0,0 +1,279 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+importScripts("worker_sqlite_shared.js",
+ "resource://gre/modules/workers/require.js");
+
+self.onmessage = function onmessage(msg) {
+ try {
+ run_test();
+ } catch (ex) {
+ let {message, moduleStack, moduleName, lineNumber} = ex;
+ let error = new Error(message, moduleName, lineNumber);
+ error.stack = moduleStack;
+ dump("Uncaught error: " + error + "\n");
+ dump("Full stack: " + moduleStack + "\n");
+ throw error;
+ }
+};
+
+var Sqlite;
+
+var SQLITE_OK; /* Successful result */
+var SQLITE_ROW; /* sqlite3_step() has another row ready */
+var SQLITE_DONE; /* sqlite3_step() has finished executing */
+
+function test_init() {
+ do_print("Starting test_init");
+ // Sqlite should be loaded.
+ Sqlite = require("resource://gre/modules/sqlite/sqlite_internal.js");
+ do_check_neq(typeof Sqlite, "undefined");
+ do_check_neq(typeof Sqlite.Constants, "undefined");
+ SQLITE_OK = Sqlite.Constants.SQLITE_OK;
+ SQLITE_ROW = Sqlite.Constants.SQLITE_ROW;
+ SQLITE_DONE = Sqlite.Constants.SQLITE_DONE;
+}
+
+/**
+ * Clean up the database.
+ * @param {sqlite3_ptr} db A pointer to the database.
+ */
+function cleanupDB(db) {
+ withQuery(db, "DROP TABLE IF EXISTS TEST;", SQLITE_DONE);
+}
+
+/**
+ * Open and close sqlite3 database.
+ * @param {String} open A name of the sqlite3 open function to be
+ * used.
+ * @param {Array} openArgs = [] Optional arguments to open function.
+ * @param {Function} callback = null An optional callback to be run after the
+ * database is opened but before it is
+ * closed.
+ */
+function withDB(open, openArgs = [], callback = null) {
+ let db = Sqlite.Type.sqlite3_ptr.implementation();
+ let dbPtr = db.address();
+
+ // Open database.
+ let result = Sqlite[open].apply(Sqlite, ["data/test.db", dbPtr].concat(
+ openArgs));
+ do_check_eq(result, SQLITE_OK);
+
+ // Drop the test table if it already exists.
+ cleanupDB(db);
+
+ try {
+ if (callback) {
+ callback(db);
+ }
+ } catch (ex) {
+ do_check_true(false);
+ throw ex;
+ } finally {
+ // Drop the test table if it still exists.
+ cleanupDB(db);
+ // Close data base.
+ result = Sqlite.close(db);
+ do_check_eq(result, SQLITE_OK);
+ }
+}
+
+/**
+ * Execute an SQL query using sqlite3 API.
+ * @param {sqlite3_ptr} db A pointer to the database.
+ * @param {String} sql A SQL query string.
+ * @param {Number} stepResult Expected result code after evaluating the
+ * SQL statement.
+ * @param {Function} bind An optional callback with SQL binding steps.
+ * @param {Function} callback An optional callback that runs after the SQL
+ * query completes.
+ */
+function withQuery(db, sql, stepResult, bind, callback) {
+ // Create an instance of a single SQL statement.
+ let sqlStmt = Sqlite.Type.sqlite3_stmt_ptr.implementation();
+ let sqlStmtPtr = sqlStmt.address();
+
+ // Unused portion of an SQL query.
+ let unused = Sqlite.Type.cstring.implementation();
+ let unusedPtr = unused.address();
+
+ // Compile an SQL statement.
+ let result = Sqlite.prepare_v2(db, sql, sql.length, sqlStmtPtr, unusedPtr);
+ do_check_eq(result, SQLITE_OK);
+
+ try {
+ if (bind) {
+ bind(sqlStmt);
+ }
+
+ // Evaluate an SQL statement.
+ result = Sqlite.step(sqlStmt);
+ do_check_eq(result, stepResult);
+
+ if (callback) {
+ callback(sqlStmt);
+ }
+ } catch (ex) {
+ do_check_true(false);
+ throw ex;
+ } finally {
+ // Destroy a prepared statement object.
+ result = Sqlite.finalize(sqlStmt);
+ do_check_eq(result, SQLITE_OK);
+ }
+}
+
+function test_open_close() {
+ do_print("Starting test_open_close");
+ do_check_eq(typeof Sqlite.open, "function");
+ do_check_eq(typeof Sqlite.close, "function");
+
+ withDB("open");
+}
+
+function test_open_v2_close() {
+ do_print("Starting test_open_v2_close");
+ do_check_eq(typeof Sqlite.open_v2, "function");
+
+ withDB("open_v2", [0x02, null]);
+}
+
+function createTableOnOpen(db) {
+ withQuery(db, "CREATE TABLE TEST(" +
+ "ID INT PRIMARY KEY NOT NULL," +
+ "FIELD1 INT," +
+ "FIELD2 REAL," +
+ "FIELD3 TEXT," +
+ "FIELD4 TEXT," +
+ "FIELD5 BLOB" +
+ ");", SQLITE_DONE);
+}
+
+function test_create_table() {
+ do_print("Starting test_create_table");
+ do_check_eq(typeof Sqlite.prepare_v2, "function");
+ do_check_eq(typeof Sqlite.step, "function");
+ do_check_eq(typeof Sqlite.finalize, "function");
+
+ withDB("open", [], createTableOnOpen);
+}
+
+/**
+ * Read column values after evaluating the SQL SELECT statement.
+ * @param {sqlite3_stmt_ptr} sqlStmt A pointer to the SQL statement.
+ */
+function onSqlite3Step(sqlStmt) {
+ // Get an int value from a query result from the ID (column 0).
+ let field = Sqlite.column_int(sqlStmt, 0);
+ do_check_eq(field, 3);
+
+ // Get an int value from a query result from the column 1.
+ field = Sqlite.column_int(sqlStmt, 1);
+ do_check_eq(field, 2);
+ // Get an int64 value from a query result from the column 1.
+ field = Sqlite.column_int64(sqlStmt, 1);
+ do_check_eq(field, 2);
+
+ // Get a double value from a query result from the column 2.
+ field = Sqlite.column_double(sqlStmt, 2);
+ do_check_eq(field, 1.2);
+
+ // Get a number of bytes of the value in the column 3.
+ let bytes = Sqlite.column_bytes(sqlStmt, 3);
+ do_check_eq(bytes, 4);
+ // Get a text(cstring) value from a query result from the column 3.
+ field = Sqlite.column_text(sqlStmt, 3);
+ do_check_eq(field.readString(), "DATA");
+
+ // Get a number of bytes of the UTF-16 value in the column 4.
+ bytes = Sqlite.column_bytes16(sqlStmt, 4);
+ do_check_eq(bytes, 8);
+ // Get a text16(wstring) value from a query result from the column 4.
+ field = Sqlite.column_text16(sqlStmt, 4);
+ do_check_eq(field.readString(), "TADA");
+
+ // Get a blob value from a query result from the column 5.
+ field = Sqlite.column_blob(sqlStmt, 5);
+ do_check_eq(ctypes.cast(field,
+ Sqlite.Type.cstring.implementation).readString(), "BLOB");
+}
+
+function test_insert_select() {
+ do_print("Starting test_insert_select");
+ do_check_eq(typeof Sqlite.column_int, "function");
+ do_check_eq(typeof Sqlite.column_int64, "function");
+ do_check_eq(typeof Sqlite.column_double, "function");
+ do_check_eq(typeof Sqlite.column_bytes, "function");
+ do_check_eq(typeof Sqlite.column_text, "function");
+ do_check_eq(typeof Sqlite.column_text16, "function");
+ do_check_eq(typeof Sqlite.column_blob, "function");
+
+ function onOpen(db) {
+ createTableOnOpen(db);
+ withQuery(db,
+ "INSERT INTO TEST VALUES (3, 2, 1.2, \"DATA\", \"TADA\", \"BLOB\");",
+ SQLITE_DONE);
+ withQuery(db, "SELECT * FROM TEST;", SQLITE_ROW, null, onSqlite3Step);
+ }
+
+ withDB("open", [], onOpen);
+}
+
+function test_insert_bind_select() {
+ do_print("Starting test_insert_bind_select");
+ do_check_eq(typeof Sqlite.bind_int, "function");
+ do_check_eq(typeof Sqlite.bind_int64, "function");
+ do_check_eq(typeof Sqlite.bind_double, "function");
+ do_check_eq(typeof Sqlite.bind_text, "function");
+ do_check_eq(typeof Sqlite.bind_text16, "function");
+ do_check_eq(typeof Sqlite.bind_blob, "function");
+
+ function onBind(sqlStmt) {
+ // Bind an int value to the ID (column 0).
+ let result = Sqlite.bind_int(sqlStmt, 1, 3);
+ do_check_eq(result, SQLITE_OK);
+
+ // Bind an int64 value to the FIELD1 (column 1).
+ result = Sqlite.bind_int64(sqlStmt, 2, 2);
+ do_check_eq(result, SQLITE_OK);
+
+ // Bind a double value to the FIELD2 (column 2).
+ result = Sqlite.bind_double(sqlStmt, 3, 1.2);
+ do_check_eq(result, SQLITE_OK);
+
+ // Destructor.
+ let destructor = Sqlite.Constants.SQLITE_TRANSIENT;
+ // Bind a text value to the FIELD3 (column 3).
+ result = Sqlite.bind_text(sqlStmt, 4, "DATA", 4, destructor);
+ do_check_eq(result, SQLITE_OK);
+
+ // Bind a text16 value to the FIELD4 (column 4).
+ result = Sqlite.bind_text16(sqlStmt, 5, "TADA", 8, destructor);
+ do_check_eq(result, SQLITE_OK);
+
+ // Bind a blob value to the FIELD5 (column 5).
+ result = Sqlite.bind_blob(sqlStmt, 6, ctypes.char.array()("BLOB"), 4,
+ destructor);
+ do_check_eq(result, SQLITE_OK);
+ }
+
+ function onOpen(db) {
+ createTableOnOpen(db);
+ withQuery(db, "INSERT INTO TEST VALUES (?, ?, ?, ?, ?, ?);", SQLITE_DONE,
+ onBind);
+ withQuery(db, "SELECT * FROM TEST;", SQLITE_ROW, null, onSqlite3Step);
+ }
+
+ withDB("open", [], onOpen);
+}
+
+function run_test() {
+ test_init();
+ test_open_close();
+ test_open_v2_close();
+ test_create_table();
+ test_insert_select();
+ test_insert_bind_select();
+ do_test_complete();
+}
diff --git a/toolkit/components/sqlite/tests/xpcshell/data/worker_sqlite_shared.js b/toolkit/components/sqlite/tests/xpcshell/data/worker_sqlite_shared.js
new file mode 100644
index 000000000..54351a02a
--- /dev/null
+++ b/toolkit/components/sqlite/tests/xpcshell/data/worker_sqlite_shared.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function send(message) {
+ self.postMessage(message);
+}
+
+function do_test_complete() {
+ send({kind: "do_test_complete", args: []});
+}
+
+function do_check_true(x) {
+ send({kind: "do_check_true", args: [!!x]});
+ if (x) {
+ dump("TEST-PASS: " + x + "\n");
+ } else {
+ throw new Error("do_check_true failed");
+ }
+}
+
+function do_check_eq(a, b) {
+ let result = a == b;
+ send({kind: "do_check_true", args: [result]});
+ if (!result) {
+ throw new Error("do_check_eq failed " + a + " != " + b);
+ }
+}
+
+function do_check_neq(a, b) {
+ let result = a != b;
+ send({kind: "do_check_true", args: [result]});
+ if (!result) {
+ throw new Error("do_check_neq failed " + a + " == " + b);
+ }
+}
+
+function do_print(x) {
+ dump("TEST-INFO: " + x + "\n");
+}
diff --git a/toolkit/components/sqlite/tests/xpcshell/test_sqlite_internal.js b/toolkit/components/sqlite/tests/xpcshell/test_sqlite_internal.js
new file mode 100644
index 000000000..725cbfaea
--- /dev/null
+++ b/toolkit/components/sqlite/tests/xpcshell/test_sqlite_internal.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+var WORKER_SOURCE_URI =
+ "chrome://test_sqlite_internal/content/worker_sqlite_internal.js";
+do_load_manifest("data/chrome.manifest");
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function() {
+ let worker = new ChromeWorker(WORKER_SOURCE_URI);
+ let deferred = Promise.defer();
+ worker.onmessage = function(event) {
+ let data = event.data;
+ switch (data.kind) {
+ case "do_check_true":
+ try {
+ do_check_true(data.args[0]);
+ } catch (ex) {
+ // Ignore errors
+ }
+ break;
+ case "do_test_complete":
+ deferred.resolve();
+ worker.terminate();
+ break;
+ case "do_print":
+ do_print(data.args[0]);
+ break;
+ }
+ };
+ worker.onerror = function(event) {
+ let error = new Error(event.message, event.filename, event.lineno);
+ worker.terminate();
+ deferred.reject(error);
+ };
+ worker.postMessage("START");
+ return deferred.promise;
+});
diff --git a/toolkit/components/sqlite/tests/xpcshell/xpcshell.ini b/toolkit/components/sqlite/tests/xpcshell/xpcshell.ini
new file mode 100644
index 000000000..d652dbb1d
--- /dev/null
+++ b/toolkit/components/sqlite/tests/xpcshell/xpcshell.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+head =
+tail =
+skip-if = toolkit == 'android' || (os == 'mac' && appname == 'thunderbird')
+support-files =
+ data/worker_sqlite_shared.js
+ data/worker_sqlite_internal.js
+ data/chrome.manifest
+
+[test_sqlite_internal.js]