1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Add SQLTester --tableresult command.

FossilOrigin-Name: 8c5b6d893df4a4e82c6d8e07507fc160b11412ede4bb903ff4e3f5ffa59a9cb9
This commit is contained in:
stephan
2023-08-09 13:16:10 +00:00
parent aec9aa9289
commit ff6b15fbb2
5 changed files with 92 additions and 39 deletions

View File

@ -47,6 +47,13 @@ enum ResultBufferMode {
ASIS ASIS
}; };
enum ResultRowMode {
//! Keep all result rows on one line, space-separated.
ONELINE,
//! Add a newline between each result row.
NEWLINE
};
/** /**
This class provides an application which aims to implement the This class provides an application which aims to implement the
rudimentary SQL-driven test tool described in the accompanying rudimentary SQL-driven test tool described in the accompanying
@ -159,9 +166,9 @@ public class SQLTester {
StringBuilder getInputBuffer(){ return inputBuffer; } StringBuilder getInputBuffer(){ return inputBuffer; }
String getInputBufferText(){ return inputBuffer.toString(); } String getInputText(){ return inputBuffer.toString(); }
String getResultBufferText(){ return resultBuffer.toString(); } String getResultText(){ return resultBuffer.toString(); }
private String takeBuffer(StringBuilder b){ private String takeBuffer(StringBuilder b){
final String rc = b.toString(); final String rc = b.toString();
@ -269,7 +276,9 @@ public class SQLTester {
} }
public int execSql(sqlite3 db, boolean throwOnError, public int execSql(sqlite3 db, boolean throwOnError,
ResultBufferMode appendMode, String sql) throws Exception { ResultBufferMode appendMode,
ResultRowMode lineMode,
String sql) throws Exception {
final OutputPointer.Int32 oTail = new OutputPointer.Int32(); final OutputPointer.Int32 oTail = new OutputPointer.Int32();
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
final byte[] sqlUtf8 = sql.getBytes(StandardCharsets.UTF_8); final byte[] sqlUtf8 = sql.getBytes(StandardCharsets.UTF_8);
@ -328,7 +337,10 @@ public class SQLTester {
Util.toss(RuntimeException.class, "Unhandled ResultBufferMode."); Util.toss(RuntimeException.class, "Unhandled ResultBufferMode.");
} }
} }
//sb.append('\n'); if( ResultRowMode.NEWLINE == lineMode ){
spacing = 0;
sb.append('\n');
}
} }
}else{ }else{
while( SQLITE_ROW == (rc = sqlite3_step(stmt)) ){} while( SQLITE_ROW == (rc = sqlite3_step(stmt)) ){}
@ -428,8 +440,9 @@ abstract class Command {
protected final void argcCheck(String[] argv, int min, int max) throws Exception{ protected final void argcCheck(String[] argv, int min, int max) throws Exception{
int argc = argv.length-1; int argc = argv.length-1;
if(argc<min || (max>=0 && argc>max)){ if(argc<min || (max>=0 && argc>max)){
if( min==max ) Util.badArg(argv[0],"requires exactly",min,"argument(s)"); if( min==max ){
else if(max>0){ Util.badArg(argv[0]," requires exactly ",min," argument(s)");
}else if(max>0){
Util.badArg(argv[0]," requires ",min,"-",max," arguments."); Util.badArg(argv[0]," requires ",min,"-",max," arguments.");
}else{ }else{
Util.badArg(argv[0]," requires at least ",min," arguments."); Util.badArg(argv[0]," requires at least ",min," arguments.");
@ -447,20 +460,19 @@ abstract class Command {
//! Throws if content is not null. //! Throws if content is not null.
protected void affirmNoContent(String content) throws Exception{ protected void affirmNoContent(String content) throws Exception{
if(null != content){ if(null != content){
Util.badArg(this.getClass().getName(),"does not accept content."); Util.badArg(this.getClass().getName()," does not accept content.");
} }
} }
//! Throws if content is null. //! Throws if content is null.
protected void affirmHasContent(String content) throws Exception{ protected void affirmHasContent(String content) throws Exception{
if(null == content){ if(null == content){
Util.badArg(this.getClass().getName(),"requires content."); Util.badArg(this.getClass().getName()," requires content.");
} }
} }
} }
class CloseDbCommand extends Command { class CloseDbCommand extends Command {
public CloseDbCommand(){}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,0,1); argcCheck(argv,0,1);
affirmNoContent(content); affirmNoContent(content);
@ -485,7 +497,6 @@ class CloseDbCommand extends Command {
//! --db command //! --db command
class DbCommand extends Command { class DbCommand extends Command {
public DbCommand(){}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,1); argcCheck(argv,1);
affirmNoContent(content); affirmNoContent(content);
@ -500,6 +511,13 @@ class GlobCommand extends Command {
public GlobCommand(){} public GlobCommand(){}
protected GlobCommand(boolean negate){ this.negate = negate; } protected GlobCommand(boolean negate){ this.negate = negate; }
public static String globToStrglob(String g){
/* FIXME: '#' support needs to match 1+ digits, but
sqlite3_strglob() does not support that option. We'll
need a custom glob routine for that. */;
return g.replace("#","[0-9]").trim();
}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,1); argcCheck(argv,1);
affirmNoContent(content); affirmNoContent(content);
@ -507,22 +525,22 @@ class GlobCommand extends Command {
t.incrementTestCounter(); t.incrementTestCounter();
final String sql = t.takeInputBuffer(); final String sql = t.takeInputBuffer();
//t.verbose(argv[0]," SQL =\n",sql); //t.verbose(argv[0]," SQL =\n",sql);
int rc = t.execSql(null, true, ResultBufferMode.ESCAPED, sql); int rc = t.execSql(null, true, ResultBufferMode.ESCAPED,
final String result = t.getResultBufferText().trim(); ResultRowMode.ONELINE, sql);
final String result = t.getResultText().trim();
final String sArgs = Util.argvToString(argv); final String sArgs = Util.argvToString(argv);
//t.verbose(argv[0]," rc = ",rc," result buffer:\n", result,"\nargs:\n",sArgs); //t.verbose(argv[0]," rc = ",rc," result buffer:\n", result,"\nargs:\n",sArgs);
final String glob = argv[1].replace("#","[0-9]"); final String glob = globToStrglob(argv[1]);
rc = sqlite3_strglob(glob, result); rc = sqlite3_strglob(glob, result);
if( (negate && 0==rc) || (!negate && 0!=rc) ){ if( (negate && 0==rc) || (!negate && 0!=rc) ){
Util.toss(TestFailure.class, this.getClass().getSimpleName(), Util.toss(TestFailure.class, argv[0], " mismatch: ",
" glob mismatch: ",glob," vs input: ",result); glob," vs input: ",result);
} }
} }
} }
//! --new command //! --new command
class NewDbCommand extends Command { class NewDbCommand extends Command {
public NewDbCommand(){}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,1); argcCheck(argv,1);
affirmNoContent(content); affirmNoContent(content);
@ -535,7 +553,6 @@ class NewDbCommand extends Command {
//! Placeholder dummy/no-op command //! Placeholder dummy/no-op command
class NoopCommand extends Command { class NoopCommand extends Command {
public NoopCommand(){}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
} }
} }
@ -549,7 +566,6 @@ class NotGlobCommand extends GlobCommand {
//! --null command //! --null command
class NullCommand extends Command { class NullCommand extends Command {
public NullCommand(){}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,1); argcCheck(argv,1);
affirmNoContent(content); affirmNoContent(content);
@ -560,7 +576,6 @@ class NullCommand extends Command {
//! --open command //! --open command
class OpenDbCommand extends Command { class OpenDbCommand extends Command {
public OpenDbCommand(){}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,1); argcCheck(argv,1);
affirmNoContent(content); affirmNoContent(content);
@ -572,7 +587,6 @@ class OpenDbCommand extends Command {
//! --print command //! --print command
class PrintCommand extends Command { class PrintCommand extends Command {
public PrintCommand(){}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
if( 1==argv.length && null==content ){ if( 1==argv.length && null==content ){
Util.badArg(argv[0]," requires at least 1 argument or body content."); Util.badArg(argv[0]," requires at least 1 argument or body content.");
@ -583,15 +597,15 @@ class PrintCommand extends Command {
} }
class ResultCommand extends Command { class ResultCommand extends Command {
public ResultCommand(){}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,0,-1); argcCheck(argv,0,-1);
affirmNoContent(content); affirmNoContent(content);
t.incrementTestCounter(); t.incrementTestCounter();
final String sql = t.takeInputBuffer(); final String sql = t.takeInputBuffer();
//t.verbose(argv[0]," SQL =\n",sql); //t.verbose(argv[0]," SQL =\n",sql);
int rc = t.execSql(null, true, ResultBufferMode.ESCAPED, sql); int rc = t.execSql(null, true, ResultBufferMode.ESCAPED,
final String result = t.getResultBufferText().trim(); ResultRowMode.ONELINE, sql);
final String result = t.getResultText().trim();
final String sArgs = argv.length>1 ? Util.argvToString(argv) : ""; final String sArgs = argv.length>1 ? Util.argvToString(argv) : "";
//t.verbose(argv[0]," rc = ",rc," result buffer:\n", result,"\nargs:\n",sArgs); //t.verbose(argv[0]," rc = ",rc," result buffer:\n", result,"\nargs:\n",sArgs);
if( !result.equals(sArgs) ){ if( !result.equals(sArgs) ){
@ -601,14 +615,14 @@ class ResultCommand extends Command {
} }
class RunCommand extends Command { class RunCommand extends Command {
public RunCommand(){}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,0,1); argcCheck(argv,0,1);
affirmHasContent(content); affirmHasContent(content);
final sqlite3 db = (1==argv.length) final sqlite3 db = (1==argv.length)
? t.getCurrentDb() : t.getDbById( Integer.parseInt(argv[1]) ); ? t.getCurrentDb() : t.getDbById( Integer.parseInt(argv[1]) );
int rc = t.execSql(db, false, ResultBufferMode.NONE, content); int rc = t.execSql(db, false, ResultBufferMode.NONE,
if( 0!=rc ){ ResultRowMode.ONELINE, content);
if( 0!=rc && t.isVerbose() ){
String msg = sqlite3_errmsg(db); String msg = sqlite3_errmsg(db);
t.verbose(argv[0]," non-fatal command error #",rc,": ", t.verbose(argv[0]," non-fatal command error #",rc,": ",
msg,"\nfor SQL:\n",content); msg,"\nfor SQL:\n",content);
@ -616,8 +630,39 @@ class RunCommand extends Command {
} }
} }
class TableResultCommand extends Command {
public void process(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,0);
affirmHasContent(content);
if( !content.endsWith("\n--end") ){
Util.toss(TestFailure.class, argv[0], " must be terminated with --end.");
}else{
int n = content.length();
content = content.substring(0, n-6);
}
final String[] globs = content.split("\s*\n\s*");
if( globs.length < 1 ){
Util.toss(TestFailure.class, argv[0], " requires 1 or more globs.");
}
final String sql = t.takeInputBuffer();
t.execSql(null, true, ResultBufferMode.ESCAPED, ResultRowMode.NEWLINE, sql);
final String rbuf = t.getResultText();
final String[] res = rbuf.split("\n");
if( res.length != globs.length ){
Util.toss(TestFailure.class, argv[0], " failure: input has ",
res.length," row(s) but expecting ",globs.length);
}
for(int i = 0; i < res.length; ++i){
final String glob = GlobCommand.globToStrglob(globs[i]).replaceAll("\s+"," ");
if( 0 != sqlite3_strglob(glob, res[i]) ){
Util.toss(TestFailure.class, argv[0], " glob {",glob,
"} does not match: {",res[i],"}");
}
}
}
}
class TestCaseCommand extends Command { class TestCaseCommand extends Command {
public TestCaseCommand(){}
public void process(SQLTester t, String[] argv, String content) throws Exception{ public void process(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,1); argcCheck(argv,1);
affirmHasContent(content); affirmHasContent(content);
@ -650,6 +695,7 @@ class CommandDispatcher {
case "print": rv = new PrintCommand(); break; case "print": rv = new PrintCommand(); break;
case "result": rv = new ResultCommand(); break; case "result": rv = new ResultCommand(); break;
case "run": rv = new RunCommand(); break; case "run": rv = new RunCommand(); break;
case "tableresult": rv = new TableResultCommand(); break;
case "testcase": rv = new TestCaseCommand(); break; case "testcase": rv = new TestCaseCommand(); break;
default: rv = null; break; default: rv = null; break;
} }

View File

@ -195,7 +195,7 @@ class TestScript {
final String[] parts = block.split("\\n", 2); final String[] parts = block.split("\\n", 2);
chunk.argv = parts[0].split("\\s+"); chunk.argv = parts[0].split("\\s+");
if( parts.length>1 && parts[1].length()>0 ){ if( parts.length>1 && parts[1].length()>0 ){
chunk.content = parts[1]; chunk.content = parts[1].trim();
} }
rc.add( chunk ); rc.add( chunk );
} }

View File

@ -28,11 +28,18 @@ select 'a', 'b';
--result a b a b --result a b a b
--testcase second --testcase second
select 123 select 123
--glob #2# --glob ###
--testcase second --testcase third
select 'a' select 'a'
--notglob # --notglob #
--close --close
--open SQLTester.db --open SQLTester.db
--print Re-opened db. --print Re-opened db.
--testcase fourth
SELECT 1, 2;
SELECT 'b', 'c';
--tableresult
[0-9] #
b c
--end
--an-uknown-command --an-uknown-command

View File

@ -1,5 +1,5 @@
C Clean\sup\sthe\sSQLTester\soutput\sa\sbit\sby\susing\sthe\smodule\sname,\sinstead\sof\sfilename,\swhere\sappropriate. C Add\sSQLTester\s--tableresult\scommand.
D 2023-08-09T12:05:17.196 D 2023-08-09T13:16:10.208
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -266,10 +266,10 @@ F ext/jni/src/org/sqlite/jni/sqlite3_context.java d26573fc7b309228cb49786e907859
F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc 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/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a
F ext/jni/src/org/sqlite/jni/tester/Outer.java 3d9c40f8ed58ec0df05ca160986ea06ec84ec1f338b069cfba9604bbba467a01 F ext/jni/src/org/sqlite/jni/tester/Outer.java 3d9c40f8ed58ec0df05ca160986ea06ec84ec1f338b069cfba9604bbba467a01
F ext/jni/src/org/sqlite/jni/tester/SQLTester.java 42b694da25e20a246140e32d8aa044e65ed60c67f69adcf27c326a1d18b04228 F ext/jni/src/org/sqlite/jni/tester/SQLTester.java 59ac50bbc1abc37b34bc4cd47b564d770de2828e045ba59c056f873543de10b3
F ext/jni/src/org/sqlite/jni/tester/TestScript.java 57a5bb63e56324fe20b31142a8704b08cfc0bdff9e936620346fad659fb91759 F ext/jni/src/org/sqlite/jni/tester/TestScript.java f2a87c88ab23fa4601a985eb69bdc8b4f81cabfab04fdc3544ecefde207e08d4
F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md ae1d6706f723517e03a04ab578a539fa3df66fe38adad113f10b61eabc524d09 F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md ae1d6706f723517e03a04ab578a539fa3df66fe38adad113f10b61eabc524d09
F ext/jni/src/tests/000_first.test 954c19705c791023eb5a473de0851d3727406fdef25f4b2521b88972280b4111 F ext/jni/src/tests/000_first.test 461f465cd1e9c60f19a8fd4231721c1bbd01702d9677696d56087a58f9d2e09e
F ext/jni/src/tests/010_ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 F ext/jni/src/tests/010_ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70
F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9 F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9
F ext/lsm1/Makefile.msc f8c878b467232226de288da320e1ac71c131f5ec91e08b21f502303347260013 F ext/lsm1/Makefile.msc f8c878b467232226de288da320e1ac71c131f5ec91e08b21f502303347260013
@ -2090,8 +2090,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P ab9c945bb0b4210b3f47e6341f150f8a7cc45f9e4e4c2247e91d2528ed4772a6 P 5323e4fd254274cc527af7536c622b786394599c68eca2da6c7fc641727dbdb2
R 71db74e5804fc1b921164f2085cce4db R c0ec1fc5e7c5611a47b223974ef77855
U stephan U stephan
Z d1b5b5a17f205dc2910f24396b36f01b Z 3ccc1c720ec01884cd7c12ccaa2f6b70
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
5323e4fd254274cc527af7536c622b786394599c68eca2da6c7fc641727dbdb2 8c5b6d893df4a4e82c6d8e07507fc160b11412ede4bb903ff4e3f5ffa59a9cb9