Skip to content

Commit f3b5709

Browse files
committed
First mptest passing.
1 parent c3671fd commit f3b5709

File tree

3 files changed

+180
-31
lines changed

3 files changed

+180
-31
lines changed

packages/wa-sqlite-driver/src/OPFSCoopSyncVFS2.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@ export class OPFSCoopSyncVFS2 extends FacadeVFS {
9292
return (this as unknown as WithModule)._module;
9393
}
9494

95+
get #accessHandleOptions() {
96+
return { mode: this.readonly ? 'read-only' : 'readwrite' };
97+
}
98+
99+
get #lockOptions() {
100+
return { mode: this.readonly ? 'shared' : 'exclusive' } as const;
101+
// return { mode: 'shared' as const };
102+
}
103+
95104
async #initialize(nTemporaryFiles) {
96105
// Delete temporary directories no longer in use.
97106
const root = await navigator.storage.getDirectory();
@@ -130,9 +139,9 @@ export class OPFSCoopSyncVFS2 extends FacadeVFS {
130139
// Populate temporary directory.
131140
for (let i = 0; i < nTemporaryFiles; i++) {
132141
const tmpFile = await tmpDir.getFileHandle(`${i}.tmp`, { create: true });
133-
const tmpAccessHandle = await (tmpFile as any).createSyncAccessHandle({
134-
mode: this.readonly ? 'read-only' : 'readwrite'
135-
});
142+
const tmpAccessHandle = await (tmpFile as any).createSyncAccessHandle(
143+
this.#accessHandleOptions
144+
);
136145
this.unboundAccessHandles.add(tmpAccessHandle);
137146
}
138147
}
@@ -531,9 +540,7 @@ export class OPFSCoopSyncVFS2 extends FacadeVFS {
531540
if (subPersistentFile) {
532541
subPersistentFile.accessHandle = await (
533542
subPersistentFile.fileHandle as any
534-
).createSyncAccessHandle({
535-
mode: this.readonly ? 'read-only' : 'readwrite'
536-
});
543+
).createSyncAccessHandle(this.#accessHandleOptions);
537544
}
538545
})
539546
);
@@ -574,18 +581,14 @@ export class OPFSCoopSyncVFS2 extends FacadeVFS {
574581
setTimeout(notify);
575582

576583
this.log?.(`lock requested: ${lockName}`);
577-
navigator.locks.request(
578-
lockName,
579-
{ mode: this.readonly ? 'shared' : 'exclusive' },
580-
(lock) => {
581-
// We have the lock. Stop asking other connections for it.
582-
this.log?.(`lock acquired: ${lockName}`, lock);
583-
clearInterval(notifyId);
584-
return new Promise<() => void>((res) => {
585-
resolve(res as () => void);
586-
});
587-
}
588-
);
584+
navigator.locks.request(lockName, this.#lockOptions, (lock) => {
585+
// We have the lock. Stop asking other connections for it.
586+
this.log?.(`lock acquired: ${lockName}`, lock);
587+
clearInterval(notifyId);
588+
return new Promise<() => void>((res) => {
589+
resolve(res as () => void);
590+
});
591+
});
589592
});
590593
}
591594
}

packages/wa-sqlite-driver/src/wa-sqlite-driver.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,6 @@ class StatementImpl implements SqliteDriverStatement {
190190
}
191191

