Skip to content

Commit 73d73d8

Browse files
committed
Fix web tests
1 parent ce1be73 commit 73d73d8

File tree

7 files changed

+155
-42
lines changed

7 files changed

+155
-42
lines changed

sqlite3_web/lib/src/client.dart

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ final class RemoteDatabase implements Database {
9393
}
9494

9595
@override
96-
FileSystem get fileSystem => throw UnimplementedError();
96+
late final FileSystem fileSystem = RemoteFileSystem(this);
9797

9898
@override
9999
Future<int> get lastInsertRowId async {
@@ -391,8 +391,8 @@ final class DatabaseClient implements WebSqlite {
391391
}
392392

393393
@override
394-
Future<Database> connect(
395-
String name, StorageMode type, AccessMode access) async {
394+
Future<Database> connect(String name, StorageMode type, AccessMode access,
395+
{bool onlyOpenVfs = false}) async {
396396
await startWorkers();
397397

398398
WorkerConnection connection;
@@ -421,6 +421,7 @@ final class DatabaseClient implements WebSqlite {
421421
wasmUri: wasmUri,
422422
databaseName: name,
423423
storageMode: type.resolveToVfs(shared),
424+
onlyOpenVfs: onlyOpenVfs,
424425
),
425426
MessageType.simpleSuccessResponse,
426427
);
@@ -431,7 +432,8 @@ final class DatabaseClient implements WebSqlite {
431432
}
432433

433434
@override
434-
Future<ConnectToRecommendedResult> connectToRecommended(String name) async {
435+
Future<ConnectToRecommendedResult> connectToRecommended(String name,
436+
{bool onlyOpenVfs = false}) async {
435437
final probed = await runFeatureDetection(databaseName: name);
436438

437439
// If we have an existing database in storage, we want to keep using that
@@ -460,7 +462,8 @@ final class DatabaseClient implements WebSqlite {
460462

461463
final (storage, access) = availableImplementations.firstOrNull ??
462464
(StorageMode.inMemory, AccessMode.inCurrentContext);
463-
final database = await connect(name, storage, access);
465+
final database =
466+
await connect(name, storage, access, onlyOpenVfs: onlyOpenVfs);
464467

465468
return ConnectToRecommendedResult(
466469
database: database,

sqlite3_web/lib/src/database.dart

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,27 @@ abstract class WebSqlite {
162162

163163
/// Connects to a database identified by its [name] stored under [type] and
164164
/// accessed via the given [access] mode.
165-
Future<Database> connect(String name, StorageMode type, AccessMode access);
165+
///
166+
/// When [onlyOpenVfs] is enabled, only the underlying file system for the
167+
/// database is initialized before [connect] returns. By default, the database
168+
/// will also be opened in [connect]. Otherwise, the database will be opened
169+
/// on the worker when it's first used.
170+
/// Only opening the VFS can be used to, for instance, check if the database
171+
/// already exists and to initialize it manually if it doesn't.
172+
Future<Database> connect(String name, StorageMode type, AccessMode access,
173+
{bool onlyOpenVfs = false});
166174

167175
/// Starts a feature detection via [runFeatureDetection] and then [connect]s
168176
/// to the best database available.
169-
Future<ConnectToRecommendedResult> connectToRecommended(String name);
177+
///
178+
/// When [onlyOpenVfs] is enabled, only the underlying file system for the
179+
/// database is initialized before [connect] returns. By default, the database
180+
/// will also be opened in [connect]. Otherwise, the database will be opened
181+
/// on the worker when it's first used.
182+
/// Only opening the VFS can be used to, for instance, check if the database
183+
/// already exists and to initialize it manually if it doesn't.
184+
Future<ConnectToRecommendedResult> connectToRecommended(String name,
185+
{bool onlyOpenVfs = false});
170186

171187
/// Entrypoints for workers hosting datbases.
172188
static void workerEntrypoint({

sqlite3_web/lib/src/protocol.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class _UniqueFieldNames {
5353
static const id = 'i';
5454
static const updateKind = 'k';
5555
static const tableNames = 'n';
56+
static const onlyOpenVfs = 'o';
5657
static const parameters = 'p';
5758
static const storageMode = 's';
5859
static const sql = 's'; // not used in same message
@@ -201,12 +202,14 @@ final class OpenRequest extends Request {
201202

202203
final String databaseName;
203204
final FileSystemImplementation storageMode;
205+
final bool onlyOpenVfs;
204206

205207
OpenRequest({
206208
required super.requestId,
207209
required this.wasmUri,
208210
required this.databaseName,
209211
required this.storageMode,
212+
required this.onlyOpenVfs,
210213
});
211214

212215
factory OpenRequest.deserialize(JSObject object) {
@@ -217,6 +220,9 @@ final class OpenRequest extends Request {
217220
wasmUri:
218221
Uri.parse((object[_UniqueFieldNames.wasmUri] as JSString).toDart),
219222
requestId: object.requestId,
223+
onlyOpenVfs:
224+
// The onlyOpenVfs field was not set in earlier clients.
225+
(object[_UniqueFieldNames.onlyOpenVfs] as JSBoolean?)?.toDart == true,
220226
);
221227
}
222228

@@ -229,6 +235,7 @@ final class OpenRequest extends Request {
229235
object[_UniqueFieldNames.databaseName] = databaseName.toJS;
230236
object[_UniqueFieldNames.storageMode] = storageMode.toJS;
231237
object[_UniqueFieldNames.wasmUri] = wasmUri.toString().toJS;
238+
object[_UniqueFieldNames.onlyOpenVfs] = onlyOpenVfs.toJS;
232239
}
233240
}
234241

sqlite3_web/lib/src/worker.dart

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,12 @@ final class _ClientConnection extends ProtocolChannel
182182
try {
183183
database =
184184
_runner.findDatabase(request.databaseName, request.storageMode);
185-
await database.opened;
185+
186+
await (request.onlyOpenVfs ? database.vfs : database.opened);
187+
186188
connectionDatabase = _ConnectionDatabase(database);
187189
_openedDatabases.add(connectionDatabase);
190+
188191
return SimpleSuccessResponse(
189192
response: database.id.toJS, requestId: request.requestId);
190193
} catch (e) {
@@ -242,22 +245,20 @@ final class _ClientConnection extends ProtocolChannel
242245
return SimpleSuccessResponse(
243246
response: null, requestId: request.requestId);
244247
case FileSystemFlushRequest():
245-
if (database?.database.vfs case IndexedDbFileSystem idb) {
248+
if (await database?.database.vfs case IndexedDbFileSystem idb) {
246249
await idb.flush();
247250
}
248251

249252
return SimpleSuccessResponse(
250253
response: null, requestId: request.requestId);
251254
case FileSystemExistsQuery(:final fsType):
252-
await database!.database.opened;
253-
final vfs = database.database.vfs!;
255+
final vfs = await database!.database.vfs;
254256
final exists = vfs.xAccess(fsType.pathInVfs, 0) == 1;
255257

256258
return SimpleSuccessResponse(
257259
response: exists.toJS, requestId: request.requestId);
258260
case FileSystemAccess(:final buffer, :final fsType):
259-
await database!.database.opened;
260-
final vfs = database.database.vfs!;
261+
final vfs = await database!.database.vfs;
261262
final file = vfs
262263
.xOpen(
263264
Sqlite3Filename(fsType.pathInVfs), SqlFlag.SQLITE_OPEN_CREATE)
@@ -321,23 +322,23 @@ final class DatabaseState {
321322
int refCount = 1;
322323

323324
Future<WorkerDatabase>? _database;
325+
Future<void>? _openVfs;
326+
VirtualFileSystem? _resolvedVfs;
324327

325328
/// Runs additional async work, such as flushing the VFS to IndexedDB when
326329
/// the database is closed.
327330
FutureOr<void> Function()? closeHandler;
328-
VirtualFileSystem? vfs;
329331

330332
DatabaseState(
331333
{required this.id,
332334
required this.runner,
333335
required this.name,
334336
required this.mode});
335337

336-
Future<WorkerDatabase> get opened async {
337-
final database = _database ??= Future.sync(() async {
338-
final sqlite3 = await runner._sqlite3!;
339-
final vfsName = 'vfs-web-$id';
338+
String get vfsName => 'vfs-web-$id';
340339

340+
Future<VirtualFileSystem> get vfs async {
341+
await (_openVfs ??= Future.sync(() async {
341342
switch (mode) {
342343
case FileSystemImplementation.opfsLocks:
343344
final options = WasmVfs.createOptions(root: pathForOpfs(name));
@@ -349,22 +350,31 @@ final class DatabaseState {
349350
await EventStreamProviders.messageEvent.forTarget(worker).first;
350351

351352
final wasmVfs =
352-
vfs = WasmVfs(workerOptions: options, vfsName: vfsName);
353+
_resolvedVfs = WasmVfs(workerOptions: options, vfsName: vfsName);
353354
closeHandler = wasmVfs.close;
354355
case FileSystemImplementation.opfsShared:
355-
final simple = vfs = await SimpleOpfsFileSystem.loadFromStorage(
356-
pathForOpfs(name),
357-
vfsName: vfsName);
356+
final simple = _resolvedVfs =
357+
await SimpleOpfsFileSystem.loadFromStorage(pathForOpfs(name),
358+
vfsName: vfsName);
358359
closeHandler = simple.close;
359360
case FileSystemImplementation.indexedDb:
360-
final idb = vfs =
361+
final idb = _resolvedVfs =
361362
await IndexedDbFileSystem.open(dbName: name, vfsName: vfsName);
362363
closeHandler = idb.close;
363364
case FileSystemImplementation.inMemory:
364-
vfs = InMemoryFileSystem(name: vfsName);
365+
_resolvedVfs = InMemoryFileSystem(name: vfsName);
365366
}
367+
}));
368+
369+
return _resolvedVfs!;
370+
}
371+
372+
Future<WorkerDatabase> get opened async {
373+
final database = _database ??= Future.sync(() async {
374+
final sqlite3 = await runner._sqlite3!;
375+
final fileSystem = await vfs;
366376

367-
sqlite3.registerVirtualFileSystem(vfs!);
377+
sqlite3.registerVirtualFileSystem(fileSystem);
368378
return await runner._controller.openDatabase(
369379
sqlite3,
370380
// We're currently using /database as the in-VFS path. This is because
@@ -391,7 +401,7 @@ final class DatabaseState {
391401
final database = await _database!;
392402

393403
database.database.dispose();
394-
if (vfs case final vfs?) {
404+
if (_resolvedVfs case final vfs?) {
395405
sqlite3.unregisterVirtualFileSystem(vfs);
396406
}
397407

sqlite3_web/test/integration_test.dart

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:async';
12
import 'dart:io';
23

34
import 'package:sqlite3_web/src/types.dart';
@@ -82,9 +83,24 @@ void main() {
8283
group(browser.name, () {
8384
late Process driverProcess;
8485
late TestWebDriver driver;
86+
var isStoppingProcess = false;
87+
final processStopped = Completer<void>();
88+
89+
setUpAll(() async {
90+
final process = driverProcess = await browser.spawnDriver();
91+
process.exitCode.then((code) {
92+
if (!isStoppingProcess) {
93+
throw 'Webdriver stopped (code $code) before tearing down tests.';
94+
}
8595

86-
setUpAll(() async => driverProcess = await browser.spawnDriver());
87-
tearDownAll(() => driverProcess.kill());
96+
processStopped.complete();
97+
});
98+
});
99+
tearDownAll(() {
100+
isStoppingProcess = true;
101+
driverProcess.kill();
102+
return processStopped.future;
103+
});
88104

89105
setUp(() async {
90106
final rawDriver = await createDriver(
@@ -103,9 +119,12 @@ void main() {
103119
},
104120
);
105121

106-
rawDriver.logs.get(LogType.browser).listen((entry) {
107-
print('[console]: ${entry.message}');
108-
});
122+
// logs.get() isn't supported on Firefox
123+
if (browser != Browser.firefox) {
124+
rawDriver.logs.get(LogType.browser).listen((entry) {
125+
print('[console]: ${entry.message}');
126+
});
127+
}
109128

110129
driver = TestWebDriver(server, rawDriver);
111130
await driver.driver.get('http://localhost:8080/');
@@ -138,11 +157,18 @@ void main() {
138157

139158
for (final (storage, access) in browser.availableImplementations) {
140159
test('$storage through $access', () async {
141-
await driver.openDatabase((storage, access));
160+
await driver.openDatabase(
161+
implementation: (storage, access),
162+
onlyOpenVfs: true,
163+
);
164+
await driver.assertFile(false);
165+
142166
await driver.execute('CREATE TABLE foo (bar TEXT);');
143167
expect(await driver.countUpdateEvents(), 0);
144168
await driver.execute("INSERT INTO foo (bar) VALUES ('hello');");
145169
expect(await driver.countUpdateEvents(), 1);
170+
171+
expect(await driver.assertFile(true), isPositive);
146172
});
147173
}
148174
});

sqlite3_web/tool/server.dart

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,18 @@ class TestWebDriver {
136136
);
137137
}
138138

139-
Future<(StorageMode, AccessMode)> openDatabase(
140-
[(StorageMode, AccessMode)? implementation]) async {
139+
Future<(StorageMode, AccessMode)> openDatabase({
140+
(StorageMode, AccessMode)? implementation,
141+
bool onlyOpenVfs = false,
142+
}) async {
141143
final desc = switch (implementation) {
142144
null => null,
143145
(var storage, var access) => '${storage.name}:${access.name}'
144146
};
145147

148+
final method = onlyOpenVfs ? 'open_only_vfs' : 'open';
146149
final res = await driver
147-
.executeAsync('open(arguments[0], arguments[1])', [desc]) as String?;
150+
.executeAsync('$method(arguments[0], arguments[1])', [desc]) as String?;
148151

149152
// This returns the storage/access mode actually chosen.
150153
final split = res!.split(':');
@@ -175,4 +178,20 @@ class TestWebDriver {
175178
throw 'test_second failed! More information may be available in the console.';
176179
}
177180
}
181+
182+
Future<int?> assertFile(bool shouldExist) async {
183+
final res = await driver.executeAsync(
184+
'assert_file(arguments[0], arguments[1])', [shouldExist.toString()]);
185+
res!;
186+
187+
if (res == false) {
188+
throw 'assertFile failed! Expected $shouldExist, match return was $res';
189+
}
190+
191+
if (res is int) {
192+
return res;
193+
} else {
194+
return null;
195+
}
196+
}
178197
}

0 commit comments

Comments
 (0)