[Pkg-javascript-commits] [node-sqlite3] 11/20: Imported Upstream version 2.1.15+ds1

Jérémy Lal kapouer at alioth.debian.org
Wed Sep 4 12:34:10 UTC 2013


This is an automated email from the git hooks/post-receive script.

kapouer pushed a commit to branch master
in repository node-sqlite3.

commit 90eef8c64515bcce15b05e5fcc66273ea90529bd
Author: Jérémy Lal <kapouer at melix.org>
Date:   Wed Sep 4 13:39:11 2013 +0200

    Imported Upstream version 2.1.15+ds1
---
 .gitignore                          |   26 +-
 .travis.yml                         |    6 +
 CHANGELOG.md                        |    9 +
 Makefile                            |   28 +-
 README.md                           |  115 +++++--
 binding.gyp                         |   59 ++++
 configure                           |    2 +-
 lib/sqlite3.js                      |    2 +-
 package.json                        |   22 +-
 src/async.h                         |   30 +-
 src/database.cc                     |   46 ++-
 src/database.h                      |    6 +-
 src/macros.h                        |    2 +-
 src/{sqlite3.cc => node_sqlite3.cc} |    3 +-
 src/statement.cc                    |   32 +-
 src/statement.h                     |   23 +-
 src/threading.h                     |   48 +++
 test/affected.test.js               |   28 +-
 test/blob.test.js                   |   36 +-
 test/cache.test.js                  |   71 ++--
 test/constants.test.js              |   72 ++--
 test/database_fail.test.js          |  361 +++++++++-----------
 test/each.test.js                   |   62 ++--
 test/exec.test.js                   |   30 +-
 test/extension.test.js              |   31 +-
 test/fts-content.test.js            |   13 +
 test/map.test.js                    |  127 ++++---
 test/named_columns.test.js          |   34 +-
 test/named_params.test.js           |   59 ++--
 test/null_error.test.js             |   34 +-
 test/open_close.test.js             |  194 +++++------
 test/other_objects.test.js          |  137 ++++----
 test/parallel_insert.test.js        |   46 +--
 test/prepare.test.js                |  622 +++++++++++++++++------------------
 test/profile.test.js                |   74 +++--
 test/rerun.test.js                  |   65 ++--
 test/scheduling.test.js             |   80 ++---
 test/serialization.test.js          |  127 +++----
 test/support/createdb.js            |   38 ++-
 test/support/helper.js              |   11 +-
 test/trace.test.js                  |  105 +++---
 test/unicode.test.js                |   64 ++--
 wscript                             |  146 --------
 43 files changed, 1579 insertions(+), 1547 deletions(-)

diff --git a/.gitignore b/.gitignore
index c0c4a11..57aa6de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,21 @@
-.DS_Store
 .lock-wscript
-build/
-TODO
-wiki
-lib/sqlite3_bindings.node
-deps/sqlite-autoconf-3070800
\ No newline at end of file
+*.dylib
+*.so
+*.o
+*.lo
+*.Makefile
+*.target.gyp.mk
+build
+out
+Release
+Debug
+node_modules
+.deps
+Makefile.gyp
+gyp-mac-tool
+.dirstamp
+npm-debug.log
+test/support/big.db
+lib/node_sqlite3.node
+test/tmp
+.DS_Store
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..c248402
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+language: node_js
+node_js:
+  - "0.10"
+  - "0.8"
+  - "0.6"
+before_script: "npm install mocha"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..08a58de
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,9 @@
+# Changlog
+
+## 2.1.15
+
+Released August 7th, 2013
+
+ - Minor readme additions and code optimizations
+
+
diff --git a/Makefile b/Makefile
index 5734417..7924cdd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,24 +1,14 @@
-
 build:
-	node-waf build
+	node-gyp build
 
 clean:
-	node-waf clean
-
-db:
-	@if ! [ -f test/support/big.db ]; then                                     \
-		echo "Creating test database... This may take several minutes." ;      \
-		node test/support/createdb.js ;                                        \
-	fi
+	rm -f test/support/big.db*
+	rm -f test/tmp/*
+	rm -rf ./deps/sqlite-autoconf-*/
+	rm -rf ./build
+	rm -rf ./out
 
