You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2024/02/20 12:33:31 UTC

(sis) branch geoapi-4.0 updated (8cdda23851 -> 1c81e3ce85)

This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


    from 8cdda23851 Enable JUnit parallel tests in `endorsed` modules. This requires the addition of a few resource locks.
     new 9c5d68f70b Logs handled by the wrong `LoggingWatcher` during execution of tests in parallel. Require Java 16 for the test (but not for the main code, which still on Java 11) for avoiding an unreliable hack in `JDK16` placeholder class.
     new d17ebf34c0 Add a common base class for tests that depend on the Garbage Collector activity. Those tests are executed in isolation for making them a little bit less undeterministic.
     new c0fc9b4dfe Better identification of code that will need to be replaced in Java 16.
     new e3289235a9 Opportunistically use "pattern matching for instanceof" in test code. The main code is unchanged (stay on Java 11).
     new 1c81e3ce85 Fix some synchronization holes in parallel execution of tests.

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 endorsed/build.gradle.kts                          |   2 +
 .../sis/coverage/grid/j2d/TilePlaceholder.java     |   4 +-
 .../main/org/apache/sis/image/TileCache.java       |   3 +-
 .../apache/sis/feature/AbstractFeatureTest.java    |  14 +--
 .../org/apache/sis/feature/FeatureTestCase.java    |   2 +-
 .../feature/builder/FeatureTypeBuilderTest.java    |   4 +-
 .../apache/sis/feature/test/FeatureComparator.java |  34 +++---
 .../test/org/apache/sis/filter/PeriodLiteral.java  |   3 +-
 .../sis/metadata/PropertyConsistencyCheck.java     |   9 +-
 .../test/org/apache/sis/metadata/TreeNodeTest.java |   4 +-
 .../org/apache/sis/metadata/TreeTableViewTest.java |   4 +-
 .../org/apache/sis/metadata/sql/TestDatabase.java  |   4 +-
 .../apache/sis/util/iso/NameMarshallingTest.java   |  19 +++-
 .../apache/sis/xml/bind/gco/MultiplicityTest.java  |   2 +-
 .../apache/sis/xml/test/DocumentComparator.java    |  48 ++++-----
 .../org/apache/sis/xml/test/PackageVerifier.java   |  22 ++--
 .../test/org/apache/sis/xml/test/TestCase.java     |   3 +
 .../coverage/MultiResolutionCoverageLoader.java    |   3 +-
 .../apache/sis/referencing/AuthorityFactories.java |   2 +
 .../referencing/factory/sql/EPSGDataAccess.java    |   4 +-
 .../apache/sis/geometry/AbstractEnvelopeTest.java  |   6 +-
 .../apache/sis/geometry/GeneralEnvelopeTest.java   |   3 +-
 .../org/apache/sis/io/wkt/WKTDictionaryTest.java   |   4 +-
 .../org/apache/sis/referencing/Assertions.java     |  26 +++--
 .../sis/referencing/EPSGFactoryFallbackTest.java   |   5 +
 .../sis/referencing/GeodeticObjectVerifier.java    |   4 +-
 .../factory/ConcurrentAuthorityFactoryTest.java    |  38 ++++---
 .../operation/CoordinateOperationFinderTest.java   |   3 +
 .../operation/CoordinateOperationRegistryTest.java |   5 +-
 .../DefaultCoordinateOperationFactoryTest.java     |   3 +
 .../projection/MapProjectionTestCase.java          |   4 +-
 .../projection/ProjectionResultComparator.java     |   3 +-
 .../operation/provider/MapProjectionTest.java      |   4 +-
 .../transform/CoordinateSystemTransformTest.java   |   3 +
 .../operation/transform/MathTransformTestCase.java |   8 +-
 .../operation/transform/MathTransformWrapper.java  |   4 +-
 .../transform/TransformResultComparator.java       |   6 +-
 .../report/CoordinateOperationMethods.java         |  16 +--
 .../report/CoordinateReferenceSystems.java         |  24 ++---
 .../referencing/util/j2d/ShapeUtilitiesExt.java    |   9 +-
 .../apache/sis/storage/netcdf/base/TestCase.java   |  15 ++-
 .../org/apache/sis/storage/CoverageSubset.java     |   7 +-
 .../org/apache/sis/storage/folder/StoreTest.java   |   4 +-
 .../sis/storage/test/CoverageReadConsistency.java  |   3 +
 .../apache/sis/storage/test/SubsampledImage.java   |  13 +--
 .../src/org.apache.sis.util/main/module-info.java  |   1 +
 .../main/org/apache/sis/pending/jdk/JDK16.java     |  47 +++------
 .../main/org/apache/sis/system/Configuration.java  |   3 +-
 .../main/org/apache/sis/util/collection/Cache.java |   3 +-
 .../test/org/apache/sis/io/LineAppenderTest.java   |   4 +-
 .../org/apache/sis/io/TabulationExpansionTest.java |   4 +-
 .../test/org/apache/sis/io/WordWrapTest.java       |   4 +-
 .../org/apache/sis/measure/SystemUnitTest.java     |   7 +-
 .../test/org/apache/sis/test/LoggingWatcher.java   |  40 ++++----
 .../org/apache/sis/test/TestConfiguration.java     |  11 --
 .../test/org/apache/sis/test/TestUtilities.java    |  76 +-------------
 .../org/apache/sis/util/collection/CacheTest.java  |   7 +-
 .../apache/sis/util/collection/RangeSetTest.java   |   2 -
 .../apache/sis/util/collection/TestCaseWithGC.java | 114 +++++++++++++++++++++
 .../sis/util/collection/TreeTableFormatTest.java   |   3 +
 .../sis/util/collection/WeakHashSetTest.java       |  15 ++-
 .../sis/util/collection/WeakValueHashMapTest.java  |   7 +-
 .../sis/util/logging/PerformanceLevelTest.java     |   2 +
 incubator/build.gradle.kts                         |   2 +
 .../test/org/apache/sis/map/SEPortrayerTest.java   |   6 +-
 .../storage/coveragejson/binding/BindingTest.java  |   8 +-
 optional/build.gradle.kts                          |   2 +
 67 files changed, 408 insertions(+), 370 deletions(-)
 create mode 100644 endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/TestCaseWithGC.java


(sis) 04/05: Opportunistically use "pattern matching for instanceof" in test code. The main code is unchanged (stay on Java 11).

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit e3289235a9756b87a4ba423152f23c258c1248d9
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Feb 20 12:09:57 2024 +0100

    Opportunistically use "pattern matching for instanceof" in test code.
    The main code is unchanged (stay on Java 11).
---
 endorsed/build.gradle.kts                          |  4 +-
 .../apache/sis/feature/AbstractFeatureTest.java    | 14 +++----
 .../org/apache/sis/feature/FeatureTestCase.java    |  2 +-
 .../feature/builder/FeatureTypeBuilderTest.java    |  4 +-
 .../apache/sis/feature/test/FeatureComparator.java | 34 ++++++++-------
 .../test/org/apache/sis/filter/PeriodLiteral.java  |  3 +-
 .../sis/metadata/PropertyConsistencyCheck.java     |  9 ++--
 .../test/org/apache/sis/metadata/TreeNodeTest.java |  4 +-
 .../org/apache/sis/metadata/TreeTableViewTest.java |  4 +-
 .../org/apache/sis/metadata/sql/TestDatabase.java  |  4 +-
 .../apache/sis/xml/test/DocumentComparator.java    | 48 +++++++++++-----------
 .../org/apache/sis/xml/test/PackageVerifier.java   | 22 +++++-----
 .../apache/sis/geometry/AbstractEnvelopeTest.java  |  6 +--
 .../apache/sis/geometry/GeneralEnvelopeTest.java   |  3 +-
 .../org/apache/sis/io/wkt/WKTDictionaryTest.java   |  4 +-
 .../org/apache/sis/referencing/Assertions.java     | 26 ++++++------
 .../sis/referencing/GeodeticObjectVerifier.java    |  4 +-
 .../projection/MapProjectionTestCase.java          |  4 +-
 .../projection/ProjectionResultComparator.java     |  3 +-
 .../operation/provider/MapProjectionTest.java      |  4 +-
 .../operation/transform/MathTransformTestCase.java |  8 ++--
 .../operation/transform/MathTransformWrapper.java  |  4 +-
 .../transform/TransformResultComparator.java       |  6 +--
 .../report/CoordinateOperationMethods.java         | 16 ++++----
 .../report/CoordinateReferenceSystems.java         | 24 +++++------
 .../referencing/util/j2d/ShapeUtilitiesExt.java    |  9 ++--
 .../apache/sis/storage/netcdf/base/TestCase.java   | 12 ++----
 .../org/apache/sis/storage/folder/StoreTest.java   |  4 +-
 .../apache/sis/storage/test/SubsampledImage.java   | 13 +++---
 .../test/org/apache/sis/io/LineAppenderTest.java   |  4 +-
 .../org/apache/sis/io/TabulationExpansionTest.java |  4 +-
 .../test/org/apache/sis/io/WordWrapTest.java       |  4 +-
 .../org/apache/sis/measure/SystemUnitTest.java     |  7 ++--
 .../test/org/apache/sis/test/TestUtilities.java    |  8 +---
 incubator/build.gradle.kts                         |  2 +
 .../test/org/apache/sis/map/SEPortrayerTest.java   |  6 +--
 .../storage/coveragejson/binding/BindingTest.java  |  8 ++--
 optional/build.gradle.kts                          |  2 +
 38 files changed, 160 insertions(+), 187 deletions(-)

diff --git a/endorsed/build.gradle.kts b/endorsed/build.gradle.kts
index 6a8f136011..61b183502b 100644
--- a/endorsed/build.gradle.kts
+++ b/endorsed/build.gradle.kts
@@ -101,11 +101,11 @@ tasks.compileJava {
     options.release.set(11)         // The version of both Java source code and compiled byte code.
 }
 tasks.compileTestJava {
+    options.compilerArgs.add("-source")         // "source", not "release", because we accept any target version.
+    options.compilerArgs.add("16")              // Minimal Java version required by some API that the tests use.
     srcDir.list().forEach {
         addRead(options.compilerArgs, it, "org.apache.sis.test.endorsed,org.junit.jupiter.api")
     }
-    options.compilerArgs.add("-source")         // "source", not "release", because we accept any target version.
-    options.compilerArgs.add("16")              // Minimal Java version required by some API that the tests use.
     addExportForTests(options.compilerArgs)
 }
 
diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/AbstractFeatureTest.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/AbstractFeatureTest.java
index 24ba610414..637b6a10d9 100644
--- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/AbstractFeatureTest.java
+++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/AbstractFeatureTest.java
@@ -67,14 +67,14 @@ public final class AbstractFeatureTest extends FeatureTestCase {
          */
         CustomFeature(final DefaultFeatureType type) {
             super(type);
-            for (final PropertyType pt : type.getProperties(true)) {
-                if (pt instanceof AttributeType<?>) {
-                    Object value = ((AttributeType<?>) pt).getDefaultValue();
-                    if (isMultiValued(pt)) {
+            for (final PropertyType property : type.getProperties(true)) {
+                if (property instanceof AttributeType<?> attribute) {
+                    Object value = attribute.getDefaultValue();
+                    if (isMultiValued(property)) {
                         value = new ArrayList<>(PropertyView.singletonOrEmpty(value));
                     }
                     if (value != null) {
-                        values.put(pt.getName().toString(), value);
+                        values.put(property.getName().toString(), value);
                     }
                 }
             }
@@ -122,8 +122,8 @@ public final class AbstractFeatureTest extends FeatureTestCase {
             }
             if (value != null) {
                 final Class<?> base;
-                if (property instanceof AttributeType<?>) {
-                    base = ((AttributeType<?>) property).getValueClass();
+                if (property instanceof AttributeType<?> attribute) {
+                    base = attribute.getValueClass();
                 } else {
                     base = FeatureType.class;
                 }
diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/FeatureTestCase.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/FeatureTestCase.java
index 09b04194ea..a0e939392d 100644
--- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/FeatureTestCase.java
+++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/FeatureTestCase.java
@@ -357,7 +357,7 @@ public abstract class FeatureTestCase extends TestCase {
         final DataQuality quality = feature.quality();
         for (final Element report : quality.getReports()) {
             for (final Result result : report.getResults()) {
-                if (result instanceof ConformanceResult && !((ConformanceResult) result).pass()) {
+                if (result instanceof ConformanceResult r && !r.pass()) {
                     assertTrue(anomalyIndex < anomalousProperties.length, "Too many reports");
                     final String propertyName = anomalousProperties[anomalyIndex];
                     @SuppressWarnings("deprecation")
diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
index 6332d83ac4..001d297b87 100644
--- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
+++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
@@ -378,8 +378,8 @@ public final class FeatureTypeBuilderTest extends TestCase {
      */
     private static void assertPropertyEquals(final String name, final Class<?> valueClass, IdentifiedType property) {
         assertEquals(name, property.getName().toString());
-        if (property instanceof Operation) {
-            property = ((Operation) property).getResult();
+        if (property instanceof Operation op) {
+            property = op.getResult();
         }
         assertEquals(valueClass, attributeType(property).getValueClass());
     }
diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/test/FeatureComparator.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/test/FeatureComparator.java
index 79b7cec702..31a2c18b25 100644
--- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/test/FeatureComparator.java
+++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/test/FeatureComparator.java
@@ -178,14 +178,14 @@ public class FeatureComparator {
      */
     private void compareType(final IdentifiedType expected, final IdentifiedType actual) {
         boolean recognized = false;
-        if (expected instanceof FeatureType) {
+        if (expected instanceof FeatureType type) {
             var c = assertInstanceOf(FeatureType.class, actual, this::path);
-            compareFeatureType((FeatureType) expected, c);
+            compareFeatureType(type, c);
             recognized = true;
         }
-        if (expected instanceof PropertyType) {
+        if (expected instanceof PropertyType type) {
             var c = assertInstanceOf(PropertyType.class, actual, this::path);
-            comparePropertyType((PropertyType) expected, c);
+            comparePropertyType(type, c);
             recognized = true;
         }
         if (!recognized) {
@@ -256,8 +256,8 @@ public class FeatureComparator {
             while (expectedIter.hasNext()) {
                 final Object expectedElement = expectedIter.next();
                 final Object actualElement = actualIter.next();
-                if (expectedElement instanceof Feature) {
-                    compareFeature((Feature) expectedElement, (Feature) actualElement);
+                if (expectedElement instanceof Feature instance) {
+                    compareFeature(instance, (Feature) actualElement);
                 } else {
                     assertEquals(expectedElement, actualElement);
                 }
@@ -274,17 +274,17 @@ public class FeatureComparator {
      * @throws AssertionError if the actual property is not equal to the expected property.
      */
     private void comparePropertyType(final PropertyType expected, final PropertyType actual) {
-        if (expected instanceof AttributeType) {
+        if (expected instanceof AttributeType<?> type) {
             var c = assertInstanceOf(AttributeType.class, actual, this::path);
-            compareAttribute((AttributeType) expected, c);
+            compareAttribute(type, c);
         }
-        if (expected instanceof FeatureAssociationRole) {
+        if (expected instanceof FeatureAssociationRole role) {
             var c = assertInstanceOf(FeatureAssociationRole.class, actual, this::path);
-            compareFeatureAssociationRole((FeatureAssociationRole) expected, c);
+            compareFeatureAssociationRole(role, c);
         }
-        if (expected instanceof Operation) {
+        if (expected instanceof Operation op) {
             var c = assertInstanceOf(Operation.class, actual, this::path);
-            compareOperation((Operation) expected, c);
+            compareOperation(op, c);
         }
     }
 
@@ -380,10 +380,8 @@ public class FeatureComparator {
         if (!ignoreDescription) {
             assertEquals(expected.getDescription(), actual.getDescription(), () -> path() + "Description differ.");
         }
-        if (expected instanceof Deprecable && actual instanceof Deprecable) {
-            assertEquals(((Deprecable) expected).isDeprecated(),
-                         ((Deprecable) actual).isDeprecated(),
-                         () -> path() + "Deprecated state differ.");
+        if (expected instanceof Deprecable de && actual instanceof Deprecable da) {
+            assertEquals(de.isDeprecated(), da.isDeprecated(), () -> path() + "Deprecated state differ.");
         }
     }
 
@@ -442,8 +440,8 @@ public class FeatureComparator {
      * in a singleton collection.
      */
     private static Collection<?> asCollection(final Object value) {
-        if (value instanceof Collection<?>) {
-            return (Collection<?>) value;
+        if (value instanceof Collection<?> c) {
+            return c;
         } else {
             return CollectionsExt.singletonOrEmpty(value);
         }
diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/PeriodLiteral.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/PeriodLiteral.java
index bbe5d1b1bc..5624bfe21e 100644
--- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/PeriodLiteral.java
+++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/PeriodLiteral.java
@@ -100,8 +100,7 @@ final class PeriodLiteral implements Period, Literal<Feature,Period>, Serializab
      */
     @Override
     public boolean equals(final Object other) {
-        if (other instanceof PeriodLiteral) {
-            final PeriodLiteral p = (PeriodLiteral) other;
+        if (other instanceof PeriodLiteral p) {
             return begin == p.begin && end == p.end;
         }
         return false;
diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyConsistencyCheck.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyConsistencyCheck.java
index 69e958c20c..e4c3de1322 100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyConsistencyCheck.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyConsistencyCheck.java
@@ -217,8 +217,8 @@ public abstract class PropertyConsistencyCheck extends AnnotationConsistencyChec
             fail(e.toString());
             return;
         }
-        if (instance instanceof AbstractMetadata) {
-            validate((AbstractMetadata) instance);
+        if (instance instanceof AbstractMetadata md) {
+            validate(md);
         }
         /*
          * Iterate over all properties defined in the interface,
@@ -255,9 +255,8 @@ public abstract class PropertyConsistencyCheck extends AnnotationConsistencyChec
                 assertFalse(isMap | isCollection, "Null values are not allowed to be collections.");
             } else {
                 assertTrue(propertyType.isInstance(value), "Wrong property type.");
-                if (value instanceof CheckedContainer<?>) {
-                    assertTrue(elementType.isAssignableFrom(((CheckedContainer<?>) value).getElementType()),
-                               "Wrong element type in collection.");
+                if (value instanceof CheckedContainer<?> c) {
+                    assertTrue(elementType.isAssignableFrom(c.getElementType()), "Wrong element type in collection.");
                 }
                 if (isMap) {
                     assertTrue(((Map<?,?>) value).isEmpty(), "Collections shall be initially empty.");
diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeTest.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeTest.java
index be142cca70..c4e21e2255 100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeTest.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeTest.java
@@ -410,8 +410,8 @@ public final class TreeNodeTest extends TestCase {
     {
         final Object actual = node.getValue(column);
         Object unlocalized = actual;
-        if (unlocalized instanceof InternationalString) {
-            unlocalized = ((InternationalString) unlocalized).toString(Locale.ROOT);
+        if (unlocalized instanceof InternationalString i18n) {
+            unlocalized = i18n.toString(Locale.ROOT);
         }
         assertEquals(expected[index++], unlocalized, "values[" + index + ']');
         for (final TreeTable.Node child : node.getChildren()) {
diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeTableViewTest.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeTableViewTest.java
index 967db40900..6fd4062020 100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeTableViewTest.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeTableViewTest.java
@@ -181,8 +181,8 @@ public final class TreeTableViewTest extends TestCase {
      * Verifies the value of the given international string in English.
      */
     private static void assertI18nEq(final String expected, Object text) {
-        if (text instanceof InternationalString) {
-            text = ((InternationalString) text).toString(Locale.ENGLISH);
+        if (text instanceof InternationalString i18n) {
+            text = i18n.toString(Locale.ENGLISH);
         }
         assertEquals(expected, text);
     }
diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/sql/TestDatabase.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/sql/TestDatabase.java
index b945c42983..8b0ba0dbea 100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/sql/TestDatabase.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/sql/TestDatabase.java
@@ -311,8 +311,8 @@ public class TestDatabase implements AutoCloseable {
     public final void executeSQL(final List<?> scripts) throws IOException, SQLException {
         try (Connection c = source.getConnection(); ScriptRunner r = new ScriptRunner(c, 1000)) {
             for (final Object sql : scripts) {
-                if (sql instanceof String) {
-                    r.run((String) sql);
+                if (sql instanceof String s) {
+                    r.run(s);
                 } else {
                     final var s = (Supplier<?>) sql;
                     r.run(s.toString(), (InputStream) s.get());
diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/DocumentComparator.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/DocumentComparator.java
index 30865ee640..8755724a66 100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/DocumentComparator.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/DocumentComparator.java
@@ -202,16 +202,16 @@ public class DocumentComparator {
         ArgumentChecks.ensureNonNull("expected", expected);
         ArgumentChecks.ensureNonNull("actual",   actual);
         DocumentBuilder builder = null;
-        if (expected instanceof Node) {
-            expectedDoc = (Node) expected;
+        if (expected instanceof Node node) {
+            expectedDoc = node;
         } else {
             builder = newDocumentBuilder();
             try (InputStream stream = toInputStream(expected)) {
                 expectedDoc = builder.parse(stream);
             }
         }
-        if (actual instanceof Node) {
-            actualDoc = (Node) actual;
+        if (actual instanceof Node node) {
+            actualDoc = node;
         } else {
             if (builder == null) {
                 builder = newDocumentBuilder();
@@ -240,12 +240,12 @@ public class DocumentComparator {
      * See the constructor Javadoc for the list of allowed input type.
      */
     private static InputStream toInputStream(final Object input) throws IOException {
-        if (input instanceof InputStream) return (InputStream) input;
-        if (input instanceof File)        return new FileInputStream((File) input);
-        if (input instanceof URI)         return ((URI) input).toURL().openStream();
-        if (input instanceof URL)         return ((URL) input).openStream();
-        if (input instanceof Path)        return Files.newInputStream((Path) input);
-        if (input instanceof String)      return new ByteArrayInputStream(input.toString().getBytes("UTF-8"));
+        if (input instanceof InputStream t) return t;
+        if (input instanceof File t)        return new FileInputStream(t);
+        if (input instanceof URI t)         return t.toURL().openStream();
+        if (input instanceof URL t)         return t.openStream();
+        if (input instanceof Path t)        return Files.newInputStream(t);
+        if (input instanceof String t)      return new ByteArrayInputStream(t.getBytes("UTF-8"));
         throw new IOException("Cannot handle input type: " + (input != null ? input.getClass() : input));
     }
 
@@ -308,16 +308,16 @@ public class DocumentComparator {
          * Check text value for types:
          * TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE
          */
-        if (expected instanceof CDATASection) {
-            compareCDATASectionNode((CDATASection) expected, actual);
-        } else if (expected instanceof Text) {
-            compareTextNode((Text) expected, actual);
-        } else if (expected instanceof Comment) {
-            compareCommentNode((Comment) expected, actual);
-        } else if (expected instanceof ProcessingInstruction) {
-            compareProcessingInstructionNode((ProcessingInstruction) expected, actual);
-        } else if (expected instanceof Attr) {
-            compareAttributeNode((Attr) expected, actual);
+        if (expected instanceof CDATASection t) {
+            compareCDATASectionNode(t, actual);
+        } else if (expected instanceof Text t) {
+            compareTextNode(t, actual);
+        } else if (expected instanceof Comment t) {
+            compareCommentNode(t, actual);
+        } else if (expected instanceof ProcessingInstruction t) {
+            compareProcessingInstructionNode(t, actual);
+        } else if (expected instanceof Attr t) {
+            compareAttributeNode(t, actual);
         } else {
             compareNames(expected, actual);
             compareAttributes(expected, actual);
@@ -663,8 +663,8 @@ public class DocumentComparator {
      * than zero has been provided.
      */
     private static double doubleValue(final Comparable<?> property) {
-        if (property instanceof Number) {
-            return ((Number) property).doubleValue();
+        if (property instanceof Number n) {
+            return n.doubleValue();
         }
         if (property instanceof CharSequence) try {
             return Double.parseDouble(property.toString());
@@ -723,8 +723,8 @@ public class DocumentComparator {
         final List<String> hierarchy = new ArrayList<>();
         while (node != null) {
             hierarchy.add(node.getNodeName());
-            if (node instanceof Attr) {
-                node = ((Attr) node).getOwnerElement();
+            if (node instanceof Attr t) {
+                node = t.getOwnerElement();
             } else {
                 node = node.getParentNode();
             }
diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/PackageVerifier.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/PackageVerifier.java
index a615042c7e..2444be7fe4 100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/PackageVerifier.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/PackageVerifier.java
@@ -202,21 +202,21 @@ final class PackageVerifier {
                 for (final XmlJavaTypeAdapter adapter : adapters.value()) {
                     Class<?> propertyType = adapter.type();
                     if (propertyType == XmlJavaTypeAdapter.DEFAULT.class) {
-                        for (Class<?> c = adapter.value(); ; c = c.getSuperclass()) {
-                            final Type type = c.getGenericSuperclass();
-                            if (type == null) {
+                        for (Class<?> adapterClass = adapter.value(); ; adapterClass = adapterClass.getSuperclass()) {
+                            final Type adapterType = adapterClass.getGenericSuperclass();
+                            if (adapterType == null) {
                                 throw new SchemaException(String.format(
                                         "Cannot infer type for %s adapter.", adapter.value().getName()));
                             }
-                            if (type instanceof ParameterizedType) {
-                                final Type[] p = ((ParameterizedType) type).getActualTypeArguments();
-                                if (p.length == 2) {
-                                    Type pt = p[1];
-                                    if (pt instanceof ParameterizedType) {
-                                        pt = ((ParameterizedType) pt).getRawType();
+                            if (adapterType instanceof ParameterizedType p1) {
+                                final Type[] parameters = p1.getActualTypeArguments();
+                                if (parameters.length == 2) {
+                                    Type typeInAPI = parameters[1];
+                                    if (typeInAPI instanceof ParameterizedType p2) {
+                                        typeInAPI = p2.getRawType();
                                     }
-                                    if (pt instanceof Class<?>) {
-                                        propertyType = (Class<?>) pt;
+                                    if (typeInAPI instanceof Class<?> pc) {
+                                        propertyType = pc;
                                         break;
                                     }
                                 }
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/AbstractEnvelopeTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/AbstractEnvelopeTest.java
index 882fbcfdf3..191d24f7db 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/AbstractEnvelopeTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/AbstractEnvelopeTest.java
@@ -262,8 +262,7 @@ public final class AbstractEnvelopeTest extends TestCase {
             assertEquals(+180, envelope.getMaximum (0), label);
             assertEquals(   4, envelope.getMedian  (0), label);     // Note the alternance with the previous test methods.
             assertEquals( NaN, envelope.getSpan    (0), label);     // testCrossingAntiMeridian() + 360°.
-            if (envelope instanceof AbstractEnvelope) {
-                final var ext = (AbstractEnvelope) envelope;
+            if (envelope instanceof AbstractEnvelope ext) {
                 assertTrue (ext.contains  (inside),           label);
                 assertFalse(ext.contains  (outside),          label);
                 assertFalse(ext.contains  (intersect, false), label);
@@ -274,8 +273,7 @@ public final class AbstractEnvelopeTest extends TestCase {
                 assertContains(ext, contained);
                 break;
             }
-            if (envelope instanceof Rectangle2D) {
-                final var ext = (Rectangle2D) envelope;
+            if (envelope instanceof Rectangle2D ext) {
                 assertTrue (ext.contains  (inside),    label);
                 assertFalse(ext.contains  (outside),   label);
                 assertFalse(ext.contains  (intersect), label);
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/GeneralEnvelopeTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/GeneralEnvelopeTest.java
index 01a266d668..2ffe2efe00 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/GeneralEnvelopeTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/GeneralEnvelopeTest.java
@@ -123,8 +123,7 @@ public class GeneralEnvelopeTest extends TestCase {
         assertEquals(ymax,   test .getMaximum (1), "ymax");
         assertEquals(ymin,   lower.getOrdinate(1), "ymin");
         assertEquals(ymax,   upper.getOrdinate(1), "ymax");
-        if (test instanceof Envelope2D) {
-            final Envelope2D ri = (Envelope2D) test;
+        if (test instanceof Envelope2D ri) {
             assertEquals(xmin, ri.getMinX(), "xmin");
             assertEquals(xmax, ri.getMaxX(), "xmax");
             assertEquals(ymin, ri.getMinY(), "ymin");
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/WKTDictionaryTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/WKTDictionaryTest.java
index 14881eb561..e46ccd6f0b 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/WKTDictionaryTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/WKTDictionaryTest.java
@@ -227,8 +227,8 @@ public final class WKTDictionaryTest extends TestCase {
          * distinct instance, separated by identity comparison (not by {@link Object#equals(Object)}).
          */
         @Override public void accept(final Object value) {
-            if (value instanceof StoredTree) {
-                ((StoredTree) value).forEachValue(this);
+            if (value instanceof StoredTree tree) {
+                tree.forEachValue(this);
             }
             counts.merge(value, 1, this);
         }
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
index 890733dae7..f60b96ce9c 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
@@ -317,9 +317,9 @@ public final class Assertions extends Static {
     public static void assertContains(final RectangularShape outer, final Rectangle2D inner) {
         assertTrue(outer.contains  (inner), "outer.contains(inner)");
         assertTrue(outer.intersects(inner), "outer.intersects(inner)");
-        if (outer instanceof Rectangle2D) {
-            assertTrue (inner.intersects((Rectangle2D) outer), "inner.intersects(outer)");
-            assertFalse(inner.contains  ((Rectangle2D) outer), "inner.contains(outer)");
+        if (outer instanceof Rectangle2D r) {
+            assertTrue (inner.intersects(r), "inner.intersects(outer)");
+            assertFalse(inner.contains  (r), "inner.contains(outer)");
         }
         assertTrue(outer.contains(inner.getCenterX(), inner.getCenterY()), "outer.contains(centerX, centerY)");
     }
@@ -337,8 +337,7 @@ public final class Assertions extends Static {
         assertTrue(outer.contains  (inner, false), "outer.contains(inner)");
         assertTrue(outer.intersects(inner, true),  "outer.intersects(inner)");
         assertTrue(outer.intersects(inner, false), "outer.intersects(inner)");
-        if (inner instanceof AbstractEnvelope) {
-            final AbstractEnvelope ai = (AbstractEnvelope) inner;
+        if (inner instanceof AbstractEnvelope ai) {
             assertTrue (ai.intersects(outer, true),  "inner.intersects(outer)");
             assertTrue (ai.intersects(outer, false), "inner.intersects(outer)");
             assertFalse(ai.contains  (outer, true),  "inner.contains(outer)");
@@ -365,9 +364,9 @@ public final class Assertions extends Static {
     public static void assertDisjoint(final RectangularShape r1, final Rectangle2D r2) {
         assertFalse(r1.intersects(r2), "r1.intersects(r2)");
         assertFalse(r1.contains(r2), "r1.contains(r2)");
-        if (r1 instanceof Rectangle2D) {
-            assertFalse(r2.intersects((Rectangle2D) r1), "r2.intersects(r1)");
-            assertFalse(r2.contains  ((Rectangle2D) r1), "r2.contains(r1)");
+        if (r1 instanceof Rectangle2D r) {
+            assertFalse(r2.intersects(r), "r2.intersects(r1)");
+            assertFalse(r2.contains  (r), "r2.contains(r1)");
         }
         for (int i=0; i<9; i++) {
             final double x, y;
@@ -400,8 +399,7 @@ public final class Assertions extends Static {
         assertFalse(e1.intersects(e2, true),  "e1.intersects(e2)");
         assertFalse(e1.contains  (e2, false), "e1.contains(e2)");
         assertFalse(e1.contains  (e2, true),  "e1.contains(e2)");
-        if (e2 instanceof AbstractEnvelope) {
-            final AbstractEnvelope ae = (AbstractEnvelope) e2;
+        if (e2 instanceof AbstractEnvelope ae) {
             assertFalse(ae.intersects(e1, false), "e2.intersects(e1)");
             assertFalse(ae.intersects(e1, true),  "e2.intersects(e1)");
             assertFalse(ae.contains  (e1, false), "e2.contains(e1)");
@@ -436,8 +434,8 @@ public final class Assertions extends Static {
      */
     public static void assertIsIdentity(final MathTransform transform) {
         assertTrue(transform.isIdentity(), "isIdentity()");
-        if (transform instanceof LinearTransform) {
-            assertTrue(((LinearTransform) transform).getMatrix().isIdentity(), "getMatrix().isIdentity()");
+        if (transform instanceof LinearTransform linear) {
+            assertTrue(linear.getMatrix().isIdentity(), "getMatrix().isIdentity()");
         }
     }
 
@@ -449,8 +447,8 @@ public final class Assertions extends Static {
      */
     public static void assertIsNotIdentity(final MathTransform transform) {
         assertFalse(transform.isIdentity(), "isIdentity()");
-        if (transform instanceof LinearTransform) {
-            assertFalse(((LinearTransform) transform).getMatrix().isIdentity(), "getMatrix().isIdentity()");
+        if (transform instanceof LinearTransform linear) {
+            assertFalse(linear.getMatrix().isIdentity(), "getMatrix().isIdentity()");
         }
     }
 
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/GeodeticObjectVerifier.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/GeodeticObjectVerifier.java
index 6e8d7a3519..a5559e1592 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/GeodeticObjectVerifier.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/GeodeticObjectVerifier.java
@@ -66,8 +66,8 @@ public final class GeodeticObjectVerifier {
     private static void assertIsWorld(final Extent extent, boolean isMandatory) {
         if (extent != null) {
             for (final GeographicExtent element : extent.getGeographicElements()) {
-                if (element instanceof GeographicBoundingBox) {
-                    assertIsWorld((GeographicBoundingBox) element);
+                if (element instanceof GeographicBoundingBox bbox) {
+                    assertIsWorld(bbox);
                     isMandatory = false;
                 }
             }
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/MapProjectionTestCase.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/MapProjectionTestCase.java
index 6bf8923257..6661e00b60 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/MapProjectionTestCase.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/MapProjectionTestCase.java
@@ -158,9 +158,9 @@ abstract class MapProjectionTestCase extends MathTransformTestCase {
     final NormalizedProjection getKernel() {
         NormalizedProjection kernel = null;
         for (final MathTransform component : MathTransforms.getSteps(transform)) {
-            if (component instanceof NormalizedProjection) {
+            if (component instanceof NormalizedProjection proj) {
                 assertNull(kernel, "Found more than one kernel.");
-                kernel = (NormalizedProjection) component;
+                kernel = proj;
             }
         }
         assertNotNull(kernel);
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/ProjectionResultComparator.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/ProjectionResultComparator.java
index b7d78396d6..f419d97f78 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/ProjectionResultComparator.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/ProjectionResultComparator.java
@@ -98,8 +98,7 @@ final class ProjectionResultComparator extends NormalizedProjection {
         final List<MathTransform> steps = MathTransforms.getSteps(transform);
         for (int i=steps.size(); --i >= 0;) {
             final MathTransform step = steps.get(i);
-            if (step instanceof NormalizedProjection) {
-                final NormalizedProjection spherical = (NormalizedProjection) step;
+            if (step instanceof NormalizedProjection spherical) {
                 final Class<?> sphericalClass = spherical.getClass();
                 final Class<?> ellipticalClass = sphericalClass.getSuperclass();
                 assertEquals("Spherical", sphericalClass.getSimpleName(), "Class name for the spherical formulas.");
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/MapProjectionTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/MapProjectionTest.java
index 22a4d82b25..6c4b1085e3 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/MapProjectionTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/MapProjectionTest.java
@@ -131,8 +131,8 @@ public final class MapProjectionTest extends TestCase {
         assertEquals(isMandatory ? 1 : 0, actual.getMinimumOccurs());
         if (epsgName != null) {
             for (final GenericName alias : actual.getAlias()) {
-                if (alias instanceof Identifier && ((Identifier) alias).getAuthority() != Citations.EPSG) {
-                    assertOgcIdentifierEquals(ogcName, (Identifier) alias);
+                if (alias instanceof Identifier id && id.getAuthority() != Citations.EPSG) {
+                    assertOgcIdentifierEquals(ogcName, id);
                     return;
                 }
             }
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
index 57b25dbd52..5bba742206 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
@@ -193,8 +193,8 @@ public abstract class MathTransformTestCase extends TransformTestCase {
      */
     @Debug
     private String getName() {
-        if (transform instanceof Parameterized) {
-            final ParameterDescriptorGroup descriptor = ((Parameterized) transform).getParameterDescriptors();
+        if (transform instanceof Parameterized pmt) {
+            final ParameterDescriptorGroup descriptor = pmt.getParameterDescriptors();
             if (descriptor != null) {
                 final Identifier identifier = descriptor.getName();
                 if (identifier != null) {
@@ -432,8 +432,8 @@ public abstract class MathTransformTestCase extends TransformTestCase {
             wkt = transform.toString();
         }
         table.append(wkt).nextColumn();
-        if (transform instanceof FormattableObject) {
-            wkt = ((FormattableObject) transform).toString(Convention.INTERNAL);
+        if (transform instanceof FormattableObject fmt) {
+            wkt = fmt.toString(Convention.INTERNAL);
         } else {
             wkt = transform.toString();
         }
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformWrapper.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformWrapper.java
index b2d6d836df..2525e3e4c8 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformWrapper.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformWrapper.java
@@ -212,8 +212,8 @@ public class MathTransformWrapper extends FormattableObject implements MathTrans
      */
     @Override
     protected final String formatTo(final Formatter formatter) {
-        if (transform instanceof AbstractMathTransform) {
-            return ((AbstractMathTransform) transform).formatTo(formatter);
+        if (transform instanceof AbstractMathTransform fmt) {
+            return fmt.formatTo(formatter);
         }
         throw new UnformattableObjectException();
     }
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/TransformResultComparator.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/TransformResultComparator.java
index 09155035ea..ca7bb0085f 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/TransformResultComparator.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/TransformResultComparator.java
@@ -198,8 +198,7 @@ final class TransformResultComparator implements MathTransform {
     {
         double tolerance;
         final MathTransform t1, r1, t2, r2;
-        if (tr1 instanceof TransformResultComparator) {
-            final TransformResultComparator c = (TransformResultComparator) tr1;
+        if (tr1 instanceof TransformResultComparator c) {
             t1 = c.tested;
             r1 = c.reference;
             tolerance = c.tolerance;
@@ -207,8 +206,7 @@ final class TransformResultComparator implements MathTransform {
             t1 = r1 = tr1;
             tolerance = 0;
         }
-        if (tr2 instanceof TransformResultComparator) {
-            final TransformResultComparator c = (TransformResultComparator) tr2;
+        if (tr2 instanceof TransformResultComparator c) {
             t2 = c.tested;
             r2 = c.reference;
             if (c.tolerance > tolerance) {
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
index a277e048be..2c972e617a 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
@@ -346,7 +346,7 @@ public class CoordinateOperationMethods extends HTMLGenerator {
                     remarks = "Optional ";
                 } else {
                     final Comparable<?> min = param.getMinimumValue();
-                    if ((min instanceof Number) && ((Number) min).doubleValue() == ((Number) param.getMaximumValue()).doubleValue()) {
+                    if ((min instanceof Number n) && n.doubleValue() == ((Number) param.getMaximumValue()).doubleValue()) {
                         remarks = "Unmodifiable ";
                     } else {
                         remarks = "See note ";
@@ -425,10 +425,10 @@ public class CoordinateOperationMethods extends HTMLGenerator {
             } catch (FactoryException e) {
                 continue;                                                   // Ignore and inspect the next element.
             }
-            if (crs instanceof GeneralDerivedCRS) {
-                final GeographicBoundingBox candidate = CRS.getGeographicBoundingBox(crs);
+            if (crs instanceof GeneralDerivedCRS derived) {
+                final GeographicBoundingBox candidate = CRS.getGeographicBoundingBox(derived);
                 if (candidate != null) {
-                    final String name = ((GeneralDerivedCRS) crs).getConversionFromBase().getMethod().getName().getCode();
+                    final String name = derived.getConversionFromBase().getMethod().getName().getCode();
                     DefaultGeographicBoundingBox validity = domainOfValidity.get(name);
                     if (validity == null) {
                         validity = new DefaultGeographicBoundingBox(candidate);
@@ -469,11 +469,11 @@ public class CoordinateOperationMethods extends HTMLGenerator {
     private String getDefaultValue(final ParameterDescriptor<?> param, final String unit) {
         Object defaultValue = param.getDefaultValue();
         if (defaultValue != null) {
-            if (defaultValue instanceof Number) {
+            if (defaultValue instanceof Number n) {
                 // Trim the fractional part if unnecessary (e.g. "0.0" to "0").
-                defaultValue = Numbers.narrowestNumber((Number) defaultValue);
-            } else if (defaultValue instanceof String) {
-                return (String) defaultValue;
+                defaultValue = Numbers.narrowestNumber(n);
+            } else if (defaultValue instanceof String s) {
+                return s;
             }
         } else {
             if (ArraysExt.contains(defaultToLatitudeOfOrigin, param)) {
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
index 7462e24c44..da0c4e162b 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
@@ -411,8 +411,8 @@ public final class CoordinateReferenceSystems extends AuthorityCodesReport {
         if (crs instanceof GeographicCRS) {
             return (crs.getCoordinateSystem().getDimension() == 3) ? "Geographic 3D" : "Geographic";
         }
-        if (crs instanceof GeneralDerivedCRS) {
-            final OperationMethod method = ((GeneralDerivedCRS) crs).getConversionFromBase().getMethod();
+        if (crs instanceof GeneralDerivedCRS derived) {
+            final OperationMethod method = derived.getConversionFromBase().getMethod();
             final Identifier identifier = IdentifiedObjects.getIdentifier(method, Citations.EPSG);
             if (identifier != null) {
                 return "<a href=\"CoordinateOperationMethods.html#" + identifier.getCode()
@@ -428,13 +428,13 @@ public final class CoordinateReferenceSystems extends AuthorityCodesReport {
             }
             return "Geocentric";
         }
-        if (crs instanceof VerticalCRS) {
-            final VerticalDatumType type = ((VerticalCRS) crs).getDatum().getVerticalDatumType();
+        if (crs instanceof VerticalCRS vertical) {
+            final VerticalDatumType type = vertical.getDatum().getVerticalDatumType();
             return CharSequences.camelCaseToSentence(type.name().toLowerCase(getLocale())) + " height";
         }
-        if (crs instanceof CompoundCRS) {
+        if (crs instanceof CompoundCRS compound) {
             final StringBuilder buffer = new StringBuilder();
-            for (final CoordinateReferenceSystem component : ((CompoundCRS) crs).getComponents()) {
+            for (final CoordinateReferenceSystem component : compound.getComponents()) {
                 if (buffer.length() != 0) {
                     buffer.append(" + ");
                 }
@@ -531,16 +531,16 @@ public final class CoordinateReferenceSystems extends AuthorityCodesReport {
          * If the object is deprecated, find the replacement.
          * We do not take the whole comment because it may be pretty long.
          */
-        if (object instanceof Deprecable) {
-            row.isDeprecated = ((Deprecable) object).isDeprecated();
+        if (object instanceof Deprecable dep) {
+            row.isDeprecated = dep.isDeprecated();
             if (row.isDeprecated) {
                 String replacedBy = null;
                 InternationalString i18n = object.getRemarks();
                 for (final Identifier id : object.getIdentifiers()) {
-                    if (id instanceof Deprecable && ((Deprecable) id).isDeprecated()) {
-                        i18n = ((Deprecable) id).getRemarks();
-                        if (id instanceof DeprecatedCode) {
-                            replacedBy = ((DeprecatedCode) id).replacedBy;
+                    if (id instanceof Deprecable did && did.isDeprecated()) {
+                        i18n = did.getRemarks();
+                        if (id instanceof DeprecatedCode dc) {
+                            replacedBy = dc.replacedBy;
                         }
                         break;
                     }
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/util/j2d/ShapeUtilitiesExt.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/util/j2d/ShapeUtilitiesExt.java
index e7414410f5..7286033e29 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/util/j2d/ShapeUtilitiesExt.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/util/j2d/ShapeUtilitiesExt.java
@@ -120,19 +120,16 @@ public final class ShapeUtilitiesExt {
     public static Point2D.Double pointOnBezier(final Shape bezier, final double t) {
         final double x, y;
         final double mt = 1 - t;
-        if (bezier instanceof Line2D) {
-            final Line2D z = (Line2D) bezier;
+        if (bezier instanceof Line2D z) {
             x = mt * z.getX1()  +  t * z.getX2();
             y = mt * z.getY1()  +  t * z.getY2();
-        } else if (bezier instanceof QuadCurve2D) {
-            final QuadCurve2D z = (QuadCurve2D) bezier;
+        } else if (bezier instanceof QuadCurve2D z) {
             final double a = mt * mt;
             final double b = mt * t * 2;
             final double c =  t * t;
             x = a * z.getX1()  +  b * z.getCtrlX()  +  c * z.getX2();
             y = a * z.getY1()  +  b * z.getCtrlY()  +  c * z.getY2();
-        } else if (bezier instanceof CubicCurve2D) {
-            final CubicCurve2D z = (CubicCurve2D) bezier;
+        } else if (bezier instanceof CubicCurve2D z) {
             final double a = mt * mt * mt;
             final double b = mt * mt * t  * 3;
             final double c = mt * (t * t) * 3;
diff --git a/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/TestCase.java b/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/TestCase.java
index b231a151bd..3b7e9af51a 100644
--- a/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/TestCase.java
+++ b/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/TestCase.java
@@ -220,15 +220,9 @@ public abstract class TestCase extends org.apache.sis.test.TestCase {
          * only after we have closed all other files.
          */
         if (failure != null) {
-            if (failure instanceof IOException) {
-                throw (IOException) failure;
-            }
-            if (failure instanceof RuntimeException) {
-                throw (RuntimeException) failure;
-            }
-            if (failure instanceof Error) {
-                throw (Error) failure;
-            }
+            if (failure instanceof IOException e) throw e;
+            if (failure instanceof RuntimeException e) throw e;
+            if (failure instanceof Error e) throw e;
             throw new UndeclaredThrowableException(failure);
         }
     }
diff --git a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/folder/StoreTest.java b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/folder/StoreTest.java
index 09535b53ae..ccd89be0dd 100644
--- a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/folder/StoreTest.java
+++ b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/folder/StoreTest.java
@@ -116,8 +116,8 @@ public final class StoreTest extends TestCase {
             for (Identification info : resource.getMetadata().getIdentificationInfo()) {
                 final String id = Citations.getIdentifier(info.getCitation());
                 assertTrue(identifiers.remove(id), id);
-                if (resource instanceof Aggregate) {
-                    verifyContent((Aggregate) resource, identifiers);
+                if (resource instanceof Aggregate aggregate) {
+                    verifyContent(aggregate, identifiers);
                 }
             }
         }
diff --git a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/SubsampledImage.java b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/SubsampledImage.java
index 69d55bb6bf..1b4f2dc2f0 100644
--- a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/SubsampledImage.java
+++ b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/SubsampledImage.java
@@ -94,8 +94,7 @@ final class SubsampledImage extends PlanarImage {
         this.offX   = offX;
         this.offY   = offY;
         final SampleModel sourceModel = source.getSampleModel();
-        if (sourceModel instanceof PixelInterleavedSampleModel) {
-            final PixelInterleavedSampleModel sm = (PixelInterleavedSampleModel) sourceModel;
+        if (sourceModel instanceof PixelInterleavedSampleModel sm) {
             final int   pixelStride    = sm.getPixelStride();
             final int   scanlineStride = sm.getScanlineStride();
             final int   strideOffset   = pixelStride*offX + scanlineStride*offY;
@@ -117,8 +116,7 @@ final class SubsampledImage extends PlanarImage {
                     divExclusive(sm.getWidth(),  subX),
                     divExclusive(sm.getHeight(), subY),
                     pixelStride*subX, scanlineStride*subY, bandOffsets);
-        } else if (sourceModel instanceof MultiPixelPackedSampleModel) {
-            final MultiPixelPackedSampleModel sm = (MultiPixelPackedSampleModel) sourceModel;
+        } else if (sourceModel instanceof MultiPixelPackedSampleModel sm) {
             assertEquals(1, subX, "Subsampling on the X axis is not supported.");
             model = new MultiPixelPackedSampleModel(sm.getDataType(),
                     divExclusive(sm.getWidth(),  subX),
@@ -203,9 +201,9 @@ final class SubsampledImage extends PlanarImage {
                 throw e;
             }
             final String warning = image.verify();
-            if (warning != null && (source instanceof PlanarImage)) {
+            if (warning != null && (source instanceof PlanarImage planar)) {
                 // Source warning may be "source.height", which we replace by "height".
-                final String s = Strings.orEmpty(((PlanarImage) source).verify());
+                final String s = Strings.orEmpty(planar.verify());
                 assertEquals(s.substring(s.lastIndexOf('.') + 1), warning, s);
             }
             return image;
@@ -365,8 +363,7 @@ final class SubsampledImage extends PlanarImage {
      */
     @Override
     public boolean equals(final Object object) {
-        if (object instanceof SubsampledImage) {
-            final SubsampledImage other = (SubsampledImage) object;
+        if (object instanceof SubsampledImage other) {
             return source.equals(other.source) &&
                    subX == other.subX &&
                    subY == other.subY &&
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LineAppenderTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LineAppenderTest.java
index 04f9a011a6..0e8d11bf5e 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LineAppenderTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/LineAppenderTest.java
@@ -56,8 +56,8 @@ public class LineAppenderTest extends AppenderTestCase {
     @Override
     void run(final String lineSeparator) throws IOException {
         final Appendable f = appender;
-        if (f instanceof LineAppender) {
-            assertEquals(" ", ((LineAppender) f).getLineSeparator());
+        if (f instanceof LineAppender la) {
+            assertEquals(" ", la.getLineSeparator());
         }
         assertSame(f, f.append("Le vrai" + lineSeparator + "policitien, "));
         assertSame(f, f.append("c'est celui\r\nqui\r")); // Line separator broken on two method calls.
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TabulationExpansionTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TabulationExpansionTest.java
index 23a6e8cf3b..0ecc48b19f 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TabulationExpansionTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/TabulationExpansionTest.java
@@ -55,8 +55,8 @@ public final class TabulationExpansionTest extends LineAppenderTest {
     @Override
     void run(final String lineSeparator) throws IOException {
         final Appendable f = appender;
-        if (f instanceof LineAppender) {
-            assertEquals(8, ((LineAppender) f).getTabulationWidth());
+        if (f instanceof LineAppender la) {
+            assertEquals(8, la.getTabulationWidth());
         }
         assertSame(f, f.append("12\t8"   + lineSeparator));
         assertSame(f, f.append("1234\t8" + lineSeparator));
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapTest.java
index e662e8fa86..feaea17279 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/io/WordWrapTest.java
@@ -73,8 +73,8 @@ public class WordWrapTest extends LineAppenderTest {
     @Override
     void run(final String lineSeparator) throws IOException {
         final Appendable f = appender;
-        if (f instanceof LineAppender) {
-            assertEquals(LINE_LENGTH, ((LineAppender) f).getMaximalLineLength());
+        if (f instanceof LineAppender la) {
+            assertEquals(LINE_LENGTH, la.getMaximalLineLength());
         }
         final String BLUE    = X364.FOREGROUND_BLUE   .sequence();
         final String DEFAULT = X364.FOREGROUND_DEFAULT.sequence();
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/measure/SystemUnitTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/measure/SystemUnitTest.java
index d3e1a6cfab..dc0bba58d0 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/measure/SystemUnitTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/measure/SystemUnitTest.java
@@ -61,11 +61,10 @@ public final class SystemUnitTest extends TestCase {
     @Test
     public void verifyRelatedUnits() throws ReflectiveOperationException {
         for (final Field f : Units.class.getFields()) {
-            final Object value = f.get(null);
-            if (value instanceof SystemUnit<?>) {
-                final ConventionalUnit<?>[] related = ((SystemUnit<?>) value).related();
+            if (f.get(null) instanceof SystemUnit<?> value) {
+                final ConventionalUnit<?>[] related = value.related();
                 if (related != null) {
-                    final String symbol = ((SystemUnit<?>) value).getSymbol();
+                    final String symbol = value.getSymbol();
                     for (final ConventionalUnit<?> r : related) {
                         assertNotNull(r, symbol);
                         assertInstanceOf(LinearConverter.class, r.toTarget, symbol);
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java
index 464ad86ba8..a955d968a1 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java
@@ -377,12 +377,8 @@ public final class TestUtilities extends Static {
      */
     public static void rethrownIfNotNull(final Throwable failure) {
         if (failure != null) {
-            if (failure instanceof Error) {
-                throw (Error) failure;
-            }
-            if (failure instanceof RuntimeException) {
-                throw (RuntimeException) failure;
-            }
+            if (failure instanceof Error e) throw e;
+            if (failure instanceof RuntimeException e) throw e;
             throw new UndeclaredThrowableException(failure);
         }
     }
diff --git a/incubator/build.gradle.kts b/incubator/build.gradle.kts
index 10f43f7745..157103840f 100644
--- a/incubator/build.gradle.kts
+++ b/incubator/build.gradle.kts
@@ -82,6 +82,8 @@ tasks.compileJava {
     options.release.set(11)         // The version of both Java source code and compiled byte code.
 }
 tasks.compileTestJava {
+    options.compilerArgs.add("-source")         // "source", not "release", because we accept any target version.
+    options.compilerArgs.add("16")              // For consistency with the value set in the `endorsed` modules.
     srcDir.list().forEach {
         addRead(options.compilerArgs, it, "org.apache.sis.test.incubator,org.junit.jupiter.api")
     }
diff --git a/incubator/src/org.apache.sis.portrayal.map/test/org/apache/sis/map/SEPortrayerTest.java b/incubator/src/org.apache.sis.portrayal.map/test/org/apache/sis/map/SEPortrayerTest.java
index cde2a88bb3..f3e9ef9f59 100644
--- a/incubator/src/org.apache.sis.portrayal.map/test/org/apache/sis/map/SEPortrayerTest.java
+++ b/incubator/src/org.apache.sis.portrayal.map/test/org/apache/sis/map/SEPortrayerTest.java
@@ -172,15 +172,13 @@ public class SEPortrayerTest {
         presentations.stream().forEach(new Consumer<Presentation>() {
             @Override
             public void accept(Presentation t) {
-                if (t instanceof SEPresentation) {
-                    SEPresentation se = (SEPresentation) t;
+                if (t instanceof SEPresentation se) {
                     Feature Feature = se.getCandidate();
                     ids.add(new Match(String.valueOf(Feature.getPropertyValue(AttributeConvention.IDENTIFIER)),
                             se.getLayer(),
                             se.getResource(),
                             se.getSymbolizer()));
-                } else if (t instanceof ExceptionPresentation) {
-                    final ExceptionPresentation ep = (ExceptionPresentation) t;
+                } else if (t instanceof ExceptionPresentation ep) {
                     ids.add(new Match(ep.getException()));
                 }
             }
diff --git a/incubator/src/org.apache.sis.storage.coveragejson/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java b/incubator/src/org.apache.sis.storage.coveragejson/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
index 46bb1ff72c..4449517327 100644
--- a/incubator/src/org.apache.sis.storage.coveragejson/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
+++ b/incubator/src/org.apache.sis.storage.coveragejson/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
@@ -219,10 +219,10 @@ public class BindingTest {
     private static List<Object> asList(Object... array) {
         final List<Object> lst = new ArrayList<>(array.length);
         for (int i=0;i<array.length;i++) {
-            if (array[i] instanceof Integer) {
-                lst.add(BigDecimal.valueOf((Integer) array[i]));
-            } else if (array[i] instanceof Double) {
-                lst.add(BigDecimal.valueOf((Double) array[i]));
+            if (array[i] instanceof Integer v) {
+                lst.add(BigDecimal.valueOf(v));
+            } else if (array[i] instanceof Double v) {
+                lst.add(BigDecimal.valueOf(v));
             } else {
                 lst.add(array[i]);
             }
diff --git a/optional/build.gradle.kts b/optional/build.gradle.kts
index 37ecbf7ccf..9d3b5aa332 100644
--- a/optional/build.gradle.kts
+++ b/optional/build.gradle.kts
@@ -84,6 +84,8 @@ tasks.compileJava {
     options.release.set(16)         // The version of both Java source code and compiled byte code.
 }
 tasks.compileTestJava {
+    options.compilerArgs.add("-source")         // "source", not "release", because we accept any target version.
+    options.compilerArgs.add("16")
     patchForTests(options.compilerArgs);
     srcDir.list().forEach {
         addRead(options.compilerArgs, it, "org.apache.sis.test.optional,org.junit.jupiter.api")


(sis) 03/05: Better identification of code that will need to be replaced in Java 16.

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit c0fc9b4dfee471dce9850230de66687abd6daced
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Feb 20 11:18:09 2024 +0100

    Better identification of code that will need to be replaced in Java 16.
---
 .../apache/sis/coverage/grid/j2d/TilePlaceholder.java  |  4 ++--
 .../main/org/apache/sis/image/TileCache.java           |  3 ++-
 .../map/coverage/MultiResolutionCoverageLoader.java    |  3 ++-
 .../sis/referencing/factory/sql/EPSGDataAccess.java    |  4 ++--
 .../main/org/apache/sis/storage/CoverageSubset.java    |  7 +++----
 endorsed/src/org.apache.sis.util/main/module-info.java |  1 +
 .../main/org/apache/sis/pending/jdk/JDK16.java         | 18 +++++++++++++++---
 .../main/org/apache/sis/util/collection/Cache.java     |  3 ++-
 8 files changed, 29 insertions(+), 14 deletions(-)

diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/j2d/TilePlaceholder.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/j2d/TilePlaceholder.java
index 8b367f2af4..d817942a9d 100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/j2d/TilePlaceholder.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/j2d/TilePlaceholder.java
@@ -28,6 +28,7 @@ import java.awt.image.WritableRaster;
 import java.awt.image.RenderedImage;
 import java.awt.image.BufferedImage;
 import java.lang.ref.WeakReference;
+import org.apache.sis.pending.jdk.JDK16;
 import org.apache.sis.util.Disposable;
 import org.apache.sis.util.Workaround;
 import org.apache.sis.util.collection.WeakHashSet;
@@ -206,8 +207,7 @@ public class TilePlaceholder {
                 r = reference;
             }
             if (r != null) {
-                return r.get() == tile.getDataBuffer();
-                // TODO: use r.refersTo(tile.getDataBuffer()) with JDK16.
+                return JDK16.refersTo(r, tile.getDataBuffer());
             }
         }
         return false;
diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/TileCache.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/TileCache.java
index 45a795c5c4..a9b6684b61 100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/TileCache.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/TileCache.java
@@ -23,6 +23,7 @@ import java.lang.ref.Reference;
 import org.apache.sis.util.collection.Cache;
 import org.apache.sis.util.internal.Numerics;
 import org.apache.sis.feature.internal.Resources;
+import org.apache.sis.pending.jdk.JDK16;
 
 
 /**
@@ -148,7 +149,7 @@ final class TileCache extends Cache<TileCache.Key, Raster> {
          * The {@link #dispose()} should have been invoked in such cases.
          */
         final boolean isEmpty() {
-            return image.get() == null;     // TODO: use `refersTo(null)` with JDK16.
+            return JDK16.refersTo(image, null);
         }
 
         /**
diff --git a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/MultiResolutionCoverageLoader.java b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/MultiResolutionCoverageLoader.java
index 1a0d6ab1e2..37e7243388 100644
--- a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/MultiResolutionCoverageLoader.java
+++ b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/MultiResolutionCoverageLoader.java
@@ -37,6 +37,7 @@ import org.apache.sis.coverage.grid.GridRoundingMode;
 import org.apache.sis.math.DecimalFunctions;
 import org.apache.sis.io.TableAppender;
 import org.apache.sis.system.Configuration;
+import org.apache.sis.pending.jdk.JDK16;
 
 
 /**
@@ -358,7 +359,7 @@ dimensions: for (int j=0; j<tgtDim; j++) {
                 table.nextColumn();
             }
             final Reference<GridCoverage> ref = coverages[level];
-            if (ref != null && ref.get() != null) {     // TODO: use !refersTo(null) in JDK16.
+            if (ref != null && !JDK16.refersTo(ref, null)) {
                 table.append("cached");
             }
             table.nextLine();
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
index 3f3ec15ab8..45749cd97c 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
@@ -116,6 +116,7 @@ import org.apache.sis.util.logging.Logging;
 import org.apache.sis.measure.MeasurementRange;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.measure.Units;
+import org.apache.sis.pending.jdk.JDK16;
 import static org.apache.sis.util.Utilities.equalsIgnoreMetadata;
 import static org.apache.sis.util.internal.StandardDateFormat.UTC;
 import static org.apache.sis.referencing.internal.ServicesForMetadata.CONNECTION;
@@ -3248,8 +3249,7 @@ next:                   while (r.next()) {
             System.gc();                // For cleaning as much weak references as we can before we check them.
             final Iterator<CloseableReference> it = authorityCodes.values().iterator();
             while (it.hasNext()) {
-                final AuthorityCodes codes = it.next().get();       // TODO: use referTo(null) with JDK16.
-                if (codes == null) {
+                if (JDK16.refersTo(it.next(), null)) {
                     it.remove();
                 } else {
                     /*
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/CoverageSubset.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/CoverageSubset.java
index 4911067365..59e0c8bd5b 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/CoverageSubset.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/CoverageSubset.java
@@ -19,7 +19,6 @@ package org.apache.sis.storage;
 import java.util.List;
 import java.util.Arrays;
 import java.util.Optional;
-import java.util.stream.Collectors;
 import org.opengis.util.GenericName;
 import org.opengis.util.FactoryException;
 import org.opengis.metadata.Metadata;
@@ -38,6 +37,7 @@ import org.apache.sis.storage.internal.Resources;
 import org.apache.sis.storage.base.MetadataBuilder;
 import org.apache.sis.storage.base.StoreUtilities;
 import org.apache.sis.util.internal.UnmodifiableArrayList;
+import org.apache.sis.pending.jdk.JDK16;
 
 
 /**
@@ -144,9 +144,8 @@ final class CoverageSubset extends AbstractGridCoverageResource {
     public List<double[]> getResolutions() throws DataStoreException {
         List<double[]> resolutions = source.getResolutions();
         if (reduction != null) {
-            resolutions.stream().map((resolution) -> {
-                return reduction.apply(new DirectPositionView.Double(resolution)).getCoordinate();
-            }).collect(Collectors.toList());        // TODO: replace by Stream.toList() on JDK16.
+            JDK16.toList(resolutions.stream()
+                    .map((resolution) -> reduction.apply(new DirectPositionView.Double(resolution)).getCoordinate()));
         }
         return resolutions;
     }
diff --git a/endorsed/src/org.apache.sis.util/main/module-info.java b/endorsed/src/org.apache.sis.util/main/module-info.java
index 87bc6184b0..25cdc40738 100644
--- a/endorsed/src/org.apache.sis.util/main/module-info.java
+++ b/endorsed/src/org.apache.sis.util/main/module-info.java
@@ -165,5 +165,6 @@ module org.apache.sis.util {
             org.apache.sis.storage.sql,
             org.apache.sis.storage.netcdf,
             org.apache.sis.storage.geotiff,
+            org.apache.sis.portrayal,
             org.apache.sis.console;
 }
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK16.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK16.java
index 53270b08ea..a8b1a165ec 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK16.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK16.java
@@ -16,9 +16,10 @@
  */
 package org.apache.sis.pending.jdk;
 
+import java.lang.ref.Reference;
 import java.util.List;
 import java.util.stream.Stream;
-import org.apache.sis.util.internal.UnmodifiableArrayList;
+import java.util.stream.Collectors;
 
 
 /**
@@ -33,6 +34,18 @@ public final class JDK16 {
     private JDK16() {
     }
 
+    /**
+     * Place holder for {@link Reference#refersTo(Object)} method added in JDK16.
+     *
+     * @param  <T>  type of elements in the reference.
+     * @param  ref  the reference to check, or {@code null}.
+     * @param  obj  the value to compare to the reference value.
+     * @return whether the reference value is the given object.
+     */
+    public static <T> boolean refersTo(Reference<T> ref, T obj) {
+        return ref.get() == obj;
+    }
+
     /**
      * Place holder for {@link Stream#toList()} method added in JDK16.
      *
@@ -40,8 +53,7 @@ public final class JDK16 {
      * @param  s    the stream to convert to a list.
      * @return the stream content as a list.
      */
-    @SuppressWarnings("unchecked")
     public static <T> List<T> toList(final Stream<T> s) {
-        return (List<T>) UnmodifiableArrayList.wrap(s.toArray());
+        return s.collect(Collectors.toList());
     }
 }
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/Cache.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/Cache.java
index 81b7b74395..ca17d58b30 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/Cache.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/Cache.java
@@ -31,6 +31,7 @@ import java.util.concurrent.locks.ReentrantLock;
 import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
 import java.lang.ref.SoftReference;
+import org.apache.sis.pending.jdk.JDK16;
 import org.apache.sis.util.Disposable;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
@@ -384,7 +385,7 @@ public class Cache<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V> {
      * @return whether the given value is null or a cleared reference.
      */
     private static boolean isNull(final Object value) {
-        return (value == null) || (value instanceof Reference<?> && ((Reference<?>) value).get() == null);  // TODO: use refersTo(null) with JDK16.
+        return (value == null) || (value instanceof Reference<?> && JDK16.refersTo((Reference<?>) value, null));
     }
 
     /**


(sis) 02/05: Add a common base class for tests that depend on the Garbage Collector activity. Those tests are executed in isolation for making them a little bit less undeterministic.

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit d17ebf34c0cd3732e4e0c8fde0e6f89606ab3bcc
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Feb 20 10:59:50 2024 +0100

    Add a common base class for tests that depend on the Garbage Collector activity.
    Those tests are executed in isolation for making them a little bit less undeterministic.
---
 .../org/apache/sis/test/TestConfiguration.java     |  11 --
 .../test/org/apache/sis/test/TestUtilities.java    |  68 ------------
 .../org/apache/sis/util/collection/CacheTest.java  |   7 +-
 .../apache/sis/util/collection/RangeSetTest.java   |   2 -
 .../apache/sis/util/collection/TestCaseWithGC.java | 114 +++++++++++++++++++++
 .../sis/util/collection/WeakHashSetTest.java       |  15 ++-
 .../sis/util/collection/WeakValueHashMapTest.java  |   7 +-
 7 files changed, 124 insertions(+), 100 deletions(-)

diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestConfiguration.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestConfiguration.java
index 99f869a629..8b2e92012b 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestConfiguration.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestConfiguration.java
@@ -58,15 +58,4 @@ public final class TestConfiguration extends Static {
      */
     private TestConfiguration() {
     }
-
-    /**
-     * Returns {@code true} if tests that may depend on the garbage collector activity are allowed.
-     * Those tests are a little bit dangerous since they may randomly fail on a server too busy for
-     * running the garbage collector as fast as expected.
-     *
-     * @return {@code true} if tests that may depend on garbage collector activity are allowed.
-     */
-    public static boolean allowGarbageCollectorDependentTests() {
-        return true;
-    }
 }
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java
index b929a5181e..464ad86ba8 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java
@@ -21,7 +21,6 @@ import java.util.Locale;
 import java.util.TimeZone;
 import java.util.Iterator;
 import java.util.Random;
-import java.util.concurrent.Callable;
 import java.io.PrintWriter;
 import java.io.IOException;
 import java.io.InputStream;
@@ -62,14 +61,6 @@ public final class TestUtilities extends Static {
      */
     private static final int SEPARATOR_WIDTH = 80;
 
-    /**
-     * Maximal time that {@code waitFoo()} methods can wait, in milliseconds.
-     *
-     * @see #waitForBlockedState(Thread)
-     * @see #waitForGarbageCollection(Callable)
-     */
-    private static final int MAXIMAL_WAIT_TIME = 1000;
-
     /**
      * Date parser and formatter using the {@code "yyyy-MM-dd HH:mm:ss"} pattern
      * and UTC time zone.
@@ -396,65 +387,6 @@ public final class TestUtilities extends Static {
         }
     }
 
-    /**
-     * Waits up to one second for the given thread to reach the
-     * {@linkplain java.lang.Thread.State#BLOCKED blocked} or the
-     * {@linkplain java.lang.Thread.State#WAITING waiting} state.
-     *
-     * @param  thread  the thread to wait for blocked or waiting state.
-     * @throws IllegalThreadStateException if the thread has terminated its execution,
-     *         or has not reached the waiting or blocked state before the timeout.
-     * @throws InterruptedException if this thread has been interrupted while waiting.
-     */
-    public static void waitForBlockedState(final Thread thread) throws IllegalThreadStateException, InterruptedException {
-        int retry = MAXIMAL_WAIT_TIME / 5;              // 5 shall be the same number as in the call to Thread.sleep.
-        do {
-            Thread.sleep(5);
-            switch (thread.getState()) {
-                case WAITING:
-                case BLOCKED: return;
-                case TERMINATED: throw new IllegalThreadStateException("The thread has completed execution.");
-            }
-        } while (--retry != 0);
-        throw new IllegalThreadStateException("The thread is not in a blocked or waiting state.");
-    }
-
-    /**
-     * Waits up to one second for the garbage collector to do its work. This method can be invoked
-     * only if {@link TestConfiguration#allowGarbageCollectorDependentTests()} returns {@code true}.
-     *
-     * <p>Note that this method does not throw any exception if the given condition has not been
-     * reached before the timeout. Instead, it is the caller responsibility to test the return
-     * value. This method is designed that way because the caller can usually produce a more
-     * accurate error message about which value has not been garbage collected as expected.</p>
-     *
-     * @param  stopCondition  a condition which return {@code true} if this method can stop waiting,
-     *         or {@code false} if it needs to ask again for garbage collection.
-     * @return {@code true} if the given condition has been met, or {@code false} if we waited up
-     *         to the timeout without meeting the given condition.
-     * @throws InterruptedException if this thread has been interrupted while waiting.
-     */
-    public static boolean waitForGarbageCollection(final Callable<Boolean> stopCondition) throws InterruptedException {
-        assertTrue(TestConfiguration.allowGarbageCollectorDependentTests(), "GC-dependent tests not allowed in this run.");
-        int retry = MAXIMAL_WAIT_TIME / 50;             // 50 shall be the same number as in the call to Thread.sleep.
-        boolean stop;
-        do {
-            if (--retry == 0) {
-                return false;
-            }
-            Thread.sleep(50);
-            System.gc();
-            try {
-                stop = stopCondition.call();
-            } catch (RuntimeException e) {
-                throw e;
-            } catch (Exception e) {
-                throw new AssertionError(e);
-            }
-        } while (!stop);
-        return true;
-    }
-
     /**
      * Copies the full content of the given input stream in a temporary file and returns the channel for that file.
      * The file is opened with {@link StandardOpenOption#DELETE_ON_CLOSE}, together with read and write options.
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/CacheTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/CacheTest.java
index a2e120f7fd..3335ad6953 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/CacheTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/CacheTest.java
@@ -34,9 +34,7 @@ import org.apache.sis.util.internal.StandardDateFormat;
 // Test dependencies
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
-import org.junit.jupiter.api.parallel.Isolated;
 import org.apache.sis.test.TestUtilities;
-import org.apache.sis.test.TestCase;
 import org.apache.sis.test.Performance;
 import static org.apache.sis.test.Assertions.assertMapEquals;
 
@@ -46,8 +44,7 @@ import static org.apache.sis.test.Assertions.assertMapEquals;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-@Isolated("Depends on garbage collector activity")
-public final class CacheTest extends TestCase {
+public final class CacheTest extends TestCaseWithGC {
     /**
      * Creates a new test case.
      */
@@ -164,7 +161,7 @@ public final class CacheTest extends TestCase {
         assertTrue(handler instanceof Cache<?,?>.Work);
         final OtherThread thread = new OtherThread();
         thread.start();
-        TestUtilities.waitForBlockedState(thread);
+        waitForBlockedState(thread);
         assertNull(cache.peek(keyByOtherThread), "The blocked thread shall not have added a value.");
         /*
          * Write. This will release the lock and let the other thread continue its job.
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/RangeSetTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/RangeSetTest.java
index 814dc234f6..08a006d0f8 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/RangeSetTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/RangeSetTest.java
@@ -23,7 +23,6 @@ import java.util.Random;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.SortedSet;
-import java.io.PrintWriter;
 import org.apache.sis.measure.Range;
 import org.apache.sis.measure.NumberRange;
 import static org.apache.sis.util.internal.StandardDateFormat.MILLISECONDS_PER_DAY;
@@ -575,7 +574,6 @@ public final class RangeSetTest extends TestCase {
      */
     @Performance
     public void stress() throws InterruptedException {
-        final PrintWriter out = TestCase.out;
         final Random r = TestUtilities.createRandomNumberGenerator();
         for (int p=0; p<10; p++) {
             final long start = System.nanoTime();
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/TestCaseWithGC.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/TestCaseWithGC.java
new file mode 100644
index 0000000000..ef24870f74
--- /dev/null
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/TestCaseWithGC.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.util.collection;
+
+import java.util.concurrent.Callable;
+
+// Test dependencies
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.parallel.Isolated;
+import org.apache.sis.test.TestCase;
+
+
+/**
+ * Base class of tests that may depend on the garbage collector activity.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+@Isolated("Depends on garbage collector activity")
+abstract class TestCaseWithGC extends TestCase {
+    /**
+     * Whether the tests that may depend on the garbage collector activity are allowed.
+     * Those tests are a little bit dangerous since they may randomly fail on a server
+     * too busy for running the garbage collector as fast as expected.
+     */
+    static final boolean GC_DEPENDENT_TESTS_ENABLED = true;
+
+    /**
+     * Maximal time that {@code waitFoo()} methods can wait, in milliseconds.
+     *
+     * @see #waitForBlockedState(Thread)
+     * @see #waitForGarbageCollection(Callable)
+     */
+    private static final int MAXIMAL_WAIT_TIME = 1000;
+
+    /**
+     * Creates a new test case.
+     */
+    protected TestCaseWithGC() {
+    }
+
+    /**
+     * Waits up to one second for the garbage collector to do its work.
+     * This method can be invoked only if {@link #GC_DEPENDENT_TESTS_ENABLED} is {@code true}.
+     *
+     * <p>Note that this method does not throw any exception if the given condition has not been
+     * reached before the timeout. Instead, it is the caller responsibility to test the return value.
+     * This method is designed that way because the caller can usually produce a more accurate error
+     * message about which value has not been garbage collected as expected.</p>
+     *
+     * @param  stopCondition  a condition which return {@code true} if this method can stop waiting,
+     *         or {@code false} if it needs to ask again for garbage collection.
+     * @return {@code true} if the given condition has been met, or {@code false} if we waited up
+     *         to the timeout without meeting the given condition.
+     * @throws InterruptedException if this thread has been interrupted while waiting.
+     */
+    static boolean waitForGarbageCollection(final Callable<Boolean> stopCondition) throws InterruptedException {
+        assertTrue(GC_DEPENDENT_TESTS_ENABLED, "GC-dependent tests not allowed in this run.");
+        int retry = MAXIMAL_WAIT_TIME / 50;     // 50 shall be the same number as in the call to Thread.sleep.
+        boolean stop;
+        do {
+            if (--retry == 0) {
+                return false;
+            }
+            Thread.sleep(50);
+            System.gc();
+            try {
+                stop = stopCondition.call();
+            } catch (RuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new AssertionError(e);
+            }
+        } while (!stop);
+        return true;
+    }
+
+    /**
+     * Waits up to one second for the given thread to reach the blocked or the waiting state.
+     *
+     * @param  thread  the thread to wait for blocked or waiting state.
+     * @throws IllegalThreadStateException if the thread has terminated its execution,
+     *         or has not reached the waiting or blocked state before the timeout.
+     * @throws InterruptedException if this thread has been interrupted while waiting.
+     *
+     * @see java.lang.Thread.State#BLOCKED
+     * @see java.lang.Thread.State#WAITING
+     */
+    static void waitForBlockedState(final Thread thread) throws IllegalThreadStateException, InterruptedException {
+        int retry = MAXIMAL_WAIT_TIME / 5;      // 5 shall be the same number as in the call to Thread.sleep.
+        do {
+            Thread.sleep(5);
+            switch (thread.getState()) {
+                case WAITING:
+                case BLOCKED: return;
+                case TERMINATED: throw new IllegalThreadStateException("The thread has completed execution.");
+            }
+        } while (--retry != 0);
+        throw new IllegalThreadStateException("The thread is not in a blocked or waiting state.");
+    }
+}
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/WeakHashSetTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/WeakHashSetTest.java
index 7275a5d7e5..04071b1c9a 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/WeakHashSetTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/WeakHashSetTest.java
@@ -22,10 +22,7 @@ import java.util.Random;
 // Test dependencies
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
-import org.apache.sis.test.TestCase;
-import org.apache.sis.test.TestConfiguration;
 import static org.apache.sis.test.Assertions.assertSetEquals;
-import static org.apache.sis.test.TestUtilities.waitForGarbageCollection;
 
 
 /**
@@ -34,7 +31,7 @@ import static org.apache.sis.test.TestUtilities.waitForGarbageCollection;
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  */
-public final class WeakHashSetTest extends TestCase {
+public final class WeakHashSetTest extends TestCaseWithGC {
     /**
      * The size of the test sets to be created.
      */
@@ -149,12 +146,12 @@ public final class WeakHashSetTest extends TestCase {
                 assertTrue(weakSet.containsAll(strongSet), "containsAll:");
             }
             /*
-             * The test below needs the garbage collector to complete fully its job in a timely
-             * manner. A failure in those tests is not necessarily a WeakValueHashMap bug, as it
-             * could be caused by a heavy server load preventing GC to complete its work. If this
-             * happen too often, we may turn off the "allow garbage collector dependent tests" flag.
+             * The test below needs the garbage collector to complete fully its job in a timely manner.
+             * A failure in those tests is not necessarily a WeakValueHashMap bug, as it could be caused
+             * by a heavy server load preventing GC to complete its work. If this happen too often,
+             * we may turn off the "allow garbage collector dependent tests" flag.
              */
-            if (TestConfiguration.allowGarbageCollectorDependentTests()) {
+            if (GC_DEPENDENT_TESTS_ENABLED) {
                 waitForGarbageCollection(() -> weakSet.size() == strongSet.size());
                 assertSetEquals(strongSet, weakSet);
                 /*
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/WeakValueHashMapTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/WeakValueHashMapTest.java
index a3b42a8284..f5ad861fd7 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/WeakValueHashMapTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/WeakValueHashMapTest.java
@@ -23,11 +23,8 @@ import java.util.Random;
 // Test dependencies
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
-import org.apache.sis.test.TestCase;
-import org.apache.sis.test.TestConfiguration;
 import org.apache.sis.test.TestUtilities;
 import static org.apache.sis.test.Assertions.assertMapEquals;
-import static org.apache.sis.test.TestUtilities.waitForGarbageCollection;
 
 
 /**
@@ -36,7 +33,7 @@ import static org.apache.sis.test.TestUtilities.waitForGarbageCollection;
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  */
-public final class WeakValueHashMapTest extends TestCase {
+public final class WeakValueHashMapTest extends TestCaseWithGC {
     /**
      * The size of the test sets to be created.
      */
@@ -160,7 +157,7 @@ public final class WeakValueHashMapTest extends TestCase {
              * could be caused by a heavy server load preventing GC to complete its work. If this
              * happen too often, we may turn off the "allow garbage collector dependent tests" flag.
              */
-            if (TestConfiguration.allowGarbageCollectorDependentTests()) {
+            if (GC_DEPENDENT_TESTS_ENABLED) {
                 waitForGarbageCollection(() -> weakMap.size() == strongMap.size());
                 assertMapEquals(strongMap, weakMap);
                 /*


(sis) 05/05: Fix some synchronization holes in parallel execution of tests.

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 1c81e3ce85623ce445f564bc95d98e8b5cf29447
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Feb 20 13:25:04 2024 +0100

    Fix some synchronization holes in parallel execution of tests.
---
 .../org/apache/sis/util/iso/NameMarshallingTest.java  | 19 ++++++++++++++-----
 .../org/apache/sis/xml/bind/gco/MultiplicityTest.java |  2 +-
 .../test/org/apache/sis/xml/test/TestCase.java        |  3 +++
 .../apache/sis/referencing/AuthorityFactories.java    |  2 ++
 .../sis/referencing/EPSGFactoryFallbackTest.java      |  5 +++++
 .../operation/CoordinateOperationFinderTest.java      |  3 +++
 .../operation/CoordinateOperationRegistryTest.java    |  5 ++++-
 .../DefaultCoordinateOperationFactoryTest.java        |  3 +++
 .../transform/CoordinateSystemTransformTest.java      |  3 +++
 .../org/apache/sis/storage/netcdf/base/TestCase.java  |  3 +++
 .../sis/storage/test/CoverageReadConsistency.java     |  3 +++
 .../main/org/apache/sis/system/Configuration.java     |  3 ++-
 .../sis/util/collection/TreeTableFormatTest.java      |  3 +++
 .../apache/sis/util/logging/PerformanceLevelTest.java |  2 ++
 14 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/NameMarshallingTest.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/NameMarshallingTest.java
index 37bb603ecb..d361648071 100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/NameMarshallingTest.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/NameMarshallingTest.java
@@ -50,8 +50,6 @@ import static org.apache.sis.metadata.Assertions.assertXmlEquals;
 public final class NameMarshallingTest extends TestCase {
     /**
      * A poll of configured {@link Marshaller} and {@link Unmarshaller}, created when first needed.
-     *
-     * @see #disposeMarshallerPool()
      */
     private MarshallerPool pool;
 
@@ -62,14 +60,23 @@ public final class NameMarshallingTest extends TestCase {
     }
 
     /**
-     * Returns the XML representation of the given name, wrapped
-     * in a mock {@code <gml:IO_IdentifiedObject>} element.
+     * Returns the marshaller pool, created when first needed.
      */
-    private String marshal(final GenericName name) throws JAXBException {
+    private synchronized MarshallerPool pool() throws JAXBException {
         if (pool == null) {
             pool = new MarshallerPool(JAXBContext.newInstance(IdentifiedObjectMock.class),
                                       Map.of(XML.LENIENT_UNMARSHAL, Boolean.TRUE));
         }
+        return pool;
+    }
+
+    /**
+     * Returns the XML representation of the given name, wrapped
+     * in a mock {@code <gml:IO_IdentifiedObject>} element.
+     */
+    private String marshal(final GenericName name) throws JAXBException {
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
+        final MarshallerPool pool = pool();
         final Marshaller marshaller = pool.acquireMarshaller();
         marshaller.setProperty(XML.METADATA_VERSION, VERSION_2007);
         final String xml = marshal(marshaller, new IdentifiedObjectMock(null, name));
@@ -81,6 +88,8 @@ public final class NameMarshallingTest extends TestCase {
      * Converse of {@link #marshal(GenericName)}.
      */
     private GenericName unmarshal(final String xml) throws JAXBException {
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
+        final MarshallerPool pool = pool();
         final Unmarshaller unmarshaller = pool.acquireUnmarshaller();
         final Object value = unmarshal(unmarshaller, xml);
         pool.recycle(unmarshaller);
diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gco/MultiplicityTest.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gco/MultiplicityTest.java
index 0c6e89083a..695bead899 100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gco/MultiplicityTest.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gco/MultiplicityTest.java
@@ -70,7 +70,7 @@ public final class MultiplicityTest extends TestUsingFile {
      * @throws JAXBException if an error occurred while creating the pool.
      */
     @Override
-    protected MarshallerPool getMarshallerPool() throws JAXBException {
+    protected synchronized MarshallerPool getMarshallerPool() throws JAXBException {
         if (pool == null) {
             pool = new MarshallerPool(JAXBContext.newInstance(FeatureAttributeMock.class),
                                       Map.of(XML.LENIENT_UNMARSHAL, Boolean.TRUE));
diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/TestCase.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/TestCase.java
index c9b35ee213..10f1546c27 100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/TestCase.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/TestCase.java
@@ -45,6 +45,8 @@ import org.junit.jupiter.api.AfterEach;
 import static org.junit.jupiter.api.Assertions.*;
 import static org.apache.sis.metadata.Assertions.assertXmlEquals;
 import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
 import org.junit.jupiter.api.parallel.ResourceAccessMode;
 import org.junit.jupiter.api.parallel.ResourceLock;
 import org.apache.sis.test.LoggingWatcher;
@@ -67,6 +69,7 @@ import org.apache.sis.test.LoggingWatcher;
  *
  * @see DocumentComparator
  */
+@Execution(ExecutionMode.SAME_THREAD)
 public abstract class TestCase extends org.apache.sis.test.TestCase {
     /**
      * Base class of (un)marshalling tests that may emit logs.
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
index aabc203bbb..ffcb67404c 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AuthorityFactories.java
@@ -32,6 +32,7 @@ import org.apache.sis.referencing.util.LazySet;
 import org.apache.sis.system.Reflect;
 import org.apache.sis.system.Loggers;
 import org.apache.sis.system.Modules;
+import org.apache.sis.system.Configuration;
 import org.apache.sis.system.SystemListener;
 import org.apache.sis.referencing.internal.EPSGFactoryProxy;
 import org.apache.sis.referencing.factory.MultiAuthoritiesFactory;
@@ -117,6 +118,7 @@ final class AuthorityFactories<T extends AuthorityFactory> extends LazySet<T> {
     /**
      * Sets the EPSG factory to the given value.
      */
+    @Configuration
     static void EPSG(final GeodeticAuthorityFactory factory) {
         synchronized (EPSG) {
             EPSG[0] = factory;
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/EPSGFactoryFallbackTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/EPSGFactoryFallbackTest.java
index ca1bca5988..e045f53a60 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/EPSGFactoryFallbackTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/EPSGFactoryFallbackTest.java
@@ -35,12 +35,14 @@ import org.opengis.referencing.datum.Datum;
 import org.opengis.referencing.datum.Ellipsoid;
 import org.opengis.referencing.datum.PrimeMeridian;
 import org.apache.sis.referencing.factory.GeodeticAuthorityFactory;
+import org.apache.sis.system.Configuration;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.Utilities;
 
 // Test dependencies
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.parallel.Isolated;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.test.TestUtilities;
 import static org.apache.sis.test.Assertions.assertEqualsIgnoreMetadata;
@@ -52,6 +54,7 @@ import static org.apache.sis.test.Assertions.assertSetEquals;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@Isolated("Temporarily modifies the system-wide EPSG factory.")
 public final class EPSGFactoryFallbackTest extends TestCase {
     /**
      * Creates a new test case.
@@ -222,6 +225,7 @@ public final class EPSGFactoryFallbackTest extends TestCase {
     /**
      * Sets the EPSG factory to the given instance and clears the cache of all {@link CommonCRS} enumeration values.
      */
+    @Configuration
     private static void setEPSGFactory(final GeodeticAuthorityFactory factory) {
         AuthorityFactories.EPSG(factory);
         for (final CommonCRS          crs : CommonCRS         .values()) crs.clear();
@@ -231,6 +235,7 @@ public final class EPSGFactoryFallbackTest extends TestCase {
 
     /**
      * Compares all CRS created by {@link EPSGFactoryFallback} with CRS created by the real EPSG database.
+     * This test must be run in a class annotated with {@link Isolated}.
      *
      * @throws FactoryException if a CRS cannot be constructed.
      */
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
index 5e2dd7e319..45cbf87e62 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
@@ -63,6 +63,8 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.referencing.cs.HardCodedCS;
 import org.apache.sis.referencing.crs.HardCodedCRS;
@@ -80,6 +82,7 @@ import org.opengis.test.Assertions;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@Execution(ExecutionMode.SAME_THREAD)
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class CoordinateOperationFinderTest extends MathTransformTestCase {
     /**
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
index 3fa8e71b87..d1351677a0 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
@@ -40,8 +40,10 @@ import org.apache.sis.referencing.operation.transform.MathTransformTestCase;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
 import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
 import static org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierEqual;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
@@ -67,6 +69,7 @@ import org.opengis.metadata.Identifier;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@Execution(ExecutionMode.SAME_THREAD)
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class CoordinateOperationRegistryTest extends MathTransformTestCase {
     /**
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
index d43ed1e1fd..ed1dbf765e 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
@@ -41,6 +41,8 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
 import static org.apache.sis.test.Assertions.assertMessageContains;
 import static org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierEqual;
 
@@ -57,6 +59,7 @@ import static org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierE
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@Execution(ExecutionMode.SAME_THREAD)
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class DefaultCoordinateOperationFactoryTest extends MathTransformTestCase {
     /**
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
index 8323fdd2f6..e0d6c5e211 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
@@ -34,6 +34,8 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.apache.sis.test.FailureDetailsReporter;
 import org.apache.sis.referencing.cs.HardCodedCS;
@@ -45,6 +47,7 @@ import org.opengis.test.referencing.TransformTestCase;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@Execution(ExecutionMode.SAME_THREAD)
 @ExtendWith(FailureDetailsReporter.class)
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class CoordinateSystemTransformTest extends TransformTestCase {
diff --git a/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/TestCase.java b/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/TestCase.java
index 3b7e9af51a..856a5242b1 100644
--- a/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/TestCase.java
+++ b/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/base/TestCase.java
@@ -37,6 +37,8 @@ import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import org.opengis.test.dataset.TestData;
@@ -50,6 +52,7 @@ import org.opengis.test.dataset.TestData;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@Execution(ExecutionMode.SAME_THREAD)
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public abstract class TestCase extends org.apache.sis.test.TestCase {
     /**
diff --git a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
index 93fb274bc1..b3d94e842a 100644
--- a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
+++ b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
@@ -44,6 +44,8 @@ import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
 
@@ -66,6 +68,7 @@ import org.apache.sis.test.TestCase;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@Execution(ExecutionMode.SAME_THREAD)
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public abstract class CoverageReadConsistency<S extends DataStore> extends TestCase {
     /**
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Configuration.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Configuration.java
index 8b67f31648..6def89a975 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Configuration.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Configuration.java
@@ -25,7 +25,8 @@ import java.lang.annotation.RetentionPolicy;
 /**
  * Annotates methods having a system-wide impact on the configuration of the Apache SIS library.
  * Also used for some static final constants fixed to arbitrary values that could be customized.
- * This annotation should not be used on test classes for avoiding to pollute usage searches.
+ * When applied to test classes, this annotation helps to identify which tests cannot be executed
+ * in parallel.
  *
  * <h2>Application to static final constants</h2>
  * We do not annotate all static constants having arbitrary values because there is too many of them.
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/TreeTableFormatTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/TreeTableFormatTest.java
index 0940167d0f..040f3f63a7 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/TreeTableFormatTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/collection/TreeTableFormatTest.java
@@ -26,6 +26,8 @@ import static org.apache.sis.util.collection.TableColumn.*;
 // Test dependencies
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.parallel.Resources;
+import org.junit.jupiter.api.parallel.ResourceLock;
 import org.apache.sis.test.TestCase;
 import static org.apache.sis.test.Assertions.assertMultilinesEquals;
 
@@ -201,6 +203,7 @@ public final class TreeTableFormatTest extends TestCase {
      * Those types shall be handled in a special way.
      */
     @Test
+    @ResourceLock(Resources.LOCALE)
     public void testLocalizedFormat() {
         final Locale locale = Locale.getDefault();
         try {
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/logging/PerformanceLevelTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/logging/PerformanceLevelTest.java
index 67b4fe8ef0..42c217e23c 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/logging/PerformanceLevelTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/logging/PerformanceLevelTest.java
@@ -23,6 +23,7 @@ import static org.apache.sis.util.logging.PerformanceLevel.*;
 // Test dependencies
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.parallel.ResourceLock;
 import org.apache.sis.test.TestCase;
 
 
@@ -51,6 +52,7 @@ public final class PerformanceLevelTest extends TestCase {
      * Tests modifying the configuration.
      */
     @Test
+    @ResourceLock("Logging")
     public void testSetMinDuration() {
         final long t1 = SLOWNESS.getMinDuration(TimeUnit.SECONDS);
         final long t2 = SLOWER  .getMinDuration(TimeUnit.SECONDS);


(sis) 01/05: Logs handled by the wrong `LoggingWatcher` during execution of tests in parallel. Require Java 16 for the test (but not for the main code, which still on Java 11) for avoiding an unreliable hack in `JDK16` placeholder class.

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 9c5d68f70b565659e86fa67344bf510eb81522a6
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Feb 20 10:36:06 2024 +0100

    Logs handled by the wrong `LoggingWatcher` during execution of tests in parallel.
    Require Java 16 for the test (but not for the main code, which still on Java 11)
    for avoiding an unreliable hack in `JDK16` placeholder class.
---
 endorsed/build.gradle.kts                          |  2 ++
 .../factory/ConcurrentAuthorityFactoryTest.java    | 38 +++++++++++++-------
 .../main/org/apache/sis/pending/jdk/JDK16.java     | 29 ----------------
 .../test/org/apache/sis/test/LoggingWatcher.java   | 40 ++++++++++------------
 4 files changed, 46 insertions(+), 63 deletions(-)

diff --git a/endorsed/build.gradle.kts b/endorsed/build.gradle.kts
index d3198451b9..6a8f136011 100644
--- a/endorsed/build.gradle.kts
+++ b/endorsed/build.gradle.kts
@@ -104,6 +104,8 @@ tasks.compileTestJava {
     srcDir.list().forEach {
         addRead(options.compilerArgs, it, "org.apache.sis.test.endorsed,org.junit.jupiter.api")
     }
+    options.compilerArgs.add("-source")         // "source", not "release", because we accept any target version.
+    options.compilerArgs.add("16")              // Minimal Java version required by some API that the tests use.
     addExportForTests(options.compilerArgs)
 }
 
diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/ConcurrentAuthorityFactoryTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/ConcurrentAuthorityFactoryTest.java
index 884c82c107..b1c7e84cf1 100644
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/ConcurrentAuthorityFactoryTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/ConcurrentAuthorityFactoryTest.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
 import java.lang.reflect.Field;
 import org.opengis.util.FactoryException;
 import static org.apache.sis.util.internal.StandardDateFormat.NANOS_PER_MILLISECOND;
@@ -118,10 +119,11 @@ public final class ConcurrentAuthorityFactoryTest extends TestCase {
         assertEquals(1, factory.countAvailableDataAccess(), "Expected one valid DAO.");
         assertFalse (createdDAOs.get(0).isClosed(),         "Should not be disposed yet.");
 
-        sleepUntilAfterTimeout(3 * ConcurrentAuthorityFactory.TIMEOUT_RESOLUTION, factory);
-        assertEquals(createdDAOs, factory.createdDAOs(),    "Expected no new DAO.");
-        assertEquals(0, factory.countAvailableDataAccess(), "Worker should be disposed.");
-        assertTrue  (createdDAOs.get(0).isClosed(),         "Worker should be disposed.");
+        boolean expired;
+        expired = sleepUntilAfterTimeout(3 * ConcurrentAuthorityFactory.TIMEOUT_RESOLUTION, factory);
+        assertEquals(createdDAOs, factory.createdDAOs(),    unexpectedDAO("Expected no new DAO.",       expired));
+        assertEquals(0, factory.countAvailableDataAccess(), unexpectedDAO("Worker should be disposed.", expired));
+        assertTrue  (createdDAOs.get(0).isClosed(),         unexpectedDAO("Worker should be disposed.", expired));
         /*
          * Ask again for the same object and check that no new DAO
          * were created because the value was taken from the cache.
@@ -156,11 +158,11 @@ public final class ConcurrentAuthorityFactoryTest extends TestCase {
         assertFalse (createdDAOs.get(1).isClosed(),         "Should not be disposed yet.");
         assertEquals(createdDAOs, factory.createdDAOs(),    "Expected no new DAO.");
 
-        sleepUntilAfterTimeout(3 * ConcurrentAuthorityFactory.TIMEOUT_RESOLUTION, factory);
-        assertEquals(createdDAOs, factory.createdDAOs(),    "Expected no new DAO.");
-        assertEquals(0, factory.countAvailableDataAccess(), "Worker should be disposed.");
-        assertTrue  (createdDAOs.get(1).isClosed(),         "Worker should be disposed.");
-        assertTrue  (createdDAOs.get(0).isClosed(),         "Worker should be disposed.");
+        expired = sleepUntilAfterTimeout(3 * ConcurrentAuthorityFactory.TIMEOUT_RESOLUTION, factory);
+        assertEquals(createdDAOs, factory.createdDAOs(),    unexpectedDAO("Expected no new DAO.",       expired));
+        assertEquals(0, factory.countAvailableDataAccess(), unexpectedDAO("Worker should be disposed.", expired));
+        assertTrue  (createdDAOs.get(1).isClosed(),         unexpectedDAO("Worker should be disposed.", expired));
+        assertTrue  (createdDAOs.get(0).isClosed(),         unexpectedDAO("Worker should be disposed.", expired));
     }
 
     /**
@@ -182,19 +184,31 @@ public final class ConcurrentAuthorityFactoryTest extends TestCase {
      * it is subject to the hazard of thread scheduling.</p>
      *
      * @param  waitTime  the time to wait, in nanoseconds.
+     * @return whether there is more pending factories to dispose.
      */
-    private static void sleepUntilAfterTimeout(final long waitTime, final ConcurrentAuthorityFactory<?> factory)
+    private static boolean sleepUntilAfterTimeout(final long waitTime, final ConcurrentAuthorityFactory<?> factory)
             throws InterruptedException
     {
         Thread.sleep(TimeUnit.NANOSECONDS.toMillis(waitTime));
         int n = 3;
         while (factory.isCleanScheduled()) {
-            ConcurrentAuthorityFactory.LOGGER.warning("Execution of ConcurrentAuthorityFactory.disposeExpired() has been delayed.");
             Thread.sleep(TIMEOUT / NANOS_PER_MILLISECOND);
             System.gc();
             if (--n == 0) {
-                break;
+                return true;
             }
         }
+        return false;
+    }
+
+    /**
+     * Returns the supplier of the error message to report if a timeout-dependent test fails.
+     *
+     * @param  message  the message.
+     * @param  expired  the result of the call to {@link #sleepUntilAfterTimeout(long, ConcurrentAuthorityFactory)}.
+     * @return the supplied to give to a JUnit {@code assert} method.
+     */
+    private static Supplier<String> unexpectedDAO(final String message, final boolean expired) {
+        return () -> expired ? message + " Note: the execution of ConcurrentAuthorityFactory.disposeExpired() has been delayed." : message;
     }
 }
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK16.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK16.java
index a756e706c7..53270b08ea 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK16.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK16.java
@@ -18,7 +18,6 @@ package org.apache.sis.pending.jdk;
 
 import java.util.List;
 import java.util.stream.Stream;
-import java.util.logging.LogRecord;
 import org.apache.sis.util.internal.UnmodifiableArrayList;
 
 
@@ -45,32 +44,4 @@ public final class JDK16 {
     public static <T> List<T> toList(final Stream<T> s) {
         return (List<T>) UnmodifiableArrayList.wrap(s.toArray());
     }
-
-    /**
-     * Place holder for {@code LogRecord.getLongThreadID()}.
-     *
-     * @param  record  the record for which to get the thread identifier.
-     * @return thread identifier of the given record.
-     */
-    @SuppressWarnings("deprecation")
-    public static long getLongThreadID(final LogRecord record) {
-        return record.getThreadID();
-    }
-
-    /**
-     * Returns {@code true} if the thread identifier of the given record is probably equal to the given identifier.
-     * This method reproduces the hashing algorithm used in {@code LogRecord.shortThreadID(long)} private method.
-     *
-     * @param  record    the record for which to compare the thread identifier.
-     * @param  threadId  the thread identifier to compare.
-     * @return whether the thread identifier of the given record is probably equal to the given value.
-     */
-    @SuppressWarnings("deprecation")
-    public static boolean isSameThread(final LogRecord record, final long threadId) {
-        int hash = Long.hashCode(threadId);
-        if (threadId < 0 || threadId > Integer.MAX_VALUE) {
-            if (hash >= 0) hash = ~hash;        // Makes it negative.
-        }
-        return record.getThreadID() == hash;
-    }
 }
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/LoggingWatcher.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/LoggingWatcher.java
index 5c3697cf6e..f4047385b7 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/LoggingWatcher.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/LoggingWatcher.java
@@ -25,7 +25,6 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.logging.LogRecord;
 import java.util.logging.SimpleFormatter;
-import org.apache.sis.pending.jdk.JDK16;
 
 // Test dependencies
 import static org.junit.jupiter.api.Assertions.*;
@@ -129,7 +128,7 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba
      * All filters registered on the logger. This is a singleton containing only {@code this},
      * unless many tests are running in parallel with their own {@code LoggingWatcher}.
      */
-    private Queue<Filter> allFilters;
+    private Queue<LoggingWatcher> allFilters;
 
     /**
      * Creates a new watcher for the given logger.
@@ -183,14 +182,13 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba
         synchronized (logger) {
             assertNull(allFilters);
             final Filter current = logger.getFilter();
-            if (current instanceof LoggingWatcher) {
-                allFilters = ((LoggingWatcher) current).allFilters;
+            if (current != null) {
+                var w = assertInstanceOf(LoggingWatcher.class, current, () -> "The \"" + logger.getName()
+                                + "\" logger has a " + current.getClass().getCanonicalName() + " filter.");
+                allFilters = w.allFilters;
                 assertNotNull(allFilters);
             } else {
                 allFilters = new LinkedList<>();
-                if (current != null) {
-                    allFilters.add(current);
-                }
                 logger.setFilter(this);
             }
             allFilters.add(this);
@@ -221,26 +219,24 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba
      */
     @Override
     public final boolean isLoggable(final LogRecord record) {
-        /*
-         * In the simple mono-thread case, everything use fields of `this`.
-         * However, if many tests are running in parallel, we need to check
-         * which `LoggingWatcher` should take the given log record.
-         */
-        LoggingWatcher owner = this;
-        synchronized (logger) {
-            for (final Filter filter : allFilters) {
-                if (filter instanceof LoggingWatcher) {
-                    final var w = (LoggingWatcher) filter;
-                    if (w.isMultiThread || JDK16.isSameThread(record, w.threadId)) {
+        if (record.getLevel().intValue() >= Level.INFO.intValue()) {
+            /*
+             * In the simple mono-thread case, everything use fields of `this`.
+             * However, if many tests are running in parallel, we need to check
+             * which `LoggingWatcher` should take the given log record.
+             */
+            LoggingWatcher owner = null;
+            synchronized (logger) {
+                for (final LoggingWatcher w : allFilters) {
+                    if (w.isMultiThread || w.threadId == record.getLongThreadID()) {
                         owner = w;
                         break;
                     }
-                } else if (!filter.isLoggable(record)) {
-                    return false;
                 }
             }
-        }
-        if (record.getLevel().intValue() >= Level.INFO.intValue()) {
+            if (owner == null) {
+                return true;
+            }
             synchronized (owner) {
                 owner.messages.add(owner.formatter.formatMessage(record));
             }