class TestScriptFailed extends SQLTesterException {
constructor(testScript, ...args){
- super(testScript.getPutputPrefix(),': ',...args);
+ super(testScript.getOutputPrefix(),': ',...args);
}
isFatal() { return true; }
}
const __SAB = ('undefined'===typeof globalThis.SharedArrayBuffer)
? function(){} : globalThis.SharedArrayBuffer;
+
+const Rx = newObj({
+ requiredProperties: / REQUIRED_PROPERTIES:[ \t]*(\S.*)\s*$/,
+ scriptModuleName: / SCRIPT_MODULE_NAME:[ \t]*(\S+)\s*$/,
+ mixedModuleName: / ((MIXED_)?MODULE_NAME):[ \t]*(\S+)\s*$/,
+ command: /^--(([a-z-]+)( .*)?)$/,
+ //! "Special" characters - we have to escape output if it contains any.
+ special: /[\x00-\x20\x22\x5c\x7b\x7d]/,
+ //! Either of '{' or '}'.
+ squiggly: /[{}]/
+});
+
const Util = newObj({
toss,
return 0==sqlite3.wasm.sqlite3_wasm_vfs_unlink(0,fn);
},
- argvToString: (list)=>list.join(" "),
+ argvToString: (list)=>{
+ const m = [...list];
+ m.shift();
+ return m.join(" ")
+ },
utf8Decode: function(arrayBuffer, begin, end){
return __utf8Decoder.decode(
);
},
- utf8Encode: (str)=>__utf8Encoder.encode(str)
+ utf8Encode: (str)=>__utf8Encoder.encode(str),
+
+ strglob: sqlite3.wasm.xWrap('sqlite3_wasm_SQLTester_strglob','int',
+ ['string','string'])
})/*Util*/;
class Outer {
class SQLTester {
+ //! Console output utility.
#outer = new Outer().outputPrefix( ()=>'SQLTester: ' );
+ //! List of input script files.
#aFiles = [];
+ //! Test input buffer.
#inputBuffer = [];
+ //! Test result buffer.
#resultBuffer = [];
+ //! Output representation of SQL NULL.
#nullView = "nil";
- #metrics = newObj({
- nTotalTest: 0, nTestFile: 0, nAbortedScript: 0
+ metrics = newObj({
+ //! Total tests run
+ nTotalTest: 0,
+ //! Total test script files run
+ nTestFile: 0,
+ //! Number of scripts which were aborted
+ nAbortedScript: 0,
+ //! Incremented by test case handlers
+ nTest: 0
});
#emitColNames = false;
+ //! True to keep going regardless of how a test fails.
#keepGoing = false;
#db = newObj({
+ //! The list of available db handles.
list: new Array(7),
+ //! Index into this.list of the current db.
iCurrentDb: 0,
+ //! Name of the default db, re-created for each script.
initialDbName: "test.db",
+ //! Buffer for REQUIRED_PROPERTIES pragmas.
initSql: ['select 1;'],
+ //! (sqlite3*) to the current db.
currentDb: function(){
return this.list[this.iCurrentDb];
}
outln(...args){ return this.#outer.outln(...args); }
out(...args){ return this.#outer.out(...args); }
+ incrementTestCounter(){
+ ++this.metrics.nTotalTest;
+ ++this.metrics.nTest;
+ }
+
reset(){
this.clearInputBuffer();
this.clearResultBuffer();
this.#clearBuffer(this.#db.initSql);
this.closeAllDbs();
- this.nTest = 0;
+ this.metrics.nTest = 0;
this.nullView = "nil";
this.emitColNames = false;
this.#db.iCurrentDb = 0;
Util.unlink(this.#db.initialDbName);
this.openDb(0, this.#db.initialDbName, true);
}else{
- this.#outer.outln("WARNING: setupInitialDb() unexpectedly ",
+ this.#outer.outln("WARNING: setupInitialDb() was unexpectedly ",
"triggered while it is opened.");
}
}
#appendDbErr(pDb, sb, rc){
sb.push(sqlite3.capi.sqlite3_js_rc_str(rc), ' ');
const msg = this.#escapeSqlValue(sqlite3.capi.sqlite3_errmsg(pDb));
- if( '{' == msg.charAt(0) ){
+ if( '{' === msg.charAt(0) ){
sb.push(msg);
}else{
sb.push('{', msg, '}');
}
}
+ #checkDbRc(pDb,rc){
+ sqlite3.oo1.DB.checkRc(pDb, rc);
+ }
+
execSql(pDb, throwOnError, appendMode, lineMode, sql){
- sql = sqlite3.capi.sqlite3_js_sql_to_string(sql);
- this.#outer.outln("execSql() is TODO. ",sql);
- return 0;
+ if( !pDb && !this.#db.list[0] ){
+ this.#setupInitialDb();
+ }
+ if( !pDb ) pDb = this.#db.currentDb();
+ const wasm = sqlite3.wasm, capi = sqlite3.capi;
+ sql = (sql instanceof Uint8Array)
+ ? sql
+ : new TextEncoder("utf-8").encode(capi.sqlite3_js_sql_to_string(sql));
+ const self = this;
+ const sb = (ResultBufferMode.NONE===appendMode) ? null : this.#resultBuffer;
+ let rc = 0;
+ wasm.scopedAllocCall(function(){
+ let sqlByteLen = sql.byteLength;
+ const ppStmt = wasm.scopedAlloc(
+ /* output (sqlite3_stmt**) arg and pzTail */
+ (2 * wasm.ptrSizeof) + (sqlByteLen + 1/* SQL + NUL */)
+ );
+ const pzTail = ppStmt + wasm.ptrSizeof /* final arg to sqlite3_prepare_v2() */;
+ let pSql = pzTail + wasm.ptrSizeof;
+ const pSqlEnd = pSql + sqlByteLen;
+ wasm.heap8().set(sql, pSql);
+ wasm.poke8(pSql + sqlByteLen, 0/*NUL terminator*/);
+ let pos = 0, n = 1, spacing = 0;
+ while( pSql && wasm.peek8(pSql) ){
+ wasm.pokePtr([ppStmt, pzTail], 0);
+ rc = capi.sqlite3_prepare_v3(
+ pDb, pSql, sqlByteLen, 0, ppStmt, pzTail
+ );
+ if( 0!==rc ){
+ if(throwOnError){
+ throw new DbException(pDb, rc);
+ }else if( sb ){
+ self.#appendDbErr(db, sb, rc);
+ }
+ break;
+ }
+ const pStmt = wasm.peekPtr(ppStmt);
+ pSql = wasm.peekPtr(pzTail);
+ sqlByteLen = pSqlEnd - pSql;
+ if(!pStmt) continue /* only whitespace or comments */;
+ if( sb ){
+ const nCol = capi.sqlite3_column_count(pStmt);
+ let colName, val;
+ while( capi.SQLITE_ROW === (rc = capi.sqlite3_step(pStmt)) ) {
+ for( let i=0; i < nCol; ++i ){
+ if( spacing++ > 0 ) sb.push(' ');
+ if( self.#emitColNames ){
+ colName = capi.sqlite3_column_name(pStmt, i);
+ switch(appendMode){
+ case ResultBufferMode.ASIS: sb.push( colName ); break;
+ case ResultBufferMode.ESCAPED:
+ sb.push( self.#escapeSqlValue(colName) );
+ break;
+ default:
+ self.toss("Unhandled ResultBufferMode.");
+ }
+ sb.push(' ');
+ }
+ val = capi.sqlite3_column_text(pStmt, i);
+ if( null===val ){
+ sb.push( self.#nullView );
+ continue;
+ }
+ switch(appendMode){
+ case ResultBufferMode.ASIS: sb.push( val ); break;
+ case ResultBufferMode.ESCAPED:
+ sb.push( self.#escapeSqlValue(val) );
+ break;
+ }
+ }/* column loop */
+ }/* row loop */
+ if( ResultRowMode.NEWLINE === lineMode ){
+ spacing = 0;
+ sb.push('\n');
+ }
+ }else{ // no output but possibly other side effects
+ while( capi.SQLITE_ROW === (rc = capi.sqlite3_step(pStmt)) ) {}
+ }
+ capi.sqlite3_finalize(pStmt);
+ if( capi.SQLITE_ROW===rc || capi.SQLITE_DONE===rc) rc = 0;
+ else if( rc!=0 ){
+ if( sb ){
+ self.#appendDbErr(db, sb, rc);
+ }
+ break;
+ }
+ }/* SQL script loop */;
+ })/*scopedAllocCall()*/;
+ return rc;
}
}/*SQLTester*/
}
}
-const Rx = newObj({
- requiredProperties: / REQUIRED_PROPERTIES:[ \t]*(\S.*)\s*$/,
- scriptModuleName: / SCRIPT_MODULE_NAME:[ \t]*(\S+)\s*$/,
- mixedModuleName: / ((MIXED_)?MODULE_NAME):[ \t]*(\S+)\s*$/,
- command: /^--(([a-z-]+)( .*)?)$/,
- //! "Special" characters - we have to escape output if it contains any.
- special: /[\x00-\x20\x22\x5c\x7b\x7d]/,
- //! Either of '{' or '}'.
- squiggly: /[{}]/
-});
-
class TestScript {
#cursor = new Cursor();
#moduleName = null;
return m ? m[1].trim().split(/\s+/) : null;
}
+
+ #isCommandLine(line, checkForImpl){
+ let m = Rx.command.exec(line);
+ if( m && checkForImpl ){
+ m = !!CommandDispatcher.getCommandByName(m[2]);
+ }
+ return !!m;
+ }
+
+ fetchCommandBody(tester){
+ const sb = [];
+ let line;
+ while( (null !== (line = this.peekLine())) ){
+ this.#checkForDirective(tester, line);
+ if( this.#isCommandLine(line, true) ) break;
+ sb.push(line,"\n");
+ this.consumePeeked();
+ }
+ line = sb.join('');
+ return !!line.trim() ? line : null;
+ }
+
run(tester){
this.reset();
this.#outer.verbosity(tester.verbosity());
const oldPB = cur.putbackPos;
const oldPBL = cur.putbackLineNo;
const oldLine = cur.lineNo;
- const rc = this.getLine();
- cur.peekedPos = cur.pos;
- cur.peekedLineNo = cur.lineNo;
- cur.pos = oldPos;
- cur.lineNo = oldLine;
- cur.putbackPos = oldPB;
- cur.putbackLineNo = oldPBL;
- return rc;
+ try {
+ return this.getLine();
+ }finally{
+ cur.peekedPos = cur.pos;
+ cur.peekedLineNo = cur.lineNo;
+ cur.pos = oldPos;
+ cur.lineNo = oldLine;
+ cur.putbackPos = oldPB;
+ cur.putbackLineNo = oldPBL;
+ }
}
let id;
if(argv.length>1){
const arg = argv[1];
- if("all".equals(arg)){
+ if( "all" === arg ){
t.closeAllDbs();
return;
}
}
}
+//! --glob command
+class GlobCommand extends Command {
+ #negate = false;
+ constructor(negate=false){
+ super();
+ this.#negate = negate;
+ }
+
+ process(t, ts, argv){
+ this.argcCheck(ts,argv,1,-1);
+ t.incrementTestCounter();
+ const sql = t.takeInputBuffer();
+ let rc = t.execSql(null, true, ResultBufferMode.ESCAPED,
+ ResultRowMode.ONELINE, sql);
+ const result = t.getResultText();
+ const sArgs = Util.argvToString(argv);
+ //t2.verbose2(argv[0]," rc = ",rc," result buffer:\n", result,"\nargs:\n",sArgs);
+ const glob = Util.argvToString(argv);
+ rc = Util.strglob(glob, result);
+ if( (this.#negate && 0===rc) || (!this.#negate && 0!==rc) ){
+ ts.toss(argv[0], " mismatch: ", glob," vs input: ",result);
+ }
+ }
+}
+
+//! --notglob command
+class NotGlobCommand extends GlobCommand {
+ constructor(){super(true);}
+}
+
//! --open command
class OpenDbCommand extends Command {
#createIfNeeded = false;
}
}
+//! --result command
+class ResultCommand extends Command {
+ #bufferMode;
+ constructor(resultBufferMode = ResultBufferMode.ESCAPED){
+ super();
+ this.#bufferMode = resultBufferMode;
+ }
+ process(t, ts, argv){
+ this.argcCheck(ts,argv,0,-1);
+ t.incrementTestCounter();
+ const sql = t.takeInputBuffer();
+ //ts.verbose2(argv[0]," SQL =\n",sql);
+ t.execSql(null, false, this.#bufferMode, ResultRowMode.ONELINE, sql);
+ const result = t.getResultText().trim();
+ const sArgs = argv.length>1 ? Util.argvToString(argv) : "";
+ if( result !== sArgs ){
+ t.outln(argv[0]," FAILED comparison. Result buffer:\n",
+ result,"\nExpected result:\n",sArgs);
+ ts.toss(argv[0]+" comparison failed.");
+ }
+ }
+}
+
+//! --json command
+class JsonCommand extends ResultCommand {
+ constructor(){ super(ResultBufferMode.ASIS); }
+}
+
+//! --run command
+class RunCommand extends Command {
+ process(t, ts, argv){
+ this.argcCheck(ts,argv,0,1);
+ const pDb = (1==argv.length)
+ ? t.currentDb() : t.getDbById( parseInt(argv[1]) );
+ const sql = t.takeInputBuffer();
+ const rc = t.execSql(pDb, false, ResultBufferMode.NONE,
+ ResultRowMode.ONELINE, sql);
+ if( 0!==rc && t.verbosity()>0 ){
+ const msg = sqlite3.capi.sqlite3_errmsg(pDb);
+ ts.verbose1(argv[0]," non-fatal command error #",rc,": ",
+ msg,"\nfor SQL:\n",sql);
+ }
+ }
+}
+
+//! --tableresult command
+class TableResultCommand extends Command {
+ #jsonMode;
+ constructor(jsonMode=false){
+ super();
+ this.#jsonMode = jsonMode;
+ }
+ process(t, ts, argv){
+ this.argcCheck(ts,argv,0);
+ t.incrementTestCounter();
+ let body = ts.fetchCommandBody(t);
+ log("TRC fetchCommandBody: ",body);
+ if( null===body ) ts.toss("Missing ",argv[0]," body.");
+ body = body.trim();
+ if( !body.endsWith("\n--end") ){
+ ts.toss(argv[0], " must be terminated with --end\\n");
+ }else{
+ body = body.substring(0, body.length-6);
+ log("TRC fetchCommandBody reshaped:",body);
+ }
+ const globs = body.split(/\s*\n\s*/);
+ if( globs.length < 1 ){
+ ts.toss(argv[0], " requires 1 or more ",
+ (this.#jsonMode ? "json snippets" : "globs"),".");
+ }
+ log("TRC fetchCommandBody globs:",globs);
+ const sql = t.takeInputBuffer();
+ t.execSql(null, true,
+ this.#jsonMode ? ResultBufferMode.ASIS : ResultBufferMode.ESCAPED,
+ ResultRowMode.NEWLINE, sql);
+ const rbuf = t.getResultText().trim();
+ const res = rbuf.split(/\r?\n/);
+ log("TRC fetchCommandBody rbuf, res:",rbuf, res);
+ if( res.length !== globs.length ){
+ ts.toss(argv[0], " failure: input has ", res.length,
+ " row(s) but expecting ",globs.length);
+ }
+ for(let i = 0; i < res.length; ++i){
+ const glob = globs[i].replaceAll(/\s+/g," ").trim();
+ //ts.verbose2(argv[0]," <<",glob,">> vs <<",res[i],">>");
+ if( this.#jsonMode ){
+ if( glob!==res[i] ){
+ ts.toss(argv[0], " json <<",glob, ">> does not match: <<",
+ res[i],">>");
+ }
+ }else if( 0!=Util.strglob(glob, res[i]) ){
+ ts.toss(argv[0], " glob <<",glob,">> does not match: <<",res[i],">>");
+ }
+ }
+ }
+}
+
+//! --json-block command
+class JsonBlockCommand extends TableResultCommand {
+ constructor(){ super(true); }
+}
//! --testcase command
class TestCaseCommand extends Command {
case "close": rv = new CloseDbCommand(); break;
case "column-names": rv = new ColumnNamesCommand(); break;
case "db": rv = new DbCommand(); break;
- //case "glob": rv = new GlobCommand(); break;
- //case "json": rv = new JsonCommand(); break;
- //case "json-block": rv = new JsonBlockCommand(); break;
+ case "glob": rv = new GlobCommand(); break;
+ case "json": rv = new JsonCommand(); break;
+ case "json-block": rv = new JsonBlockCommand(); break;
case "new": rv = new NewDbCommand(); break;
- //case "notglob": rv = new NotGlobCommand(); break;
+ case "notglob": rv = new NotGlobCommand(); break;
case "null": rv = new NullCommand(); break;
case "oom": rv = new NoopCommand(); break;
case "open": rv = new OpenDbCommand(); break;
case "print": rv = new PrintCommand(); break;
- //case "result": rv = new ResultCommand(); break;
- //case "run": rv = new RunCommand(); break;
- //case "tableresult": rv = new TableResultCommand(); break;
+ case "result": rv = new ResultCommand(); break;
+ case "run": rv = new RunCommand(); break;
+ case "tableresult": rv = new TableResultCommand(); break;
case "testcase": rv = new TestCaseCommand(); break;
case "verbosity": rv = new VerbosityCommand(); break;
}
-C Get\sthe\sJS\sSQLTester\scommand\shandlers\sin\splace\ssans\sthose\swhich\shave\sto\srun\sSQL.
-D 2023-08-29T15:39:57.155
+C JS\sSQLTestRunner\scan\snow\srun\sthe\sJava\simpl's\score-most\ssanity\stests,\smissing\sonly\ssupport\sfor\sdirectives.
+D 2023-08-29T20:01:01.586
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad
F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc
F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a
-F ext/jni/src/org/sqlite/jni/tester/SQLTester.java c8a9f20694e66f4d7ed677cd6d1f0d829f802c347a1f413ac2446c62e4cba23d
+F ext/jni/src/org/sqlite/jni/tester/SQLTester.java a9f4b9e12109645b21fef15807973706dd958aad9fe1c835693fcb8e95abe949
F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e
F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d
F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70
F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2
F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576
F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193
-F ext/wasm/SQLTester/SQLTester.mjs 90fc3d2eb831afed237c18b78c22b8871d8f855a742715ebee571a60b9fcd98e
-F ext/wasm/SQLTester/SQLTester.run.mjs 30a459ec400495cc52f1d693703f1629e141947a19eaf868a8e4c1fd3ef2a114
+F ext/wasm/SQLTester/SQLTester.mjs 345736d970dc56e2c1041f8583fc602eedd8a64d455864f312db7d3208e640ea
+F ext/wasm/SQLTester/SQLTester.run.mjs 2dfa1407f5f188dadafe6f21f7a6740b4f07d59c594781a01eedadec16b2ddfe
F ext/wasm/SQLTester/index.html 88d87e3ccbc33e7ab3773a8e48c1172e876951c4be31d1307c3700671262cddf
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b
F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b1881715d2a650dda7b3c2576d0e
F ext/wasm/api/sqlite3-api-glue.js b65e546568f1dfb35205b9792feb5146a6323d71b55cda58e2ed30def6dd52f3
F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8
-F ext/wasm/api/sqlite3-api-prologue.js 7fe51f06cd855634cb3765f830393f544fb532ead1cf95b5de3dd0befc81b92d
+F ext/wasm/api/sqlite3-api-prologue.js 723908946bd624d367e4df7093e9a6c9725606dc526953ea601cad8d7ce88538
F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b1738645c0134562bb84e88e2fec
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379
F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 561463ac5380e4ccf1839a1922e6d7a5585660f32e3b9701a270b78cd35566cf
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js d9e62d42b86f7bb3143eb071628b24e2ba7dcc749e41a0e9d3e2451bfea1a6b6
-F ext/wasm/api/sqlite3-wasm.c 6773e949034369ddd2a1efdedc39b2808a10b7274b0769188905432e561feebe
+F ext/wasm/api/sqlite3-wasm.c 65d60439671e24d50d9119ca805ac1c68fb36129e164377eb46f8d037bd88b07
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f
F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 8fcc2a553c1e26734902bbdee0c38183ee22b7b5c75f07405529bb79db34145a
-R 8ed29d2cdb1f79e88ba5ccad29a3151d
+P d21b1217964a53f33b7ba3958b34aa8560dff8ede33e66f54aa0afbab7099ec3
+R 7266768b4057594984eb0965145c2068
U stephan
-Z 988ed72426998a455381761a89d498de
+Z 703bcb5450951150eb9347fe40faa521
# Remove this line to create a well-formed Fossil manifest.