mirror of https://github.com/AMT-Cheif/drift.git
Add more tests in moor_ffi subproject
This commit is contained in:
parent
0d56594933
commit
b99bc820da
|
@ -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.
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
});
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
});
|
||||||
|
}
|
|
@ -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));
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue