1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +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

@ -218,10 +218,12 @@ test: $(SQLite3Jni.class) $(sqlite3-jni.dll)
tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test))
.PHONY: tester
tester.flags ?= --verbose
tester: $(CLASS_FILES.tester) $(sqlite3-jni.dll)
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath) \
org.sqlite.jni.tester.SQLTester $(tester.scripts)
org.sqlite.jni.tester.SQLTester $(tester.flags) $(tester.scripts)
$(package.jar): $(CLASS_FILES) $(MAKEFILE)
rm -f $(dir.src)/c/*~ $(dir.src.jni)/*~

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

View File

@ -1,5 +1,5 @@
C Add\sa\sJNI-layer\ssqlite3.toString()\sfor\sdebugging.
D 2023-08-08T11:46:26.111
C Implement\sthe\snew/open/close\sSQLTester\scommands.
D 2023-08-08T11:46:46.234
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -230,7 +230,7 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
F ext/jni/GNUmakefile 2b800c74db98b64b63ec1da48dc4a27738f88951f0ca43011288abf80c1b5e80
F ext/jni/GNUmakefile 0d071597509ef4a9ac4b7712dac9ef29ded0db4819721c3b3c15e24d534827f6
F ext/jni/README.md e965674505e105626127ad45e628e4d19fcd379cdafc4d23c814c1ac2c55681d
F ext/jni/src/c/sqlite3-jni.c eb105982266523f4cd9c5007c3cc713855aca520a0f24fce138e4ecfd573fc20
F ext/jni/src/c/sqlite3-jni.h bc3ecd3f6e479fd45b80214f6256584cc599336ae222822fa1e603c22ff1fb19
@ -264,11 +264,11 @@ F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c38
F ext/jni/src/org/sqlite/jni/sqlite3_context.java d26573fc7b309228cb49786e9078597d96232257defa955a3425d10897bca810
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/Outer.java c35a54bd3fd3363ba2abb5533453454d8ffe3f942c9a37a7921c8f6739762e82
F ext/jni/src/org/sqlite/jni/tester/SQLTester.java 1832399b73a1e246892149cbfad0ca5b8cf1ed69072322059fa9a14b2da2b1f1
F ext/jni/src/org/sqlite/jni/tester/Outer.java 07b5d68bdc4a01173f2515954a250dce6affcc4efb85b1ac50d24ad05b166bf4
F ext/jni/src/org/sqlite/jni/tester/SQLTester.java 2261be136c31a432a7416fa86810b97a28a2ad8012463ae68f0975ceac38b42f
F ext/jni/src/org/sqlite/jni/tester/TestScript.java 38652e01cab9c07b20741829f54ef2f4a5c25a73b2c77213dd9198d4268acc51
F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md 2627f8ac7c3d3f502404d9a9b8481bad5c2d11df7fdf25fbd0e1dbd2bcaa54c3
F ext/jni/src/tests/000_first.test 00b2347d4b974e67682859c292bc0d200788ab3f462eac922b8036f4e07114fc
F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md 99d2b8521af9362382ca57b19ae2f3f48db9909221aa9b20ae1a97255120760c
F ext/jni/src/tests/000_first.test f58d5f22e2db31b20c6e744a247d14222508c88ed876b03a723c89f540948518
F ext/jni/src/tests/010_ignored.test ce2de6742ff1bf98d8976fda0f260ff3d280e8f8c0a99309fb59fcfef2556fcd
F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9
F ext/lsm1/Makefile.msc f8c878b467232226de288da320e1ac71c131f5ec91e08b21f502303347260013
@ -2089,8 +2089,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 eb5440f71be32812f6310756b8e30958002e8e8e41a7eb16f081058ff733b47c
R 4cc267c21faf6068f5badbc12783e171
P 456691649aa2a7672d5d110acdde92426a9d34552863db3e0c86b73d9c5d9aac
R 4258bda3ec968b33da5f24ae8a1129aa
U stephan
Z 92a4b7bac87478c8b69fd6008856678d
Z 1a99f4c0beb5aad623e0f3c7cf22f4ec
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
456691649aa2a7672d5d110acdde92426a9d34552863db3e0c86b73d9c5d9aac
dc823bf00f78e7cd626329220c42c46da12d565e3273a08eda5fb512c1d807c6