From e621556724c044aa09e7c4fdab4e8407df83d216 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 30 Aug 2023 11:54:43 +0000 Subject: [PATCH] Add a UI, of sorts, to the JS SQLTester. FossilOrigin-Name: 249e82b9917ea47c56ee1cbd3345a977d335fd3fc0d67a1ef157813ef4571c7c --- ext/wasm/SQLTester/GNUmakefile | 2 +- ext/wasm/SQLTester/SQLTester.mjs | 165 ++++++++++++++++++--------- ext/wasm/SQLTester/SQLTester.run.mjs | 73 ++++++++---- ext/wasm/SQLTester/index.html | 99 +++++++++++++++- manifest | 18 +-- manifest.uuid | 2 +- 6 files changed, 267 insertions(+), 92 deletions(-) diff --git a/ext/wasm/SQLTester/GNUmakefile b/ext/wasm/SQLTester/GNUmakefile index 63388084d9..8fa1247138 100644 --- a/ext/wasm/SQLTester/GNUmakefile +++ b/ext/wasm/SQLTester/GNUmakefile @@ -39,7 +39,7 @@ $(test-list.mjs): $(bin.touint8array) $(tests.all) $(MAKEFILE) @echo "Created $@" $(test-list.mjs.gz): $(test-list.mjs) gzip -c $< > $@ -CLEAAN_FILES += $(test-list.mjs.gz) +CLEAN_FILES += $(test-list.mjs.gz) all: $(test-list.mjs.gz) else @echo "Cannot build $(test-list.mjs) for lack of input test files."; \ diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs index 0ec82ca464..895c646116 100644 --- a/ext/wasm/SQLTester/SQLTester.mjs +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -10,10 +10,11 @@ ** ************************************************************************* ** This file contains the main application entry pointer for the JS -** implementation of the SQLTester framework. This version is not well -** documented because the one it's a direct port of is documented: -** in the main SQLite3 source tree, see -** ext/jni/src/org/sqlite/jni/tester/SQLite3Tester.java. +** implementation of the SQLTester framework. +** +** This version is not well-documented because it's a direct port of +** the Java immplementation, which is documented: in the main SQLite3 +** source tree, see ext/jni/src/org/sqlite/jni/tester/SQLite3Tester.java. */ import sqlite3ApiInit from '/jswasm/sqlite3.mjs'; @@ -24,6 +25,12 @@ const log = (...args)=>{ console.log('SQLTester:',...args); }; +/** + Try to install vfsName as the new default VFS. Once this succeeds + (returns true) then it becomes a no-op on future calls. Throws if + vfs registration as the default VFS fails but has no side effects + if vfsName is not currently registered. +*/ const tryInstallVfs = function f(vfsName){ if(f.vfsName) return false; const pVfs = sqlite3.capi.sqlite3_vfs_find(vfsName); @@ -39,9 +46,13 @@ const tryInstallVfs = function f(vfsName){ }; tryInstallVfs.vfsName = undefined; -if( 1 ){ +if( 0 && globalThis.WorkerGlobalScope ){ // Try OPFS storage, if available... - if(sqlite3.installOpfsSAHPoolVfs){ + if( 0 && sqlite3.oo1.OpfsDb ){ + /* Really slow with these tests */ + tryInstallVfs("opfs"); + } + if( sqlite3.installOpfsSAHPoolVfs ){ await sqlite3.installOpfsSAHPoolVfs({ clearOnInit: true, initialCapacity: 15, @@ -52,14 +63,16 @@ if( 1 ){ log("OpfsSAHPool could not load:",e); }); } - if(sqlite3.oo1.OpfsDb){ - tryInstallVfs("opfs"); - } } -const wPost = (type,...payload)=>{ - postMessage({type, payload}); -}; +const wPost = (function(){ + return (('undefined'===typeof WorkerGlobalScope) + ? ()=>{} + : (type, payload)=>{ + postMessage({type, payload}); + }); +})(); +//log("WorkerGlobalScope",globalThis.WorkerGlobalScope); // Return a new enum entry value const newE = ()=>Object.create(null); @@ -99,6 +112,12 @@ class SQLTesterException extends globalThis.Error { } this.name = 'SQLTesterException'; } + /** + If this overrideable method returns false (the default) then + exceptions of that type are fatal to a whole test run, instead of + just the test which triggered it. If the the "keep going" flag + is set, this preference is ignored. + */ isFatal() { return false; } } @@ -107,7 +126,7 @@ SQLTesterException.toss = (...args)=>{ } class DbException extends SQLTesterException { - constructor(testScript, pDb, rc, closeDb){ + constructor(testScript, pDb, rc, closeDb=false){ super(testScript, "DB error #"+rc+": "+sqlite3.capi.sqlite3_errmsg(pDb)); this.name = 'DbException'; if( closeDb ) sqlite3.capi.sqlite3_close_v2(pDb); @@ -137,12 +156,14 @@ class IncompatibleDirective extends SQLTesterException { } } +//! For throwing where an expression is required. const toss = (errType, ...args)=>{ throw new errType(...args); }; const __utf8Decoder = new TextDecoder(); const __utf8Encoder = new TextEncoder('utf-8'); +//! Workaround for Util.utf8Decode() const __SAB = ('undefined'===typeof globalThis.SharedArrayBuffer) ? function(){} : globalThis.SharedArrayBuffer; @@ -190,26 +211,41 @@ class Outer { #verbosity = 0; #logger = console.log.bind(console); - constructor(){ + constructor(func){ + if(func) this.setFunc(func); + } + + logger(...args){ + if(args.length){ + this.#logger = args[0]; + return this; + } + return this.#logger; } out(...args){ - if(!this.#lnBuf.length && this.getOutputPrefix ){ + if( this.getOutputPrefix && !this.#lnBuf.length ){ this.#lnBuf.push(this.getOutputPrefix()); } this.#lnBuf.push(...args); return this; } - outln(...args){ - if(!this.#lnBuf.length && this.getOutputPrefix ){ + + #outlnImpl(vLevel, ...args){ + if( this.getOutputPrefix && !this.#lnBuf.length ){ this.#lnBuf.push(this.getOutputPrefix()); } this.#lnBuf.push(...args,'\n'); - this.#logger(this.#lnBuf.join('')); + const msg = this.#lnBuf.join(''); this.#lnBuf.length = 0; + this.#logger(msg); return this; } + outln(...args){ + return this.#outlnImpl(0,...args); + } + outputPrefix(){ if( 0==arguments.length ){ return (this.getOutputPrefix @@ -220,9 +256,9 @@ class Outer { } } - verboseN(lvl, argv){ + verboseN(lvl, args){ if( this.#verbosity>=lvl ){ - this.outln('VERBOSE ',lvl,': ',...argv); + this.#outlnImpl(lvl,'VERBOSE ',lvl,': ',...args); } } verbose1(...args){ return this.verboseN(1,args); } @@ -230,13 +266,8 @@ class Outer { verbose3(...args){ return this.verboseN(3,args); } verbosity(){ - let rc; - if(arguments.length){ - rc = this.#verbosity; - this.#verbosity = arguments[0]; - }else{ - rc = this.#verbosity; - } + const rc = this.#verbosity; + if(arguments.length) this.#verbosity = +arguments[0]; return rc; } @@ -261,7 +292,7 @@ class SQLTester { nTestFile: 0, //! Number of scripts which were aborted nAbortedScript: 0, - //! Incremented by test case handlers + //! Test-case count for to the current TestScript nTest: 0 }); #emitColNames = false; @@ -288,6 +319,24 @@ class SQLTester { outln(...args){ return this.#outer.outln(...args); } out(...args){ return this.#outer.out(...args); } + outer(...args){ + if(args.length){ + this.#outer = args[0]; + return this; + } + return this.#outer; + } + verbose1(...args){ return this.#outer.verboseN(1,args); } + verbose2(...args){ return this.#outer.verboseN(2,args); } + verbose3(...args){ return this.#outer.verboseN(3,args); } + verbosity(...args){ + const rc = this.#outer.verbosity(...args); + return args.length ? this : rc; + } + setLogger(func){ + this.#outer.logger(func); + return this; + } incrementTestCounter(){ ++this.metrics.nTotalTest; @@ -339,8 +388,6 @@ class SQLTester { return this.#takeBuffer(this.#resultBuffer); } - verbosity(...args){ return this.#outer.verbosity(...args); } - nullValue(){ if( 0==arguments.length ){ return this.#nullView; @@ -427,7 +474,7 @@ class SQLTester { pDb = wasm.peekPtr(ppOut); }); if( 0==rc && this.#db.initSql.length > 0){ - //this.#outer.verbose2("RUNNING DB INIT CODE: ",this.#db.initSql.toString()); + this.#outer.verbose2("RUNNING DB INIT CODE: ",this.#db.initSql.toString()); rc = this.execSql(pDb, false, ResultBufferMode.NONE, null, this.#db.initSql.join('')); } @@ -464,6 +511,8 @@ class SQLTester { runTests(){ const tStart = (new Date()).getTime(); let isVerbose = this.verbosity(); + this.metrics.nAbortedScript = 0; + this.metrics.nTotalTest = 0; for(const ts of this.#aScripts){ this.reset(); ++this.metrics.nTestFile; @@ -471,11 +520,6 @@ class SQLTester { const timeStart = (new Date()).getTime(); let msgTail = ''; try{ - if( isVerbose ){ - this.#outer.verbose1("Running ",ts.filename()); - }else{ - msgTail = ' '+ts.filename(); - } ts.run(this); }catch(e){ if(e instanceof SQLTesterException){ @@ -483,21 +527,35 @@ class SQLTester { this.outln("🔥EXCEPTION: ",e); ++this.metrics.nAbortedScript; if( this.#keepGoing ){ - this.outln("Continuing anyway becaure of the keep-going option."); + this.outln("Continuing anyway because of the keep-going option."); + }else if( e.isFatal() ){ + throw e; } - else if( e.isFatal() ) throw e; }else{ throw e; } }finally{ const timeEnd = (new Date()).getTime(); - this.outln("🏁", (threw ? "❌" : "✅"), " ", this.metrics.nTest, - " test(s) in ", (timeEnd-timeStart),"ms.",msgTail); + this.out("🏁", (threw ? "❌" : "✅"), " ", + this.metrics.nTest, " test(s) in ", + (timeEnd-timeStart),"ms. "); + const mod = ts.moduleName(); + if( mod ){ + this.out( "[",mod,"] " ); + } + this.outln(ts.filename()); } } const tEnd = (new Date()).getTime(); - this.outln("Total run-time: ",(tEnd-tStart),"ms"); Util.unlink(this.#db.initialDbName); + this.outln("Took ",(tEnd-tStart),"ms. test count = ", + this.metrics.nTotalTest,", script count = ", + this.#aScripts.length,( + this.metrics.nAbortedScript + ? ", aborted scripts = "+this.metrics.nAbortedScript + : "" + ) + ); return this; } @@ -511,10 +569,6 @@ class SQLTester { } } - /** - Returns v or some escaped form of v, as defined in the tester's - spec doc. - */ #escapeSqlValue(v){ if( !v ) return "{}"; if( !Rx.special.test(v) ){ @@ -735,6 +789,11 @@ class TestScript { this.#cursor.src = content; } + moduleName(){ + return (0==arguments.length) + ? this.#moduleName : (this.#moduleName = arguments[0]); + } + testCaseName(){ return (0==arguments.length) ? this.#testCaseName : (this.#testCaseName = arguments[0]); @@ -760,6 +819,14 @@ class TestScript { throw new TestScriptFailed(this,...args); } + verbose1(...args){ return this.#outer.verboseN(1,args); } + verbose2(...args){ return this.#outer.verboseN(2,args); } + verbose3(...args){ return this.#outer.verboseN(3,args); } + verbosity(...args){ + const rc = this.#outer.verbosity(...args); + return args.length ? this : rc; + } + #checkForDirective(tester,line){ if(line.startsWith("#")){ throw new IncompatibleDirective(this, "C-preprocessor input: "+line); @@ -818,7 +885,8 @@ class TestScript { run(tester){ this.reset(); - this.#outer.verbosity(tester.verbosity()); + this.#outer.verbosity( tester.verbosity() ); + this.#outer.logger( tester.outer().logger() ); let line, directive, argv = []; while( null != (line = this.getLine()) ){ this.verbose3("run() input line: ",line); @@ -942,11 +1010,6 @@ class TestScript { cur.lineNo = cur.putbackLineNo; } - verbose1(...args){ return this.#outer.verboseN(1,args); } - verbose2(...args){ return this.#outer.verboseN(2,args); } - verbose3(...args){ return this.#outer.verboseN(3,args); } - verbosity(...args){ return this.#outer.verbosity(...args); } - }/*TestScript*/; //! --close command diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs index e6730253b6..4a08904592 100644 --- a/ext/wasm/SQLTester/SQLTester.run.mjs +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -12,7 +12,7 @@ ** This file contains a test application for SQLTester.js. */ import {default as ns} from './SQLTester.mjs'; -import {default as tests} from './test-list.mjs'; +import {default as allTests} from './test-list.mjs'; globalThis.sqlite3 = ns.sqlite3; const log = function f(...args){ @@ -33,8 +33,6 @@ const affirm = function(expr, msg){ } } -log("SQLTester is ready."); - let ts = new ns.TestScript('/foo.test',` /* ** This is a comment. There are many like it but this one is mine. @@ -56,10 +54,10 @@ let ts = new ns.TestScript('/foo.test',` --oom --db 0 --new my.db ---null zilchy +--null zilch --testcase 1.0 SELECT 1, null; ---result 1 zilchy +--result 1 zilch --glob *zil* --notglob *ZIL* SELECT 1, 2; @@ -94,29 +92,54 @@ SELECT json_array(1,2,3) --print Until next time `); -const sqt = new ns.SQLTester(); -try{ - if( 0 ){ - affirm( !sqt.getCurrentDb(), 'sqt.getCurrentDb()' ); - sqt.openDb('/foo.db', true); - affirm( !!sqt.getCurrentDb(),'sqt.getCurrentDb()' ); - sqt.verbosity(0); - if(false){ +const sqt = new ns.SQLTester() + .setLogger(console.log.bind(console)) + .verbosity(1) + .addTestScript(ts); + +const runTests = function(){ + try{ + if( 0 ){ + affirm( !sqt.getCurrentDb(), 'sqt.getCurrentDb()' ); + sqt.openDb('/foo.db', true); + affirm( !!sqt.getCurrentDb(),'sqt.getCurrentDb()' ); affirm( 'zilch' !== sqt.nullValue() ); ts.run(sqt); affirm( 'zilch' === sqt.nullValue() ); + sqt.addTestScript(ts); + sqt.runTests(); + }else{ + for(const t of allTests){ + sqt.addTestScript( new ns.TestScript(t) ); + } + allTests.length = 0; + sqt.runTests(); } - sqt.addTestScript(ts); - sqt.runTests(); - }else{ - for(const t of tests){ - sqt.addTestScript( new ns.TestScript(t) ); - } - tests.length = 0; - sqt.verbosity(0); - sqt.runTests(); + }finally{ + //log( "Metrics:", sqt.metrics ); + sqt.reset(); } -}finally{ - log( "Metrics:", sqt.metrics ); - sqt.reset(); +}; + +if( globalThis.WorkerGlobalScope ){ + const wPost = (type,payload)=>globalThis.postMessage({type, payload}); + globalThis.onmessage = function({data}){ + switch(data.type){ + case 'run-tests':{ + try{ runTests(); } + finally{ wPost('tests-end'); } + break; + } + default: + log("unhandled onmessage: ",data); + break; + } + }; + sqt.setLogger((msg)=>{ + wPost('stdout', {message: msg}); + }); + wPost('is-ready'); + //globalThis.onmessage({data:{type:'run-tests'}}); +}else{ + runTests(); } diff --git a/ext/wasm/SQLTester/index.html b/ext/wasm/SQLTester/index.html index 8ae3e27a36..ebd828c645 100644 --- a/ext/wasm/SQLTester/index.html +++ b/ext/wasm/SQLTester/index.html @@ -8,19 +8,108 @@ SQLTester + -

SQLTester App. +

SQLTester for JS/WASM

+

This app reads in a build-time-defined set of SQLTester test + scripts and runs them through the test suite.

-

All stuff on this page happens in the dev console.

-
+
+ Options + + + + + +
diff --git a/manifest b/manifest index 7088c67f05..15a2c4be2d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\smechanism\swith\swhich\sto\simport\sexternal\sSQLTester\sscripts\sinto\sthe\sJS\stesting\stool. -D 2023-08-30T00:22:54.642 +C Add\sa\sUI,\sof\ssorts,\sto\sthe\sJS\sSQLTester. +D 2023-08-30T11:54:43.323 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -548,10 +548,10 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 -F ext/wasm/SQLTester/GNUmakefile ba0430646d75a832d1647d6d204c999112d831f5e85d3ed99d8f663fea20fe19 -F ext/wasm/SQLTester/SQLTester.mjs 378868be0fcbbb92456aea10e3056f88b042e106b2c348aa51060e93726f6e10 -F ext/wasm/SQLTester/SQLTester.run.mjs 2695490e2092af0c6a9d1e4128edf830648687a54ec1f0ecd16bd1be083f3938 -F ext/wasm/SQLTester/index.html 317636557257608b103fa740c07f2d440d57b924ef2072e59c1372d4a4004c06 +F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff +F ext/wasm/SQLTester/SQLTester.mjs 4a8fc2194a67180772830f503129c93fe44887b9573f50a4e24868b4bbc817f4 +F ext/wasm/SQLTester/SQLTester.run.mjs a50a1f9314d22d68b62a2f21d8913de163fce1fa420229711d6749c2b4fff9d0 +F ext/wasm/SQLTester/index.html e5f18af71749d81d86e4649d8c6efc9b78361738cb8e5c5014ba0dd3dc3de6ac F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b @@ -2113,8 +2113,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 7cef4a8300826adbdcb3b205e134a4272b12b4aa7dbee97731ac12282a4a9f06 -R 018b862c43a96a36d2df3223b087af85 +P bb08ba020ce1d86ca6aa92f43d5ae915f67d08fa73120e1f603d150e76166624 +R 8240e3c6439a4fca46c74a72b36ea777 U stephan -Z 18f8112180ee272e1f7e50a87a0ce726 +Z cef4bceac8cb673301c00562148f0e5f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7168a7b8ad..a0ecd6b9de 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bb08ba020ce1d86ca6aa92f43d5ae915f67d08fa73120e1f603d150e76166624 \ No newline at end of file +249e82b9917ea47c56ee1cbd3345a977d335fd3fc0d67a1ef157813ef4571c7c \ No newline at end of file -- 2.39.5