Add more tests in moor_ffi subproject

This commit is contained in:
Simon Binder 2019-09-22 16:26:17 +02:00
parent 0d56594933
commit b99bc820da
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
9 changed files with 125 additions and 14 deletions

View File

@ -96,7 +96,9 @@ dev_dependencies:
In the file where you created a `FlutterQueryExecutor`, replace the `moor_flutter` import In the file where you created a `FlutterQueryExecutor`, replace the `moor_flutter` import
with both `package:moor/moor.dart` and `package:moor_ffi/moor_ffi.dart`. with both `package:moor/moor.dart` and `package:moor_ffi/moor_ffi.dart`.
In all other project files that use moor apis (e.g. a `Value` class for companions), just import `package:moor/moor.dart`. In all other project files that use moor apis (e.g. a `Value` class for companions), just import `package:moor/moor.dart`.
Finally, replace usages of `FlutterQueryExecutor` with `VmDatabase`. Finally, replace usages of `FlutterQueryExecutor` with `VmDatabase`.
Note that, at the moment, there is no counterpart for `FlutterQueryExecutor.inDatabasePath` and that the async API using
a background isolate is not available yet. Both shortcomings with be fixed by the upcoming moor 2.0 release.

View File

@ -44,6 +44,7 @@ class Database implements BaseDatabase {
if (resultCode == Errors.SQLITE_OK) { if (resultCode == Errors.SQLITE_OK) {
return Database._(dbPointer); return Database._(dbPointer);
} else { } else {
bindings.sqlite3_close_v2(dbPointer);
throw SqliteException._fromErrorCode(dbPointer, resultCode); throw SqliteException._fromErrorCode(dbPointer, resultCode);
} }
} }
@ -56,6 +57,12 @@ class Database implements BaseDatabase {
@override @override
void close() { void close() {
// close all prepared statements first
_isClosed = true;
for (var stmt in _preparedStmt) {
stmt.close();
}
final code = bindings.sqlite3_close_v2(_db); final code = bindings.sqlite3_close_v2(_db);
SqliteException exception; SqliteException exception;
if (code != Errors.SQLITE_OK) { if (code != Errors.SQLITE_OK) {
@ -63,10 +70,7 @@ class Database implements BaseDatabase {
} }
_isClosed = true; _isClosed = true;
for (var stmt in _preparedStmt) { // we don't need to deallocate the _db pointer, sqlite takes care of that
stmt.close();
}
_db.free();
if (exception != null) { if (exception != null) {
throw exception; throw exception;
@ -82,6 +86,7 @@ class Database implements BaseDatabase {
@override @override
void execute(String sql) { void execute(String sql) {
_ensureOpen(); _ensureOpen();
final sqlPtr = CBlob.allocateString(sql); final sqlPtr = CBlob.allocateString(sql);
final errorOut = Pointer<Pointer<CBlob>>.allocate(); final errorOut = Pointer<Pointer<CBlob>>.allocate();
@ -125,7 +130,10 @@ class Database implements BaseDatabase {
throw SqliteException._fromErrorCode(_db, resultCode); throw SqliteException._fromErrorCode(_db, resultCode);
} }
return PreparedStatement._(stmt, this); final prepared = PreparedStatement._(stmt, this);
_preparedStmt.add(prepared);
return prepared;
} }
@override @override

View File

@ -48,6 +48,8 @@ class DbOperationProxy {
final SendPort send; final SendPort send;
final Isolate isolate; final Isolate isolate;
var closed = false;
int _currentRequestId = 0; int _currentRequestId = 0;
DbOperationProxy( DbOperationProxy(
@ -57,6 +59,10 @@ class DbOperationProxy {
Future<dynamic> sendRequest(IsolateCommandType type, dynamic data, Future<dynamic> sendRequest(IsolateCommandType type, dynamic data,
{int preparedStmtId}) { {int preparedStmtId}) {
if (closed) {
throw StateError('Tried to call a database method after .close()');
}
final id = _currentRequestId++; final id = _currentRequestId++;
final cmd = IsolateCommand(id, type, data) final cmd = IsolateCommand(id, type, data)
..preparedStatementId = preparedStmtId; ..preparedStatementId = preparedStmtId;
@ -80,6 +86,7 @@ class DbOperationProxy {
} }
void close() { void close() {
closed = true;
_receivePort.close(); _receivePort.close();
backgroundMsgs.close(); backgroundMsgs.close();
isolate.kill(); isolate.kill();
@ -128,9 +135,17 @@ class BackgroundIsolateRunner {
final response = _handleCommand(data); final response = _handleCommand(data);
send.send(IsolateResponse(data.requestId, response, null)); send.send(IsolateResponse(data.requestId, response, null));
} catch (e) { } catch (e) {
if (e is Error) {
// errors contain a StackTrace, which cannot be sent. So we just
// send the description of that stacktrace.
final exception =
Exception('Error in background isolate: $e\n${e.stackTrace}');
send.send(IsolateResponse(data.requestId, null, exception));
} else {
send.send(IsolateResponse(data.requestId, null, e)); send.send(IsolateResponse(data.requestId, null, e));
} }
} }
}
}); });
} }

View File

@ -22,7 +22,7 @@ class PreparedStatement implements BasePreparedStatement {
void _ensureNotFinalized() { void _ensureNotFinalized() {
if (_closed) { if (_closed) {
throw Exception('Tried to operate on a released prepared statement'); throw StateError('Tried to operate on a released prepared statement');
} }
} }

View File

@ -1,16 +1,15 @@
name: moor_ffi name: moor_ffi
description: "Experimental moor implementation that uses dart:ffi" description: "Experimental sqlite bindings using dart:ffi"
version: 0.0.1 version: 0.0.1
author: author: Simon Binder <oss@simonbinder.eu>
homepage: homepage: https://github.com/simolus3/moor/tree/develop/moor_ffi
issue_tracker: https://github.com/simolus3/moor/issues
environment: environment:
sdk: ">=2.5.0-dev <2.6.0" sdk: ">=2.5.0-dev <2.6.0"
dependencies: dependencies:
moor: ">=1.7.0 <2.1.0" moor: ">=1.7.0 <2.1.0"
# flutter:
# sdk: flutter
dev_dependencies: dev_dependencies:
test: ^1.6.0 test: ^1.6.0

View File

@ -0,0 +1,21 @@
import 'package:moor/moor.dart';
import 'package:moor_ffi/src/ffi/blob.dart';
import 'package:test/test.dart';
void main() {
test('utf8 store and load test', () {
final content = 'Hasta Mañana';
final blob = CBlob.allocateString(content);
expect(blob.load<CBlob>().readString(), content);
blob.free();
});
test('blob load and store test', () {
final data = List.generate(256, (x) => x);
final blob = CBlob.allocate(Uint8List.fromList(data));
expect(blob.load<CBlob>().read(256), data);
blob.free();
});
}

View File

@ -5,6 +5,8 @@ import 'package:path/path.dart' as p;
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:moor_ffi/database.dart'; import 'package:moor_ffi/database.dart';
import 'suite/insert.dart' as insert;
import 'suite/prepared_statements.dart' as prepared_statements;
import 'suite/select.dart' as select; import 'suite/select.dart' as select;
import 'suite/user_version.dart' as user_version; import 'suite/user_version.dart' as user_version;
@ -59,6 +61,8 @@ void main() {
} }
void _declareAll(TestedDatabase db) { void _declareAll(TestedDatabase db) {
insert.main(db);
prepared_statements.main(db);
select.main(db); select.main(db);
user_version.main(db); user_version.main(db);
} }

View File

@ -0,0 +1,18 @@
import 'package:test/test.dart';
import '../runners.dart';
void main(TestedDatabase db) {
test('insert statements report their id', () async {
final opened = await db.openMemory();
await opened
.execute('CREATE TABLE tbl(a INTEGER PRIMARY KEY AUTOINCREMENT)');
for (var i = 0; i < 5; i++) {
await opened.execute('INSERT INTO tbl DEFAULT VALUES');
expect(await opened.getLastInsertId(), i + 1);
}
await opened.close();
});
}

View File

@ -0,0 +1,44 @@
import 'package:test/test.dart';
import '../runners.dart';
void main(TestedDatabase db) {
test('prepared statements can be used multiple times', () async {
final opened = await db.openMemory();
await opened.execute('CREATE TABLE tbl (a TEXT);');
final stmt = await opened.prepare('INSERT INTO tbl(a) VALUES(?)');
await stmt.execute(['a']);
await stmt.execute(['b']);
await stmt.close();
final select = await opened.prepare('SELECT * FROM tbl ORDER BY a');
final result = await select.select();
expect(result, hasLength(2));
expect(result.map((row) => row['a']), ['a', 'b']);
await select.close();
await opened.close();
});
test('prepared statements cannot be used after close', () async {
final opened = await db.openMemory();
final stmt = await opened.prepare('SELECT ?');
await stmt.close();
expect(stmt.select, throwsA(anything));
await opened.close();
});
test('prepared statements cannot be used after db is closed', () async {
final opened = await db.openMemory();
final stmt = await opened.prepare('SELECT 1');
await opened.close();
expect(stmt.select, throwsA(anything));
});
}