-ifndef only
-test: build db
-	@rm -rf ./test/tmp && mkdir -p ./test/tmp
-	@expresso -I lib test/*.test.js
-else
-test: build db
-	@rm -rf ./test/tmp && mkdir -p ./test/tmp
-	@expresso -I lib test/${only}.test.js
-endif
+test:
+	npm test
 
-.PHONY: build clean test
\ No newline at end of file
+.PHONY: build clean test
diff --git a/README.md b/README.md
index 88e4e0c..486fd26 100644
--- a/README.md
+++ b/README.md
@@ -1,31 +1,37 @@
 # NAME
 
-node-sqlite3 - Asynchronous, non-blocking [SQLite3](http://sqlite.org/) bindings for [node.js](https://github.com/joyent/node) 0.2-0.4 (versions 2.0.x) and **0.6.x** (versions 2.1.x).
+node-sqlite3 - Asynchronous, non-blocking [SQLite3](http://sqlite.org/) bindings for [Node.js](http://nodejs.org/) 0.2-0.4 (versions 2.0.x), **0.6.13+, 0.8.x, and 0.10.x** (versions 2.1.x).
 
+(Can also run in [node-webkit](https://github.com/rogerwang/node-webkit) if it uses a supported version of Node's engine.)
+
+[![Build Status](https://travis-ci.org/developmentseed/node-sqlite3.png?branch=master)](https://travis-ci.org/developmentseed/node-sqlite3)
+[![npm package version](https://badge.fury.io/js/sqlite3.png)](https://npmjs.org/package/sqlite3)
 
 
 # USAGE
 
-Install with `npm install sqlite3`.
+**Note:**   the module must be [installed](#installing) before use.
 
-    var sqlite3 = require('sqlite3').verbose();
-    var db = new sqlite3.Database(':memory:');
+``` js
+var sqlite3 = require('sqlite3').verbose();
+var db = new sqlite3.Database(':memory:');
 
-    db.serialize(function() {
-      db.run("CREATE TABLE lorem (info TEXT)");
+db.serialize(function() {
+  db.run("CREATE TABLE lorem (info TEXT)");
 
-      var stmt = db.prepare("INSERT INTO lorem VALUES (?)");
-      for (var i = 0; i < 10; i++) {
-          stmt.run("Ipsum " + i);
-      }
-      stmt.finalize();
+  var stmt = db.prepare("INSERT INTO lorem VALUES (?)");
+  for (var i = 0; i < 10; i++) {
+      stmt.run("Ipsum " + i);
+  }
+  stmt.finalize();
 
-      db.each("SELECT rowid AS id, info FROM lorem", function(err, row) {
-          console.log(row.id + ": " + row.info);
-      });
-    });
+  db.each("SELECT rowid AS id, info FROM lorem", function(err, row) {
+      console.log(row.id + ": " + row.info);
+  });
+});
 
-    db.close();
+db.close();
+```
 
 
 
@@ -46,29 +52,81 @@ Install with `npm install sqlite3`.
 See the [API documentation](https://github.com/developmentseed/node-sqlite3/wiki) in the wiki.
 
 
-# BUILDING
+# INSTALLING
+
+You can use [`npm`](https://github.com/isaacs/npm) to download and install:
+
+* The latest `sqlite3` package: `npm install sqlite3`
+
+* GitHub's `master` branch: `npm install https://github.com/developmentseed/node-sqlite3/tarball/master`
+
+In both cases the module is automatically built with npm's internal version of `node-gyp`,
+and thus your system must meet [node-gyp's requirements](https://github.com/TooTallNate/node-gyp#installation).
+
+It is also possible to make your own build of `sqlite3` from its source instead of its npm package ([see below](#building-from-the-source)).
+
+It is possible to use the installed package in [node-webkit](https://github.com/rogerwang/node-webkit) instead of the vanilla Node.js, but a rebuild is required before use (see the next section).
+
+
+# REBUILDING FOR NODE-WEBKIT
+
+Because of ABI differences, only a rebuilt version of `sqlite3` can be used in [node-webkit](https://github.com/rogerwang/node-webkit).
+
+After the `sqlite3` module is installed (according to the previous section), do the following:
+
+1. Install [`nw-gyp`](https://github.com/rogerwang/nw-gyp) globally: `npm install nw-gyp -g` *(unless already installed)*
+
+2. Use `nw-gyp` to rebuild the module: `nw-gyp rebuild --target=0.6.2`
+
+Remember the following:
 
-Make sure you have the sources for `sqlite3` installed. Mac OS X ships with these by default. If you don't have them installed, install the `-dev` package with your package manager, e.g. `apt-get install libsqlite3-dev` for Debian/Ubuntu. Make sure that you have at least `libsqlite3` >= 3.6.
+* In the `nw-gyp rebuild` command, specify the actual target version of your node-webkit. The command must be run in sqlite3's directory (where its `package.json` resides).
 
-To obtain and build the bindings:
+* After the `sqlite3` package is rebuilt for node-webkit it cannot run in the vanilla Node.js (and vice versa).
+   * For example, `npm test` of the node-webkit's package would fail.
+   * If you need `sqlite3` package both for Node.js and node-webkit, then you should make two separate installations of `sqlite3` (in different directories) and rebuild only one of them for node-webkit.
 
-    git clone git://github.com/developmentseed/node-sqlite3.git
-    cd node-sqlite3
-    ./configure
+Visit the “[Using Node modules](https://github.com/rogerwang/node-webkit/wiki/Using-Node-modules)” article in the node-webkit's wiki for more details.
+
+
+# BUILDING FROM THE SOURCE
+
+Unless building via `npm install` (which uses its own `node-gyp`) you will need `node-gyp` installed globally:
+
+    npm install node-gyp -g
+
+The sqlite3 module depends only on libsqlite3. However, by default, an internal/bundled copy of sqlite will be built and statically linked, so an externally installed sqlite3 is not required.
+
+If you wish to install against an external sqlite then you need to pass the `--sqlite` argument to `node-gyp`, `npm install` or the `configure` wrapper.
+
+    ./configure --sqlite=/usr/local
     make
 
-You can also use [`npm`](https://github.com/isaacs/npm) to download and install them:
+Or, using the node-gyp directly:
 
-    npm install sqlite3
+     node-gyp --sqlite=/usr/local
+     make
+
+Or, using npm:
+
+     npm install --sqlite=/usr/local
+
+If building against an external sqlite3 make sure to have the development headers available. Mac OS X ships with these by default. If you don't have them installed, install the `-dev` package with your package manager, e.g. `apt-get install libsqlite3-dev` for Debian/Ubuntu. Make sure that you have at least `libsqlite3` >= 3.6.
+
+Note, if building against homebrew-installed sqlite on OS X you can do:
+
+    ./configure --sqlite=/usr/local/opt/sqlite/
+    make
 
 
+# TESTING
 
-# TESTS
+[mocha](https://github.com/visionmedia/mocha) is required to run unit tests.
 
-[expresso](https://github.com/visionmedia/expresso) is required to run unit tests.
+In sqlite3's directory (where its `package.json` resides) run the following:
 
-    npm install expresso
-    make test
+    npm install mocha
+    npm test
 
 
 
@@ -86,6 +144,7 @@ You can also use [`npm`](https://github.com/isaacs/npm) to download and install
 * [Carter Thaxton](https://github.com/carter-thaxton)
 * [Audrius Kažukauskas](https://github.com/audriusk)
 * [Johannes Schauer](https://github.com/pyneo)
+* [Mithgol](https://github.com/Mithgol)
 
 
 
diff --git a/binding.gyp b/binding.gyp
new file mode 100644
index 0000000..12a3704
--- /dev/null
+++ b/binding.gyp
@@ -0,0 +1,59 @@
+{
+  'includes': [ 'deps/common-sqlite.gypi' ],
+  'variables': {
+      'sqlite%':'internal',
+  },
+  'conditions': [
+      ['OS=="win"', {
+        'variables': {
+          'copy_command%': 'copy',
+        },
+      },{
+        'variables': {
+          'copy_command%': 'cp',
+        },
+      }]
+  ],
+  'targets': [
+    {
+      'target_name': 'node_sqlite3',
+      'conditions': [
+        ['sqlite != "internal"', {
+            'libraries': [
+               '-L<@(sqlite)/lib',
+               '-lsqlite3'
+            ],
+            'include_dirs': [ '<@(sqlite)/include' ]
+        },
+        {
+            'dependencies': [
+              'deps/sqlite3.gyp:sqlite3'
+            ]
+        }
+        ]
+      ],
+      'sources': [
+        'src/database.cc',
+        'src/node_sqlite3.cc',
+        'src/statement.cc'
+      ],
+    },
+    {
+      'target_name': 'action_after_build',
+      'type': 'none',
+      'dependencies': [ 'node_sqlite3' ],
+      'actions': [
+        {
+          'action_name': 'move_node_module',
+          'inputs': [
+            '<@(PRODUCT_DIR)/node_sqlite3.node'
+          ],
+          'outputs': [
+            'lib/node_sqlite3.node'
+          ],
+          'action': ['<@(copy_command)', '<@(PRODUCT_DIR)/node_sqlite3.node', 'lib/node_sqlite3.node']
+        }
+      ]
+    }
+  ]
+}
diff --git a/configure b/configure
index d153015..17af137 100755
--- a/configure
+++ b/configure
@@ -1,3 +1,3 @@
 #!/bin/sh
 
-node-waf configure $@
\ No newline at end of file
+node-gyp configure $@
diff --git a/lib/sqlite3.js b/lib/sqlite3.js
index 8db674a..62541cf 100644
--- a/lib/sqlite3.js
+++ b/lib/sqlite3.js
@@ -1,4 +1,4 @@
-var sqlite3 = module.exports = exports = require('./sqlite3_bindings.node');
+var sqlite3 = module.exports = exports = require('./node_sqlite3.node');
 var path = require('path');
 var util = require('util');
 var EventEmitter = require('events').EventEmitter;
diff --git a/package.json b/package.json
index d476e91..af7f054 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,12 @@
 {
     "name": "sqlite3",
     "description": "Asynchronous, non-blocking SQLite3 bindings",
-    "version": "2.1.1",
+    "version": "2.1.15",
     "homepage": "http://github.com/developmentseed/node-sqlite3",
     "author": {
-      "name": "Development Seed",
-      "url": "http://developmentseed.org/",
-      "email": "info at developmentseed.org"
+        "name": "Development Seed",
+        "url": "http://developmentseed.org/",
+        "email": "info at developmentseed.org"
     },
     "contributors": [
         "Konstantin Käfer <mail at kkaefer.com>",
@@ -20,17 +20,21 @@
         "Tom MacWright <tom at developmentseed.org>",
         "Carter Thaxton <carter.thaxton at gmail.com>",
         "Audrius Kažukauskas <audrius at neutrino.lt>",
-        "Johannes Schauer <josch at pyneo.org>"
+        "Johannes Schauer <josch at pyneo.org>",
+        "Nathan Rajlich <nathan at tootallnate.net>",
+        "AJ ONeal <coolaj86 at gmail.com>",
+        "Mithgol"
     ],
     "repository": {
         "type": "git",
         "url": "git://github.com/developmentseed/node-sqlite3.git"
     },
-    "devDependencies": {
-      "step": "0.0.4"
-    },
     "engines": {
-        "node": "~0.6.0"
+        "node": ">= 0.6.13 && < 0.11.0"
+    },
+    "scripts": {
+        "pretest": "node test/support/createdb.js",
+        "test": "mocha -R spec --timeout 200000"
     },
     "licenses": [{ "type": "BSD" }],
     "main": "./lib/sqlite3"
diff --git a/src/async.h b/src/async.h
index b910ae6..37b4248 100644
--- a/src/async.h
+++ b/src/async.h
@@ -1,6 +1,13 @@
 #ifndef NODE_SQLITE3_SRC_ASYNC_H
 #define NODE_SQLITE3_SRC_ASYNC_H
 
+#include "threading.h"
+#include <node_version.h>
+
+#if defined(NODE_SQLITE3_BOOST_THREADING)
+#include <boost/thread/mutex.hpp>
+#endif
+
 
 // Generic uv_async handler.
 template <class Item, class Parent> class Async {
@@ -8,7 +15,7 @@ template <class Item, class Parent> class Async {
 
 protected:
     uv_async_t watcher;
-    pthread_mutex_t mutex;
+    NODE_SQLITE3_MUTEX_t
     std::vector<Item*> data;
     Callback callback;
 public:
@@ -18,18 +25,22 @@ public:
     Async(Parent* parent_, Callback cb_)
         : callback(cb_), parent(parent_) {
         watcher.data = this;
-        pthread_mutex_init(&mutex, NULL);
+        NODE_SQLITE3_MUTEX_INIT
         uv_async_init(uv_default_loop(), &watcher, listener);
     }
 
     static void listener(uv_async_t* handle, int status) {
         Async* async = static_cast<Async*>(handle->data);
         std::vector<Item*> rows;
-        pthread_mutex_lock(&async->mutex);
+        NODE_SQLITE3_MUTEX_LOCK(&async->mutex)
         rows.swap(async->data);
-        pthread_mutex_unlock(&async->mutex);
+        NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex)
         for (unsigned int i = 0, size = rows.size(); i < size; i++) {
+#if NODE_VERSION_AT_LEAST(0, 7, 9)
+            uv_unref((uv_handle_t *)&async->watcher);
+#else
             uv_unref(uv_default_loop());
+#endif
             async->callback(async->parent, rows[i]);
         }
     }
@@ -39,7 +50,6 @@ public:
         assert(handle->data != NULL);
         Async* async = static_cast<Async*>(handle->data);
         delete async;
-        handle->data = NULL;
     }
 
     void finish() {
@@ -52,10 +62,14 @@ public:
 
     void add(Item* item) {
         // Make sure node runs long enough to deliver the messages.
+#if NODE_VERSION_AT_LEAST(0, 7, 9)
+        uv_ref((uv_handle_t *)&watcher);
+#else
         uv_ref(uv_default_loop());
-        pthread_mutex_lock(&mutex);
+#endif
+        NODE_SQLITE3_MUTEX_LOCK(&mutex);
         data.push_back(item);
-        pthread_mutex_unlock(&mutex);
+        NODE_SQLITE3_MUTEX_UNLOCK(&mutex)
     }
 
     void send() {
@@ -68,7 +82,7 @@ public:
     }
 
     ~Async() {
-        pthread_mutex_destroy(&mutex);
+        NODE_SQLITE3_MUTEX_DESTROY
     }
 };
 
diff --git a/src/database.cc b/src/database.cc
index 70e092a..50206e6 100644
--- a/src/database.cc
+++ b/src/database.cc
@@ -1,5 +1,4 @@
 #include <string.h>
-#include <v8.h>
 #include <node.h>
 
 #include "macros.h"
@@ -21,6 +20,7 @@ void Database::Init(Handle<Object> target) {
 
     NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close);
     NODE_SET_PROTOTYPE_METHOD(constructor_template, "exec", Exec);
+    NODE_SET_PROTOTYPE_METHOD(constructor_template, "wait", Wait);
     NODE_SET_PROTOTYPE_METHOD(constructor_template, "loadExtension", LoadExtension);
     NODE_SET_PROTOTYPE_METHOD(constructor_template, "serialize", Serialize);
     NODE_SET_PROTOTYPE_METHOD(constructor_template, "parallelize", Parallelize);
@@ -112,9 +112,11 @@ Handle<Value> Database::New(const Arguments& args) {
     REQUIRE_ARGUMENT_STRING(0, filename);
     int pos = 1;
 
-    int mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
+    int mode;
     if (args.Length() >= pos && args[pos]->IsInt32()) {
         mode = args[pos++]->Int32Value();
+    } else {
+        mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
     }
 
     Local<Function> callback;
@@ -129,7 +131,7 @@ Handle<Value> Database::New(const Arguments& args) {
     args.This()->Set(String::NewSymbol("mode"), Integer::New(mode), ReadOnly);
 
     // Start opening the database.
-    OpenBaton* baton = new OpenBaton(db, callback, *filename, SQLITE_OPEN_FULLMUTEX | mode);
+    OpenBaton* baton = new OpenBaton(db, callback, *filename, mode);
     Work_BeginOpen(baton);
 
     return args.This();
@@ -137,7 +139,7 @@ Handle<Value> Database::New(const Arguments& args) {
 
 void Database::Work_BeginOpen(Baton* baton) {
     int status = uv_queue_work(uv_default_loop(),
-        &baton->request, Work_Open, Work_AfterOpen);
+        &baton->request, Work_Open, (uv_after_work_cb)Work_AfterOpen);
     assert(status == 0);
 }
 
@@ -220,7 +222,7 @@ void Database::Work_BeginClose(Baton* baton) {
 
     baton->db->RemoveCallbacks();
     int status = uv_queue_work(uv_default_loop(),
-        &baton->request, Work_Close, Work_AfterClose);
+        &baton->request, Work_Close, (uv_after_work_cb)Work_AfterClose);
     assert(status == 0);
 }
 
@@ -499,7 +501,7 @@ void Database::Work_BeginExec(Baton* baton) {
     assert(baton->db->handle);
     assert(baton->db->pending == 0);
     int status = uv_queue_work(uv_default_loop(),
-        &baton->request, Work_Exec, Work_AfterExec);
+        &baton->request, Work_Exec, (uv_after_work_cb)Work_AfterExec);
     assert(status == 0);
 }
 
@@ -549,6 +551,36 @@ void Database::Work_AfterExec(uv_work_t* req) {
     delete baton;
 }
 
+Handle<Value> Database::Wait(const Arguments& args) {
+    HandleScope scope;
+    Database* db = ObjectWrap::Unwrap<Database>(args.This());
+
+    OPTIONAL_ARGUMENT_FUNCTION(0, callback);
+
+    Baton* baton = new Baton(db, callback);
+    db->Schedule(Work_Wait, baton, true);
+
+    return args.This();
+}
+
+void Database::Work_Wait(Baton* baton) {
+    HandleScope scope;
+
+    assert(baton->db->locked);
+    assert(baton->db->open);
+    assert(baton->db->handle);
+    assert(baton->db->pending == 0);
+
+    if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
+        Local<Value> argv[] = { Local<Value>::New(Null()) };
+        TRY_CATCH_CALL(baton->db->handle_, baton->callback, 1, argv);
+    }
+
+    baton->db->Process();
+
+    delete baton;
+}
+
 Handle<Value> Database::LoadExtension(const Arguments& args) {
     HandleScope scope;
     Database* db = ObjectWrap::Unwrap<Database>(args.This());
@@ -568,7 +600,7 @@ void Database::Work_BeginLoadExtension(Baton* baton) {
     assert(baton->db->handle);
     assert(baton->db->pending == 0);
     int status = uv_queue_work(uv_default_loop(),
-        &baton->request, Work_LoadExtension, Work_AfterLoadExtension);
+        &baton->request, Work_LoadExtension, (uv_after_work_cb)Work_AfterLoadExtension);
     assert(status == 0);
 }
 
diff --git a/src/database.h b/src/database.h
index 0018950..b2e4853 100644
--- a/src/database.h
+++ b/src/database.h
@@ -1,7 +1,6 @@
 #ifndef NODE_SQLITE3_SRC_DATABASE_H
 #define NODE_SQLITE3_SRC_DATABASE_H
 
-#include <v8.h>
 #include <node.h>
 
 #include <string>
@@ -39,13 +38,11 @@ public:
         Baton(Database* db_, Handle<Function> cb_) :
                 db(db_), status(SQLITE_OK) {
             db->Ref();
-            uv_ref(uv_default_loop());
             request.data = this;
             callback = Persistent<Function>::New(cb_);
         }
         virtual ~Baton() {
             db->Unref();
-            uv_unref(uv_default_loop());
             callback.Dispose();
         }
     };
@@ -134,6 +131,9 @@ protected:
     static void Work_Exec(uv_work_t* req);
     static void Work_AfterExec(uv_work_t* req);
 
+    static Handle<Value> Wait(const Arguments& args);
+    static void Work_Wait(Baton* baton);
+
     static Handle<Value> Close(const Arguments& args);
     static void Work_BeginClose(Baton* baton);
     static void Work_Close(uv_work_t* req);
diff --git a/src/macros.h b/src/macros.h
index d5c91fd..dbd14d0 100644
--- a/src/macros.h
+++ b/src/macros.h
@@ -136,7 +136,7 @@ const char* sqlite_authorizer_string(int type);
     baton->stmt->locked = true;                                                \
     baton->stmt->db->pending++;                                                \
     int status = uv_queue_work(uv_default_loop(),                              \
-        &baton->request, Work_##type, Work_After##type);                       \
+        &baton->request, Work_##type, (uv_after_work_cb)Work_After##type);                       \
     assert(status == 0);
 
 #define STATEMENT_INIT(type)                                                   \
diff --git a/src/sqlite3.cc b/src/node_sqlite3.cc
similarity index 98%
rename from src/sqlite3.cc
rename to src/node_sqlite3.cc
index b73d08c..b588bbd 100644
--- a/src/sqlite3.cc
+++ b/src/node_sqlite3.cc
@@ -1,4 +1,3 @@
-#include <v8.h>
 #include <node.h>
 #include <node_buffer.h>
 
@@ -104,4 +103,4 @@ const char* sqlite_authorizer_string(int type) {
     }
 }
 
-NODE_MODULE(sqlite3_bindings, RegisterModule);
+NODE_MODULE(node_sqlite3, RegisterModule);
diff --git a/src/statement.cc b/src/statement.cc
index 07b6182..1ed3de4 100644
--- a/src/statement.cc
+++ b/src/statement.cc
@@ -1,7 +1,7 @@
 #include <string.h>
-#include <v8.h>
 #include <node.h>
 #include <node_buffer.h>
+#include <node_version.h>
 
 #include "macros.h"
 #include "database.h"
@@ -119,7 +119,7 @@ void Statement::Work_BeginPrepare(Database::Baton* baton) {
     assert(baton->db->open);
     baton->db->pending++;
     int status = uv_queue_work(uv_default_loop(),
-        &baton->request, Work_Prepare, Work_AfterPrepare);
+        &baton->request, Work_Prepare, (uv_after_work_cb)Work_AfterPrepare);
     assert(status == 0);
 }
 
@@ -191,9 +191,6 @@ template <class T> Values::Field*
     else if (source->IsDate()) {
         return new Values::Float(pos, source->NumberValue());
     }
-    else if (source->IsUndefined()) {
-        return NULL;
-    }
     else {
         return NULL;
     }
@@ -261,7 +258,7 @@ bool Statement::Bind(const Parameters parameters) {
     Parameters::const_iterator it = parameters.begin();
     Parameters::const_iterator end = parameters.end();
 
-    for (; it < end; it++) {
+    for (; it < end; ++it) {
         Values::Field* field = *it;
 
         if (field != NULL) {
@@ -545,7 +542,7 @@ void Statement::Work_AfterAll(uv_work_t* req) {
                 Local<Array> result(Array::New(baton->rows.size()));
                 Rows::const_iterator it = baton->rows.begin();
                 Rows::const_iterator end = baton->rows.end();
-                for (int i = 0; it < end; it++, i++) {
+                for (int i = 0; it < end; ++it, i++) {
                     result->Set(i, RowToJS(*it));
                     delete *it;
                 }
@@ -622,11 +619,10 @@ void Statement::Work_Each(uv_work_t* req) {
                 sqlite3_mutex_leave(mtx);
                 Row* row = new Row();
                 GetRow(row, stmt->handle);
-
-                pthread_mutex_lock(&async->mutex);
+                NODE_SQLITE3_MUTEX_LOCK(&async->mutex)
                 async->data.push_back(row);
                 retrieved++;
-                pthread_mutex_unlock(&async->mutex);
+                NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex)
 
                 uv_async_send(&async->watcher);
             }
@@ -649,7 +645,6 @@ void Statement::CloseCallback(uv_handle_t* handle) {
     assert(handle->data != NULL);
     Async* async = static_cast<Async*>(handle->data);
     delete async;
-    handle->data = NULL;
 }
 
 void Statement::AsyncEach(uv_async_t* handle, int status) {
@@ -659,9 +654,9 @@ void Statement::AsyncEach(uv_async_t* handle, int status) {
     while (true) {
         // Get the contents out of the data cache for us to process in the JS callback.
         Rows rows;
-        pthread_mutex_lock(&async->mutex);
+        NODE_SQLITE3_MUTEX_LOCK(&async->mutex)
         rows.swap(async->data);
-        pthread_mutex_unlock(&async->mutex);
+        NODE_SQLITE3_MUTEX_UNLOCK(&async->mutex)
 
         if (rows.empty()) {
             break;
@@ -673,7 +668,7 @@ void Statement::AsyncEach(uv_async_t* handle, int status) {
 
             Rows::const_iterator it = rows.begin();
             Rows::const_iterator end = rows.end();
-            for (int i = 0; it < end; it++, i++) {
+            for (int i = 0; it < end; ++it, i++) {
                 argv[1] = RowToJS(*it);
                 async->retrieved++;
                 TRY_CATCH_CALL(async->stmt->handle_, async->item_cb, 2, argv);
@@ -747,7 +742,7 @@ Local<Object> Statement::RowToJS(Row* row) {
 
     Row::const_iterator it = row->begin();
     Row::const_iterator end = row->end();
-    for (int i = 0; it < end; it++, i++) {
+    for (int i = 0; it < end; ++it, i++) {
         Values::Field* field = *it;
 
         Local<Value> value;
@@ -763,8 +758,11 @@ Local<Object> Statement::RowToJS(Row* row) {
                 value = Local<Value>(String::New(((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size()));
             } break;
             case SQLITE_BLOB: {
-                Buffer *buffer = Buffer::New(((Values::Blob*)field)->value, ((Values::Blob*)field)->length);
-                value = Local<Value>::New(buffer->handle_);
+#if NODE_VERSION_AT_LEAST(0, 11, 3)
+                value = Local<Value>::New(Buffer::New(((Values::Blob*)field)->value, ((Values::Blob*)field)->length));
+#else
+                value = Local<Value>::New(Buffer::New(((Values::Blob*)field)->value, ((Values::Blob*)field)->length)->handle_);
+#endif
             } break;
             case SQLITE_NULL: {
                 value = Local<Value>::New(Null());
diff --git a/src/statement.h b/src/statement.h
index 3f76307..0b6e60c 100644
--- a/src/statement.h
+++ b/src/statement.h
@@ -1,10 +1,10 @@
 #ifndef NODE_SQLITE3_SRC_STATEMENT_H
 #define NODE_SQLITE3_SRC_STATEMENT_H
 
-#include <v8.h>
 #include <node.h>
 
 #include "database.h"
+#include "threading.h"
 
 #include <cstdlib>
 #include <cstring>
@@ -86,7 +86,6 @@ public:
 
         Baton(Statement* stmt_, Handle<Function> cb_) : stmt(stmt_) {
             stmt->Ref();
-            uv_ref(uv_default_loop());
             request.data = this;
             callback = Persistent<Function>::New(cb_);
         }
@@ -96,7 +95,6 @@ public:
                 DELETE_FIELD(field);
             }
             stmt->Unref();
-            uv_unref(uv_default_loop());
             callback.Dispose();
         }
     };
@@ -158,7 +156,7 @@ public:
         uv_async_t watcher;
         Statement* stmt;
         Rows data;
-        pthread_mutex_t mutex;
+        NODE_SQLITE3_MUTEX_t;
         bool completed;
         int retrieved;
 
@@ -170,7 +168,7 @@ public:
         Async(Statement* st, uv_async_cb async_cb) :
                 stmt(st), completed(false), retrieved(0) {
             watcher.data = this;
-            pthread_mutex_init(&mutex, NULL);
+            NODE_SQLITE3_MUTEX_INIT
             stmt->Ref();
             uv_async_init(uv_default_loop(), &watcher, async_cb);
         }
@@ -179,7 +177,7 @@ public:
             stmt->Unref();
             item_cb.Dispose();
             completed_cb.Dispose();
-            pthread_mutex_destroy(&mutex);
+            NODE_SQLITE3_MUTEX_DESTROY
         }
     };
 
@@ -197,11 +195,6 @@ public:
         if (!finalized) Finalize();
     }
 
-protected:
-    static void Work_BeginPrepare(Database::Baton* baton);
-    static void Work_Prepare(uv_work_t* req);
-    static void Work_AfterPrepare(uv_work_t* req);
-
     WORK_DEFINITION(Bind);
     WORK_DEFINITION(Get);
     WORK_DEFINITION(Run);
@@ -209,10 +202,16 @@ protected:
     WORK_DEFINITION(Each);
     WORK_DEFINITION(Reset);
 
+    static Handle<Value> Finalize(const Arguments& args);
+
+protected:
+    static void Work_BeginPrepare(Database::Baton* baton);
+    static void Work_Prepare(uv_work_t* req);
+    static void Work_AfterPrepare(uv_work_t* req);
+
     static void AsyncEach(uv_async_t* handle, int status);
     static void CloseCallback(uv_handle_t* handle);
 
-    static Handle<Value> Finalize(const Arguments& args);
     static void Finalize(Baton* baton);
     void Finalize();
 
diff --git a/src/threading.h b/src/threading.h
new file mode 100644
index 0000000..fe738a4
--- /dev/null
+++ b/src/threading.h
@@ -0,0 +1,48 @@
+#ifndef NODE_SQLITE3_SRC_THREADING_H
+#define NODE_SQLITE3_SRC_THREADING_H
+
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+    #define NODE_SQLITE3_MUTEX_t HANDLE mutex;
+
+    #define NODE_SQLITE3_MUTEX_INIT mutex = CreateMutex(NULL, FALSE, NULL);
+
+    #define NODE_SQLITE3_MUTEX_LOCK(m) WaitForSingleObject(*m, INFINITE);
+
+    #define NODE_SQLITE3_MUTEX_UNLOCK(m) ReleaseMutex(*m);
+
+    #define NODE_SQLITE3_MUTEX_DESTROY CloseHandle(mutex);
+
+#elif defined(NODE_SQLITE3_BOOST_THREADING)
+
+#include <boost/thread/mutex.hpp>
+
+    #define NODE_SQLITE3_MUTEX_t boost::mutex mutex;
+
+    #define NODE_SQLITE3_MUTEX_INIT
+
+    #define NODE_SQLITE3_MUTEX_LOCK(m) (*m).lock();
+
+    #define NODE_SQLITE3_MUTEX_UNLOCK(m) (*m).unlock();
+
+    #define NODE_SQLITE3_MUTEX_DESTROY mutex.unlock();
+
+#else
+
+    #define NODE_SQLITE3_MUTEX_t pthread_mutex_t mutex;
+
+    #define NODE_SQLITE3_MUTEX_INIT pthread_mutex_init(&mutex,NULL);
+
+    #define NODE_SQLITE3_MUTEX_LOCK(m) pthread_mutex_lock(m);
+
+    #define NODE_SQLITE3_MUTEX_UNLOCK(m) pthread_mutex_unlock(m);
+
+    #define NODE_SQLITE3_MUTEX_DESTROY pthread_mutex_destroy(&mutex);
+
+#endif
+
+
+#endif // NODE_SQLITE3_SRC_THREADING_H
diff --git a/test/affected.test.js b/test/affected.test.js
index eb01bb6..f0bf192 100644
--- a/test/affected.test.js
+++ b/test/affected.test.js
@@ -1,15 +1,14 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test row changes and lastID'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var finished = false;
+describe('query properties', function() {
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database(':memory:');
+        db.run("CREATE TABLE foo (id INT, txt TEXT)", done);
+    });
 
-    db.serialize(function() {
-        db.run("CREATE TABLE foo (id INT, txt TEXT)");
+    it('should return the correct lastID', function(done) {
         var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
         var j = 1;
         for (var i = 0; i < 1000; i++) {
@@ -20,15 +19,14 @@ exports['test row changes and lastID'] = function(beforeExit) {
                 assert.equal(j++, this.lastID);
             });
         }
+        db.wait(done);
+    });
 
+    it('should return the correct changes count', function(done) {
         db.run("UPDATE foo SET id = id + 1 WHERE id % 2 = 0", function(err) {
             if (err) throw err;
             assert.equal(500, this.changes);
-            finished = true;
+            done();
         });
     });
-
-    beforeExit(function() {
-        assert.ok(finished);
-    })
-}
+});
diff --git a/test/blob.test.js b/test/blob.test.js
index 2627950..5c2b0d8 100644
--- a/test/blob.test.js
+++ b/test/blob.test.js
@@ -1,30 +1,37 @@
-var sqlite3 = require('sqlite3'),
-    Step = require('step'),
+var sqlite3 = require('..'),
     fs = require('fs'),
-    assert = require('assert')
+    assert = require('assert'),
     Buffer = require('buffer').Buffer;
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
 // lots of elmo
 var elmo = fs.readFileSync(__dirname + '/support/elmo.png');
 
-exports['blob test'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
+describe('blob', function() {
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database(':memory:');
+        db.run("CREATE TABLE elmos (id INT, image BLOB)", done);
+    });
+
     var total = 10;
     var inserted = 0;
     var retrieved = 0;
 
-    db.serialize(function() {
-        db.run('CREATE TABLE elmos (id INT, image BLOB)');
 
+    it('should insert blobs', function(done) {
         for (var i = 0; i < total; i++) {
             db.run('INSERT INTO elmos (id, image) VALUES (?, ?)', i, elmo, function(err) {
                 if (err) throw err;
                 inserted++;
             });
         }
+        db.wait(function() {
+            assert.equal(inserted, total);
+            done();
+        });
+    });
 
+    it('should retrieve the blobs', function(done) {
         db.all('SELECT id, image FROM elmos ORDER BY id', function(err, rows) {
             if (err) throw err;
             for (var i = 0; i < rows.length; i++) {
@@ -39,12 +46,9 @@ exports['blob test'] = function(beforeExit) {
 
                 retrieved++;
             }
-        });
 
+            assert.equal(retrieved, total);
+            done();
+        });
     });
-
-    beforeExit(function() {
-        assert.equal(inserted, total);
-        assert.equal(retrieved, total);
-    })
-}
+});
diff --git a/test/cache.test.js b/test/cache.test.js
index dbdc09c..9866dbf 100644
--- a/test/cache.test.js
+++ b/test/cache.test.js
@@ -1,47 +1,42 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 var helper = require('./support/helper');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test caching Database objects while opening'] = function(beforeExit) {
-    var filename = 'test/tmp/test_cache.db';
-    helper.deleteFile(filename);
-    var opened1 = false, opened2 = false
-    var db1 = new sqlite3.cached.Database(filename, function(err) {
-        if (err) throw err;
-        opened1 = true;
-    });
-    var db2 = new sqlite3.cached.Database(filename, function(err) {
-        if (err) throw err;
-        opened2 = true;
+describe('cache', function() {
+    before(function() {
+        helper.ensureExists('test/tmp');
     });
-    assert.equal(db1, db2);
 
-    beforeExit(function() {
-        assert.ok(opened1);
-        assert.ok(opened2);
+    it('should cache Database objects while opening', function(done) {
+        var filename = 'test/tmp/test_cache.db';
+        helper.deleteFile(filename);
+        var opened1 = false, opened2 = false;
+        var db1 = new sqlite3.cached.Database(filename, function(err) {
+            if (err) throw err;
+            opened1 = true;
+            if (opened1 && opened2) done();
+        });
+        var db2 = new sqlite3.cached.Database(filename, function(err) {
+            if (err) throw err;
+            opened2 = true;
+            if (opened1 && opened2) done();
+        });
+        assert.equal(db1, db2);
     });
-};
 
-exports['test caching Database objects after it is open'] = function(beforeExit) {
-    var filename = 'test/tmp/test_cache2.db';
-    helper.deleteFile(filename);
-    var opened1 = false, opened2 = false
-    var db1, db2;
-    db1 = new sqlite3.cached.Database(filename, function(err) {
-        if (err) throw err;
-        opened1 = true;
-        setTimeout(function() {
-            db2 = new sqlite3.cached.Database(filename, function(err) {
-                opened2 = true;
-            });
-        }, 100);
-    });
+    it('should cache Database objects after they are open', function(done) {
+        var filename = 'test/tmp/test_cache2.db';
+        helper.deleteFile(filename);
+        var db1, db2;
+        db1 = new sqlite3.cached.Database(filename, function(err) {
+            if (err) throw err;
+            process.nextTick(function() {
+                db2 = new sqlite3.cached.Database(filename, function(err) {
+                    done();
 
-    beforeExit(function() {
-        assert.equal(db1, db2);
-        assert.ok(opened1);
-        assert.ok(opened2);
+                });
+                assert.equal(db1, db2);
+            });
+        });
     });
-};
+});
diff --git a/test/constants.test.js b/test/constants.test.js
index 7ddbaf6..126d101 100644
--- a/test/constants.test.js
+++ b/test/constants.test.js
@@ -1,38 +1,40 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
+describe('constants', function() {
+    it('should have the right OPEN_* flags', function() {
+        assert.ok(sqlite3.OPEN_READONLY === 1);
+        assert.ok(sqlite3.OPEN_READWRITE === 2);
+        assert.ok(sqlite3.OPEN_CREATE === 4);
+    });
 
-exports['test constants'] = function() {
-    assert.ok(sqlite3.OPEN_READONLY === 1);
-    assert.ok(sqlite3.OPEN_READWRITE === 2);
-    assert.ok(sqlite3.OPEN_CREATE === 4);
-
-    assert.ok(sqlite3.OK === 0);
-    assert.ok(sqlite3.ERROR === 1);
-    assert.ok(sqlite3.INTERNAL === 2);
-    assert.ok(sqlite3.PERM === 3);
-    assert.ok(sqlite3.ABORT === 4);
-    assert.ok(sqlite3.BUSY === 5);
-    assert.ok(sqlite3.LOCKED === 6);
-    assert.ok(sqlite3.NOMEM === 7);
-    assert.ok(sqlite3.READONLY === 8);
-    assert.ok(sqlite3.INTERRUPT === 9);
-    assert.ok(sqlite3.IOERR === 10);
-    assert.ok(sqlite3.CORRUPT === 11);
-    assert.ok(sqlite3.NOTFOUND === 12);
-    assert.ok(sqlite3.FULL === 13);
-    assert.ok(sqlite3.CANTOPEN === 14);
-    assert.ok(sqlite3.PROTOCOL === 15);
-    assert.ok(sqlite3.EMPTY === 16);
-    assert.ok(sqlite3.SCHEMA === 17);
-    assert.ok(sqlite3.TOOBIG === 18);
-    assert.ok(sqlite3.CONSTRAINT === 19);
-    assert.ok(sqlite3.MISMATCH === 20);
-    assert.ok(sqlite3.MISUSE === 21);
-    assert.ok(sqlite3.NOLFS === 22);
-    assert.ok(sqlite3.AUTH === 23);
-    assert.ok(sqlite3.FORMAT === 24);
-    assert.ok(sqlite3.RANGE === 25);
-    assert.ok(sqlite3.NOTADB === 26);
-};
\ No newline at end of file
+    it('should have the right error flags', function() {
+        assert.ok(sqlite3.OK === 0);
+        assert.ok(sqlite3.ERROR === 1);
+        assert.ok(sqlite3.INTERNAL === 2);
+        assert.ok(sqlite3.PERM === 3);
+        assert.ok(sqlite3.ABORT === 4);
+        assert.ok(sqlite3.BUSY === 5);
+        assert.ok(sqlite3.LOCKED === 6);
+        assert.ok(sqlite3.NOMEM === 7);
+        assert.ok(sqlite3.READONLY === 8);
+        assert.ok(sqlite3.INTERRUPT === 9);
+        assert.ok(sqlite3.IOERR === 10);
+        assert.ok(sqlite3.CORRUPT === 11);
+        assert.ok(sqlite3.NOTFOUND === 12);
+        assert.ok(sqlite3.FULL === 13);
+        assert.ok(sqlite3.CANTOPEN === 14);
+        assert.ok(sqlite3.PROTOCOL === 15);
+        assert.ok(sqlite3.EMPTY === 16);
+        assert.ok(sqlite3.SCHEMA === 17);
+        assert.ok(sqlite3.TOOBIG === 18);
+        assert.ok(sqlite3.CONSTRAINT === 19);
+        assert.ok(sqlite3.MISMATCH === 20);
+        assert.ok(sqlite3.MISUSE === 21);
+        assert.ok(sqlite3.NOLFS === 22);
+        assert.ok(sqlite3.AUTH === 23);
+        assert.ok(sqlite3.FORMAT === 24);
+        assert.ok(sqlite3.RANGE === 25);
+        assert.ok(sqlite3.NOTADB === 26);
+    });
+});
diff --git a/test/database_fail.test.js b/test/database_fail.test.js
index 817772b..35589ec 100644
--- a/test/database_fail.test.js
+++ b/test/database_fail.test.js
@@ -1,212 +1,153 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-exports['test Database() without new'] = function(beforeExit) {
-    assert.throws(function() {
-        sqlite3.Database(':memory:');
-    }, (/Use the new operator to create new Database objects/));
-
-    assert.throws(function() {
-        sqlite3.Statement();
-    }, (/Use the new operator to create new Statement objects/));
-};
-
-exports['test Database#get prepare fail'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var completed = false;
-
-    db.get('SELECT id, txt FROM foo', function(err, row) {
-        if (err) {
-            assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
-            assert.equal(err.errno, sqlite3.ERROR);
-            assert.equal(err.code, 'SQLITE_ERROR');
-            completed = true;
-        }
-    });
-
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-};
-
-exports['test Database#all prepare fail'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var completed = false;
-
-    db.all('SELECT id, txt FROM foo', function(err, row) {
-        if (err) {
-            assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
-            assert.equal(err.errno, sqlite3.ERROR);
-            assert.equal(err.code, 'SQLITE_ERROR');
-            completed = true;
-        }
-    });
-
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-};
-
-exports['test Database#run prepare fail'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var completed = false;
-
-    db.run('SELECT id, txt FROM foo', function(err, row) {
-        if (err) {
-            assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
-            assert.equal(err.errno, sqlite3.ERROR);
-            assert.equal(err.code, 'SQLITE_ERROR');
-            completed = true;
-        }
-    });
-
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-};
-
-exports['test Database#each prepare fail'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var completed = false;
-
-    db.each('SELECT id, txt FROM foo', function(err, row) {
-        assert.ok(false, "this should not be called");
-    }, function(err, num) {
-        if (err) {
-            assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
-            assert.equal(err.errno, sqlite3.ERROR);
-            assert.equal(err.code, 'SQLITE_ERROR');
-            completed = true;
-        }
-    });
-
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-};
-
-exports['test Database#each prepare fail without completion handler'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var completed = false;
-
-    db.each('SELECT id, txt FROM foo', function(err, row) {
-        if (err) {
-            assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
-            assert.equal(err.errno, sqlite3.ERROR);
-            assert.equal(err.code, 'SQLITE_ERROR');
-            completed = true;
-        }
-        else {
-            assert.ok(false, 'this should not be called')
-        }
-    });
-
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-};
-
-exports['test Database#get prepare fail with param binding'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var completed = false;
-
-    db.get('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
-        if (err) {
-            assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
-            assert.equal(err.errno, sqlite3.ERROR);
-            assert.equal(err.code, 'SQLITE_ERROR');
-            completed = true;
-        }
-    });
-
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-};
-
-exports['test Database#all prepare fail with param binding'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var completed = false;
-
-    db.all('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
-        if (err) {
-            assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
-            assert.equal(err.errno, sqlite3.ERROR);
-            assert.equal(err.code, 'SQLITE_ERROR');
-            completed = true;
-        }
-    });
-
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-};
-
-exports['test Database#run prepare fail with param binding'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var completed = false;
-
-    db.run('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
-        if (err) {
-            assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
-            assert.equal(err.errno, sqlite3.ERROR);
-            assert.equal(err.code, 'SQLITE_ERROR');
-            completed = true;
-        }
-    });
-
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-};
-
-exports['test Database#each prepare fail with param binding'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var completed = false;
-
-    db.each('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
-        assert.ok(false, "this should not be called");
-    }, function(err, num) {
-        if (err) {
-            assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
-            assert.equal(err.errno, sqlite3.ERROR);
-            assert.equal(err.code, 'SQLITE_ERROR');
-            completed = true;
-        }
-    });
-
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-};
-
-exports['test Database#each prepare fail with param binding without completion handler'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var completed = false;
-
-    db.each('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
-        if (err) {
-            assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
-            assert.equal(err.errno, sqlite3.ERROR);
-            assert.equal(err.code, 'SQLITE_ERROR');
-            completed = true;
-        }
-        else {
-            assert.ok(false, 'this should not be called')
-        }
-    });
-
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-};
+describe('error handling', function() {
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database(':memory:', done);
+    });
+
+    it('throw when calling Database() without new', function() {
+        assert.throws(function() {
+            sqlite3.Database(':memory:');
+        }, (/Use the new operator to create new Database objects/));
+
+        assert.throws(function() {
+            sqlite3.Statement();
+        }, (/Use the new operator to create new Statement objects/));
+    });
+
+    it('should error when calling Database#get on a missing table', function(done) {
+        db.get('SELECT id, txt FROM foo', function(err, row) {
+            if (err) {
+                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
+                assert.equal(err.errno, sqlite3.ERROR);
+                assert.equal(err.code, 'SQLITE_ERROR');
+                done();
+            } else {
+                done(new Error('Completed query without error, but expected error'));
+            }
+        });
+    });
+
+    it('Database#all prepare fail', function(done) {
+        db.all('SELECT id, txt FROM foo', function(err, row) {
+            if (err) {
+                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
+                assert.equal(err.errno, sqlite3.ERROR);
+                assert.equal(err.code, 'SQLITE_ERROR');
+                done();
+            } else {
+                done(new Error('Completed query without error, but expected error'));
+            }
+        });
+    });
+
+    it('Database#run prepare fail', function(done) {
+        db.run('SELECT id, txt FROM foo', function(err, row) {
+            if (err) {
+                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
+                assert.equal(err.errno, sqlite3.ERROR);
+                assert.equal(err.code, 'SQLITE_ERROR');
+                done();
+            } else {
+                done(new Error('Completed query without error, but expected error'));
+            }
+        });
+    });
+
+    it('Database#each prepare fail', function(done) {
+        db.each('SELECT id, txt FROM foo', function(err, row) {
+            assert.ok(false, "this should not be called");
+        }, function(err, num) {
+            if (err) {
+                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
+                assert.equal(err.errno, sqlite3.ERROR);
+                assert.equal(err.code, 'SQLITE_ERROR');
+                done();
+            } else {
+                done(new Error('Completed query without error, but expected error'));
+            }
+        });
+    });
+
+    it('Database#each prepare fail without completion handler', function(done) {
+        db.each('SELECT id, txt FROM foo', function(err, row) {
+            if (err) {
+                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
+                assert.equal(err.errno, sqlite3.ERROR);
+                assert.equal(err.code, 'SQLITE_ERROR');
+                done();
+            } else {
+                done(new Error('Completed query without error, but expected error'));
+            }
+        });
+    });
+
+    it('Database#get prepare fail with param binding', function(done) {
+        db.get('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
+            if (err) {
+                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
+                assert.equal(err.errno, sqlite3.ERROR);
+                assert.equal(err.code, 'SQLITE_ERROR');
+                done();
+            } else {
+                done(new Error('Completed query without error, but expected error'));
+            }
+        });
+    });
+
+    it('Database#all prepare fail with param binding', function(done) {
+        db.all('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
+            if (err) {
+                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
+                assert.equal(err.errno, sqlite3.ERROR);
+                assert.equal(err.code, 'SQLITE_ERROR');
+                done();
+            } else {
+                done(new Error('Completed query without error, but expected error'));
+            }
+        });
+    });
+
+    it('Database#run prepare fail with param binding', function(done) {
+        db.run('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
+            if (err) {
+                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
+                assert.equal(err.errno, sqlite3.ERROR);
+                assert.equal(err.code, 'SQLITE_ERROR');
+                done();
+            } else {
+                done(new Error('Completed query without error, but expected error'));
+            }
+        });
+    });
+
+    it('Database#each prepare fail with param binding', function(done) {
+        db.each('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
+            assert.ok(false, "this should not be called");
+        }, function(err, num) {
+            if (err) {
+                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
+                assert.equal(err.errno, sqlite3.ERROR);
+                assert.equal(err.code, 'SQLITE_ERROR');
+                done();
+            } else {
+                done(new Error('Completed query without error, but expected error'));
+            }
+        });
+    });
+
+    it('Database#each prepare fail with param binding without completion handler', function(done) {
+        db.each('SELECT id, txt FROM foo WHERE id = ?', 1, function(err, row) {
+            if (err) {
+                assert.equal(err.message, 'SQLITE_ERROR: no such table: foo');
+                assert.equal(err.errno, sqlite3.ERROR);
+                assert.equal(err.code, 'SQLITE_ERROR');
+                done();
+            } else {
+                done(new Error('Completed query without error, but expected error'));
+            }
+        });
+    });
+});
diff --git a/test/each.test.js b/test/each.test.js
index cae634c..02729c6 100644
--- a/test/each.test.js
+++ b/test/each.test.js
@@ -1,42 +1,38 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
-var util = require('util');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test Statement#each'] = function(beforeExit) {
-    var db = new sqlite3.Database('test/support/big.db', sqlite3.OPEN_READONLY);
-
-    var total = 100000;
-    var retrieved = 0;
-
-    db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) {
-        if (err) throw err;
-        retrieved++;
-    });
-
-    beforeExit(function() {
-        assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows.");
+describe('each', function() {
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database('test/support/big.db', sqlite3.OPEN_READONLY, done);
     });
-};
 
-exports['test Statement#each with complete callback'] = function(beforeExit) {
-    var db = new sqlite3.Database('test/support/big.db', sqlite3.OPEN_READONLY);
+    it('retrieve 100,000 rows with Statement#each', function(done) {
+        var total = 100000;
+        var retrieved = 0;
 
-    var total = 10000;
-    var retrieved = 0;
-    var completed = false;
+        db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) {
+            if (err) throw err;
+            retrieved++;
+        });
 
-    db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) {
-        if (err) throw err;
-        retrieved++;
-    }, function(err, num) {
-        assert.equal(retrieved, num);
-        completed = true;
+        db.wait(function() {
+            assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows.");
+            done();
+        });
     });
 
-    beforeExit(function() {
-        assert.ok(completed);
-        assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows.");
+    it('Statement#each with complete callback', function(done) {
+        var total = 10000;
+        var retrieved = 0;
+
+        db.each('SELECT id, txt FROM foo LIMIT 0, ?', total, function(err, row) {
+            if (err) throw err;
+            retrieved++;
+        }, function(err, num) {
+            assert.equal(retrieved, num);
+            assert.equal(retrieved, total, "Only retrieved " + retrieved + " out of " + total + " rows.");
+            done();
+        });
     });
-};
+});
diff --git a/test/exec.test.js b/test/exec.test.js
index 59f72ef..e3d5532 100644
--- a/test/exec.test.js
+++ b/test/exec.test.js
@@ -1,20 +1,21 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 var fs = require('fs');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test Database#exec'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-    var finished = false;
-    var sql = fs.readFileSync('test/support/script.sql', 'utf8');
+describe('exec', function() {
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database(':memory:', done);
+    });
 
-    db.exec(sql, function(err) {
-        if (err) throw err;
+    it('Database#exec', function(done) {
+        var sql = fs.readFileSync('test/support/script.sql', 'utf8');
+        db.exec(sql, done);
+    });
 
+    it('retrieve database structure', function(done) {
         db.all("SELECT type, name FROM sqlite_master ORDER BY type, name", function(err, rows) {
             if (err) throw err;
-
             assert.deepEqual(rows, [
                 { type: 'index', name: 'grid_key_lookup' },
                 { type: 'index', name: 'grid_utfgrid_lookup' },
@@ -32,12 +33,7 @@ exports['test Database#exec'] = function(beforeExit) {
                 { type: 'view', name: 'grids' },
                 { type: 'view', name: 'tiles' }
             ]);
-
-            finished = true;
+            done();
         });
     });
-
-    beforeExit(function() {
-        assert.ok(finished);
-    });
-};
+});
diff --git a/test/extension.test.js b/test/extension.test.js
index 5d7c4b8..36c350f 100644
--- a/test/extension.test.js
+++ b/test/extension.test.js
@@ -1,25 +1,26 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
-var path = require('path');
+var exists = require('fs').existsSync || require('path').existsSync;
 
-if (process.setMaxListeners) process.setMaxListeners(0);
+/*
+
+// disabled because this is not a generically safe test to run on all systems
 
 var spatialite_ext = '/usr/local/lib/libspatialite.dylib';
 
-exports['test loadExtension'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-    var completed = false;
+describe('loadExtension', function(done) {
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database(':memory:', done);
+    });
 
-    if (path.existsSync(spatialite_ext)) {
-        db.loadExtension(spatialite_ext, function(err) {
-            if (err) throw err;
-            completed = true;
+    if (exists(spatialite_ext)) {
+        it('libspatialite', function(done) {
+            db.loadExtension(spatialite_ext, done);
         });
     } else {
-        completed = true;
+        it('libspatialite');
     }
+});
 
-    beforeExit(function() {
-        assert.ok(completed);
-    });
-}
+*/
\ No newline at end of file
diff --git a/test/fts-content.test.js b/test/fts-content.test.js
new file mode 100644
index 0000000..c7095a9
--- /dev/null
+++ b/test/fts-content.test.js
@@ -0,0 +1,13 @@
+var sqlite3 = require('..');
+var assert = require('assert');
+
+describe('fts', function() {
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database(':memory:', done);
+    });
+
+    it('should create a new fts4 table', function(done) {
+        db.exec('CREATE VIRTUAL TABLE t1 USING fts4(content="", a, b, c);', done);
+    });
+});
diff --git a/test/map.test.js b/test/map.test.js
index 52ffc76..db190a7 100644
--- a/test/map.test.js
+++ b/test/map.test.js
@@ -1,74 +1,63 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test Database#map() with two columns'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var count = 10;
-    var inserted = 0;
-    var retrieved = false;
-
-    db.serialize(function() {
-       db.run("CREATE TABLE foo (id INT, value TEXT)");
-
-       var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
-       for (var i = 5; i < count; i++) {
-           stmt.run(i, 'Value for ' + i, function(err) {
-               if (err) throw err;
-               inserted++;
-           });
-       }
-       stmt.finalize();
-
-       db.map("SELECT * FROM foo", function(err, map) {
-           retrieved = true;
-           assert.deepEqual(map, { 5: 'Value for 5', 6: 'Value for 6', 7: 'Value for 7', 8: 'Value for 8', 9: 'Value for 9' });
-       });
-
-    });
-
-    beforeExit(function() {
-        assert.equal(inserted, 5);
-        assert.ok(retrieved);
-    });
-};
-
-exports['test Database#map() with three columns'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var count = 10;
-    var inserted = 0;
-    var retrieved = false;
-
-    db.serialize(function() {
-       db.run("CREATE TABLE foo (id INT, value TEXT, other TEXT)");
-
-       var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?)");
-       for (var i = 5; i < count; i++) {
-           stmt.run(i, 'Value for ' + i, null, function(err) {
-               if (err) throw err;
-               inserted++;
-           });
-       }
-       stmt.finalize();
-
-       db.map("SELECT * FROM foo", function(err, map) {
-           retrieved = true;
-           assert.deepEqual(map, {
-               5: { id: 5, value: 'Value for 5', other: null },
-               6: { id: 6, value: 'Value for 6', other: null },
-               7: { id: 7, value: 'Value for 7', other: null },
-               8: { id: 8, value: 'Value for 8', other: null },
-               9: { id: 9, value: 'Value for 9', other: null }
-           });
-       });
-
+describe('map', function() {
+    it('test Database#map() with two columns', function(done) {
+        var count = 10;
+        var inserted = 0;
+
+        var db = new sqlite3.Database(':memory:');
+        db.serialize(function() {
+            db.run("CREATE TABLE foo (id INT, value TEXT)");
+
+            var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
+            for (var i = 5; i < count; i++) {
+                stmt.run(i, 'Value for ' + i, function(err) {
+                    if (err) throw err;
+                    inserted++;
+                });
+            }
+            stmt.finalize();
+
+            db.map("SELECT * FROM foo", function(err, map) {
+                if (err) throw err;
+                assert.deepEqual(map, { 5: 'Value for 5', 6: 'Value for 6', 7: 'Value for 7', 8: 'Value for 8', 9: 'Value for 9' });
+                assert.equal(inserted, 5);
+                done();
+            });
+        });
     });
 
-    beforeExit(function() {
-        assert.equal(inserted, 5);
-        assert.ok(retrieved);
+    it('test Database#map() with three columns', function(done) {
+        var db = new sqlite3.Database(':memory:');
+
+        var count = 10;
+        var inserted = 0;
+
+        db.serialize(function() {
+            db.run("CREATE TABLE foo (id INT, value TEXT, other TEXT)");
+
+            var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?)");
+            for (var i = 5; i < count; i++) {
+                stmt.run(i, 'Value for ' + i, null, function(err) {
+                    if (err) throw err;
+                    inserted++;
+                });
+            }
+            stmt.finalize();
+
+            db.map("SELECT * FROM foo", function(err, map) {
+                if (err) throw err;
+                assert.deepEqual(map, {
+                    5: { id: 5, value: 'Value for 5', other: null },
+                    6: { id: 6, value: 'Value for 6', other: null },
+                    7: { id: 7, value: 'Value for 7', other: null },
+                    8: { id: 8, value: 'Value for 8', other: null },
+                    9: { id: 9, value: 'Value for 9', other: null }
+                });
+                assert.equal(inserted, 5);
+                done();
+            });
+        });
     });
-};
+});
diff --git a/test/named_columns.test.js b/test/named_columns.test.js
index 7ad261d..fcb8f2c 100644
--- a/test/named_columns.test.js
+++ b/test/named_columns.test.js
@@ -1,33 +1,29 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test named columns'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var finished = false;
+describe('named columns', function() {
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database(':memory:', done);
+    });
 
-    db.serialize(function() {
-        db.run("CREATE TABLE foo (txt TEXT, num INT)");
+    it('should create the table', function(done) {
+        db.run("CREATE TABLE foo (txt TEXT, num INT)", done);
+    });
 
+    it('should insert a value', function(done) {
         db.run("INSERT INTO foo VALUES($text, $id)", {
             $id: 1,
             $text: "Lorem Ipsum"
-        });
+        }, done);
+    });
 
+    it('should retrieve the values', function(done) {
         db.get("SELECT txt, num FROM foo ORDER BY num", function(err, row) {
             if (err) throw err;
-
             assert.equal(row.txt, "Lorem Ipsum");
             assert.equal(row.num, 1);
-
-            finished = true;
+            done();
         });
-
-    });
-
-    beforeExit(function() {
-        assert.ok(finished);
     });
-};
\ No newline at end of file
+});
diff --git a/test/named_params.test.js b/test/named_params.test.js
index d942ef7..c03f2dd 100644
--- a/test/named_params.test.js
+++ b/test/named_params.test.js
@@ -1,42 +1,56 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test named parameters'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var finished = false;
+describe('named parameters', function() {
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database(':memory:', done);
+    });
 
