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();
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;}
}else{
throw new IllegalArgumentException("Unhandled flag: "+flag);
}
+ continue;
}
t.addTestScript(a);
}
}
/**
- 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:
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.");
}
}
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]);
}
}
+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);
}
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();
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;
}
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 */
+ }
+ }
+
+}
-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
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
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
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.