diff --git a/docs/api.md b/docs/api.md index bb8bd6f53..dee8e0dcd 100644 --- a/docs/api.md +++ b/docs/api.md @@ -15,6 +15,7 @@ - [Database#aggregate()](#aggregatename-options---this) - [Database#loadExtension()](#loadextensionpath---this) - [Database#exec()](#execstring---this) +- [Database#withUndefinedBehavior()](#withUndefinedBehaviorfunction---this) - [Database#close()](#close---this) - [Properties](#properties) @@ -263,6 +264,18 @@ const migration = fs.readFileSync('migrate-schema.sql', 'utf8'); db.exec(migration); ``` +### .withUndefinedBehavior(*function*) -> *this* + +Immediately synchronously executes the given function. Inside the function operations with potentially undefined behavior (as defined by the SQLite documentation) are allowed. This means certain sanity checks that better-sqlite3 performs are disabled. For example doing writes inside an iterator or executing statements inside the implementation of a user-defined function. + +Only use `withUndefinedBehavior` if you absolutely know what you are doing. Use it as low as possible in your call chain to only cover the absolute minimum number of operations you need to do. + +```js +db.withUndefinedBehavior(() => { + // TODO: docs +}); +``` + ### .close() -> *this* Closes the database connection. After invoking this method, no statements can be created or executed. diff --git a/src/objects/database.lzz b/src/objects/database.lzz index 21fe1757b..c234985f7 100644 --- a/src/objects/database.lzz +++ b/src/objects/database.lzz @@ -55,6 +55,7 @@ public: const bool safe_ints; bool was_js_error; unsigned short iterators; + unsigned short undefinedBehaviorBlocks; }; inline State* GetState() { @@ -98,6 +99,7 @@ private: was_js_error(false), has_logger(_logger->IsFunction()), iterators(0), + undefinedBehaviorBlocks(0), logger(isolate, _logger), stmts(), backups() { @@ -118,6 +120,7 @@ private: NODE_SET_PROTOTYPE_METHOD(t, "function", JS_function); NODE_SET_PROTOTYPE_METHOD(t, "aggregate", JS_aggregate); NODE_SET_PROTOTYPE_METHOD(t, "loadExtension", JS_loadExtension); + NODE_SET_PROTOTYPE_METHOD(t, "withUndefinedBehavior", JS_withUndefinedBehavior); NODE_SET_PROTOTYPE_METHOD(t, "close", JS_close); NODE_SET_PROTOTYPE_METHOD(t, "defaultSafeIntegers", JS_defaultSafeIntegers); NODE_SET_PROTOTYPE_GETTER(t, "open", JS_open); @@ -183,8 +186,8 @@ private: Database* db = Unwrap<Database>(info.This()); REQUIRE_ARGUMENT_STRING(first, v8::Local<v8::String> source); REQUIRE_DATABASE_OPEN(db); - REQUIRE_DATABASE_NOT_BUSY(db); - REQUIRE_DATABASE_NO_ITERATORS(db); + PREFER_DATABASE_NOT_BUSY(db); + PREFER_DATABASE_NO_ITERATORS(db); db->busy = true; UseIsolate; if (db->Log(isolate, source)) { @@ -318,6 +321,18 @@ private: sqlite3_free(error); } + NODE_METHOD(JS_withUndefinedBehavior) { + Database* db = Unwrap<Database>(info.This()); + REQUIRE_ARGUMENT_FUNCTION(first, v8::Local<v8::Function> fn); + assert(db->undefinedBehaviorBlocks < USHRT_MAX); + db->undefinedBehaviorBlocks += 1; + UseIsolate; + v8::Local<v8::Function>::Cast(v8::Local<v8::Value>::New(isolate, fn)) + ->Call(OnlyContext, v8::Undefined(isolate), 0, NULL); + assert(db->undefinedBehaviorBlocks > 0); + db->undefinedBehaviorBlocks -= 1; + } + NODE_METHOD(JS_close) { Database* db = Unwrap<Database>(info.This()); if (db->open) { @@ -375,6 +390,7 @@ private: bool was_js_error; const bool has_logger; unsigned short iterators; + unsigned short undefinedBehaviorBlocks; const CopyablePersistent<v8::Value> logger; std::set<Statement*, Database::CompareStatement> stmts; std::set<Backup*, Database::CompareBackup> backups; diff --git a/src/objects/statement-iterator.lzz b/src/objects/statement-iterator.lzz index 0a31d15a9..4c851f549 100644 --- a/src/objects/statement-iterator.lzz +++ b/src/objects/statement-iterator.lzz @@ -68,14 +68,14 @@ private: NODE_METHOD(JS_next) { StatementIterator* iter = Unwrap<StatementIterator>(info.This()); - REQUIRE_DATABASE_NOT_BUSY(iter->db_state); + PREFER_DATABASE_NOT_BUSY(iter->db_state); if (iter->alive) iter->Next(info); else info.GetReturnValue().Set(DoneRecord(OnlyIsolate)); } NODE_METHOD(JS_return) { StatementIterator* iter = Unwrap<StatementIterator>(info.This()); - REQUIRE_DATABASE_NOT_BUSY(iter->db_state); + PREFER_DATABASE_NOT_BUSY(iter->db_state); if (iter->alive) iter->Return(info); else info.GetReturnValue().Set(DoneRecord(OnlyIsolate)); } diff --git a/src/util/macros.lzz b/src/util/macros.lzz index 437a87126..1b8d95799 100644 --- a/src/util/macros.lzz +++ b/src/util/macros.lzz @@ -63,9 +63,15 @@ void ThrowRangeError(const char* message) { EasyIsolate; isolate->ThrowException #define REQUIRE_DATABASE_NOT_BUSY(db) \ if (db->busy) \ return ThrowTypeError("This database connection is busy executing a query") +#define PREFER_DATABASE_NOT_BUSY(db) \ + if (db->busy && !db->undefinedBehaviorBlocks) \ + return ThrowTypeError("This database connection is busy executing a query") #define REQUIRE_DATABASE_NO_ITERATORS(db) \ if (db->iterators) \ return ThrowTypeError("This database connection is busy executing a query") +#define PREFER_DATABASE_NO_ITERATORS(db) \ + if (db->iterators && !db->undefinedBehaviorBlocks) \ + return ThrowTypeError("This database connection is busy executing a query") #define REQUIRE_STATEMENT_NOT_LOCKED(stmt) \ if (stmt->locked) \ return ThrowTypeError("This statement is busy executing a query") diff --git a/src/util/query-macros.lzz b/src/util/query-macros.lzz index 0e10a95bf..f42159074 100644 --- a/src/util/query-macros.lzz +++ b/src/util/query-macros.lzz @@ -21,7 +21,7 @@ sqlite3_stmt* handle = stmt->handle; \ Database* db = stmt->db; \ REQUIRE_DATABASE_OPEN(db->GetState()); \ - REQUIRE_DATABASE_NOT_BUSY(db->GetState()); \ + PREFER_DATABASE_NOT_BUSY(db->GetState()); \ MUTATE_CHECK(); \ const bool bound = stmt->bound; \ if (!bound) { \ diff --git a/test/42.integrity.js b/test/42.integrity.js index 1dd96ac9c..abac1b5fb 100644 --- a/test/42.integrity.js +++ b/test/42.integrity.js @@ -37,24 +37,30 @@ describe('integrity checks', function () { self.db.close(); fn(); }; + const sameWithUndefinedBehavor = function(fn) { + return function() { + fn.call(this); + this.db.withUndefinedBehavior(() => fn.call(this)); + } + }; describe('Database#prepare()', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { whileIterating(this, allowed(() => this.db.prepare('SELECT 555'))); whileIterating(this, allowed(() => this.db.prepare('DELETE FROM entries'))); normally(allowed(() => this.db.prepare('SELECT 555'))); normally(allowed(() => this.db.prepare('DELETE FROM entries'))); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => this.db.prepare('SELECT 555'))); whileBusy(this, blocked(() => this.db.prepare('DELETE FROM entries'))); normally(allowed(() => this.db.prepare('SELECT 555'))); normally(allowed(() => this.db.prepare('DELETE FROM entries'))); - }); - specify('while closed (blocked)', function () { + })); + specify('while closed (blocked)', sameWithUndefinedBehavor(function () { whileClosed(this, blocked(() => this.db.prepare('SELECT 555'))); whileClosed(this, blocked(() => this.db.prepare('DELETE FROM entries'))); - }); + })); }); describe('Database#exec()', function () { @@ -63,45 +69,59 @@ describe('integrity checks', function () { whileIterating(this, blocked(() => this.db.exec('DELETE FROM entries'))); normally(allowed(() => this.db.exec('SELECT 555'))); normally(allowed(() => this.db.exec('DELETE FROM entries'))); + + this.db.withUndefinedBehavior(() => { + whileIterating(this, allowed(() => this.db.exec('SELECT 555'))); + whileIterating(this, allowed(() => this.db.exec('DELETE FROM entries'))); + normally(allowed(() => this.db.exec('SELECT 555'))); + normally(allowed(() => this.db.exec('DELETE FROM entries'))); + }); }); specify('while busy (blocked)', function () { whileBusy(this, blocked(() => this.db.exec('SELECT 555'))); whileBusy(this, blocked(() => this.db.exec('DELETE FROM entries'))); normally(allowed(() => this.db.exec('SELECT 555'))); normally(allowed(() => this.db.exec('DELETE FROM entries'))); - }); - specify('while closed (blocked)', function () { + + this.db.withUndefinedBehavior(() => { + whileBusy(this, allowed(() => this.db.exec('SELECT 555'))); + whileBusy(this, allowed(() => this.db.exec('DELETE FROM entries'))); + normally(allowed(() => this.db.exec('SELECT 555'))); + normally(allowed(() => this.db.exec('DELETE FROM entries'))); + }); + }); + specify('while closed (blocked)', sameWithUndefinedBehavor(function () { whileClosed(this, blocked(() => this.db.exec('SELECT 555'))); whileClosed(this, blocked(() => this.db.exec('DELETE FROM entries'))); - }); + })); }); describe('Database#pragma()', function () { - specify('while iterating (blocked)', function () { + specify('while iterating (blocked)', sameWithUndefinedBehavor(function () { whileIterating(this, blocked(() => this.db.pragma('cache_size'))); normally(allowed(() => this.db.pragma('cache_size'))); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => this.db.pragma('cache_size'))); normally(allowed(() => this.db.pragma('cache_size'))); - }); - specify('while closed (blocked)', function () { + })); + specify('while closed (blocked)', sameWithUndefinedBehavor(function () { whileClosed(this, blocked(() => this.db.pragma('cache_size'))); - }); + })); }); describe('Database#checkpoint()', function () { - specify('while iterating (blocked)', function () { + specify('while iterating (blocked)', sameWithUndefinedBehavor(function () { whileIterating(this, blocked(() => this.db.checkpoint())); normally(allowed(() => this.db.checkpoint())); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => this.db.checkpoint())); normally(allowed(() => this.db.checkpoint())); - }); - specify('while closed (blocked)', function () { + })); + specify('while closed (blocked)', sameWithUndefinedBehavor(function () { whileClosed(this, blocked(() => this.db.checkpoint())); - }); + })); }); describe('Database#backup()', function () { @@ -128,43 +148,43 @@ describe('integrity checks', function () { }); describe('Database#function()', function () { - specify('while iterating (blocked)', function () { + specify('while iterating (blocked)', sameWithUndefinedBehavor(function () { let i = 0; whileIterating(this, blocked(() => this.db.function(`fn_${++i}`, () => {}))); expect(i).to.equal(5); normally(allowed(() => this.db.function(`fn_${++i}`, () => {}))); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { let i = 0; whileBusy(this, blocked(() => this.db.function(`fn_${++i}`, () => {}))); expect(i).to.equal(5); normally(allowed(() => this.db.function(`fn_${++i}`, () => {}))); - }); - specify('while closed (blocked)', function () { + })); + specify('while closed (blocked)', sameWithUndefinedBehavor(function () { let i = 0; whileClosed(this, blocked(() => this.db.function(`fn_${++i}`, () => {}))); expect(i).to.equal(1); - }); + })); }); describe('Database#aggregate()', function () { - specify('while iterating (blocked)', function () { + specify('while iterating (blocked)', sameWithUndefinedBehavor(function () { let i = 0; whileIterating(this, blocked(() => this.db.aggregate(`agg_${++i}`, { step: () => {} }))); expect(i).to.equal(5); normally(allowed(() => this.db.aggregate(`agg_${++i}`, { step: () => {} }))); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { let i = 0; whileBusy(this, blocked(() => this.db.aggregate(`agg_${++i}`, { step: () => {} }))); expect(i).to.equal(5); normally(allowed(() => this.db.aggregate(`agg_${++i}`, { step: () => {} }))); - }); - specify('while closed (blocked)', function () { + })); + specify('while closed (blocked)', sameWithUndefinedBehavor(function () { let i = 0; whileClosed(this, blocked(() => this.db.aggregate(`agg_${++i}`, { step: () => {} }))); expect(i).to.equal(1); - }); + })); }); describe('Database#loadExtension()', function () { @@ -181,73 +201,73 @@ describe('integrity checks', function () { } }); - specify('while iterating (blocked)', function () { + specify('while iterating (blocked)', sameWithUndefinedBehavor(function () { whileIterating(this, blocked(() => this.db.loadExtension(filepath))); normally(allowed(() => this.db.loadExtension(filepath))); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => this.db.loadExtension(filepath))); normally(allowed(() => this.db.loadExtension(filepath))); - }); - specify('while closed (blocked)', function () { + })); + specify('while closed (blocked)', sameWithUndefinedBehavor(function () { whileClosed(this, blocked(() => this.db.loadExtension(filepath))); - }); + })); }); describe('Database#close()', function () { - specify('while iterating (blocked)', function () { + specify('while iterating (blocked)', sameWithUndefinedBehavor(function () { whileIterating(this, blocked(() => this.db.close())); normally(allowed(() => this.db.close())); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => this.db.close())); normally(allowed(() => this.db.close())); - }); - specify('while closed (allowed)', function () { + })); + specify('while closed (allowed)', sameWithUndefinedBehavor(function () { whileClosed(this, allowed(() => this.db.close())); - }); + })); }); describe('Database#defaultSafeIntegers()', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { let bool = true; whileIterating(this, allowed(() => this.db.defaultSafeIntegers(bool = !bool))); - }); - specify('while busy (allowed)', function () { + })); + specify('while busy (allowed)', sameWithUndefinedBehavor(function () { let bool = true; whileBusy(this, allowed(() => this.db.defaultSafeIntegers(bool = !bool))); - }); - specify('while closed (allowed)', function () { + })); + specify('while closed (allowed)', sameWithUndefinedBehavor(function () { let bool = true; whileClosed(this, allowed(() => this.db.defaultSafeIntegers(bool = !bool))); - }); + })); }); describe('Database#open', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { whileIterating(this, allowed(() => expect(this.db.open).to.be.true)); - }); - specify('while busy (allowed)', function () { + })); + specify('while busy (allowed)', sameWithUndefinedBehavor(function () { whileBusy(this, allowed(() => expect(this.db.open).to.be.true)); - }); - specify('while closed (allowed)', function () { + })); + specify('while closed (allowed)', sameWithUndefinedBehavor(function () { whileClosed(this, allowed(() => expect(this.db.open).to.be.false)); - }); + })); }); describe('Database#inTransaction', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { this.db.exec('BEGIN'); whileIterating(this, allowed(() => expect(this.db.inTransaction).to.be.true)); - }); - specify('while busy (allowed)', function () { + })); + specify('while busy (allowed)', sameWithUndefinedBehavor(function () { this.db.exec('BEGIN'); whileBusy(this, allowed(() => expect(this.db.inTransaction).to.be.true)); - }); - specify('while closed (allowed)', function () { + })); + specify('while closed (allowed)', sameWithUndefinedBehavor(function () { this.db.exec('BEGIN'); whileClosed(this, allowed(() => expect(this.db.inTransaction).to.be.false)); - }); + })); }); describe('Statement#run()', function () { @@ -325,114 +345,114 @@ describe('integrity checks', function () { stmt.__bound = true; } }; - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { whileIterating(this, allowed(() => bind(this.reader))); - }); - specify('while self-iterating (blocked)', function () { + })); + specify('while self-iterating (blocked)', sameWithUndefinedBehavor(function () { whileIterating(this, blocked(() => bind(this.iterator))); normally(allowed(() => bind(this.iterator))); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => bind(this.reader))); normally(allowed(() => bind(this.reader))); - }); - specify('while closed (blocked)', function () { + })); + specify('while closed (blocked)', sameWithUndefinedBehavor(function () { whileClosed(this, blocked(() => bind(this.reader))); - }); + })); }); describe('Statement#pluck()', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { whileIterating(this, allowed(() => this.reader.pluck())); normally(allowed(() => this.reader.pluck())); - }); - specify('while self-iterating (blocked)', function () { + })); + specify('while self-iterating (blocked)', sameWithUndefinedBehavor(function () { whileIterating(this, blocked(() => this.iterator.pluck())); normally(allowed(() => this.iterator.pluck())); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => this.reader.pluck())); normally(allowed(() => this.reader.pluck())); - }); - specify('while closed (allowed)', function () { + })); + specify('while closed (allowed)', sameWithUndefinedBehavor(function () { whileClosed(this, allowed(() => this.reader.pluck())); - }); + })); }); describe('Statement#expand()', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { whileIterating(this, allowed(() => this.reader.expand())); normally(allowed(() => this.reader.expand())); - }); - specify('while self-iterating (blocked)', function () { + })); + specify('while self-iterating (blocked)', sameWithUndefinedBehavor(function () { whileIterating(this, blocked(() => this.iterator.expand())); normally(allowed(() => this.iterator.expand())); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => this.reader.expand())); normally(allowed(() => this.reader.expand())); - }); - specify('while closed (allowed)', function () { + })); + specify('while closed (allowed)', sameWithUndefinedBehavor(function () { whileClosed(this, allowed(() => this.reader.expand())); - }); + })); }); describe('Statement#raw()', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { whileIterating(this, allowed(() => this.reader.raw())); normally(allowed(() => this.reader.raw())); - }); - specify('while self-iterating (blocked)', function () { + })); + specify('while self-iterating (blocked)', sameWithUndefinedBehavor(function () { whileIterating(this, blocked(() => this.iterator.raw())); normally(allowed(() => this.iterator.raw())); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => this.reader.raw())); normally(allowed(() => this.reader.raw())); - }); - specify('while closed (allowed)', function () { + })); + specify('while closed (allowed)', sameWithUndefinedBehavor(function () { whileClosed(this, allowed(() => this.reader.raw())); - }); + })); }); describe('Statement#safeIntegers()', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { whileIterating(this, allowed(() => this.reader.safeIntegers())); normally(allowed(() => this.reader.safeIntegers())); - }); - specify('while self-iterating (blocked)', function () { + })); + specify('while self-iterating (blocked)', sameWithUndefinedBehavor(function () { whileIterating(this, blocked(() => this.iterator.safeIntegers())); normally(allowed(() => this.iterator.safeIntegers())); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => this.reader.safeIntegers())); normally(allowed(() => this.reader.safeIntegers())); - }); - specify('while closed (allowed)', function () { + })); + specify('while closed (allowed)', sameWithUndefinedBehavor(function () { whileClosed(this, allowed(() => this.reader.safeIntegers())); - }); + })); }); describe('Statement#columns()', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { whileIterating(this, allowed(() => this.reader.columns())); normally(allowed(() => this.reader.columns())); - }); - specify('while self-iterating (allowed)', function () { + })); + specify('while self-iterating (allowed)', sameWithUndefinedBehavor(function () { whileIterating(this, allowed(() => this.iterator.columns())); normally(allowed(() => this.iterator.columns())); - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { whileBusy(this, blocked(() => this.reader.columns())); normally(allowed(() => this.reader.columns())); - }); - specify('while closed (blocked)', function () { + })); + specify('while closed (blocked)', sameWithUndefinedBehavor(function () { whileClosed(this, blocked(() => this.reader.columns())); - }); + })); }); describe('StatementIterator#next()', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { const iterator = this.reader.iterate(); try { whileIterating(this, allowed(() => iterator.next())); @@ -440,8 +460,8 @@ describe('integrity checks', function () { } finally { iterator.return(); } - }); - specify('while self-iterating (allowed)', function () { + })); + specify('while self-iterating (allowed)', sameWithUndefinedBehavor(function () { const iterator = this.iterator.iterate(); try { let count = 0; @@ -453,8 +473,8 @@ describe('integrity checks', function () { } finally { iterator.return(); } - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { const iterator = this.reader.iterate(); try { whileBusy(this, blocked(() => iterator.next())); @@ -462,16 +482,16 @@ describe('integrity checks', function () { } finally { iterator.return(); } - }); - specify('while closed (allowed)', function () { + })); + specify('while closed (allowed)', sameWithUndefinedBehavor(function () { const iterator = this.reader.iterate(); iterator.return(); whileClosed(this, allowed(() => iterator.next())); - }); + })); }); describe('StatementIterator#return()', function () { - specify('while iterating (allowed)', function () { + specify('while iterating (allowed)', sameWithUndefinedBehavor(function () { const iterator = this.reader.iterate(); try { whileIterating(this, allowed(() => iterator.return())); @@ -479,8 +499,8 @@ describe('integrity checks', function () { } finally { iterator.return(); } - }); - specify('while self-iterating (allowed)', function () { + })); + specify('while self-iterating (allowed)', sameWithUndefinedBehavor(function () { const iterator = this.iterator.iterate(); try { let count = 0; @@ -492,8 +512,8 @@ describe('integrity checks', function () { } finally { iterator.return(); } - }); - specify('while busy (blocked)', function () { + })); + specify('while busy (blocked)', sameWithUndefinedBehavor(function () { const iterator = this.reader.iterate(); try { whileBusy(this, blocked(() => iterator.return())); @@ -501,11 +521,14 @@ describe('integrity checks', function () { } finally { iterator.return(); } - }); - specify('while closed (allowed)', function () { + })); + specify('while closed (allowed)', sameWithUndefinedBehavor(function () { const iterator = this.reader.iterate(); iterator.return(); whileClosed(this, allowed(() => iterator.return())); - }); + })); }); -}); + + // TODO: nested withUndefinedBehavior blocks + // TODO: throwing inside withUndefinedBehavior blocks +}); \ No newline at end of file