4 The author disclaims copyright to this source code. In place of a
5 legal notice, here is a blessing:
7 * May you do good and not evil.
8 * May you find forgiveness for yourself and forgive others.
9 * May you share freely, never taking more than you give.
11 ***********************************************************************
13 Main functional and regression tests for the sqlite3 WASM API.
15 This mini-framework works like so:
17 This script adds a series of test groups, each of which contains an
18 arbitrary number of tests, into a queue. After loading of the
19 sqlite3 WASM/JS module is complete, that queue is processed. If any
20 given test fails, the whole thing fails. This script is built such
21 that it can run from the main UI thread or worker thread. Test
22 groups and individual tests can be assigned a predicate function
23 which determines whether to run them or not, and this is
24 specifically intended to be used to toggle certain tests on or off
25 for the main/worker threads.
27 Each test group defines a state object which gets applied as each
28 test function's `this`. Test functions can use that to, e.g., set up
29 a db in an early test and close it in a later test. Each test gets
30 passed the sqlite3 namespace object as its only argument.
35 Set up our output channel differently depending
36 on whether we are running in a worker thread or
40 /* Predicate for tests/groups. */
41 const isUIThread = ()=>(self.window===self && self.document);
42 /* Predicate for tests/groups. */
43 const isWorker = ()=>!isUIThread();
44 /* Predicate for tests/groups. */
45 const testIsTodo = ()=>false;
46 const haveWasmCTests = ()=>{
47 return !!wasm.exports.sqlite3_wasm_test_intptr;
50 const mapToString = (v)=>{
52 case 'number': case 'string': case 'boolean':
53 case 'undefined': case 'bigint':
57 if(null===v) return 'null';
58 if(v instanceof Error){
65 return JSON.stringify(v,undefined,2);
67 const normalizeArgs = (args)=>args.map(mapToString);
69 console.log("Running in the UI thread.");
70 const logTarget = document.querySelector('#test-output');
71 logClass = function(cssClass,...args){
72 const ln = document.createElement('div');
74 for(const c of (Array.isArray(cssClass) ? cssClass : [cssClass])){
78 ln.append(document.createTextNode(normalizeArgs(args).join(' ')));
81 const cbReverse = document.querySelector('#cb-log-reverse');
82 const cbReverseKey = 'tester1:cb-log-reverse';
83 const cbReverseIt = ()=>{
84 logTarget.classList[cbReverse.checked ? 'add' : 'remove']('reverse');
85 //localStorage.setItem(cbReverseKey, cbReverse.checked ? 1 : 0);
87 cbReverse.addEventListener('change', cbReverseIt, true);
88 /*if(localStorage.getItem(cbReverseKey)){
89 cbReverse.checked = !!(+localStorage.getItem(cbReverseKey));
92 }else{ /* Worker thread */
93 console
.log("Running in a Worker thread.");
94 logClass = function(cssClass
,...args
){
97 payload
:{cssClass
, args
: normalizeArgs(args
)}
102 const reportFinalTestStatus = function(pass
){
104 const e
= document
.querySelector('#color-target');
105 e
.classList
.add(pass
? 'tests-pass' : 'tests-fail');
107 postMessage({type
:'test-result', payload
:{pass
}});
110 const log
= (...args
)=>{
111 //console.log(...args);
112 logClass('',...args
);
114 const warn
= (...args
)=>{
115 console
.warn(...args
);
116 logClass('warning',...args
);
118 const error
= (...args
)=>{
119 console
.error(...args
);
120 logClass('error',...args
);
123 const toss
= (...args
)=>{
125 throw new Error(args
.join(' '));
127 const tossQuietly
= (...args
)=>{
128 throw new Error(args
.join(' '));
131 const roundMs
= (ms
)=>Math
.round(ms
*100)/100;
134 Helpers for writing sqlite3-specific tests.
137 /** Running total of the number of tests run via
140 /* Separator line for log messages. */
141 separator
: '------------------------------------------------------------',
143 If expr is a function, it is called and its result
144 is returned, coerced to a bool, else expr, coerced to
147 toBool: function(expr
){
148 return (expr
instanceof Function
) ? !!expr() : !!expr
;
150 /** Throws if expr is false. If expr is a function, it is called
151 and its result is evaluated. If passed multiple arguments,
152 those after the first are a message string which get applied
153 as an exception message if the assertion fails. The message
154 arguments are concatenated together with a space between each.
156 assert
: function f(expr
, ...msg
){
158 if(!this.toBool(expr
)){
159 throw new Error(msg
.length
? msg
.join(' ') : "Assertion failed.");
163 /** Calls f() and squelches any exception it throws. If it
164 does not throw, this function throws. */
165 mustThrow: function(f
, msg
){
168 try{ f(); } catch(e
){err
=e
;}
169 if(!err
) throw new Error(msg
|| "Expected exception.");
173 Works like mustThrow() but expects filter to be a regex,
174 function, or string to match/filter the resulting exception
175 against. If f() does not throw, this test fails and an Error is
176 thrown. If filter is a regex, the test passes if
177 filter.test(error.message) passes. If it's a function, the test
178 passes if filter(error) returns truthy. If it's a string, the
179 test passes if the filter matches the exception message
180 precisely. In all other cases the test fails, throwing an
183 If it throws, msg is used as the error report unless it's falsy,
184 in which case a default is used.
186 mustThrowMatching: function(f
, filter
, msg
){
189 try{ f(); } catch(e
){err
=e
;}
190 if(!err
) throw new Error(msg
|| "Expected exception.");
192 if(filter
instanceof RegExp
) pass
= filter
.test(err
.message
);
193 else if(filter
instanceof Function
) pass
= filter(err
);
194 else if('string' === typeof filter
) pass
= (err
.message
=== filter
);
196 throw new Error(msg
|| ("Filter rejected this exception: "+err
.message
));
200 /** Throws if expr is truthy or expr is a function and expr()
202 throwIf: function(expr
, msg
){
204 if(this.toBool(expr
)) throw new Error(msg
|| "throwIf() failed");
207 /** Throws if expr is falsy or expr is a function and expr()
209 throwUnless: function(expr
, msg
){
211 if(!this.toBool(expr
)) throw new Error(msg
|| "throwUnless() failed");
214 eqApprox
: (v1
,v2
,factor
=0.05)=>(v1
>=(v2
-factor
) && v1
<=(v2
+factor
)),
215 TestGroup
: (function(){
216 let groupCounter
= 0;
217 const TestGroup = function(name
, predicate
){
218 this.number
= ++groupCounter
;
220 this.predicate
= predicate
;
223 TestGroup
.prototype = {
224 addTest: function(testObj
){
225 this.tests
.push(testObj
);
228 run
: async
function(sqlite3
){
229 log(TestUtil
.separator
);
230 logClass('group-start',"Group #"+this.number
+':',this.name
);
232 if(this.predicate
&& !this.predicate(sqlite3
)){
233 logClass('warning',indent
,
234 "SKIPPING group because predicate says to.");
237 const assertCount
= TestUtil
.counter
;
238 const groupState
= Object
.create(null);
240 let runtime
= 0, i
= 0;
241 for(const t
of this.tests
){
243 const n
= this.number
+"."+i
;
244 log(indent
, n
+":", t
.name
);
245 if(t
.predicate
&& !t
.predicate(sqlite3
)){
246 logClass('warning', indent
, indent
,
247 'SKIPPING because predicate says to');
248 skipped
.push( n
+': '+t
.name
);
250 const tc
= TestUtil
.counter
, now
= performance
.now();
251 await t
.test
.call(groupState
, sqlite3
);
252 const then
= performance
.now();
253 runtime
+= then
- now
;
254 logClass('faded',indent
, indent
,
255 TestUtil
.counter
- tc
, 'assertion(s) in',
256 roundMs(then
-now
),'ms');
260 "Group #"+this.number
+":",(TestUtil
.counter
- assertCount
),
261 "assertion(s) in",roundMs(runtime
),"ms");
263 logClass('warning',"SKIPPED test(s) in group",this.number
+":",skipped
);
270 currentTestGroup
: undefined,
271 addGroup: function(name
, predicate
){
272 this.testGroups
.push( this.currentTestGroup
=
273 new this.TestGroup(name
, predicate
) );
276 addTest: function(name
, callback
){
278 if(1===arguments
.length
){
279 const opt
= arguments
[0];
280 predicate
= opt
.predicate
;
284 this.currentTestGroup
.addTest({
285 name
, predicate
, test
: callback
289 runTests
: async
function(sqlite3
){
290 return new Promise(async
function(pok
,pnok
){
293 for(let g
of this.testGroups
){
294 const now
= performance
.now();
295 await g
.run(sqlite3
);
296 runtime
+= performance
.now() - now
;
298 log(TestUtil
.separator
);
299 logClass(['strong','green'],
300 "Done running tests.",TestUtil
.counter
,"assertions in",
301 roundMs(runtime
),'ms');
303 reportFinalTestStatus(true);
307 reportFinalTestStatus(false);
315 let capi
, wasm
/*assigned after module init*/;
316 ////////////////////////////////////////////////////////////////////////
317 // End of infrastructure setup. Now define the tests...
318 ////////////////////////////////////////////////////////////////////////
320 ////////////////////////////////////////////////////////////////////
321 T
.g('Basic sanity checks')
322 .t('Namespace object checks', function(sqlite3
){
323 const wasmCtypes
= wasm
.ctype
;
324 T
.assert(wasmCtypes
.structs
[0].name
==='sqlite3_vfs').
325 assert(wasmCtypes
.structs
[0].members
.szOsFile
.sizeof
>=4).
326 assert(wasmCtypes
.structs
[1/*sqlite3_io_methods*/
327 ].members
.xFileSize
.offset
>0);
328 [ /* Spot-check a handful of constants to make sure they got installed... */
329 'SQLITE_SCHEMA','SQLITE_NULL','SQLITE_UTF8',
330 'SQLITE_STATIC', 'SQLITE_DIRECTONLY',
331 'SQLITE_OPEN_CREATE', 'SQLITE_OPEN_DELETEONCLOSE'
332 ].forEach((k
)=>T
.assert('number' === typeof capi
[k
]));
333 [/* Spot-check a few of the WASM API methods. */
334 'alloc', 'dealloc', 'installFunction'
335 ].forEach((k
)=>T
.assert(wasm
[k
] instanceof Function
));
337 T
.assert(capi
.sqlite3_errstr(capi
.SQLITE_IOERR_ACCESS
).indexOf("I/O")>=0).
338 assert(capi
.sqlite3_errstr(capi
.SQLITE_CORRUPT
).indexOf('malformed')>0).
339 assert(capi
.sqlite3_errstr(capi
.SQLITE_OK
) === 'not an error');
342 throw new sqlite3
.WasmAllocError
;
344 T
.assert(e
instanceof Error
)
345 .assert(e
instanceof sqlite3
.WasmAllocError
)
346 .assert("Allocation failed." === e
.message
);
349 throw new sqlite3
.WasmAllocError("test",{
353 T
.assert(3 === e
.cause
)
354 .assert("test" === e
.message
);
356 try {throw new sqlite3
.WasmAllocError("test","ing",".")}
357 catch(e
){T
.assert("test ing ." === e
.message
)}
359 try{ throw new sqlite3
.SQLite3Error(capi
.SQLITE_SCHEMA
) }
360 catch(e
){ T
.assert('SQLITE_SCHEMA' === e
.message
) }
361 try{ sqlite3
.SQLite3Error
.toss(capi
.SQLITE_CORRUPT
,{cause
: true}) }
363 T
.assert('SQLITE_CORRUPT'===e
.message
)
364 .assert(true===e
.cause
);
367 ////////////////////////////////////////////////////////////////////
368 .t('strglob/strlike', function(sqlite3
){
369 T
.assert(0===capi
.sqlite3_strglob("*.txt", "foo.txt")).
370 assert(0!==capi
.sqlite3_strglob("*.txt", "foo.xtx")).
371 assert(0===capi
.sqlite3_strlike("%.txt", "foo.txt", 0)).
372 assert(0!==capi
.sqlite3_strlike("%.txt", "foo.xtx", 0));
374 ////////////////////////////////////////////////////////////////////
375 ;/*end of basic sanity checks*/
377 ////////////////////////////////////////////////////////////////////
378 T
.g('C/WASM Utilities')
379 .t('sqlite3.wasm namespace', function(sqlite3
){
381 const chr
= (x
)=>x
.charCodeAt(0);
382 //log("heap getters...");
384 const li
= [8, 16, 32];
385 if(w
.bigIntEnabled
) li
.push(64);
388 const s
= w
.heapForSize(n
,false);
389 T
.assert(bpe
===s
.BYTES_PER_ELEMENT
).
390 assert(w
.heapForSize(s
.constructor) === s
);
391 const u
= w
.heapForSize(n
,true);
392 T
.assert(bpe
===u
.BYTES_PER_ELEMENT
).
394 assert(w
.heapForSize(u
.constructor) === u
);
400 const ip
= w
.isPtr32
;
404 .assert(!ip(0xffffffff))
405 .assert(ip(0x7fffffff))
407 .assert(!ip(null)/*might change: under consideration*/)
411 //log("jstrlen()...");
413 T
.assert(3 === w
.jstrlen("abc")).assert(4 === w
.jstrlen("äbc"));
416 //log("jstrcpy()...");
419 let ua
= new Uint8Array(8), rc
,
420 refill
= ()=>ua
.fill(fillChar
);
422 rc
= w
.jstrcpy("hello", ua
);
423 T
.assert(6===rc
).assert(0===ua
[5]).assert(chr('o')===ua
[4]);
426 rc
= w
.jstrcpy("HELLO", ua
, 0, -1, false);
427 T
.assert(5===rc
).assert(chr('!')===ua
[5]).assert(chr('O')===ua
[4]);
429 rc
= w
.jstrcpy("the end", ua
, 4);
430 //log("rc,ua",rc,ua);
431 T
.assert(4===rc
).assert(0===ua
[7]).
432 assert(chr('e')===ua
[6]).assert(chr('t')===ua
[4]);
434 rc
= w
.jstrcpy("the end", ua
, 4, -1, false);
435 T
.assert(4===rc
).assert(chr(' ')===ua
[7]).
436 assert(chr('e')===ua
[6]).assert(chr('t')===ua
[4]);
438 rc
= w
.jstrcpy("", ua
, 0, 1, true);
439 //log("rc,ua",rc,ua);
440 T
.assert(1===rc
).assert(0===ua
[0]);
442 rc
= w
.jstrcpy("x", ua
, 0, 1, true);
443 //log("rc,ua",rc,ua);
444 T
.assert(1===rc
).assert(0===ua
[0]);
446 rc
= w
.jstrcpy('äbä', ua
, 0, 1, true);
447 T
.assert(1===rc
, 'Must not write partial multi-byte char.')
450 rc
= w
.jstrcpy('äbä', ua
, 0, 2, true);
451 T
.assert(1===rc
, 'Must not write partial multi-byte char.')
454 rc
= w
.jstrcpy('äbä', ua
, 0, 2, false);
455 T
.assert(2===rc
).assert(fillChar
!==ua
[1]).assert(fillChar
===ua
[2]);
458 //log("cstrncpy()...");
460 const scope
= w
.scopedAllocPush();
462 let cStr
= w
.scopedAllocCString("hello");
463 const n
= w
.cstrlen(cStr
);
464 let cpy
= w
.scopedAlloc(n
+10);
465 let rc
= w
.cstrncpy(cpy
, cStr
, n
+10);
466 T
.assert(n
+1 === rc
).
467 assert("hello" === w
.cstringToJs(cpy
)).
468 assert(chr('o') === w
.getMemValue(cpy
+n
-1)).
469 assert(0 === w
.getMemValue(cpy
+n
));
470 let cStr2
= w
.scopedAllocCString("HI!!!");
471 rc
= w
.cstrncpy(cpy
, cStr2
, 3);
473 assert("HI!lo" === w
.cstringToJs(cpy
)).
474 assert(chr('!') === w
.getMemValue(cpy
+2)).
475 assert(chr('l') === w
.getMemValue(cpy
+3));
477 w
.scopedAllocPop(scope
);
481 //log("jstrToUintArray()...");
483 let a
= w
.jstrToUintArray("hello", false);
484 T
.assert(5===a
.byteLength
).assert(chr('o')===a
[4]);
485 a
= w
.jstrToUintArray("hello", true);
486 T
.assert(6===a
.byteLength
).assert(chr('o')===a
[4]).assert(0===a
[5]);
487 a
= w
.jstrToUintArray("äbä", false);
488 T
.assert(5===a
.byteLength
).assert(chr('b')===a
[2]);
489 a
= w
.jstrToUintArray("äbä", true);
490 T
.assert(6===a
.byteLength
).assert(chr('b')===a
[2]).assert(0===a
[5]);
493 //log("allocCString()...");
495 const cstr
= w
.allocCString("hällo, world");
496 const n
= w
.cstrlen(cstr
);
498 .assert(0===w
.getMemValue(cstr
+n
))
499 .assert(chr('d')===w
.getMemValue(cstr
+n
-1));
502 //log("scopedAlloc() and friends...");
504 const alloc
= w
.alloc
, dealloc
= w
.dealloc
;
505 w
.alloc
= w
.dealloc
= null;
506 T
.assert(!w
.scopedAlloc
.level
)
507 .mustThrowMatching(()=>w
.scopedAlloc(1), /^No scopedAllocPush/)
508 .mustThrowMatching(()=>w
.scopedAllocPush(), /missing alloc/);
510 T
.mustThrowMatching(()=>w
.scopedAllocPush(), /missing alloc/);
512 T
.mustThrowMatching(()=>w
.scopedAllocPop(), /^Invalid state/)
513 .mustThrowMatching(()=>w
.scopedAlloc(1), /^No scopedAllocPush/)
514 .mustThrowMatching(()=>w
.scopedAlloc
.level
=0, /read-only/);
515 const asc
= w
.scopedAllocPush();
518 const p1
= w
.scopedAlloc(16),
519 p2
= w
.scopedAlloc(16);
520 T
.assert(1===w
.scopedAlloc
.level
)
521 .assert(Number
.isFinite(p1
))
522 .assert(Number
.isFinite(p2
))
523 .assert(asc
[0] === p1
)
524 .assert(asc
[1]===p2
);
525 asc2
= w
.scopedAllocPush();
526 const p3
= w
.scopedAlloc(16);
527 T
.assert(2===w
.scopedAlloc
.level
)
528 .assert(Number
.isFinite(p3
))
529 .assert(2===asc
.length
)
530 .assert(p3
===asc2
[0]);
532 const [z1
, z2
, z3
] = w
.scopedAllocPtr(3);
533 T
.assert('number'===typeof z1
).assert(z2
>z1
).assert(z3
>z2
)
534 .assert(0===w
.getMemValue(z1
,'i32'), 'allocPtr() must zero the targets')
535 .assert(0===w
.getMemValue(z3
,'i32'));
537 // Pop them in "incorrect" order to make sure they behave:
538 w
.scopedAllocPop(asc
);
539 T
.assert(0===asc
.length
);
540 T
.mustThrowMatching(()=>w
.scopedAllocPop(asc
),
541 /^Invalid state object/);
543 T
.assert(2===asc2
.length
,'Should be p3 and z1');
544 w
.scopedAllocPop(asc2
);
545 T
.assert(0===asc2
.length
);
546 T
.mustThrowMatching(()=>w
.scopedAllocPop(asc2
),
547 /^Invalid state object/);
550 T
.assert(0===w
.scopedAlloc
.level
);
551 w
.scopedAllocCall(function(){
552 T
.assert(1===w
.scopedAlloc
.level
);
553 const [cstr
, n
] = w
.scopedAllocCString("hello, world", true);
555 .assert(0===w
.getMemValue(cstr
+n
))
556 .assert(chr('d')===w
.getMemValue(cstr
+n
-1));
562 const pJson
= w
.xCall('sqlite3_wasm_enum_json');
563 T
.assert(Number
.isFinite(pJson
)).assert(w
.cstrlen(pJson
)>300);
568 T
.mustThrowMatching(()=>w
.xWrap('sqlite3_libversion',null,'i32'),
570 assert(w
.xWrap
.resultAdapter('i32') instanceof Function
).
571 assert(w
.xWrap
.argAdapter('i32') instanceof Function
);
572 let fw
= w
.xWrap('sqlite3_libversion','utf8');
573 T
.mustThrowMatching(()=>fw(1), /requires 0 arg/);
575 T
.assert('string'===typeof rc
).assert(rc
.length
>5);
576 rc
= w
.xCallWrapped('sqlite3_wasm_enum_json','*');
577 T
.assert(rc
>0 && Number
.isFinite(rc
));
578 rc
= w
.xCallWrapped('sqlite3_wasm_enum_json','utf8');
579 T
.assert('string'===typeof rc
).assert(rc
.length
>300);
580 if(haveWasmCTests()){
581 fw
= w
.xWrap('sqlite3_wasm_test_str_hello', 'utf8:free',['i32']);
583 T
.assert('hello'===rc
);
588 w
.xWrap
.resultAdapter('thrice', (v
)=>3n
*BigInt(v
));
589 w
.xWrap
.argAdapter('twice', (v
)=>2n
*BigInt(v
));
590 fw
= w
.xWrap('sqlite3_wasm_test_int64_times2','thrice','twice');
594 w
.scopedAllocCall(function(){
595 let pI1
= w
.scopedAlloc(8), pI2
= pI1
+4;
596 w
.setMemValue(pI1
, 0,'*')(pI2
, 0, '*');
597 let f
= w
.xWrap('sqlite3_wasm_test_int64_minmax',undefined,['i64*','i64*']);
598 let r1
= w
.getMemValue(pI1
, 'i64'), r2
= w
.getMemValue(pI2
, 'i64');
599 T
.assert(!Number
.isSafeInteger(r1
)).assert(!Number
.isSafeInteger(r2
));
606 ////////////////////////////////////////////////////////////////////
607 .t('sqlite3.StructBinder (jaccwabyt)', function(sqlite3
){
608 const S
= sqlite3
, W
= S
.wasm
;
609 const MyStructDef
= {
612 p4
: {offset
: 0, sizeof
: 4, signature
: "i"},
613 pP
: {offset
: 4, sizeof
: 4, signature
: "P"},
614 ro
: {offset
: 8, sizeof
: 4, signature
: "i", readOnly
: true},
615 cstr
: {offset
: 12, sizeof
: 4, signature
: "s"}
619 const m
= MyStructDef
;
620 m
.members
.p8
= {offset
: m
.sizeof
, sizeof
: 8, signature
: "j"};
621 m
.sizeof
+= m
.members
.p8
.sizeof
;
623 const StructType
= S
.StructBinder
.StructType
;
624 const K
= S
.StructBinder('my_struct',MyStructDef
);
625 T
.mustThrowMatching(()=>K(), /via 'new'/).
626 mustThrowMatching(()=>new K('hi'), /^Invalid pointer/);
627 const k1
= new K(), k2
= new K();
629 T
.assert(k1
.constructor === K
).
631 assert(k1
instanceof K
).
632 assert(K
.prototype.lookupMember('p4').key
=== '$p4').
633 assert(K
.prototype.lookupMember('$p4').name
=== 'p4').
634 mustThrowMatching(()=>K
.prototype.lookupMember('nope'), /not a mapped/).
635 assert(undefined === K
.prototype.lookupMember('nope',false)).
636 assert(k1
instanceof StructType
).
637 assert(StructType
.isA(k1
)).
638 assert(K
.resolveToInstance(k1
.pointer
)===k1
).
639 mustThrowMatching(()=>K
.resolveToInstance(null,true), /is-not-a my_struct/).
640 assert(k1
=== StructType
.instanceForPointer(k1
.pointer
)).
641 mustThrowMatching(()=>k1
.$ro
= 1, /read-only/);
642 Object
.keys(MyStructDef
.members
).forEach(function(key
){
643 key
= K
.memberKey(key
);
644 T
.assert(0 == k1
[key
],
645 "Expecting allocation to zero the memory "+
646 "for "+key
+" but got: "+k1
[key
]+
647 " from "+k1
.memoryDump());
649 T
.assert('number' === typeof k1
.pointer
).
650 mustThrowMatching(()=>k1
.pointer
= 1, /pointer/).
651 assert(K
.instanceForPointer(k1
.pointer
) === k1
);
652 k1
.$p4
= 1; k1
.$pP
= 2;
653 T
.assert(1 === k1
.$p4
).assert(2 === k1
.$pP
);
654 if(MyStructDef
.members
.$p8
){
655 k1
.$p8
= 1/*must not throw despite not being a BigInt*/;
656 k1
.$p8
= BigInt(Number
.MAX_SAFE_INTEGER
* 2);
657 T
.assert(BigInt(2 * Number
.MAX_SAFE_INTEGER
) === k1
.$p8
);
659 T
.assert(!k1
.ondispose
);
660 k1
.setMemberCString('cstr', "A C-string.");
661 T
.assert(Array
.isArray(k1
.ondispose
)).
662 assert(k1
.ondispose
[0] === k1
.$cstr
).
663 assert('number' === typeof k1
.$cstr
).
664 assert('A C-string.' === k1
.memberToJsString('cstr'));
666 T
.assert(k1
.$pP
=== k2
);
667 k1
.$pP
= null/*null is special-cased to 0.*/;
668 T
.assert(0===k1
.$pP
);
669 let ptr
= k1
.pointer
;
671 T
.assert(undefined === k1
.pointer
).
672 assert(undefined === K
.instanceForPointer(ptr
)).
673 mustThrowMatching(()=>{k1
.$pP
=1}, /disposed instance/);
676 T
.assert(k3
=== K
.instanceForPointer(ptr
));
679 assert(undefined === k2
.pointer
).
680 assert(undefined === k3
.pointer
).
681 assert(undefined === K
.instanceForPointer(ptr
));
687 if(!W
.bigIntEnabled
){
688 log("Skipping WasmTestStruct tests: BigInt not enabled.");
693 W
.ctype
.structs
.filter((e
)=>'WasmTestStruct'===e
.name
)[0];
694 const autoResolvePtr
= true /* EXPERIMENTAL */;
696 WTStructDesc
.members
.ppV
.signature
= 'P';
698 const WTStruct
= S
.StructBinder(WTStructDesc
);
699 //log(WTStruct.structName, WTStruct.structInfo);
700 const wts
= new WTStruct();
701 //log("WTStruct.prototype keys:",Object.keys(WTStruct.prototype));
703 T
.assert(wts
.constructor === WTStruct
).
704 assert(WTStruct
.memberKeys().indexOf('$ppV')>=0).
705 assert(wts
.memberKeys().indexOf('$v8')>=0).
707 assert(WTStruct
.isA(wts
)).
708 assert(wts
instanceof WTStruct
).
709 assert(wts
instanceof StructType
).
710 assert(StructType
.isA(wts
)).
711 assert(wts
=== StructType
.instanceForPointer(wts
.pointer
));
712 T
.assert(wts
.pointer
>0).assert(0===wts
.$v4
).assert(0n
===wts
.$v8
).
713 assert(0===wts
.$ppV
).assert(0===wts
.$xFunc
).
714 assert(WTStruct
.instanceForPointer(wts
.pointer
) === wts
);
716 W
.xGet('sqlite3_wasm_test_struct'/*name gets mangled in -O3 builds!*/);
718 //log("wts.pointer =",wts.pointer);
719 const wtsFunc = function(arg
){
720 /*log("This from a JS function called from C, "+
721 "which itself was called from JS. arg =",arg);*/
723 T
.assert(WTStruct
.instanceForPointer(arg
) === wts
);
725 tossQuietly("Testing exception propagation.");
728 wts
.$v4
= 10; wts
.$v8
= 20;
729 wts
.$xFunc
= W
.installFunction(wtsFunc
, wts
.memberSignature('xFunc'))
730 T
.assert(0===counter
).assert(10 === wts
.$v4
).assert(20n
=== wts
.$v8
)
731 .assert(0 === wts
.$ppV
).assert('number' === typeof wts
.$xFunc
)
732 .assert(0 === wts
.$cstr
)
733 .assert(wts
.memberIsString('$cstr'))
734 .assert(!wts
.memberIsString('$v4'))
735 .assert(null === wts
.memberToJsString('$cstr'))
736 .assert(W
.functionEntry(wts
.$xFunc
) instanceof Function
);
737 /* It might seem silly to assert that the values match
738 what we just set, but recall that all of those property
739 reads and writes are, via property interceptors,
740 actually marshaling their data to/from a raw memory
741 buffer, so merely reading them back is actually part of
742 testing the struct-wrapping API. */
744 testFunc(wts
.pointer
);
745 //log("wts.pointer, wts.$ppV",wts.pointer, wts.$ppV);
746 T
.assert(1===counter
).assert(20 === wts
.$v4
).assert(40n
=== wts
.$v8
)
747 .assert(autoResolvePtr
? (wts
.$ppV
=== wts
) : (wts
.$ppV
=== wts
.pointer
))
748 .assert('string' === typeof wts
.memberToJsString('cstr'))
749 .assert(wts
.memberToJsString('cstr') === wts
.memberToJsString('$cstr'))
750 .mustThrowMatching(()=>wts
.memberToJsString('xFunc'),
751 /Invalid member type signature for C-string/)
753 testFunc(wts
.pointer
);
754 T
.assert(2===counter
).assert(40 === wts
.$v4
).assert(80n
=== wts
.$v8
)
755 .assert(autoResolvePtr
? (wts
.$ppV
=== wts
) : (wts
.$ppV
=== wts
.pointer
));
756 /** The 3rd call to wtsFunc throw from JS, which is called
757 from C, which is called from JS. Let's ensure that
758 that exception propagates back here... */
759 T
.mustThrowMatching(()=>testFunc(wts
.pointer
),/^Testing/);
760 W
.uninstallFunction(wts
.$xFunc
);
765 //WTStruct.debugFlags(0x03);
767 T
.assert(wts
=== wts
.$ppV
)
768 //WTStruct.debugFlags(0);
770 wts
.setMemberCString('cstr', "A C-string.");
771 T
.assert(Array
.isArray(wts
.ondispose
)).
772 assert(wts
.ondispose
[0] === wts
.$cstr
).
773 assert('A C-string.' === wts
.memberToJsString('cstr'));
774 const ptr
= wts
.pointer
;
776 T
.assert(ptr
).assert(undefined === wts
.pointer
).
777 assert(undefined === WTStruct
.instanceForPointer(ptr
))
783 ////////////////////////////////////////////////////////////////////
784 .t('sqlite3.StructBinder part 2', function(sqlite3
){
785 // https://www.sqlite.org/c3ref/vfs.html
786 // https://www.sqlite.org/c3ref/io_methods.html
787 const sqlite3_io_methods
= capi
.sqlite3_io_methods
,
788 sqlite3_vfs
= capi
.sqlite3_vfs
,
789 sqlite3_file
= capi
.sqlite3_file
;
790 //log("struct sqlite3_file", sqlite3_file.memberKeys());
791 //log("struct sqlite3_vfs", sqlite3_vfs.memberKeys());
792 //log("struct sqlite3_io_methods", sqlite3_io_methods.memberKeys());
793 const installMethod
= function callee(tgt
, name
, func
){
794 if(1===arguments
.length
){
795 return (n
,f
)=>callee(tgt
,n
,f
);
797 if(!callee
.argcProxy
){
798 callee
.argcProxy = function(func
,sig
){
799 return function(...args
){
800 if(func
.length
!==arguments
.length
){
801 toss("Argument mismatch. Native signature is:",sig
);
803 return func
.apply(this, args
);
806 callee
.ondisposeRemoveFunc = function(){
807 if(this.__ondispose
){
809 this.__ondispose
.forEach(
811 if('number'===typeof v
){
812 try{wasm
.uninstallFunction(v
)}
814 }else{/*wasm function wrapper property*/
819 delete this.__ondispose
;
823 const sigN
= tgt
.memberSignature(name
),
824 memKey
= tgt
.memberKey(name
);
825 //log("installMethod",tgt, name, sigN);
826 if(!tgt
.__ondispose
){
827 T
.assert(undefined === tgt
.ondispose
);
828 tgt
.ondispose
= [callee
.ondisposeRemoveFunc
];
829 tgt
.__ondispose
= [];
831 const fProxy
= callee
.argcProxy(func
, sigN
);
832 const pFunc
= wasm
.installFunction(fProxy
, tgt
.memberSignature(name
, true));
835 ACHTUNG: function pointer IDs are from a different pool than
836 allocation IDs, starting at 1 and incrementing in steps of 1,
837 so if we set tgt[memKey] to those values, we'd very likely
838 later misinterpret them as plain old pointer addresses unless
839 unless we use some silly heuristic like "all values <5k are
840 presumably function pointers," or actually perform a function
841 lookup on every pointer to first see if it's a function. That
842 would likely work just fine, but would be kludgy.
844 It turns out that "all values less than X are functions" is
845 essentially how it works in wasm: a function pointer is
846 reported to the client as its index into the
847 __indirect_function_table.
849 So... once jaccwabyt can be told how to access the
850 function table, it could consider all pointer values less
851 than that table's size to be functions. As "real" pointer
852 values start much, much higher than the function table size,
853 that would likely work reasonably well. e.g. the object
854 pointer address for sqlite3's default VFS is (in this local
855 setup) 65104, whereas the function table has fewer than 600
858 const wrapperKey
= '$'+memKey
;
859 tgt
[wrapperKey
] = fProxy
;
860 tgt
.__ondispose
.push(pFunc
, wrapperKey
);
861 //log("tgt.__ondispose =",tgt.__ondispose);
862 return (n
,f
)=>callee(tgt
, n
, f
);
865 const installIOMethods
= function instm(iom
){
866 (iom
instanceof capi
.sqlite3_io_methods
) || toss("Invalid argument type.");
867 if(!instm
._requireFileArg
){
868 instm
._requireFileArg = function(arg
,methodName
){
869 arg
= capi
.sqlite3_file
.resolveToInstance(arg
);
871 err("sqlite3_io_methods::xClose() was passed a non-sqlite3_file.");
876 // https://sqlite.org/c3ref/io_methods.html
877 xClose
: /*i(P)*/function(f
){
878 /* int (*xClose)(sqlite3_file*) */
879 log("xClose(",f
,")");
880 if(!(f
= instm
._requireFileArg(f
,'xClose'))) return capi
.SQLITE_MISUSE
;
881 f
.dispose(/*noting that f has externally-owned memory*/);
884 xRead
: /*i(Ppij)*/function(f
,dest
,n
,offset
){
885 /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */
886 log("xRead(",arguments
,")");
887 if(!(f
= instm
._requireFileArg(f
))) return capi
.SQLITE_MISUSE
;
888 wasm
.heap8().fill(0, dest
+ offset
, n
);
891 xWrite
: /*i(Ppij)*/function(f
,dest
,n
,offset
){
892 /* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */
893 log("xWrite(",arguments
,")");
894 if(!(f
=instm
._requireFileArg(f
,'xWrite'))) return capi
.SQLITE_MISUSE
;
897 xTruncate
: /*i(Pj)*/function(f
){
898 /* int (*xTruncate)(sqlite3_file*, sqlite3_int64 size) */
899 log("xTruncate(",arguments
,")");
900 if(!(f
=instm
._requireFileArg(f
,'xTruncate'))) return capi
.SQLITE_MISUSE
;
903 xSync
: /*i(Pi)*/function(f
){
904 /* int (*xSync)(sqlite3_file*, int flags) */
905 log("xSync(",arguments
,")");
906 if(!(f
=instm
._requireFileArg(f
,'xSync'))) return capi
.SQLITE_MISUSE
;
909 xFileSize
: /*i(Pp)*/function(f
,pSz
){
910 /* int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize) */
911 log("xFileSize(",arguments
,")");
912 if(!(f
=instm
._requireFileArg(f
,'xFileSize'))) return capi
.SQLITE_MISUSE
;
913 wasm
.setMemValue(pSz
, 0/*file size*/);
916 xLock
: /*i(Pi)*/function(f
){
917 /* int (*xLock)(sqlite3_file*, int) */
918 log("xLock(",arguments
,")");
919 if(!(f
=instm
._requireFileArg(f
,'xLock'))) return capi
.SQLITE_MISUSE
;
922 xUnlock
: /*i(Pi)*/function(f
){
923 /* int (*xUnlock)(sqlite3_file*, int) */
924 log("xUnlock(",arguments
,")");
925 if(!(f
=instm
._requireFileArg(f
,'xUnlock'))) return capi
.SQLITE_MISUSE
;
928 xCheckReservedLock
: /*i(Pp)*/function(){
929 /* int (*xCheckReservedLock)(sqlite3_file*, int *pResOut) */
930 log("xCheckReservedLock(",arguments
,")");
933 xFileControl
: /*i(Pip)*/function(){
934 /* int (*xFileControl)(sqlite3_file*, int op, void *pArg) */
935 log("xFileControl(",arguments
,")");
936 return capi
.SQLITE_NOTFOUND
;
938 xSectorSize
: /*i(P)*/function(){
939 /* int (*xSectorSize)(sqlite3_file*) */
940 log("xSectorSize(",arguments
,")");
943 xDeviceCharacteristics
:/*i(P)*/function(){
944 /* int (*xDeviceCharacteristics)(sqlite3_file*) */
945 log("xDeviceCharacteristics(",arguments
,")");
951 Object
.keys(instm
._methods
).forEach(
952 (k
)=>installMethod(iom
, k
, instm
._methods
[k
])
954 }/*installIOMethods()*/;
956 const iom
= new sqlite3_io_methods
, sfile
= new sqlite3_file
;
957 const err
= console
.error
.bind(console
);
959 const IOM
= sqlite3_io_methods
, S3F
= sqlite3_file
;
960 //log("iom proto",iom,iom.constructor.prototype);
961 //log("sfile",sfile,sfile.constructor.prototype);
962 T
.assert(0===sfile
.$pMethods
).assert(iom
.pointer
> 0);
964 sfile
.$pMethods
= iom
.pointer
;
965 T
.assert(iom
.pointer
=== sfile
.$pMethods
)
966 .assert(IOM
.resolveToInstance(iom
))
967 .assert(undefined ===IOM
.resolveToInstance(sfile
))
968 .mustThrow(()=>IOM
.resolveToInstance(0,true))
969 .assert(S3F
.resolveToInstance(sfile
.pointer
))
970 .assert(undefined===S3F
.resolveToInstance(iom
))
971 .assert(iom
===IOM
.resolveToInstance(sfile
.$pMethods
));
972 T
.assert(0===iom
.$iVersion
);
973 installIOMethods(iom
);
974 T
.assert(1===iom
.$iVersion
);
975 //log("iom.__ondispose",iom.__ondispose);
976 T
.assert(Array
.isArray(iom
.__ondispose
)).assert(iom
.__ondispose
.length
>10);
979 T
.assert(undefined === iom
.__ondispose
);
982 const dVfs
= new sqlite3_vfs(capi
.sqlite3_vfs_find(null));
984 const SB
= sqlite3
.StructBinder
;
985 T
.assert(dVfs
instanceof SB
.StructType
)
986 .assert(dVfs
.pointer
)
987 .assert('sqlite3_vfs' === dVfs
.structName
)
988 .assert(!!dVfs
.structInfo
)
989 .assert(SB
.StructType
.hasExternalPointer(dVfs
))
990 .assert(dVfs
.$iVersion
>0)
991 .assert('number'===typeof dVfs
.$zName
)
992 .assert('number'===typeof dVfs
.$xSleep
)
993 .assert(wasm
.functionEntry(dVfs
.$xOpen
))
994 .assert(dVfs
.memberIsString('zName'))
995 .assert(dVfs
.memberIsString('$zName'))
996 .assert(!dVfs
.memberIsString('pAppData'))
997 .mustThrowMatching(()=>dVfs
.memberToJsString('xSleep'),
998 /Invalid member type signature for C-string/)
999 .mustThrowMatching(()=>dVfs
.memberSignature('nope'), /nope is not a mapped/)
1000 .assert('string' === typeof dVfs
.memberToJsString('zName'))
1001 .assert(dVfs
.memberToJsString('zName')===dVfs
.memberToJsString('$zName'))
1003 //log("Default VFS: @",dVfs.pointer);
1004 Object
.keys(sqlite3_vfs
.structInfo
.members
).forEach(function(mname
){
1005 const mk
= sqlite3_vfs
.memberKey(mname
), mbr
= sqlite3_vfs
.structInfo
.members
[mname
],
1006 addr
= dVfs
[mk
], prefix
= 'defaultVfs.'+mname
;
1007 if(1===mbr
.signature
.length
){
1008 let sep
= '?', val
= undefined;
1009 switch(mbr
.signature
[0]){
1010 // TODO: move this into an accessor, e.g. getPreferredValue(member)
1011 case 'i': case 'j': case 'f': case 'd': sep
= '='; val
= dVfs
[mk
]; break
1012 case 'p': case 'P': sep
= '@'; val
= dVfs
[mk
]; break;
1013 case 's': sep
= '=';
1014 val
= dVfs
.memberToJsString(mname
);
1017 //log(prefix, sep, val);
1019 //log(prefix," = funcptr @",addr, wasm.functionEntry(addr));
1024 T
.assert(undefined===dVfs
.pointer
);
1026 }/*StructBinder part 2*/)
1028 ////////////////////////////////////////////////////////////////////
1029 .t('sqlite3.wasm.pstack', function(sqlite3
){
1030 const P
= wasm
.pstack
;
1031 const isAllocErr
= (e
)=>e
instanceof sqlite3
.WasmAllocError
;
1032 const stack
= P
.pointer
;
1033 T
.assert(0===stack
% 8 /* must be 8-byte aligned */);
1035 const remaining
= P
.remaining
;
1036 T
.assert(P
.quota
>= 4096)
1037 .assert(remaining
=== P
.quota
)
1038 .mustThrowMatching(()=>P
.alloc(0), isAllocErr
)
1039 .mustThrowMatching(()=>P
.alloc(-1), isAllocErr
);
1040 let p1
= P
.alloc(12);
1041 T
.assert(p1
=== stack
- 16/*8-byte aligned*/)
1042 .assert(P
.pointer
=== p1
);
1043 let p2
= P
.alloc(7);
1044 T
.assert(p2
=== p1
-8/*8-byte aligned, stack grows downwards*/)
1045 .mustThrowMatching(()=>P
.alloc(remaining
), isAllocErr
)
1046 .assert(24 === stack
- p2
)
1047 .assert(P
.pointer
=== p2
);
1048 let n
= remaining
- (stack
- p2
);
1049 let p3
= P
.alloc(n
);
1050 T
.assert(p3
=== stack
-remaining
)
1051 .mustThrowMatching(()=>P
.alloc(1), isAllocErr
);
1056 T
.assert(P
.pointer
=== stack
);
1058 const [p1
, p2
, p3
] = P
.allocChunks(3,4);
1059 T
.assert(P
.pointer
=== stack
-16/*always rounded to multiple of 8*/)
1060 .assert(p2
=== p1
+ 4)
1061 .assert(p3
=== p2
+ 4);
1062 T
.mustThrowMatching(()=>P
.allocChunks(1024, 1024 * 16),
1063 (e
)=>e
instanceof sqlite3
.WasmAllocError
)
1068 T
.assert(P
.pointer
=== stack
);
1070 let [p1
, p2
, p3
] = P
.allocPtr(3,false);
1071 let sPos
= stack
-16/*always rounded to multiple of 8*/;
1072 T
.assert(P
.pointer
=== sPos
)
1073 .assert(p2
=== p1
+ 4)
1074 .assert(p3
=== p2
+ 4);
1075 [p1
, p2
, p3
] = P
.allocPtr(3);
1076 T
.assert(P
.pointer
=== sPos
-24/*3 x 8 bytes*/)
1077 .assert(p2
=== p1
+ 8)
1078 .assert(p3
=== p2
+ 8);
1080 T
.assert('number'===typeof p1
);
1086 ////////////////////////////////////////////////////////////////////
1087 ;/*end of C/WASM utils checks*/
1089 T.g('sqlite3_randomness()')
1090 .t('To memory buffer', function(sqlite3){
1091 const stack = wasm.pstack.pointer;
1094 const p = wasm.pstack.alloc(n);
1095 T.assert(0===wasm.getMemValue(p))
1096 .assert(0===wasm.getMemValue(p+n-1));
1097 T.assert(undefined === capi.sqlite3_randomness(n - 10, p));
1099 const heap = wasm.heap8u();
1100 for(j = 0; j < 10 && 0===check; ++j){
1101 check += heap[p + j];
1103 T.assert(check > 0);
1105 // Ensure that the trailing bytes were not modified...
1106 for(j = n - 10; j < n && 0===check; ++j){
1107 check += heap[p + j];
1109 T.assert(0===check);
1111 wasm.pstack.restore(stack);
1114 .t('To byte array', function(sqlite3){
1115 const ta = new Uint8Array(117);
1117 for(i=0; i<ta.byteLength && 0===n; ++i){
1121 .assert(ta === capi.sqlite3_randomness(ta));
1122 for(i=ta.byteLength-10; i<ta.byteLength && 0===n; ++i){
1126 const t0 = new Uint8Array(0);
1127 T.assert(t0 === capi.sqlite3_randomness(t0),
1128 "0-length array is a special case");
1130 ;/*end sqlite3_randomness() checks*/
1132 ////////////////////////////////////////////////////////////////////////
1134 .t('Create db', function(sqlite3
){
1135 const dbFile
= '/tester1.db';
1136 wasm
.sqlite3_wasm_vfs_unlink(0, dbFile
);
1137 const db
= this.db
= new sqlite3
.oo1
.DB(dbFile
);
1138 T
.assert(Number
.isInteger(db
.pointer
))
1139 .mustThrowMatching(()=>db
.pointer
=1, /read-only/)
1140 .assert(0===sqlite3
.capi
.sqlite3_extended_result_codes(db
.pointer
,1))
1141 .assert('main'===db
.dbName(0))
1142 .assert('string' === typeof db
.dbVfsName());
1143 // Custom db error message handling via sqlite3_prepare_v2/v3()
1144 let rc
= capi
.sqlite3_prepare_v3(db
.pointer
, {/*invalid*/}, -1, 0, null, null);
1145 T
.assert(capi
.SQLITE_MISUSE
=== rc
)
1146 .assert(0 === capi
.sqlite3_errmsg(db
.pointer
).indexOf("Invalid SQL"))
1147 .assert(dbFile
=== db
.dbFilename())
1148 .assert(!db
.dbFilename('nope'));
1151 ////////////////////////////////////////////////////////////////////
1152 .t('DB.Stmt', function(S
){
1153 let st
= this.db
.prepare(
1154 new TextEncoder('utf-8').encode("select 3 as a")
1156 //debug("statement =",st);
1158 T
.assert(Number
.isInteger(st
.pointer
))
1159 .mustThrowMatching(()=>st
.pointer
=1, /read-only/)
1160 .assert(1===this.db
.openStatementCount())
1161 .assert(!st
._mayGet
)
1162 .assert('a' === st
.getColumnName(0))
1163 .assert(1===st
.columnCount
)
1164 .assert(0===st
.parameterCount
)
1165 .mustThrow(()=>st
.bind(1,null))
1166 .assert(true===st
.step())
1167 .assert(3 === st
.get(0))
1168 .mustThrow(()=>st
.get(1))
1169 .mustThrow(()=>st
.get(0,~capi
.SQLITE_INTEGER
))
1170 .assert(3 === st
.get(0,capi
.SQLITE_INTEGER
))
1171 .assert(3 === st
.getInt(0))
1172 .assert('3' === st
.get(0,capi
.SQLITE_TEXT
))
1173 .assert('3' === st
.getString(0))
1174 .assert(3.0 === st
.get(0,capi
.SQLITE_FLOAT
))
1175 .assert(3.0 === st
.getFloat(0))
1176 .assert(3 === st
.get({}).a
)
1177 .assert(3 === st
.get([])[0])
1178 .assert(3 === st
.getJSON(0))
1179 .assert(st
.get(0,capi
.SQLITE_BLOB
) instanceof Uint8Array
)
1180 .assert(1===st
.get(0,capi
.SQLITE_BLOB
).length
)
1181 .assert(st
.getBlob(0) instanceof Uint8Array
)
1182 .assert('3'.charCodeAt(0) === st
.getBlob(0)[0])
1184 .assert(false===st
.step())
1185 .assert(!st
._mayGet
)
1187 T
.assert(0===capi
.sqlite3_strglob("*.txt", "foo.txt")).
1188 assert(0!==capi
.sqlite3_strglob("*.txt", "foo.xtx")).
1189 assert(0===capi
.sqlite3_strlike("%.txt", "foo.txt", 0)).
1190 assert(0!==capi
.sqlite3_strlike("%.txt", "foo.xtx", 0));
1194 T
.assert(!st
.pointer
)
1195 .assert(0===this.db
.openStatementCount());
1198 ////////////////////////////////////////////////////////////////////////
1199 .t('sqlite3_js_...()', function(){
1202 const vfsList
= capi
.sqlite3_js_vfs_list();
1203 T
.assert(vfsList
.length
>1);
1204 T
.assert('string'===typeof vfsList
[0]);
1205 //log("vfsList =",vfsList);
1206 for(const v
of vfsList
){
1207 T
.assert('string' === typeof v
)
1208 .assert(capi
.sqlite3_vfs_find(v
) > 0);
1212 Trivia: the magic db name ":memory:" does not actually use the
1213 "memdb" VFS unless "memdb" is _explicitly_ provided as the VFS
1214 name. Instead, it uses the default VFS with an in-memory btree.
1215 Thus this.db's VFS may not be memdb even though it's an in-memory
1218 const pVfsMem
= capi
.sqlite3_vfs_find('memdb'),
1219 pVfsDflt
= capi
.sqlite3_vfs_find(0),
1220 pVfsDb
= capi
.sqlite3_js_db_vfs(db
.pointer
);
1221 T
.assert(pVfsMem
> 0)
1222 .assert(pVfsDflt
> 0)
1224 .assert(pVfsMem
!== pVfsDflt
1225 /* memdb lives on top of the default vfs */)
1226 .assert(pVfsDb
=== pVfsDflt
|| pVfsdb
=== pVfsMem
)
1228 /*const vMem = new capi.sqlite3_vfs(pVfsMem),
1229 vDflt = new capi.sqlite3_vfs(pVfsDflt),
1230 vDb = new capi.sqlite3_vfs(pVfsDb);*/
1231 const duv
= capi
.sqlite3_js_db_uses_vfs
;
1232 T
.assert(pVfsDflt
=== duv(db
.pointer
, 0)
1233 || pVfsMem
=== duv(db
.pointer
,0))
1234 .assert(!duv(db
.pointer
, "foo"))
1236 }/*sqlite3_js_...()*/)
1238 ////////////////////////////////////////////////////////////////////
1239 .t('Table t', function(sqlite3
){
1243 sql
:['CREATE TABLE t(a,b);',
1244 // ^^^ using TEMP TABLE breaks the db export test
1245 "INSERT INTO t(a,b) VALUES(1,2),(3,4),",
1246 "(?,?),('blob',X'6869')"/*intentionally missing semicolon to test for
1247 off-by-one bug in string-to-WASM conversion*/],
1251 //debug("Exec'd SQL:", list);
1253 .assert(2 === list
.length
)
1254 .assert('string'===typeof list
[1])
1255 .assert(4===db
.changes());
1256 if(wasm
.bigIntEnabled
){
1257 T
.assert(4n
===db
.changes(false,true));
1259 let blob
= db
.selectValue("select b from t where a='blob'");
1260 T
.assert(blob
instanceof Uint8Array
).
1261 assert(0x68===blob
[0] && 0x69===blob
[1]);
1263 let counter
= 0, colNames
= [];
1265 db
.exec(new TextEncoder('utf-8').encode("SELECT a a, b b FROM t"),{
1268 columnNames
: colNames
,
1269 callback: function(row
,stmt
){
1271 T
.assert((row
.a
%2 && row
.a
<6) || 'blob'===row
.a
);
1274 T
.assert(2 === colNames
.length
)
1275 .assert('a' === colNames
[0])
1276 .assert(4 === counter
)
1277 .assert(4 === list
.length
);
1279 db
.exec("SELECT a a, b b FROM t",{
1281 callback: function(row
,stmt
){
1283 T
.assert(Array
.isArray(row
))
1284 .assert((0===row
[1]%2 && row
[1]<7)
1285 || (row
[1] instanceof Uint8Array
));
1288 T
.assert(8 === counter
);
1289 T
.assert(Number
.MIN_SAFE_INTEGER
===
1290 db
.selectValue("SELECT "+Number
.MIN_SAFE_INTEGER
)).
1291 assert(Number
.MAX_SAFE_INTEGER
===
1292 db
.selectValue("SELECT "+Number
.MAX_SAFE_INTEGER
));
1293 if(wasm
.bigIntEnabled
&& haveWasmCTests()){
1294 const mI
= wasm
.xCall('sqlite3_wasm_test_int64_max');
1295 const b
= BigInt(Number
.MAX_SAFE_INTEGER
* 2);
1296 T
.assert(b
=== db
.selectValue("SELECT "+b
)).
1297 assert(b
=== db
.selectValue("SELECT ?", b
)).
1298 assert(mI
== db
.selectValue("SELECT $x", {$x
:mI
}));
1300 /* Curiously, the JS spec seems to be off by one with the definitions
1301 of MIN/MAX_SAFE_INTEGER:
1303 https://github.com/emscripten-core/emscripten/issues/17391 */
1304 T.mustThrow(()=>db.selectValue("SELECT "+(Number.MAX_SAFE_INTEGER+1))).
1305 mustThrow(()=>db.selectValue("SELECT "+(Number.MIN_SAFE_INTEGER-1)));
1308 let st = db.prepare("update t set b=:b where a='blob'");
1310 const ndx = st.getParamIndex(':b');
1312 st.bindAsBlob(ndx, "ima blob").reset(true);
1318 db.prepare("/*empty SQL*/");
1319 toss("Must not be reached
.");
1321 T.assert(e instanceof sqlite3.SQLite3Error)
1322 .assert(0==e.message.indexOf('Cannot prepare empty'));
1326 ////////////////////////////////////////////////////////////////////////
1327 .t('selectArray/Object()', function(sqlite3){
1329 let rc = db.selectArray('select a, b from t where a=?', 5);
1330 T.assert(Array.isArray(rc))
1331 .assert(2===rc.length)
1332 .assert(5===rc[0] && 6===rc[1]);
1333 rc = db.selectArray('select a, b from t where b=-1');
1334 T.assert(undefined === rc);
1335 rc = db.selectObject('select a A, b b from t where b=?', 6);
1336 T.assert(rc && 'object'===typeof rc)
1339 rc = db.selectArray('select a, b from t where b=-1');
1340 T.assert(undefined === rc);
1343 ////////////////////////////////////////////////////////////////////////
1344 .t('sqlite3_js_db_export()', function(){
1346 const xp = capi.sqlite3_js_db_export(db.pointer);
1347 T.assert(xp instanceof Uint8Array)
1348 .assert(xp.byteLength>0)
1349 .assert(0 === xp.byteLength % 512);
1350 }/*sqlite3_js_db_export()*/)
1352 ////////////////////////////////////////////////////////////////////
1353 .t('Scalar UDFs', function(sqlite3){
1355 db.createFunction("foo
",(pCx,a,b)=>a+b);
1356 T.assert(7===db.selectValue("select
foo(3,4)")).
1357 assert(5===db.selectValue("select
foo(3,?)",2)).
1358 assert(5===db.selectValue("select
foo(?,?2)",[1,4])).
1359 assert(5===db.selectValue("select
foo($a
,$b
)",{$a:0,$b:5}));
1360 db.createFunction("bar
", {
1362 xFunc: (pCx,...args)=>{
1364 for(const v of args) rc += v;
1369 xFunc: (pCx,arg)=>arg
1371 T.assert(0===db.selectValue("select
bar()")).
1372 assert(1===db.selectValue("select
bar(1)")).
1373 assert(3===db.selectValue("select
bar(1,2)")).
1374 assert(-1===db.selectValue("select
bar(1,2,-4)")).
1375 assert('hi' === db.selectValue("select
asis('hi')")).
1376 assert('hi' === db.selectValue("select
?",'hi')).
1377 assert(null === db.selectValue("select
null")).
1378 assert(null === db.selectValue("select
asis(null)")).
1379 assert(1 === db.selectValue("select
?",1)).
1380 assert(2 === db.selectValue("select
?",[2])).
1381 assert(3 === db.selectValue("select
$a
",{$a:3})).
1382 assert(T.eqApprox(3.1,db.selectValue("select
3.0 + 0.1"))).
1383 assert(T.eqApprox(1.3,db.selectValue("select
asis(1 + 0.3)")));
1385 let blobArg = new Uint8Array(2);
1386 blobArg.set([0x68, 0x69], 0);
1387 let blobRc = db.selectValue("select
asis(?1)", blobArg);
1388 T.assert(blobRc instanceof Uint8Array).
1389 assert(2 === blobRc.length).
1390 assert(0x68==blobRc[0] && 0x69==blobRc[1]);
1391 blobRc = db.selectValue("select
asis(X
'6869')");
1392 T.assert(blobRc instanceof Uint8Array).
1393 assert(2 === blobRc.length).
1394 assert(0x68==blobRc[0] && 0x69==blobRc[1]);
1396 blobArg = new Int8Array(2);
1397 blobArg.set([0x68, 0x69]);
1398 //debug("blobArg
=",blobArg);
1399 blobRc = db.selectValue("select
asis(?1)", blobArg);
1400 T.assert(blobRc instanceof Uint8Array).
1401 assert(2 === blobRc.length);
1402 //debug("blobRc
=",blobRc);
1403 T.assert(0x68==blobRc[0] && 0x69==blobRc[1]);
1406 ////////////////////////////////////////////////////////////////////
1408 name: 'Aggregate UDFs',
1409 test: function(sqlite3){
1411 const sjac = capi.sqlite3_js_aggregate_context;
1415 const ac = sjac(pCtx, 4);
1416 wasm.setMemValue(ac, wasm.getMemValue(ac,'i32') + Number(n), 'i32');
1419 const ac = sjac(pCtx, 0);
1420 return ac ? wasm.getMemValue(ac,'i32') : 0;
1423 let v = db.selectValue([
1425 "select
3 union all select
5 union all select
7",
1426 ") select
summer(v
), summer(v
+1) from cte
"
1427 /* ------------------^^^^^^^^^^^ ensures that we're handling
1428 sqlite3_aggregate_context() properly. */
1431 T.mustThrowMatching(()=>db.selectValue("select
summer(1,2)"),
1432 /wrong number of arguments/);
1437 xStep: (pCtx, ...args)=>{
1438 const ac = sjac(pCtx, 4);
1439 let sum = wasm.getMemValue(ac, 'i32');
1440 for(const v of args) sum += Number(v);
1441 wasm.setMemValue(ac, sum, 'i32');
1444 const ac = sjac(pCtx, 0);
1445 capi.sqlite3_result_int( pCtx, ac ? wasm.getMemValue(ac,'i32') : 0 );
1446 // xFinal() may either return its value directly or call
1447 // sqlite3_result_xyz() and return undefined. Both are
1448 // functionally equivalent.
1451 T.assert(18===db.selectValue('select summerN(1,8,9), summerN(2,3,4)'));
1452 T.mustThrowMatching(()=>{
1453 db.createFunction('nope',{
1454 xFunc: ()=>{}, xStep: ()=>{}
1456 }, /scalar or aggregate\?/);
1457 T.mustThrowMatching(()=>{
1458 db.createFunction('nope',{xStep: ()=>{}});
1459 }, /Missing xFinal/);
1460 T.mustThrowMatching(()=>{
1461 db.createFunction('nope',{xFinal: ()=>{}});
1462 }, /Missing xStep/);
1463 T.mustThrowMatching(()=>{
1464 db.createFunction('nope',{});
1465 }, /Missing function-type properties/);
1466 T.mustThrowMatching(()=>{
1467 db.createFunction('nope',{xFunc:()=>{}, xDestroy:'nope'});
1468 }, /xDestroy property must be a function/);
1469 T.mustThrowMatching(()=>{
1470 db.createFunction('nope',{xFunc:()=>{}, pApp:'nope'});
1471 }, /Invalid value for pApp/);
1473 }/*aggregate UDFs*/)
1475 ////////////////////////////////////////////////////////////////////////
1477 name: 'Aggregate UDFs (64-bit)',
1478 predicate: ()=>wasm.bigIntEnabled,
1479 test: function(sqlite3){
1481 const sjac = capi.sqlite3_js_aggregate_context;
1485 const ac = sjac(pCtx, 8);
1486 wasm.setMemValue(ac, wasm.getMemValue(ac,'i64') + BigInt(n), 'i64');
1489 const ac = sjac(pCtx, 0);
1490 return ac ? wasm.getMemValue(ac,'i64') : 0n;
1493 let v = db.selectValue([
1495 "select
9007199254740991 union all select
1 union all select
2",
1496 ") select
summer64(v
), summer64(v
+1) from cte
"
1498 T.assert(9007199254740994n===v);
1500 }/*aggregate UDFs*/)
1502 ////////////////////////////////////////////////////////////////////
1504 name: 'Window UDFs',
1506 /* Example window function, table, and results taken from:
1507 https://sqlite.org/windowfunctions.html#udfwinfunc */
1509 const sjac = (cx,n=4)=>capi.sqlite3_js_aggregate_context(cx,n);
1510 const xValueFinal = (pCtx)=>{
1511 const ac = sjac(pCtx, 0);
1512 return ac ? wasm.getMemValue(ac,'i32') : 0;
1514 const xStepInverse = (pCtx, n)=>{
1515 const ac = sjac(pCtx);
1516 wasm.setMemValue(ac, wasm.getMemValue(ac,'i32') + Number(n), 'i32');
1520 xStep: (pCtx, n)=>xStepInverse(pCtx, n),
1521 xInverse: (pCtx, n)=>xStepInverse(pCtx, -n),
1522 xFinal: xValueFinal,
1526 "CREATE TEMP TABLE
twin(x
, y
); INSERT INTO twin VALUES
",
1527 "('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)"
1530 returnValue: 'resultRows',
1532 "SELECT x
, winsumint(y
) OVER (",
1533 "ORDER BY x ROWS BETWEEN
1 PRECEDING AND
1 FOLLOWING
",
1535 "FROM twin ORDER BY x
;"
1538 T.assert(Array.isArray(rc))
1539 .assert(5 === rc.length);
1541 for(const row of rc){
1543 case 1: T.assert('a'===row[0] && 9===row[1]); break;
1544 case 2: T.assert('b'===row[0] && 12===row[1]); break;
1545 case 3: T.assert('c'===row[0] && 16===row[1]); break;
1546 case 4: T.assert('d'===row[0] && 12===row[1]); break;
1547 case 5: T.assert('e'===row[0] && 9===row[1]); break;
1548 default: toss("Too many rows to window
function.");
1551 const resultRows = [];
1554 returnValue: 'resultRows',
1556 "SELECT x
, winsumint(y
) OVER (",
1557 "ORDER BY x ROWS BETWEEN
1 PRECEDING AND
1 FOLLOWING
",
1559 "FROM twin ORDER BY x
;"
1562 T.assert(rc === resultRows)
1563 .assert(5 === rc.length);
1566 returnValue: 'saveSql',
1567 sql: "select
1; select
2; -- empty
\n; select
3"
1569 T.assert(Array.isArray(rc))
1570 .assert(3===rc.length)
1571 .assert('select 1;' === rc[0])
1572 .assert('select 2;' === rc[1])
1573 .assert('-- empty\n; select 3' === rc[2]
1574 /* Strange but true. */);
1576 T.mustThrowMatching(()=>{
1577 db.exec({sql:'', returnValue: 'nope'});
1578 }, /^Invalid returnValue/);
1580 db.exec("DROP TABLE twin
");
1584 ////////////////////////////////////////////////////////////////////
1585 .t("ATTACH
", function(){
1587 const resultRows = [];
1589 sql:new TextEncoder('utf-8').encode([
1590 // ^^^ testing string-vs-typedarray handling in exec()
1591 "attach
'session' as foo
;",
1592 "create table foo
.bar(a
);",
1593 "insert into foo
.bar(a
) values(1),(2),(3);",
1594 "select a
from foo
.bar order by a
;"
1599 T.assert(3===resultRows.length)
1600 .assert(2===resultRows[1]);
1601 T.assert(2===db.selectValue('select a from foo.bar where a>1 order by a'));
1602 let colCount = 0, rowCount = 0;
1603 const execCallback = function(pVoid, nCols, aVals, aNames){
1606 T.assert(2===aVals.length)
1607 .assert(2===aNames.length)
1608 .assert(+(aVals[1]) === 2 * +(aVals[0]));
1610 let rc = capi.sqlite3_exec(
1611 db.pointer, "select a
, a
*2 from foo
.bar
", execCallback,
1614 T.assert(0===rc).assert(3===rowCount).assert(2===colCount);
1615 rc = capi.sqlite3_exec(
1616 db.pointer, "select a
from foo
.bar
", ()=>{
1617 tossQuietly("Testing throwing
from exec() callback
.");
1620 T.assert(capi.SQLITE_ABORT === rc);
1621 db.exec("detach foo
");
1622 T.mustThrow(()=>db.exec("select
* from foo
.bar
"));
1625 ////////////////////////////////////////////////////////////////////
1627 name: 'C-side WASM tests (if compiled in)',
1628 predicate: haveWasmCTests,
1630 const w = wasm, db = this.db;
1631 const stack = w.scopedAllocPush();
1633 const origValue = 512;
1634 const ptrValType = 'i32';
1636 ptrInt = w.scopedAlloc(4);
1637 w.setMemValue(ptrInt,origValue, ptrValType);
1638 const cf = w.xGet('sqlite3_wasm_test_intptr');
1639 const oldPtrInt = ptrInt;
1640 //log('ptrInt',ptrInt);
1641 //log('getMemValue(ptrInt)',w.getMemValue(ptrInt));
1642 T.assert(origValue === w.getMemValue(ptrInt, ptrValType));
1643 const rc = cf(ptrInt);
1644 //log('cf(ptrInt)',rc);
1645 //log('ptrInt',ptrInt);
1646 //log('getMemValue(ptrInt)',w.getMemValue(ptrInt,ptrValType));
1647 T.assert(2*origValue === rc).
1648 assert(rc === w.getMemValue(ptrInt,ptrValType)).
1649 assert(oldPtrInt === ptrInt);
1650 const pi64 = w.scopedAlloc(8)/*ptr to 64-bit integer*/;
1651 const o64 = 0x010203040506/*>32-bit integer*/;
1652 const ptrType64 = 'i64';
1653 if(w.bigIntEnabled){
1654 w.setMemValue(pi64, o64, ptrType64);
1655 //log("pi64
=",pi64, "o64
= 0x
",o64.toString(16), o64);
1656 const v64 = ()=>w.getMemValue(pi64,ptrType64)
1657 //log("getMemValue(pi64
)",v64());
1658 T.assert(v64() == o64);
1659 //T.assert(o64 === w.getMemValue(pi64, ptrType64));
1660 const cf64w = w.xGet('sqlite3_wasm_test_int64ptr');
1662 //log("getMemValue(pi64
)",v64());
1663 T.assert(v64() == BigInt(2 * o64));
1665 T.assert(v64() == BigInt(4 * o64));
1667 const biTimes2 = w.xGet('sqlite3_wasm_test_int64_times2');
1668 T.assert(BigInt(2 * o64) ===
1669 biTimes2(BigInt(o64)/*explicit conv. required to avoid TypeError
1670 in the call :/ */));
1672 const pMin = w.scopedAlloc(16);
1673 const pMax = pMin + 8;
1674 const g64 = (p)=>w.getMemValue(p,ptrType64);
1675 w.setMemValue(pMin, 0, ptrType64);
1676 w.setMemValue(pMax, 0, ptrType64);
1678 w.xCall('sqlite3_wasm_test_int64_min'),
1679 w.xCall('sqlite3_wasm_test_int64_max')
1681 T.assert(minMaxI64[0] < BigInt(Number.MIN_SAFE_INTEGER)).
1682 assert(minMaxI64[1] > BigInt(Number.MAX_SAFE_INTEGER));
1683 //log("int64_min
/max() =",minMaxI64, typeof minMaxI64[0]);
1684 w.xCall('sqlite3_wasm_test_int64_minmax', pMin, pMax);
1685 T.assert(g64(pMin) === minMaxI64[0], "int64 mismatch
").
1686 assert(g64(pMax) === minMaxI64[1], "int64 mismatch
");
1687 //log("pMin
",g64(pMin), "pMax
",g64(pMax));
1688 w.setMemValue(pMin, minMaxI64[0], ptrType64);
1689 T.assert(g64(pMin) === minMaxI64[0]).
1690 assert(minMaxI64[0] === db.selectValue("select
?",g64(pMin))).
1691 assert(minMaxI64[1] === db.selectValue("select
?",g64(pMax)));
1692 const rxRange = /too big/;
1693 T.mustThrowMatching(()=>{db.prepare("select
?").bind(minMaxI64[0] - BigInt(1))},
1695 mustThrowMatching(()=>{db.prepare("select
?").bind(minMaxI64[1] + BigInt(1))},
1696 (e)=>rxRange.test(e.message));
1698 log("No BigInt support
. Skipping related tests
.");
1699 log("\"The problem
\" here is that we can manipulate
, at the
byte level
,",
1700 "heap memory to
set 64-bit values
, but we can
't get those values",
1701 "back into JS because of the lack of 64-bit integer support.");
1704 const x = w.scopedAlloc(1), y = w.scopedAlloc(1), z = w.scopedAlloc(1);
1705 //log("x=",x,"y=",y,"z=",z); // just looking at the alignment
1706 w.scopedAllocPop(stack);
1709 }/* jaccwabyt-specific tests */)
1711 .t('Close db
', function(){
1712 T.assert(this.db).assert(Number.isInteger(this.db.pointer));
1713 wasm.exports.sqlite3_wasm_db_reset(this.db.pointer);
1715 T.assert(!this.db.pointer);
1717 ;/* end of oo1 checks */
1719 ////////////////////////////////////////////////////////////////////////
1721 .t('kvvfs sanity checks
', function(sqlite3){
1724 !capi.sqlite3_vfs_find('kvvfs
'),
1725 "Expecting kvvfs to be unregistered."
1727 log("kvvfs is (correctly) unavailable in a Worker.");
1730 const filename = 'session
';
1731 const pVfs = capi.sqlite3_vfs_find('kvvfs
');
1733 const JDb = sqlite3.oo1.JsStorageDb;
1734 const unlink = ()=>JDb.clearStorage(filename);
1736 let db = new JDb(filename);
1739 'create table
kvvfs(a
);',
1740 'insert into
kvvfs(a
) values(1),(2),(3)'
1742 T.assert(3 === db.selectValue('select
count(*) from kvvfs
'));
1744 db = new JDb(filename);
1745 db.exec('insert into
kvvfs(a
) values(4),(5),(6)');
1746 T.assert(6 === db.selectValue('select
count(*) from kvvfs
'));
1751 }/*kvvfs sanity checks*/)
1752 ;/* end kvvfs tests */
1754 ////////////////////////////////////////////////////////////////////////
1755 T.g('OPFS (Worker thread only and only
in supported browsers
)',
1756 (sqlite3)=>{return !!sqlite3.opfs})
1758 name: 'OPFS sanity checks
',
1759 test: async function(sqlite3){
1760 const opfs = sqlite3.opfs;
1761 const filename = 'sqlite3
-tester1
.db
';
1762 const pVfs = capi.sqlite3_vfs_find('opfs
');
1764 const unlink = (fn=filename)=>wasm.sqlite3_wasm_vfs_unlink(pVfs,fn);
1766 let db = new opfs.OpfsDb(filename);
1769 'create table
p(a
);',
1770 'insert into
p(a
) values(1),(2),(3)'
1772 T.assert(3 === db.selectValue('select
count(*) from p
'));
1774 db = new opfs.OpfsDb(filename);
1775 db.exec('insert into
p(a
) values(4),(5),(6)');
1776 T.assert(6 === db.selectValue('select
count(*) from p
'));
1783 // Sanity-test sqlite3_wasm_vfs_create_file()...
1787 T.assert(!(await opfs.entryExists(filename)));
1788 let rc = wasm.sqlite3_wasm_vfs_create_file(
1789 pVfs, filename, null, fSize
1792 .assert(await opfs.entryExists(filename));
1793 const fh = await opfs.rootDirectory.getFileHandle(filename);
1794 sh = await fh.createSyncAccessHandle();
1795 T.assert(fSize === await sh.getSize());
1797 if(sh) await sh.close();
1802 // Some sanity checks of the opfs utility functions...
1803 const testDir = '/sqlite3
-opfs
-'+opfs.randomFilename(12);
1804 const aDir = testDir+'/test/dir';
1805 T.assert(await opfs.mkdir(aDir), "mkdir failed")
1806 .assert(await opfs.mkdir(aDir), "mkdir must pass if the dir exists")
1807 .assert(!(await opfs.unlink(testDir+'/test
')), "delete 1 should have failed (dir not empty)")
1808 .assert((await opfs.unlink(testDir+'/test/dir')), "delete 2 failed")
1809 .assert(!(await opfs.unlink(testDir+'/test/dir')),
1810 "delete 2b should have failed (dir already deleted)")
1811 .assert((await opfs.unlink(testDir, true)), "delete 3 failed")
1812 .assert(!(await opfs.entryExists(testDir)),
1813 "entryExists(",testDir,") should have failed");
1815 }/*OPFS sanity checks*/)
1816 ;/* end OPFS tests */
1818 ////////////////////////////////////////////////////////////////////////
1819 log("Loading and initializing sqlite3 WASM module...");
1822 If sqlite3.js is in a directory other than this script, in order
1823 to get sqlite3.js to resolve sqlite3.wasm properly, we have to
1824 explicitly tell it where sqlite3.js is being loaded from. We do
1825 that by passing the `sqlite3.dir=theDirName` URL argument to
1826 _this_ script. That URL argument will be seen by the JS/WASM
1827 loader and it will adjust the sqlite3.wasm path accordingly. If
1828 sqlite3.js/.wasm are in the same directory as this script then
1831 URL arguments passed as part
of the filename via
importScripts()
1832 are simply lost
, and such scripts see the self
.location
of
1835 let sqlite3Js
= 'sqlite3.js';
1836 const urlParams
= new URL(self
.location
.href
).searchParams
;
1837 if(urlParams
.has('sqlite3.dir')){
1838 sqlite3Js
= urlParams
.get('sqlite3.dir') + '/' + sqlite3Js
;
1840 importScripts(sqlite3Js
);
1842 self
.sqlite3InitModule({
1845 }).then(function(sqlite3
){
1846 //console.log('sqlite3 =',sqlite3);
1847 log("Done initializing WASM/JS bits. Running tests...");
1848 capi
= sqlite3
.capi
;
1849 wasm
= sqlite3
.wasm
;
1850 log("sqlite3 version:",capi
.sqlite3_libversion(),
1851 capi
.sqlite3_sourceid());
1852 if(wasm
.bigIntEnabled
){
1853 log("BigInt/int64 support is enabled.");
1855 logClass('warning',"BigInt/int64 support is disabled.");
1857 if(haveWasmCTests()){
1858 log("sqlite3_wasm_test_...() APIs are available.");
1860 logClass('warning',"sqlite3_wasm_test_...() APIs unavailable.");
1862 TestUtil
.runTests(sqlite3
);