Skip to content

Commit

Permalink
Isolate snapshot test (#239)
Browse files Browse the repository at this point in the history
* isolate snapshot test (and test setup and teardown)

* test overwriting and adding key after snapshotting

* add test for implementations that cannot support snapshots

* list tests in readme

* cb => done
  • Loading branch information
vweevers authored Jun 22, 2018
1 parent d806bf1 commit e2f96ca
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 22 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,30 @@ Provided with the current instance of `AbstractLevelDOWN` by default.
### `AbstractChainedBatch#_serializeKey(key)`
### `AbstractChainedBatch#_serializeValue(value)`

## Test Suite

To prove that your implementation is `abstract-leveldown` compliant, include the test suite found in `abstract/`. For examples please see the test suites of implementations like [`leveldown`](https://github.com/Level/leveldown), [`level-js`](https://github.com/Level/level-js) or [`memdown`](https://github.com/Level/memdown).

As not every implementation can be fully compliant due to limitations of its underlying storage, some tests may be skipped.

| Test | Required | Skip if |
|:-----------------------|:---------|:----------------------------------------|
| `leveldown` | :x: | Constructor has no `location` argument |
| `open` | :heavy_check_mark: | - |
| `put` | :heavy_check_mark: | - |
| `del` | :heavy_check_mark: | - |
| `get` | :heavy_check_mark: | - |
| `put-get-del` | :heavy_check_mark: | - |
| `batch` | :heavy_check_mark: | - |
| `chained-batch` | :heavy_check_mark: | - |
| `close` | :heavy_check_mark: | - |
| `iterator` | :heavy_check_mark: | - |
| `iterator-range` | :heavy_check_mark: | - |
| `iterator-snapshot` | :x: | Reads don't operate on a snapshot<br>Snapshots are created asynchronously |
| `iterator-no-snapshot` | :x: | The `iterator-snapshot` test is included |

If snapshots are an optional feature of your implementation, both `iterator-snapshot` and `iterator-no-snapshot` may be included.

<a name="contributing"></a>
## Contributing

Expand Down
71 changes: 71 additions & 0 deletions abstract/iterator-no-snapshot-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
exports.setUp = function (leveldown, test, testCommon) {
test('setUp common', testCommon.setUp)
}

exports.noSnapshot = function (leveldown, test, testCommon) {
function make (run) {
return function (t) {
var db = leveldown(testCommon.location())
var operations = [
{ type: 'put', key: 'a', value: 'a' },
{ type: 'put', key: 'b', value: 'b' },
{ type: 'put', key: 'c', value: 'c' }
]

db.open(function (err) {
t.ifError(err, 'no open error')

db.batch(operations, function (err) {
t.ifError(err, 'no batch error')

// For this test it is important that we don't read eagerly.
// NOTE: highWaterMark is not an abstract option atm, but
// it is supported by leveldown, rocksdb and others.
var it = db.iterator({ highWaterMark: 0 })

run(db, function (err) {
t.ifError(err, 'no run error')
verify(t, it, db)
})
})
})
}
}

function verify (t, it, db) {
testCommon.collectEntries(it, function (err, entries) {
t.ifError(err, 'no iterator error')

var kv = entries.map(function (entry) {
return entry.key.toString() + entry.value.toString()
})

if (kv.length === 3) {
t.same(kv, ['aa', 'bb', 'cc'], 'maybe supports snapshots')
} else {
t.same(kv, ['aa', 'cc'], 'ignores keys that have been deleted in the mean time')
}

db.close(t.end.bind(t))
})
}

test('delete key after creating iterator', make(function (db, done) {
db.del('b', done)
}))

test('batch delete key after creating iterator', make(function (db, done) {
db.batch([{ type: 'del', key: 'b' }], done)
}))
}

exports.tearDown = function (test, testCommon) {
test('tearDown', testCommon.tearDown)
}

exports.all = function (leveldown, test, testCommon) {
testCommon = testCommon || require('../testCommon')
exports.setUp(leveldown, test, testCommon)
exports.noSnapshot(leveldown, test, testCommon)
exports.tearDown(test, testCommon)
}
90 changes: 90 additions & 0 deletions abstract/iterator-snapshot-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
exports.setUp = function (leveldown, test, testCommon) {
test('setUp common', testCommon.setUp)
}

exports.snapshot = function (leveldown, test, testCommon) {
function make (run) {
return function (t) {
var db = leveldown(testCommon.location())

db.open(function (err) {
t.ifError(err, 'no open error')

db.put('z', 'from snapshot', function (err) {
t.ifError(err, 'no put error')

// For this test it is important that we don't read eagerly.
// NOTE: highWaterMark is not an abstract option atm, but
// it is supported by leveldown, rocksdb and others.
var it = db.iterator({ highWaterMark: 0 })

run(t, db, it, function end (err) {
t.ifError(err, 'no run error')

it.end(function (err) {
t.ifError(err, 'no iterator end error')
db.close(t.end.bind(t))
})
})
})
})
}
}

test('delete key after snapshotting', make(function (t, db, it, end) {
db.del('z', function (err) {
t.ifError(err, 'no del error')

it.next(function (err, key, value) {
t.ifError(err, 'no next error')
t.ok(key, 'got a key')
t.is(key.toString(), 'z', 'correct key')
t.is(value.toString(), 'from snapshot', 'correct value')

end()
})
})
}))

test('overwrite key after snapshotting', make(function (t, db, it, end) {
db.put('z', 'not from snapshot', function (err) {
t.ifError(err, 'no put error')

it.next(function (err, key, value) {
t.ifError(err, 'no next error')
t.ok(key, 'got a key')
t.is(key.toString(), 'z', 'correct key')
t.is(value.toString(), 'from snapshot', 'correct value')

end()
})
})
}))

test('add key after snapshotting that sorts first', make(function (t, db, it, end) {
db.put('a', 'not from snapshot', function (err) {
t.ifError(err, 'no put error')

it.next(function (err, key, value) {
t.ifError(err, 'no next error')

t.ok(key, 'got a key')
t.is(key.toString(), 'z', 'correct key')
t.is(value.toString(), 'from snapshot', 'correct value')

end()
})
})
}))
}

exports.tearDown = function (test, testCommon) {
test('tearDown', testCommon.tearDown)
}

exports.all = function (leveldown, test, testCommon) {
testCommon = testCommon || require('../testCommon')
exports.setUp(leveldown, test, testCommon)
exports.snapshot(leveldown, test, testCommon)
exports.tearDown(test, testCommon)
}
23 changes: 1 addition & 22 deletions abstract/iterator-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,27 +152,7 @@ module.exports.iterator = function (leveldown, test, testCommon) {
}

module.exports.snapshot = function (leveldown, test, testCommon) {
test('setUp #2', function (t) {
db.close(function () {
db = leveldown(testCommon.location())
db.open(function () {
db.put('foobatch1', 'bar1', t.end.bind(t))
})
})
})

test('iterator create snapshot correctly', function (t) {
var iterator = db.iterator()
db.del('foobatch1', function () {
iterator.next(function (err, key, value) {
t.error(err)
t.ok(key, 'got a key')
t.is(key.toString(), 'foobatch1', 'correct key')
t.is(value.toString(), 'bar1', 'correct value')
iterator.end(t.end.bind(t))
})
})
})
console.error('DEPRECATED: the snapshot test has moved to iterator-snapshot-test.js')
}

module.exports.tearDown = function (test, testCommon) {
Expand All @@ -187,6 +167,5 @@ module.exports.all = function (leveldown, test, testCommon) {
module.exports.args(test)
module.exports.sequence(test)
module.exports.iterator(leveldown, test, testCommon)
module.exports.snapshot(leveldown, test, testCommon)
module.exports.tearDown(test, testCommon)
}
10 changes: 10 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ require('./abstract/close-test').close(factory, test, testCommon)
require('./abstract/iterator-test').setUp(factory, test, testCommon)
require('./abstract/iterator-test').args(test)
require('./abstract/iterator-test').sequence(test)
require('./abstract/iterator-test').tearDown(test, testCommon)

require('./abstract/iterator-range-test').setUp(factory, test, testCommon, [])
require('./abstract/iterator-range-test').tearDown(test, testCommon)

require('./abstract/iterator-snapshot-test').setUp(factory, test, testCommon)
require('./abstract/iterator-snapshot-test').tearDown(test, testCommon)

require('./abstract/iterator-no-snapshot-test').setUp(factory, test, testCommon)
require('./abstract/iterator-no-snapshot-test').tearDown(test, testCommon)

function implement (ctor, methods) {
function Test () {
Expand Down

0 comments on commit e2f96ca

Please sign in to comment.