mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-26 12:52:40 +02:00
fix(db): prevent concurrent startup locks
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:spotiflac_android/services/sqlite_helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('SingleFlightInitializer', () {
|
||||
test('coalesces concurrent initialization and caches the result', () async {
|
||||
final initializer = SingleFlightInitializer<int>();
|
||||
final gate = Completer<void>();
|
||||
var calls = 0;
|
||||
|
||||
Future<int> create() async {
|
||||
calls++;
|
||||
await gate.future;
|
||||
return 42;
|
||||
}
|
||||
|
||||
final first = initializer.getOrCreate(create);
|
||||
final second = initializer.getOrCreate(create);
|
||||
|
||||
expect(identical(first, second), isTrue);
|
||||
expect(calls, 1);
|
||||
|
||||
gate.complete();
|
||||
expect(await Future.wait([first, second]), [42, 42]);
|
||||
expect(await initializer.getOrCreate(create), 42);
|
||||
expect(calls, 1);
|
||||
});
|
||||
|
||||
test('allows retry after initialization fails', () async {
|
||||
final initializer = SingleFlightInitializer<int>();
|
||||
var calls = 0;
|
||||
|
||||
Future<int> create() {
|
||||
calls++;
|
||||
if (calls == 1) throw StateError('first attempt failed');
|
||||
return Future<int>.value(7);
|
||||
}
|
||||
|
||||
await expectLater(initializer.getOrCreate(create), throwsStateError);
|
||||
expect(await initializer.getOrCreate(create), 7);
|
||||
expect(calls, 2);
|
||||
});
|
||||
|
||||
test('reset permits a fresh initialization', () async {
|
||||
final initializer = SingleFlightInitializer<int>();
|
||||
var value = 1;
|
||||
|
||||
expect(await initializer.getOrCreate(() async => value), 1);
|
||||
value = 2;
|
||||
expect(await initializer.getOrCreate(() async => value), 1);
|
||||
|
||||
initializer.reset();
|
||||
expect(await initializer.getOrCreate(() async => value), 2);
|
||||
});
|
||||
});
|
||||
|
||||
group('SQLite startup configuration', () {
|
||||
final source = File('lib/services/sqlite_helpers.dart').readAsStringSync();
|
||||
|
||||
test('waits for transient locks before configuring the connection', () {
|
||||
final configureIndex = source.indexOf('onConfigure:');
|
||||
final busyTimeoutIndex = source.indexOf('PRAGMA busy_timeout');
|
||||
final journalModeIndex = source.indexOf('PRAGMA journal_mode');
|
||||
|
||||
expect(configureIndex, greaterThanOrEqualTo(0));
|
||||
expect(busyTimeoutIndex, greaterThan(configureIndex));
|
||||
expect(busyTimeoutIndex, lessThan(journalModeIndex));
|
||||
});
|
||||
|
||||
test('sets incremental auto-vacuum only before app tables exist', () {
|
||||
final schemaCheckIndex = source.indexOf('FROM sqlite_master');
|
||||
final emptySchemaGuardIndex = source.indexOf('if (tables.isEmpty)');
|
||||
final autoVacuumMatches = RegExp(
|
||||
r"PRAGMA auto_vacuum = INCREMENTAL",
|
||||
).allMatches(source).toList();
|
||||
final onCreateIndex = source.indexOf('onCreate:');
|
||||
|
||||
expect(autoVacuumMatches, hasLength(1));
|
||||
expect(schemaCheckIndex, greaterThanOrEqualTo(0));
|
||||
expect(emptySchemaGuardIndex, greaterThan(schemaCheckIndex));
|
||||
expect(
|
||||
autoVacuumMatches.single.start,
|
||||
greaterThan(emptySchemaGuardIndex),
|
||||
);
|
||||
expect(autoVacuumMatches.single.start, lessThan(onCreateIndex));
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user