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

Implement the new/open/close SQLTester commands.

FossilOrigin-Name: dc823bf00f78e7cd626329220c42c46da12d565e3273a08eda5fb512c1d807c6
This commit is contained in:
stephan
2023-08-08 11:46:46 +00:00
parent ec9b37b33e
commit 405dffd591
7 changed files with 249 additions and 78 deletions

View File

@ -13,8 +13,11 @@
*/
package org.sqlite.jni.tester;
/**
Console output utility class.
*/
class Outer {
public boolean isVerbose = true;
public boolean verbose = false;
public static void out(Object val){
System.out.print(val);
@ -38,16 +41,14 @@ class Outer {
@SuppressWarnings("unchecked")
public Outer verbose(Object... vals){
if(isVerbose) outln(vals);
if(verbose) outln(vals);
return this;
}
public void setVerbose(boolean b){
isVerbose = b;
verbose = b;
}
public boolean getVerbose(){
return isVerbose;
}
public boolean isVerbose(){return verbose;}
}

View File

@ -28,13 +28,18 @@ import static org.sqlite.jni.SQLite3Jni.*;
public class SQLTester {
//! List of input script files.
private final java.util.List<String> listInFiles = new ArrayList<>();
//! Console output utility.
private final Outer outer = new Outer();
//! Test input buffer.
private final StringBuilder inputBuffer = new StringBuilder();
//! Test result buffer.
private final StringBuilder resultBuffer = new StringBuilder();
private String nullView;
private int nTotalTest = 0;
private int nTestFile = 0;
private int nTest;
private sqlite3[] aDb = {};
private final sqlite3[] aDb = new sqlite3[7];
private int iCurrentDb = 0;
public SQLTester(){
reset();
@ -67,45 +72,100 @@ public class SQLTester {
public void runTests() throws Exception {
// process each input file
outln("Verbose =",outer.isVerbose());
for(String f : listInFiles){
reset();
++nTestFile;
final TestScript ts = new TestScript(f);
ts.setVerbose(this.outer.getVerbose());
verbose(">>> Test",ts.getName(),"...");
outln("---------> Test",ts.getName(),"...");
ts.run(this);
verbose("<<< Ran",nTest,"test(s) in",f);
outln("<---------",nTest,"test(s) in",f);
}
}
private void resetDbs(){
for(sqlite3 db : aDb) sqlite3_close_v2(db);
private StringBuilder resetBuffer(StringBuilder b){
b.delete(0, b.length());
return b;
}
StringBuilder resetInputBuffer(){
inputBuffer.delete(0, inputBuffer.length());
return inputBuffer;
return resetBuffer(inputBuffer);
}
StringBuilder getInputBuffer(){
return inputBuffer;
StringBuilder resetResultBuffer(){
return resetBuffer(resultBuffer);
}
String getInputBufferText(){
return inputBuffer.toString();
}
StringBuilder getInputBuffer(){ return inputBuffer; }
String takeInputBuffer(){
final String rc = inputBuffer.toString();
resetInputBuffer();
String getInputBufferText(){ return inputBuffer.toString(); }
private String takeBuffer(StringBuilder b){
final String rc = b.toString();
resetBuffer(b);
return rc;
}
String takeInputBuffer(){ return takeBuffer(inputBuffer); }
String takeResultBuffer(){ return takeBuffer(resultBuffer); }
int getCurrentDbId(){ return iCurrentDb; }
SQLTester affirmDbId(int n) throws Exception{
if(n<0 || n>=aDb.length){
Util.toss(IllegalArgumentException.class,"illegal db number.");
}
return this;
}
sqlite3 setCurrentDb(int n) throws Exception{
return affirmDbId(n).aDb[n];
}
sqlite3 getCurrentDb(){ return aDb[iCurrentDb]; }
void closeDb(int id) throws Exception{
final sqlite3 db = affirmDbId(id).aDb[id];
if( null != db ){
sqlite3_close_v2(db);
aDb[id] = null;
}
}
void closeDb() throws Exception { closeDb(iCurrentDb); }
void closeAllDbs(){
for(int i = 0; i<aDb.length; ++i){
sqlite3_close_v2(aDb[i]);
aDb[i] = null;
}
}
sqlite3 openDb(String name, boolean createIfNeeded) throws Exception {
closeDb();
int flags = SQLITE_OPEN_READWRITE;
if( createIfNeeded ) flags |= SQLITE_OPEN_CREATE;
final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
int rc = sqlite3_open_v2(name, out, flags, null);
final sqlite3 db = out.getValue();
if( 0!=rc ){
final String msg = sqlite3_errmsg(db);
sqlite3_close(db);
Util.toss("db open failed with code",rc,"and message:",msg);
}
return aDb[iCurrentDb] = db;
}
/**
Resets all tester context state except for that related to
tracking running totals.
*/
void reset(){
nTest = 0;
nullView = "nil";
resetInputBuffer();
resetDbs();
closeAllDbs();
}
void setNullValue(String v){nullView = v;}
@ -124,6 +184,7 @@ public class SQLTester {
}else{
throw new IllegalArgumentException("Unhandled flag: "+flag);
}
continue;
}
t.addTestScript(a);
}
@ -133,7 +194,8 @@ public class SQLTester {
}
/**
Base class for test script commands.
Base class for test script commands. It provides a set of utility
APIs for concrete command implementations.
Each subclass must have a ctor with this signature:
@ -144,37 +206,24 @@ public class SQLTester {
very basic argc validation.
The content is any text content which was specified after the
command. Any command which does not permit content must pass that
argument to affirmNoContent() in their constructor.
command, or null if there is null. Any command which does not
permit content must pass that argument to affirmNoContent() in
their constructor. Similary, those which require content should
pass it to affirmHasContent().
For simplicity, instantiating the test is intended to execute it,
as opposed to delaying execution until a method devoted to that.
Tests must throw on error.
*/
class Command {
protected SQLTester tester;
Command(SQLTester t){tester = t;}
protected final void toss(Class<? extends Exception> errorType, Object... msg) throws Exception {
StringBuilder sb = new StringBuilder();
int i = 0;
for(Object s : msg) sb.append(((0==i++) ? "" : " ")+s);
final java.lang.reflect.Constructor<? extends Exception> ctor =
errorType.getConstructor(String.class);
throw ctor.newInstance(sb.toString());
}
protected final void toss(Object... msg) throws Exception{
toss(RuntimeException.class, msg);
}
protected final void badArg(Object... msg) throws Exception{
toss(IllegalArgumentException.class, msg);
}
protected Command(){}
protected final void argcCheck(String[] argv, int min, int max) throws Exception{
int argc = argv.length-1;
if(argc<min || argc>max){
if( min==max ) badArg(argv[0],"requires exactly",min,"argument(s)");
else badArg(argv[0],"requires",min,"-",max,"arguments.");
if( min==max ) Util.badArg(argv[0],"requires exactly",min,"argument(s)");
else Util.badArg(argv[0],"requires",min,"-",max,"arguments.");
}
}
@ -182,25 +231,90 @@ class Command {
argcCheck(argv, argc, argc);
}
//! Throws if content is not null.
protected void affirmNoContent(String content) throws Exception{
if(null != content){
badArg(this.getClass().getName(),"does not accept content.");
Util.badArg(this.getClass().getName(),"does not accept content.");
}
}
//! Throws if content is null.
protected void affirmHasContent(String content) throws Exception{
if(null == content){
Util.badArg(this.getClass().getName(),"requires content.");
}
}
}
class CloseDbCommand extends Command {
public CloseDbCommand(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,0,1);
affirmNoContent(content);
Integer id;
if(argv.length>1){
String arg = argv[1];
if("all".equals(arg)){
t.verbose(argv[0],"all dbs");
t.closeAllDbs();
return;
}
else{
id = Integer.parseInt(arg);
}
}else{
id = t.getCurrentDbId();
}
t.closeDb(id);
t.verbose(argv[0],"db",id);
}
}
class DbCommand extends Command {
public DbCommand(SQLTester t, String[] argv, String content) throws Exception{
super(t);
argcCheck(argv,1);
affirmNoContent(content);
//t.verbose(argv[0],argv[1]);
final sqlite3 db = t.setCurrentDb( Integer.parseInt(argv[1]) );
t.verbose(argv[0],"set db to",db);
}
}
class GlobCommand extends Command {
protected GlobCommand(boolean negate, SQLTester t,
String[] argv, String content) throws Exception{
argcCheck(argv,1);
affirmNoContent(content);
final String glob = argv[1].replace("#","[0-9]");
t.verbose(argv[0],"is TODO. Pattern =",glob);
}
public GlobCommand(SQLTester t, String[] argv, String content) throws Exception{
this(false, t, argv, content);
}
}
class NewDbCommand extends Command {
public NewDbCommand(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,1);
affirmNoContent(content);
String fname = argv[1];
Util.unlink(fname);
final sqlite3 db = t.openDb(fname, true);
t.verbose(argv[0],"db",db);
}
}
class NoopCommand extends Command {
public NoopCommand(SQLTester t, String[] argv, String content) throws Exception{
}
}
class NotGlobCommand extends GlobCommand {
public NotGlobCommand(SQLTester t, String[] argv, String content) throws Exception{
super(true, t, argv, content);
}
}
class NullCommand extends Command {
public NullCommand(SQLTester t, String[] argv, String content) throws Exception{
super(t);
argcCheck(argv,1);
affirmNoContent(content);
t.setNullValue(argv[1]);
@ -208,9 +322,20 @@ class NullCommand extends Command {
}
}
class OpenDbCommand extends Command {
public OpenDbCommand(SQLTester t, String[] argv, String content) throws Exception{
argcCheck(argv,1);
affirmNoContent(content);
String fname = argv[1];
Util.unlink(fname);
final sqlite3 db = t.openDb(fname, false);
t.verbose(argv[0],"db",db);
}
}
class PrintCommand extends Command {
public PrintCommand(SQLTester t, String[] argv, String content) throws Exception{
super(t);
argcCheck(argv,0);
t.outln(content);
}
@ -218,7 +343,6 @@ class PrintCommand extends Command {
class ResultCommand extends Command {
public ResultCommand(SQLTester t, String[] argv, String content) throws Exception{
super(t);
argcCheck(argv,0);
//t.verbose(argv[0],"command is TODO");
t.incrementTestCounter();
@ -227,19 +351,28 @@ class ResultCommand extends Command {
class TestCaseCommand extends Command {
public TestCaseCommand(SQLTester t, String[] argv, String content) throws Exception{
super(t);
argcCheck(argv,1);
//t.verbose(argv[0],argv[1]);
affirmHasContent(content);
t.resetInputBuffer();
t.resetResultBuffer().append(content);
t.verbose(argv[0],"result buffer:",content);
}
}
class CommandDispatcher {
static Class getCommandByName(String name){
switch(name){
case "db": return DbCommand.class;
case "null": return NullCommand.class;
case "print": return PrintCommand.class;
case "result": return ResultCommand.class;
case "close": return CloseDbCommand.class;
case "db": return DbCommand.class;
case "glob": return GlobCommand.class;
case "new": return NewDbCommand.class;
case "notglob": return NotGlobCommand.class;
case "null": return NullCommand.class;
case "oom": return NoopCommand.class;
case "open": return OpenDbCommand.class;
case "print": return PrintCommand.class;
case "result": return ResultCommand.class;
case "testcase": return TestCaseCommand.class;
default: return null;
}
@ -259,3 +392,32 @@ class CommandDispatcher {
ctor.newInstance(tester, argv, content);
}
}
final class Util {
public static void toss(Class<? extends Exception> errorType, Object... msg) throws Exception {
StringBuilder sb = new StringBuilder();
int i = 0;
for(Object s : msg) sb.append(((0==i++) ? "" : " ")+s);
final java.lang.reflect.Constructor<? extends Exception> ctor =
errorType.getConstructor(String.class);
throw ctor.newInstance(sb.toString());
}
public static void toss(Object... msg) throws Exception{
toss(RuntimeException.class, msg);
}
public static void badArg(Object... msg) throws Exception{
toss(IllegalArgumentException.class, msg);
}
public static void unlink(String filename){
try{
final java.io.File f = new java.io.File(filename);
f.delete();
}catch(Exception e){
/* ignore */
}
}
}

View File

@ -170,10 +170,12 @@ which database connection to use moving forward.
The --close command causes an existing database connetion to close.
This command is a no-op if the database connection is not currently
open. There can be up to 7 different database connections, numbered
0 through 6. The number of the database connection to close is an argument
to the --close command. Or if the argument to --close is "all" then all
open database connections are closed.
open. There can be up to 7 different database connections, numbered 0
through 6. The number of the database connection to close is an
argument to the --close command, which will fail if an out-of-range
value is provided. Or if the argument to --close is "all" then all
open database connections are closed. If passed no argument, the
currently-active database is assumed.
### The --null command

View File

@ -4,10 +4,11 @@
junk
--new SQLTester.db
--null zilch
--oom
--print
This is from the print command.
--db 1
--- also ignored
--testcase first
input for the first
@ -16,5 +17,8 @@ command;
hello world
--testcase second
select 1
--result /* ignored */
1
--glob # /* ignored */
--testcase second
select 'a'
--notglob #
--close