|
1 | 1 | import 'dart:async'; |
| 2 | +import 'dart:isolate'; |
2 | 3 | import 'dart:math'; |
3 | 4 |
|
| 5 | +import 'package:async/async.dart'; |
| 6 | +import 'package:sqlite3/sqlite3.dart'; |
4 | 7 | import 'package:sqlite_async/sqlite_async.dart'; |
5 | 8 | import 'package:sqlite_async/src/database_utils.dart'; |
| 9 | +import 'package:sqlite_async/src/isolate_connection_factory.dart'; |
6 | 10 | import 'package:test/test.dart'; |
7 | 11 |
|
8 | 12 | import 'util.dart'; |
@@ -163,5 +167,84 @@ void main() { |
163 | 167 | UpdateNotification.single('assets') |
164 | 168 | ])); |
165 | 169 | }); |
| 170 | + |
| 171 | + test('watch in isolate', () async { |
| 172 | + final db = await setupDatabase(path: path); |
| 173 | + await createTables(db); |
| 174 | + |
| 175 | + const baseTime = 20; |
| 176 | + |
| 177 | + const throttleDuration = Duration(milliseconds: baseTime); |
| 178 | + |
| 179 | + final rows = await db.execute( |
| 180 | + 'INSERT INTO customers(name) VALUES (?) RETURNING id', |
| 181 | + ['a customer']); |
| 182 | + final id = rows[0]['id']; |
| 183 | + |
| 184 | + var done = false; |
| 185 | + inserts() async { |
| 186 | + while (!done) { |
| 187 | + await db.execute( |
| 188 | + 'INSERT INTO assets(make, customer_id) VALUES (?, ?)', |
| 189 | + ['test', id]); |
| 190 | + await Future.delayed( |
| 191 | + Duration(milliseconds: Random().nextInt(baseTime * 2))); |
| 192 | + } |
| 193 | + } |
| 194 | + |
| 195 | + const numberOfQueries = 10; |
| 196 | + |
| 197 | + inserts(); |
| 198 | + |
| 199 | + final factory = db.isolateConnectionFactory(); |
| 200 | + |
| 201 | + var l = await inIsolateWatch(factory, numberOfQueries, throttleDuration); |
| 202 | + |
| 203 | + var results = l[0] as List<ResultSet>; |
| 204 | + var times = l[1] as List<DateTime>; |
| 205 | + done = true; |
| 206 | + |
| 207 | + var lastCount = 0; |
| 208 | + for (var r in results) { |
| 209 | + final count = r.first['count']; |
| 210 | + // This is not strictly incrementing, since we can't guarantee the |
| 211 | + // exact order between reads and writes. |
| 212 | + // We can guarantee that there will always be a read after the last write, |
| 213 | + // but the previous read may have been after the same write in some cases. |
| 214 | + expect(count, greaterThanOrEqualTo(lastCount)); |
| 215 | + lastCount = count; |
| 216 | + } |
| 217 | + |
| 218 | + // The number of read queries must not be greater than the number of writes overall. |
| 219 | + expect(numberOfQueries, lessThanOrEqualTo(results.last.first['count'])); |
| 220 | + |
| 221 | + DateTime? lastTime; |
| 222 | + for (var r in times) { |
| 223 | + if (lastTime != null) { |
| 224 | + var diff = r.difference(lastTime); |
| 225 | + expect(diff, greaterThanOrEqualTo(throttleDuration)); |
| 226 | + } |
| 227 | + lastTime = r; |
| 228 | + } |
| 229 | + }); |
| 230 | + }); |
| 231 | +} |
| 232 | + |
| 233 | +Future<List<Object>> inIsolateWatch(IsolateConnectionFactory factory, |
| 234 | + int numberOfQueries, Duration throttleDuration) async { |
| 235 | + return await Isolate.run(() async { |
| 236 | + final db = factory.open(); |
| 237 | + |
| 238 | + final stream = db.watch( |
| 239 | + 'SELECT count() AS count FROM assets INNER JOIN customers ON customers.id = assets.customer_id', |
| 240 | + throttle: throttleDuration); |
| 241 | + List<DateTime> times = []; |
| 242 | + final results = await stream.take(numberOfQueries).map((e) { |
| 243 | + times.add(DateTime.now()); |
| 244 | + return e; |
| 245 | + }).toList(); |
| 246 | + |
| 247 | + db.close(); |
| 248 | + return [results, times]; |
166 | 249 | }); |
167 | 250 | } |
0 commit comments