From: stephan Date: Thu, 13 Oct 2022 08:03:31 +0000 (+0000) Subject: Port the first 180-odd unit tests from testing1.* into the new tester1.*. Fix a stray... X-Git-Tag: version-3.40.0~166 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d85d08398dbd06f6d8e1a96a58e6f538090c0fb9;p=thirdparty%2Fsqlite.git Port the first 180-odd unit tests from testing1.* into the new tester1.*. Fix a stray-keystroke-induced typo which broke pstack.allocChunks(). FossilOrigin-Name: ef689e33e464829f5cbe4ca24a53d9dba59abe74d3d80a37a91b93a4eccccf2d --- diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 61bbeabb15..10d09d7404 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -941,8 +941,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( */ allocChunks: (n,sz)=>{ const mem = wasm.pstack.alloc(n * sz); - const r -= []; + const rc = []; let i = 0, offset = 0; for(; i < n; offset = (sz * ++i)){ rc.push(mem + offset); diff --git a/ext/wasm/common/testing.css b/ext/wasm/common/testing.css index 8782e3862d..c8629a2c62 100644 --- a/ext/wasm/common/testing.css +++ b/ext/wasm/common/testing.css @@ -36,9 +36,13 @@ span.labeled-input { background-color: yellow; } .warning { color: firebrick; } +.green { color: darkgreen; } +.group-start { color: blue; } +.group-end { color: blue; } .input-wrapper { white-space: nowrap; } #test-output { border: 1px inset; + border-radius: 0.25em; padding: 0.25em; /*max-height: 30em;*/ overflow: auto; diff --git a/ext/wasm/tester1-worker.html b/ext/wasm/tester1-worker.html index 32af0f47c9..bb5de0762f 100644 --- a/ext/wasm/tester1-worker.html +++ b/ext/wasm/tester1-worker.html @@ -15,12 +15,14 @@

sqlite3 WASM/JS tester #1 (Worker thread)

+
diff --git a/ext/wasm/tester1.js b/ext/wasm/tester1.js index d4b6cf7d1a..ae4cc0c949 100644 --- a/ext/wasm/tester1.js +++ b/ext/wasm/tester1.js @@ -11,6 +11,23 @@ *********************************************************************** Main functional and regression tests for the sqlite3 WASM API. + + This mini-framework works like so: + + This script adds a series of test groups, each of which contains an + arbitrary number of tests, into a queue. After loading of the + sqlite3 WASM/JS module is complete, that queue is processed. If any + given test fails, the whole thing fails. This script is built such + that it can run from the main UI thread or worker thread. Test + groups and individual tests can be assigned a predicate function + which determines whether to run them or not, and this is + specifically intended to be used to toggle certain tests on or off + for the main/worker threads. + + Each test group defines a state object which gets applied as each + test function's `this`. Test functions can use that to, e.g., set up + a db in an early test and close it in a later test. Each test gets + passed the sqlite3 namespace object as its only argument. */ 'use strict'; (function(){ @@ -19,8 +36,16 @@ on whether we are running in a worker thread or the main (UI) thread. */ - let logHtml; + let logClass; + /* Predicate for tests/groups. */ const isUIThread = ()=>(self.window===self && self.document); + /* Predicate for tests/groups. */ + const isWorker = ()=>!isUIThread(); + /* Predicate for tests/groups. */ + const testIsTodo = ()=>false; + const haveJaccwabytTests = function(){ + return !!wasm.exports.jaccwabyt_test_int64_max; + }; const mapToString = (v)=>{ switch(typeof v){ case 'number': case 'string': case 'boolean': @@ -29,20 +54,28 @@ default: break; } if(null===v) return 'null'; + if(v instanceof Error){ + v = { + message: v.message, + stack: v.stack, + errorClass: v.name + }; + } return JSON.stringify(v,undefined,2); }; const normalizeArgs = (args)=>args.map(mapToString); if( isUIThread() ){ - console.log("Running UI thread."); - logHtml = function(cssClass,...args){ + console.log("Running in UI thread."); + const logTarget = document.querySelector('#test-output'); + logClass = function(cssClass,...args){ const ln = document.createElement('div'); if(cssClass) ln.classList.add(cssClass); ln.append(document.createTextNode(normalizeArgs(args).join(' '))); - document.body.append(ln); + logTarget.append(ln); }; }else{ /* Worker thread */ console.log("Running Worker thread."); - logHtml = function(cssClass,...args){ + logClass = function(cssClass,...args){ postMessage({ type:'log', payload:{cssClass, args: normalizeArgs(args)} @@ -51,21 +84,24 @@ } const log = (...args)=>{ //console.log(...args); - logHtml('',...args); + logClass('',...args); } const warn = (...args)=>{ console.warn(...args); - logHtml('warning',...args); + logClass('warning',...args); } const error = (...args)=>{ console.error(...args); - logHtml('error',...args); + logClass('error',...args); }; const toss = (...args)=>{ error(...args); throw new Error(args.join(' ')); }; + const tossQuietly = (...args)=>{ + throw new Error(args.join(' ')); + }; /** Helpers for writing sqlite3-specific tests. @@ -74,6 +110,8 @@ /** Running total of the number of tests run via this API. */ counter: 0, + /* Separator line for log messages. */ + separator: '------------------------------------------------------------', /** If expr is a function, it is called and its result is returned, coerced to a bool, else expr, coerced to @@ -155,32 +193,531 @@ if(!this.toBool(expr)) throw new Error(msg || "throwUnless() failed"); return this; }, - TestGroup: class { - constructor(name){ + eqApprox: (v1,v2,factor=0.05)=>(v1>=(v2-factor) && v1<=(v2+factor)), + TestGroup: (function(){ + let groupCounter = 0; + const TestGroup = function(name, predicate){ + this.number = ++groupCounter; this.name = name; + this.predicate = predicate; this.tests = []; - } - push(name,callback){ - } - }/*TestGroup*/, + }; + TestGroup.prototype = { + addTest: function(testObj){ + this.tests.push(testObj); + return this; + }, + run: async function(sqlite3){ + if(this.predicate && !this.predicate()){ + log("SKIPPING test group #"+this.number,this.name); + return; + } + log(TestUtil.separator); + logClass('group-start',"Group #"+this.number+':',this.name); + const indent = '....'; + const assertCount = TestUtil.counter; + const groupState = Object.create(null); + const skipped = []; + for(let i in this.tests){ + const t = this.tests[i]; + const n = this.number+"."+i; + if(t.predicate && !t.predicate()){ + logClass('warning',indent, n+': SKIPPING',t.name); + skipped.push( n+': '+t.name ); + }else{ + const tc = TestUtil.counter + log(indent, n+":", t.name); + await t.test.call(groupState, sqlite3); + //log(indent, indent, 'assertion count:',TestUtil.counter - tc); + } + } + logClass('green', + "Group #"+this.number,"assertion count:",(TestUtil.counter - assertCount)); + if(skipped.length){ + log("SKIPPED test(s) in group",this.number+":",skipped); + } + } + }; + return TestGroup; + })()/*TestGroup*/, testGroups: [], currentTestGroup: undefined, - addGroup: function(name){ - if(this.testGroups[name]){ - toss("Test group already exists:",name); - } + addGroup: function(name, predicate){ this.testGroups.push( this.currentTestGroup = new this.TestGroup(name) ); return this; }, addTest: function(name, callback){ - this.currentTestGroup.push(name, callback); + let predicate; + if(1===arguments.length){ + const opt = arguments[0]; + predicate = opt.predicate; + name = opt.name; + callback = opt.test; + } + this.currentTestGroup.addTest({ + name, predicate, test: callback + }); + return this; }, - runTests: function(){ - toss("TODO: runTests()"); + runTests: async function(sqlite3){ + return new Promise(async function(pok,pnok){ + try { + for(let g of this.testGroups){ + await g.run(sqlite3); + } + log(TestUtil.separator); + log("Done running tests. Total assertion count:",TestUtil.counter); + pok(); + }catch(e){ + error(e); + pnok(e); + } + }.bind(this)); } }/*TestUtil*/; - + const T = TestUtil; + T.g = T.addGroup; + T.t = T.addTest; + let capi, wasm/*assigned after module init*/; + //////////////////////////////////////////////////////////////////////// + // End of infrastructure setup. Now define the tests... + //////////////////////////////////////////////////////////////////////// + T.g('Basic sanity checks') + .t('Namespace object checks', function(sqlite3){ + const wasmCtypes = wasm.ctype; + T.assert(wasmCtypes.structs[0].name==='sqlite3_vfs'). + assert(wasmCtypes.structs[0].members.szOsFile.sizeof>=4). + assert(wasmCtypes.structs[1/*sqlite3_io_methods*/ + ].members.xFileSize.offset>0); + [ /* Spot-check a handful of constants to make sure they got installed... */ + 'SQLITE_SCHEMA','SQLITE_NULL','SQLITE_UTF8', + 'SQLITE_STATIC', 'SQLITE_DIRECTONLY', + 'SQLITE_OPEN_CREATE', 'SQLITE_OPEN_DELETEONCLOSE' + ].forEach((k)=>T.assert('number' === typeof capi[k])); + [/* Spot-check a few of the WASM API methods. */ + 'alloc', 'dealloc', 'installFunction' + ].forEach((k)=>T.assert(wasm[k] instanceof Function)); + + T.assert(capi.sqlite3_errstr(capi.SQLITE_IOERR_ACCESS).indexOf("I/O")>=0). + assert(capi.sqlite3_errstr(capi.SQLITE_CORRUPT).indexOf('malformed')>0). + assert(capi.sqlite3_errstr(capi.SQLITE_OK) === 'not an error'); + + try { + throw new sqlite3.WasmAllocError; + }catch(e){ + T.assert(e instanceof Error) + .assert(e instanceof sqlite3.WasmAllocError); + } + }) + .t('strglob/strlike', function(sqlite3){ + T.assert(0===capi.sqlite3_strglob("*.txt", "foo.txt")). + assert(0!==capi.sqlite3_strglob("*.txt", "foo.xtx")). + assert(0===capi.sqlite3_strlike("%.txt", "foo.txt", 0)). + assert(0!==capi.sqlite3_strlike("%.txt", "foo.xtx", 0)); + }) + + .t('sqlite3.capi.wasm.pstack', function(sqlite3){ + const w = sqlite3.capi.wasm, P = w.pstack; + const isAllocErr = (e)=>e instanceof sqlite3.WasmAllocError; + const stack = P.pointer; + T.assert(0===stack % 8 /* must be 8-byte aligned */); + try{ + const remaining = P.remaining; + T.assert(P.quota >= 4096) + .assert(remaining === P.quota) + .mustThrowMatching(()=>P.alloc(0), isAllocErr) + .mustThrowMatching(()=>P.alloc(-1), isAllocErr); + let p1 = P.alloc(12); + T.assert(p1 === stack - 16/*8-byte aligned*/) + .assert(P.pointer === p1); + let p2 = P.alloc(7); + T.assert(p2 === p1-8/*8-byte aligned, stack grows downwards*/) + .mustThrowMatching(()=>P.alloc(remaining), isAllocErr) + .assert(24 === stack - p2) + .assert(P.pointer === p2); + let n = remaining - (stack - p2); + let p3 = P.alloc(n); + T.assert(p3 === stack-remaining) + .mustThrowMatching(()=>P.alloc(1), isAllocErr); + }finally{ + P.restore(stack); + } + + T.assert(P.pointer === stack); + try { + const [p1, p2, p3] = P.allocChunks(3,4); + T.assert(P.pointer === stack-16/*always rounded to multiple of 8*/) + .assert(p2 === p1 + 4) + .assert(p3 === p2 + 4); + T.mustThrowMatching(()=>P.allocChunks(1024, 1024 * 16), + (e)=>e instanceof sqlite3.WasmAllocError) + }finally{ + P.restore(stack); + } + + T.assert(P.pointer === stack); + try { + let [p1, p2, p3] = P.allocPtr(3,false); + let sPos = stack-16/*always rounded to multiple of 8*/; + T.assert(P.pointer === sPos) + .assert(p2 === p1 + 4) + .assert(p3 === p2 + 4); + [p1, p2, p3] = P.allocPtr(3); + T.assert(P.pointer === sPos-24/*3 x 8 bytes*/) + .assert(p2 === p1 + 8) + .assert(p3 === p2 + 8); + p1 = P.allocPtr(); + T.assert('number'===typeof p1); + }finally{ + P.restore(stack); + } + }/*pstack tests*/) + ;/*end of basic sanity checks*/ + + //////////////////////////////////////////////////////////////////////// + T.g('sqlite3.oo1 sanity checks') + .t('Create db', function(sqlite3){ + const db = this.db = new sqlite3.oo1.DB(); + T.assert(Number.isInteger(db.pointer)). + mustThrowMatching(()=>db.pointer=1, /read-only/). + assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1)). + assert('main'===db.dbName(0)); + + // Custom db error message handling via sqlite3_prepare_v2/v3() + let rc = capi.sqlite3_prepare_v3(db.pointer, {/*invalid*/}, -1, 0, null, null); + T.assert(capi.SQLITE_MISUSE === rc) + .assert(0 === capi.sqlite3_errmsg(db.pointer).indexOf("Invalid SQL")); + }) + .t('DB.Stmt sanity checks', function(S){ + let pId; + let st = this.db.prepare( + new TextEncoder('utf-8').encode("select 3 as a") + ); + //debug("statement =",st); + try { + T.assert(Number.isInteger(st.pointer)) + .mustThrowMatching(()=>st.pointer=1, /read-only/) + .assert(1===this.db.openStatementCount()) + .assert(!st._mayGet) + .assert('a' === st.getColumnName(0)) + .assert(1===st.columnCount) + .assert(0===st.parameterCount) + .mustThrow(()=>st.bind(1,null)) + .assert(true===st.step()) + .assert(3 === st.get(0)) + .mustThrow(()=>st.get(1)) + .mustThrow(()=>st.get(0,~capi.SQLITE_INTEGER)) + .assert(3 === st.get(0,capi.SQLITE_INTEGER)) + .assert(3 === st.getInt(0)) + .assert('3' === st.get(0,capi.SQLITE_TEXT)) + .assert('3' === st.getString(0)) + .assert(3.0 === st.get(0,capi.SQLITE_FLOAT)) + .assert(3.0 === st.getFloat(0)) + .assert(3 === st.get({}).a) + .assert(3 === st.get([])[0]) + .assert(3 === st.getJSON(0)) + .assert(st.get(0,capi.SQLITE_BLOB) instanceof Uint8Array) + .assert(1===st.get(0,capi.SQLITE_BLOB).length) + .assert(st.getBlob(0) instanceof Uint8Array) + .assert('3'.charCodeAt(0) === st.getBlob(0)[0]) + .assert(st._mayGet) + .assert(false===st.step()) + .assert(!st._mayGet) + ; + pId = st.pointer; + T.assert(0===capi.sqlite3_strglob("*.txt", "foo.txt")). + assert(0!==capi.sqlite3_strglob("*.txt", "foo.xtx")). + assert(0===capi.sqlite3_strlike("%.txt", "foo.txt", 0)). + assert(0!==capi.sqlite3_strlike("%.txt", "foo.xtx", 0)); + }finally{ + st.finalize(); + } + T.assert(!st.pointer) + .assert(0===this.db.openStatementCount()); + }) + .t('Table t', function(sqlite3){ + const db = this.db; + let list = []; + db.exec({ + sql:['CREATE TABLE t(a,b);', + "INSERT INTO t(a,b) VALUES(1,2),(3,4),", + "(?,?),('blob',X'6869')"/*intentionally missing semicolon to test for + off-by-one bug in string-to-WASM conversion*/], + saveSql: list, + bind: [5,6] + }); + //debug("Exec'd SQL:", list); + T.assert(2 === list.length) + .assert('string'===typeof list[1]) + .assert(4===db.changes()); + if(capi.wasm.bigIntEnabled){ + T.assert(4n===db.changes(false,true)); + } + let blob = db.selectValue("select b from t where a='blob'"); + T.assert(blob instanceof Uint8Array). + assert(0x68===blob[0] && 0x69===blob[1]); + blob = null; + let counter = 0, colNames = []; + list.length = 0; + db.exec(new TextEncoder('utf-8').encode("SELECT a a, b b FROM t"),{ + rowMode: 'object', + resultRows: list, + columnNames: colNames, + callback: function(row,stmt){ + ++counter; + T.assert((row.a%2 && row.a<6) || 'blob'===row.a); + } + }); + T.assert(2 === colNames.length) + .assert('a' === colNames[0]) + .assert(4 === counter) + .assert(4 === list.length); + list.length = 0; + db.exec("SELECT a a, b b FROM t",{ + rowMode: 'array', + callback: function(row,stmt){ + ++counter; + T.assert(Array.isArray(row)) + .assert((0===row[1]%2 && row[1]<7) + || (row[1] instanceof Uint8Array)); + } + }); + T.assert(8 === counter); + T.assert(Number.MIN_SAFE_INTEGER === + db.selectValue("SELECT "+Number.MIN_SAFE_INTEGER)). + assert(Number.MAX_SAFE_INTEGER === + db.selectValue("SELECT "+Number.MAX_SAFE_INTEGER)); + if(capi.wasm.bigIntEnabled && haveJaccwabytTests()){ + const mI = capi.wasm.xCall('jaccwabyt_test_int64_max'); + const b = BigInt(Number.MAX_SAFE_INTEGER * 2); + T.assert(b === db.selectValue("SELECT "+b)). + assert(b === db.selectValue("SELECT ?", b)). + assert(mI == db.selectValue("SELECT $x", {$x:mI})); + }else{ + /* Curiously, the JS spec seems to be off by one with the definitions + of MIN/MAX_SAFE_INTEGER: + + https://github.com/emscripten-core/emscripten/issues/17391 */ + T.mustThrow(()=>db.selectValue("SELECT "+(Number.MAX_SAFE_INTEGER+1))). + mustThrow(()=>db.selectValue("SELECT "+(Number.MIN_SAFE_INTEGER-1))); + } + + let st = db.prepare("update t set b=:b where a='blob'"); + try { + const ndx = st.getParamIndex(':b'); + T.assert(1===ndx); + st.bindAsBlob(ndx, "ima blob").reset(true); + } finally { + st.finalize(); + } + + try { + db.prepare("/*empty SQL*/"); + toss("Must not be reached."); + }catch(e){ + T.assert(e instanceof sqlite3.SQLite3Error) + .assert(0==e.message.indexOf('Cannot prepare empty')); + } + }) + + .t('Scalar UDFs', function(sqlite3){ + const db = this.db; + db.createFunction("foo",(pCx,a,b)=>a+b); + T.assert(7===db.selectValue("select foo(3,4)")). + assert(5===db.selectValue("select foo(3,?)",2)). + assert(5===db.selectValue("select foo(?,?2)",[1,4])). + assert(5===db.selectValue("select foo($a,$b)",{$a:0,$b:5})); + db.createFunction("bar", { + arity: -1, + callback: function(pCx){ + var rc = 0; + for(let i = 1; i < arguments.length; ++i) rc += arguments[i]; + return rc; + } + }).createFunction({ + name: "asis", + callback: (pCx,arg)=>arg + }); + T.assert(0===db.selectValue("select bar()")). + assert(1===db.selectValue("select bar(1)")). + assert(3===db.selectValue("select bar(1,2)")). + assert(-1===db.selectValue("select bar(1,2,-4)")). + assert('hi' === db.selectValue("select asis('hi')")). + assert('hi' === db.selectValue("select ?",'hi')). + assert(null === db.selectValue("select null")). + assert(null === db.selectValue("select asis(null)")). + assert(1 === db.selectValue("select ?",1)). + assert(2 === db.selectValue("select ?",[2])). + assert(3 === db.selectValue("select $a",{$a:3})). + assert(T.eqApprox(3.1,db.selectValue("select 3.0 + 0.1"))). + assert(T.eqApprox(1.3,db.selectValue("select asis(1 + 0.3)"))); + + let blobArg = new Uint8Array(2); + blobArg.set([0x68, 0x69], 0); + let blobRc = db.selectValue("select asis(?1)", blobArg); + T.assert(blobRc instanceof Uint8Array). + assert(2 === blobRc.length). + assert(0x68==blobRc[0] && 0x69==blobRc[1]); + blobRc = db.selectValue("select asis(X'6869')"); + T.assert(blobRc instanceof Uint8Array). + assert(2 === blobRc.length). + assert(0x68==blobRc[0] && 0x69==blobRc[1]); + + blobArg = new Int8Array(2); + blobArg.set([0x68, 0x69]); + //debug("blobArg=",blobArg); + blobRc = db.selectValue("select asis(?1)", blobArg); + T.assert(blobRc instanceof Uint8Array). + assert(2 === blobRc.length); + //debug("blobRc=",blobRc); + T.assert(0x68==blobRc[0] && 0x69==blobRc[1]); + }) + + .t({ + name: 'Aggregate UDFs (tests are TODO)', + predicate: testIsTodo + }) + + .t({ + name: 'Window UDFs (tests are TODO)', + predicate: testIsTodo + }) + + .t("ATTACH", function(){ + const db = this.db; + const resultRows = []; + db.exec({ + sql:new TextEncoder('utf-8').encode([ + // ^^^ testing string-vs-typedarray handling in exec() + "attach 'session' as foo;" /* name 'session' is magic for kvvfs! */, + "create table foo.bar(a);", + "insert into foo.bar(a) values(1),(2),(3);", + "select a from foo.bar order by a;" + ].join('')), + rowMode: 0, + resultRows + }); + T.assert(3===resultRows.length) + .assert(2===resultRows[1]); + T.assert(2===db.selectValue('select a from foo.bar where a>1 order by a')); + let colCount = 0, rowCount = 0; + const execCallback = function(pVoid, nCols, aVals, aNames){ + colCount = nCols; + ++rowCount; + T.assert(2===aVals.length) + .assert(2===aNames.length) + .assert(+(aVals[1]) === 2 * +(aVals[0])); + }; + let rc = capi.sqlite3_exec( + db.pointer, "select a, a*2 from foo.bar", execCallback, + 0, 0 + ); + T.assert(0===rc).assert(3===rowCount).assert(2===colCount); + rc = capi.sqlite3_exec( + db.pointer, "select a from foo.bar", ()=>{ + tossQuietly("Testing throwing from exec() callback."); + }, 0, 0 + ); + T.assert(capi.SQLITE_ABORT === rc); + db.exec("detach foo"); + T.mustThrow(()=>db.exec("select * from foo.bar")); + }) + + .t({ + name: 'Jaccwabyt-specific int-pointer tests (if compiled in)', + predicate: haveJaccwabytTests, + test: function(){ + const w = wasm, db = this.db; + const stack = w.scopedAllocPush(); + let ptrInt; + const origValue = 512; + const ptrValType = 'i32'; + try{ + ptrInt = w.scopedAlloc(4); + w.setMemValue(ptrInt,origValue, ptrValType); + const cf = w.xGet('jaccwabyt_test_intptr'); + const oldPtrInt = ptrInt; + //log('ptrInt',ptrInt); + //log('getMemValue(ptrInt)',w.getMemValue(ptrInt)); + T.assert(origValue === w.getMemValue(ptrInt, ptrValType)); + const rc = cf(ptrInt); + //log('cf(ptrInt)',rc); + //log('ptrInt',ptrInt); + //log('getMemValue(ptrInt)',w.getMemValue(ptrInt,ptrValType)); + T.assert(2*origValue === rc). + assert(rc === w.getMemValue(ptrInt,ptrValType)). + assert(oldPtrInt === ptrInt); + const pi64 = w.scopedAlloc(8)/*ptr to 64-bit integer*/; + const o64 = 0x010203040506/*>32-bit integer*/; + const ptrType64 = 'i64'; + if(w.bigIntEnabled){ + w.setMemValue(pi64, o64, ptrType64); + //log("pi64 =",pi64, "o64 = 0x",o64.toString(16), o64); + const v64 = ()=>w.getMemValue(pi64,ptrType64) + //log("getMemValue(pi64)",v64()); + T.assert(v64() == o64); + //T.assert(o64 === w.getMemValue(pi64, ptrType64)); + const cf64w = w.xGet('jaccwabyt_test_int64ptr'); + cf64w(pi64); + //log("getMemValue(pi64)",v64()); + T.assert(v64() == BigInt(2 * o64)); + cf64w(pi64); + T.assert(v64() == BigInt(4 * o64)); + + const biTimes2 = w.xGet('jaccwabyt_test_int64_times2'); + T.assert(BigInt(2 * o64) === + biTimes2(BigInt(o64)/*explicit conv. required to avoid TypeError + in the call :/ */)); + + const pMin = w.scopedAlloc(16); + const pMax = pMin + 8; + const g64 = (p)=>w.getMemValue(p,ptrType64); + w.setMemValue(pMin, 0, ptrType64); + w.setMemValue(pMax, 0, ptrType64); + const minMaxI64 = [ + w.xCall('jaccwabyt_test_int64_min'), + w.xCall('jaccwabyt_test_int64_max') + ]; + T.assert(minMaxI64[0] < BigInt(Number.MIN_SAFE_INTEGER)). + assert(minMaxI64[1] > BigInt(Number.MAX_SAFE_INTEGER)); + //log("int64_min/max() =",minMaxI64, typeof minMaxI64[0]); + w.xCall('jaccwabyt_test_int64_minmax', pMin, pMax); + T.assert(g64(pMin) === minMaxI64[0], "int64 mismatch"). + assert(g64(pMax) === minMaxI64[1], "int64 mismatch"); + //log("pMin",g64(pMin), "pMax",g64(pMax)); + w.setMemValue(pMin, minMaxI64[0], ptrType64); + T.assert(g64(pMin) === minMaxI64[0]). + assert(minMaxI64[0] === db.selectValue("select ?",g64(pMin))). + assert(minMaxI64[1] === db.selectValue("select ?",g64(pMax))); + const rxRange = /too big/; + T.mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[0] - BigInt(1))}, + rxRange). + mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[1] + BigInt(1))}, + (e)=>rxRange.test(e.message)); + }else{ + log("No BigInt support. Skipping related tests."); + log("\"The problem\" here is that we can manipulate, at the byte level,", + "heap memory to set 64-bit values, but we can't get those values", + "back into JS because of the lack of 64-bit integer support."); + } + }finally{ + const x = w.scopedAlloc(1), y = w.scopedAlloc(1), z = w.scopedAlloc(1); + //log("x=",x,"y=",y,"z=",z); // just looking at the alignment + w.scopedAllocPop(stack); + } + } + }/* jaccwabyt-specific tests */) + + .t('Close db', function(){ + T.assert(this.db).assert(Number.isInteger(this.db.pointer)); + wasm.exports.sqlite3_wasm_db_reset(this.db.pointer); + this.db.close(); + T.assert(!this.db.pointer); + }) + ;/* end of oo1 checks */ + + //////////////////////////////////////////////////////////////////////// log("Loading and initializing sqlite3 WASM module..."); if(!isUIThread()){ importScripts("sqlite3.js"); @@ -190,11 +727,15 @@ printErr: error }).then(function(sqlite3){ //console.log('sqlite3 =',sqlite3); - log("Done initializing. Running tests..."); - try { - TestUtil.runTests(); - }catch(e){ - error("Tests failed:",e.message); + log("Done initializing WASM/JS bits. Running tests..."); + capi = sqlite3.capi; + wasm = capi.wasm; + log("sqlite3 version:",capi.sqlite3_libversion(), + capi.sqlite3_sourceid()); + log("BigInt/int64 support is",(wasm.bigIntEnabled ? "enabled" : "disabled")); + if(haveJaccwabytTests()){ + log("Jaccwabyt test C code found. Jaccwabyt-specific low-level tests."); } + TestUtil.runTests(sqlite3); }); })(); diff --git a/ext/wasm/testing1.js b/ext/wasm/testing1.js index 5ef6d15b0b..6bda587733 100644 --- a/ext/wasm/testing1.js +++ b/ext/wasm/testing1.js @@ -94,7 +94,6 @@ "INSERT INTO t(a,b) VALUES(1,2),(3,4),", "(?,?),('blob',X'6869')"/*intentionally missing semicolon to test for off-by-one bug in string-to-WASM conversion*/], - multi: true, saveSql: list, bind: [5,6] }); @@ -258,7 +257,6 @@ "insert into foo.bar(a) values(1),(2),(3);", "select a from foo.bar order by a;" ].join('')), - multi: true, rowMode: 0, resultRows }); @@ -1083,7 +1081,7 @@ }finally{ P.restore(stack); } -}/*testPstack()*/; + }/*testPstack()*/; const clearKvvfs = function(){ const sz = sqlite3.capi.sqlite3_web_kvvfs_size(); diff --git a/manifest b/manifest index d95623c883..2913fa1744 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Updates\sto\sthe\sfuzzer\squery\sinvariant\schecker\s-\stracking\schanges\smade\nover\sin\sdbsqlfuzz. -D 2022-10-12T18:40:25.766 +C Port\sthe\sfirst\s180-odd\sunit\stests\sfrom\stesting1.*\sinto\sthe\snew\stester1.*.\sFix\sa\sstray-keystroke-induced\stypo\swhich\sbroke\spstack.allocChunks(). +D 2022-10-13T08:03:31.255 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -487,7 +487,7 @@ F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb4 F ext/wasm/api/sqlite3-api-glue.js 3b2a43c7b2ceb2b60f5f4a1afefbd93508c2fe0f2e667eea278afe570be4b606 F ext/wasm/api/sqlite3-api-oo1.js ac1e08d36bdfb5aa0a2d75b7d4c892fd51819d34c932370c3282810672bcc086 F ext/wasm/api/sqlite3-api-opfs.js 5a8ab3b76880c8ada8710ca9ba1ca5b160872edfd8bd5322e4f179a7f41cc616 -F ext/wasm/api/sqlite3-api-prologue.js daf288df965cab1e8eabee4451f6ba3beb03d5a579722b0b0f90e5203962f515 +F ext/wasm/api/sqlite3-api-prologue.js 5c56056810333974c971f6310c5c9698cf7ca8b06f6d2f1986cec819d0f5bbad F ext/wasm/api/sqlite3-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c a321f12ceedac8611c1377ccfb5df0c0547bd9395f7fd7613827de365d994948 @@ -495,7 +495,7 @@ F ext/wasm/batch-runner.html cf1a410c92bad50fcec2ddc71390b4e9df63a6ea1bef12a5163 F ext/wasm/batch-runner.js 5bae81684728b6be157d1f92b39824153f0fd019345b39f2ab8930f7ee2a57d8 F ext/wasm/common/SqliteTestUtil.js 647bf014bd30bdd870a7e9001e251d12fc1c9ec9ce176a1004b838a4b33c5c05 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f -F ext/wasm/common/testing.css bd6b23c4666be765139c1e50569cac301d949d5e251f7ad442376995ec5aa39a +F ext/wasm/common/testing.css 12bd88c69eea1b61649ad9add3ff24f0fbc3b79a7616d74ab3997b7271cd8482 F ext/wasm/common/whwasmutil.js bc8522a071f4754af7b50f53807b95f691d2f9e44fc3b3e8c65dff6ef2485c0d F ext/wasm/demo-123-worker.html e50b51dc7271b9d3cc830cb7c2fba294d622f56b7acb199f7257d11195a63d49 F ext/wasm/demo-123.html 7c239c9951d1b113f9f532969ac039294cf1dcfee2b3ae0a2c1ed2b3d59f8dfa @@ -526,13 +526,13 @@ F ext/wasm/sqlite3-worker1-promiser.js 307d7837420ca6a9d3780dfc81194f1c0715637e6 F ext/wasm/sqlite3-worker1.js 466e9bd39409ab03f3e00999887aaffc11e95b416e2689596e3d7f1516673fdf F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5 F ext/wasm/test-opfs-vfs.js 56c3d725044c668fa7910451e96c1195d25ad95825f9ac79f747a7759d1973d0 -F ext/wasm/tester1-worker.html 9d24bfc5aec4d81ce00695dea2dbed262eb486f59e3ce75746de9f5b58b128a0 -F ext/wasm/tester1.html 13ad0dc087bdd8be1e9a4869d591d0c9915e89c8e191bce7803dc23b45cdd912 -F ext/wasm/tester1.js 2f05d90d7c8f519ff6a09bb5a7a1a3ad853aea72c4e51553f2c004446465b179 +F ext/wasm/tester1-worker.html 0af7a22025ff1da72a84765d64f8f221844a57c6e6e314acf3a30f176101fd3f +F ext/wasm/tester1.html fde0e0bdeaaa2c39877c749dc86a8c1c306f771c3d75b89a6289a5ed11243e9d +F ext/wasm/tester1.js d25cf7615bd39ab9ab4be2f231fecf1fff1378227e695083dc35adc4fdff668e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 F ext/wasm/testing-worker1-promiser.js bd788e33c1807e0a6dda9c9a9d784bd3350ca49c9dd8ae2cc8719b506b6e013e F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae -F ext/wasm/testing1.js 15fc3dca6b2fc22c1458cca2cc2c484281dfc91f01c048ad4e38d5d3924ea961 +F ext/wasm/testing1.js 2034cf6972ab1506e75e41e532ca7cd3060e194c37edab3ff65d00d8a8af8ba2 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 88f40ef3cd8201bdadd120a711c36bbf0ce56cc0eab1d5e7debb71fed7822494 F ext/wasm/wasmfs.make 3cce1820006196de140f90f2da4b4ea657083fb5bfee7d125be43f7a85748c8f @@ -2034,8 +2034,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 368fa6b25bc803ded7c1a0184615980902657879370caec22ceea42496ec0566 -R b6417444abfe9dc882e90feaa0e7d85d -U drh -Z 6e9d8b3a258c07649344995d3cb09455 +P 4ca16a304ad10fbb48f78b4384b347fe883e1a4f222f113ac981e89845c3e113 +R 624f8080126a42bc3dcbc00f2cc651c0 +U stephan +Z 26dc7314d65c80886c8a3b55a6ce8b4e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ca633fd6b3..7a450195f2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4ca16a304ad10fbb48f78b4384b347fe883e1a4f222f113ac981e89845c3e113 \ No newline at end of file +ef689e33e464829f5cbe4ca24a53d9dba59abe74d3d80a37a91b93a4eccccf2d \ No newline at end of file