-    db.serialize(function() {
-        db.run("CREATE TABLE foo (txt TEXT, num INT)");
+    it('should create the table', function(done) {
+        db.run("CREATE TABLE foo (txt TEXT, num INT)", done);
+    });
 
+    it('should insert a value with $ placeholders', function(done) {
         db.run("INSERT INTO foo VALUES($text, $id)", {
             $id: 1,
             $text: "Lorem Ipsum"
-        });
+        }, done);
+    });
 
+    it('should insert a value with : placeholders', function(done) {
         db.run("INSERT INTO foo VALUES(:text, :id)", {
-            ":id": 2,
-            ":text": "Dolor Sit Amet"
-        });
+            ':id': 2,
+            ':text': "Dolor Sit Amet"
+        }, done);
+    });
 
+    it('should insert a value with @ placeholders', function(done) {
         db.run("INSERT INTO foo VALUES(@txt, @id)", {
             "@id": 3,
             "@txt": "Consectetur Adipiscing Elit"
-        });
+        }, done);
+    });
 
-        db.run("INSERT INTO foo VALUES(@txt, @id)", [ 'Sed Do Eiusmod', 4 ]);
-        db.run("INSERT INTO foo VALUES(?2, ?4)", [ null, 'Tempor Incididunt', null, 5 ]);
+    it('should insert a value with @ placeholders using an array', function(done) {
+        db.run("INSERT INTO foo VALUES(@txt, @id)", [ 'Sed Do Eiusmod', 4 ], done);
+    });
+
+    it('should insert a value with indexed placeholders', function(done) {
+        db.run("INSERT INTO foo VALUES(?2, ?4)",
+            [ null, 'Tempor Incididunt', null, 5 ], done);
+    });
 
