1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Port the SQLTester 'v1' commands to the 'v2' evaluation bits. Still TODO is swapping out v1 with these separate impls.

FossilOrigin-Name: 0cf57e5b0f90779e450e9db1ca009610df5e6f4487337d49017636bde3bb02d6
This commit is contained in:
stephan
2023-08-09 23:47:14 +00:00
parent 35af4c5ff1
commit e35a703b76
5 changed files with 302 additions and 40 deletions

View File

@ -234,7 +234,7 @@ public class SQLTester {
sqlite3 getCurrentDb(){ return aDb[iCurrentDb]; }
sqlite3 getDbById(int id) throws Exception{
return affirmDbId(id).aDb[iCurrentDb];
return affirmDbId(id).aDb[id];
}
void closeDb(int id) throws Exception{

View File

@ -12,6 +12,8 @@
** This file contains the TestScript2 part of the SQLTester framework.
*/
package org.sqlite.jni.tester;
import static org.sqlite.jni.SQLite3Jni.*;
import org.sqlite.jni.sqlite3;
import java.util.Arrays;
import java.nio.charset.StandardCharsets;
import java.util.regex.*;
@ -22,6 +24,12 @@ class SQLTestException extends RuntimeException {
}
}
class TestScript2Failed extends SQLTestException {
public TestScript2Failed(TestScript2 ts, String msg){
super(ts.getOutputPrefix()+": "+msg);
}
}
class SkipTestRemainder2 extends SQLTestException {
public SkipTestRemainder2(TestScript2 ts){
super(ts.getOutputPrefix()+": skipping remainder");
@ -73,23 +81,221 @@ abstract class Command2 {
}
}
//! --close command
class CloseDbCommand2 extends Command2 {
public void process(SQLTester t, TestScript2 ts, String[] argv) throws Exception{
argcCheck(argv,0,1);
Integer id;
if(argv.length>1){
String arg = argv[1];
if("all".equals(arg)){
t.closeAllDbs();
return;
}
else{
id = Integer.parseInt(arg);
}
}else{
id = t.getCurrentDbId();
}
t.closeDb(id);
}
}
//! --db command
class DbCommand2 extends Command2 {
public void process(SQLTester t, TestScript2 ts, String[] argv) throws Exception{
argcCheck(argv,1);
t.setCurrentDb( Integer.parseInt(argv[1]) );
}
}
//! --glob command
class GlobCommand2 extends Command2 {
private boolean negate = false;
public GlobCommand2(){}
protected GlobCommand2(boolean negate){ this.negate = negate; }
public void process(SQLTester t, TestScript2 ts, String[] argv) throws Exception{
argcCheck(argv,1);
t.incrementTestCounter();
final String sql = t.takeInputBuffer();
int rc = t.execSql(null, true, ResultBufferMode.ESCAPED,
ResultRowMode.ONELINE, sql);
final String result = t.getResultText();
final String sArgs = Util.argvToString(argv);
//t.verbose(argv[0]," rc = ",rc," result buffer:\n", result,"\nargs:\n",sArgs);
final String glob = argv[1];
rc = SQLTester.strglob(glob, result);
if( (negate && 0==rc) || (!negate && 0!=rc) ){
ts.toss(argv[0], " mismatch: ", glob," vs input: ",result);
}
}
}
//! --json command
class JsonCommand2 extends ResultCommand2 {
public JsonCommand2(){ super(ResultBufferMode.ASIS); }
}
//! --json-block command
class JsonBlockCommand2 extends TableResultCommand2 {
public JsonBlockCommand2(){ super(true); }
}
//! --new command
class NewDbCommand2 extends OpenDbCommand2 {
public NewDbCommand2(){ super(true); }
}
//! Placeholder dummy/no-op commands
class NoopCommand2 extends Command2 {
public void process(SQLTester t, TestScript2 ts, String[] argv) throws Exception{
}
}
//! --notglob command
class NotGlobCommand2 extends GlobCommand2 {
public NotGlobCommand2(){
super(true);
}
}
//! --null command
class NullCommand2 extends Command2 {
public void process(
SQLTester st, TestScript2 ts, String[] argv
) throws Exception{
argcCheck(argv,1);
st.setNullValue( argv[1] );
}
}
//! --open command
class OpenDbCommand2 extends Command2 {
private boolean createIfNeeded = false;
public OpenDbCommand2(){}
protected OpenDbCommand2(boolean c){createIfNeeded = c;}
public void process(SQLTester t, TestScript2 ts, String[] argv) throws Exception{
argcCheck(argv,1);
t.openDb(argv[1], createIfNeeded);
}
}
//! --print command
class PrintCommand2 extends Command2 {
public void process(
SQLTester st, TestScript2 ts, String[] argv
) throws Exception{
st.out(ts.getOutputPrefix(),": ");
if( 1==argv.length ){
st.outln( st.getInputText() );
final String body = ts.fetchCommandBody();
if( 1==argv.length && null==body ){
st.out( st.getInputText() );
}else{
st.outln( Util.argvToString(argv) );
}
final String body = ts.fetchCommandBody();
if( null!=body ){
st.out(body,"\n");
st.out(body);
}
}
}
//! --result command
class ResultCommand2 extends Command2 {
private final ResultBufferMode bufferMode;
protected ResultCommand2(ResultBufferMode bm){ bufferMode = bm; }
public ResultCommand2(){ this(ResultBufferMode.ESCAPED); }
public void process(SQLTester t, TestScript2 ts, String[] argv) throws Exception{
argcCheck(argv,0,-1);
t.incrementTestCounter();
final String sql = t.takeInputBuffer();
//t.verbose(argv[0]," SQL =\n",sql);
int rc = t.execSql(null, false, bufferMode, ResultRowMode.ONELINE, sql);
final String result = t.getResultText().trim();
final String sArgs = argv.length>1 ? Util.argvToString(argv) : "";
if( !result.equals(sArgs) ){
t.outln(argv[0]," FAILED comparison. Result buffer:\n",
result,"\nargs:\n",sArgs);
ts.toss(argv[0]+" comparison failed.");
}
}
}
//! --run command
class RunCommand2 extends Command2 {
public void process(SQLTester t, TestScript2 ts, String[] argv) throws Exception{
argcCheck(argv,0,1);
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);
if( 0!=rc && t.isVerbose() ){
String msg = sqlite3_errmsg(db);
t.verbose(argv[0]," non-fatal command error #",rc,": ",
msg,"\nfor SQL:\n",sql);
}
}
}
//! --tableresult command
class TableResultCommand2 extends Command2 {
private final boolean jsonMode;
protected TableResultCommand2(boolean jsonMode){ this.jsonMode = jsonMode; }
public TableResultCommand2(){ this(false); }
public void process(SQLTester t, TestScript2 ts, String[] argv) throws Exception{
argcCheck(argv,0);
t.incrementTestCounter();
String body = ts.fetchCommandBody();
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.");
}else{
int n = body.length();
body = body.substring(0, n-6);
}
final String[] globs = body.split("\\s*\\n\\s*");
if( globs.length < 1 ){
ts.toss(argv[0], " requires 1 or more ",
(jsonMode ? "json snippets" : "globs"),".");
}
final String sql = t.takeInputBuffer();
t.execSql(null, true,
jsonMode ? ResultBufferMode.ASIS : ResultBufferMode.ESCAPED,
ResultRowMode.NEWLINE, sql);
final String rbuf = t.getResultText();
final String[] res = rbuf.split("\n");
if( res.length != globs.length ){
ts.toss(argv[0], " failure: input has ", res.length,
" row(s) but expecting ",globs.length);
}
for(int i = 0; i < res.length; ++i){
final String glob = globs[i].replaceAll("\\s+"," ").trim();
//t.verbose(argv[0]," <<",glob,">> vs <<",res[i],">>");
if( jsonMode ){
if( !glob.equals(res[i]) ){
ts.toss(argv[0], " json <<",glob, ">> does not match: <<",
res[i],">>");
}
}else if( 0 != SQLTester.strglob(glob, res[i]) ){
ts.toss(argv[0], " glob <<",glob,">> does not match: <<",res[i],">>");
}
}
}
}
//! --testcase command
class TestCaseCommand2 extends Command2 {
public void process(SQLTester t, TestScript2 ts, String[] argv) throws Exception{
argcCheck(argv,1);
// TODO?: do something with the test name
final String body = ts.fetchCommandBody();
t.clearResultBuffer();
t.clearInputBuffer().append(null==body ? "" : body);
}
}
class CommandDispatcher2 {
private static java.util.Map<String,Command2> commandMap =
@ -103,7 +309,21 @@ class CommandDispatcher2 {
Command2 rv = commandMap.get(name);
if( null!=rv ) return rv;
switch(name){
case "close": rv = new CloseDbCommand2(); break;
case "db": rv = new DbCommand2(); break;
case "glob": rv = new GlobCommand2(); break;
case "json": rv = new JsonCommand2(); break;
case "json-block": rv = new JsonBlockCommand2(); break;
case "new": rv = new NewDbCommand2(); break;
case "notglob": rv = new NotGlobCommand2(); break;
case "null": rv = new NullCommand2(); break;
case "oom": rv = new NoopCommand2(); break;
case "open": rv = new OpenDbCommand2(); break;
case "print": rv = new PrintCommand2(); break;
case "result": rv = new ResultCommand2(); break;
case "run": rv = new RunCommand2(); break;
case "tableresult": rv = new TableResultCommand2(); break;
case "testcase": rv = new TestCaseCommand2(); break;
default: rv = null; break;
}
if( null!=rv ) commandMap.put(name, rv);
@ -187,11 +407,10 @@ class TestScript2 {
return "["+(moduleName==null ? filename : moduleName)+"] line "+
cur.lineNo;
}
@SuppressWarnings("unchecked")
private TestScript2 verbose(Object... vals){
private TestScript2 verboseN(int level, Object... vals){
final int verbosity = outer.getVerbosity();
if(verbosity>0){
if(verbosity>=level){
outer.out("VERBOSE",(verbosity>1 ? "+ " : " "),
getOutputPrefix(),": ");
outer.outln(vals);
@ -199,6 +418,9 @@ class TestScript2 {
return this;
}
private TestScript2 verbose1(Object... vals){return verboseN(1,vals);}
private TestScript2 verbose2(Object... vals){return verboseN(2,vals);}
@SuppressWarnings("unchecked")
public TestScript2 warn(Object... vals){
outer.out("WARNING ", getOutputPrefix(),": ");
@ -355,7 +577,7 @@ class TestScript2 {
if(line.startsWith("#")){
throw new IncompatibleDirective(this, "C-preprocessor input: "+line);
}else if(line.startsWith("---")){
new IncompatibleDirective(this, "Triple-dash: "+line);
new IncompatibleDirective(this, "triple-dash: "+line);
}
Matcher m = patternScriptModuleName.matcher(line);
if( m.find() ){
@ -370,12 +592,19 @@ class TestScript2 {
if( m.find() ){
throw new IncompatibleDirective(this, m.group(1)+": "+m.group(3));
}
if( line.indexOf("\n|")>=0 ){
throw new IncompatibleDirective(this, "newline-pipe combination.");
}
return;
}
public boolean isCommandLine(String line){
public boolean isCommandLine(String line, boolean checkForImpl){
final Matcher m = patternCommand.matcher(line);
return m.find();
boolean rc = m.find();
if( rc && checkForImpl ){
rc = null!=CommandDispatcher2.getCommandByName(m.group(2));
}
return rc;
}
/**
@ -388,33 +617,45 @@ class TestScript2 {
}
/**
Fetches lines until the next command. Throws if
Fetches lines until the next recognized command. Throws if
checkForDirective() does. Returns null if there is no input or
it's only whitespace. The returned string is trim()'d of
leading/trailing whitespace.
it's only whitespace. The returned string retains all whitespace.
Note that "subcommands", --command-like constructs in the body
which do not match a known command name are considered to be
content, not commands.
*/
public String fetchCommandBody(){
final StringBuilder sb = new StringBuilder();
String line;
while( (null != (line = peekLine())) ){
checkForDirective(line);
if( !isCommandLine(line) ){
if( !isCommandLine(line, true) ){
sb.append(line).append("\n");
consumePeeked();
}else{
break;
}
}
line = sb.toString().trim();
return line.isEmpty() ? null : line;
line = sb.toString();
return line.trim().isEmpty() ? null : line;
}
public void processCommand(SQLTester t, String[] argv) throws Exception{
//verbose("got argv: ",argv[0], " ", Util.argvToString(argv));
//verbose("Input buffer = ",t.getInputBuffer());
verbose1("running command: ",argv[0], " ", Util.argvToString(argv));
if(outer.getVerbosity()>1){
final String input = t.getInputText();
if( !input.isEmpty() ) verbose2("Input buffer = ",input);
}
CommandDispatcher2.dispatch(t, this, argv);
}
public void toss(Object... msg) throws TestScript2Failed {
StringBuilder sb = new StringBuilder();
for(Object s : msg) sb.append(s);
throw new TestScript2Failed(this, sb.toString());
}
/**
Runs this test script in the context of the given tester object.
*/

View File

@ -1,4 +1,4 @@
/*
/*
** This is a comment. There are many like it but this one is mine.
**
** SCRIPT_MODULE_NAME: sanity-check
@ -8,14 +8,35 @@
** REQUIRED_PROPERTIES:
**
*/
/*#if foo*/
/*---foo*/
/* --print without flags dumps current input buffer */
--print
non-command/non-directive text after --print is also emitted.
--print 🤩😃
SELECT 1;
SELECT 2;
--print the end
--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
--close
--print 🤩😃 the end