Run tests with null assertions enabled

This commit is contained in:
Simon Binder 2020-11-22 17:40:53 +01:00
parent ca41c38272
commit 51e62d4e2f
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
20 changed files with 110 additions and 94 deletions

View File

@ -29,7 +29,7 @@ jobs:
# build, test and upload coverage # build, test and upload coverage
- run: dart run build_runner build --delete-conflicting-outputs - run: dart run build_runner build --delete-conflicting-outputs
working-directory: moor working-directory: moor
- run: dart --no-sound-null-safety test #-x background_isolate --coverage=coverage - run: dart --no-sound-null-safety --null-assertions test #-x background_isolate --coverage=coverage
working-directory: moor working-directory: moor
# - uses: actions/upload-artifact@v2 # - uses: actions/upload-artifact@v2
# with: # with:

View File

@ -1,5 +1,7 @@
## unreleased ## unreleased (breaking - 4.0)
- __Breaking__: Removed the second type parameter from `TypedResult.read`
- Support null safety
- Changed the sql representation of text types from `VARCHAR` to `TEXT` - Changed the sql representation of text types from `VARCHAR` to `TEXT`
- Added extensions for `isNull` and `isNotNull` - Added extensions for `isNull` and `isNotNull`
- Support creating a `VmDatabase` from a raw sqlite3 `Database` via `VmDatabase.opened` - Support creating a `VmDatabase` from a raw sqlite3 `Database` via `VmDatabase.opened`

View File

