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 <[email protected]>
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,

Reply via email to