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:
@ -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;}
|
||||
|
||||
}
|
||||
|
@ -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 */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user