@ -143,7 +143,7 @@ class TypedResult {
/// as a column, for instance via [JoinedSelectStatement.addColumns]. /// as a column, for instance via [JoinedSelectStatement.addColumns].
/// ///
/// To access the underlying columns directly, use [rawData]. /// To access the underlying columns directly, use [rawData].
D? read<D, T extends SqlType<D>>(Expression<D> expr) { D? read<D>(Expression<D> expr) {
if (_parsedExpressions != null) { if (_parsedExpressions != null) {
return _parsedExpressions![expr] as D; return _parsedExpressions![expr] as D;
} }

View File

@ -1,14 +1,13 @@
//@dart=2.9
import 'package:moor/moor.dart'; import 'package:moor/moor.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
final nullable = GeneratedDateTimeColumn('name', null, true); final nullable = GeneratedDateTimeColumn('name', 'table', true);
final nonNull = GeneratedDateTimeColumn('name', null, false); final nonNull = GeneratedDateTimeColumn('name', 'table', false);
test('should write column definition', () { test('should write column definition', () {
final nonNullQuery = GenerationContext(null, null); final nonNullQuery = GenerationContext.fromDb(null);
final nullableQuery = GenerationContext(null, null); final nullableQuery = GenerationContext.fromDb(null);
nonNull.writeColumnDefinition(nonNullQuery); nonNull.writeColumnDefinition(nonNullQuery);
nullable.writeColumnDefinition(nullableQuery); nullable.writeColumnDefinition(nullableQuery);
@ -17,7 +16,7 @@ void main() {
}); });
test('can compare', () { test('can compare', () {
final ctx = GenerationContext(null, null); final ctx = GenerationContext.fromDb(null);
nonNull.isSmallerThan(currentDateAndTime).writeInto(ctx); nonNull.isSmallerThan(currentDateAndTime).writeInto(ctx);
expect(ctx.sql, "name < strftime('%s', CURRENT_TIMESTAMP)"); expect(ctx.sql, "name < strftime('%s', CURRENT_TIMESTAMP)");

View File

@ -14,7 +14,7 @@ void main() {
hasAutoIncrement: true, hasAutoIncrement: true,
); );
final context = GenerationContext.fromDb(TodoDb(null)); final context = GenerationContext.fromDb(TodoDb());
column.writeColumnDefinition(context); column.writeColumnDefinition(context);
expect( expect(
@ -30,7 +30,7 @@ void main() {
hasAutoIncrement: false, hasAutoIncrement: false,
); );
final context = GenerationContext.fromDb(TodoDb(null)); final context = GenerationContext.fromDb(TodoDb());
column.writeColumnDefinition(context); column.writeColumnDefinition(context);
expect(context.sql, equals('foo INTEGER NOT NULL PRIMARY KEY')); expect(context.sql, equals('foo INTEGER NOT NULL PRIMARY KEY'));

View File

@ -2,6 +2,8 @@ import 'package:moor/moor.dart';
// ignore: import_of_legacy_library_into_null_safe // ignore: import_of_legacy_library_into_null_safe
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import '../utils/null_executor.dart';
part 'todos.g.dart'; part 'todos.g.dart';
mixin AutoIncrement on Table { mixin AutoIncrement on Table {
@ -112,7 +114,7 @@ class CustomConverter extends TypeConverter<MyCustomObject, String> {
}, },
) )
class TodoDb extends _$TodoDb { class TodoDb extends _$TodoDb {
TodoDb(QueryExecutor e) : super(e) { TodoDb([QueryExecutor? e]) : super(e ?? const NullExecutor()) {
moorRuntimeOptions.dontWarnAboutMultipleDatabases = true; moorRuntimeOptions.dontWarnAboutMultipleDatabases = true;
} }
TodoDb.connect(DatabaseConnection connection) : super.connect(connection) { TodoDb.connect(DatabaseConnection connection) : super.connect(connection) {

View File

@ -87,7 +87,7 @@ class MockTransactionExecutor extends MockTransactionsInternal {
when(runInsert(any, any)).thenAnswer((_) => Future.value(0)); when(runInsert(any, any)).thenAnswer((_) => Future.value(0));
when(runCustom(any, any)).thenAnswer((_) => Future.value()); when(runCustom(any, any)).thenAnswer((_) => Future.value());
when(runBatched(any)).thenAnswer((_) => Future.value()); when(runBatched(any)).thenAnswer((_) => Future.value());
when(ensureOpen(any)).thenAnswer((_) => Future.value()); when(ensureOpen(any)).thenAnswer((_) => Future.value(true));
when(send()).thenAnswer((_) => Future.value(null)); when(send()).thenAnswer((_) => Future.value(null));
when(rollback()).thenAnswer((_) => Future.value(null)); when(rollback()).thenAnswer((_) => Future.value(null));

View File

@ -0,0 +1,51 @@
import 'package:moor/backends.dart';
class NullExecutor implements QueryExecutor {
const NullExecutor();
@override
TransactionExecutor beginTransaction() {
throw UnsupportedError('beginTransaction');
}
@override
Future<bool> ensureOpen(QueryExecutorUser user) {
throw UnsupportedError('ensureOpen');
}
@override
Future<void> runBatched(BatchedStatements statements) {
throw UnsupportedError('runBatched');
}
@override
Future<void> runCustom(String statement, [List? args]) {
throw UnsupportedError('runCustom');
}
@override
Future<int> runDelete(String statement, List args) {
throw UnsupportedError('runDelete');
}
@override
Future<int> runInsert(String statement, List args) {
throw UnsupportedError('runInsert');
}
@override
Future<List<Map<String, dynamic>>> runSelect(String statement, List args) {
throw UnsupportedError('runSelect');
}
@override
Future<int> runUpdate(String statement, List args) {
throw UnsupportedError('runUpdate');
}
@override
Future<void> close() => Future.value();
@override
SqlDialect get dialect => SqlDialect.sqlite;
}

View File

@ -20,13 +20,9 @@ void main() {
}); });
test('can deserialize ints as doubles', () { test('can deserialize ints as doubles', () {
final entry = TableWithoutPKData.fromJson({ const serializer = ValueSerializer.defaults();
'notReallyAnId': 3,
'someFloat': 4,
});
expect(entry, expect(serializer.fromJson<double>(3), 3.0);
TableWithoutPKData(notReallyAnId: 3, someFloat: 4, custom: null));
}); });
test('default serializer can be overridden globally', () { test('default serializer can be overridden globally', () {

View File

@ -43,19 +43,20 @@ void main() {
final db = DelegatedDatabase(delegate, isSequential: sequential); final db = DelegatedDatabase(delegate, isSequential: sequential);
await db.ensureOpen(_FakeExecutorUser()); await db.ensureOpen(_FakeExecutorUser());
expect(await db.runSelect(null, null), isEmpty); expect(await db.runSelect('select', const []), isEmpty);
expect(await db.runUpdate(null, null), 3); expect(await db.runUpdate('update', const []), 3);
expect(await db.runInsert(null, null), 4); expect(await db.runInsert('insert', const []), 4);
await db.runCustom(null); await db.runCustom('custom');
await db.runBatched(null); final batched = BatchedStatements([], []);
await db.runBatched(batched);
verifyInOrder([ verifyInOrder([
delegate.isOpen, delegate.isOpen,
delegate.runSelect(null, null), delegate.runSelect('select', const []),
delegate.runUpdate(null, null), delegate.runUpdate('update', const []),
delegate.runInsert(null, null), delegate.runInsert('insert', const []),
delegate.runCustom(null, []), delegate.runCustom('custom', const []),
delegate.runBatched(null), delegate.runBatched(batched),
]); ]);
}); });
} }
@ -150,12 +151,12 @@ void main() {
final transaction = db.beginTransaction(); final transaction = db.beginTransaction();
await transaction.ensureOpen(_FakeExecutorUser()); await transaction.ensureOpen(_FakeExecutorUser());
await transaction.runSelect(null, null); await transaction.runSelect('SELECT 1;', const []);
await transaction.send(); await transaction.send();
verifyInOrder([ verifyInOrder([
delegate.runCustom('BEGIN TRANSACTION', []), delegate.runCustom('BEGIN TRANSACTION', []),
delegate.runSelect(null, null), delegate.runSelect('SELECT 1;', const []),
delegate.runCustom('COMMIT TRANSACTION', []), delegate.runCustom('COMMIT TRANSACTION', []),
]); ]);
}); });

View File

@ -1,4 +1,3 @@
//@dart=2.9
import 'package:moor/moor.dart'; import 'package:moor/moor.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@ -6,8 +5,8 @@ import '../data/tables/todos.dart';
import '../data/utils/expect_equality.dart'; import '../data/utils/expect_equality.dart';
void main() { void main() {
final expression = GeneratedIntColumn('col', null, false); final expression = GeneratedIntColumn('col', 'table', false);
final db = TodoDb(null); final db = TodoDb();
final comparisons = { final comparisons = {
expression.isSmallerThan: '<', expression.isSmallerThan: '<',
@ -24,7 +23,7 @@ void main() {
}; };
group('can compare with other expressions', () { group('can compare with other expressions', () {
final compare = GeneratedIntColumn('compare', null, false); final compare = GeneratedIntColumn('compare', 'table', false);
comparisons.forEach((fn, value) { comparisons.forEach((fn, value) {
test('for operator $value', () { test('for operator $value', () {
@ -54,8 +53,8 @@ void main() {
group('between', () { group('between', () {
test('other expressions', () { test('other expressions', () {
final low = GeneratedIntColumn('low', null, false); final low = GeneratedIntColumn('low', 'table', false);
final high = GeneratedIntColumn('high', null, false); final high = GeneratedIntColumn('high', 'table', false);
final ctx = GenerationContext.fromDb(db); final ctx = GenerationContext.fromDb(db);
expression.isBetween(low, high).writeInto(ctx); expression.isBetween(low, high).writeInto(ctx);

View File

@ -32,7 +32,7 @@ void main() {
} }
void testStringMapping(String dart, String expectedLiteral) { void testStringMapping(String dart, String expectedLiteral) {
final ctx = GenerationContext.fromDb(TodoDb(null)); final ctx = GenerationContext.fromDb(TodoDb());
final constant = Constant(dart); final constant = Constant(dart);
constant.writeInto(ctx); constant.writeInto(ctx);

View File

@ -8,7 +8,7 @@ import '../data/utils/expect_generated.dart';
typedef _Extractor = Expression<int> Function(Expression<DateTime> d); typedef _Extractor = Expression<int> Function(Expression<DateTime> d);
void main() { void main() {
final column = GeneratedDateTimeColumn('val', null, false); final column = GeneratedDateTimeColumn('val', 'table', false);
group('extracting information via top-level method', () { group('extracting information via top-level method', () {
final expectedResults = <_Extractor, String>{ final expectedResults = <_Extractor, String>{

View File

@ -7,10 +7,10 @@ import '../data/utils/expect_generated.dart';
void main() { void main() {
test('in expressions are generated', () { test('in expressions are generated', () {
final innerExpression = GeneratedTextColumn('name', null, true); final innerExpression = GeneratedTextColumn('name', 'table', true);
final isInExpression = innerExpression.isIn(['Max', 'Tobias']); final isInExpression = innerExpression.isIn(['Max', 'Tobias']);
final context = GenerationContext.fromDb(TodoDb(null)); final context = GenerationContext.fromDb(TodoDb());
isInExpression.writeInto(context); isInExpression.writeInto(context);
expect(context.sql, 'name IN (?, ?)'); expect(context.sql, 'name IN (?, ?)');
@ -18,7 +18,7 @@ void main() {
}); });
test('not in expressions are generated', () { test('not in expressions are generated', () {
final innerExpression = GeneratedTextColumn('name', null, true); final innerExpression = GeneratedTextColumn('name', 'table', true);
final isNotIn = innerExpression.isNotIn(['Max', 'Tobias']); final isNotIn = innerExpression.isNotIn(['Max', 'Tobias']);
expect(isNotIn, generates('name NOT IN (?, ?)', ['Max', 'Tobias'])); expect(isNotIn, generates('name NOT IN (?, ?)', ['Max', 'Tobias']));

View File

@ -1,4 +1,3 @@
//@dart=2.9
import 'package:moor/moor.dart'; import 'package:moor/moor.dart';
import 'package:moor/moor.dart' as moor; import 'package:moor/moor.dart' as moor;
import 'package:test/test.dart'; import 'package:test/test.dart';
@ -7,7 +6,7 @@ import '../data/utils/expect_equality.dart';
import '../data/utils/expect_generated.dart'; import '../data/utils/expect_generated.dart';
void main() { void main() {
final innerExpression = GeneratedTextColumn('name', null, true); final innerExpression = GeneratedTextColumn('name', 'table', true);
test('IS NULL expressions are generated', () { test('IS NULL expressions are generated', () {
final oldFunction = moor.isNull(innerExpression); final oldFunction = moor.isNull(innerExpression);
@ -30,7 +29,7 @@ void main() {
}); });
test('generates COALESCE expressions', () { test('generates COALESCE expressions', () {
final expr = moor.coalesce([const Constant<int>(null), const Constant(3)]); final expr = moor.coalesce([const Constant<int?>(null), const Constant(3)]);
expect(expr, generates('COALESCE(NULL, 3)')); expect(expr, generates('COALESCE(NULL, 3)'));
}); });

View File

@ -1,4 +1,3 @@
//@dart=2.9
import 'package:moor/moor.dart'; import 'package:moor/moor.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@ -6,8 +5,8 @@ import '../data/tables/todos.dart';
import '../data/utils/expect_generated.dart'; import '../data/utils/expect_generated.dart';
void main() { void main() {
final expression = GeneratedTextColumn('col', null, false); final expression = GeneratedTextColumn('col', 'table', false);
final db = TodoDb(null); final db = TodoDb();
test('generates like expressions', () { test('generates like expressions', () {
final ctx = GenerationContext.fromDb(db); final ctx = GenerationContext.fromDb(db);

View File

@ -8,7 +8,7 @@ void main() {
test('maps the variable to sql', () { test('maps the variable to sql', () {
final variable = final variable =
Variable(DateTime.fromMillisecondsSinceEpoch(1551297563000)); Variable(DateTime.fromMillisecondsSinceEpoch(1551297563000));
final ctx = GenerationContext.fromDb(TodoDb(null)); final ctx = GenerationContext.fromDb(TodoDb());
variable.writeInto(ctx); variable.writeInto(ctx);
@ -17,8 +17,8 @@ void main() {
}); });
test('writes null directly for null values', () { test('writes null directly for null values', () {
final variable = Variable.withString(null); const variable = Variable<String>(null);
final ctx = GenerationContext.fromDb(TodoDb(null)); final ctx = GenerationContext.fromDb(TodoDb());
variable.writeInto(ctx); variable.writeInto(ctx);

View File

@ -1,4 +1,3 @@
//@dart=2.9
@TestOn('vm') @TestOn('vm')
import 'package:moor/moor.dart'; import 'package:moor/moor.dart';
import 'package:moor/extensions/moor_ffi.dart'; import 'package:moor/extensions/moor_ffi.dart';
@ -9,8 +8,8 @@ import '../data/tables/todos.dart';
import '../data/utils/expect_generated.dart'; import '../data/utils/expect_generated.dart';
void main() { void main() {
final a = GeneratedRealColumn('a', null, false); final a = GeneratedRealColumn('a', 'table', false);
final b = GeneratedRealColumn('b', null, false); final b = GeneratedRealColumn('b', 'table', false);
test('pow', () { test('pow', () {
expect(sqlPow(a, b), generates('pow(a, b)')); expect(sqlPow(a, b), generates('pow(a, b)'));
@ -25,7 +24,7 @@ void main() {
test('atan', () => expect(sqlAtan(a), generates('atan(a)'))); test('atan', () => expect(sqlAtan(a), generates('atan(a)')));
test('containsCase', () { test('containsCase', () {
final c = GeneratedTextColumn('a', null, false); final c = GeneratedTextColumn('a', 'table', false);
expect(c.containsCase('foo'), generates('moor_contains(a, ?, 0)', ['foo'])); expect(c.containsCase('foo'), generates('moor_contains(a, ?, 0)', ['foo']));
expect( expect(
@ -39,11 +38,11 @@ void main() {
// insert exactly one row so that we can evaluate expressions from Dart // insert exactly one row so that we can evaluate expressions from Dart
await db.into(db.pureDefaults).insert(PureDefaultsCompanion.insert()); await db.into(db.pureDefaults).insert(PureDefaultsCompanion.insert());
Future<bool> evaluate(Expression<bool> expr) async { Future<bool> evaluate(Expression<bool?> expr) async {
final result = await (db.selectOnly(db.pureDefaults)..addColumns([expr])) final result = await (db.selectOnly(db.pureDefaults)..addColumns([expr]))
.getSingle(); .getSingle();
return result.read(expr); return result!.read<bool?>(expr)!;
} }
expect( expect(
@ -59,7 +58,7 @@ void main() {
}); });
group('regexp flags', () { group('regexp flags', () {
TodoDb db; late TodoDb db;
setUp(() async { setUp(() async {
db = TodoDb(VmDatabase.memory()); db = TodoDb(VmDatabase.memory());
@ -69,11 +68,11 @@ void main() {
tearDown(() => db.close()); tearDown(() => db.close());
Future<bool> evaluate(Expression<bool> expr) async { Future<bool> evaluate(Expression<bool?> expr) async {
final result = await (db.selectOnly(db.pureDefaults)..addColumns([expr])) final result = await (db.selectOnly(db.pureDefaults)..addColumns([expr]))
.getSingle(); .getSingle();
return result.read(expr); return result!.read<bool?>(expr)!;
} }
test('multiLine', () { test('multiLine', () {

View File

@ -1,32 +0,0 @@
// Tests that wouldn't compile with null safety enabled.
//@dart=2.9
import 'package:moor/moor.dart';
import 'package:test/test.dart';
import 'data/tables/todos.dart';
import 'data/utils/mocks.dart';
void main() {
TodoDb db;
MockExecutor executor;
MockStreamQueries streamQueries;
setUp(() {
executor = MockExecutor();
streamQueries = MockStreamQueries();
final connection = createConnection(executor, streamQueries);
db = TodoDb.connect(connection);
});
test("doesn't allow writing null rows", () {
expect(
() {
return db.into(db.todosTable).insert(null);
},
throwsA(const TypeMatcher<InvalidDataException>().having(
(e) => e.message, 'message', contains('Cannot write null row'))),
);
});
}

View File

@ -27,7 +27,8 @@ void main() {
clearInteractions(inner); clearInteractions(inner);
lazy.beginTransaction(); lazy.beginTransaction();
await lazy.runBatched(null); final batched = BatchedStatements([], []);
await lazy.runBatched(batched);
await lazy.runCustom('custom_stmt'); await lazy.runCustom('custom_stmt');
await lazy.runDelete('delete_stmt', [1]); await lazy.runDelete('delete_stmt', [1]);
await lazy.runInsert('insert_stmt', [2]); await lazy.runInsert('insert_stmt', [2]);
@ -35,7 +36,7 @@ void main() {
await lazy.runUpdate('update_stmt', [4]); await lazy.runUpdate('update_stmt', [4]);
verifyInOrder([ verifyInOrder([
inner.runBatched(null), inner.runBatched(batched),
inner.runCustom('custom_stmt'), inner.runCustom('custom_stmt'),
inner.runDelete('delete_stmt', [1]), inner.runDelete('delete_stmt', [1]),
inner.runInsert('insert_stmt', [2]), inner.runInsert('insert_stmt', [2]),