You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by am...@apache.org on 2022/05/23 17:27:12 UTC
[sis] 01/02: chore(Storage): add a test class dedicated to temporal value conversion.
This is an automated email from the ASF dual-hosted git repository.
amanin pushed a commit to branch fix/sql-temporal
in repository https://gitbox.apache.org/repos/asf/sis.git
commit f52c54a4ea29d211dfbddb66bb0b155a58a4e239
Author: Alexis Manin <al...@geomatys.com>
AuthorDate: Mon May 23 19:22:29 2022 +0200
chore(Storage): add a test class dedicated to temporal value conversion.
---
.../sql/feature/TemporalValueGetterTest.java | 191 +++++++++++++++++++++
.../org/apache/sis/test/suite/SQLTestSuite.java | 1 +
2 files changed, 192 insertions(+)
diff --git a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/TemporalValueGetterTest.java b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/TemporalValueGetterTest.java
new file mode 100644
index 0000000000..399ac2d48c
--- /dev/null
+++ b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/TemporalValueGetterTest.java
@@ -0,0 +1,191 @@
+package org.apache.sis.internal.sql.feature;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.apache.sis.test.TestCase;
+import org.apache.sis.test.sql.TestDatabase;
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.AssumptionViolatedException;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static java.time.ZoneOffset.ofHours;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Check access and conversion to database datetime values.
+ */
+public class TemporalValueGetterTest extends TestCase {
+
+ private static final String DB_TITLE = "Test Temporal values";
+ private static final String TEST_SCHEMA = "SIS_TEST_TEMPORAL";
+ private static final String DEFAULT_DRIVER = "DEFAULT";
+ public static final String PG_DRIVER = "POSTGRESQL";
+ private static Map<String, TestDatabase> DATABASES;
+
+ @BeforeClass
+ public static void initTestDatabases() throws Exception {
+ if (DATABASES != null) throw new IllegalStateException("Test database should be null before initialization");
+ DATABASES = new HashMap<>();
+ DATABASES.put(DEFAULT_DRIVER, TestDatabase.create(DB_TITLE));
+ DATABASES.put("HSQLDB", TestDatabase.createOnHSQLDB(DB_TITLE, true));
+ DATABASES.put("H2", TestDatabase.createOnH2(DB_TITLE));
+ try {
+ final TestDatabase db = TestDatabase.createOnPostgreSQL(TEST_SCHEMA, true);
+ DATABASES.put(PG_DRIVER, db);
+ } catch (AssumptionViolatedException e) {
+ // Ok, environment does not contain a PostGreSQL test database
+ out.println("[FINE] PostgreSQL database deactivated due to assumption requirement not met: "+e.getMessage());
+ }
+ }
+
+ @AfterClass
+ public static void disposeTestDatabases() throws Exception {
+ if (DATABASES != null) {
+ List<Exception> closeErrors = DATABASES.values().stream()
+ .map(db -> {
+ try {
+ db.close();
+ return null;
+ } catch (Exception e) {
+ return e;
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ if (!closeErrors.isEmpty()) {
+ final Exception base = closeErrors.get(0);
+ for (int i = 1 ; i < closeErrors.size() ; i++) {
+ base.addSuppressed(closeErrors.get(i));
+ }
+ throw base;
+ }
+ }
+ }
+
+ @Test
+ public void sql_date_should_translate_to_java_time_LocalDate() {
+ testOnEachDatabase((driver, db) -> {
+ execute(driver, db,
+ "CREATE TABLE TEST_DATE (\"value\" DATE)",
+ "INSERT INTO TEST_DATE (\"value\") VALUES ('2022-05-01'), ('2022-05-31'), (null)"
+ );
+
+ ValueGetter<LocalDate> getter = ValueGetter.AsDate.INSTANCE;
+ execute(driver, db, "SELECT \"value\" FROM TEST_DATE", r -> {
+ r.next();
+ assertEquals(LocalDate.of(2022, 5, 1), getter.getValue(null, r, 1));
+ r.next();
+ assertEquals(LocalDate.of(2022, 5, 31), getter.getValue(null, r, 1));
+ r.next();
+ // Ensure no NPE occurs, i.e the converter is resilient to null inputs
+ assertNull(getter.getValue(null, r, 1));
+ assertFalse("Defense against test change: only 3 values should be present in the test table", r.next());
+ });
+ });
+ }
+
+ @Test
+ public void sql_timestamp_should_translate_to_java_time_LocalDateTime() {
+ testOnEachDatabase((driver, db) -> {
+ execute(driver, db,
+ "CREATE TABLE TEST_TIMESTAMP_WITHOUT_TIMEZONE (\"value\" TIMESTAMP)",
+ "INSERT INTO TEST_TIMESTAMP_WITHOUT_TIMEZONE (\"value\") VALUES ('2022-05-01 01:01:01'), ('2022-05-31 23:59:59.999'), (null)"
+ );
+
+ ValueGetter<LocalDateTime> getter = ValueGetter.AsLocalDateTime.INSTANCE;
+ execute(driver, db, "SELECT \"value\" FROM TEST_TIMESTAMP_WITHOUT_TIMEZONE", r -> {
+ r.next();
+ assertEquals(LocalDateTime.of(2022, 5, 1, 1, 1, 1), getter.getValue(null, r, 1));
+ r.next();
+ assertEquals(LocalDateTime.of(2022, 5, 31, 23, 59, 59, 999_000_000), getter.getValue(null, r, 1));
+ r.next();
+ // Ensure no NPE occurs, i.e the converter is resilient to null inputs
+ assertNull(getter.getValue(null, r, 1));
+ assertFalse("Defense against test change: only 3 values should be present in the test table", r.next());
+ });
+ });
+ }
+
+ @Test
+ public void sql_timestamp_with_timezone_should_translate_to_offset_date_time() {
+ testOnEachDatabase((driver, db) -> {
+ Assume.assumeFalse("Derby does not support TIMESTAMP WITH TIME ZONE data type", DEFAULT_DRIVER.equals(driver));
+ execute(driver, db,
+ "CREATE TABLE TEST_TIMESTAMP_WITH_TIMEZONE (\"value\" TIMESTAMP WITH TIME ZONE)",
+ "INSERT INTO TEST_TIMESTAMP_WITH_TIMEZONE (\"value\") VALUES ('2022-05-01 01:01:01+01:00'), ('2022-05-31 23:59:59.999+02:00'), (null)"
+ );
+
+ ValueGetter<Instant> getter = ValueGetter.AsInstant.INSTANCE;
+ execute(driver, db, "SELECT \"value\" FROM TEST_TIMESTAMP_WITH_TIMEZONE", r -> {
+ r.next();
+ assertEquals(LocalDateTime.of(2022, 5, 1, 1, 1, 1).atOffset(ofHours(1)).toInstant(), getter.getValue(null, r, 1));
+ r.next();
+ assertEquals(LocalDateTime.of(2022, 5, 31, 23, 59, 59, 999_000_000).atOffset(ofHours(2)).toInstant(), getter.getValue(null, r, 1));
+ r.next();
+ // Ensure no NPE occurs, i.e the converter is resilient to null inputs
+ assertNull(getter.getValue(null, r, 1));
+ assertFalse("Defense against test change: only 3 values should be present in the test table", r.next());
+ });
+ });
+ }
+
+ private void testOnEachDatabase(DatabaseTester tester) {
+ DATABASES.forEach((driver, db) -> {
+ try {
+ tester.test(driver, db);
+ } catch (AssumptionViolatedException e) {
+ // OK, user has deactivated test upon some conditions
+ out.println("[FINE] Error ignored because of failed assumption. Driver="+driver+", reason="+e.getMessage());
+ } catch (AssertionError e) {
+ throw new AssertionError("Test failed for driver: "+driver, e);
+ } catch (Exception e) {
+ throw new RuntimeException("Error while testing driver: "+driver, e);
+ }
+ });
+ }
+
+ @FunctionalInterface
+ private interface DatabaseTester {
+ void test(String driver, TestDatabase db) throws Exception;
+ }
+
+ @FunctionalInterface
+ private interface QueryAction {
+ void accept(ResultSet queryResult) throws Exception;
+ }
+
+ private static void execute(String driver, TestDatabase db, String firstQuery, String... moreQueries) throws SQLException {
+ try (Connection c = db.source.getConnection()) {
+ if (PG_DRIVER.equals(driver)) c.setSchema(TEST_SCHEMA);
+ try (Statement s = c.createStatement()) {
+ s.execute(firstQuery);
+ for (String q : moreQueries) s.execute(q);
+ }
+ }
+ }
+
+ private static void execute(String driver, TestDatabase db, String sqlQuery, QueryAction action) throws Exception {
+ try (Connection c = db.source.getConnection()) {
+ if (PG_DRIVER.equals(driver)) c.setSchema(TEST_SCHEMA);
+ try (Statement s = c.createStatement();
+ ResultSet r = s.executeQuery(sqlQuery)
+ ) {
+ action.accept(r);
+ }
+ }
+ }
+}
diff --git a/storage/sis-sqlstore/src/test/java/org/apache/sis/test/suite/SQLTestSuite.java b/storage/sis-sqlstore/src/test/java/org/apache/sis/test/suite/SQLTestSuite.java
index 566261d653..673e53cf43 100644
--- a/storage/sis-sqlstore/src/test/java/org/apache/sis/test/suite/SQLTestSuite.java
+++ b/storage/sis-sqlstore/src/test/java/org/apache/sis/test/suite/SQLTestSuite.java
@@ -25,6 +25,7 @@ import org.junit.BeforeClass;
* All tests from the {@code sis-sqlstore} module, in rough dependency order.
*/
@Suite.SuiteClasses({
+ org.apache.sis.internal.sql.feature.TemporalValueGetterTest.class,
org.apache.sis.internal.sql.feature.GeometryGetterTest.class,
org.apache.sis.internal.sql.feature.SelectionClauseWriterTest.class,
org.apache.sis.internal.sql.postgis.BandTest.class,