Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions libsql-conformance/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

<properties>
<docker.config.dir>${project.build.directory}/docker-config</docker.config.dir>
<xerial.version>3.53.4.0</xerial.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -155,4 +156,41 @@
</plugin>
</plugins>
</build>

<profiles>
<!--
Parity baseline: run the same vendored test manifest against the pinned
xerial 3.53.4.0 driver instead of the libSQL driver.
-->
<profile>
<id>xerial-control</id>
<dependencies>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>${xerial.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<includes combine.self="override">
<include>**/*Test.java</include>
</includes>
<excludes combine.self="override">
<exclude>**/com/github/magnusp/**/*Test.java</exclude>
</excludes>
<systemPropertyVariables>
<conformance.control.driver>xerial</conformance.control.driver>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.github.magnusp.libsql.conformance;

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;

/**
* Selects the upstream xerial driver for the parity control run.
*
* <p>The control run compiles the same vendored test manifest as the libSQL run but executes it
* against sqlite-jdbc 3.53.4.0. Both drivers are on the test class path, so the control run first
* removes every driver visible to this class loader and then registers the pinned xerial driver
* explicitly. That keeps {@code jdbc:sqlite:} URLs from reaching the libSQL driver.
*/
final class XerialControl {

static final String ENABLED_PROPERTY = "conformance.control.driver";
static final String XERIAL_DRIVER = "org.sqlite.JDBC";

private static final String PROBE_URL = "jdbc:sqlite::memory:";

private XerialControl() {}

static boolean isEnabled() {
return "xerial".equals(System.getProperty(ENABLED_PROPERTY));
}

static void register() {
deregisterVisibleDrivers();
try {
Driver driver =
(Driver) Class.forName(XERIAL_DRIVER).getConstructor().newInstance();
DriverManager.registerDriver(driver);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException(
"Xerial control run could not load " + XERIAL_DRIVER, e);
} catch (SQLException e) {
throw new IllegalStateException(
"Xerial control run could not register " + XERIAL_DRIVER, e);
}
verify();
}

private static void deregisterVisibleDrivers() {
ClassLoader classLoader = XerialControl.class.getClassLoader();
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
if (driver.getClass().getClassLoader() == classLoader) {
try {
DriverManager.deregisterDriver(driver);
} catch (SQLException e) {
throw new IllegalStateException(
"Xerial control run could not deregister " + driver, e);
}
}
}
}

private static void verify() {
try {
Driver driver = DriverManager.getDriver(PROBE_URL);
if (!XERIAL_DRIVER.equals(driver.getClass().getName())) {
throw new IllegalStateException(
"Xerial control run resolved "
+ driver.getClass().getName()
+ " for "
+ PROBE_URL
+ " instead of "
+ XERIAL_DRIVER);
}
} catch (SQLException e) {
throw new IllegalStateException(
"Xerial control run found no driver for " + PROBE_URL, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ public final class XerialLauncherSessionListener implements LauncherSessionListe

@Override
public void launcherSessionOpened(LauncherSession session) {
if (XerialControl.isEnabled()) {
XerialControl.register();
return;
}
XerialConformanceDriver.register();
LibsqlConformanceGuard.verify();
}
Expand Down
57 changes: 55 additions & 2 deletions upstream-xerial_sqlite-jdbc.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,63 @@
# # divergence: <path> -- <non-empty reason>
# Use the reason to say which libSQL or Hrana behavior makes parity impossible.
#
# Selection rationale. The conformance suite selects tests that exercise JDBC
# behavior through ordinary SQL, so the Hrana transport can satisfy them
# unchanged. Tests that drive sqlite-jdbc's native extension API, connection
# configuration surface, or known libSQL parity gaps stay upstream. Each
# selected and rejected class from the pinned 3.53.4.0 test tree is listed.

# Selected support code.
# selected: src/test/java/org/sqlite/Utils.java -- compile-option assumptions shared by selected tests; not a test class.

# Selected tests.
# selected: src/test/java/org/sqlite/ResultSetTest.java -- ResultSet navigation and getter conversions over ordinary SQL.
# selected: src/test/java/org/sqlite/InsertQueryTest.java -- transaction reuse and commit visibility across shared and independent connections.
# selected: src/test/java/org/sqlite/FetchSizeTest.java -- fetch-size configuration and stable result ordering.
# selected: src/test/java/org/sqlite/CachedRowSetTest.java -- disconnected CachedRowSet population through a JDBC connection.
# selected: src/test/java/org/sqlite/JSON1Test.java -- SQL JSON function output and text conversion.
# selected: src/test/java/org/sqlite/MetadataLeakTest.java -- metadata result sets close the statement that produced them.
# selected: src/test/java/org/sqlite/PreparedStatementThreadTest.java -- concurrent execute and close must not crash or hide failures.
# selected: src/test/java/org/sqlite/ResultSetWithoutResultsTest.java -- statement result-kind transitions leave no stale result set.
# selected: src/test/java/org/sqlite/RSMetaDataTest.java -- ResultSetMetaData column names, types, and nullability.

# Rejected tests.
# rejected: src/test/java/org/sqlite/TypeMapTest.java -- removed: libSQL accepts but ignores type maps (tracked by #48), so a parity assertion would be red; asserting the no-op would be a weak test.
# rejected: src/test/java/org/sqlite/BackupTest.java -- drives sqlite-jdbc's native backup API; Hrana exposes no backup primitive.
# rejected: src/test/java/org/sqlite/BusyHandlerTest.java -- registers native busy-handler callbacks that Hrana does not expose.
# rejected: src/test/java/org/sqlite/CollationTest.java -- registers JVM collations through sqlite-jdbc's native extension API.
# rejected: src/test/java/org/sqlite/ConnectionTest.java -- exercises sqlite-jdbc URL, open-mode, and resource-loading configuration that has no Hrana equivalent; covered JDBC paths are asserted by selected tests.
# rejected: src/test/java/org/sqlite/DBMetaDataTest.java -- DatabaseMetaData parity is tracked by #41, #42, and #50; selecting it now would be red.
# rejected: src/test/java/org/sqlite/ErrorMessageTest.java -- asserts sqlite-jdbc wording and native result codes; sqld returns different messages and error codes.
# rejected: src/test/java/org/sqlite/ExtendedCommandTest.java -- exercises sqlite-jdbc's dot-command parser, not the JDBC API.
# rejected: src/test/java/org/sqlite/ExtensionTest.java -- loads native SQLite extensions; the libSQL server does not permit extension loading.
# rejected: src/test/java/org/sqlite/JDBCTest.java -- mixes driver URL handling, extension loading, and sqlite-jdbc read-only configuration; broad driver-level coverage is asserted by selected tests.
# rejected: src/test/java/org/sqlite/ListenerTest.java -- update, commit, rollback, and authorizer hooks require native callbacks unavailable over Hrana.
# rejected: src/test/java/org/sqlite/MathFunctionsTest.java -- passes only when the server binary was compiled with SQLITE_ENABLE_MATH_FUNCTIONS; the sqld image build is not pinned to that option.
# rejected: src/test/java/org/sqlite/MultipleClassLoaderTest.java -- verifies sqlite-jdbc's native library loading per class loader; libSQL has no native library.
# rejected: src/test/java/org/sqlite/PrepStmtTest.java -- includes getGeneratedKeys (#39) and CLOB/binary binding parity that are not yet implemented; selected prepared-statement paths cover the rest.
# rejected: src/test/java/org/sqlite/ProgressHandlerTest.java -- registers native progress callbacks that Hrana does not expose.
# rejected: src/test/java/org/sqlite/QueryTest.java -- date/time and blob conversion cases belong to the value-conversion lane (#31, #32, #33, #34); selecting it now would mix known gaps into the parity baseline.
# rejected: src/test/java/org/sqlite/ReadUncommittedTest.java -- depends on sqlite-jdbc shared-cache and read-uncommitted connection configuration; sqld does not expose it.
# rejected: src/test/java/org/sqlite/SavepointTest.java -- savepoints are not implemented by the libSQL driver.
# rejected: src/test/java/org/sqlite/SerializeTest.java -- serializes and restores a native database image; Hrana has no equivalent.
# rejected: src/test/java/org/sqlite/SQLiteConfigTest.java -- tests sqlite-jdbc's configuration object, not the JDBC API.
# rejected: src/test/java/org/sqlite/SQLiteConnectionPoolDataSourceTest.java -- tests sqlite-jdbc DataSource and pooling classes that libSQL does not provide.
# rejected: src/test/java/org/sqlite/SQLiteDataSourceTest.java -- tests sqlite-jdbc DataSource classes that libSQL does not provide.
# rejected: src/test/java/org/sqlite/SQLiteJDBCLoaderTest.java -- tests the native SQLite library loader.
# rejected: src/test/java/org/sqlite/StatementTest.java -- statement reuse and lifecycle parity is tracked by #47; selected ResultSet tests cover the current read paths.
# rejected: src/test/java/org/sqlite/TransactionTest.java -- asserts SQLite file-locking and multi-connection wait behavior that a single sqld server cannot reproduce; commit visibility is covered by InsertQueryTest.
# rejected: src/test/java/org/sqlite/UDFCustomErrorTest.java -- registers JVM UDFs and inspects native error callbacks.
# rejected: src/test/java/org/sqlite/UDFTest.java -- registers JVM UDFs through sqlite-jdbc's native extension API.
# rejected: src/test/java/org/sqlite/util/OSInfoTest.java -- tests sqlite-jdbc's OS and native-library detection utility.
# rejected: src/test/java/org/sqlite/architecture/CodingRulesTest.java -- ArchUnit rules over sqlite-jdbc source conventions; not runtime JDBC behavior.
# rejected: src/test/java/org/sqlite/architecture/TestCodingRulesTest.java -- ArchUnit rules over sqlite-jdbc test conventions; not runtime JDBC behavior.

# First conformance batch.
src/test/java/org/sqlite/Utils.java
src/test/java/org/sqlite/ResultSetTest.java
src/test/java/org/sqlite/InsertQueryTest.java
src/test/java/org/sqlite/FetchSizeTest.java
src/test/java/org/sqlite/TypeMapTest.java
src/test/java/org/sqlite/CachedRowSetTest.java

# Second batch: metadata, JSON, threading, and empty result sets.
Expand All @@ -32,4 +83,6 @@ VERSION
# when its last connection closes, so the conformance tests must close every
# connection they open; leaving one open leaks the namespace for the whole run.
# divergence: src/test/java/org/sqlite/JSON1Test.java -- Hrana holds a namespace until its last connection closes, so the conformance copy closes the leaked per-test connections.
# divergence: src/test/java/org/sqlite/CachedRowSetTest.java -- Hrana holds a namespace until its last connection closes, so the conformance copy closes the leaked connection.
# divergence: src/test/java/org/sqlite/CachedRowSetTest.java -- Hrana holds a namespace until its last connection closes, so the conformance copy closes the leaked connection and asserts the row it reads.
# divergence: src/test/java/org/sqlite/InsertQueryTest.java -- upstream misuses the connection factory so the transaction is applied to a different connection and never asserts the result; the conformance copy reuses the transaction connection, asserts rollback and commit visibility, and asserts the final count (#51).
# divergence: src/test/java/org/sqlite/PreparedStatementThreadTest.java -- upstream swallows ExecutionException, which also wraps assertion failures; the conformance copy rethrows AssertionError so the race test can fail (#51).
8 changes: 3 additions & 5 deletions upstream-xerial_sqlite-jdbc/CHECKSUMS.sha256
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,22 @@ a0670cf020edf700524c43c85321c2ba027a8bace5682001db60875d4a9fe52b ./NOTICE
# upstream 0a60829ce6edd56de46fb87303401c5c1e1e666da5631056340f5c5ce902dcdf ./VERSION
0a60829ce6edd56de46fb87303401c5c1e1e666da5631056340f5c5ce902dcdf ./VERSION
# upstream 1351fdca2644e90ac9028ed99c6096c9ecee82ccb7d45cedd9bb454887fd2c6b ./src/test/java/org/sqlite/CachedRowSetTest.java
9e896e20522e4c5f4bc786ad7f8c5b6765428ad531fc0c07130b5f0beb81756f ./src/test/java/org/sqlite/CachedRowSetTest.java
e1743294d2e5e781fae3b647d0bfc2eeeeb856c5276efe308f8b58979b3ab178 ./src/test/java/org/sqlite/CachedRowSetTest.java
# upstream 9fb0f2df7de66cac0a46b241c0c1cd3311a80646d0431d377097741b99053760 ./src/test/java/org/sqlite/FetchSizeTest.java
9fb0f2df7de66cac0a46b241c0c1cd3311a80646d0431d377097741b99053760 ./src/test/java/org/sqlite/FetchSizeTest.java
# upstream 6dfcecb209e01829ba0aea782b47351443e3970ad1d33ac4c3fb979e6dff1f40 ./src/test/java/org/sqlite/InsertQueryTest.java
6dfcecb209e01829ba0aea782b47351443e3970ad1d33ac4c3fb979e6dff1f40 ./src/test/java/org/sqlite/InsertQueryTest.java
ae4568fd773f158f04e89df8e0fe2326381b6af7ed1316193830d38f7cadbae5 ./src/test/java/org/sqlite/InsertQueryTest.java
# upstream bb07398946480ca44c70d03e6f8c399f53506b44c83a312e2d28aebd239c0238 ./src/test/java/org/sqlite/JSON1Test.java
075745907a5174b922db6a45b9c29f45901af1835af27a0014f5fac835b41d0f ./src/test/java/org/sqlite/JSON1Test.java
# upstream 242bb51330240174bab5f443aa4d8de1049e6d947374e080a636c4aa4d0049ee ./src/test/java/org/sqlite/MetadataLeakTest.java
242bb51330240174bab5f443aa4d8de1049e6d947374e080a636c4aa4d0049ee ./src/test/java/org/sqlite/MetadataLeakTest.java
# upstream 7af8ae0999a0bf26a20c96ab23131505bd79827e6af17742771e36e9d8d985f4 ./src/test/java/org/sqlite/PreparedStatementThreadTest.java
7af8ae0999a0bf26a20c96ab23131505bd79827e6af17742771e36e9d8d985f4 ./src/test/java/org/sqlite/PreparedStatementThreadTest.java
bc6d868d2069dcc5db54a7720e7ecd9f725c83dbf910ec8ec61222b98f6198c1 ./src/test/java/org/sqlite/PreparedStatementThreadTest.java
# upstream d787e71bdf2b8d12a2669eb06c432cf2a39ac77f678a92459e3dd9b6c4519a43 ./src/test/java/org/sqlite/RSMetaDataTest.java
d787e71bdf2b8d12a2669eb06c432cf2a39ac77f678a92459e3dd9b6c4519a43 ./src/test/java/org/sqlite/RSMetaDataTest.java
# upstream 6065d284933c8462cc13217992820a9261075c0e9ee126df6cbe9961d90303f9 ./src/test/java/org/sqlite/ResultSetTest.java
6065d284933c8462cc13217992820a9261075c0e9ee126df6cbe9961d90303f9 ./src/test/java/org/sqlite/ResultSetTest.java
# upstream 958c1f2ff5ca2f1257dfd847207ea793839969b234d140576b51af346697233c ./src/test/java/org/sqlite/ResultSetWithoutResultsTest.java
958c1f2ff5ca2f1257dfd847207ea793839969b234d140576b51af346697233c ./src/test/java/org/sqlite/ResultSetWithoutResultsTest.java
# upstream ed2fb4bf7319bcba0662b83707375501337173956bae7c57dda6e6a2c46b7505 ./src/test/java/org/sqlite/TypeMapTest.java
ed2fb4bf7319bcba0662b83707375501337173956bae7c57dda6e6a2c46b7505 ./src/test/java/org/sqlite/TypeMapTest.java
# upstream bef3eddca2a6b80472050a3a52d5e7246128f0644d29c24ed091709b0ed6f121 ./src/test/java/org/sqlite/Utils.java
bef3eddca2a6b80472050a3a52d5e7246128f0644d29c24ed091709b0ed6f121 ./src/test/java/org/sqlite/Utils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.sqlite;

import static org.assertj.core.api.Assertions.assertThat;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
Expand All @@ -22,6 +24,11 @@ public void gh_224() throws SQLException {
try (CachedRowSet crs = factory.createCachedRowSet()) {
crs.setCommand("select * from person");
crs.execute(connection);

assertThat(crs.next()).isTrue();
assertThat(crs.getInt("id")).isEqualTo(1);
assertThat(crs.getString("name")).isEqualTo("leo");
assertThat(crs.next()).isFalse();
}
}
}
Expand Down
Loading
Loading