From: stephan Date: Mon, 7 Aug 2023 21:04:13 +0000 (+0000) Subject: Initial skeleton for adding an SQL-driven test script interpreter for the JNI bindings. X-Git-Tag: version-3.43.0~47^2~77 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3897a882f7d86e27d66aba682e666ec1be1aa958;p=thirdparty%2Fsqlite.git Initial skeleton for adding an SQL-driven test script interpreter for the JNI bindings. FossilOrigin-Name: 2aa8f0edecd3fc30eec28987cdbf1003ace154ddc1447b6f8715ecf38d3b06fb --- diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index a1325434ae..b2f4dc03f9 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -24,6 +24,7 @@ dir.src.c := $(dir.src)/c dir.bld := $(dir.jni)/bld dir.bld.c := $(dir.bld) dir.src.jni := $(dir.src)/org/sqlite/jni +dir.src.jni.tester := $(dir.src.jni)/tester $(dir.bld.c): mkdir -p $@ @@ -34,7 +35,9 @@ DISTCLEAN_FILES := $(dir.jni)/*~ $(dir.src.c)/*~ $(dir.src.jni)/*~ sqlite3-jni.h := $(dir.src.c)/sqlite3-jni.h .NOTPARALLEL: $(sqlite3-jni.h) SQLite3Jni.java := src/org/sqlite/jni/SQLite3Jni.java -SQLite3Jni.class := $(subst .java,.class,$(SQLite3Jni.java)) +SQLTester.java := src/org/sqlite/jni/tester/SQLTester.java +SQLite3Jni.class := $(SQLite3Jni.java:.java=.class) +SQLTester.class := $(SQLTester.java:.java=.class) ######################################################################## # The future of FTS5 customization in this API is as yet unclear. @@ -43,7 +46,7 @@ enable.fts5 ?= 1 # Be explicit about which Java files to compile so that we can work on # in-progress files without requiring them to be in a compilable statae. -JAVA_FILES := $(patsubst %,$(dir.src.jni)/%,\ +JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ BusyHandler.java \ Collation.java \ CollationNeeded.java \ @@ -64,7 +67,7 @@ JAVA_FILES := $(patsubst %,$(dir.src.jni)/%,\ ValueHolder.java \ ) ifeq (1,$(enable.fts5)) - JAVA_FILES += $(patsubst %,$(dir.src.jni)/%,\ + JAVA_FILES.main += $(patsubst %,$(dir.src.jni)/%,\ fts5_api.java \ fts5_extension_function.java \ fts5_tokenizer.java \ @@ -77,6 +80,16 @@ ifeq (1,$(enable.fts5)) TesterFts5.java \ ) endif +JAVA_FILES.tester := $(patsubst %,$(dir.src.jni.tester)/%,\ + SQLTester.java \ + TestScript.java \ +) + +CLASS_FILES.main := $(JAVA_FILES.main:.java=.class) +CLASS_FILES.tester := $(JAVA_FILES.tester:.java=.class) + +JAVA_FILES += $(JAVA_FILES.main) $(JAVA_FILES.tester) + CLASS_FILES := define DOTCLASS_DEPS $(1).class: $(1).java $(MAKEFILE) @@ -84,6 +97,7 @@ all: $(1).class CLASS_FILES += $(1).class endef $(foreach B,$(basename $(JAVA_FILES)),$(eval $(call DOTCLASS_DEPS,$(B)))) +$(CLASS_FILES.tester): $(CLASS_FILES.main) javac.flags ?= -Xlint:unchecked -Xlint:deprecation java.flags ?= jnicheck ?= 1 @@ -193,12 +207,21 @@ $(sqlite3-jni.dll): $(dir.bld.c) $(sqlite3-jni.c) $(SQLite3Jni.java) $(MAKEFILE) $(sqlite3-jni.c) -shared -o $@ all: $(sqlite3-jni.dll) +.PHONY: test test.flags ?= -v test: $(SQLite3Jni.class) $(sqlite3-jni.dll) $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \ $(java.flags) -cp $(classpath) \ org.sqlite.jni.Tester1 $(if $(test.flags),-- $(test.flags),) +tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test)) +.PHONY: tester + +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) + $(package.jar): $(CLASS_FILES) $(MAKEFILE) rm -f $(dir.src)/c/*~ $(dir.src.jni)/*~ $(bin.jar) -cfe $@ org.sqlite.Tester1 -C src org -C src c diff --git a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java new file mode 100644 index 0000000000..ce76eb31f4 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java @@ -0,0 +1,81 @@ +package org.sqlite.jni.tester; +import java.util.List; +import java.util.ArrayList; +import static org.sqlite.jni.SQLite3Jni.*; + +/** + This class provides an application which aims to implement the + rudimentary SQL-driven test tool described in the accompanying + test-script-interpreter.md. + + This is a work in progress. +*/ +public class SQLTester { + //! List of input script files. + private java.util.List listInFiles = new ArrayList<>(); + private boolean isVerbose = true; + + public SQLTester(){ + } + + public void setVerbose(boolean b){ + isVerbose = b; + } + + public static void out(T val){ + System.out.print(val); + } + + public static void outln(T val){ + System.out.println(val); + } + + @SuppressWarnings("unchecked") + public static void out(T... vals){ + int n = 0; + for(T v : vals) out((n++>0 ? " " : "")+v); + } + + @SuppressWarnings("unchecked") + public static void outln(T... vals){ + out(vals); + out("\n"); + } + + @SuppressWarnings("unchecked") + private void verbose(T... vals){ + if(isVerbose) outln(vals); + } + + //! Adds the given test script to the to-test list. + public void addTestScript(String filename){ + listInFiles.add(filename); + verbose("Added file",filename); + } + + public void runTests() throws Exception { + // process each input file + for(String f : listInFiles){ + verbose("Running test script",f); + final TestScript ts = new TestScript(f); + } + } + + public static void main(String[] argv) throws Exception{ + final SQLTester t = new SQLTester(); + for(String a : argv){ + if(a.startsWith("-")){ + final String flag = a.replaceFirst("-+",""); + if( flag.equals("verbose") ){ + t.setVerbose(true); + }else if( flag.equals("quiet") ) { + t.setVerbose(false); + }else{ + throw new IllegalArgumentException("Unhandled flag: "+flag); + } + } + t.addTestScript(a); + } + t.runTests(); + } +} diff --git a/ext/jni/src/org/sqlite/jni/tester/TestScript.java b/ext/jni/src/org/sqlite/jni/tester/TestScript.java new file mode 100644 index 0000000000..45d42045c1 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/tester/TestScript.java @@ -0,0 +1,36 @@ +package org.sqlite.jni.tester; +import java.io.*; +import java.nio.charset.StandardCharsets; +//import java.util.List; +//import java.util.ArrayList; + +/** + This class represents a single test script. It handles (or delegates) + its input and parsing. Iteration and evalution are deferred to other, + as-yet-non-existent, classes. + +*/ +public class TestScript { + //! Test script content. + private String content; + + private byte[] readFile(String filename) throws IOException { + return java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(filename)); + } + + /** + Initializes the script with the content of the given file. + */ + public TestScript(String filename) throws IOException{ + this.content = new String(readFile(filename), + StandardCharsets.UTF_8); + } + + /** + Initializes the script with the given content, copied + at construction-time. + */ + public TestScript(StringBuffer content){ + this.content = content.toString(); + } +} diff --git a/ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md b/ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md new file mode 100644 index 0000000000..36454f9d3b --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md @@ -0,0 +1,200 @@ +# Specifications For A Rudimentary SQLite Test Script Interpreter + +## Overview + +The purpose of the Test Script Interpreter is to read and interpret +script files that contain SQL commands and desired results. The +interpreter will check results and report an discrepencies found. + +The test script files are ASCII text files. The filename always ends with +".test". Each script is evaluated independently; context does not carry +forward from one script to the next. So, for example, the --null command +run in one test script does not cause any changes in the behavior of +subsequent test scripts. All open database connections are closed +at the end of each test script. All database files created by a test +script are deleted when the script finishes. + +## Parsing Rules: + + 1. Ignore the entire script if the script does not contain the + string "SCRIPT_MODULE_NAME:". + + 2. Ignore any script that contains the character sequence "\\n\|" + (0x0a, 0x7c). In other words, ignore scripts that contain the + pipe character at the beginning of a line. Such lines represent + test database file content in the "dbtotxt" format. We might add + support for this later, but omit it for the first version. + + 3. Ignore individual lines that begin with '#' (C-preprocessor lines). + + 4. If a line begins with exactly two minus signs followed by a + lowercase letter, that is a command. Process commands as described + below. + + 5. All other lines should be accumulated into the "input buffer". + The various commands will have access to this input buffer. + Some commands will reset the buffer. + +## Commands: + +Each command looks like an SQL comment. The command begins at the left +margin (no leading space) and starts with exactly 2 minus signs ("-"). +The command name consists of lowercase letters and maybe a "-" or two. +Some commands have arguments. +The arguments are separated from the command name by one or more spaces. + +Commands have access to the input buffer and might reset the input buffer. +The command can also optionally read (and consume) additional text from +script that comes after the command. + +Unknown or unrecognized commands should cause an error message to be +printed and execution to stop. + +The initial implemention will only recognize a few commands. Other +commands may be added later. The following is the initial set of +commands: + +### The --testcase command + +Every test case starts with a --testcase command. The --testcase command +resets both the "input buffer" and the "result buffer". +The argument to the --testcase command is the +name of the test case. That test case name is used for logging and debugging +and when printing errors. + +### The --result command + +The --result command tries to execute the text in the input buffer as SQL. +For each row of result coming out of this SQL, the text of that result is +appended to the "result buffer". If a result row contains multiple columns, +the columns are processed from left to right. For each column, text is +appended to the result buffer according to the following rules: + + * If the result buffer already contains some text, append a space. + (In this way, all column values and all row values are separated from + each other by a single space.) + + * If the sqlite3_column_text() returns NULL, then append "nil" - or + some other text that is specified by the --null command - and skip + all subsequent rules. + + * If sqlite3_column_text() does not contain any special characters, + append it to the result buffer without any formatting and skip all + subsequent rules. + + * If sqlite3_column_text() does not contains curly braces, then put + the text inside of `{...}` and append it and skip all subsequent rules. + + * Append the text within double-quotes (`"..."`) and within the text + escape '"' and '\\' by prepending a single '\\' and escape any + control characters (characters less than 0x20) using octal notation: + '\\NNN'. + +If an error is encountered while running the SQL, then append the +symbolic C-preprocessor name for the error +code (ex: "SQLITE_CONSTRAINT") as if it were a column value. Then append +the error message text as if it where a column value. Then stop processing. + +After the SQL text has been run, compare the content of the result buffer +against the argument to the --result command and report a testing error if +there are any differences. + +The --result command resets the input buffer, but it does not reset +the result buffer. This distinction does not matter for the --result +command itself, but it is important for related commands like --glob +and --notglob. Sometimes test cases will contains a bunch of SQL +followed by multiple --glob and/or --notglob statements. All of the +globs should be evaluted agains the result buffer correct, but the SQL +should only be run once. This is accomplished by resetting the input +buffer but not the result buffer. + +### The --glob command + +The --glob command works just like --result except that the argument to +--glob is interpreted as a TEST-GLOB pattern and the results are compared +using that glob pattern rather than using strcmp(). Other than that, +the two operate the same. + +The TEST-GLOB pattern is slightly different for a standard GLOB: + + * The '*' character matches zero or more characters. + + * The '?' character matches any single character + + * The '[...]' character sequence machines a single character + in between the brackets. + + * The '#' character matches one or more digits (This is the main + difference between standard unix-glob and TEST-GLOB. unix-glob + does not have this feature. It was added to because it comes + up a lot during SQLite testing.) + +### The --notglob command + +The --notglob command works just like --glob except that it reports an +error if the GLOB does match, rather than if the GLOB does not matches. + +### The --oom command + +This command is to be used for out-of-memory testing. It means that +OOM errors should be simulated to ensure that SQLite is able to deal with +them. This command can be silently ignored for now. We might add support +for this later. + +### The --tableresult command + +The --tableresult command works like --glob except that the GLOB pattern +to be matched is taken from subsequent lines of the input script up to +the next --end. Every span of one or more whitespace characters in this +pattern text is collapsed into a single space (0x20). +Leading and trailing whitespace are removed from the pattern. +The --end that ends the GLOB pattern is not part of the GLOB pattern, but +the --end is consumed from the script input. + +### The --new and --open commands + +The --new and --open commands cause a database file to be opened. +The name of the file is the argument to the command. The --new command +opens an initially empty database (it deletes the file before opening it) +whereas the --open command opens an existing database if it already +exists. + +### The --db command + +The script interpreter can have up to 7 different SQLite database +connections open at a time. The --db command is used to switch between +them. The argument to --db is an integer between 0 and 6 that selects +which database connection to use moving forward. + +### The --close command + +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. + +### The --null command + +The NULL command changes the text that is used to represent SQL NULL +values in the result buffer. + +### The --run command + +The --run command executes text in the input buffer as if it where SQL. +However, nothing is added to the result buffer. Any output from the SQL +is silently ignored. Errors in the SQL are silently ignored. + +The --run command normally executes the SQL in the current database +connection. However, if --run has an argument that is an integer between +0 and 6 then the SQL is run in the alternative database connection specified +by that argument. + +### The --json and --json-block commands + +The --json and --json-block commands work like --result and --tableresult, +respectively. The difference is that column values are appended to the +result buffer literally, without every enclosing the values in `{...}` or +`"..."` and without escaping any characters in the column value and comparison +is always an exact strcmp() not a GLOB. diff --git a/ext/jni/src/tests/000_first.test b/ext/jni/src/tests/000_first.test new file mode 100644 index 0000000000..b66c32ce89 --- /dev/null +++ b/ext/jni/src/tests/000_first.test @@ -0,0 +1,4 @@ +/* A script for testing the org.sqlite.jni.tester infrastructure */ + +# this line is ignored + diff --git a/manifest b/manifest index 47b778161b..42c5e1016a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C When\sconverting\sa\sJava\sexception\sto\sa\sdb\serror\smessage,\suse\sThrowable.toString()\sinstead\sof\sgetMessage()\sso\sthat\sthe\sexception\stype's\sname\sis\sincluded.\sMore\sinternal\sAPI\srenaming\sfor\sconsistency. -D 2023-08-07T11:18:44.649 +C Initial\sskeleton\sfor\sadding\san\sSQL-driven\stest\sscript\sinterpreter\sfor\sthe\sJNI\sbindings. +D 2023-08-07T21:04:13.706 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 61d9bbc179a49523a142928455b3297779b9c40f25783ecf1538279e426cbc99 +F ext/jni/GNUmakefile e492513ab2fc6da4f01d6745c852d3ef0afa336994e91a513b523ae8ececcde8 F ext/jni/README.md e965674505e105626127ad45e628e4d19fcd379cdafc4d23c814c1ac2c55681d F ext/jni/src/c/sqlite3-jni.c e6463b3fc8ef000d9a5dd1649fe96a4cfc5aac21a43276424cf28d72548c5921 F ext/jni/src/c/sqlite3-jni.h 6c06cdb1e43ce56544dfbe3335a46174dae15d03337d06e55aa7256f85d2ce1b @@ -264,6 +264,10 @@ F ext/jni/src/org/sqlite/jni/sqlite3.java ff3729426704626a6019d97bfee512a83f253c 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/SQLTester.java d51db80b241ba90dea253b453cd7bb44648fdf0bb84370225f63ac541e727e4a +F ext/jni/src/org/sqlite/jni/tester/TestScript.java 67721f78f753c719e465db07abd928fa3c96ebc3917c797b42d21d89aa671a82 +F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md ba3cf6584783939c8797a67203e63ec588430fdc0b7719f24873e6731c6d0445 +F ext/jni/src/tests/000_first.test 9d20eef5d8985ce32dc039004f186a973ca629afee9ab2134f4ec18d1748e426 F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9 F ext/lsm1/Makefile.msc f8c878b467232226de288da320e1ac71c131f5ec91e08b21f502303347260013 F ext/lsm1/lsm-test/README 87ea529d2abe615e856d4714bfe8bb185e6c2771b8612aa6298588b7b43e6f86 @@ -2083,8 +2087,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 9a494394b9eb28cf88dc5e7075a4b8c682c8e14fdd6837b595bec8011d7e9e72 -R 482519d6e278134ac56541913ad9c20f +P 2d44720d06d9e50cb037e92981d2473a3ad0b7560f2f5923d428f59de6fd6aaa +R 85efb1bd754dc306bbc436950f6f48cb U stephan -Z 5c40003605167b9b55a6a352144a1fa7 +Z 24f4287e10d577104a22d65944b7f529 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 16b707af8d..f2bb1ef1b8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2d44720d06d9e50cb037e92981d2473a3ad0b7560f2f5923d428f59de6fd6aaa \ No newline at end of file +2aa8f0edecd3fc30eec28987cdbf1003ace154ddc1447b6f8715ecf38d3b06fb \ No newline at end of file