diff --git a/CHANGELOG.md b/CHANGELOG.md index fa59c99..e3ca0aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## Next + +- Add latest changes from master + ## 0.7.0-alpha.3 - Add latest changes from master diff --git a/lib/sqlite3_common.dart b/lib/sqlite3_common.dart index 4c1ddc0..eae3c6f 100644 --- a/lib/sqlite3_common.dart +++ b/lib/sqlite3_common.dart @@ -1,2 +1,2 @@ -// Exports common Sqlite3 exports which are available on web and ffi environments +// Exports common Sqlite3 exports which are available in different environments. export 'package:sqlite3/common.dart'; diff --git a/test/native/basic_test.dart b/test/native/basic_test.dart index c1fa4aa..263ab39 100644 --- a/test/native/basic_test.dart +++ b/test/native/basic_test.dart @@ -305,10 +305,11 @@ void main() { // 4. Now second connection is ready. Second query has two connections to choose from. // 5. However, first connection is closed, so it's removed from the pool. // 6. Triggers `Concurrent modification during iteration: Instance(length:1) of '_GrowableList'` - final db = await testUtils.setupDatabase(path: path, initStatements: [ + final db = SqliteDatabase.withFactory( + await testUtils.testFactory(path: path, initStatements: [ // Second connection to sleep more than first connection 'SELECT test_sleep(test_connection_number() * 10)' - ]); + ])); await db.initialize(); final future1 = db.get('SELECT test_sleep(10) as sleep'); diff --git a/test/utils/abstract_test_utils.dart b/test/utils/abstract_test_utils.dart index 65cf132..f1ec6ea 100644 --- a/test/utils/abstract_test_utils.dart +++ b/test/utils/abstract_test_utils.dart @@ -22,9 +22,12 @@ abstract class AbstractTestUtils { Future testFactory( {String? path, String sqlitePath = '', + List initStatements = const [], SqliteOptions options = const SqliteOptions.defaults()}) async { return TestDefaultSqliteOpenFactory( - path: path ?? dbPath(), sqliteOptions: options); + path: path ?? dbPath(), + sqliteOptions: options, + ); } /// Creates a SqliteDatabaseConnection diff --git a/test/utils/web_test_utils.dart b/test/utils/web_test_utils.dart index 6efb044..6a17a98 100644 --- a/test/utils/web_test_utils.dart +++ b/test/utils/web_test_utils.dart @@ -40,9 +40,11 @@ class TestUtils extends AbstractTestUtils { Future testFactory( {String? path, String? sqlitePath, + List initStatements = const [], SqliteOptions options = const SqliteOptions.defaults()}) async { await _isInitialized; - return super.testFactory(path: path, options: webOptions); + return super.testFactory( + path: path, options: webOptions, initStatements: initStatements); } @override diff --git a/test/watch_shared_test.dart b/test/watch_shared_test.dart deleted file mode 100644 index ae3e468..0000000 --- a/test/watch_shared_test.dart +++ /dev/null @@ -1,260 +0,0 @@ -@TestOn('!browser') -// TODO watched query tests on web -// require a forked version of Drift's worker -// The forked version is not published yet. -import 'dart:async'; -import 'dart:math'; -import 'package:sqlite_async/sqlite_async.dart'; -import 'package:sqlite_async/src/utils/shared_utils.dart'; -import 'package:test/test.dart'; - -import 'utils/test_utils_impl.dart'; - -final testUtils = TestUtils(); - -createTables(SqliteDatabase db) async { - await db.writeTransaction((tx) async { - await tx.execute( - 'CREATE TABLE assets(id INTEGER PRIMARY KEY AUTOINCREMENT, make TEXT, customer_id INTEGER)'); - await tx.execute('CREATE INDEX assets_customer ON assets(customer_id)'); - await tx.execute( - 'CREATE TABLE customers(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)'); - await tx.execute( - 'CREATE TABLE other_customers(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)'); - await tx.execute('CREATE VIEW assets_alias AS SELECT * FROM assets'); - }); -} - -void main() { - group('Query Watch Tests', () { - late String path; - List sqlitePaths = []; - - setUp(() async { - path = testUtils.dbPath(); - sqlitePaths = testUtils.findSqliteLibraries(); - await testUtils.cleanDb(path: path); - }); - - for (var sqlite in sqlitePaths) { - test('getSourceTables - $sqlite', () async { - final db = SqliteDatabase.withFactory( - await testUtils.testFactory(path: path, sqlitePath: sqlite)); - await db.initialize(); - await createTables(db); - - var versionRow = await db.get('SELECT sqlite_version() as version'); - print('Testing SQLite ${versionRow['version']} - $sqlite'); - - final tables = await getSourceTables(db, - 'SELECT * FROM assets INNER JOIN customers ON assets.customer_id = customers.id'); - expect(tables, equals({'assets', 'customers'})); - - final tables2 = await getSourceTables(db, - 'SELECT count() FROM assets INNER JOIN "other_customers" AS oc ON assets.customer_id = oc.id AND assets.make = oc.name'); - expect(tables2, equals({'assets', 'other_customers'})); - - final tables3 = await getSourceTables(db, 'SELECT count() FROM assets'); - expect(tables3, equals({'assets'})); - - final tables4 = - await getSourceTables(db, 'SELECT count() FROM assets_alias'); - expect(tables4, equals({'assets'})); - - final tables5 = - await getSourceTables(db, 'SELECT sqlite_version() as version'); - expect(tables5, equals({})); - }); - } - - test('watch', () async { - final db = await testUtils.setupDatabase(path: path); - await createTables(db); - - const baseTime = 20; - - const throttleDuration = Duration(milliseconds: baseTime); - - final stream = db.watch( - 'SELECT count() AS count FROM assets INNER JOIN customers ON customers.id = assets.customer_id', - throttle: throttleDuration); - - final rows = await db.execute( - 'INSERT INTO customers(name) VALUES (?) RETURNING id', - ['a customer']); - final id = rows[0]['id']; - - var done = false; - inserts() async { - while (!done) { - await db.execute( - 'INSERT INTO assets(make, customer_id) VALUES (?, ?)', - ['test', id]); - await Future.delayed( - Duration(milliseconds: Random().nextInt(baseTime * 2))); - } - } - - const numberOfQueries = 10; - - inserts(); - try { - List times = []; - final results = await stream.take(numberOfQueries).map((e) { - times.add(DateTime.now()); - return e; - }).toList(); - - var lastCount = 0; - for (var r in results) { - final count = r.first['count']; - // This is not strictly incrementing, since we can't guarantee the - // exact order between reads and writes. - // We can guarantee that there will always be a read after the last write, - // but the previous read may have been after the same write in some cases. - expect(count, greaterThanOrEqualTo(lastCount)); - lastCount = count; - } - - // The number of read queries must not be greater than the number of writes overall. - expect(numberOfQueries, lessThanOrEqualTo(results.last.first['count'])); - - DateTime? lastTime; - for (var r in times) { - if (lastTime != null) { - var diff = r.difference(lastTime); - expect(diff, greaterThanOrEqualTo(throttleDuration)); - } - lastTime = r; - } - } finally { - done = true; - } - }); - - test('onChange', () async { - final db = await testUtils.setupDatabase(path: path); - await createTables(db); - - const baseTime = 20; - - const throttleDuration = Duration(milliseconds: baseTime); - - var done = false; - inserts() async { - while (!done) { - await db.execute('INSERT INTO assets(make) VALUES (?)', ['test']); - await Future.delayed( - Duration(milliseconds: Random().nextInt(baseTime))); - } - } - - inserts(); - - final stream = db.onChange({'assets', 'customers'}, - throttle: throttleDuration).asyncMap((event) async { - // This is where queries would typically be executed - return event; - }); - - var events = await stream.take(3).toList(); - done = true; - - expect( - events, - equals([ - UpdateNotification.empty(), - UpdateNotification.single('assets'), - UpdateNotification.single('assets') - ])); - }); - - test('single onChange', () async { - final db = await testUtils.setupDatabase(path: path); - await createTables(db); - - const baseTime = 20; - - const throttleDuration = Duration(milliseconds: baseTime); - - final stream = db.onChange({'assets', 'customers'}, - throttle: throttleDuration, - triggerImmediately: false).asyncMap((event) async { - // This is where queries would typically be executed - return event; - }); - - var eventsFuture = stream.take(1).toList(); - await db.execute('INSERT INTO assets(make) VALUES (?)', ['test']); - var events = await eventsFuture; - - expect(events, equals([UpdateNotification.single('assets')])); - }); - - test('watch with parameters', () async { - final db = await testUtils.setupDatabase(path: path); - await createTables(db); - - const baseTime = 20; - - const throttleDuration = Duration(milliseconds: baseTime); - - final rows = await db.execute( - 'INSERT INTO customers(name) VALUES (?) RETURNING id', - ['a customer']); - final id = rows[0]['id']; - - final stream = db.watch( - 'SELECT count() AS count FROM assets WHERE customer_id = ?', - parameters: [id], - throttle: throttleDuration); - - var done = false; - inserts() async { - while (!done) { - await db.execute( - 'INSERT INTO assets(make, customer_id) VALUES (?, ?)', - ['test', id]); - await Future.delayed( - Duration(milliseconds: Random().nextInt(baseTime * 2))); - } - } - - const numberOfQueries = 10; - - inserts(); - try { - List times = []; - final results = await stream.take(numberOfQueries).map((e) { - times.add(DateTime.now()); - return e; - }).toList(); - - var lastCount = 0; - for (var r in results) { - final count = r.first['count']; - // This is not strictly incrementing, since we can't guarantee the - // exact order between reads and writes. - // We can guarantee that there will always be a read after the last write, - // but the previous read may have been after the same write in some cases. - expect(count, greaterThanOrEqualTo(lastCount)); - lastCount = count; - } - - // The number of read queries must not be greater than the number of writes overall. - expect(numberOfQueries, lessThanOrEqualTo(results.last.first['count'])); - - DateTime? lastTime; - for (var r in times) { - if (lastTime != null) { - var diff = r.difference(lastTime); - expect(diff, greaterThanOrEqualTo(throttleDuration)); - } - lastTime = r; - } - } finally { - done = true; - } - }); - }); -}