+    it('should insert a value with autoindexed placeholders', function(done) {
         db.run("INSERT INTO foo VALUES(?, ?)", {
             2: 6,
             1: "Ut Labore Et Dolore"
-        });
+        }, done);
+    });
 
+    it('should retrieve all inserted values', function(done) {
         db.all("SELECT txt, num FROM foo ORDER BY num", function(err, rows) {
             if (err) throw err;
-
             assert.equal(rows[0].txt, "Lorem Ipsum");
             assert.equal(rows[0].num, 1);
             assert.equal(rows[1].txt, "Dolor Sit Amet");
@@ -49,12 +63,7 @@ exports['test named parameters'] = function(beforeExit) {
             assert.equal(rows[4].num, 5);
             assert.equal(rows[5].txt, "Ut Labore Et Dolore");
             assert.equal(rows[5].num, 6);
-
-            finished = true;
+            done();
         });
     });
-
-    beforeExit(function() {
-        assert.ok(finished);
-    });
-};
\ No newline at end of file
+});
diff --git a/test/null_error.test.js b/test/null_error.test.js
index 713d42b..8c34d9b 100644
--- a/test/null_error.test.js
+++ b/test/null_error.test.js
@@ -1,17 +1,22 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 var helper = require('./support/helper');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test SQLITE_OK error'] = function(beforeExit) {
-    var completed = false;
+describe('null error', function() {
     var filename = 'test/tmp/test_sqlite_ok_error.db';
-    helper.deleteFile(filename);
-    var db = new sqlite3.Database(filename);
+    var db;
 
-    db.run("CREATE TABLE febp_data (leacode TEXT, leaname TEXT, state TEXT, postcode TEXT, fips TEXT, titleistim TEXT, ideastim TEXT, ideapool TEXT, ideapoolname TEXT, localebasis TEXT, localetype2 TEXT, version TEXT, leacount_2006 TEXT, ppexpend_2005 TEXT, ppexpend_2006 TEXT, ppexpend_2007 TEXT, ppexpend_2008 TEXT, ppexpendrank_2006 TEXT, ppexpendrank_2007 TEXT, ppexpendrank_2008 TEXT, rankppexpend_2005 TEXT, opbud_2004 TEXT, opbud_2006 TEXT, opbud_2007 TEXT, opbud_2008 TEXT, titlei_200 [...]
-        if (err) throw err;
+    before(function(done) {
+        helper.ensureExists('test/tmp');
+        helper.deleteFile(filename);
+        db = new sqlite3.Database(filename, done);
+    });
+
+    it('should create a table', function(done) {
+        db.run("CREATE TABLE febp_data (leacode TEXT, leaname TEXT, state TEXT, postcode TEXT, fips TEXT, titleistim TEXT, ideastim TEXT, ideapool TEXT, ideapoolname TEXT, localebasis TEXT, localetype2 TEXT, version TEXT, leacount_2006 TEXT, ppexpend_2005 TEXT, ppexpend_2006 TEXT, ppexpend_2007 TEXT, ppexpend_2008 TEXT, ppexpendrank_2006 TEXT, ppexpendrank_2007 TEXT, ppexpendrank_2008 TEXT, rankppexpend_2005 TEXT, opbud_2004 TEXT, opbud_2006 TEXT, opbud_2007 TEXT, opbud_2008 TEXT, titlei [...]
+    });
+
+    it('should insert rows with lots of null values', function(done) {
         var stmt = db.prepare('INSERT INTO febp_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', function(err) {
             if (err) throw err;
 
@@ -21,15 +26,16 @@ exports['test SQLITE_OK error'] = function(beforeExit) {
 
             stmt.finalize(function(err) {
                 if (err) throw err;
-                completed = true;
+                done();
             });
         });
-
     });
 
-    beforeExit(function() {
-        assert.ok(completed);
+    it('should have created the database', function() {
         assert.fileExists(filename);
+    });
+
+    after(function() {
         helper.deleteFile(filename);
     });
-};
\ No newline at end of file
+});
diff --git a/test/open_close.test.js b/test/open_close.test.js
index 2f7d002..649e217 100644
--- a/test/open_close.test.js
+++ b/test/open_close.test.js
@@ -1,129 +1,129 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 var fs = require('fs');
 var helper = require('./support/helper');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['open and close non-existent database'] = function(beforeExit) {
-    var opened, closed;
-
-    helper.deleteFile('test/tmp/test_create.db');
-    var db = new sqlite3.Database('test/tmp/test_create.db', function(err) {
-        if (err) throw err;
-        assert.ok(!opened);
-        assert.ok(!closed);
-        opened = true;
+describe('open/close', function() {
+    before(function() {
+        helper.ensureExists('test/tmp');
     });
 
-    db.close(function(err) {
-        if (err) throw err;
-        assert.ok(opened);
-        assert.ok(!closed);
-        closed = true;
-    });
+    describe('open and close non-existant database', function() {
+        before(function() {
+            helper.deleteFile('test/tmp/test_create.db');
+        });
 
-    beforeExit(function() {
-        assert.ok(opened, 'Database not opened');
-        assert.ok(closed, 'Database not closed');
-        assert.fileExists('test/tmp/test_create.db');
-        helper.deleteFile('test/tmp/test_create.db');
-    });
-};
+        var db;
+        it('should open the database', function(done) {
+            db = new sqlite3.Database('test/tmp/test_create.db', done);
+        });
+
+        it('should close the database', function(done) {
+            db.close(done);
+        });
 
-exports['open inaccessible database'] = function(beforeExit) {
-    var notOpened;
+        it('should have created the file', function() {
+            assert.fileExists('test/tmp/test_create.db');
+        });
 
-    var db = new sqlite3.Database('/usr/bin/test.db', function(err) {
-        if (err && err.errno === sqlite3.CANTOPEN) {
-            notOpened = true;
-        }
-        else if (err) throw err;
+        after(function() {
+            helper.deleteFile('test/tmp/test_create.db');
+        });
     });
 
-    beforeExit(function() {
-        assert.ok(notOpened, 'Database could be opened');
+    it('should not be unable to open an inaccessible database', function(done) {
+        // NOTE: test assumes that the user is not allowed to create new files
+        // in /usr/bin.
+        var db = new sqlite3.Database('/test/tmp/directory-does-not-exist/test.db', function(err) {
+            if (err && err.errno === sqlite3.CANTOPEN) {
+                done();
+            } else if (err) {
+                done(err);
+            } else {
+                done('Opened database that should be inaccessible');
+            }
+        });
     });
-};
 
 
-exports['open non-existent database without create'] = function(beforeExit) {
-    var notOpened;
+    describe('creating database without create flag', function() {
+        before(function() {
+            helper.deleteFile('test/tmp/test_readonly.db');
+        });
 
-    helper.deleteFile('tmp/test_readonly.db');
-    var db = new sqlite3.Database('tmp/test_readonly.db', sqlite3.OPEN_READONLY,
-        function(err) {
-            if (err && err.errno === sqlite3.CANTOPEN) {
-                notOpened = true;
-            }
-            else if (err) throw err;
+        it('should fail to open the database', function(done) {
+            new sqlite3.Database('tmp/test_readonly.db', sqlite3.OPEN_READONLY, function(err) {
+                if (err && err.errno === sqlite3.CANTOPEN) {
+                    done();
+                } else if (err) {
+                    done(err);
+                } else {
+                    done('Created database without create flag');
+                }
+            });
         });
 
-    beforeExit(function() {
-        assert.ok(notOpened, 'Database could be opened');
-        assert.fileDoesNotExist('tmp/test_readonly.db');
+        it('should not have created the file', function() {
+            assert.fileDoesNotExist('test/tmp/test_readonly.db');
+        });
+
+        after(function() {
+            helper.deleteFile('test/tmp/test_readonly.db');
+        });
     });
-};
 
-exports['open and close memory database queuing'] = function(beforeExit) {
-    var opened = 0, closed = 0, closeFailed = 0;
+    describe('open and close memory database queuing', function() {
+        var db;
+        it('should open the database', function(done) {
+            db = new sqlite3.Database(':memory:', done);
+        });
 
-    var db = new sqlite3.Database(':memory:', function openedCallback(err) {
-        if (err) throw err;
-        opened++;
-    });
+        it('should close the database', function(done) {
+            db.close(done);
+        });
 
-    function closedCallback(err) {
-        if (closed > 0) {
-            assert.ok(err, 'No error object received on second close');
-            assert.ok(err.errno === sqlite3.MISUSE);
-            closeFailed++;
-        }
-        else if (err) throw err;
-        else closed++;
-    }
-
-    db.close(closedCallback);
-    db.close(closedCallback);
-
-    beforeExit(function() {
-        assert.equal(opened, 1, 'Database not opened');
-        assert.equal(closed, 1, 'Database not closed');
-        assert.equal(closeFailed, 1, 'Database could be closed again');
+        it('shouldn\'t close the database again', function(done) {
+            db.close(function(err) {
+                assert.ok(err, 'No error object received on second close');
+                assert.ok(err.errno === sqlite3.MISUSE);
+                done();
+            });
+        });
     });
-};
 
-exports['test closing with open statements'] = function(beforeExit) {
-    var completed = false;
-    var completedSecond = false;
-    var closed = false;
+    describe('closing with unfinalized statements', function(done) {
+        var completed = false;
+        var completedSecond = false;
+        var closed = false;
 
-    var db = new sqlite3.Database(':memory:');
+        var db;
+        before(function() {
+            db = new sqlite3.Database(':memory:', done);
+        });
+
+        it('should create a table', function(done) {
+            db.run("CREATE TABLE foo (id INT, num INT)", done);
+        });
 
-    db.serialize(function() {
-        db.run("CREATE TABLE foo (id INT, num INT)");
+        var stmt;
+        it('should prepare/run a statement', function(done) {
+            stmt = db.prepare('INSERT INTO foo VALUES (?, ?)');
+            stmt.run(1, 2, done);
+        });
 
-        var stmt = db.prepare('INSERT INTO foo VALUES (?, ?)')
-        stmt.run(1, 2);
+        it('should fail to close the database', function(done) {
+            db.close(function(err) {
+                assert.ok(err.message,
+                    "SQLITE_BUSY: unable to close due to unfinalised statements");
+                done();
+            });
+        });
 
-        db.close(function(err) {
-            assert.ok(err.message,
-                "SQLITE_BUSY: unable to close due to unfinalised statements");
-            completed = true;
+        it('should succeed to close the database after finalizing', function(done) {
             stmt.run(3, 4, function() {
-                completedSecond = true;
                 stmt.finalize();
-                db.close(function(err) {
-                    if (err) throw err;
-                    closed = true;
-                });
+                db.close(done);
             });
         });
     });
-
-    beforeExit(function() {
-        assert.ok(completed);
-        assert.ok(completedSecond);
-        assert.ok(closed);
-    });
-};
+});
diff --git a/test/other_objects.test.js b/test/other_objects.test.js
index d38bf8c..1559517 100644
--- a/test/other_objects.test.js
+++ b/test/other_objects.test.js
@@ -1,36 +1,45 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test Date() and RegExp() serialization'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
+describe('data types', function() {
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database(':memory:');
+        db.run("CREATE TABLE txt_table (txt TEXT)");
+        db.run("CREATE TABLE int_table (int INTEGER)");
+        db.run("CREATE TABLE flt_table (flt FLOAT)");
+        db.wait(done);
+    });
 
-    var retrieved = false;
-    var date = new Date();
+    beforeEach(function(done) {
+        db.exec('DELETE FROM txt_table; DELETE FROM int_table; DELETE FROM flt_table;', done);
+    });
 
-    db.serialize(function() {
-        db.run("CREATE TABLE foo (txt TEXT, num FLOAT)");
-        db.run("INSERT INTO foo VALUES(?, ?)", (/^f\noo/), date);
-        db.get("SELECT txt, num FROM foo", function(err, row) {
+    it('should serialize Date()', function(done) {
+        var date = new Date();
+        db.run("INSERT INTO int_table VALUES(?)", date, function (err) {
             if (err) throw err;
-            assert.equal(row.txt, '/^f\\noo/');
-            assert.equal(row.num, +date);
-            retrieved = true;
-        })
+            db.get("SELECT int FROM int_table", function(err, row) {
+                if (err) throw err;
+                assert.equal(row.int, +date);
+                done();
+            });
+        });
     });
 
-    beforeExit(function() {
-        assert.ok(retrieved);
+    it('should serialize RegExp()', function(done) {
+        var regexp = /^f\noo/;
+        db.run("INSERT INTO txt_table VALUES(?)", regexp, function (err) {
+            if (err) throw err;
+            db.get("SELECT txt FROM txt_table", function(err, row) {
+                if (err) throw err;
+                assert.equal(row.txt, String(regexp));
+                done();
+            });
+        });
     });
-};
-
-exports['test large floats'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var retrieved = false;
 
-    var numbers = [
+    [
         4294967296.249,
         Math.PI,
         3924729304762836.5,
@@ -42,39 +51,20 @@ exports['test large floats'] = function(beforeExit) {
         -9.293476892934982e+300,
         -2.3948728634826374e+83,
         -Infinity
-    ];
-
-    db.serialize(function() {
-        db.run("CREATE TABLE foo (id INT, num FLOAT)");
-
-        var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
-        for (var i = 0; i < numbers.length; i++) {
-            stmt.run(i, numbers[i]);
-        }
-        stmt.finalize();
-
-        db.all("SELECT num FROM foo ORDER BY id", function(err, rows) {
-            if (err) throw err;
-
-            for (var i = 0; i < rows.length; i++) {
-                assert.equal(numbers[i], rows[i].num);
-            }
-
-            retrieved = true;
-        })
+    ].forEach(function(flt) {
+        it('should serialize float ' + flt, function(done) {
+            db.run("INSERT INTO flt_table VALUES(?)", flt, function (err) {
+                if (err) throw err;
+                db.get("SELECT flt FROM flt_table", function(err, row) {
+                    if (err) throw err;
+                    assert.equal(row.flt, flt);
+                    done();
+                });
+            });
+        });
     });
 
-    beforeExit(function() {
-        assert.ok(retrieved);
-    });
-};
-
-exports['test large integers'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var retrieved = false;
-
-    var numbers = [
+    [
         4294967299,
         3924729304762836,
         new Date().valueOf(),
@@ -84,29 +74,16 @@ exports['test large integers'] = function(beforeExit) {
         -9.293476892934982e+300,
         -2.3948728634826374e+83,
         -Infinity
-    ];
-
-    db.serialize(function() {
-        db.run("CREATE TABLE foo (id INT, num INTEGER)");
-
-        var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
-        for (var i = 0; i < numbers.length; i++) {
-            stmt.run(i, numbers[i]);
-        }
-        stmt.finalize();
-
-        db.all("SELECT num FROM foo ORDER BY id", function(err, rows) {
-            if (err) throw err;
-
-            for (var i = 0; i < rows.length; i++) {
-                assert.equal(numbers[i], rows[i].num);
-            }
-
-            retrieved = true;
-        })
-    });
-
-    beforeExit(function() {
-        assert.ok(retrieved);
+    ].forEach(function(integer) {
+        it('should serialize integer ' + integer, function(done) {
+            db.run("INSERT INTO int_table VALUES(?)", integer, function (err) {
+                if (err) throw err;
+                db.get("SELECT int AS integer FROM int_table", function(err, row) {
+                    if (err) throw err;
+                    assert.equal(row.integer, integer);
+                    done();
+                });
+            });
+        });
     });
-};
+});
diff --git a/test/parallel_insert.test.js b/test/parallel_insert.test.js
index 9011733..fc3829b 100644
--- a/test/parallel_insert.test.js
+++ b/test/parallel_insert.test.js
@@ -1,38 +1,44 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
-var util = require('util');
 var helper = require('./support/helper');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test parallel inserts'] = function(beforeExit) {
-    var completed = false;
-    helper.deleteFile('test/tmp/test_parallel_inserts.db');
-    var db = new sqlite3.Database('test/tmp/test_parallel_inserts.db');
+describe('parallel', function() {
+    var db;
+    before(function(done) {
+        helper.deleteFile('test/tmp/test_parallel_inserts.db');
+        helper.ensureExists('test/tmp');
+        db = new sqlite3.Database('test/tmp/test_parallel_inserts.db', done);
+    });
 
     var columns = [];
     for (var i = 0; i < 128; i++) {
         columns.push('id' + i);
     }
 
-    db.serialize(function() {
-        db.run("CREATE TABLE foo (" + columns + ")");
+    it('should create the table', function(done) {
+        db.run("CREATE TABLE foo (" + columns + ")", done);
     });
 
-    for (var i = 0; i < 1000; i++) {
-        for (var values = [], j = 0; j < columns.length; j++) {
-            values.push(i * j);
+    it('should insert in parallel', function(done) {
+        for (var i = 0; i < 1000; i++) {
+            for (var values = [], j = 0; j < columns.length; j++) {
+                values.push(i * j);
+            }
+            db.run("INSERT INTO foo VALUES (" + values + ")");
         }
-        db.run("INSERT INTO foo VALUES (" + values + ")");
-    }
 
-    db.close(function() {
-        completed = true;
+        db.wait(done);
     });
 
-    beforeExit(function() {
-        assert.ok(completed);
+    it('should close the database', function(done) {
+        db.close(done);
+    });
+
+    it('should verify that the database exists', function() {
         assert.fileExists('test/tmp/test_parallel_inserts.db');
+    });
+
+    after(function() {
         helper.deleteFile('test/tmp/test_parallel_inserts.db');
     });
-};
+});
diff --git a/test/prepare.test.js b/test/prepare.test.js
index 51be351..eb9889e 100644
--- a/test/prepare.test.js
+++ b/test/prepare.test.js
@@ -1,406 +1,376 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
-var Step = require('step');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test simple prepared statement with invalid SQL'] = function(beforeExit) {
-    var error = false;
-    var db = new sqlite3.Database(':memory:');
-    db.prepare('CRATE TALE foo text bar)', function(err, statement) {
-        if (err && err.errno == sqlite3.ERROR &&
-            err.message === 'SQLITE_ERROR: near "CRATE": syntax error') {
-            error = true;
-        }
-        else throw err;
-    }).finalize();
-    db.close();
+describe('prepare', function() {
+    describe('invalid SQL', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database(':memory:', done); });
+
+        var stmt;
+        it('should fail preparing a statement with invalid SQL', function(done) {
+            stmt = db.prepare('CRATE TALE foo text bar)', function(err, statement) {
+                if (err && err.errno == sqlite3.ERROR &&
+                    err.message === 'SQLITE_ERROR: near "CRATE": syntax error') {
+                    done();
+                }
+                else throw err;
+            });
+        });
 
-    beforeExit(function() {
-        assert.ok(error, 'No error thrown for invalid SQL');
+        after(function(done) { db.close(done); });
     });
-};
-
-exports['test simple prepared statement'] = function(beforeExit) {
-    var run = false;
-    var db = new sqlite3.Database(':memory:');
-    db.prepare("CREATE TABLE foo (text bar)")
-        .run(function(err) {
-            if (err) throw err;
-            run = true;
-        })
-        .finalize()
-    .close();
-
-    beforeExit(function() {
-        assert.ok(run, 'Database query did not run');
+
+    describe('simple prepared statement', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database(':memory:', done); });
+
+        it('should prepare, run and finalize the statement', function(done) {
+            db.prepare("CREATE TABLE foo (text bar)")
+                .run()
+                .finalize(done);
+        });
+
+        after(function(done) { db.close(done); });
     });
-};
 
-exports['test inserting and retrieving rows'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
+    describe('inserting and retrieving rows', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database(':memory:', done); });
+
+        var inserted = 0;
+        var retrieved = 0;
 
-    var inserted = 0;
-    var retrieved = 0;
+        // We insert and retrieve that many rows.
+        var count = 1000;
 
-    // We insert and retrieve that many rows.
-    var count = 1000;
+        it('should create the table', function(done) {
+            db.prepare("CREATE TABLE foo (txt text, num int, flt float, blb blob)").run().finalize(done);
+        });
 
-    Step(
-        function() {
-            db.prepare("CREATE TABLE foo (txt text, num int, flt float, blb blob)").run(this);
-        },
-        function(err) {
-            if (err) throw err;
-            var group = this.group();
+        it('should insert ' + count + ' rows', function(done) {
             for (var i = 0; i < count; i++) {
-                db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)")
-                  .run(
-                      'String ' + i,
-                      i,
-                      i * Math.PI,
-                      // null (SQLite sets this implicitly)
-                      group()
-                  );
+                db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)").run(
+                    'String ' + i,
+                    i,
+                    i * Math.PI,
+                    // null (SQLite sets this implicitly)
+                    function(err) {
+                        if (err) throw err;
+                        inserted++;
+                    }
+                ).finalize(function(err) {
+                    if (err) throw err;
+                    if (inserted == count) done();
+                });
             }
-        },
-        function(err, rows) {
-            if (err) throw err;
-            inserted += rows.length;
+        });
+
+        it('should prepare a statement and run it ' + (count + 5) + ' times', function(done) {
             var stmt = db.prepare("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err) {
                 if (err) throw err;
                 assert.equal(stmt.sql, 'SELECT txt, num, flt, blb FROM foo ORDER BY num');
             });
 
-            var group = this.group();
-            for (var i = 0; i < count + 5; i++) {
-                stmt.get(group());
-            }
-        },
-        function(err, rows) {
-            if (err) throw err;
-            assert.equal(count + 5, rows.length, "Didn't run all queries");
+            for (var i = 0; i < count + 5; i++) (function(i) {
+                stmt.get(function(err, row) {
+                    if (err) throw err;
 
-            for (var i = 0; i < count; i++) {
-                assert.equal(rows[i].txt, 'String ' + i);
-                assert.equal(rows[i].num, i);
-                assert.equal(rows[i].flt, i * Math.PI);
-                assert.equal(rows[i].blb, null);
-                retrieved++;
-            }
+                    if (retrieved >= 1000) {
+                        assert.equal(row, undefined);
+                    } else {
+                        assert.equal(row.txt, 'String ' + i);
+                        assert.equal(row.num, i);
+                        assert.equal(row.flt, i * Math.PI);
+                        assert.equal(row.blb, null);
+                    }
 
-            for (var i = rows; i < count + 5; i++) {
-                assert.ok(!rows[i]);
-            }
-        }
-    );
+                    retrieved++;
+                });
+            })(i);
 
-    beforeExit(function() {
-        assert.equal(count, inserted, "Didn't insert all rows");
-        assert.equal(count, retrieved, "Didn't retrieve all rows");
+            stmt.finalize(done);
+        });
+
+        it('should have retrieved ' + (count + 5) + ' rows', function() {
+            assert.equal(count + 5, retrieved, "Didn't retrieve all rows");
+        });
+
+
+        after(function(done) { db.close(done); });
     });
-};
 
-exports['test retrieving reset() function'] = function(beforeExit) {
-    var db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY);
+    describe('retrieving reset() function', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
 
-    var retrieved = 0;
+        var retrieved = 0;
 
-    Step(
-        function() {
+        it('should retrieve the same row over and over again', function(done) {
             var stmt = db.prepare("SELECT txt, num, flt, blb FROM foo ORDER BY num");
-
-            var group = this.group();
             for (var i = 0; i < 10; i++) {
                 stmt.reset();
-                stmt.get(group());
+                stmt.get(function(err, row) {
+                    if (err) throw err;
+                    assert.equal(row.txt, 'String 0');
+                    assert.equal(row.num, 0);
+                    assert.equal(row.flt, 0.0);
+                    assert.equal(row.blb, null);
+                    retrieved++;
+                });
             }
-        },
-        function(err, rows) {
-            if (err) throw err;
-            for (var i = 0; i < rows.length; i++) {
-                assert.equal(rows[i].txt, 'String 0');
-                assert.equal(rows[i].num, 0);
-                assert.equal(rows[i].flt, 0.0);
-                assert.equal(rows[i].blb, null);
-                retrieved++;
-            }
-        }
-    );
+            stmt.finalize(done);
+        });
 
-    beforeExit(function() {
-        assert.equal(10, retrieved, "Didn't retrieve all rows");
+        it('should have retrieved 10 rows', function() {
+            assert.equal(10, retrieved, "Didn't retrieve all rows");
+        });
+
+        after(function(done) { db.close(done); });
     });
-};
 
-exports['test multiple get() parameter binding'] = function(beforeExit) {
-    var db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY);
+    describe('multiple get() parameter binding', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
 
-    var retrieved = 0;
+        var retrieved = 0;
 
-    Step(
-        function() {
+        it('should retrieve particular rows', function(done) {
             var stmt = db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num = ?");
 
-            var group = this.group();
-            for (var i = 0; i < 10; i++) {
-                stmt.get(i * 10 + 1, group());
-            }
-        },
-        function(err, rows) {
-            if (err) throw err;
-            for (var i = 0; i < rows.length; i++) {
-                var val = i * 10 + 1;
-                assert.equal(rows[i].txt, 'String ' + val);
-                assert.equal(rows[i].num, val);
-                assert.equal(rows[i].flt, val * Math.PI);
-                assert.equal(rows[i].blb, null);
-                retrieved++;
-            }
-        }
-    );
-
-    beforeExit(function() {
-        assert.equal(10, retrieved, "Didn't retrieve all rows");
+            for (var i = 0; i < 10; i++) (function(i) {
+                stmt.get(i * 10 + 1, function(err, row) {
+                    if (err) throw err;
+                    var val = i * 10 + 1;
+                    assert.equal(row.txt, 'String ' + val);
+                    assert.equal(row.num, val);
+                    assert.equal(row.flt, val * Math.PI);
+                    assert.equal(row.blb, null);
+                    retrieved++;
+                });
+            })(i);
+
+            stmt.finalize(done);
+        });
+
+        it('should have retrieved 10 rows', function() {
+            assert.equal(10, retrieved, "Didn't retrieve all rows");
+        });
+
+        after(function(done) { db.close(done); });
     });
-};
-
-
-exports['test prepare() parameter binding'] = function(beforeExit) {
-    var db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY);
-
-    var retrieved = 0;
 
-    db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num = ? AND txt = ?", 10, 'String 10')
-      .get(function(err, row) {
-        if (err) throw err;
-        assert.equal(row.txt, 'String 10');
-        assert.equal(row.num, 10);
-        assert.equal(row.flt, 10 * Math.PI);
-        assert.equal(row.blb, null);
-        retrieved++;
+    describe('prepare() parameter binding', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
+
+        var retrieved = 0;
+
+        it('should retrieve particular rows', function(done) {
+            db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num = ? AND txt = ?", 10, 'String 10')
+                .get(function(err, row) {
+                    if (err) throw err;
+                    assert.equal(row.txt, 'String 10');
+                    assert.equal(row.num, 10);
+                    assert.equal(row.flt, 10 * Math.PI);
+                    assert.equal(row.blb, null);
+                    retrieved++;
+                })
+                .finalize(done);
+        });
+
+        it('should have retrieved 1 row', function() {
+            assert.equal(1, retrieved, "Didn't retrieve all rows");
+        });
+
+        after(function(done) { db.close(done); });
     });
 
-    beforeExit(function() {
-        assert.equal(1, retrieved, "Didn't retrieve all rows");
+    describe('all()', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
+
+        var retrieved = 0;
+        var count = 1000;
+
+        it('should retrieve particular rows', function(done) {
+            db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num < ? ORDER BY num", count)
+                .all(function(err, rows) {
+                    if (err) throw err;
+                    for (var i = 0; i < rows.length; i++) {
+                        assert.equal(rows[i].txt, 'String ' + i);
+                        assert.equal(rows[i].num, i);
+                        assert.equal(rows[i].flt, i * Math.PI);
+                        assert.equal(rows[i].blb, null);
+                        retrieved++;
+                    }
+                })
+                .finalize(done);
+        });
+
+        it('should have retrieved all rows', function() {
+            assert.equal(count, retrieved, "Didn't retrieve all rows");
+        });
+
+        after(function(done) { db.close(done); });
     });
-};
 
+    describe('all()', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
 
-exports['test get() parameter binding'] = function(beforeExit) {
-    var db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY);
+        it('should retrieve particular rows', function(done) {
+           db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num > 5000")
+                .all(function(err, rows) {
+                    if (err) throw err;
+                    assert.ok(rows.length === 0);
+                })
+                .finalize(done);
+        });
 
-    var retrieved = 0;
-
-    db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num = ? AND txt = ?")
-      .get(10, 'String 10', function(err, row) {
-        if (err) throw err;
-        assert.equal(row.txt, 'String 10');
-        assert.equal(row.num, 10);
-        assert.equal(row.flt, 10 * Math.PI);
-        assert.equal(row.blb, null);
-        retrieved++;
+        after(function(done) { db.close(done); });
     });
 
-    beforeExit(function() {
-        assert.equal(1, retrieved, "Didn't retrieve all rows");
-    });
-};
-
-exports['test all()'] = function(beforeExit) {
-    var db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY);
-
-    var retrieved = 0;
-    var count = 1000;
-
-    db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num < ? ORDER BY num", count)
-      .all(function(err, rows) {
-        if (err) throw err;
-        for (var i = 0; i < rows.length; i++) {
-            assert.equal(rows[i].txt, 'String ' + i);
-            assert.equal(rows[i].num, i);
-            assert.equal(rows[i].flt, i * Math.PI);
-            assert.equal(rows[i].blb, null);
-            retrieved++;
+    describe('high concurrency', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database(':memory:', done); });
+
+        function randomString() {
+            var str = '';
+            for (var i = Math.random() * 300; i > 0; i--) {
+                str += String.fromCharCode(Math.floor(Math.random() * 256));
+            }
+            return str;
         }
-    });
 
-    beforeExit(function() {
-        assert.equal(count, retrieved, "Didn't retrieve all rows");
-    });
-};
+        // Generate random data.
+        var data = [];
+        var length = Math.floor(Math.random() * 1000) + 200;
+        for (var i = 0; i < length; i++) {
+            data.push([ randomString(), i, i * Math.random(), null ]);
+        }
 
+        var inserted = 0;
+        var retrieved = 0;
 
+        it('should create the table', function(done) {
+            db.prepare("CREATE TABLE foo (txt text, num int, flt float, blb blob)").run().finalize(done);
+        });
 
-exports['test all() parameter binding'] = function(beforeExit) {
-    var db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY);
+        it('should insert all values', function(done) {
+            for (var i = 0; i < data.length; i++) {
+                var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
+                stmt.run(data[i][0], data[i][1], data[i][2], data[i][3], function(err) {
+                    if (err) throw err;
+                    inserted++;
+                }).finalize(function(err) {
+                    if (err) throw err;
+                    if (inserted == data.length) done();
+                });
+            }
+        });
 
-    var retrieved = 0;
-    var count = 1000;
+        it('should retrieve all values', function(done) {
+            db.prepare("SELECT txt, num, flt, blb FROM foo")
+                .all(function(err, rows) {
+                    if (err) throw err;
 
-    db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num < ? ORDER BY num")
-      .all(count, function(err, rows) {
-        if (err) throw err;
-        for (var i = 0; i < rows.length; i++) {
-            assert.equal(rows[i].txt, 'String ' + i);
-            assert.equal(rows[i].num, i);
-            assert.equal(rows[i].flt, i * Math.PI);
-            assert.equal(rows[i].blb, null);
-            retrieved++;
-        }
-    });
+                    for (var i = 0; i < rows.length; i++) {
+                        assert.ok(data[rows[i].num] !== true);
 
-    beforeExit(function() {
-        assert.equal(count, retrieved, "Didn't retrieve all rows");
-    });
-};
+                        assert.equal(rows[i].txt, data[rows[i].num][0]);
+                        assert.equal(rows[i].num, data[rows[i].num][1]);
+                        assert.equal(rows[i].flt, data[rows[i].num][2]);
+                        assert.equal(rows[i].blb, data[rows[i].num][3]);
 
-exports['test all() without results'] = function(beforeExit) {
-    var db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY);
+                        // Mark the data row as already retrieved.
+                        data[rows[i].num] = true;
+                        retrieved++;
 
-    var empty = false;
+                    }
 
-    db.prepare("SELECT txt, num, flt, blb FROM foo WHERE num > 5000")
-      .all(function(err, rows) {
-        if (err) throw err;
-        assert.ok(rows.length === 0);
-        empty = true;
-    });
+                    assert.equal(retrieved, data.length);
+                    assert.equal(retrieved, inserted);
+                })
+                .finalize(done);
+        });
 
-    beforeExit(function() {
-        assert.ok(empty, "Didn't retrieve an empty result set");
+        after(function(done) { db.close(done); });
     });
-};
 
-exports['test high concurrency'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
 
-    function randomString() {
-        var str = '';
-        for (var i = Math.random() * 300; i > 0; i--) {
-            str += String.fromCharCode(Math.floor(Math.random() * 256));
-        }
-        return str;
-    };
-
-    // Generate random data.
-    var data = [];
-    var length = Math.floor(Math.random() * 1000) + 200;
-    for (var i = 0; i < length; i++) {
-        data.push([ randomString(), i, i * Math.random(), null ]);
-    }
-
-    var inserted = false;
-    var retrieved = 0;
-
-    Step(
-        function() {
-            db.prepare("CREATE TABLE foo (txt text, num int, flt float, blb blob)").run(this);
-        },
-        function(err) {
-            if (err) throw err;
-            var group = this.group();
-            for (var i = 0; i < data.length; i++) {
-                var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
-                stmt.run(data[i][0], data[i][1], data[i][2], data[i][3], group());
-            }
-        },
-        function(err, result) {
-            if (err) throw err;
-            assert.ok(result.length === length, 'Invalid length');
-            inserted = true;
-
-            db.prepare("SELECT txt, num, flt, blb FROM foo")
-              .all(function(err, rows) {
-                 if (err) throw err;
+    describe('test Database#get()', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY, done); });
 
-                 for (var i = 0; i < rows.length; i++) {
-                     assert.ok(data[rows[i].num] !== true);
+        var retrieved = 0;
 
-                     assert.equal(rows[i].txt, data[rows[i].num][0]);
-                     assert.equal(rows[i].num, data[rows[i].num][1]);
-                     assert.equal(rows[i].flt, data[rows[i].num][2]);
-                     assert.equal(rows[i].blb, data[rows[i].num][3]);
-
-                     // Mark the data row as already retrieved.
-                     data[rows[i].num] = true;
-                     retrieved++;
-                 }
+        it('should get a row', function(done) {
+            db.get("SELECT txt, num, flt, blb FROM foo WHERE num = ? AND txt = ?", 10, 'String 10', function(err, row) {
+                if (err) throw err;
+                assert.equal(row.txt, 'String 10');
+                assert.equal(row.num, 10);
+                assert.equal(row.flt, 10 * Math.PI);
+                assert.equal(row.blb, null);
+                retrieved++;
+                done();
             });
-        }
-    );
-
-    beforeExit(function() {
-        assert.ok(inserted);
-    });
-};
+        });
 
-exports['test Database#get()'] = function(beforeExit) {
-    var db = new sqlite3.Database('test/support/prepare.db', sqlite3.OPEN_READONLY);
+        it('should have retrieved all rows', function() {
+            assert.equal(1, retrieved, "Didn't retrieve all rows");
+        });
 
-    var retrieved = 0;
-
-    db.get("SELECT txt, num, flt, blb FROM foo WHERE num = ? AND txt = ?", 10, 'String 10', function(err, row) {
-        if (err) throw err;
-        assert.equal(row.txt, 'String 10');
-        assert.equal(row.num, 10);
-        assert.equal(row.flt, 10 * Math.PI);
-        assert.equal(row.blb, null);
-        retrieved++;
+        after(function(done) { db.close(done); });
     });
 
-    beforeExit(function() {
-        assert.equal(1, retrieved, "Didn't retrieve all rows");
-    });
-};
+    describe('Database#run() and Database#all()', function() {
+        var db;
+        before(function(done) { db = new sqlite3.Database(':memory:', done); });
 
-exports['test Database#run() and Database#all()'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
+        var inserted = 0;
+        var retrieved = 0;
 
-    var inserted = 0;
-    var retrieved = 0;
+        // We insert and retrieve that many rows.
+        var count = 1000;
 
-    // We insert and retrieve that many rows.
-    var count = 1000;
+        it('should create the table', function(done) {
+            db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)", done);
+        });
 
-    Step(
-        function() {
-            db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)", this);
-        },
-        function(err) {
-            if (err) throw err;
-            var group = this.group();
+        it('should insert ' + count + ' rows', function(done) {
             for (var i = 0; i < count; i++) {
                 db.run("INSERT INTO foo VALUES(?, ?, ?, ?)",
                     'String ' + i,
                     i,
                     i * Math.PI,
                     // null (SQLite sets this implicitly)
-                    group()
-                 );
+                    function(err) {
+                        if (err) throw err;
+                        inserted++;
+                        if (inserted == count) done();
+                    }
+                );
             }
-        },
-        function(err, rows) {
-            if (err) throw err;
-            inserted += rows.length;
-            db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", this)
-        },
-        function(err, rows) {
-            if (err) throw err;
-            assert.equal(count, rows.length, "Couldn't retrieve all rows");
+        });
 
-            for (var i = 0; i < count; i++) {
-                assert.equal(rows[i].txt, 'String ' + i);
-                assert.equal(rows[i].num, i);
-                assert.equal(rows[i].flt, i * Math.PI);
-                assert.equal(rows[i].blb, null);
-                retrieved++;
-            }
-        }
-    );
+        it('should retrieve all rows', function(done) {
+            db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) {
+                if (err) throw err;
+                for (var i = 0; i < rows.length; i++) {
+                    assert.equal(rows[i].txt, 'String ' + i);
+                    assert.equal(rows[i].num, i);
+                    assert.equal(rows[i].flt, i * Math.PI);
+                    assert.equal(rows[i].blb, null);
+                    retrieved++;
+                }
+
+                assert.equal(retrieved, count);
+                assert.equal(retrieved, inserted);
+
+                done();
+            });
+        });
 
-    beforeExit(function() {
-        assert.equal(count, inserted, "Didn't insert all rows");
-        assert.equal(count, retrieved, "Didn't retrieve all rows");
+        after(function(done) { db.close(done); });
     });
-};
+});
diff --git a/test/profile.test.js b/test/profile.test.js
index 9d97bd6..5dc29b4 100644
--- a/test/profile.test.js
+++ b/test/profile.test.js
@@ -1,40 +1,56 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
-var util = require('util');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test Database profiling'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
+describe('profiling', function() {
     var create = false;
     var select = false;
 
-    db.on('profile', function(sql, nsecs) {
-        assert.ok(typeof nsecs === "number");
-        if (sql.match(/^SELECT/)) {
-            assert.ok(!select);
-            assert.equal(sql, "SELECT * FROM foo");
-            select = true;
-        }
-        else if (sql.match(/^CREATE/)) {
-            assert.ok(!create);
-            assert.equal(sql, "CREATE TABLE foo (id int)");
-            create = true;
-        }
-        else {
-            assert.ok(false);
-        }
+    var db;
+    before(function(done) {
+        db = new sqlite3.Database(':memory:', done);
+
+        db.on('profile', function(sql, nsecs) {
+            assert.ok(typeof nsecs === "number");
+            if (sql.match(/^SELECT/)) {
+                assert.ok(!select);
+                assert.equal(sql, "SELECT * FROM foo");
+                select = true;
+            }
+            else if (sql.match(/^CREATE/)) {
+                assert.ok(!create);
+                assert.equal(sql, "CREATE TABLE foo (id int)");
+                create = true;
+            }
+            else {
+                assert.ok(false);
+            }
+        });
     });
 
-    db.serialize(function() {
-        db.run("CREATE TABLE foo (id int)");
-        db.run("SELECT * FROM foo");
+    it('should profile a create table', function(done) {
+        assert.ok(!create);
+        db.run("CREATE TABLE foo (id int)", function(err) {
+            if (err) throw err;
+            process.nextTick(function() {
+                assert.ok(create);
+                done();
+            });
+        });
     });
 
-    db.close();
 
-    beforeExit(function() {
-        assert.ok(create);
-        assert.ok(select);
+    it('should profile a select', function(done) {
+        assert.ok(!select);
+        db.run("SELECT * FROM foo", function(err) {
+            if (err) throw err;
+            process.nextTick(function() {
+                assert.ok(select);
+                done();
+            });
+        });
+    });
+
+    after(function(done) {
+        db.close(done);
     });
-};
+});
diff --git a/test/rerun.test.js b/test/rerun.test.js
index 3be8b02..e98562e 100644
--- a/test/rerun.test.js
+++ b/test/rerun.test.js
@@ -1,43 +1,50 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
-exports['test running the same statement multiple times'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
+describe('rerunning statements', function() {
+    var db;
+    before(function(done) { db = new sqlite3.Database(':memory:', done); });
 
     var count = 10;
     var inserted = 0;
     var retrieved = 0;
 
-    db.serialize(function() {
-       db.run("CREATE TABLE foo (id int)");
+    it('should create the table', function(done) {
+        db.run("CREATE TABLE foo (id int)", done);
+    });
 
-       var stmt = db.prepare("INSERT INTO foo VALUES(?)");
-       for (var i = 5; i < count; i++) {
-           stmt.run(i, function(err) {
-               if (err) throw err;
-               inserted++;
-           });
-       }
-       stmt.finalize();
+    it('should insert repeatedly, reusing the same statement', function(done) {
+        var stmt = db.prepare("INSERT INTO foo VALUES(?)");
+        for (var i = 5; i < count; i++) {
+            stmt.run(i, function(err) {
+                if (err) throw err;
+                inserted++;
+            });
+        }
+        stmt.finalize(done);
+    });
 
-       var collected = [];
-       var stmt = db.prepare("SELECT id FROM foo WHERE id = ?");
-       for (var i = 0; i < count; i++) {
-           stmt.get(i, function(err, row) {
-               if (err) throw err;
-               if (row) collected.push(row);
-           });
-       }
-       stmt.finalize(function() {
-           retrieved += collected.length;
-           assert.deepEqual(collected, [ { id: 5 }, { id: 6 }, { id: 7 }, { id: 8 }, { id: 9 } ]);
-       });
+    it('should retrieve repeatedly, resuing the same statement', function(done) {
+        var collected = [];
+        var stmt = db.prepare("SELECT id FROM foo WHERE id = ?");
+        for (var i = 0; i < count; i++) {
+            stmt.get(i, function(err, row) {
+                if (err) throw err;
+                if (row) collected.push(row);
+            });
+        }
+        stmt.finalize(function(err) {
+            if (err) throw err;
+            retrieved += collected.length;
+            assert.deepEqual(collected, [ { id: 5 }, { id: 6 }, { id: 7 }, { id: 8 }, { id: 9 } ]);
+            done();
+        });
     });
 
-    beforeExit(function() {
+    it('should have inserted and retrieved the right amount', function() {
         assert.equal(inserted, 5);
         assert.equal(retrieved, 5);
     });
-};
+
+    after(function(done) { db.close(done); });
+});
diff --git a/test/scheduling.test.js b/test/scheduling.test.js
index d28d2be..e00a8cf 100644
--- a/test/scheduling.test.js
+++ b/test/scheduling.test.js
@@ -1,60 +1,44 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
+describe('scheduling', function() {
+    it('scheduling after the database was closed', function(done) {
+        var db = new sqlite3.Database(':memory:');
+        db.on('error', function(err) {
+            assert.ok(err.message && err.message.indexOf("SQLITE_MISUSE: Database handle is closed") > -1);
+            done();
+        });
 
-exports['test scheduling a query after the database was closed'] = function(beforeExit) {
-    var error = false;
-    var db = new sqlite3.Database(':memory:');
-    db.on('error', function(err) {
-        error = true;
-        assert.equal(err.message, "SQLITE_MISUSE: Database handle is closed");
+        db.close();
+        db.run("CREATE TABLE foo (id int)");
     });
 
-    db.close();
-    db.run("CREATE TABLE foo (id int)");
-
-    beforeExit(function() {
-        assert.ok(error);
-    });
-};
 
+    it('scheduling a query with callback after the database was closed', function(done) {
+        var db = new sqlite3.Database(':memory:');
+        db.on('error', function(err) {
+            assert.ok(false, 'Event was accidentally triggered');
+        });
 
-exports['test scheduling a query with callback after the database was closed'] = function(beforeExit) {
-    var error = false;
-    var errorEvent = false;
-    var db = new sqlite3.Database(':memory:');
-    db.on('error', function(err) {
-        errorEvent = true;
+        db.close();
+        db.run("CREATE TABLE foo (id int)", function(err) {
+            assert.ok(err.message && err.message.indexOf("SQLITE_MISUSE: Database handle is closed") > -1);
+            done();
+        });
     });
 
-    db.close();
-    db.run("CREATE TABLE foo (id int)", function(err) {
-        assert.ok(err.message, "SQLITE_MISUSE: Database handle is closed");
-        error = true;
-    });
+    it('running a query after the database was closed', function(done) {
+        var db = new sqlite3.Database(':memory:');
 
-    beforeExit(function() {
-        assert.ok(error);
-        assert.ok(!errorEvent);
-    });
-};
-
-exports['test running a query after the database was closed'] = function(beforeExit) {
-    var error = false;
-    var db = new sqlite3.Database(':memory:');
-
-    var stmt = db.prepare("SELECT * FROM sqlite_master", function(err) {
-        if (err) throw err;
-        db.close(function(err) {
-            assert.ok(err);
-            error = true;
-            assert.equal(err.message, "SQLITE_BUSY: unable to close due to unfinalised statements");
-            stmt.run();
-        });
-    });
+        var stmt = db.prepare("SELECT * FROM sqlite_master", function(err) {
+            if (err) throw err;
+            db.close(function(err) {
+                assert.ok(err);
+                assert.ok(err.message && err.message.indexOf("SQLITE_BUSY: unable to close due to") > -1);
 
-    beforeExit(function() {
-        assert.ok(error);
+                // Running a statement now should not fail.
+                stmt.run(done);
+            });
+        });
     });
-};
+});
diff --git a/test/serialization.test.js b/test/serialization.test.js
index 5bf7707..ccc5867 100644
--- a/test/serialization.test.js
+++ b/test/serialization.test.js
@@ -1,10 +1,10 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
 
-exports['test serialize() and parallelize()'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
+describe('serialize() and parallelize()', function() {
+    var db;
+    before(function(done) { db = new sqlite3.Database(':memory:', done); });
 
     var inserted1 = 0;
     var inserted2 = 0;
@@ -12,62 +12,33 @@ exports['test serialize() and parallelize()'] = function(beforeExit) {
 
     var count = 1000;
 
-    db.serialize();
-    db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)");
-    db.parallelize();
-
-    var stmt1 = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
-    var stmt2 = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
-    for (var i = 0; i < count; i++) {
-        stmt1.run('String ' + i, i, i * Math.PI, function(err) {
-            if (err) throw err;
-            inserted1++;
-            // Might sometimes fail, but should work fine most of the time.
-            assert.ok(inserted2 >= Math.floor(0.75 * inserted1));
-        });
-        i++;
-        stmt2.run('String ' + i, i, i * Math.PI, function(err) {
-            if (err) throw err;
-            inserted2++;
-            assert.ok(inserted1 >= Math.floor(0.75 * inserted2));
-        });
-    }
-    db.serialize();
-    db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) {
-        if (err) throw err;
-        for (var i = 0; i < rows.length; i++) {
-            assert.equal(rows[i].txt, 'String ' + i);
-            assert.equal(rows[i].num, i);
-            assert.equal(rows[i].flt, i * Math.PI);
-            assert.equal(rows[i].blb, null);
-            retrieved++;
-        }
-    });
-
-    beforeExit(function() {
-        assert.equal(count, inserted1 + inserted2, "Didn't insert all rows");
-        assert.equal(count, retrieved, "Didn't retrieve all rows");
-    });
-}
-
-exports['test serialize(fn)'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-
-    var inserted = 0;
-    var retrieved = 0;
-
-    var count = 1000;
-    db.serialize(function() {
+    it('should toggle', function(done) {
+        db.serialize();
         db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)");
+        db.parallelize(done);
+    });
 
-        var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
+    it('should insert rows', function() {
+        var stmt1 = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
+        var stmt2 = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
         for (var i = 0; i < count; i++) {
-            stmt.run('String ' + i, i, i * Math.PI, function(err) {
+            // Interleaved inserts with two statements.
+            stmt1.run('String ' + i, i, i * Math.PI, function(err) {
+                if (err) throw err;
+                inserted1++;
+            });
+            i++;
+            stmt2.run('String ' + i, i, i * Math.PI, function(err) {
                 if (err) throw err;
-                inserted++;
+                inserted2++;
             });
         }
+        stmt1.finalize();
+        stmt2.finalize();
+    });
 
+    it('should have inserted all the rows after synchronizing with serialize()', function(done) {
+        db.serialize();
         db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) {
             if (err) throw err;
             for (var i = 0; i < rows.length; i++) {
@@ -77,11 +48,57 @@ exports['test serialize(fn)'] = function(beforeExit) {
                 assert.equal(rows[i].blb, null);
                 retrieved++;
             }
+
+            assert.equal(count, inserted1 + inserted2, "Didn't insert all rows");
+            assert.equal(count, retrieved, "Didn't retrieve all rows");
+            done();
         });
     });
 
-    beforeExit(function() {
+    after(function(done) { db.close(done); });
+});
+
+describe('serialize(fn)', function() {
+    var db;
+    before(function(done) { db = new sqlite3.Database(':memory:', done); });
+
+    var inserted = 0;
+    var retrieved = 0;
+
+    var count = 1000;
+
+    it('should call the callback', function(done) {
+        db.serialize(function() {
+            db.run("CREATE TABLE foo (txt text, num int, flt float, blb blob)");
+
+            var stmt = db.prepare("INSERT INTO foo VALUES(?, ?, ?, ?)");
+            for (var i = 0; i < count; i++) {
+                stmt.run('String ' + i, i, i * Math.PI, function(err) {
+                    if (err) throw err;
+                    inserted++;
+                });
+            }
+            stmt.finalize();
+
+            db.all("SELECT txt, num, flt, blb FROM foo ORDER BY num", function(err, rows) {
+                if (err) throw err;
+                for (var i = 0; i < rows.length; i++) {
+                    assert.equal(rows[i].txt, 'String ' + i);
+                    assert.equal(rows[i].num, i);
+                    assert.equal(rows[i].flt, i * Math.PI);
+                    assert.equal(rows[i].blb, null);
+                    retrieved++;
+                }
+                done();
+            });
+        });
+    });
+
+
+    it('should have inserted and retrieved all rows', function() {
         assert.equal(count, inserted, "Didn't insert all rows");
         assert.equal(count, retrieved, "Didn't retrieve all rows");
     });
-}
+
+    after(function(done) { db.close(done); });
+});
diff --git a/test/support/createdb.js b/test/support/createdb.js
old mode 100644
new mode 100755
index 4e0ea5e..2cb24c6
--- a/test/support/createdb.js
+++ b/test/support/createdb.js
@@ -1,5 +1,13 @@
+#!/usr/bin/env node
+
+var existsSync = require('fs').existsSync || require('path').existsSync;
+var path = require('path');
+
 var sqlite3 = require('../../lib/sqlite3');
 
+var count = 1000000;
+var db_path = path.join(__dirname,'big.db');
+
 function randomString() {
     var str = '';
     var chars = 'abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY0123456789  ';
@@ -9,18 +17,20 @@ function randomString() {
     return str;
 };
 
-var db = new sqlite3.Database('test/support/big.db');
 
-var count = 10000000;
-
-db.serialize(function() {
-    db.run("CREATE TABLE foo (id INT, txt TEXT)");
-
-    db.run("BEGIN TRANSACTION");
-    var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
-    for (var i = 0; i < count; i++) {
-        stmt.run(i, randomString());
-    }
-    stmt.finalize();
-    db.run("COMMIT TRANSACTION");
-});
+if (existsSync(db_path)) {
+    console.log('okay: database already created (' + db_path + ')');
+} else {
+    console.log("Creating test database... This may take several minutes.");
+    var db = new sqlite3.Database(db_path);
+    db.serialize(function() {
+        db.run("CREATE TABLE foo (id INT, txt TEXT)");
+        db.run("BEGIN TRANSACTION");
+        var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
+        for (var i = 0; i < count; i++) {
+            stmt.run(i, randomString());
+        }
+        stmt.finalize();
+        db.run("COMMIT TRANSACTION");
+    });
+}
diff --git a/test/support/helper.js b/test/support/helper.js
index 4febeca..10dc20c 100644
--- a/test/support/helper.js
+++ b/test/support/helper.js
@@ -1,21 +1,28 @@
 var assert = require('assert');
 var fs = require('fs');
+var pathExists = require('fs').existsSync || require('path').existsSync;
 
 exports.deleteFile = function(name) {
     try {
         fs.unlinkSync(name);
     } catch(err) {
-        if (err.errno !== process.ENOENT && err.code !== 'ENOENT') {
+        if (err.errno !== process.ENOENT && err.code !== 'ENOENT' && err.syscall !== 'unlink') {
             throw err;
         }
     }
 };
 
+exports.ensureExists = function(name,cb) {
+    if (!pathExists(name)) {
+        fs.mkdirSync(name);
+    };
+}
+
 assert.fileDoesNotExist = function(name) {
     try {
         fs.statSync(name);
     } catch(err) {
-        if (err.errno !== process.ENOENT && err.code !== 'ENOENT') {
+        if (err.errno !== process.ENOENT && err.code !== 'ENOENT' && err.syscall !== 'unlink') {
             throw err;
         }
     }
diff --git a/test/trace.test.js b/test/trace.test.js
index 7f4237c..3f790bd 100644
--- a/test/trace.test.js
+++ b/test/trace.test.js
@@ -1,66 +1,67 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
-var util = require('util');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
+describe('tracing', function() {
+    it('Database tracing', function(done) {
+        var db = new sqlite3.Database(':memory:');
+        var create = false;
+        var select = false;
 
-exports['test Database tracing'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
-    var create = false;
-    var select = false;
+        db.on('trace', function(sql) {
+            if (sql.match(/^SELECT/)) {
+                assert.ok(!select);
+                assert.equal(sql, "SELECT * FROM foo");
+                select = true;
+            }
+            else if (sql.match(/^CREATE/)) {
+                assert.ok(!create);
+                assert.equal(sql, "CREATE TABLE foo (id int)");
+                create = true;
+            }
+            else {
+                assert.ok(false);
+            }
+        });
 
-    db.on('trace', function(sql) {
-        if (sql.match(/^SELECT/)) {
-            assert.ok(!select);
-            assert.equal(sql, "SELECT * FROM foo");
-            select = true;
-        }
-        else if (sql.match(/^CREATE/)) {
-            assert.ok(!create);
-            assert.equal(sql, "CREATE TABLE foo (id int)");
-            create = true;
-        }
-        else {
-            assert.ok(false);
-        }
-    });
+        db.serialize(function() {
+            db.run("CREATE TABLE foo (id int)");
+            db.run("SELECT * FROM foo");
+        });
 
-    db.serialize(function() {
-        db.run("CREATE TABLE foo (id int)");
-        db.run("SELECT * FROM foo");
+        db.close(function(err) {
+            if (err) throw err;
+            assert.ok(create);
+            assert.ok(select);
+            done();
+        });
     });
 
-    db.close();
 
-    beforeExit(function() {
-        assert.ok(create);
-        assert.ok(select);
-    });
-};
+    it('test disabling tracing #1', function(done) {
+        var db = new sqlite3.Database(':memory:');
 
-exports['test disabling tracing #1'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
+        db.on('trace', function(sql) {});
+        db.removeAllListeners('trace');
+        db._events['trace'] = function(sql) {
+            assert.ok(false);
+        };
 
-    db.on('trace', function(sql) {});
-    db.removeAllListeners('trace');
-    db._events['trace'] = function(sql) {
-        assert.ok(false);
-    };
+        db.run("CREATE TABLE foo (id int)");
+        db.close(done);
+    });
 
-    db.run("CREATE TABLE foo (id int)");
-    db.close();
-};
 
-exports['test disabling tracing #2'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
+    it('test disabling tracing #2', function(done) {
+        var db = new sqlite3.Database(':memory:');
 
-    var trace = function(sql) {};
-    db.on('trace', trace);
-    db.removeListener('trace', trace);
-    db._events['trace'] = function(sql) {
-        assert.ok(false);
-    };
+        var trace = function(sql) {};
+        db.on('trace', trace);
+        db.removeListener('trace', trace);
+        db._events['trace'] = function(sql) {
+            assert.ok(false);
+        };
 
-    db.run("CREATE TABLE foo (id int)");
-    db.close();
-};
+        db.run("CREATE TABLE foo (id int)");
+        db.close(done);
+    });
+});
diff --git a/test/unicode.test.js b/test/unicode.test.js
index 4b77123..179431a 100644
--- a/test/unicode.test.js
+++ b/test/unicode.test.js
@@ -1,20 +1,19 @@
-var sqlite3 = require('sqlite3');
+var sqlite3 = require('..');
 var assert = require('assert');
 
-if (process.setMaxListeners) process.setMaxListeners(0);
-
 function randomString() {
     var str = '';
     for (var i = Math.random() * 300; i > 0; i--) {
         str += String.fromCharCode(Math.floor(Math.random() * 65536));
     }
     return str;
-};
+}
 
-exports['test unicode characters'] = function(beforeExit) {
-    var db = new sqlite3.Database(':memory:');
+describe('unicode', function() {
+    var db;
+    before(function(done) { db = new sqlite3.Database(':memory:', done); });
 
-    // Generate random data.
+        // Generate random data.
     var data = [];
     var length = Math.floor(Math.random() * 1000) + 200;
     for (var i = 0; i < length; i++) {
@@ -24,30 +23,37 @@ exports['test unicode characters'] = function(beforeExit) {
     var inserted = 0;
     var retrieved = 0;
 
-    db.serialize(function() {
-       db.run("CREATE TABLE foo (id int, txt text)");
-
-       var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
-       for (var i = 0; i < data.length; i++) {
-           stmt.run(i, data[i], function(err) {
-               if (err) throw err;
-               inserted++;
-           });
-       }
-       stmt.finalize();
-
-       db.all("SELECT txt FROM foo ORDER BY id", function(err, rows) {
-           if (err) throw err;
-
-           for (var i = 0; i < rows.length; i++) {
-               assert.equal(rows[i].txt, data[i]);
-               retrieved++;
-           }
-       });
+    it('should create the table', function(done) {
+        db.run("CREATE TABLE foo (id int, txt text)", done);
+    });
+
+    it('should insert all values', function(done) {
+        var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
+        for (var i = 0; i < data.length; i++) {
+            stmt.run(i, data[i], function(err) {
+                if (err) throw err;
+                inserted++;
+            });
+        }
+        stmt.finalize(done);
     });
 
-    beforeExit(function() {
+    it('should retrieve all values', function(done) {
+        db.all("SELECT txt FROM foo ORDER BY id", function(err, rows) {
+            if (err) throw err;
+
+            for (var i = 0; i < rows.length; i++) {
+                assert.equal(rows[i].txt, data[i]);
+                retrieved++;
+            }
+            done();
+        });
+    });
+
+    it('should have inserted and retrieved the correct amount', function() {
         assert.equal(inserted, length);
         assert.equal(retrieved, length);
     });
-};
+
+    after(function(done) { db.close(done); });
+});
diff --git a/wscript b/wscript
deleted file mode 100644
index 7114252..0000000
--- a/wscript
+++ /dev/null
@@ -1,146 +0,0 @@
-import os
-import sys
-import Options
-from Configure import ConfigurationError
-from os.path import exists
-from shutil import copy2 as copy, rmtree
-
-# node-wafadmin
-import Options
-import Utils
-
-
-TARGET = 'sqlite3_bindings'
-TARGET_FILE = '%s.node' % TARGET
-built = 'build/Release/%s' % TARGET_FILE
-dest = 'lib/%s' % TARGET_FILE
-
-BUNDLED_SQLITE3_VERSION = '3070800'
-BUNDLED_SQLITE3 = 'sqlite-autoconf-%s' % BUNDLED_SQLITE3_VERSION
-BUNDLED_SQLITE3_TAR = 'sqlite-autoconf-%s.tar.gz' % BUNDLED_SQLITE3_VERSION
-SQLITE3_TARGET = 'deps/%s' % BUNDLED_SQLITE3
-
-sqlite3_test_program = '''
-#include "stdio.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-#include <sqlite3.h>
-#ifdef __cplusplus
-}
-#endif
-
-int
-main() {
-    return 0;
-}
-'''
-
-
-def set_options(opt):
-  opt.tool_options("compiler_cxx")
-  opt.add_option( '--with-sqlite3'
-                , action='store'
-                , default=None
-                , help='Directory prefix containing sqlite "lib" and "include" files (default is to compile against internal copy of sqlite v%s' % BUNDLED_SQLITE3_VERSION
-                , dest='sqlite3_dir'
-                )
-
-def _conf_exit(conf,msg):
-    conf.fatal('\n\n' + msg + '\n...check the build/config.log for details')
-
-def _build_paths(conf,prefix):
-    if not os.path.exists(prefix):
-        _conf_exit(conf,'configure path of %s not found' % prefix)
-    norm_path = os.path.normpath(os.path.realpath(prefix))
-    if norm_path.endswith('lib') or norm_path.endswith('include'):
-        norm_path = os.path.dirname(norm_path)
-    return os.path.join('%s' % norm_path,'lib'),os.path.join('%s' % norm_path,'include')
-
-def configure(conf):
-  conf.check_tool("compiler_cxx")
-  conf.check_tool("node_addon")
-
-  o = Options.options
-
-  if not o.sqlite3_dir:
-      configure_internal_sqlite3(conf)
-  else:
-      lib, include = _build_paths(conf,o.sqlite3_dir)
-
-      if conf.check_cxx(lib='sqlite3',
-                fragment=sqlite3_test_program,
-                uselib_store='SQLITE3',
-                libpath=lib,
-                msg='Checking for libsqlite3 at %s' % lib,
-                includes=include):
-          Utils.pprint('GREEN', 'Sweet, found viable sqlite3 dependency at: %s ' % o.sqlite3_dir)
-      else:
-          _conf_exit(conf,'sqlite3 libs/headers not found at %s' % o.sqlite3_dir)
-
-  linkflags = []
-  if os.environ.has_key('LINKFLAGS'):
-      linkflags.extend(os.environ['LINKFLAGS'].split(' '))
-
-  if not o.sqlite3_dir and Options.platform == 'darwin':
-      linkflags.append('-Wl,-search_paths_first')
-
-  conf.env.append_value("LINKFLAGS", linkflags)
-
-def configure_internal_sqlite3(conf):
-      Utils.pprint('GREEN','Note: will build against internal copy of sqlite3 v%s\n(pass --with-sqlite3=/usr/local to build against an external version)' % BUNDLED_SQLITE3_VERSION)
-
-      os.chdir('deps')
-      if not os.path.exists(BUNDLED_SQLITE3):
-          os.system('tar xf %s' % BUNDLED_SQLITE3_TAR)
-      os.chdir(BUNDLED_SQLITE3)
-      cxxflags = ''
-      if os.environ.has_key('CFLAGS'):
-          cxxflags += os.environ['CFLAGS']
-          cxxflags += ' '
-      if os.environ.has_key('CXXFLAGS'):
-          cxxflags += os.environ['CXXFLAGS']
-      # LINKFLAGS appear to be picked up automatically...
-      if not os.path.exists('config.status'):
-          cmd = "CFLAGS='%s -DSQLITE_ENABLE_RTREE=1 -fPIC -O3 -DNDEBUG' ./configure --enable-static --disable-shared" % cxxflags
-          if Options.platform == 'darwin':
-              cmd += ' --disable-dependency-tracking'
-          os.system(cmd)
-      os.chdir('../../')
-
-      conf.env.append_value("CPPPATH_SQLITE3", ['../deps/%s' % BUNDLED_SQLITE3])
-      conf.env.append_value("LINKFLAGS", ['-L../deps/%s/.libs' % BUNDLED_SQLITE3, '-lsqlite3'])
-
-def build_internal_sqlite3(bld):
-    if not Options.commands['clean'] and '../deps' in bld.env['CPPPATH_SQLITE3'][0]:
-        if not os.path.exists(SQLITE3_TARGET):
-            Utils.pprint('RED','Please re-run ./configure or node-waf configure')
-            sys.exit()
-        os.chdir(SQLITE3_TARGET)
-        os.system('make')
-        os.chdir('../../')
-
-def clean_internal_sqlite3():
-    if os.path.exists(SQLITE3_TARGET):
-        rmtree(SQLITE3_TARGET)
-
-def build(bld):
-  obj = bld.new_task_gen("cxx", "shlib", "node_addon")
-  build_internal_sqlite3(bld)
-  obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE",
-                  "-DSQLITE_ENABLE_RTREE=1", "-pthread", "-Wall"]
-  # uncomment the next line to remove '-undefined dynamic_lookup'
-  # in order to review linker errors (v8, libev/eio references can be ignored)
-  #obj.env['LINKFLAGS_MACBUNDLE'] = ['-bundle']
-  obj.target = TARGET
-  obj.source = "src/sqlite3.cc src/database.cc src/statement.cc"
-  obj.uselib = "SQLITE3"
-
-def shutdown():
-  if Options.commands['clean']:
-      if exists(TARGET_FILE):
-        unlink(TARGET_FILE)
-      clean_internal_sqlite3()
-  else:
-    if exists(built):
-      copy(built, dest)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/collab-maint/node-sqlite3.git



More information about the Pkg-javascript-commits mailing list