192192
async _finalize() {
193-
console.log('finalizing...', this.con.path);
194193
// Wait for these to complete, but ignore any errors.
195194
// TODO: also wait for run/step to complete
196195
await this.preparePromise;
@@ -278,7 +277,6 @@ export class WaSqliteConnection implements SqliteDriverConnection {
278277

279278
async close() {
280279
await m.runExclusive(async () => {
281-
console.log('closing...', this.path);
282280
for (let statement of this.statements) {
283281
if (statement.options.persist) {
284282
statement.finalize();

packages/wa-sqlite-driver/test/src/mptest-runner.test.ts

Lines changed: 159 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type {
88

99
import { waSqliteSingleWorker } from '../../lib/index.js';
1010

11-
const scriptModules = (import.meta as any).glob('./mptest/**/*test', {
11+
const scriptModules = import.meta.glob('./mptest/**/*', {
1212
as: 'raw',
1313
eager: true
1414
});
@@ -68,6 +68,13 @@ interface ClientContext {
6868
pending: Set<Promise<void>>;
6969
}
7070

71+
interface TaskControl {
72+
promise: Promise<void>;
73+
resolve: () => void;
74+
reject: (err: unknown) => void;
75+
isResolved: () => boolean;
76+
}
77+
7178
class MptestRunner {
7279
private clients = new Map<number, ClientContext>();
7380
private pendingTasks = new Set<Promise<void>>();
@@ -142,6 +149,32 @@ class MptestRunner {
142149
});
143150
}
144151

152+
private createTaskControl(): TaskControl {
153+
let resolved = false;
154+
let resolveFn: () => void = () => {};
155+
let rejectFn: (err: unknown) => void = () => {};
156+
const promise = new Promise<void>((resolve, reject) => {
157+
resolveFn = () => {
158+
if (!resolved) {
159+
resolved = true;
160+
resolve();
161+
}
162+
};
163+
rejectFn = (err) => {
164+
if (!resolved) {
165+
resolved = true;
166+
reject(err);
167+
}
168+
};
169+
});
170+
return {
171+
promise,
172+
resolve: resolveFn,
173+
reject: rejectFn,
174+
isResolved: () => resolved
175+
};
176+
}
177+
145178
private async scheduleTask(
146179
parent: ScriptContext,
147180
clientId: number,
@@ -150,6 +183,8 @@ class MptestRunner {
150183
taskLabel: string
151184
): Promise<void> {
152185
const client = await this.ensureClient(clientId);
186+
const taskControl = this.createTaskControl();
187+
this.trackTask(client, taskControl.promise);
153188
const run = async () => {
154189
await this.runScriptInternal(
155190
{
@@ -159,12 +194,19 @@ class MptestRunner {
159194
displayName: `${parent.displayName}#client${clientId}:${taskLabel}`
160195
},
161196
script,
162-
startLine
197+
startLine,
198+
taskControl
163199
);
200+
taskControl.resolve();
164201
};
165-
const task = client.queue.then(run);
202+
const task = client.queue.then(run).catch((err) => {
203+
if (taskControl.isResolved()) {
204+
return;
205+
}
206+
taskControl.reject(err);
207+
throw err;
208+
});
166209
client.queue = task.catch(() => {});
167-
this.trackTask(client, task);
168210
}
169211

170212
private async waitForAll(timeoutMs: number): Promise<void> {
@@ -221,7 +263,8 @@ class MptestRunner {
221263
private async runScriptInternal(
222264
context: ScriptContext,
223265
script: string,
224-
initialLine: number
266+
initialLine: number,
267+
taskControl?: TaskControl
225268
): Promise<void> {
226269
const result = new ResultBuffer();
227270
let index = 0;
@@ -265,6 +308,10 @@ class MptestRunner {
265308
throw new Error(
266309
`${context.displayName}:${prevLine} expected [${expectedRaw}] but got [${result.toString()}]`
267310
);
311+
} else {
312+
console.log(
313+
`${context.displayName}:${prevLine} expected [${expectedRaw}] matched [${result.toString()}]`
314+
);
268315
}
269316
result.reset();
270317
break;
@@ -295,6 +342,10 @@ class MptestRunner {
295342
);
296343
break;
297344
}
345+
case 'finish': {
346+
taskControl?.resolve();
347+
break;
348+
}
298349
case 'wait': {
299350
const target = command.args[0] ?? 'all';
300351
const timeout = command.args[1] ? Number(command.args[1]) : 10000;
@@ -323,7 +374,8 @@ class MptestRunner {
323374
displayName: baseName(resolved)
324375
},
325376
subScript,
326-
1
377+
1,
378+
taskControl
327379
);
328380
break;
329381
}
@@ -374,8 +426,8 @@ class MptestRunner {
374426
if (!rows || rows.length === 0) {
375427
return false;
376428
}
377-
const row = rows[0] as SqliteRowRaw;
378-
const value = row[0];
429+
const rawRows = rows as SqliteRowRaw[];
430+
const value = rawRows[0]?.[0];
379431
if (value == null) {
380432
return false;
381433
}
@@ -404,12 +456,16 @@ class MptestRunner {
404456
if (!sql.trim()) {
405457
continue;
406458
}
459+
if (!hasNonCommentContent(sql)) {
460+
continue;
461+
}
407462
const statement = context.connection.prepare(sql, { rawResults: true });
408463
try {
409464
const { rows } = await statement.step();
410465
if (rows) {
411-
for (const row of rows) {
412-
for (const value of row as SqliteRowRaw) {
466+
const rawRows = rows as SqliteRowRaw[];
467+
for (const row of rawRows) {
468+
for (const value of row) {
413469
result.append(value);
414470
}
415471
}
@@ -428,7 +484,9 @@ class MptestRunner {
428484
}
429485

430486
describe('mptest scripts', () => {
431-
for (const script of topLevelScripts) {
487+
// const scripts = topLevelScripts;
488+
const scripts = ['mptest/multiwrite01.test'];
489+
for (const script of scripts) {
432490
test(script, async () => {
433491
const dbPath = `mptest-${sanitizeForFilename(script)}-${Math.random()
434492
.toString(36)
@@ -861,6 +919,96 @@ function toHex(data: Uint8Array): string {
861919
return hex;
862920
}
863921

922+
function hasNonCommentContent(sql: string): boolean {
923+
let i = 0;
924+
let inSingle = false;
925+
let inDouble = false;
926+
let inBracket = false;
927+
928+
const advance = (step = 1) => {
929+
i += step;
930+
};
931+
932+
while (i < sql.length) {
933+
const c = sql[i];
934+
const next = sql[i + 1];
935+
936+
if (inSingle) {
937+
if (c === "'") {
938+
if (next === "'") {
939+
advance(2);
940+
continue;
941+
}
942+
inSingle = false;
943+
advance();
944+
continue;
945+
}
946+
advance();
947+
continue;
948+
}
949+
if (inDouble) {
950+
if (c === '"') {
951+
if (next === '"') {
952+
advance(2);
953+
continue;
954+
}
955+
inDouble = false;
956+
advance();
957+
continue;
958+
}
959+
advance();
960+
continue;
961+
}
962+
if (inBracket) {
963+
if (c === ']') {
964+
inBracket = false;
965+
}
966+
advance();
967+
continue;
968+
}
969+
970+
if (c === "'") {
971+
inSingle = true;
972+
advance();
973+
continue;
974+
}
975+
if (c === '"') {
976+
inDouble = true;
977+
advance();
978+
continue;
979+
}
980+
if (c === '[') {
981+
inBracket = true;
982+
advance();
983+
continue;
984+
}
985+
986+
if (c === '-' && next === '-') {
987+
advance(2);
988+
while (i < sql.length && sql[i] !== '\n') {
989+
advance();
990+
}
991+
continue;
992+
}
993+
if (c === '/' && next === '*') {
994+
advance(2);
995+
while (i < sql.length && !(sql[i] === '*' && sql[i + 1] === '/')) {
996+
advance();
997+
}
998+
if (i < sql.length) {
999+
advance(2);
1000+
}
1001+
continue;
1002+
}
1003+
if (/\s/.test(c ?? '')) {
1004+
advance();
1005+
continue;
1006+
}
1007+
return true;
1008+
}
1009+
return false;
1010+
}
1011+
8641012
function sleepMs(ms: number): Promise<void> {
8651013
if (ms <= 0) {
8661014
return Promise.resolve();

0 commit comments

Comments
 (0)