[Pkg-javascript-commits] [node-abstract-leveldown] 01/02: Imported Upstream version 0.12.3
Andrew Kelley
andrewrk-guest at moszumanska.debian.org
Sun Jun 29 05:48:18 UTC 2014
This is an automated email from the git hooks/post-receive script.
andrewrk-guest pushed a commit to branch master
in repository node-abstract-leveldown.
commit 1d4aab7120fcdb6a796ec41b524cf8327319a5da
Author: Andrew Kelley <superjoe30 at gmail.com>
Date: Sun Jun 29 05:40:11 2014 +0000
Imported Upstream version 0.12.3
---
.gitignore | 1 +
.jshintrc | 60 ++++
.travis.yml | 10 +
CHANGELOG.md | 18 ++
CONTRIBUTING.md | 27 ++
LICENSE | 39 +++
README.md | 159 +++++++++++
abstract-chained-batch.js | 81 ++++++
abstract-iterator.js | 49 ++++
abstract-leveldown.js | 258 ++++++++++++++++++
abstract/approximate-size-test.js | 121 +++++++++
abstract/batch-test.js | 144 ++++++++++
abstract/chained-batch-test.js | 222 +++++++++++++++
abstract/close-test.js | 24 ++
abstract/del-test.js | 77 ++++++
abstract/get-test.js | 125 +++++++++
abstract/iterator-test.js | 460 +++++++++++++++++++++++++++++++
abstract/leveldown-test.js | 17 ++
abstract/open-test.js | 96 +++++++
abstract/put-get-del-test.js | 167 ++++++++++++
abstract/put-test.js | 92 +++++++
abstract/ranges-test.js | 435 +++++++++++++++++++++++++++++
abstract/util.js | 8 +
package.json | 46 ++++
test.js | 559 ++++++++++++++++++++++++++++++++++++++
testCommon.js | 75 +++++
26 files changed, 3370 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..ba4514a
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,60 @@
+{
+ "predef": [ ]
+ , "bitwise": false
+ , "camelcase": false
+ , "curly": false
+ , "eqeqeq": false
+ , "forin": false
+ , "immed": false
+ , "latedef": false
+ , "newcap": true
+ , "noarg": true
+ , "noempty": true
+ , "nonew": true
+ , "plusplus": false
+ , "quotmark": true
+ , "regexp": false
+ , "undef": true
+ , "unused": true
+ , "strict": false
+ , "trailing": true
+ , "maxlen": 120
+ , "asi": true
+ , "boss": true
+ , "debug": true
+ , "eqnull": true
+ , "esnext": true
+ , "evil": true
+ , "expr": true
+ , "funcscope": false
+ , "globalstrict": false
+ , "iterator": false
+ , "lastsemic": true
+ , "laxbreak": true
+ , "laxcomma": true
+ , "loopfunc": true
+ , "multistr": false
+ , "onecase": false
+ , "proto": false
+ , "regexdash": false
+ , "scripturl": true
+ , "smarttabs": false
+ , "shadow": false
+ , "sub": true
+ , "supernew": false
+ , "validthis": true
+ , "browser": true
+ , "couch": false
+ , "devel": false
+ , "dojo": false
+ , "mootools": false
+ , "node": true
+ , "nonstandard": true
+ , "prototypejs": false
+ , "rhino": false
+ , "worker": true
+ , "wsh": false
+ , "nomen": false
+ , "onevar": true
+ , "passfail": false
+}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..fe3f4eb
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: node_js
+node_js:
+ - 0.8
+ - "0.10"
+branches:
+ only:
+ - master
+notifications:
+ email:
+ - rod at vagg.org
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..89c9013
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,18 @@
+### 0.11.1 - Nov 15 2013
+ * Adjust approximate-size-test.js to account for snappy compression
+
+### 0.11.0 - Oct 14 2013
+ * Introduce _setupIteratorOptions() method to fix options object prior to _iterator() call; makes working with gt/gte/lt/lte options a little easier (@rvagg)
+
+### 0.10.2 - Sep 6 2013
+
+ * Refactor duplicated versions of isTypedArray into util.js (@rvagg)
+ * Refactor duplicated versions of 'NotFound' checks into util.js, fixed too-strict version in get-test.js (@rvagg)
+
+### 0.10.1 - Aug 29 2013
+
+ * Relax check for 'Not Found: ' in error message to be case insensitive in get-test.js (@rvagg)
+
+### 0.10.0 - Aug 19 2013
+
+ * Added test for gt, gte, lt, lte ranges (@dominictarr)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..2641fd0
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,27 @@
+# LevelUP is an OPEN Open Source Project
+
+-----------------------------------------
+
+## What?
+
+Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
+
+## Rules
+
+There are a few basic ground-rules for contributors:
+
+1. **No `--force` pushes** or modifying the Git history in any way.
+1. **Non-master branches** ought to be used for ongoing work.
+1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors.
+1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor.
+1. Contributors should attempt to adhere to the prevailing code-style.
+
+## Releases
+
+Declaring formal releases remains the prerogative of the project maintainer.
+
+## Changes to this arrangement
+
+This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change.
+
+-----------------------------------------
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..76d07a0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,39 @@
+Copyright 2012, Rod Vagg (the "Original Author")
+All rights reserved.
+
+MIT +no-false-attribs License
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+Distributions of all or part of the Software intended to be used
+by the recipients as they would use the unmodified Software,
+containing modifications that substantially alter, remove, or
+disable functionality of the Software, outside of the documented
+configuration mechanisms provided by the Software, shall be
+modified such that the Original Author's bug reporting email
+addresses and urls are either replaced with the contact information
+of the parties responsible for the changes, or removed entirely.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+
+Except where noted, this license applies to any and all software
+programs and associated documentation files created by the
+Original Author, when distributed with the Software.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1db6d6b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,159 @@
+# Abstract LevelDOWN [![Build Status](https://secure.travis-ci.org/rvagg/abstract-leveldown.png)](http://travis-ci.org/rvagg/abstract-leveldown)
+
+[![NPM](https://nodei.co/npm/abstract-leveldown.png?downloads=true)](https://nodei.co/npm/abstract-leveldown/)
+[![NPM](https://nodei.co/npm-dl/abstract-leveldown.png?months=6)](https://nodei.co/npm/abstract-leveldown/)
+
+An abstract prototype matching the **[LevelDOWN](https://github.com/rvagg/node-leveldown/)** API. Useful for extending **[LevelUP](https://github.com/rvagg/node-levelup)** functionality by providing a replacement to LevelDOWN.
+
+As of version 0.7, LevelUP allows you to pass a `'db'` option when you create a new instance. This will override the default LevelDOWN store with a LevelDOWN API compatible object.
+
+**Abstract LevelDOWN** provides a simple, operational *noop* base prototype that's ready for extending. By default, all operations have sensible "noops" (operations that essentially do nothing). For example, simple operations such as `.open(callback)` and `.close(callback)` will simply invoke the callback (on a *next tick*). More complex operations perform sensible actions, for example: `.get(key, callback)` will always return a `'NotFound'` `Error` on the callback.
+
+You add functionality by implementing the underscore versions of the operations. For example, to implement a `put()` operation you add a `_put()` method to your object. Each of these underscore methods override the default *noop* operations and are always provided with **consistent arguments**, regardless of what is passed in by the client.
+
+Additionally, all methods provide argument checking and sensible defaults for optional arguments. All bad-argument errors are compatible with LevelDOWN (they pass the LevelDOWN method arguments tests). For example, if you call `.open()` without a callback argument you'll get an `Error('open() requires a callback argument')`. Where optional arguments are involved, your underscore methods will receive sensible defaults. A `.get(key, callback)` will pass through to a `._get(key, options, ca [...]
+
+## Example
+
+A simplistic in-memory LevelDOWN replacement
+
+```js
+var util = require('util')
+ , AbstractLevelDOWN = require('./').AbstractLevelDOWN
+
+// constructor, passes through the 'location' argument to the AbstractLevelDOWN constructor
+function FakeLevelDOWN (location) {
+ AbstractLevelDOWN.call(this, location)
+}
+
+// our new prototype inherits from AbstractLevelDOWN
+util.inherits(FakeLevelDOWN, AbstractLevelDOWN)
+
+// implement some methods
+
+FakeLevelDOWN.prototype._open = function (options, callback) {
+ // initialise a memory storage object
+ this._store = {}
+ // optional use of nextTick to be a nice async citizen
+ process.nextTick(function () { callback(null, this) }.bind(this))
+}
+
+FakeLevelDOWN.prototype._put = function (key, value, options, callback) {
+ key = '_' + key // safety, to avoid key='__proto__'-type skullduggery
+ this._store[key] = value
+ process.nextTick(callback)
+}
+
+FakeLevelDOWN.prototype._get = function (key, options, callback) {
+ var value = this._store['_' + key]
+ if (value === undefined) {
+ // 'NotFound' error, consistent with LevelDOWN API
+ return process.nextTick(function () { callback(new Error('NotFound')) })
+ }
+ process.nextTick(function () {
+ callback(null, value)
+ })
+}
+
+FakeLevelDOWN.prototype._del = function (key, options, callback) {
+ delete this._store['_' + key]
+ process.nextTick(callback)
+}
+
+// now use it in LevelUP
+
+var levelup = require('levelup')
+
+var db = levelup('/who/cares/', {
+ // the 'db' option replaces LevelDOWN
+ db: function (location) { return new FakeLevelDOWN(location) }
+})
+
+db.put('foo', 'bar', function (err) {
+ if (err) throw err
+ db.get('foo', function (err, value) {
+ if (err) throw err
+ console.log('Got foo =', value)
+ })
+})
+```
+
+See [MemDOWN](https://github.com/rvagg/memdown/) if you are looking for a complete in-memory replacement for LevelDOWN.
+
+## Extensible API
+
+Remember that each of these methods, if you implement them, will receive exactly the number and order of arguments described. Optional arguments will be converted to sensible defaults.
+
+### AbstractLevelDOWN(location)
+### AbstractLevelDOWN#_open(options, callback)
+### AbstractLevelDOWN#_close(callback)
+### AbstractLevelDOWN#_get(key, options, callback)
+### AbstractLevelDOWN#_put(key, value, options, callback)
+### AbstractLevelDOWN#_del(key, options, callback)
+### AbstractLevelDOWN#_batch(array, options, callback)
+
+If `batch()` is called without argument or with only an options object then it should return a `Batch` object with chainable methods. Otherwise it will invoke a classic batch operation.
+
+### AbstractLevelDOWN#_chainedBatch()
+
+By default an `batch()` operation without argument returns a blank `AbstractChainedBatch` object. The prototype is available on the main exports for you to extend. If you want to implement chainable batch operations then you should extend the `AbstractChaindBatch` and return your object in the `_chainedBatch()` method.
+
+### AbstractLevelDOWN#_approximateSize(start, end, callback)
+### AbstractLevelDOWN#_iterator(options)
+
+By default an `iterator()` operation returns a blank `AbstractIterator` object. The prototype is available on the main exports for you to extend. If you want to implement iterator operations then you should extend the `AbstractIterator` and return your object in the `_iterator(options)` method.
+
+`AbstractIterator` implements the basic state management found in LevelDOWN. It keeps track of when a `next()` is in progress and when an `end()` has been called so it doesn't allow concurrent `next()` calls, it does it allow `end()` while a `next()` is in progress and it doesn't allow either `next()` or `end()` after `end()` has been called.
+
+### AbstractIterator(db)
+
+Provided with the current instance of `AbstractLevelDOWN` by default.
+
+### AbstractIterator#_next(callback)
+### AbstractIterator#_end(callback)
+
+### AbstractChainedBatch
+Provided with the current instance of `AbstractLevelDOWN` by default.
+
+### AbstractChainedBatch#_put(key, value)
+### AbstractChainedBatch#_del(key)
+### AbstractChainedBatch#_clear()
+### AbstractChainedBatch#_write(options, callback)
+
+<a name="contributing"></a>
+Contributing
+------------
+
+Abstract LevelDOWN is an **OPEN Open Source Project**. This means that:
+
+> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
+
+See the [CONTRIBUTING.md](https://github.com/rvagg/node-levelup/blob/master/CONTRIBUTING.md) file for more details.
+
+### Contributors
+
+Abstract LevelDOWN is only possible due to the excellent work of the following contributors:
+
+<table><tbody>
+<tr><th align="left">Rod Vagg</th><td><a href="https://github.com/rvagg">GitHub/rvagg</a></td><td><a href="http://twitter.com/rvagg">Twitter/@rvagg</a></td></tr>
+<tr><th align="left">John Chesley</th><td><a href="https://github.com/chesles/">GitHub/chesles</a></td><td><a href="http://twitter.com/chesles">Twitter/@chesles</a></td></tr>
+<tr><th align="left">Jake Verbaten</th><td><a href="https://github.com/raynos">GitHub/raynos</a></td><td><a href="http://twitter.com/raynos2">Twitter/@raynos2</a></td></tr>
+<tr><th align="left">Dominic Tarr</th><td><a href="https://github.com/dominictarr">GitHub/dominictarr</a></td><td><a href="http://twitter.com/dominictarr">Twitter/@dominictarr</a></td></tr>
+<tr><th align="left">Max Ogden</th><td><a href="https://github.com/maxogden">GitHub/maxogden</a></td><td><a href="http://twitter.com/maxogden">Twitter/@maxogden</a></td></tr>
+<tr><th align="left">Lars-Magnus Skog</th><td><a href="https://github.com/ralphtheninja">GitHub/ralphtheninja</a></td><td><a href="http://twitter.com/ralphtheninja">Twitter/@ralphtheninja</a></td></tr>
+<tr><th align="left">David Björklund</th><td><a href="https://github.com/kesla">GitHub/kesla</a></td><td><a href="http://twitter.com/david_bjorklund">Twitter/@david_bjorklund</a></td></tr>
+<tr><th align="left">Julian Gruber</th><td><a href="https://github.com/juliangruber">GitHub/juliangruber</a></td><td><a href="http://twitter.com/juliangruber">Twitter/@juliangruber</a></td></tr>
+<tr><th align="left">Paolo Fragomeni</th><td><a href="https://github.com/hij1nx">GitHub/hij1nx</a></td><td><a href="http://twitter.com/hij1nx">Twitter/@hij1nx</a></td></tr>
+<tr><th align="left">Anton Whalley</th><td><a href="https://github.com/No9">GitHub/No9</a></td><td><a href="https://twitter.com/antonwhalley">Twitter/@antonwhalley</a></td></tr>
+<tr><th align="left">Matteo Collina</th><td><a href="https://github.com/mcollina">GitHub/mcollina</a></td><td><a href="https://twitter.com/matteocollina">Twitter/@matteocollina</a></td></tr>
+<tr><th align="left">Pedro Teixeira</th><td><a href="https://github.com/pgte">GitHub/pgte</a></td><td><a href="https://twitter.com/pgte">Twitter/@pgte</a></td></tr>
+<tr><th align="left">James Halliday</th><td><a href="https://github.com/substack">GitHub/substack</a></td><td><a href="https://twitter.com/substack">Twitter/@substack</a></td></tr>
+</tbody></table>
+
+<a name="licence"></a>
+Licence & copyright
+-------------------
+
+Copyright (c) 2012-2013 Abstract LevelDOWN contributors (listed above).
+
+Abstract LevelDOWN is licensed under an MIT +no-false-attribs license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details.
diff --git a/abstract-chained-batch.js b/abstract-chained-batch.js
new file mode 100644
index 0000000..7e48059
--- /dev/null
+++ b/abstract-chained-batch.js
@@ -0,0 +1,81 @@
+/* Copyright (c) 2013 Rod Vagg, MIT License */
+
+function AbstractChainedBatch (db) {
+ this._db = db
+ this._operations = []
+ this._written = false
+}
+
+AbstractChainedBatch.prototype._checkWritten = function () {
+ if (this._written)
+ throw new Error('write() already called on this batch')
+}
+
+AbstractChainedBatch.prototype.put = function (key, value) {
+ this._checkWritten()
+
+ var err = this._db._checkKeyValue(key, 'key', this._db._isBuffer)
+ if (err) throw err
+ err = this._db._checkKeyValue(value, 'value', this._db._isBuffer)
+ if (err) throw err
+
+ if (!this._db._isBuffer(key)) key = String(key)
+ if (!this._db._isBuffer(value)) value = String(value)
+
+ if (typeof this._put == 'function' )
+ this._put(key, value)
+ else
+ this._operations.push({ type: 'put', key: key, value: value })
+
+ return this
+}
+
+AbstractChainedBatch.prototype.del = function (key) {
+ this._checkWritten()
+
+ var err = this._db._checkKeyValue(key, 'key', this._db._isBuffer)
+ if (err) throw err
+
+ if (!this._db._isBuffer(key)) key = String(key)
+
+ if (typeof this._del == 'function' )
+ this._del(key)
+ else
+ this._operations.push({ type: 'del', key: key })
+
+ return this
+}
+
+AbstractChainedBatch.prototype.clear = function () {
+ this._checkWritten()
+
+ this._operations = []
+
+ if (typeof this._clear == 'function' )
+ this._clear()
+
+ return this
+}
+
+AbstractChainedBatch.prototype.write = function (options, callback) {
+ this._checkWritten()
+
+ if (typeof options == 'function')
+ callback = options
+ if (typeof callback != 'function')
+ throw new Error('write() requires a callback argument')
+ if (typeof options != 'object')
+ options = {}
+
+ this._written = true
+
+ if (typeof this._write == 'function' )
+ return this._write(callback)
+
+ if (typeof this._db._batch == 'function')
+ return this._db._batch(this._operations, options, callback)
+
+ process.nextTick(callback)
+}
+
+module.exports = AbstractChainedBatch
\ No newline at end of file
diff --git a/abstract-iterator.js b/abstract-iterator.js
new file mode 100644
index 0000000..04ed6a5
--- /dev/null
+++ b/abstract-iterator.js
@@ -0,0 +1,49 @@
+/* Copyright (c) 2013 Rod Vagg, MIT License */
+
+function AbstractIterator (db) {
+ this.db = db
+ this._ended = false
+ this._nexting = false
+}
+
+AbstractIterator.prototype.next = function (callback) {
+ var self = this
+
+ if (typeof callback != 'function')
+ throw new Error('next() requires a callback argument')
+
+ if (self._ended)
+ return callback(new Error('cannot call next() after end()'))
+ if (self._nexting)
+ return callback(new Error('cannot call next() before previous next() has completed'))
+
+ self._nexting = true
+ if (typeof self._next == 'function') {
+ return self._next(function () {
+ self._nexting = false
+ callback.apply(null, arguments)
+ })
+ }
+
+ process.nextTick(function () {
+ self._nexting = false
+ callback()
+ })
+}
+
+AbstractIterator.prototype.end = function (callback) {
+ if (typeof callback != 'function')
+ throw new Error('end() requires a callback argument')
+
+ if (this._ended)
+ return callback(new Error('end() already called on iterator'))
+
+ this._ended = true
+
+ if (typeof this._end == 'function')
+ return this._end(callback)
+
+ process.nextTick(callback)
+}
+
+module.exports = AbstractIterator
diff --git a/abstract-leveldown.js b/abstract-leveldown.js
new file mode 100644
index 0000000..e56a641
--- /dev/null
+++ b/abstract-leveldown.js
@@ -0,0 +1,258 @@
+/* Copyright (c) 2013 Rod Vagg, MIT License */
+
+var xtend = require('xtend')
+ , AbstractIterator = require('./abstract-iterator')
+ , AbstractChainedBatch = require('./abstract-chained-batch')
+
+function AbstractLevelDOWN (location) {
+ if (!arguments.length || location === undefined)
+ throw new Error('constructor requires at least a location argument')
+
+ if (typeof location != 'string')
+ throw new Error('constructor requires a location string argument')
+
+ this.location = location
+}
+
+AbstractLevelDOWN.prototype.open = function (options, callback) {
+ if (typeof options == 'function')
+ callback = options
+
+ if (typeof callback != 'function')
+ throw new Error('open() requires a callback argument')
+
+ if (typeof options != 'object')
+ options = {}
+
+ if (typeof this._open == 'function')
+ return this._open(options, callback)
+
+ process.nextTick(callback)
+}
+
+AbstractLevelDOWN.prototype.close = function (callback) {
+ if (typeof callback != 'function')
+ throw new Error('close() requires a callback argument')
+
+ if (typeof this._close == 'function')
+ return this._close(callback)
+
+ process.nextTick(callback)
+}
+
+AbstractLevelDOWN.prototype.get = function (key, options, callback) {
+ var err
+
+ if (typeof options == 'function')
+ callback = options
+
+ if (typeof callback != 'function')
+ throw new Error('get() requires a callback argument')
+
+ if (err = this._checkKeyValue(key, 'key', this._isBuffer))
+ return callback(err)
+
+ if (!this._isBuffer(key))
+ key = String(key)
+
+ if (typeof options != 'object')
+ options = {}
+
+ if (typeof this._get == 'function')
+ return this._get(key, options, callback)
+
+ process.nextTick(function () { callback(new Error('NotFound')) })
+}
+
+AbstractLevelDOWN.prototype.put = function (key, value, options, callback) {
+ var err
+
+ if (typeof options == 'function')
+ callback = options
+
+ if (typeof callback != 'function')
+ throw new Error('put() requires a callback argument')
+
+ if (err = this._checkKeyValue(key, 'key', this._isBuffer))
+ return callback(err)
+
+ if (err = this._checkKeyValue(value, 'value', this._isBuffer))
+ return callback(err)
+
+ if (!this._isBuffer(key))
+ key = String(key)
+
+ // coerce value to string in node, don't touch it in browser
+ // (indexeddb can store any JS type)
+ if (!this._isBuffer(value) && !process.browser)
+ value = String(value)
+
+ if (typeof options != 'object')
+ options = {}
+
+ if (typeof this._put == 'function')
+ return this._put(key, value, options, callback)
+
+ process.nextTick(callback)
+}
+
+AbstractLevelDOWN.prototype.del = function (key, options, callback) {
+ var err
+
+ if (typeof options == 'function')
+ callback = options
+
+ if (typeof callback != 'function')
+ throw new Error('del() requires a callback argument')
+
+ if (err = this._checkKeyValue(key, 'key', this._isBuffer))
+ return callback(err)
+
+ if (!this._isBuffer(key))
+ key = String(key)
+
+ if (typeof options != 'object')
+ options = {}
+
+ if (typeof this._del == 'function')
+ return this._del(key, options, callback)
+
+ process.nextTick(callback)
+}
+
+AbstractLevelDOWN.prototype.batch = function (array, options, callback) {
+ if (!arguments.length)
+ return this._chainedBatch()
+
+ if (typeof options == 'function')
+ callback = options
+
+ if (typeof callback != 'function')
+ throw new Error('batch(array) requires a callback argument')
+
+ if (!Array.isArray(array))
+ return callback(new Error('batch(array) requires an array argument'))
+
+ if (typeof options != 'object')
+ options = {}
+
+ var i = 0
+ , l = array.length
+ , e
+ , err
+
+ for (; i < l; i++) {
+ e = array[i]
+ if (typeof e != 'object')
+ continue
+
+ if (err = this._checkKeyValue(e.type, 'type', this._isBuffer))
+ return callback(err)
+
+ if (err = this._checkKeyValue(e.key, 'key', this._isBuffer))
+ return callback(err)
+
+ if (e.type == 'put') {
+ if (err = this._checkKeyValue(e.value, 'value', this._isBuffer))
+ return callback(err)
+ }
+ }
+
+ if (typeof this._batch == 'function')
+ return this._batch(array, options, callback)
+
+ process.nextTick(callback)
+}
+
+//TODO: remove from here, not a necessary primitive
+AbstractLevelDOWN.prototype.approximateSize = function (start, end, callback) {
+ if ( start == null
+ || end == null
+ || typeof start == 'function'
+ || typeof end == 'function') {
+ throw new Error('approximateSize() requires valid `start`, `end` and `callback` arguments')
+ }
+
+ if (typeof callback != 'function')
+ throw new Error('approximateSize() requires a callback argument')
+
+ if (!this._isBuffer(start))
+ start = String(start)
+
+ if (!this._isBuffer(end))
+ end = String(end)
+
+ if (typeof this._approximateSize == 'function')
+ return this._approximateSize(start, end, callback)
+
+ process.nextTick(function () {
+ callback(null, 0)
+ })
+}
+
+AbstractLevelDOWN.prototype._setupIteratorOptions = function (options) {
+ var self = this
+
+ options = xtend(options)
+
+ ;[ 'start', 'end', 'gt', 'gte', 'lt', 'lte' ].forEach(function (o) {
+ if (options[o] && self._isBuffer(options[o]) && options[o].length === 0)
+ delete options[o]
+ })
+
+ options.reverse = !!options.reverse
+
+ // fix `start` so it takes into account gt, gte, lt, lte as appropriate
+ if (options.reverse && options.lt)
+ options.start = options.lt
+ if (options.reverse && options.lte)
+ options.start = options.lte
+ if (!options.reverse && options.gt)
+ options.start = options.gt
+ if (!options.reverse && options.gte)
+ options.start = options.gte
+
+ if ((options.reverse && options.lt && !options.lte)
+ || (!options.reverse && options.gt && !options.gte))
+ options.exclusiveStart = true // start should *not* include matching key
+
+ return options
+}
+
+AbstractLevelDOWN.prototype.iterator = function (options) {
+ if (typeof options != 'object')
+ options = {}
+
+ options = this._setupIteratorOptions(options)
+
+ if (typeof this._iterator == 'function')
+ return this._iterator(options)
+
+ return new AbstractIterator(this)
+}
+
+AbstractLevelDOWN.prototype._chainedBatch = function () {
+ return new AbstractChainedBatch(this)
+}
+
+AbstractLevelDOWN.prototype._isBuffer = function (obj) {
+ return Buffer.isBuffer(obj)
+}
+
+AbstractLevelDOWN.prototype._checkKeyValue = function (obj, type) {
+ if (obj === null || obj === undefined)
+ return new Error(type + ' cannot be `null` or `undefined`')
+
+ if (obj === null || obj === undefined)
+ return new Error(type + ' cannot be `null` or `undefined`')
+
+ if (this._isBuffer(obj)) {
+ if (obj.length === 0)
+ return new Error(type + ' cannot be an empty Buffer')
+ } else if (String(obj) === '')
+ return new Error(type + ' cannot be an empty String')
+}
+
+module.exports.AbstractLevelDOWN = AbstractLevelDOWN
+module.exports.AbstractIterator = AbstractIterator
+module.exports.AbstractChainedBatch = AbstractChainedBatch
diff --git a/abstract/approximate-size-test.js b/abstract/approximate-size-test.js
new file mode 100644
index 0000000..b67d428
--- /dev/null
+++ b/abstract/approximate-size-test.js
@@ -0,0 +1,121 @@
+var db
+
+module.exports.setUp = function (leveldown, test, testCommon) {
+ test('setUp common', testCommon.setUp)
+ test('setUp db', function (t) {
+ db = leveldown(testCommon.location())
+ db.open(t.end.bind(t))
+ })
+}
+
+module.exports.args = function (test) {
+ test('test argument-less approximateSize() throws', function (t) {
+ t.throws(
+ db.approximateSize.bind(db)
+ , { name: 'Error', message: 'approximateSize() requires valid `start`, `end` and `callback` arguments' }
+ , 'no-arg approximateSize() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 1-arg, approximateSize() throws', function (t) {
+ t.throws(
+ db.approximateSize.bind(db, 'foo')
+ , { name: 'Error', message: 'approximateSize() requires valid `start`, `end` and `callback` arguments' }
+ , 'callback-less, 1-arg approximateSize() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 2-arg, approximateSize() throws', function (t) {
+ t.throws(
+ db.approximateSize.bind(db, 'foo', 'bar')
+ , { name: 'Error', message: 'approximateSize() requires a callback argument' }
+ , 'callback-less, 2-arg approximateSize() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 3-arg, approximateSize() throws', function (t) {
+ t.throws(
+ db.approximateSize.bind(db, function () {})
+ , { name: 'Error', message: 'approximateSize() requires valid `start`, `end` and `callback` arguments' }
+ , 'callback-only approximateSize() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-only approximateSize() throws', function (t) {
+ t.throws(
+ db.approximateSize.bind(db, function () {})
+ , { name: 'Error', message: 'approximateSize() requires valid `start`, `end` and `callback` arguments' }
+ , 'callback-only approximateSize() throws'
+ )
+ t.end()
+ })
+
+ test('test 1-arg + callback approximateSize() throws', function (t) {
+ t.throws(
+ db.approximateSize.bind(db, 'foo', function () {})
+ , { name: 'Error', message: 'approximateSize() requires valid `start`, `end` and `callback` arguments' }
+ , '1-arg + callback approximateSize() throws'
+ )
+ t.end()
+ })
+}
+
+module.exports.approximateSize = function (test) {
+ test('test approximateSize()', function (t) {
+ var data = Array.apply(null, Array(10000)).map(function () {
+ return 'aaaaaaaaaa'
+ }).join('')
+
+ db.batch(
+ Array.apply(null, Array(10)).map(function (x, i) {
+ return { type: 'put', key: 'foo' + i, value: data }
+ })
+ , function (err) {
+ t.notOk(err, 'no error')
+
+ // cycle open/close to ensure a pack to .sst
+
+ db.close(function (err) {
+ t.notOk(err, 'no error')
+
+ db.open(function (err) {
+ t.notOk(err, 'no error')
+
+ db.approximateSize('!', '~', function (err, size) {
+ t.notOk(err, 'no error')
+
+ t.type(size, 'number')
+ t.ok(
+ size > 40000 // account for snappy compression
+ // original would be ~100000
+ , 'size reports a reasonable amount (' + size + ')'
+ )
+
+ db.close(function (err) {
+ t.notOk(err, 'no error')
+ t.end()
+ })
+ })
+ })
+ })
+ }
+ )
+ })
+}
+
+module.exports.tearDown = function (test, testCommon) {
+ test('tearDown', function (t) {
+ db.close(testCommon.tearDown.bind(null, t))
+ })
+}
+
+module.exports.all = function (leveldown, test, testCommon) {
+ module.exports.setUp(leveldown, test, testCommon)
+ module.exports.args(test)
+ module.exports.approximateSize(test)
+ module.exports.tearDown(test, testCommon)
+}
diff --git a/abstract/batch-test.js b/abstract/batch-test.js
new file mode 100644
index 0000000..4585840
--- /dev/null
+++ b/abstract/batch-test.js
@@ -0,0 +1,144 @@
+var db
+ , verifyNotFoundError = require('./util').verifyNotFoundError
+ , isTypedArray = require('./util').isTypedArray
+
+module.exports.setUp = function (leveldown, test, testCommon) {
+ test('setUp common', testCommon.setUp)
+ test('setUp db', function (t) {
+ db = leveldown(testCommon.location())
+ db.open(t.end.bind(t))
+ })
+}
+
+module.exports.args = function (test) {
+ test('test callback-less, 2-arg, batch() throws', function (t) {
+ t.throws(db.batch.bind(db, 'foo', {}), 'callback-less, 2-arg batch() throws')
+ t.end()
+ })
+
+ test('test batch() with missing `value`', function (t) {
+ db.batch([{ type: 'put', key: 'foo1' }], function (err) {
+ t.equal(err.message, 'value cannot be `null` or `undefined`', 'correct error message')
+ t.end()
+ })
+ })
+
+ test('test batch() with null `value`', function (t) {
+ db.batch([{ type: 'put', key: 'foo1', value: null }], function (err) {
+ t.equal(err.message, 'value cannot be `null` or `undefined`', 'correct error message')
+ t.end()
+ })
+ })
+
+ test('test batch() with missing `key`', function (t) {
+ db.batch([{ type: 'put', value: 'foo1' }], function (err) {
+ t.equal(err.message, 'key cannot be `null` or `undefined`', 'correct error message')
+ t.end()
+ })
+ })
+
+ test('test batch() with null `key`', function (t) {
+ db.batch([{ type: 'put', key: null, value: 'foo1' }], function (err) {
+ t.equal(err.message, 'key cannot be `null` or `undefined`', 'correct error message')
+ t.end()
+ })
+ })
+
+ test('test batch() with missing `key` and `value`', function (t) {
+ db.batch([{ type: 'put' }], function (err) {
+ t.equal(err.message, 'key cannot be `null` or `undefined`', 'correct error message')
+ t.end()
+ })
+ })
+}
+
+module.exports.batch = function (test) {
+ test('test batch() with empty array', function (t) {
+ db.batch([], function (err) {
+ t.notOk(err, 'no error')
+ t.end()
+ })
+ })
+
+ test('test simple batch()', function (t) {
+ db.batch([{ type: 'put', key: 'foo', value: 'bar' }], function (err) {
+ t.notOk(err, 'no error')
+
+ db.get('foo', function (err, value) {
+ t.notOk(err, 'no error')
+ var result
+ if (isTypedArray(value)) {
+ result = String.fromCharCode.apply(null, new Uint16Array(value))
+ } else {
+ t.ok(typeof Buffer != 'undefined' && value instanceof Buffer)
+ result = value.toString()
+ }
+ t.equal(result, 'bar')
+ t.end()
+ })
+ })
+ })
+
+ test('test multiple batch()', function (t) {
+ db.batch([
+ { type: 'put', key: 'foobatch1', value: 'bar1' }
+ , { type: 'put', key: 'foobatch2', value: 'bar2' }
+ , { type: 'put', key: 'foobatch3', value: 'bar3' }
+ , { type: 'del', key: 'foobatch2' }
+ ], function (err) {
+ t.notOk(err, 'no error')
+
+ var r = 0
+ , done = function () {
+ if (++r == 3)
+ t.end()
+ }
+
+ db.get('foobatch1', function (err, value) {
+ t.notOk(err, 'no error')
+ var result
+ if (isTypedArray(value)) {
+ result = String.fromCharCode.apply(null, new Uint16Array(value))
+ } else {
+ t.ok(typeof Buffer != 'undefined' && value instanceof Buffer)
+ result = value.toString()
+ }
+ t.equal(result, 'bar1')
+ done()
+ })
+
+ db.get('foobatch2', function (err, value) {
+ t.ok(err, 'entry not found')
+ t.ok(typeof value == 'undefined', 'value is undefined')
+ t.ok(verifyNotFoundError(err), 'NotFound error')
+ done()
+ })
+
+ db.get('foobatch3', function (err, value) {
+ t.notOk(err, 'no error')
+ var result
+ if (isTypedArray(value)) {
+ result = String.fromCharCode.apply(null, new Uint16Array(value))
+ } else {
+ t.ok(typeof Buffer != 'undefined' && value instanceof Buffer)
+ result = value.toString()
+ }
+ t.equal(result, 'bar3')
+ done()
+ })
+ })
+ })
+}
+
+module.exports.tearDown = function (test, testCommon) {
+ test('tearDown', function (t) {
+ db.close(testCommon.tearDown.bind(null, t))
+ })
+}
+
+module.exports.all = function (leveldown, test, testCommon) {
+ module.exports.setUp(leveldown, test, testCommon)
+ module.exports.args(test)
+ module.exports.batch(test)
+ module.exports.tearDown(test, testCommon)
+}
diff --git a/abstract/chained-batch-test.js b/abstract/chained-batch-test.js
new file mode 100644
index 0000000..b38a68d
--- /dev/null
+++ b/abstract/chained-batch-test.js
@@ -0,0 +1,222 @@
+var db
+
+module.exports.setUp = function (leveldown, test, testCommon) {
+ test('setUp common', testCommon.setUp)
+ test('setUp db', function (t) {
+ db = leveldown(testCommon.location())
+ db.open(t.end.bind(t))
+ })
+}
+
+module.exports.args = function (test) {
+ test('test batch#put() with missing `value`', function (t) {
+ try {
+ db.batch().put('foo1')
+ } catch (err) {
+ t.equal(err.message, 'value cannot be `null` or `undefined`', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#put() with null `value`', function (t) {
+ try {
+ db.batch().put('foo1', null)
+ } catch (err) {
+ t.equal(err.message, 'value cannot be `null` or `undefined`', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#put() with missing `key`', function (t) {
+ try {
+ db.batch().put(undefined, 'foo1')
+ } catch (err) {
+ t.equal(err.message, 'key cannot be `null` or `undefined`', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#put() with null `key`', function (t) {
+ try {
+ db.batch().put(null, 'foo1')
+ } catch (err) {
+ t.equal(err.message, 'key cannot be `null` or `undefined`', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#put() with missing `key` and `value`', function (t) {
+ try {
+ db.batch().put()
+ } catch (err) {
+ t.equal(err.message, 'key cannot be `null` or `undefined`', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#del() with missing `key`', function (t) {
+ try {
+ db.batch().del()
+ } catch (err) {
+ t.equal(err.message, 'key cannot be `null` or `undefined`', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#del() with null `key`', function (t) {
+ try {
+ db.batch().del(null)
+ } catch (err) {
+ t.equal(err.message, 'key cannot be `null` or `undefined`', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#del() with null `key`', function (t) {
+ try {
+ db.batch().del(null)
+ } catch (err) {
+ t.equal(err.message, 'key cannot be `null` or `undefined`', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#clear() doesn\'t throw', function (t) {
+ db.batch().clear()
+ t.end()
+ })
+
+ test('test batch#write() with no callback', function (t) {
+ try {
+ db.batch().write()
+ } catch (err) {
+ t.equal(err.message, 'write() requires a callback argument', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#put() after write()', function (t) {
+ var batch = db.batch().put('foo', 'bar')
+ batch.write(function () {})
+ try {
+ batch.put('boom', 'bang')
+ } catch (err) {
+ t.equal(err.message, 'write() already called on this batch', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#del() after write()', function (t) {
+ var batch = db.batch().put('foo', 'bar')
+ batch.write(function () {})
+ try {
+ batch.del('foo')
+ } catch (err) {
+ t.equal(err.message, 'write() already called on this batch', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#clear() after write()', function (t) {
+ var batch = db.batch().put('foo', 'bar')
+ batch.write(function () {})
+ try {
+ batch.clear()
+ } catch (err) {
+ t.equal(err.message, 'write() already called on this batch', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+
+ test('test batch#write() after write()', function (t) {
+ var batch = db.batch().put('foo', 'bar')
+ batch.write(function () {})
+ try {
+ batch.write(function (err) {})
+ } catch (err) {
+ t.equal(err.message, 'write() already called on this batch', 'correct error message')
+ return t.end()
+ }
+ t.fail('should have thrown')
+ t.end()
+ })
+}
+
+module.exports.batch = function (test, testCommon) {
+ test('test basic batch', function (t) {
+ db.batch(
+ [
+ { type: 'put', key: 'one', value: '1' }
+ , { type: 'put', key: 'two', value: '2' }
+ , { type: 'put', key: 'three', value: '3' }
+ ]
+ , function (err) {
+ t.notOk(err, 'no error')
+
+ db.batch()
+ .put('1', 'one')
+ .del('2', 'two')
+ .put('3', 'three')
+ .clear()
+ .put('one', 'I')
+ .put('two', 'II')
+ .del('three')
+ .put('foo', 'bar')
+ .write(function (err) {
+ t.notOk(err, 'no error')
+ testCommon.collectEntries(
+ db.iterator({ keyAsBuffer: false, valueAsBuffer: false })
+ , function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 3, 'correct number of entries')
+ var expected = [
+ { key: 'foo', value: 'bar' }
+ , { key: 'one', value: 'I' }
+ , { key: 'two', value: 'II' }
+ ]
+ t.deepEqual(data, expected)
+ t.end()
+ }
+ )
+ })
+ }
+ )
+ })
+}
+
+module.exports.tearDown = function (test, testCommon) {
+ test('tearDown', function (t) {
+ db.close(testCommon.tearDown.bind(null, t))
+ })
+}
+
+module.exports.all = function (leveldown, test, testCommon) {
+ module.exports.setUp(leveldown, test, testCommon)
+ module.exports.args(test)
+ module.exports.batch(test, testCommon)
+ module.exports.tearDown(test, testCommon)
+}
\ No newline at end of file
diff --git a/abstract/close-test.js b/abstract/close-test.js
new file mode 100644
index 0000000..7492c49
--- /dev/null
+++ b/abstract/close-test.js
@@ -0,0 +1,24 @@
+module.exports.close = function (leveldown, test, testCommon) {
+ test('test close()', function (t) {
+ var db = leveldown(testCommon.location())
+
+ db.open(function (err) {
+ t.notOk(err, 'no error')
+ t.throws(
+ db.close.bind(db)
+ , { name: 'Error', message: 'close() requires a callback argument' }
+ , 'no-arg close() throws'
+ )
+ t.throws(
+ db.close.bind(db, 'foo')
+ , { name: 'Error', message: 'close() requires a callback argument' }
+ , 'non-callback close() throws'
+ )
+
+ db.close(function (err) {
+ t.notOk(err, 'no error')
+ t.end()
+ })
+ })
+ })
+}
\ No newline at end of file
diff --git a/abstract/del-test.js b/abstract/del-test.js
new file mode 100644
index 0000000..8a70e7d
--- /dev/null
+++ b/abstract/del-test.js
@@ -0,0 +1,77 @@
+var db
+ , verifyNotFoundError = require('./util').verifyNotFoundError
+ , isTypedArray = require('./util').isTypedArray
+
+module.exports.setUp = function (leveldown, test, testCommon) {
+ test('setUp common', testCommon.setUp)
+ test('setUp db', function (t) {
+ db = leveldown(testCommon.location())
+ db.open(t.end.bind(t))
+ })
+}
+
+module.exports.args = function (test) {
+ test('test argument-less del() throws', function (t) {
+ t.throws(
+ db.del.bind(db)
+ , { name: 'Error', message: 'del() requires a callback argument' }
+ , 'no-arg del() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 1-arg, del() throws', function (t) {
+ t.throws(
+ db.del.bind(db, 'foo')
+ , { name: 'Error', message: 'del() requires a callback argument' }
+ , 'callback-less, 1-arg del() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 3-arg, del() throws', function (t) {
+ t.throws(
+ db.del.bind(db, 'foo', {})
+ , { name: 'Error', message: 'del() requires a callback argument' }
+ , 'callback-less, 2-arg del() throws'
+ )
+ t.end()
+ })
+}
+
+module.exports.del = function (test) {
+ test('test simple del()', function (t) {
+ db.put('foo', 'bar', function (err) {
+ t.notOk(err, 'no error')
+ db.del('foo', function (err) {
+ t.notOk(err, 'no error')
+ db.get('foo', function (err) {
+ t.ok(err, 'entry propertly deleted')
+ t.ok(typeof value == 'undefined', 'value is undefined')
+ t.ok(verifyNotFoundError(err), 'NotFound error')
+ t.end()
+ })
+ })
+ })
+ })
+
+ test('test del on non-existent key', function (t) {
+ db.del('blargh', function (err) {
+ t.notOk(err, 'should not error on delete')
+ t.end()
+ })
+ })
+}
+
+module.exports.tearDown = function (test, testCommon) {
+ test('tearDown', function (t) {
+ db.close(testCommon.tearDown.bind(null, t))
+ })
+}
+
+module.exports.all = function (leveldown, test, testCommon) {
+ module.exports.setUp(leveldown, test, testCommon)
+ module.exports.args(test)
+ module.exports.del(test)
+ module.exports.tearDown(test, testCommon)
+}
diff --git a/abstract/get-test.js b/abstract/get-test.js
new file mode 100644
index 0000000..2b865f0
--- /dev/null
+++ b/abstract/get-test.js
@@ -0,0 +1,125 @@
+var db
+ , verifyNotFoundError = require('./util').verifyNotFoundError
+ , isTypedArray = require('./util').isTypedArray
+
+module.exports.setUp = function (leveldown, test, testCommon) {
+ test('setUp common', testCommon.setUp)
+ test('setUp db', function (t) {
+ db = leveldown(testCommon.location())
+ db.open(t.end.bind(t))
+ })
+}
+
+module.exports.args = function (test) {
+ test('test argument-less get() throws', function (t) {
+ t.throws(
+ db.get.bind(db)
+ , { name: 'Error', message: 'get() requires a callback argument' }
+ , 'no-arg get() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 1-arg, get() throws', function (t) {
+ t.throws(
+ db.get.bind(db, 'foo')
+ , { name: 'Error', message: 'get() requires a callback argument' }
+ , 'callback-less, 1-arg get() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 3-arg, get() throws', function (t) {
+ t.throws(
+ db.get.bind(db, 'foo', {})
+ , { name: 'Error', message: 'get() requires a callback argument' }
+ , 'callback-less, 2-arg get() throws'
+ )
+ t.end()
+ })
+}
+
+module.exports.get = function (test) {
+ test('test simple get()', function (t) {
+ db.put('foo', 'bar', function (err) {
+ t.notOk(err, 'no error')
+ db.get('foo', function (err, value) {
+ t.notOk(err, 'no error')
+ t.ok(typeof value !== 'string', 'should not be string by default')
+
+ var result
+ if (isTypedArray(value)) {
+ result = String.fromCharCode.apply(null, new Uint16Array(value))
+ } else {
+ t.ok(typeof Buffer != 'undefined' && value instanceof Buffer)
+ result = value.toString()
+ }
+
+ t.equal(result, 'bar')
+
+ db.get('foo', {}, function (err, value) { // same but with {}
+ t.notOk(err, 'no error')
+ t.ok(typeof value !== 'string', 'should not be string by default')
+
+ var result
+ if (isTypedArray(value)) {
+ result = String.fromCharCode.apply(null, new Uint16Array(value))
+ } else {
+ t.ok(typeof Buffer != 'undefined' && value instanceof Buffer)
+ result = value.toString()
+ }
+
+ t.equal(result, 'bar')
+
+ db.get('foo', { asBuffer: false }, function (err, value) {
+ t.notOk(err, 'no error')
+ t.ok(typeof value === 'string', 'should be string if not buffer')
+ t.equal(value, 'bar')
+ t.end()
+ })
+ })
+ })
+ })
+ })
+
+ test('test simultaniously get()', function (t) {
+ db.put('hello', 'world', function (err) {
+ t.notOk(err, 'should not error')
+ var r = 0
+ , done = function () {
+ if (++r == 20)
+ t.end()
+ }
+ , i = 0
+ , j = 0
+
+ for (; i < 10; ++i)
+ db.get('hello', function(err, value) {
+ t.notOk(err, 'should not error')
+ t.equal(value.toString(), 'world')
+ done()
+ })
+
+ for (; j < 10; ++j)
+ db.get('not found', function(err, value) {
+ t.ok(err, 'should error')
+ t.ok(verifyNotFoundError(err), 'should have correct error message')
+ t.ok(typeof value == 'undefined', 'value is undefined')
+ done()
+ })
+ })
+ })
+}
+
+module.exports.tearDown = function (test, testCommon) {
+ test('tearDown', function (t) {
+ db.close(testCommon.tearDown.bind(null, t))
+ })
+}
+
+module.exports.all = function (leveldown, test, testCommon) {
+ module.exports.setUp(leveldown, test, testCommon)
+ module.exports.args(test)
+ module.exports.get(test)
+ module.exports.tearDown(test, testCommon)
+}
\ No newline at end of file
diff --git a/abstract/iterator-test.js b/abstract/iterator-test.js
new file mode 100644
index 0000000..ec85904
--- /dev/null
+++ b/abstract/iterator-test.js
@@ -0,0 +1,460 @@
+var db
+ , sourceData = (function () {
+ var d = []
+ , i = 0
+ , k
+ for (; i < 100; i++) {
+ k = (i < 10 ? '0' : '') + i
+ d.push({
+ type : 'put'
+ , key : k
+ , value : Math.random()
+ })
+ }
+ return d
+ }())
+ , transformSource = function (d) {
+ return { key: d.key, value: String(d.value) }
+ }
+
+module.exports.sourceData = sourceData
+module.exports.transformSource = transformSource
+
+module.exports.setUp = function (leveldown, test, testCommon) {
+ test('setUp common', testCommon.setUp)
+ test('setUp db', function (t) {
+ db = leveldown(testCommon.location())
+ db.open(t.end.bind(t))
+ })
+}
+
+module.exports.args = function (test) {
+ test('test argument-less iterator#next() throws', function (t) {
+ var iterator = db.iterator()
+ t.throws(
+ iterator.next.bind(iterator)
+ , { name: 'Error', message: 'next() requires a callback argument' }
+ , 'no-arg iterator#next() throws'
+ )
+ iterator.end(t.end.bind(t))
+ })
+
+ test('test argument-less iterator#end() after next() throws', function (t) {
+ var iterator = db.iterator()
+ iterator.next(function () {
+ t.throws(
+ iterator.end.bind(iterator)
+ , { name: 'Error', message: 'end() requires a callback argument' }
+ , 'no-arg iterator#end() throws'
+ )
+ iterator.end(t.end.bind(t))
+ })
+ })
+
+ test('test argument-less iterator#end() throws', function (t) {
+ var iterator = db.iterator()
+ t.throws(
+ iterator.end.bind(iterator)
+ , { name: 'Error', message: 'end() requires a callback argument' }
+ , 'no-arg iterator#end() throws'
+ )
+ iterator.end(t.end.bind(t))
+ })
+}
+
+module.exports.sequence = function (test) {
+ test('test twice iterator#end() callback with error', function (t) {
+ var iterator = db.iterator()
+ iterator.end(function (err) {
+ t.notOk(err, 'no error')
+ iterator.end(function(err2) {
+ t.ok(err2, 'returned error')
+ t.equal(err2.name, 'Error', 'correct error')
+ t.equal(err2.message, 'end() already called on iterator')
+ t.end()
+ })
+ })
+ })
+
+ test('test iterator#next after iterator#end() callback with error', function (t) {
+ var iterator = db.iterator()
+ iterator.end(function (err) {
+ t.notOk(err, 'no error')
+ iterator.next(function(err2) {
+ t.ok(err2, 'returned error')
+ t.equal(err2.name, 'Error', 'correct error')
+ t.equal(err2.message, 'cannot call next() after end()', 'correct message')
+ t.end()
+ })
+ })
+ })
+
+ test('test twice iterator#next() throws', function (t) {
+ var iterator = db.iterator()
+ iterator.next(function (err) {
+ t.notOk(err, 'no error')
+ iterator.end(function (err) {
+ t.notOk(err, 'no error')
+ t.end()
+ })
+ })
+
+ iterator.next(function(err) {
+ t.ok(err, 'returned error')
+ t.equal(err.name, 'Error', 'correct error')
+ t.equal(err.message, 'cannot call next() before previous next() has completed')
+ })
+ })
+}
+
+module.exports.iterator = function (leveldown, test, testCommon, collectEntries) {
+ test('test simple iterator()', function (t) {
+ var data = [
+ { type: 'put', key: 'foobatch1', value: 'bar1' }
+ , { type: 'put', key: 'foobatch2', value: 'bar2' }
+ , { type: 'put', key: 'foobatch3', value: 'bar3' }
+ ]
+ , idx = 0
+
+ db.batch(data, function (err) {
+ t.notOk(err, 'no error')
+ var iterator = db.iterator()
+ , fn = function (err, key, value) {
+ t.notOk(err, 'no error')
+ if (key && value) {
+ t.equal(key.toString(), data[idx].key, 'correct key')
+ t.equal(value.toString(), data[idx].value, 'correct value')
+ process.nextTick(next)
+ idx++
+ } else { // end
+ t.ok(typeof err === 'undefined', 'err argument is undefined')
+ t.ok(typeof key === 'undefined', 'key argument is undefined')
+ t.ok(typeof value === 'undefined', 'value argument is undefined')
+ t.equal(idx, data.length, 'correct number of entries')
+ iterator.end(function () {
+ t.end()
+ })
+ }
+ }
+ , next = function () {
+ iterator.next(fn)
+ }
+
+ next()
+ })
+ })
+
+ /** the following tests are mirroring the same series of tests in
+ * LevelUP read-stream-test.js
+ */
+
+ test('setUp #2', function (t) {
+ db.close(function () {
+ db = leveldown(testCommon.location())
+ db.open(function () {
+ db.batch(sourceData, t.end.bind(t))
+ })
+ })
+ })
+
+ test('test full data collection', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, sourceData.length, 'correct number of entries')
+ var expected = sourceData.map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, sourceData.length, 'correct number of entries')
+ var expected = sourceData.slice().reverse().map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start=0', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '00' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, sourceData.length, 'correct number of entries')
+ var expected = sourceData.map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start=50', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '50' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 50, 'correct number of entries')
+ var expected = sourceData.slice(50).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start=50 and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '50', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 51, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(49).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start being a midway key (49.5)', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '49.5' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 50, 'correct number of entries')
+ var expected = sourceData.slice(50).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start being a midway key (49999)', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '49999' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 50, 'correct number of entries')
+ var expected = sourceData.slice(50).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start being a midway key and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '49.5', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 50, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(50).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end=50', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, end: '50' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 51, 'correct number of entries')
+ var expected = sourceData.slice(0, 51).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end being a midway key (50.5)', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, end: '50.5' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 51, 'correct number of entries')
+ var expected = sourceData.slice(0, 51).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end being a midway key (50555)', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, end: '50555' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 51, 'correct number of entries')
+ var expected = sourceData.slice(0, 51).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end being a midway key and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, end: '50.5', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 49, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(0, 49).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ // end='0', starting key is actually '00' so it should avoid it
+ test('test iterator with end=0', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, end: '0' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 0, 'correct number of entries')
+ t.end()
+ })
+ })
+
+ test('test iterator with start=30 and end=70', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '30', end: '70' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 41, 'correct number of entries')
+ var expected = sourceData.slice(30, 71).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start=30 and end=70 and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '70', end: '30', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 41, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(29, 70).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with limit=20', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, limit: 20 }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 20, 'correct number of entries')
+ var expected = sourceData.slice(0, 20).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with limit=20 and start=20', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '20', limit: 20 }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 20, 'correct number of entries')
+ var expected = sourceData.slice(20, 40).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with limit=20 and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, limit: 20, reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 20, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(0, 20).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with limit=20 and start=20 and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '79', limit: 20, reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 20, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(20, 40).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ // the default limit value from levelup is -1
+ test('test iterator with limit=-1 should iterate over whole database', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, limit: -1}), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, sourceData.length, 'correct number of entries')
+ var expected = sourceData.map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end after limit', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, limit: 20, end: '50' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 20, 'correct number of entries')
+ var expected = sourceData.slice(0, 20).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end before limit', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, limit: 50, end: '19' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 20, 'correct number of entries')
+ var expected = sourceData.slice(0, 20).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start after database end', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '9a' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 0, 'correct number of entries')
+ t.end()
+ })
+ })
+
+ test('test iterator with start after database end and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, start: '9a', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, sourceData.length, 'correct number of entries')
+ var expected = sourceData.slice().reverse().map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start and end after database and and reverse=true', function (t) {
+ collectEntries(db.iterator({ start: '9b', end: '9a', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 0, 'correct number of entries')
+ t.end()
+ })
+ })
+
+ function testIteratorCollectsFullDatabase (name, iteratorOptions) {
+ iteratorOptions.keyAsBuffer = false
+ iteratorOptions.valueAsBuffer = false
+ test(name, function (t) {
+ collectEntries(db.iterator(iteratorOptions), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 100, 'correct number of entries')
+ var expected = sourceData.map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+ }
+ if (!process.browser) {
+ // Can't use buffers as query keys in indexeddb (I think :P)
+ testIteratorCollectsFullDatabase(
+ 'test iterator with start as empty buffer'
+ , { start: new Buffer(0) }
+ )
+ testIteratorCollectsFullDatabase(
+ 'test iterator with end as empty buffer'
+ , { end: new Buffer(0) }
+ )
+ }
+ testIteratorCollectsFullDatabase(
+ 'test iterator with start as empty string'
+ , { start: '' }
+ )
+ testIteratorCollectsFullDatabase(
+ 'test iterator with start as null'
+ , { start: null }
+ )
+ testIteratorCollectsFullDatabase(
+ 'test iterator with end as empty string'
+ , { end: '' }
+ )
+ testIteratorCollectsFullDatabase(
+ 'test iterator with end as null'
+ , { end: null }
+ )
+}
+
+module.exports.tearDown = function (test, testCommon) {
+ test('tearDown', function (t) {
+ db.close(testCommon.tearDown.bind(null, t))
+ })
+}
+
+module.exports.all = function (leveldown, test, testCommon) {
+ module.exports.setUp(leveldown, test, testCommon)
+ module.exports.args(test)
+ module.exports.sequence(test)
+ module.exports.iterator(leveldown, test, testCommon, testCommon.collectEntries)
+ module.exports.tearDown(test, testCommon)
+}
diff --git a/abstract/leveldown-test.js b/abstract/leveldown-test.js
new file mode 100644
index 0000000..205122b
--- /dev/null
+++ b/abstract/leveldown-test.js
@@ -0,0 +1,17 @@
+module.exports.args = function (leveldown, test) {
+ test('test database creation no-arg throws', function (t) {
+ t.throws(
+ leveldown
+ , { name: 'Error', message: 'constructor requires at least a location argument' }
+ , 'no-arg leveldown() throws'
+ )
+ t.end()
+ })
+
+ test('test database open no-arg throws', function (t) {
+ var db = leveldown('foo')
+ t.ok(db, 'database object returned')
+ t.ok(typeof db.open === 'function', 'open() function exists')
+ t.end()
+ })
+}
\ No newline at end of file
diff --git a/abstract/open-test.js b/abstract/open-test.js
new file mode 100644
index 0000000..5bbeeb0
--- /dev/null
+++ b/abstract/open-test.js
@@ -0,0 +1,96 @@
+module.exports.setUp = function (test, testCommon) {
+ test('setUp', testCommon.setUp)
+}
+
+module.exports.args = function (leveldown, test, testCommon) {
+ test('test database open no-arg throws', function (t) {
+ var db = leveldown(testCommon.location())
+ t.throws(
+ db.open.bind(db)
+ , { name: 'Error', message: 'open() requires a callback argument' }
+ , 'no-arg open() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 1-arg, open() throws', function (t) {
+ var db = leveldown(testCommon.location())
+ t.throws(
+ db.open.bind(db, {})
+ , { name: 'Error', message: 'open() requires a callback argument' }
+ , 'callback-less, 1-arg open() throws'
+ )
+ t.end()
+ })
+}
+
+module.exports.open = function (leveldown, test, testCommon) {
+ test('test database open, no options', function (t) {
+ var db = leveldown(testCommon.location())
+
+ // default createIfMissing=true, errorIfExists=false
+ db.open(function (err) {
+ t.notOk(err, 'no error')
+ db.close(function () {
+ t.end()
+ })
+ })
+ })
+
+ test('test database open, options and callback', function (t) {
+ var db = leveldown(testCommon.location())
+
+ // default createIfMissing=true, errorIfExists=false
+ db.open({}, function (err) {
+ t.notOk(err, 'no error')
+ db.close(function () {
+ t.end()
+ })
+ })
+ })
+}
+
+module.exports.openAdvanced = function (leveldown, test, testCommon) {
+ test('test database open createIfMissing:false', function (t) {
+ var db = leveldown(testCommon.location())
+
+ db.open({ createIfMissing: false }, function (err) {
+ t.ok(err, 'error')
+ t.ok(/does not exist/.test(err.message), 'error is about dir not existing')
+ t.end()
+ })
+ })
+
+ test('test database open errorIfExists:true', function (t) {
+ var location = testCommon.location()
+ , db = leveldown(location)
+
+ // make a valid database first, then close and dispose
+ db.open({}, function (err) {
+ t.notOk(err, 'no error')
+ db.close(function (err) {
+ t.notOk(err, 'no error')
+
+ // open again with 'errorIfExists'
+ db = leveldown(location)
+ db.open({ createIfMissing: false, errorIfExists: true }, function (err) {
+ t.ok(err, 'error')
+ t.ok(/exists/.test(err.message), 'error is about already existing')
+ t.end()
+ })
+ })
+ })
+ })
+}
+
+module.exports.tearDown = function (test, testCommon) {
+ test('tearDown', testCommon.tearDown)
+}
+
+module.exports.all = function (leveldown, test, testCommon) {
+ module.exports.setUp(test, testCommon)
+ module.exports.args(leveldown, test, testCommon)
+ module.exports.open(leveldown, test, testCommon)
+ module.exports.openAdvanced(leveldown, test, testCommon)
+ module.exports.tearDown(test, testCommon)
+}
\ No newline at end of file
diff --git a/abstract/put-get-del-test.js b/abstract/put-get-del-test.js
new file mode 100644
index 0000000..e7e0935
--- /dev/null
+++ b/abstract/put-get-del-test.js
@@ -0,0 +1,167 @@
+/**** SETUP & UTILITY STUFF ****/
+
+
+var db
+ , testBuffer
+ , test
+ , verifyNotFoundError = require('./util').verifyNotFoundError
+ , isTypedArray = require('./util').isTypedArray
+
+function makeGetDelErrorTests (type, key, expectedError) {
+ test('test get() with ' + type + ' causes error', function (t) {
+ db.get(key, function (err) {
+ t.ok(err, 'has error')
+ t.ok(err instanceof Error)
+ t.ok(err.message.match(expectedError), 'correct error message')
+ t.end()
+ })
+ })
+
+ test('test del() with ' + type + ' causes error', function (t) {
+ db.del(key, function (err) {
+ t.ok(err, 'has error')
+ t.ok(err instanceof Error)
+ t.ok(err.message.match(expectedError), 'correct error message')
+ t.end()
+ })
+ })
+}
+
+function makePutErrorTest (type, key, value, expectedError) {
+ test('test put() with ' + type + ' causes error', function (t) {
+ db.put(key, value, function (err) {
+ t.ok(err, 'has error')
+ t.ok(err instanceof Error)
+ t.ok(err.message.match(expectedError), 'correct error message')
+ t.end()
+ })
+ })
+}
+
+function makePutGetDelSuccessfulTest (type, key, value) {
+ test('test put()/get()/del() with ' + type, function (t) {
+ db.put(key, value, function (err) {
+ t.notOk(err, 'no error')
+ db.get(key, function (err, _value) {
+ t.notOk(err, 'no error, has key/value for `' + key + '`')
+ t.ok(Buffer.isBuffer(_value), 'is a Buffer')
+ var result = _value.toString()
+ value = value.toString()
+ t.equals(result, value)
+ db.del(key, function (err) {
+ t.notOk(err, 'no error, deleted key/value for `' + key + '`')
+ db.get(key, function (err, value) {
+ t.ok(err, 'entry propertly deleted')
+ t.ok(verifyNotFoundError(err), 'should have correct error message')
+ t.ok(typeof value == 'undefined', 'value is undefined')
+ t.end()
+ })
+ })
+ })
+ })
+ })
+}
+
+function makeErrorKeyTest (type, key, expectedError) {
+ makeGetDelErrorTests(type, key, expectedError)
+ makePutErrorTest(type, key, 'foo', expectedError)
+}
+
+/**** SETUP ENVIRONMENT ****/
+
+module.exports.setUp = function (leveldown, test, testCommon) {
+ test('setUp common', testCommon.setUp)
+ test('setUp db', function (t) {
+ db = leveldown(testCommon.location())
+ db.open(t.end.bind(t))
+ })
+}
+
+/**** TEST ERROR KEYS ****/
+
+module.exports.errorKeys = function (testFunc, BufferType) {
+ if (!BufferType) BufferType = Buffer
+ test = testFunc
+ makeErrorKeyTest('null key', null, /key cannot be `null` or `undefined`/)
+ makeErrorKeyTest('undefined key', undefined, /key cannot be `null` or `undefined`/)
+ makeErrorKeyTest('empty String key', '', /key cannot be an empty String/)
+ makeErrorKeyTest('empty Buffer key', new BufferType(0), /key cannot be an empty \w*Buffer/)
+ makeErrorKeyTest('empty Array key', [], /key cannot be an empty String/)
+}
+
+/**** TEST NON-ERROR KEYS ****/
+
+module.exports.nonErrorKeys = function (testFunc) {
+ // valid falsey keys
+ test = testFunc
+ makePutGetDelSuccessfulTest('`false` key', false, 'foo false')
+ makePutGetDelSuccessfulTest('`0` key', 0, 'foo 0')
+ makePutGetDelSuccessfulTest('`NaN` key', NaN, 'foo NaN')
+
+ // standard String key
+ makePutGetDelSuccessfulTest(
+ 'long String key'
+ , 'some long string that I\'m using as a key for this unit test, cross your fingers dude, we\'re going in!'
+ , 'foo'
+ )
+
+ if (!process.browser) {
+ // Buffer key
+ makePutGetDelSuccessfulTest('Buffer key', testBuffer, 'foo')
+ }
+
+ // non-empty Array as a value
+ makePutGetDelSuccessfulTest('Array value', 'foo', [1,2,3,4])
+}
+
+/**** TEST ERROR VALUES ****/
+
+module.exports.errorValues = function (testFunc, BufferType) {
+ if (!BufferType) BufferType = Buffer
+ test = testFunc
+ makePutErrorTest('null value', 'foo', null, /value cannot be `null` or `undefined`/)
+ makePutErrorTest('undefined value', 'foo', undefined, /value cannot be `null` or `undefined`/)
+ makePutErrorTest('empty String value', 'foo', '', /value cannot be an empty String/)
+ makePutErrorTest('empty Buffer value', 'foo', new BufferType(0), /value cannot be an empty \w*Buffer/)
+ makePutErrorTest('empty Array value', 'foo', [], /value cannot be an empty String/)
+}
+
+module.exports.nonErrorKeys = function (testFunc) {
+ // valid falsey values
+ test = testFunc
+ makePutGetDelSuccessfulTest('`false` value', 'foo false', false)
+ makePutGetDelSuccessfulTest('`0` value', 'foo 0', 0)
+ makePutGetDelSuccessfulTest('`NaN` value', 'foo NaN', NaN)
+
+ // standard String value
+ makePutGetDelSuccessfulTest(
+ 'long String value'
+ , 'foo'
+ , 'some long string that I\'m using as a key for this unit test, cross your fingers dude, we\'re going in!'
+ )
+
+ // standard Buffer value
+ makePutGetDelSuccessfulTest('Buffer value', 'foo', testBuffer)
+
+ // non-empty Array as a key
+ makePutGetDelSuccessfulTest('Array key', [1,2,3,4], 'foo')
+}
+
+/**** CLEANUP ENVIRONMENT ****/
+
+module.exports.tearDown = function (test, testCommon) {
+ test('tearDown', function (t) {
+ db.close(testCommon.tearDown.bind(null, t))
+ })
+}
+
+module.exports.all = function (leveldown, testFunc, testCommon, buffer, BufferType) {
+ testBuffer = buffer
+ test = testFunc
+ module.exports.setUp(leveldown, test, testCommon)
+ module.exports.errorKeys(test, BufferType)
+ module.exports.nonErrorKeys(test)
+ module.exports.errorValues(test, BufferType)
+ module.exports.nonErrorKeys(test)
+ module.exports.tearDown(test, testCommon)
+}
diff --git a/abstract/put-test.js b/abstract/put-test.js
new file mode 100644
index 0000000..55558ee
--- /dev/null
+++ b/abstract/put-test.js
@@ -0,0 +1,92 @@
+var db
+ , verifyNotFoundError = require('./util').verifyNotFoundError
+ , isTypedArray = require('./util').isTypedArray
+
+module.exports.setUp = function (leveldown, test, testCommon) {
+ test('setUp common', testCommon.setUp)
+ test('setUp db', function (t) {
+ db = leveldown(testCommon.location())
+ db.open(t.end.bind(t))
+ })
+}
+
+module.exports.args = function (test) {
+ test('test argument-less put() throws', function (t) {
+ t.throws(
+ db.put.bind(db)
+ , { name: 'Error', message: 'put() requires a callback argument' }
+ , 'no-arg put() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 1-arg, put() throws', function (t) {
+ t.throws(
+ db.put.bind(db, 'foo')
+ , { name: 'Error', message: 'put() requires a callback argument' }
+ , 'callback-less, 1-arg put() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 2-arg, put() throws', function (t) {
+ t.throws(
+ db.put.bind(db, 'foo', 'bar')
+ , { name: 'Error', message: 'put() requires a callback argument' }
+ , 'callback-less, 2-arg put() throws'
+ )
+ t.end()
+ })
+
+ test('test callback-less, 3-arg, put() throws', function (t) {
+ t.throws(
+ db.put.bind(db, 'foo', 'bar', {})
+ , { name: 'Error', message: 'put() requires a callback argument' }
+ , 'callback-less, 3-arg put() throws'
+ )
+ t.end()
+ })
+}
+
+module.exports.put = function (test) {
+ test('test simple put()', function (t) {
+ db.put('foo', 'bar', function (err) {
+ t.notOk(err, 'no error')
+ db.get('foo', function (err, value) {
+ t.notOk(err, 'no error')
+ var result = value.toString()
+ if (isTypedArray(value))
+ result = String.fromCharCode.apply(null, new Uint16Array(value))
+ t.equal(result, 'bar')
+ t.end()
+ })
+ })
+ })
+
+ if (process.browser) {
+ test('test object value put()', function (t) {
+ db.put('dood', {pete: 'sampras'}, function (err) {
+ t.notOk(err, 'no error')
+ db.get('dood', { asBuffer: false }, function (err, value) {
+ t.notOk(err, 'no error')
+ t.equal(JSON.stringify(value), JSON.stringify({pete: 'sampras'}))
+ t.end()
+ })
+ })
+ })
+ }
+
+}
+
+module.exports.tearDown = function (test, testCommon) {
+ test('tearDown', function (t) {
+ db.close(testCommon.tearDown.bind(null, t))
+ })
+}
+
+module.exports.all = function (leveldown, test, testCommon) {
+ module.exports.setUp(leveldown, test, testCommon)
+ module.exports.args(test)
+ module.exports.put(test)
+ module.exports.tearDown(test, testCommon)
+}
\ No newline at end of file
diff --git a/abstract/ranges-test.js b/abstract/ranges-test.js
new file mode 100644
index 0000000..9b8e1da
--- /dev/null
+++ b/abstract/ranges-test.js
@@ -0,0 +1,435 @@
+var db
+ , sourceData = require('./iterator-test').sourceData
+ , transformSource = require('./iterator-test').transformSource
+
+module.exports.setUp = function (leveldown, test, testCommon) {
+ test('setUp common', testCommon.setUp)
+ test('setUp db', function (t) {
+ db = leveldown(testCommon.location())
+ db.open(t.end.bind(t))
+ })
+}
+
+module.exports.iterator = function (leveldown, test, testCommon, collectEntries) {
+ test('test simple iterator()', function (t) {
+ var data = [
+ { type: 'put', key: 'foobatch1', value: 'bar1' }
+ , { type: 'put', key: 'foobatch2', value: 'bar2' }
+ , { type: 'put', key: 'foobatch3', value: 'bar3' }
+ ]
+ , idx = 0
+
+ db.batch(data, function (err) {
+ t.notOk(err, 'no error')
+ var iterator = db.iterator()
+ , fn = function (err, key, value) {
+ t.notOk(err, 'no error')
+ if (key && value) {
+ t.equal(key.toString(), data[idx].key, 'correct key')
+ t.equal(value.toString(), data[idx].value, 'correct value')
+ process.nextTick(next)
+ idx++
+ } else { // end
+ t.ok(typeof err === 'undefined', 'err argument is undefined')
+ t.ok(typeof key === 'undefined', 'key argument is undefined')
+ t.ok(typeof value === 'undefined', 'value argument is undefined')
+ t.equal(idx, data.length, 'correct number of entries')
+ iterator.end(function () {
+ t.end()
+ })
+ }
+ }
+ , next = function () {
+ iterator.next(fn)
+ }
+
+ next()
+ })
+ })
+
+ /** the following tests are mirroring the same series of tests in
+ * LevelUP read-stream-test.js
+ */
+
+ test('setUp #2', function (t) {
+ db.close(function () {
+ db = leveldown(testCommon.location())
+ db.open(function () {
+ db.batch(sourceData, t.end.bind(t))
+ })
+ })
+ })
+
+ test('test full data collection', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, sourceData.length, 'correct number of entries')
+ var expected = sourceData.map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, sourceData.length, 'correct number of entries')
+ var expected = sourceData.slice().reverse().map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with gte=0', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gte: '00' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, sourceData.length, 'correct number of entries')
+ var expected = sourceData.map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with gte=50', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gte: '50' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 50, 'correct number of entries')
+ var expected = sourceData.slice(50).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with lte=50 and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lte: '50', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 51, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(49).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start being a midway key (49.5)', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gte: '49.5' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 50, 'correct number of entries')
+ var expected = sourceData.slice(50).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start being a midway key (49999)', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gte: '49999' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 50, 'correct number of entries')
+ var expected = sourceData.slice(50).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start being a midway key and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lte: '49.5', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 50, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(50).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start being a midway key and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lt: '49.5', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 50, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(50).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start being a midway key and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lt: '50', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 50, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(50).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end=50', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lte: '50' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 51, 'correct number of entries')
+ var expected = sourceData.slice(0, 51).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end being a midway key (50.5)', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lte: '50.5' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 51, 'correct number of entries')
+ var expected = sourceData.slice(0, 51).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end being a midway key (50555)', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lte: '50555' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 51, 'correct number of entries')
+ var expected = sourceData.slice(0, 51).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end being a midway key (50555)', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lt: '50555' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 51, 'correct number of entries')
+ var expected = sourceData.slice(0, 51).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end being a midway key and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gte: '50.5', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 49, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(0, 49).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with gt a midway key and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gt: '50.5', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 49, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(0, 49).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with gt a midway key and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gt: '50', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 49, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(0, 49).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with gt 50 key and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gt: '50', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 49, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(0, 49).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ // end='0', starting key is actually '00' so it should avoid it
+ test('test iterator with end=0', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lte: '0' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 0, 'correct number of entries')
+ t.end()
+ })
+ })
+
+ // end='0', starting key is actually '00' so it should avoid it
+ test('test iterator with end<0', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lt: '0' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 0, 'correct number of entries')
+ t.end()
+ })
+ })
+
+ test('test iterator with start=30 and end=70', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gte: '30', lte: '70' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 41, 'correct number of entries')
+ var expected = sourceData.slice(30, 71).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start=30 and end=70', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gt: '29', lt: '71' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 41, 'correct number of entries')
+ var expected = sourceData.slice(30, 71).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start=30 and end=70 and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lte: '70', gte: '30', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 41, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(29, 70).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start=30 and end=70 and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lt: '71', gt: '29', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 41, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(29, 70).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with limit=20 and start=20', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gte: '20', limit: 20 }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 20, 'correct number of entries')
+ var expected = sourceData.slice(20, 40).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with limit=20 and start=79 and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lte: '79', limit: 20, reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 20, 'correct number of entries')
+ var expected = sourceData.slice().reverse().slice(20, 40).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end after limit', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, limit: 20, lte: '50' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 20, 'correct number of entries')
+ var expected = sourceData.slice(0, 20).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with end before limit', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, limit: 50, lte: '19' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 20, 'correct number of entries')
+ var expected = sourceData.slice(0, 20).map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start after database end', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gte: '9a' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 0, 'correct number of entries')
+ t.end()
+ })
+ })
+
+ test('test iterator with start after database end', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, gt: '9a' }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 0, 'correct number of entries')
+ t.end()
+ })
+ })
+
+ test('test iterator with start after database end and reverse=true', function (t) {
+ collectEntries(db.iterator({ keyAsBuffer: false, valueAsBuffer: false, lte: '9a', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, sourceData.length, 'correct number of entries')
+ var expected = sourceData.slice().reverse().map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+
+ test('test iterator with start and end after database and and reverse=true', function (t) {
+ collectEntries(db.iterator({ lte: '9b', gte: '9a', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 0, 'correct number of entries')
+ t.end()
+ })
+ })
+
+ test('test iterator with lt and gt after database and and reverse=true', function (t) {
+ collectEntries(db.iterator({ lt: '9b', gt: '9a', reverse: true }), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 0, 'correct number of entries')
+ t.end()
+ })
+ })
+
+ function testIteratorCollectsFullDatabase (name, iteratorOptions) {
+ iteratorOptions.keyAsBuffer = false
+ iteratorOptions.valueAsBuffer = false
+ test(name, function (t) {
+ collectEntries(db.iterator(iteratorOptions), function (err, data) {
+ t.notOk(err, 'no error')
+ t.equal(data.length, 100, 'correct number of entries')
+ var expected = sourceData.map(transformSource)
+ t.deepEqual(data, expected)
+ t.end()
+ })
+ })
+ }
+ if (!process.browser) {
+ // Can't use buffers as query keys in indexeddb (I think :P)
+ testIteratorCollectsFullDatabase(
+ 'test iterator with start as empty buffer'
+ , { start: new Buffer(0) }
+ )
+ testIteratorCollectsFullDatabase(
+ 'test iterator with end as empty buffer'
+ , { end: new Buffer(0) }
+ )
+ }
+ testIteratorCollectsFullDatabase(
+ 'test iterator with start as empty string'
+ , { gte: '' }
+ )
+ testIteratorCollectsFullDatabase(
+ 'test iterator with start as null'
+ , { gte: null }
+ )
+ testIteratorCollectsFullDatabase(
+ 'test iterator with end as empty string'
+ , { lte: '' }
+ )
+ testIteratorCollectsFullDatabase(
+ 'test iterator with end as null'
+ , { lte: null }
+ )
+}
+
+module.exports.tearDown = function (test, testCommon) {
+ test('tearDown', function (t) {
+ db.close(testCommon.tearDown.bind(null, t))
+ })
+}
+
+module.exports.all = function (leveldown, test, testCommon) {
+ module.exports.setUp(leveldown, test, testCommon)
+ module.exports.iterator(leveldown, test, testCommon, testCommon.collectEntries)
+ module.exports.tearDown(test, testCommon)
+}
diff --git a/abstract/util.js b/abstract/util.js
new file mode 100644
index 0000000..82f155e
--- /dev/null
+++ b/abstract/util.js
@@ -0,0 +1,8 @@
+module.exports.verifyNotFoundError = function verifyNotFoundError (err) {
+ return (/NotFound/i).test(err.message)
+}
+
+module.exports.isTypedArray = function isTypedArray (value) {
+ return (typeof ArrayBuffer != 'undefined' && value instanceof ArrayBuffer)
+ || (typeof Uint8Array != 'undefined' && value instanceof Uint8Array)
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..d123c5a
--- /dev/null
+++ b/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "abstract-leveldown",
+ "description": "An abstract prototype matching the LevelDOWN API",
+ "version": "0.12.3",
+ "contributors": [
+ "Rod Vagg <r at va.gg> (https://github.com/rvagg)",
+ "John Chesley <john at chesl.es> (https://github.com/chesles/)",
+ "Jake Verbaten <raynos2 at gmail.com> (https://github.com/raynos)",
+ "Dominic Tarr <dominic.tarr at gmail.com> (https://github.com/dominictarr)",
+ "Max Ogden <max at maxogden.com> (https://github.com/maxogden)",
+ "Lars-Magnus Skog <lars.magnus.skog at gmail.com> (https://github.com/ralphtheninja)",
+ "David Björklund <david.bjorklund at gmail.com> (https://github.com/kesla)",
+ "Julian Gruber <julian at juliangruber.com> (https://github.com/juliangruber)",
+ "Paolo Fragomeni <paolo at async.ly> (https://github.com/hij1nx)",
+ "Anton Whalley <anton.whalley at nearform.com> (https://github.com/No9)",
+ "Matteo Collina <matteo.collina at gmail.com> (https://github.com/mcollina)",
+ "Pedro Teixeira <pedro.teixeira at gmail.com> (https://github.com/pgte)",
+ "James Halliday <mail at substack.net> (https://github.com/substack)"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/rvagg/node-abstract-leveldown.git"
+ },
+ "homepage": "https://github.com/rvagg/node-abstract-leveldown",
+ "keywords": [
+ "leveldb",
+ "leveldown",
+ "levelup"
+ ],
+ "main": "./abstract-leveldown.js",
+ "dependencies": {
+ "xtend": "~3.0.0"
+ },
+ "devDependencies": {
+ "tap": "*",
+ "sinon": "*",
+ "rimraf": "*"
+ },
+ "browser": {
+ "rimraf": false
+ },
+ "scripts": {
+ "test": "node ./test.js"
+ },
+ "license": "MIT"
+}
diff --git a/test.js b/test.js
new file mode 100644
index 0000000..4ec2870
--- /dev/null
+++ b/test.js
@@ -0,0 +1,559 @@
+const tap = require('tap')
+ , sinon = require('sinon')
+ , util = require('util')
+ , testCommon = require('./testCommon')
+ , AbstractLevelDOWN = require('./').AbstractLevelDOWN
+ , AbstractIterator = require('./').AbstractIterator
+ , AbstractChainedBatch = require('./').AbstractChainedBatch
+
+function factory (location) {
+ return new AbstractLevelDOWN(location)
+}
+
+/*** compatibility with basic LevelDOWN API ***/
+
+require('./abstract/leveldown-test').args(factory, tap.test, testCommon)
+
+require('./abstract/open-test').args(factory, tap.test, testCommon)
+
+require('./abstract/del-test').setUp(factory, tap.test, testCommon)
+require('./abstract/del-test').args(tap.test)
+
+require('./abstract/get-test').setUp(factory, tap.test, testCommon)
+require('./abstract/get-test').args(tap.test)
+
+require('./abstract/put-test').setUp(factory, tap.test, testCommon)
+require('./abstract/put-test').args(tap.test)
+
+require('./abstract/put-get-del-test').setUp(factory, tap.test, testCommon)
+require('./abstract/put-get-del-test').errorKeys(tap.test)
+//require('./abstract/put-get-del-test').nonErrorKeys(tap.test, testCommon)
+require('./abstract/put-get-del-test').errorValues(tap.test)
+//require('./abstract/test/put-get-del-test').nonErrorKeys(tap.test, testCommon)
+require('./abstract/put-get-del-test').tearDown(tap.test, testCommon)
+
+require('./abstract/approximate-size-test').setUp(factory, tap.test, testCommon)
+require('./abstract/approximate-size-test').args(tap.test)
+
+require('./abstract/batch-test').setUp(factory, tap.test, testCommon)
+require('./abstract/batch-test').args(tap.test)
+
+require('./abstract/chained-batch-test').setUp(factory, tap.test, testCommon)
+require('./abstract/chained-batch-test').args(tap.test)
+
+require('./abstract/close-test').close(factory, tap.test, testCommon)
+
+require('./abstract/iterator-test').setUp(factory, tap.test, testCommon)
+require('./abstract/iterator-test').args(tap.test)
+require('./abstract/iterator-test').sequence(tap.test)
+
+/*** extensibility ***/
+
+tap.test('test core extensibility', function (t) {
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ t.equal(this.location, location, 'location set on `this`')
+ t.end()
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ ;new Test('foobar')
+})
+
+tap.test('test open() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedCb = function () {}
+ , expectedOptions = { options: 1 }
+ , test
+
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ Test.prototype._open = spy
+
+ test = new Test('foobar')
+ test.open(expectedCb)
+
+ t.equal(spy.callCount, 1, 'got _open() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _open() was correct')
+ t.equal(spy.getCall(0).args.length, 2, 'got two arguments')
+ t.deepEqual(spy.getCall(0).args[0], {}, 'got blank options argument')
+ t.equal(spy.getCall(0).args[1], expectedCb, 'got expected cb argument')
+
+ test.open(expectedOptions, expectedCb)
+
+ t.equal(spy.callCount, 2, 'got _open() call')
+ t.equal(spy.getCall(1).thisValue, test, '`this` on _open() was correct')
+ t.equal(spy.getCall(1).args.length, 2, 'got two arguments')
+ t.deepEqual(spy.getCall(1).args[0], expectedOptions, 'got blank options argument')
+ t.equal(spy.getCall(1).args[1], expectedCb, 'got expected cb argument')
+ t.end()
+})
+
+tap.test('test close() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedCb = function () {}
+ , test
+
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ Test.prototype._close = spy
+
+ test = new Test('foobar')
+ test.close(expectedCb)
+
+ t.equal(spy.callCount, 1, 'got _close() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _close() was correct')
+ t.equal(spy.getCall(0).args.length, 1, 'got one arguments')
+ t.equal(spy.getCall(0).args[0], expectedCb, 'got expected cb argument')
+ t.end()
+})
+
+tap.test('test get() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedCb = function () {}
+ , expectedOptions = { options: 1 }
+ , expectedKey = 'a key'
+ , test
+
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ Test.prototype._get = spy
+
+ test = new Test('foobar')
+ test.get(expectedKey, expectedCb)
+
+ t.equal(spy.callCount, 1, 'got _get() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _get() was correct')
+ t.equal(spy.getCall(0).args.length, 3, 'got three arguments')
+ t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument')
+ t.deepEqual(spy.getCall(0).args[1], {}, 'got blank options argument')
+ t.equal(spy.getCall(0).args[2], expectedCb, 'got expected cb argument')
+
+ test.get(expectedKey, expectedOptions, expectedCb)
+
+ t.equal(spy.callCount, 2, 'got _get() call')
+ t.equal(spy.getCall(1).thisValue, test, '`this` on _get() was correct')
+ t.equal(spy.getCall(1).args.length, 3, 'got three arguments')
+ t.equal(spy.getCall(1).args[0], expectedKey, 'got expected key argument')
+ t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got blank options argument')
+ t.equal(spy.getCall(1).args[2], expectedCb, 'got expected cb argument')
+ t.end()
+})
+
+tap.test('test del() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedCb = function () {}
+ , expectedOptions = { options: 1 }
+ , expectedKey = 'a key'
+ , test
+
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ Test.prototype._del = spy
+
+ test = new Test('foobar')
+ test.del(expectedKey, expectedCb)
+
+ t.equal(spy.callCount, 1, 'got _del() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _del() was correct')
+ t.equal(spy.getCall(0).args.length, 3, 'got three arguments')
+ t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument')
+ t.deepEqual(spy.getCall(0).args[1], {}, 'got blank options argument')
+ t.equal(spy.getCall(0).args[2], expectedCb, 'got expected cb argument')
+
+ test.del(expectedKey, expectedOptions, expectedCb)
+
+ t.equal(spy.callCount, 2, 'got _del() call')
+ t.equal(spy.getCall(1).thisValue, test, '`this` on _del() was correct')
+ t.equal(spy.getCall(1).args.length, 3, 'got three arguments')
+ t.equal(spy.getCall(1).args[0], expectedKey, 'got expected key argument')
+ t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got blank options argument')
+ t.equal(spy.getCall(1).args[2], expectedCb, 'got expected cb argument')
+ t.end()
+})
+
+tap.test('test put() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedCb = function () {}
+ , expectedOptions = { options: 1 }
+ , expectedKey = 'a key'
+ , expectedValue = 'a value'
+ , test
+
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ Test.prototype._put = spy
+
+ test = new Test('foobar')
+ test.put(expectedKey, expectedValue, expectedCb)
+
+ t.equal(spy.callCount, 1, 'got _put() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _put() was correct')
+ t.equal(spy.getCall(0).args.length, 4, 'got four arguments')
+ t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument')
+ t.equal(spy.getCall(0).args[1], expectedValue, 'got expected value argument')
+ t.deepEqual(spy.getCall(0).args[2], {}, 'got blank options argument')
+ t.equal(spy.getCall(0).args[3], expectedCb, 'got expected cb argument')
+
+ test.put(expectedKey, expectedValue, expectedOptions, expectedCb)
+
+ t.equal(spy.callCount, 2, 'got _put() call')
+ t.equal(spy.getCall(1).thisValue, test, '`this` on _put() was correct')
+ t.equal(spy.getCall(1).args.length, 4, 'got four arguments')
+ t.equal(spy.getCall(1).args[0], expectedKey, 'got expected key argument')
+ t.equal(spy.getCall(1).args[1], expectedValue, 'got expected value argument')
+ t.deepEqual(spy.getCall(1).args[2], expectedOptions, 'got blank options argument')
+ t.equal(spy.getCall(1).args[3], expectedCb, 'got expected cb argument')
+ t.end()
+})
+
+tap.test('test approximateSize() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedCb = function () {}
+ , expectedStart = 'a start'
+ , expectedEnd = 'an end'
+ , test
+
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ Test.prototype._approximateSize = spy
+
+ test = new Test('foobar')
+ test.approximateSize(expectedStart, expectedEnd, expectedCb)
+
+ t.equal(spy.callCount, 1, 'got _approximateSize() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _approximateSize() was correct')
+ t.equal(spy.getCall(0).args.length, 3, 'got three arguments')
+ t.equal(spy.getCall(0).args[0], expectedStart, 'got expected start argument')
+ t.equal(spy.getCall(0).args[1], expectedEnd, 'got expected end argument')
+ t.equal(spy.getCall(0).args[2], expectedCb, 'got expected cb argument')
+ t.end()
+})
+
+tap.test('test batch() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedCb = function () {}
+ , expectedOptions = { options: 1 }
+ , expectedArray = [ 1, 2 ]
+ , test
+
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ Test.prototype._batch = spy
+
+ test = new Test('foobar')
+
+ test.batch(expectedArray, expectedCb)
+
+ t.equal(spy.callCount, 1, 'got _batch() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _batch() was correct')
+ t.equal(spy.getCall(0).args.length, 3, 'got three arguments')
+ t.equal(spy.getCall(0).args[0], expectedArray, 'got expected array argument')
+ t.deepEqual(spy.getCall(0).args[1], {}, 'got expected options argument')
+ t.equal(spy.getCall(0).args[2], expectedCb, 'got expected callback argument')
+
+ test.batch(expectedArray, expectedOptions, expectedCb)
+
+ t.equal(spy.callCount, 2, 'got _batch() call')
+ t.equal(spy.getCall(1).thisValue, test, '`this` on _batch() was correct')
+ t.equal(spy.getCall(1).args.length, 3, 'got three arguments')
+ t.equal(spy.getCall(1).args[0], expectedArray, 'got expected array argument')
+ t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument')
+ t.equal(spy.getCall(1).args[2], expectedCb, 'got expected callback argument')
+
+ t.end()
+})
+
+tap.test('test chained batch() (array) extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedCb = function () {}
+ , expectedOptions = { options: 1 }
+ , expectedArray = [ 1, 2 ]
+ , test
+
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ Test.prototype._batch = spy
+
+ test = new Test('foobar')
+
+ test.batch().put('foo', 'bar').del('bang').write(expectedCb)
+
+ t.equal(spy.callCount, 1, 'got _batch() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _batch() was correct')
+ t.equal(spy.getCall(0).args.length, 3, 'got three arguments')
+ t.equal(spy.getCall(0).args[0].length, 2, 'got expected array argument')
+ t.deepEqual(spy.getCall(0).args[0][0], { type: 'put', key: 'foo', value: 'bar' }, 'got expected array argument[0]')
+ t.deepEqual(spy.getCall(0).args[0][1], { type: 'del', key: 'bang' }, 'got expected array argument[1]')
+ t.deepEqual(spy.getCall(0).args[1], {}, 'got expected options argument')
+ t.equal(spy.getCall(0).args[2], expectedCb, 'got expected callback argument')
+
+ test.batch().put('foo', 'bar').del('bang').write(expectedOptions, expectedCb)
+
+ t.equal(spy.callCount, 2, 'got _batch() call')
+ t.equal(spy.getCall(1).thisValue, test, '`this` on _batch() was correct')
+ t.equal(spy.getCall(1).args.length, 3, 'got three arguments')
+ t.equal(spy.getCall(1).args[0].length, 2, 'got expected array argument')
+ t.deepEqual(spy.getCall(1).args[0][0], { type: 'put', key: 'foo', value: 'bar' }, 'got expected array argument[0]')
+ t.deepEqual(spy.getCall(1).args[0][1], { type: 'del', key: 'bang' }, 'got expected array argument[1]')
+ t.deepEqual(spy.getCall(1).args[1], expectedOptions, 'got expected options argument')
+ t.equal(spy.getCall(1).args[2], expectedCb, 'got expected callback argument')
+
+ t.end()
+})
+
+tap.test('test chained batch() (custom _chainedBatch) extensibility', function (t) {
+ var spy = sinon.spy()
+ , test
+
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ Test.prototype._chainedBatch = spy
+
+ test = new Test('foobar')
+
+ test.batch()
+
+ t.equal(spy.callCount, 1, 'got _chainedBatch() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _chainedBatch() was correct')
+
+ test.batch()
+
+ t.equal(spy.callCount, 2, 'got _chainedBatch() call')
+ t.equal(spy.getCall(1).thisValue, test, '`this` on _chainedBatch() was correct')
+
+ t.end()
+})
+
+tap.test('test AbstractChainedBatch extensibility', function (t) {
+ function Test (db) {
+ AbstractChainedBatch.call(this, db)
+ t.equal(this._db, db, 'db set on `this`')
+ t.end()
+ }
+
+ util.inherits(Test, AbstractChainedBatch)
+
+ new Test('foobar')
+})
+
+tap.test('test write() extensibility', function (t) {
+ var spy = sinon.spy()
+ , spycb = sinon.spy()
+ , test
+
+ function Test (db) {
+ AbstractChainedBatch.call(this, db)
+ }
+
+ util.inherits(Test, AbstractChainedBatch)
+
+ Test.prototype._write = spy
+
+ test = new Test('foobar')
+ test.write(spycb)
+
+ t.equal(spy.callCount, 1, 'got _write() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _write() was correct')
+ t.equal(spy.getCall(0).args.length, 1, 'got one argument')
+ // awkward here cause of nextTick & an internal wrapped cb
+ t.type(spy.getCall(0).args[0], 'function', 'got a callback function')
+ t.equal(spycb.callCount, 0, 'spycb not called')
+ spy.getCall(0).args[0]()
+ t.equal(spycb.callCount, 1, 'spycb called, i.e. was our cb argument')
+ t.end()
+})
+
+tap.test('test put() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedKey = 'key'
+ , expectedValue = 'value'
+ , returnValue
+ , test
+
+ function Test (db) {
+ AbstractChainedBatch.call(this, db)
+ }
+
+ util.inherits(Test, AbstractChainedBatch)
+
+ Test.prototype._put = spy
+
+ test = new Test(factory('foobar'))
+ returnValue = test.put(expectedKey, expectedValue)
+ t.equal(spy.callCount, 1, 'got _put call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _put() was correct')
+ t.equal(spy.getCall(0).args.length, 2, 'got two arguments')
+ t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument')
+ t.equal(spy.getCall(0).args[1], expectedValue, 'got expected value argument')
+ t.equal(returnValue, test, 'get expected return value')
+ t.end()
+})
+
+tap.test('test del() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedKey = 'key'
+ , returnValue
+ , test
+
+ function Test (db) {
+ AbstractChainedBatch.call(this, db)
+ }
+
+ util.inherits(Test, AbstractChainedBatch)
+
+ Test.prototype._del = spy
+
+ test = new Test(factory('foobar'))
+ returnValue = test.del(expectedKey)
+ t.equal(spy.callCount, 1, 'got _del call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _del() was correct')
+ t.equal(spy.getCall(0).args.length, 1, 'got one argument')
+ t.equal(spy.getCall(0).args[0], expectedKey, 'got expected key argument')
+ t.equal(returnValue, test, 'get expected return value')
+ t.end()
+})
+
+tap.test('test clear() extensibility', function (t) {
+ var spy = sinon.spy()
+ , returnValue
+ , test
+
+ function Test (db) {
+ AbstractChainedBatch.call(this, db)
+ }
+
+ util.inherits(Test, AbstractChainedBatch)
+
+ Test.prototype._clear = spy
+
+ test = new Test(factory('foobar'))
+ returnValue = test.clear()
+ t.equal(spy.callCount, 1, 'got _clear call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _clear() was correct')
+ t.equal(spy.getCall(0).args.length, 0, 'got zero arguments')
+ t.equal(returnValue, test, 'get expected return value')
+ t.end()
+})
+
+tap.test('test iterator() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedOptions = { options: 1, reverse: false } // reverse now explicitly set
+ , test
+
+ function Test (location) {
+ AbstractLevelDOWN.call(this, location)
+ }
+
+ util.inherits(Test, AbstractLevelDOWN)
+
+ Test.prototype._iterator = spy
+
+ test = new Test('foobar')
+ test.iterator({ options: 1 })
+
+ t.equal(spy.callCount, 1, 'got _close() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _close() was correct')
+ t.equal(spy.getCall(0).args.length, 1, 'got one arguments')
+ t.deepEqual(spy.getCall(0).args[0], expectedOptions, 'got expected options argument')
+ t.end()
+})
+
+tap.test('test AbstractIterator extensibility', function (t) {
+ function Test (db) {
+ AbstractIterator.call(this, db)
+ t.equal(this.db, db, 'db set on `this`')
+ t.end()
+ }
+
+ util.inherits(Test, AbstractIterator)
+
+ ;new Test('foobar')
+})
+
+tap.test('test next() extensibility', function (t) {
+ var spy = sinon.spy()
+ , spycb = sinon.spy()
+ , test
+
+ function Test (db) {
+ AbstractIterator.call(this, db)
+ }
+
+ util.inherits(Test, AbstractIterator)
+
+ Test.prototype._next = spy
+
+ test = new Test('foobar')
+ test.next(spycb)
+
+ t.equal(spy.callCount, 1, 'got _next() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _next() was correct')
+ t.equal(spy.getCall(0).args.length, 1, 'got one arguments')
+ // awkward here cause of nextTick & an internal wrapped cb
+ t.type(spy.getCall(0).args[0], 'function', 'got a callback function')
+ t.equal(spycb.callCount, 0, 'spycb not called')
+ spy.getCall(0).args[0]()
+ t.equal(spycb.callCount, 1, 'spycb called, i.e. was our cb argument')
+ t.end()
+})
+
+tap.test('test end() extensibility', function (t) {
+ var spy = sinon.spy()
+ , expectedCb = function () {}
+ , test
+
+ function Test (db) {
+ AbstractIterator.call(this, db)
+ }
+
+ util.inherits(Test, AbstractIterator)
+
+ Test.prototype._end = spy
+
+ test = new Test('foobar')
+ test.end(expectedCb)
+
+ t.equal(spy.callCount, 1, 'got _end() call')
+ t.equal(spy.getCall(0).thisValue, test, '`this` on _end() was correct')
+ t.equal(spy.getCall(0).args.length, 1, 'got one arguments')
+ t.equal(spy.getCall(0).args[0], expectedCb, 'got expected cb argument')
+ t.end()
+})
diff --git a/testCommon.js b/testCommon.js
new file mode 100644
index 0000000..3f9d29d
--- /dev/null
+++ b/testCommon.js
@@ -0,0 +1,75 @@
+var path = require('path')
+ , fs = !process.browser && require('fs')
+ , rimraf = !process.browser && require('rimraf')
+
+var dbidx = 0
+
+ , location = function () {
+ return path.join(__dirname, '_leveldown_test_db_' + dbidx++)
+ }
+
+ , lastLocation = function () {
+ return path.join(__dirname, '_leveldown_test_db_' + dbidx)
+ }
+
+ , cleanup = function (callback) {
+ if (process.browser)
+ return callback()
+
+ fs.readdir(__dirname, function (err, list) {
+ if (err) return callback(err)
+
+ list = list.filter(function (f) {
+ return (/^_leveldown_test_db_/).test(f)
+ })
+
+ if (!list.length)
+ return callback()
+
+ var ret = 0
+
+ list.forEach(function (f) {
+ rimraf(path.join(__dirname, f), function (err) {
+ if (++ret == list.length)
+ callback()
+ })
+ })
+ })
+ }
+
+ , setUp = function (t) {
+ cleanup(function (err) {
+ t.notOk(err, 'cleanup returned an error')
+ t.end()
+ })
+ }
+
+ , tearDown = function (t) {
+ setUp(t) // same cleanup!
+ }
+
+ , collectEntries = function (iterator, callback) {
+ var data = []
+ , next = function () {
+ iterator.next(function (err, key, value) {
+ if (err) return callback(err)
+ if (!arguments.length) {
+ return iterator.end(function (err) {
+ callback(err, data)
+ })
+ }
+ data.push({ key: key, value: value })
+ process.nextTick(next)
+ })
+ }
+ next()
+ }
+
+module.exports = {
+ location : location
+ , cleanup : cleanup
+ , lastLocation : lastLocation
+ , setUp : setUp
+ , tearDown : tearDown
+ , collectEntries : collectEntries
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-abstract-leveldown.git
More information about the Pkg-javascript-commits
mailing list