From: stephan Date: Tue, 29 Aug 2023 20:01:01 +0000 (+0000) Subject: JS SQLTestRunner can now run the Java impl's core-most sanity tests, missing only... X-Git-Tag: version-3.44.0~239^2~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=aa150477961c57bc0c873faf95f0bc600fc73af6;p=thirdparty%2Fsqlite.git JS SQLTestRunner can now run the Java impl's core-most sanity tests, missing only support for directives. FossilOrigin-Name: 5e798369375ce1b0c9cdf831f835d931fbd562ff7b4db09a06d1bdca2ac1b975 --- diff --git a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java index f7550c8e46..b49c61957f 100644 --- a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java +++ b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java @@ -181,7 +181,7 @@ public class SQLTester { private int nTestFile = 0; //! Number of scripts which were aborted. private int nAbortedScript = 0; - //! Per-script test counter. + //! Incremented by test case handlers private int nTest = 0; //! True to enable column name output from execSql() private boolean emitColNames; @@ -582,6 +582,10 @@ public class SQLTester { } } }finally{ + sqlite3_reset(stmt + /* In order to trigger an exception in the + INSERT...RETURNING locking scenario: + https://sqlite.org/forum/forumpost/36f7a2e7494897df */); sqlite3_finalize(stmt); } if( 0!=rc && throwOnError ){ @@ -926,8 +930,8 @@ class RunCommand extends Command { final sqlite3 db = (1==argv.length) ? t.getCurrentDb() : t.getDbById( Integer.parseInt(argv[1]) ); final String sql = t.takeInputBuffer(); - int rc = t.execSql(db, false, ResultBufferMode.NONE, - ResultRowMode.ONELINE, sql); + final int rc = t.execSql(db, false, ResultBufferMode.NONE, + ResultRowMode.ONELINE, sql); if( 0!=rc && t.isVerbose() ){ String msg = sqlite3_errmsg(db); ts.verbose1(argv[0]," non-fatal command error #",rc,": ", @@ -950,8 +954,7 @@ class TableResultCommand extends Command { if( !body.endsWith("\n--end") ){ ts.toss(argv[0], " must be terminated with --end."); }else{ - int n = body.length(); - body = body.substring(0, n-6); + body = body.substring(0, body.length()-6); } final String[] globs = body.split("\\s*\\n\\s*"); if( globs.length < 1 ){ @@ -1240,14 +1243,15 @@ class TestScript { final int oldPB = cur.putbackPos; final int oldPBL = cur.putbackLineNo; final int oldLine = cur.lineNo; - final String rc = 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 getLine(); } + finally{ + cur.peekedPos = cur.pos; + cur.peekedLineNo = cur.lineNo; + cur.pos = oldPos; + cur.lineNo = oldLine; + cur.putbackPos = oldPB; + cur.putbackLineNo = oldPBL; + } } /** @@ -1374,11 +1378,10 @@ class TestScript { String line; while( (null != (line = peekLine())) ){ checkForDirective(tester, line); - if( !isCommandLine(line, true) ){ + if( isCommandLine(line, true) ) break; + else { sb.append(line).append("\n"); consumePeeked(); - }else{ - break; } } line = sb.toString(); diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs index 0461beb86f..c7059ad1b7 100644 --- a/ext/wasm/SQLTester/SQLTester.mjs +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -73,7 +73,7 @@ class DbException extends SQLTesterException { class TestScriptFailed extends SQLTesterException { constructor(testScript, ...args){ - super(testScript.getPutputPrefix(),': ',...args); + super(testScript.getOutputPrefix(),': ',...args); } isFatal() { return true; } } @@ -103,6 +103,18 @@ const __utf8Encoder = new TextEncoder('utf-8'); 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, @@ -110,7 +122,11 @@ const Util = newObj({ 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( @@ -120,7 +136,10 @@ const Util = newObj({ ); }, - utf8Encode: (str)=>__utf8Encoder.encode(str) + utf8Encode: (str)=>__utf8Encoder.encode(str), + + strglob: sqlite3.wasm.xWrap('sqlite3_wasm_SQLTester_strglob','int', + ['string','string']) })/*Util*/; class Outer { @@ -182,21 +201,39 @@ 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]; } @@ -208,12 +245,17 @@ class SQLTester { 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; @@ -365,7 +407,7 @@ class SQLTester { 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."); } } @@ -405,17 +447,107 @@ class SQLTester { #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*/ @@ -469,17 +601,6 @@ class Cursor { } } -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; @@ -529,6 +650,28 @@ class TestScript { 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()); @@ -621,14 +764,16 @@ class TestScript { 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; + } } @@ -667,7 +812,7 @@ class CloseDbCommand extends Command { let id; if(argv.length>1){ const arg = argv[1]; - if("all".equals(arg)){ + if( "all" === arg ){ t.closeAllDbs(); return; } @@ -697,6 +842,36 @@ class DbCommand extends Command { } } +//! --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; @@ -740,6 +915,107 @@ class PrintCommand extends Command { } } +//! --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 { @@ -770,18 +1046,18 @@ class CommandDispatcher { 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; } diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs index dc8eaa0c17..36d1ab5dcd 100644 --- a/ext/wasm/SQLTester/SQLTester.run.mjs +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -25,14 +25,46 @@ log("ns =",ns); out("Hi there. ").outln("SQLTester is ostensibly ready."); let ts = new ns.TestScript('/foo.test', ns.Util.utf8Encode( -`# comment line ---print Starting up... ---null NIL ---new :memory: ---testcase 0.0.1 -select '0.0.1'; -#--result 0.0.1 ---print done +` +--close all +--oom +--db 0 +--new my.db +--null zilch +--testcase 1.0 +SELECT 1, null; +--result 1 zilch +--glob *zil* +--notglob *ZIL* +SELECT 1, 2; +intentional error; +--run +--testcase json-1 +SELECT json_array(1,2,3) +--json [1,2,3] +--testcase tableresult-1 + select 1, 'a'; + select 2, 'b'; +--tableresult + # [a-z] + 2 b +--end +--testcase json-block-1 + select json_array(1,2,3); + select json_object('a',1,'b',2); +--json-block + [1,2,3] + {"a":1,"b":2} +--end +--testcase col-names-on +--column-names 1 + select 1 as 'a', 2 as 'b'; +--result a 1 b 2 +--testcase col-names-off +--column-names 0 + select 1 as 'a', 2 as 'b'; +--result 1 2 +--close `)); const sqt = new ns.SQLTester(); @@ -41,11 +73,12 @@ try{ sqt.openDb('/foo.db', true); log( 'sqt.getCurrentDb()', sqt.getCurrentDb() ); sqt.verbosity(0); - affirm( 'NIL' !== sqt.nullValue() ); + affirm( 'zilch' !== sqt.nullValue() ); ts.run(sqt); - affirm( 'NIL' === sqt.nullValue() ); + affirm( 'zilch' === sqt.nullValue() ); }finally{ sqt.reset(); } log( 'sqt.getCurrentDb()', sqt.getCurrentDb() ); +log( "Metrics:", sqt.metrics ); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 35c856269d..996f23298f 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -1139,15 +1139,15 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( /** Records the current pstack position, calls the given function, - and restores the pstack regardless of whether the function - throws. Returns the result of the call or propagates an - exception on error. + passing it the sqlite3 object, then restores the pstack + regardless of whether the function throws. Returns the result + of the call or propagates an exception on error. Added in 3.44. */ call: function(f){ const stackPos = wasm.pstack.pointer; - try{ return f() }finally{ + try{ return f(sqlite3) } finally{ wasm.pstack.restore(stackPos); } } diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index ff15e3b4fb..db77010d95 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -1801,6 +1801,118 @@ char * sqlite3_wasm_test_str_hello(int fail){ } return s; } + +/* +** For testing using SQLTester scripts. +** +** Return non-zero if string z matches glob pattern zGlob and zero if the +** pattern does not match. +** +** To repeat: +** +** zero == no match +** non-zero == match +** +** Globbing rules: +** +** '*' Matches any sequence of zero or more characters. +** +** '?' Matches exactly one character. +** +** [...] Matches one character from the enclosed list of +** characters. +** +** [^...] Matches one character not in the enclosed list. +** +** '#' Matches any sequence of one or more digits with an +** optional + or - sign in front, or a hexadecimal +** literal of the form 0x... +*/ +static int sqlite3_wasm_SQLTester_strnotglob(const char *zGlob, const char *z){ + int c, c2; + int invert; + int seen; + typedef int (*recurse_f)(const char *,const char *); + static const recurse_f recurse = sqlite3_wasm_SQLTester_strnotglob; + + while( (c = (*(zGlob++)))!=0 ){ + if( c=='*' ){ + while( (c=(*(zGlob++))) == '*' || c=='?' ){ + if( c=='?' && (*(z++))==0 ) return 0; + } + if( c==0 ){ + return 1; + }else if( c=='[' ){ + while( *z && recurse(zGlob-1,z)==0 ){ + z++; + } + return (*z)!=0; + } + while( (c2 = (*(z++)))!=0 ){ + while( c2!=c ){ + c2 = *(z++); + if( c2==0 ) return 0; + } + if( recurse(zGlob,z) ) return 1; + } + return 0; + }else if( c=='?' ){ + if( (*(z++))==0 ) return 0; + }else if( c=='[' ){ + int prior_c = 0; + seen = 0; + invert = 0; + c = *(z++); + if( c==0 ) return 0; + c2 = *(zGlob++); + if( c2=='^' ){ + invert = 1; + c2 = *(zGlob++); + } + if( c2==']' ){ + if( c==']' ) seen = 1; + c2 = *(zGlob++); + } + while( c2 && c2!=']' ){ + if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){ + c2 = *(zGlob++); + if( c>=prior_c && c<=c2 ) seen = 1; + prior_c = 0; + }else{ + if( c==c2 ){ + seen = 1; + } + prior_c = c2; + } + c2 = *(zGlob++); + } + if( c2==0 || (seen ^ invert)==0 ) return 0; + }else if( c=='#' ){ + if( z[0]=='0' + && (z[1]=='x' || z[1]=='X') + && sqlite3Isxdigit(z[2]) + ){ + z += 3; + while( sqlite3Isxdigit(z[0]) ){ z++; } + }else{ + if( (z[0]=='-' || z[0]=='+') && sqlite3Isdigit(z[1]) ) z++; + if( !sqlite3Isdigit(z[0]) ) return 0; + z++; + while( sqlite3Isdigit(z[0]) ){ z++; } + } + }else{ + if( c!=(*(z++)) ) return 0; + } + } + return *z==0; +} + +SQLITE_WASM_EXPORT +int sqlite3_wasm_SQLTester_strglob(const char *zGlob, const char *z){ + return !sqlite3_wasm_SQLTester_strnotglob(zGlob, z); +} + + #endif /* SQLITE_WASM_TESTS */ #undef SQLITE_WASM_EXPORT diff --git a/manifest b/manifest index 94540f2c28..40829f5f94 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -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 @@ -283,7 +283,7 @@ F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c38 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 @@ -548,8 +548,8 @@ 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/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 @@ -563,14 +563,14 @@ F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057af 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 @@ -2111,8 +2111,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 8fcc2a553c1e26734902bbdee0c38183ee22b7b5c75f07405529bb79db34145a -R 8ed29d2cdb1f79e88ba5ccad29a3151d +P d21b1217964a53f33b7ba3958b34aa8560dff8ede33e66f54aa0afbab7099ec3 +R 7266768b4057594984eb0965145c2068 U stephan -Z 988ed72426998a455381761a89d498de +Z 703bcb5450951150eb9347fe40faa521 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 60048b2641..47297d57d2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d21b1217964a53f33b7ba3958b34aa8560dff8ede33e66f54aa0afbab7099ec3 \ No newline at end of file +5e798369375ce1b0c9cdf831f835d931fbd562ff7b4db09a06d1bdca2ac1b975 \ No newline at end of file