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 cfb4f1ff06fb7e7f82e0d30c78adcc036b0541ae Author: Martin Desruisseaux <[email protected]> AuthorDate: Mon Feb 19 11:20:08 2024 +0100 Make `LoggingWatcher` partially thread-safe, and add synchronization lock in the classes that use it. Tests that do not start other threads can be run in parallel, because we can use the thread identifier for separating the log records. Tests that start worker threads need to be alone. --- .../apache/sis/filter/sqlmm/RegistryTestCase.java | 57 ++++++--- .../apache/sis/image/StatisticsCalculatorTest.java | 18 ++- .../apache/sis/metadata/iso/AllMetadataTest.java | 10 -- .../iso/identification/DefaultResolutionTest.java | 25 +--- .../maintenance/DefaultScopeDescriptionTest.java | 24 +--- .../iso/spatial/DefaultGeorectifiedTest.java | 24 +--- .../sis/xml/test/AnnotationConsistencyCheck.java | 11 +- .../test/org/apache/sis/xml/test/TestCase.java | 35 ++++++ .../sis/parameter/ParameterMarshallingTest.java | 33 +++-- .../sis/referencing/AuthorityFactoriesTest.java | 30 ++--- .../test/org/apache/sis/referencing/CRSTest.java | 43 ++++--- .../referencing/crs/DefaultProjectedCRSTest.java | 47 ++++---- .../factory/MultiAuthoritiesFactoryTest.java | 32 ++--- .../referencing/factory/sql/EPSGFactoryTest.java | 46 ++++--- .../referencing/factory/sql/EPSGInstallerTest.java | 28 +---- .../operation/SingleOperationMarshallingTest.java | 24 +--- .../apache/sis/test/integration/MetadataTest.java | 23 +--- .../sis/test/integration/MetadataVerticalTest.java | 22 +--- .../CC_GeneralOperationParameterTest.java | 32 ++--- .../main/org/apache/sis/pending/jdk/JDK16.java | 29 +++++ .../test/org/apache/sis/test/LoggingWatcher.java | 134 +++++++++++++++++++-- .../test/org/apache/sis/test/TestCaseWithLogs.java | 76 ++++++++++++ 22 files changed, 462 insertions(+), 341 deletions(-) diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/sqlmm/RegistryTestCase.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/sqlmm/RegistryTestCase.java index 221792746d..22bfa033ee 100644 --- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/sqlmm/RegistryTestCase.java +++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/sqlmm/RegistryTestCase.java @@ -34,13 +34,10 @@ import org.apache.sis.math.Vector; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; import static org.apache.sis.test.Assertions.assertMessageContains; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; -import org.apache.sis.test.TestCase; import org.apache.sis.referencing.crs.HardCodedCRS; +import org.apache.sis.test.TestCaseWithLogs; // Specific to the geoapi-3.1 and geoapi-4.0 branches: import org.opengis.feature.Feature; @@ -59,7 +56,7 @@ import org.opengis.filter.capability.AvailableFunction; * * @param <G> root class of geometry implementation. */ -public abstract class RegistryTestCase<G> extends TestCase { +public abstract class RegistryTestCase<G> extends TestCaseWithLogs { /** * Name of property value used in test feature instances. */ @@ -100,14 +97,6 @@ public abstract class RegistryTestCase<G> extends TestCase { */ private double tolerance; - /** - * For verification of log records emitted during filtering operations. - * - * @see #assertNoUnexpectedLog() - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Node.LOGGER); - /** * Creates a new test case. * @@ -116,6 +105,7 @@ public abstract class RegistryTestCase<G> extends TestCase { */ @SuppressWarnings("unchecked") protected RegistryTestCase(final Class<G> rootGeometry, final boolean supportCRS) { + super(Node.LOGGER); factory = new DefaultFilterFactory.Features<>(rootGeometry, Object.class, WraparoundMethod.SPLIT); library = (Geometries<G>) Geometries.factory(rootGeometry); assertEquals(rootGeometry, library.rootClass); @@ -127,7 +117,7 @@ public abstract class RegistryTestCase<G> extends TestCase { */ @Test public void testDescribe() { - final Registry r = new Registry(library); + final var r = new Registry(library); AvailableFunction desc = r.describe("ST_Transform"); assertEquals("SQLMM:ST_Transform", desc.getName().toFullyQualifiedName().toString()); assertEquals("OGC:Geometry", desc.getReturnType().toFullyQualifiedName().toString()); @@ -137,6 +127,9 @@ public abstract class RegistryTestCase<G> extends TestCase { assertEquals("ST_PointFromText", desc.getName().toString()); assertEquals("OGC:Point", desc.getReturnType().toFullyQualifiedName().toString()); assertEquals(library.pointClass, desc.getReturnType().toJavaType().get()); + + // Tested methods should not log. + loggings.assertNoUnexpectedLog(); } /** @@ -291,6 +284,9 @@ public abstract class RegistryTestCase<G> extends TestCase { function = factory.function("ST_Transform", reference, factory.literal("EPSG:4326")); assertPointEquals(function.apply(instance), CommonCRS.WGS84.geographic(), 30, 10); + + // Tested methods should not log. + loggings.assertNoUnexpectedLog(); } /** @@ -312,6 +308,9 @@ public abstract class RegistryTestCase<G> extends TestCase { tolerance = 1E-12; Object result = evaluate("ST_Buffer", geometry, 1); assertEnvelopeEquals(result, true, HardCodedCRS.WGS84_LATITUDE_FIRST, 9, 19, 11, 21); + + // Tested methods should not log. + loggings.assertNoUnexpectedLog(); } /** @@ -331,6 +330,9 @@ public abstract class RegistryTestCase<G> extends TestCase { */ final Object result = evaluate("ST_Centroid", geometry); assertPointEquals(result, HardCodedCRS.WGS84_LATITUDE_FIRST, 20, 20); + + // Tested methods should not log. + loggings.assertNoUnexpectedLog(); } /** @@ -348,6 +350,9 @@ public abstract class RegistryTestCase<G> extends TestCase { function = factory.function("ST_Envelope", factory.property(P_NAME, library.polylineClass)); result = function.apply(instance); assertEnvelopeEquals(result, false, null, 12, 3.3, 13.1, 5.7); + + // Tested methods should not log. + loggings.assertNoUnexpectedLog(); } /** @@ -371,6 +376,9 @@ public abstract class RegistryTestCase<G> extends TestCase { // Ensure switching argument order does not modify behavior. function = factory.function("ST_Intersects", factory.literal(point), reference); assertEquals(Boolean.TRUE, function.apply(instance)); + + // Tested methods should not log. + loggings.assertNoUnexpectedLog(); } /** @@ -406,6 +414,9 @@ public abstract class RegistryTestCase<G> extends TestCase { // Test with a missing CRS information. setGeometryCRS(null); assertEquals(Boolean.TRUE, evaluate("ST_Intersects", geometry, point)); + + // Tested methods should not log. + loggings.assertNoUnexpectedLog(); } /** @@ -420,6 +431,9 @@ public abstract class RegistryTestCase<G> extends TestCase { result = evaluate("ST_Point", 10, 20, "CRS:84"); assertPointEquals(result, CommonCRS.defaultGeographic(), 10, 20); + + // Tested methods should not log. + loggings.assertNoUnexpectedLog(); } /** @@ -432,6 +446,7 @@ public abstract class RegistryTestCase<G> extends TestCase { List.of(library.createPoint(10, 20), library.createPoint(30, 40)), HardCodedCRS.WGS84); assertPolylineEquals(result, HardCodedCRS.WGS84, 10, 20, 30, 40); + loggings.assertNoUnexpectedLog(); } /** @@ -451,6 +466,9 @@ public abstract class RegistryTestCase<G> extends TestCase { */ final Object result = evaluate("ST_Simplify", geometry, 10); assertPolylineEquals(result, HardCodedCRS.WGS84_LATITUDE_FIRST, 10, 20, 20, 20); + + // Tested methods should not log. + loggings.assertNoUnexpectedLog(); } /** @@ -463,6 +481,7 @@ public abstract class RegistryTestCase<G> extends TestCase { setGeometryCRS(HardCodedCRS.WGS84_LATITUDE_FIRST); final Object result = evaluate("ST_SimplifyPreserveTopology", geometry, 10); assertPolylineEquals(result, HardCodedCRS.WGS84_LATITUDE_FIRST, 10, 20, 20, 20); + loggings.assertNoUnexpectedLog(); } /** @@ -484,6 +503,9 @@ public abstract class RegistryTestCase<G> extends TestCase { assertNotSame(function, optimized, "Optimization should produce a new expression."); var literal = assertInstanceOf(Literal.class, optimized, "Expected immediate expression evaluation."); assertPointEquals(literal.getValue(), HardCodedCRS.WGS84_LATITUDE_FIRST, 30, 10); + + // Tested methods should not log. + loggings.assertNoUnexpectedLog(); } /** @@ -508,13 +530,8 @@ public abstract class RegistryTestCase<G> extends TestCase { final Object literal = optimized.getParameters().get(1).apply(null); final DirectPosition point = library.castOrWrap(literal).getCentroid(); assertArrayEquals(new double[] {30, 10}, point.getCoordinate()); - } - /** - * Executed after each test for verifying that no unexpected log message has been emitted. - */ - @AfterEach - public void assertNoUnexpectedLog() { + // Tested methods should not log. loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/StatisticsCalculatorTest.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/StatisticsCalculatorTest.java index 8467762a1a..a4b7fe12dc 100644 --- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/StatisticsCalculatorTest.java +++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/StatisticsCalculatorTest.java @@ -23,7 +23,6 @@ import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.ImagingOpException; import java.util.function.DoubleUnaryOperator; -import static java.util.logging.Logger.getLogger; import org.apache.sis.system.Modules; import org.apache.sis.math.Statistics; @@ -31,9 +30,7 @@ import org.apache.sis.math.Statistics; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; import static org.apache.sis.test.Assertions.assertMessageContains; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; -import org.apache.sis.test.TestCase; +import org.apache.sis.test.TestCaseWithLogs; /** @@ -42,7 +39,7 @@ import org.apache.sis.test.TestCase; * * @author Martin Desruisseaux (Geomatys) */ -public final class StatisticsCalculatorTest extends TestCase { +public final class StatisticsCalculatorTest extends TestCaseWithLogs.Isolated { /** * Size of the artificial tiles. Should be small enough so we can have many of them. * Width and height should be different in order to increase the chance to see bugs @@ -50,12 +47,6 @@ public final class StatisticsCalculatorTest extends TestCase { */ private static final int TILE_WIDTH = 5, TILE_HEIGHT = 3; - /** - * Intercepts log records for verifying them. - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(getLogger(Modules.RASTER)); - /** * The area of interest, or {@code null} if none. */ @@ -70,6 +61,7 @@ public final class StatisticsCalculatorTest extends TestCase { * Creates a new test case. */ public StatisticsCalculatorTest() { + super(Modules.RASTER); } /** @@ -139,6 +131,7 @@ public final class StatisticsCalculatorTest extends TestCase { @Test public void testParallelExecution() { compareParallelWithSequential(new ImageProcessor(), 100, 51324); + loggings.assertNoUnexpectedLog(); } /** @@ -149,6 +142,7 @@ public final class StatisticsCalculatorTest extends TestCase { public void testWithAOI() { areaOfInterest = new Ellipse2D.Float(70, -50, TILE_WIDTH*11.6f, TILE_HEIGHT*9.2f); compareParallelWithSequential(new ImageProcessor(), 19723, 44501); + loggings.assertNoUnexpectedLog(); } /** @@ -162,6 +156,7 @@ public final class StatisticsCalculatorTest extends TestCase { ImageProcessor.filterNodataValues(100, 51324, 51323, 201, 310) }; compareParallelWithSequential(operations, 101, 51322); + loggings.assertNoUnexpectedLog(); } /** @@ -219,5 +214,6 @@ public final class StatisticsCalculatorTest extends TestCase { assertTrue(Double.isNaN(op.applyAsDouble(100))); assertTrue(Double.isNaN(op.applyAsDouble(310))); assertTrue(Double.isNaN(op.applyAsDouble(201))); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/AllMetadataTest.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/AllMetadataTest.java index 5ae9444ee4..4bc46763dc 100644 --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/AllMetadataTest.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/AllMetadataTest.java @@ -18,14 +18,11 @@ package org.apache.sis.metadata.iso; import java.lang.reflect.Modifier; import org.opengis.annotation.UML; -import org.apache.sis.xml.bind.Context; import org.apache.sis.metadata.MetadataStandard; import org.apache.sis.metadata.PropertyConsistencyCheck; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; // Specific to the geoapi-3.1 and geoapi-4.0 branches: import org.opengis.annotation.Stereotype; @@ -38,13 +35,6 @@ import org.opengis.util.ControlledVocabulary; * @author Martin Desruisseaux (Geomatys) */ public final class AllMetadataTest extends PropertyConsistencyCheck { - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Context.LOGGER); - /** * Creates a new test case with all GeoAPI interfaces and code lists to test. */ diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/identification/DefaultResolutionTest.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/identification/DefaultResolutionTest.java index 8df2fe071d..d945f61806 100644 --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/identification/DefaultResolutionTest.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/identification/DefaultResolutionTest.java @@ -25,10 +25,7 @@ import static org.apache.sis.xml.bind.gml.MeasureTest.UOM_URL; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; import org.apache.sis.xml.test.TestCase; import static org.apache.sis.metadata.Assertions.assertXmlEquals; @@ -39,26 +36,12 @@ import static org.apache.sis.metadata.Assertions.assertXmlEquals; * @author Martin Desruisseaux (Geomatys) * @author Cullen Rombach (Image Matters) */ -public final class DefaultResolutionTest extends TestCase { - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Context.LOGGER); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - +public final class DefaultResolutionTest extends TestCase.WithLogs { /** * Creates a new test case. */ public DefaultResolutionTest() { + super(Context.LOGGER); } /** @@ -72,6 +55,7 @@ public final class DefaultResolutionTest extends TestCase { scale.setDenominator(100); final var metadata = new DefaultResolution(scale); assertSame(scale, metadata.getEquivalentScale()); + loggings.assertNoUnexpectedLog(); } /** @@ -102,6 +86,7 @@ public final class DefaultResolutionTest extends TestCase { metadata.setEquivalentScale(null); assertNull(metadata.getEquivalentScale()); assertNull(metadata.getDistance()); + loggings.assertNoUnexpectedLog(); } /** @@ -132,6 +117,7 @@ public final class DefaultResolutionTest extends TestCase { "</mri:MD_Resolution>", xml, "xmlns:*"); assertEquals(resolution, unmarshal(DefaultResolution.class, xml)); + loggings.assertNoUnexpectedLog(); } /** @@ -163,5 +149,6 @@ public final class DefaultResolutionTest extends TestCase { "</gmd:MD_Resolution>", xml, "xmlns:*"); assertEquals(resolution, unmarshal(DefaultResolution.class, xml)); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescriptionTest.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescriptionTest.java index 041a5aac51..9d80cb9105 100644 --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescriptionTest.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescriptionTest.java @@ -20,11 +20,8 @@ import org.apache.sis.xml.bind.Context; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; -import org.apache.sis.test.TestCase; +import org.apache.sis.test.TestCaseWithLogs; // Specific to the geoapi-4.0 branch: import org.apache.sis.util.SimpleInternationalString; @@ -35,26 +32,12 @@ import org.apache.sis.util.SimpleInternationalString; * * @author Martin Desruisseaux (Geomatys) */ -public final class DefaultScopeDescriptionTest extends TestCase { - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Context.LOGGER); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - +public final class DefaultScopeDescriptionTest extends TestCaseWithLogs { /** * Creates a new test case. */ public DefaultScopeDescriptionTest() { + super(Context.LOGGER); } /** @@ -81,5 +64,6 @@ public final class DefaultScopeDescriptionTest extends TestCase { metadata.setOther(null); assertNull(metadata.getOther()); assertNull(metadata.getDataset()); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/spatial/DefaultGeorectifiedTest.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/spatial/DefaultGeorectifiedTest.java index 9ef47f8100..b217346208 100644 --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/spatial/DefaultGeorectifiedTest.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/spatial/DefaultGeorectifiedTest.java @@ -22,11 +22,8 @@ import org.apache.sis.xml.bind.Context; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; -import org.apache.sis.test.TestCase; +import org.apache.sis.test.TestCaseWithLogs; /** @@ -34,26 +31,12 @@ import org.apache.sis.test.TestCase; * * @author Martin Desruisseaux (Geomatys) */ -public final class DefaultGeorectifiedTest extends TestCase { - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Context.LOGGER); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - +public final class DefaultGeorectifiedTest extends TestCaseWithLogs { /** * Creates a new test case. */ public DefaultGeorectifiedTest() { + super(Context.LOGGER); } /** @@ -80,5 +63,6 @@ public final class DefaultGeorectifiedTest extends TestCase { // Setting the availability flag shall bring back the description. metadata.setCheckPointAvailable(true); assertSame(description, metadata.getCheckPointDescription()); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/AnnotationConsistencyCheck.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/AnnotationConsistencyCheck.java index 6ed389a52f..3d1fc5d222 100644 --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/AnnotationConsistencyCheck.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/test/AnnotationConsistencyCheck.java @@ -38,12 +38,13 @@ import org.apache.sis.util.ArraysExt; import org.apache.sis.xml.Namespaces; import org.apache.sis.xml.util.LegacyNamespaces; import org.apache.sis.xml.bind.cat.CodeListUID; +import org.apache.sis.xml.bind.Context; // Test dependencies import org.junit.jupiter.api.Test; import org.opentest4j.AssertionFailedError; import org.apache.sis.test.TestUtilities; -import org.apache.sis.test.TestCase; +import org.apache.sis.test.TestCaseWithLogs; // Specific to the geoapi-3.1 and geoapi-4.0 branches: import org.opengis.annotation.Classifier; @@ -76,7 +77,7 @@ import org.opengis.util.ControlledVocabulary; * @author Cédric Briançon (Geomatys) * @author Martin Desruisseaux (Geomatys) */ -public abstract class AnnotationConsistencyCheck extends TestCase { +public abstract class AnnotationConsistencyCheck extends TestCaseWithLogs { /** * The {@value} string used in JAXB annotations for default names or namespaces. */ @@ -114,6 +115,7 @@ public abstract class AnnotationConsistencyCheck extends TestCase { * @param types the GeoAPI interfaces, {@link CodeList} or {@link Enum} types to test. */ protected AnnotationConsistencyCheck(final Class<?>... types) { + super(Context.LOGGER); this.types = types; // No need to clone — test classes are normally used only by SIS. } @@ -642,6 +644,7 @@ public abstract class AnnotationConsistencyCheck extends TestCase { } } } + loggings.assertNoUnexpectedLog(); } /** @@ -675,6 +678,7 @@ public abstract class AnnotationConsistencyCheck extends TestCase { assertEquals(Namespaces.getPreferredPrefix(namespace, null), ns.prefix(), "Unexpected namespace prefix."); } } + loggings.assertNoUnexpectedLog(); } /** @@ -744,6 +748,7 @@ public abstract class AnnotationConsistencyCheck extends TestCase { } assertEquals(expected, xmlType.name(), "Wrong @XmlType.name()."); } + loggings.assertNoUnexpectedLog(); } /** @@ -844,6 +849,7 @@ public abstract class AnnotationConsistencyCheck extends TestCase { assertExpectedNamespace(element.namespace(), impl, uml); } } + loggings.assertNoUnexpectedLog(); } /** @@ -933,6 +939,7 @@ public abstract class AnnotationConsistencyCheck extends TestCase { } } } + loggings.assertNoUnexpectedLog(); } /** 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 22686194b9..c9b35ee213 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 @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.Locale; import java.util.TimeZone; import java.util.Date; +import java.util.logging.Logger; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.text.ParseException; @@ -43,6 +44,10 @@ import org.apache.sis.util.Version; 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.ResourceAccessMode; +import org.junit.jupiter.api.parallel.ResourceLock; +import org.apache.sis.test.LoggingWatcher; /** @@ -63,6 +68,36 @@ import static org.apache.sis.metadata.Assertions.assertXmlEquals; * @see DocumentComparator */ public abstract class TestCase extends org.apache.sis.test.TestCase { + /** + * Base class of (un)marshalling tests that may emit logs. + */ + @ResourceLock(value=LoggingWatcher.LOCK, mode=ResourceAccessMode.READ) + public static abstract class WithLogs extends TestCase { + /** + * A JUnit extension for listening to log events. + */ + @RegisterExtension + public final LoggingWatcher loggings; + + /** + * Creates a new test case which will listen to logs emitted by the given logger. + * + * @param logger the logger to listen to. + */ + protected WithLogs(final Logger logger) { + loggings = new LoggingWatcher(logger); + } + + /** + * Creates a new test case which will listen to logs emitted by the logger of the given name. + * + * @param logger name of the logger to listen. + */ + protected WithLogs(final String logger) { + loggings = new LoggingWatcher(logger); + } + } + /** * A dummy URL to not try to load. This URL is handled in a special way by the unmarshallers created * by {@link #getMarshallerPool()}: they will not try to download the document at this address. diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterMarshallingTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterMarshallingTest.java index e48d93a2a7..2a67072385 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterMarshallingTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterMarshallingTest.java @@ -38,11 +38,8 @@ import org.apache.sis.xml.XML; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; import org.opengis.test.Validators; -import org.apache.sis.test.LoggingWatcher; import org.apache.sis.xml.test.TestCase; import static org.apache.sis.metadata.Assertions.assertXmlEquals; import static org.apache.sis.referencing.Assertions.assertAliasTipEquals; @@ -54,26 +51,12 @@ import static org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierE * * @author Martin Desruisseaux (Geomatys) */ -public final class ParameterMarshallingTest extends TestCase { - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.XML); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - +public final class ParameterMarshallingTest extends TestCase.WithLogs { /** * Creates a new test case. */ public ParameterMarshallingTest() { + super(Loggers.XML); } /** @@ -178,6 +161,7 @@ public final class ParameterMarshallingTest extends TestCase { */ assertNull(r.getValueDomain(), "valueDomain"); assertNull(r.getValueClass(), "valueClass"); // May change in any future SIS release. + loggings.assertNoUnexpectedLog(); } /** @@ -198,6 +182,7 @@ public final class ParameterMarshallingTest extends TestCase { + " </gml:OperationParameter>" + " </gml:operationParameter>" + "</gml:ParameterValue>"); + loggings.assertNoUnexpectedLog(); } /** @@ -219,6 +204,7 @@ public final class ParameterMarshallingTest extends TestCase { + " </gml:OperationParameter>" + " </gml:operationParameter>" + "</gml:ParameterValue>"); + loggings.assertNoUnexpectedLog(); } /** @@ -239,6 +225,7 @@ public final class ParameterMarshallingTest extends TestCase { + " </gml:OperationParameter>" + " </gml:operationParameter>" + "</gml:ParameterValue>"); + loggings.assertNoUnexpectedLog(); } /** @@ -259,6 +246,7 @@ public final class ParameterMarshallingTest extends TestCase { + " </gml:OperationParameter>" + " </gml:operationParameter>" + "</gml:ParameterValue>"); + loggings.assertNoUnexpectedLog(); } /** @@ -279,6 +267,7 @@ public final class ParameterMarshallingTest extends TestCase { + " </gml:OperationParameter>" + " </gml:operationParameter>" + "</gml:ParameterValue>"); + loggings.assertNoUnexpectedLog(); } /** @@ -300,6 +289,7 @@ public final class ParameterMarshallingTest extends TestCase { + " </gml:OperationParameter>" + " </gml:operationParameter>" + "</gml:ParameterValue>"); + loggings.assertNoUnexpectedLog(); } /** @@ -321,6 +311,7 @@ public final class ParameterMarshallingTest extends TestCase { + " </gml:OperationParameter>" + " </gml:operationParameter>" + "</gml:ParameterValue>"); + loggings.assertNoUnexpectedLog(); } /** @@ -336,6 +327,7 @@ public final class ParameterMarshallingTest extends TestCase { // Test unmarshalling. verifyDescriptorGroup(unmarshalFile(DefaultParameterDescriptorGroup.class, TestFile.DESCRIPTOR.openTestFile())); + loggings.assertNoUnexpectedLog(); } /** @@ -411,6 +403,7 @@ public final class ParameterMarshallingTest extends TestCase { assertMarshalEqualsFile(TestFile.VALUE.openTestFile(), ParameterFormatTest.createMercatorParameters().createValue(), "xmlns:*", "xsi:schemaLocation"); + loggings.assertNoUnexpectedLog(); } /** @@ -422,6 +415,7 @@ public final class ParameterMarshallingTest extends TestCase { @Test public void testValueGroupUnmarshalling() throws JAXBException { testValueGroupUnmarshalling(TestFile.VALUE); + loggings.assertNoUnexpectedLog(); } /** @@ -437,6 +431,7 @@ public final class ParameterMarshallingTest extends TestCase { loggings.assertNextLogContains("EPSG::8801"); loggings.assertNextLogContains("EPSG::8802"); loggings.assertNextLogContains("EPSG::8805"); + loggings.assertNoUnexpectedLog(); } /** diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AuthorityFactoriesTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AuthorityFactoriesTest.java index 9a11b2b2d5..41285b2e78 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AuthorityFactoriesTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/AuthorityFactoriesTest.java @@ -33,11 +33,8 @@ import org.apache.sis.referencing.factory.IdentifiedObjectFinder; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.TestCase; -import org.apache.sis.test.LoggingWatcher; +import org.apache.sis.test.TestCaseWithLogs; import org.apache.sis.referencing.crs.HardCodedCRS; import static org.apache.sis.test.Assertions.assertMessageContains; import static org.apache.sis.test.Assertions.assertNotDeepEquals; @@ -48,26 +45,12 @@ import static org.apache.sis.test.Assertions.assertNotDeepEquals; * * @author Martin Desruisseaux (Geomatys) */ -public final class AuthorityFactoriesTest extends TestCase { - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.CRS_FACTORY); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - +public final class AuthorityFactoriesTest extends TestCaseWithLogs { /** * Creates a new test case. */ public AuthorityFactoriesTest() { + super(Loggers.CRS_FACTORY); } /** @@ -90,6 +73,7 @@ public final class AuthorityFactoriesTest extends TestCase { } assertTrue(foundCommon, "Factory not found."); assertTrue(foundProxy, "Factory not found."); + loggings.assertNoUnexpectedLog(); } /** @@ -102,6 +86,7 @@ public final class AuthorityFactoriesTest extends TestCase { final CRSAuthorityFactory factory = AuthorityFactories.ALL; assertEquals("WGS 84", factory.getDescriptionText("EPSG:4326").toString()); assertEquals("WGS 84", factory.getDescriptionText("urn:ogc:def:crs:epsg::4326").toString()); + loggings.assertNoUnexpectedLog(); } /** @@ -127,6 +112,7 @@ public final class AuthorityFactoriesTest extends TestCase { assertSame(crs, CRS.forCode("OGC:CRS84")); assertNotDeepEquals(crs, CRS.forCode("CRS:83")); + loggings.assertNoUnexpectedLog(); } /** @@ -194,6 +180,7 @@ public final class AuthorityFactoriesTest extends TestCase { () -> factory.createCoordinateReferenceSystem("FOO:84"), "Should not work with unknown authority."); assertEquals("FOO", exception.getAuthority()); + loggings.assertNoUnexpectedLog(); } /** @@ -227,6 +214,7 @@ public final class AuthorityFactoriesTest extends TestCase { () -> factory.createCoordinateReferenceSystem("http://www.opengis.net/gml/dummy/CRS#84"), "Should not accept “dummy” as an authority"); assertMessageContains(exception); + loggings.assertNoUnexpectedLog(); } /** @@ -241,6 +229,7 @@ public final class AuthorityFactoriesTest extends TestCase { assertFalse(codes.isEmpty()); assertTrue(codes.contains("CRS:84")); assertTrue(codes.contains("AUTO:42001") || codes.contains("AUTO2:42001")); + loggings.assertNoUnexpectedLog(); } /** @@ -256,5 +245,6 @@ public final class AuthorityFactoriesTest extends TestCase { assertNotNull(find, "With scan allowed, should find the CRS."); assertTrue(HardCodedCRS.WGS84.equals(find, ComparisonMode.DEBUG)); assertSame(factory.createCoordinateReferenceSystem("CRS:84"), find); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CRSTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CRSTest.java index dbc246e0d9..65f91dabba 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CRSTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CRSTest.java @@ -39,11 +39,8 @@ import org.apache.sis.util.Utilities; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; -import org.apache.sis.test.TestCase; +import org.apache.sis.test.TestCaseWithLogs; import org.apache.sis.referencing.cs.HardCodedCS; import org.apache.sis.referencing.crs.HardCodedCRS; import org.apache.sis.referencing.operation.HardCodedConversions; @@ -57,26 +54,12 @@ import static org.apache.sis.test.Assertions.assertMessageContains; * @author Martin Desruisseaux (Geomatys) * @author Alexis Manin (Geomatys) */ -public final class CRSTest extends TestCase { - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.CRS_FACTORY); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - +public final class CRSTest extends TestCaseWithLogs { /** * Creates a new test case. */ public CRSTest() { + super(Loggers.CRS_FACTORY); } /** @@ -123,6 +106,7 @@ public final class CRSTest extends TestCase { verifyForCode(CommonCRS.Vertical.DEPTH.crs(), "EPSG:5715"); loggings.skipNextLogIfContains("EPSG:4047"); // No longer supported by EPSG. + loggings.assertNoUnexpectedLog(); } /** @@ -139,6 +123,7 @@ public final class CRSTest extends TestCase { verifyForCode(CommonCRS.NAD27.normalizedGeographic(), "CRS:27"); verifyForCode(CommonCRS.WGS84.normalizedGeographic(), "http://www.opengis.net/gml/srs/crs.xml#84"); verifyForCode(CommonCRS.NAD83.normalizedGeographic(), "http://www.opengis.net/gml/srs/crs.xml#83"); + loggings.assertNoUnexpectedLog(); } /** @@ -155,6 +140,7 @@ public final class CRSTest extends TestCase { verifyForCode(CommonCRS.Temporal.JULIAN.crs(), "urn:ogc:def:crs:OGC::JulianDate"); verifyForCode(CommonCRS.Temporal.TRUNCATED_JULIAN.crs(), "http://www.opengis.net/gml/srs/crs.xml#TruncatedJulianDate"); + loggings.assertNoUnexpectedLog(); } /** @@ -166,6 +152,7 @@ public final class CRSTest extends TestCase { public void testForInvalidCode() throws FactoryException { var e = assertThrows(NoSuchAuthorityCodeException.class, () -> CRS.forCode("EPSG:4")); assertEquals("4", e.getAuthorityCode()); + loggings.assertNoUnexpectedLog(); } /** @@ -196,6 +183,7 @@ public final class CRSTest extends TestCase { "1=http://www.opengis.net/def/crs/epsg/0/4326&" + "2=http://www.opengis.net/def/crs/epsg/0/5715", CommonCRS.WGS84.geographic(), CommonCRS.Vertical.DEPTH.crs()); + loggings.assertNoUnexpectedLog(); } /** @@ -213,6 +201,7 @@ public final class CRSTest extends TestCase { + "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]]"); assertInstanceOf(DefaultGeographicCRS.class, crs); assertEquals("GCS WGS 1984", crs.getName().getCode()); + loggings.assertNoUnexpectedLog(); } /** @@ -228,6 +217,7 @@ public final class CRSTest extends TestCase { "UNIT[\"MEtre\", 1]]")); assertMessageContains(e, "I do not exist"); + loggings.assertNoUnexpectedLog(); } /** @@ -298,6 +288,7 @@ public final class CRSTest extends TestCase { CommonCRS.NAD27.universal(20, -101), CommonCRS.NAD27.universal(18, -20)) ); + loggings.assertNoUnexpectedLog(); } /** @@ -312,6 +303,7 @@ public final class CRSTest extends TestCase { assertFalse(CRS.isHorizontalCRS(HardCodedCRS.WGS84_3D)); assertFalse(CRS.isHorizontalCRS(HardCodedCRS.GEOID_4D)); assertFalse(CRS.isHorizontalCRS(HardCodedCRS.GEOCENTRIC)); + loggings.assertNoUnexpectedLog(); } /** @@ -326,6 +318,7 @@ public final class CRSTest extends TestCase { assertSame(HardCodedCRS.WGS84, CRS.getHorizontalComponent(HardCodedCRS.WGS84)); assertSame(HardCodedCRS.WGS84_LATITUDE_FIRST, CRS.getHorizontalComponent(HardCodedCRS.WGS84_LATITUDE_FIRST)); assertEqualsIgnoreMetadata(HardCodedCRS.WGS84, CRS.getHorizontalComponent(HardCodedCRS.WGS84_3D)); + loggings.assertNoUnexpectedLog(); } /** @@ -346,6 +339,7 @@ public final class CRSTest extends TestCase { assertNull(CRS.getVerticalComponent(HardCodedCRS.WGS84_3D, false)); assertEqualsIgnoreMetadata(HardCodedCRS.ELLIPSOIDAL_HEIGHT, CRS.getVerticalComponent(HardCodedCRS.WGS84_3D, true)); + loggings.assertNoUnexpectedLog(); } /** @@ -360,6 +354,7 @@ public final class CRSTest extends TestCase { assertSame(HardCodedCRS.TIME, CRS.getTemporalComponent(HardCodedCRS.TIME)); assertSame(HardCodedCRS.TIME, CRS.getTemporalComponent(HardCodedCRS.GEOID_4D)); + loggings.assertNoUnexpectedLog(); } /** @@ -376,6 +371,7 @@ public final class CRSTest extends TestCase { assertInstanceOf(ProjectedCRS.class, horizontal); assertEquals(2, horizontal.getCoordinateSystem().getDimension()); assertTrue(CRS.isHorizontalCRS(horizontal)); + loggings.assertNoUnexpectedLog(); } /** @@ -403,6 +399,7 @@ public final class CRSTest extends TestCase { HardCodedCRS.WGS84, HardCodedCRS.GEOID_3D, HardCodedCRS.NESTED); + loggings.assertNoUnexpectedLog(); } /** @@ -420,6 +417,7 @@ public final class CRSTest extends TestCase { assertSame(HardCodedCRS.NESTED, CRS.selectDimensions(HardCodedCRS.NESTED, 0, 1, 2, 3)); assertSame(HardCodedCRS.GEOID_3D, CRS.selectDimensions(HardCodedCRS.NESTED, 0, 1, 2)); assertEqualsIgnoreMetadata(HardCodedCRS.GEOID_3D, CRS.selectDimensions(HardCodedCRS.GEOID_4D, 0, 1, 2)); + loggings.assertNoUnexpectedLog(); } /** @@ -433,6 +431,7 @@ public final class CRSTest extends TestCase { final GeographicCRS crs = HardCodedCRS.WGS84_3D; assertSame(CommonCRS.Vertical.ELLIPSOIDAL.crs(), CRS.selectDimensions(crs, 2)); assertSame(CommonCRS.WGS84.normalizedGeographic(), CRS.selectDimensions(crs, 0, 1)); + loggings.assertNoUnexpectedLog(); } /** @@ -446,6 +445,7 @@ public final class CRSTest extends TestCase { final ProjectedCRS crs = HardCodedConversions.mercator3D(); assertSame(CommonCRS.Vertical.ELLIPSOIDAL.crs(), CRS.selectDimensions(crs, 2)); assertEqualsIgnoreMetadata(HardCodedConversions.mercator(), CRS.selectDimensions(crs, 0, 1)); + loggings.assertNoUnexpectedLog(); } /** @@ -459,6 +459,7 @@ public final class CRSTest extends TestCase { assertMessageContains(e, "components"); assertSame(HardCodedCRS.WGS84, CRS.compound(HardCodedCRS.WGS84)); assertEqualsIgnoreMetadata(HardCodedCRS.WGS84_3D, CRS.compound(HardCodedCRS.WGS84, HardCodedCRS.ELLIPSOIDAL_HEIGHT)); + loggings.assertNoUnexpectedLog(); } /** @@ -508,6 +509,7 @@ public final class CRSTest extends TestCase { public void testGetGreenwichLongitude() { assertEquals(0, CRS.getGreenwichLongitude(HardCodedCRS.WGS84)); assertEquals(2.33722917, CRS.getGreenwichLongitude(HardCodedCRS.NTF), 1E-12); + loggings.assertNoUnexpectedLog(); } /** @@ -520,5 +522,6 @@ public final class CRSTest extends TestCase { public void testIdentifiedObjectLookup() throws FactoryException { IdentifiedObjectsTest.testLookupEPSG(); IdentifiedObjectsTest.testLookupWMS(); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java index 8093360f7b..b5c2f5b5a2 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/crs/DefaultProjectedCRSTest.java @@ -37,16 +37,13 @@ import org.apache.sis.measure.Units; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; import org.opengis.test.Validators; -import org.apache.sis.test.LoggingWatcher; -import static org.apache.sis.test.Assertions.assertMessageContains; import org.apache.sis.xml.test.TestCase; import org.apache.sis.referencing.cs.HardCodedCS; -import static org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierEqual; +import static org.apache.sis.test.Assertions.assertMessageContains; import static org.apache.sis.referencing.Assertions.assertWktEquals; +import static org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierEqual; // Specific to the geoapi-3.1 and geoapi-4.0 branches: import static org.opengis.test.Assertions.assertAxisDirectionsEqual; @@ -57,11 +54,12 @@ import static org.opengis.test.Assertions.assertAxisDirectionsEqual; * * @author Martin Desruisseaux (Geomatys) */ -public final class DefaultProjectedCRSTest extends TestCase { +public final class DefaultProjectedCRSTest extends TestCase.WithLogs { /** * Creates a new test case. */ public DefaultProjectedCRSTest() { + super(Loggers.COORDINATE_OPERATION); } /** @@ -74,26 +72,6 @@ public final class DefaultProjectedCRSTest extends TestCase { return DefaultProjectedCRSTest.class.getResourceAsStream("ProjectedCRS.xml"); } - /** - * A JUnit rule for listening to log events emitted during execution of {@link #testWKT1_WithExplicitAxisLength()}. - * This rule is used by the test methods for verifying that the logged messages contain the expected information. - * The expected message is something like "Parameter semi_minor could have been omitted but got a value that does - * not match the WGS84 ellipsoid". - * - * <p>This field is public because JUnit requires us to do so, but should be considered as an implementation details - * (it should have been a private field).</p> - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.COORDINATE_OPERATION); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - /** * Creates a projected CRS and verifies its parameters. * Verifies also that the constructor does not accept invalid base CRS. @@ -107,6 +85,7 @@ public final class DefaultProjectedCRSTest extends TestCase { var e = assertThrows(InvalidGeodeticParameterException.class, () -> create(HardCodedCRS.WGS84_3D), "Should not accept a three-dimensional base geodetic CRS."); assertMessageContains(e, "Lambert Conic Conformal (1SP)"); + loggings.assertNoUnexpectedLog(); } /** @@ -170,6 +149,8 @@ public final class DefaultProjectedCRSTest extends TestCase { " AXIS[“Northing”, NORTH],\n" + " AUTHORITY[“EPSG”, “27572”]]", crs); + + loggings.assertNoUnexpectedLog(); } /** @@ -200,6 +181,8 @@ public final class DefaultProjectedCRSTest extends TestCase { " AXIS[“Northing”, NORTH],\n" + " AUTHORITY[“EPSG”, “27572”]]", crs); + + loggings.assertNoUnexpectedLog(); } /** @@ -233,6 +216,8 @@ public final class DefaultProjectedCRSTest extends TestCase { " AXIS[“Northing”, NORTH],\n" + " AUTHORITY[“EPSG”, “27572”]]", crs); + + loggings.assertNoUnexpectedLog(); } /** @@ -269,6 +254,8 @@ public final class DefaultProjectedCRSTest extends TestCase { " Unit[“metre”, 1, Id[“EPSG”, 9001]],\n" + " Id[“EPSG”, 27572]]", crs); + + loggings.assertNoUnexpectedLog(); } /** @@ -324,6 +311,8 @@ public final class DefaultProjectedCRSTest extends TestCase { " Unit[“metre”, 1],\n" + " Id[“EPSG”, 27572, URI[“urn:ogc:def:crs:EPSG::27572”]]]", crs); + + loggings.assertNoUnexpectedLog(); } /** @@ -375,6 +364,8 @@ public final class DefaultProjectedCRSTest extends TestCase { " Unit[“metre”, 1],\n" + " Id[“EPSG”, 27572, URI[“urn:ogc:def:crs:EPSG::27572”]]]", crs); + + loggings.assertNoUnexpectedLog(); } /** @@ -455,6 +446,8 @@ public final class DefaultProjectedCRSTest extends TestCase { " Axis[“Northing (N)”, north],\n" + " Unit[“metre”, 1]]", crs); + + loggings.assertNoUnexpectedLog(); } /** @@ -484,6 +477,7 @@ public final class DefaultProjectedCRSTest extends TestCase { */ assertMarshalEqualsFile(openTestFile(), crs, null, STRICT, new String[] {"gml:name"}, new String[] {"xmlns:*", "xsi:schemaLocation", "gml:id"}); + loggings.assertNoUnexpectedLog(); } /** @@ -502,5 +496,6 @@ public final class DefaultProjectedCRSTest extends TestCase { assertTrue (c.equals(normalized, ComparisonMode.IGNORE_METADATA)); assertTrue (c.equals(normalized, ComparisonMode.APPROXIMATE)); assertTrue (c.equals(normalized, ComparisonMode.ALLOW_VARIANT)); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java index af96cb8510..c0fadfe355 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java @@ -40,11 +40,8 @@ import org.apache.sis.measure.Units; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; -import org.apache.sis.test.TestCase; +import org.apache.sis.test.TestCaseWithLogs; import org.apache.sis.referencing.cs.HardCodedCS; import org.apache.sis.referencing.crs.HardCodedCRS; import org.apache.sis.referencing.datum.HardCodedDatum; @@ -57,26 +54,12 @@ import static org.apache.sis.test.Assertions.assertSetEquals; * * @author Martin Desruisseaux (Geomatys) */ -public final class MultiAuthoritiesFactoryTest extends TestCase { - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.CRS_FACTORY); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - +public final class MultiAuthoritiesFactoryTest extends TestCaseWithLogs { /** * Creates a new test case. */ public MultiAuthoritiesFactoryTest() { + super(Loggers.CRS_FACTORY); } /** @@ -118,6 +101,7 @@ public final class MultiAuthoritiesFactoryTest extends TestCase { List.of(mock1, mock2), null, List.of(mock1, mock3), null); assertSetEquals(List.of("MOCK1", "MOCK2", "MOCK3"), factory.getCodeSpaces()); + loggings.assertNoUnexpectedLog(); } /** @@ -151,6 +135,7 @@ public final class MultiAuthoritiesFactoryTest extends TestCase { () -> factory.getAuthorityFactory(CRSAuthorityFactory.class, "mock1", "9.9"), "Should not have found a 'mock1' factory for the 9.9 version."); assertMessageContains(e, "MOCK1", "9.9"); + loggings.assertNoUnexpectedLog(); } /** @@ -212,6 +197,7 @@ public final class MultiAuthoritiesFactoryTest extends TestCase { () -> factory.createGeodeticDatum("MOCK2:4326"), "Should not have found an object from a non-existent factory."); assertMessageContains(e, "MOCK2"); + loggings.assertNoUnexpectedLog(); } /** @@ -237,6 +223,7 @@ public final class MultiAuthoritiesFactoryTest extends TestCase { () -> factory.createGeographicCRS("urn:ogc:def:datum:MOCK::4326"), "Should not create an object of the wrong type."); assertMessageContains(e, "datum", "GeographicCRS"); + loggings.assertNoUnexpectedLog(); } /** @@ -259,6 +246,7 @@ public final class MultiAuthoritiesFactoryTest extends TestCase { () -> factory.createDatum("http://www.opengis.net/gml/srs/mock.xml#6326"), "Should not create an object of the wrong type."); assertMessageContains(e, "crs", "Datum"); + loggings.assertNoUnexpectedLog(); } /** @@ -292,6 +280,7 @@ public final class MultiAuthoritiesFactoryTest extends TestCase { () -> factory.createObject("urn:ogc:def:crs, datum:MOCK::6326, cs:MOCK::6424, cs:MOCK::6422"), "Shall not accept to create combined URI with unexpected objects."); assertMessageContains(e); + loggings.assertNoUnexpectedLog(); } /** @@ -319,6 +308,7 @@ public final class MultiAuthoritiesFactoryTest extends TestCase { + "2=http://www.opengis.net/def/cs/MOCK/0/6424"), "Shall not accept Datum + CoordinateSystem combination."); assertMessageContains(e); + loggings.assertNoUnexpectedLog(); } /** @@ -352,6 +342,7 @@ public final class MultiAuthoritiesFactoryTest extends TestCase { assertFalse(codes.contains("MOCK:6326")); // A geodetic datum. assertFalse(codes.isEmpty()); assertArrayEquals(new String[] {"MOCK:4979", "MOCK:84", "MOCK:4326", "MOCK:5714", "MOCK:9905"}, codes.toArray()); + loggings.assertNoUnexpectedLog(); } /** @@ -391,5 +382,6 @@ public final class MultiAuthoritiesFactoryTest extends TestCase { final var factory = new MultiAuthoritiesFactory(mock, null, mock, null); final IdentifiedObjectFinder finder = factory.newIdentifiedObjectFinder(); assertSame(HardCodedDatum.WGS72, finder.findSingleton(HardCodedDatum.WGS72)); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java index e92de380bc..eedc715278 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java @@ -59,12 +59,9 @@ import org.apache.sis.referencing.factory.IdentifiedObjectFinder; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterAll; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.TestCase; -import org.apache.sis.test.LoggingWatcher; +import org.apache.sis.test.TestCaseWithLogs; import org.apache.sis.referencing.factory.TestFactorySource; import static org.apache.sis.test.Assertions.assertNotDeepEquals; import static org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierEqual; @@ -81,7 +78,7 @@ import static org.opengis.test.Assertions.assertAxisDirectionsEqual; * @author Vadim Semenov */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) -public final class EPSGFactoryTest extends TestCase { +public final class EPSGFactoryTest extends TestCaseWithLogs { /** * The source of the EPSG factory. */ @@ -93,6 +90,7 @@ public final class EPSGFactoryTest extends TestCase { * @throws FactoryException if an error occurred while creating the factory. */ public EPSGFactoryTest() throws FactoryException { + super(Loggers.CRS_FACTORY); dataEPSG = new TestFactorySource(); } @@ -106,21 +104,6 @@ public final class EPSGFactoryTest extends TestCase { dataEPSG.close(); } - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.CRS_FACTORY); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - /** * Tests the "WGS 84" geographic CRS (EPSG:4326). * @@ -139,6 +122,7 @@ public final class EPSGFactoryTest extends TestCase { assertSame(crs, factory.createCoordinateReferenceSystem("4326"), "CRS shall be cached."); assertSame(crs, factory.createGeographicCRS("EPSG::4326"), "Shall accept \"::\""); + loggings.assertNoUnexpectedLog(); } /** @@ -158,6 +142,7 @@ public final class EPSGFactoryTest extends TestCase { assertTrue(bwp.length >= 1, "Expected a transformation to WGS84."); assertSame(crs, factory.createCoordinateReferenceSystem("4274"), "CRS shall be cached."); + loggings.assertNoUnexpectedLog(); } /** @@ -174,6 +159,7 @@ public final class EPSGFactoryTest extends TestCase { assertAxisDirectionsEqual(crs.getCoordinateSystem(), AxisDirection.NORTH, AxisDirection.EAST, AxisDirection.UP); assertSame(crs, factory.createCoordinateReferenceSystem("4993"), "CRS shall be cached."); + loggings.assertNoUnexpectedLog(); } /** @@ -190,6 +176,7 @@ public final class EPSGFactoryTest extends TestCase { assertAxisDirectionsEqual(crs.getCoordinateSystem(), AxisDirection.GEOCENTRIC_X, AxisDirection.GEOCENTRIC_Y, AxisDirection.GEOCENTRIC_Z); assertSame(crs, factory.createCoordinateReferenceSystem("4915"), "CRS shall be cached."); + loggings.assertNoUnexpectedLog(); } /** @@ -210,6 +197,7 @@ public final class EPSGFactoryTest extends TestCase { verifyTransverseMercatorParmeters(crs.getConversionFromBase().getParameterValues(), -93); assertSame(crs, factory.createCoordinateReferenceSystem("2027"), "CRS shall be cached."); + loggings.assertNoUnexpectedLog(); } /** @@ -254,6 +242,7 @@ public final class EPSGFactoryTest extends TestCase { assertEquals( 0, parameters.parameter("false_northing" ).doubleValue(), "false_northing"); assertSame(crs, factory.createCoordinateReferenceSystem("2442"), "CRS shall be cached."); + loggings.assertNoUnexpectedLog(); } /** @@ -289,6 +278,7 @@ public final class EPSGFactoryTest extends TestCase { assertNotDeepEquals(crs.getConversionFromBase(), variant.getConversionFromBase()); assertNotDeepEquals(crs, variant); + loggings.assertNoUnexpectedLog(); } /** @@ -317,6 +307,7 @@ public final class EPSGFactoryTest extends TestCase { assertSame(crs, factory.createObject("NTF (Paris) / Lambert zone I")); assertSame(crs, factory.createProjectedCRS("NTF Paris Lambert zone I")); assertSame(crs, factory.createObject("NTF Paris Lambert zone I")); + loggings.assertNoUnexpectedLog(); } /** @@ -338,6 +329,7 @@ public final class EPSGFactoryTest extends TestCase { // TODO: test axis directions. assertSame(crs, factory.createCoordinateReferenceSystem("3408"), "CRS shall be cached."); + loggings.assertNoUnexpectedLog(); } /** @@ -356,6 +348,7 @@ public final class EPSGFactoryTest extends TestCase { assertAxisDirectionsEqual(crs.getCoordinateSystem(), AxisDirection.EAST, AxisDirection.NORTH); assertSame(crs, factory.createCoordinateReferenceSystem("3857"), "CRS shall be cached."); + loggings.assertNoUnexpectedLog(); } /** @@ -371,6 +364,7 @@ public final class EPSGFactoryTest extends TestCase { assertEpsgNameAndIdentifierEqual("Barcelona", 9301, crs.getDatum()); assertAxisDirectionsEqual(crs.getCoordinateSystem(), AxisDirection.NORTH, AxisDirection.EAST); assertSame(crs, factory.createCoordinateReferenceSystem("5801"), "CRS shall be cached."); + loggings.assertNoUnexpectedLog(); } /** @@ -386,6 +380,7 @@ public final class EPSGFactoryTest extends TestCase { assertEpsgNameAndIdentifierEqual("Black Sea", 5134, crs.getDatum()); assertSame(crs, factory.createCoordinateReferenceSystem("5735"), "CRS shall be cached."); assertAxisDirectionsEqual(crs.getCoordinateSystem(), AxisDirection.UP); + loggings.assertNoUnexpectedLog(); } /** @@ -416,6 +411,7 @@ public final class EPSGFactoryTest extends TestCase { assertEquals( 8.23, bbox.getEastBoundLongitude(), "eastBoundLongitude"); assertSame(crs, factory.createCoordinateReferenceSystem("7400"), "CRS shall be cached."); + loggings.assertNoUnexpectedLog(); } /** @@ -456,6 +452,7 @@ public final class EPSGFactoryTest extends TestCase { assertEquals(ref.getUnit().getSystemUnit(), axis.getUnit().getSystemUnit()); } } + loggings.assertNoUnexpectedLog(); } /** @@ -507,6 +504,7 @@ public final class EPSGFactoryTest extends TestCase { assertSame(crs, factory.createCoordinateReferenceSystem("3786"), "CRS shall be cached."); assertSame(replacement, factory.createCoordinateReferenceSystem("4088"), "CRS shall be cached."); + loggings.assertNoUnexpectedLog(); } /** @@ -536,6 +534,7 @@ public final class EPSGFactoryTest extends TestCase { var e = assertThrows(NoSuchAuthorityCodeException.class, () -> factory.createGeographicCRS("WGS83"), "Should not find a geographic CRS named “WGS83” (the actual name is “WGS 84”)."); assertEquals("WGS83", e.getAuthorityCode()); + loggings.assertNoUnexpectedLog(); } /** @@ -700,6 +699,7 @@ public final class EPSGFactoryTest extends TestCase { @SuppressWarnings({"unchecked","rawtypes"}) final Class<? extends IdentifiedObject> wrong = (Class) String.class; assertTrue(factory.getAuthorityCodes(wrong).isEmpty()); + loggings.assertNoUnexpectedLog(); } /** @@ -715,6 +715,7 @@ public final class EPSGFactoryTest extends TestCase { assertEquals("NTF (Paris) / Nord France", factory.getDescriptionText("27591").toString(Locale.US)); assertEquals("NTF (Paris) / France II", factory.getDescriptionText("27582").toString(Locale.US)); assertEquals("Ellipsoidal height", factory.getDescriptionText( "84").toString(Locale.US)); + loggings.assertNoUnexpectedLog(); } /** @@ -771,6 +772,7 @@ public final class EPSGFactoryTest extends TestCase { factory.printCacheContent(out); throw error; } + loggings.assertNoUnexpectedLog(); } /** @@ -786,6 +788,7 @@ public final class EPSGFactoryTest extends TestCase { assertEpsgNameAndIdentifierEqual("NTF (Paris) to NTF (2)", 1764, operation); assertInstanceOf(Transformation.class, operation); assertSame(operation, factory.createCoordinateOperation("1764"), "Operation shall be cached"); + loggings.assertNoUnexpectedLog(); } /** @@ -802,6 +805,7 @@ public final class EPSGFactoryTest extends TestCase { assertEpsgNameAndIdentifierEqual("BD72 to WGS 84 (1)", 1609, operation); assertEquals(1.0, ((AbstractCoordinateOperation) operation).getLinearAccuracy()); assertSame(operation, factory.createCoordinateOperation("1609"), "Operation shall be cached"); + loggings.assertNoUnexpectedLog(); } /** @@ -926,6 +930,7 @@ public final class EPSGFactoryTest extends TestCase { */ finder.setSearchDomain(IdentifiedObjectFinder.Domain.ALL_DATASET); assertSame(found, finder.findSingleton(crs), "The CRS should still in the cache."); + loggings.assertNoUnexpectedLog(); } /** @@ -979,5 +984,6 @@ public final class EPSGFactoryTest extends TestCase { assertEpsgNameAndIdentifierEqual("Beijing 1954 / 3-degree Gauss-Kruger CM 135E", 2442, it.next()); assertEpsgNameAndIdentifierEqual("Beijing 1954 / Gauss-Kruger CM 135E", 21463, it.next()); assertFalse(it.hasNext()); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java index d2498d85df..ba97805368 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java @@ -38,13 +38,10 @@ import org.apache.sis.util.internal.Constants; import org.apache.sis.metadata.sql.util.Reflection; // Test dependencies -import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.Assertions.*; -import org.apache.sis.test.LoggingWatcher; -import org.apache.sis.test.TestCase; +import org.apache.sis.test.TestCaseWithLogs; import org.apache.sis.metadata.sql.TestDatabase; @@ -64,29 +61,12 @@ import org.apache.sis.metadata.sql.TestDatabase; * * @author Martin Desruisseaux (Geomatys) */ -public final class EPSGInstallerTest extends TestCase { - /** - * A JUnit rule for listening to log events emitted during execution of tests. - * This rule is used by tests that verifies the log message content. - * - * <p>This field is public because JUnit requires us to do so, but should be considered - * as an implementation details (it should have been a private field).</p> - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.CRS_FACTORY); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - +public final class EPSGInstallerTest extends TestCaseWithLogs { /** * Creates a new test case. */ public EPSGInstallerTest() { + super(Loggers.CRS_FACTORY); } /** @@ -113,6 +93,8 @@ public final class EPSGInstallerTest extends TestCase { assertTrue(Pattern.matches(EPSGInstaller.REPLACE_STATEMENT, "UPDATE epsg.\"Coordinate Axis\"\n" + "SET coord_axis_orientation = replace(coord_axis_orientation, CHR(182), CHR(10))")); + + loggings.assertNoUnexpectedLog(); } /** diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/SingleOperationMarshallingTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/SingleOperationMarshallingTest.java index 934c72c36b..a733883df5 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/SingleOperationMarshallingTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/SingleOperationMarshallingTest.java @@ -42,12 +42,9 @@ import static org.apache.sis.metadata.iso.citation.Citations.EPSG; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; import org.opengis.test.Validators; import org.apache.sis.xml.bind.referencing.CC_OperationParameterGroupTest; -import org.apache.sis.test.LoggingWatcher; import org.apache.sis.xml.test.TestCase; import static org.apache.sis.test.TestUtilities.getSingleton; import static org.apache.sis.metadata.Assertions.assertXmlEquals; @@ -62,26 +59,12 @@ import static org.opengis.test.Assertions.assertMatrixEquals; * * @author Martin Desruisseaux (Geomatys) */ -public final class SingleOperationMarshallingTest extends TestCase { - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.XML); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - +public final class SingleOperationMarshallingTest extends TestCase.WithLogs { /** * Creates a new test case. */ public SingleOperationMarshallingTest() { + super(Loggers.XML); } /** @@ -143,6 +126,7 @@ public final class SingleOperationMarshallingTest extends TestCase { final var method = (OperationMethod) XML.unmarshal(xml); verifyMethod(method); Validators.validate(method); + loggings.assertNoUnexpectedLog(); } /** @@ -202,6 +186,7 @@ public final class SingleOperationMarshallingTest extends TestCase { Validators.validate(c); loggings.assertNextLogContains("EPSG::8801"); loggings.assertNextLogContains("EPSG::8802"); + loggings.assertNoUnexpectedLog(); } /** @@ -279,5 +264,6 @@ public final class SingleOperationMarshallingTest extends TestCase { */ Validators.validate(c); loggings.assertNextLogContains("EPSG::8602"); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java index c465037ac8..7196ae48b5 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java @@ -66,10 +66,7 @@ import org.apache.sis.util.internal.Constants; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; import org.apache.sis.test.TestUtilities; import org.apache.sis.xml.test.DocumentComparator; import org.apache.sis.xml.test.TestCase; @@ -84,31 +81,17 @@ import org.apache.sis.xml.test.TestCase; * * @see org.apache.sis.metadata.iso.DefaultMetadataTest */ -public final class MetadataTest extends TestCase { +public final class MetadataTest extends TestCase.WithLogs { /** * The resource file which contains an XML representation of a {@link Metadata} object. */ private static final String XML_FILE = "Metadata.xml"; - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.XML); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - /** * Creates a new test case. */ public MetadataTest() { + super(Loggers.XML); } /** @@ -398,6 +381,7 @@ public final class MetadataTest extends TestCase { comparator.ignoredAttributes.add(Namespaces.GML + ":id"); comparator.ignoreComments = true; comparator.compare(); + loggings.assertNoUnexpectedLog(); } /** @@ -430,5 +414,6 @@ public final class MetadataTest extends TestCase { pool.recycle(unmarshaller); final DefaultMetadata expected = createHardCoded(); assertTrue(metadata.equals(expected, ComparisonMode.DEBUG)); + loggings.assertNoUnexpectedLog(); } } diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataVerticalTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataVerticalTest.java index 173afb5690..813a54aa5e 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataVerticalTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataVerticalTest.java @@ -42,10 +42,7 @@ import org.apache.sis.xml.NilReason; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; import org.apache.sis.xml.test.TestCase; import static org.apache.sis.test.TestUtilities.getSingleton; @@ -63,11 +60,12 @@ import static org.opengis.test.Assertions.assertIdentifierEquals; * * @see org.apache.sis.metadata.iso.DefaultMetadataTest */ -public class MetadataVerticalTest extends TestCase { +public final class MetadataVerticalTest extends TestCase.WithLogs { /** * Creates a new test case. */ public MetadataVerticalTest() { + super(Loggers.XML); } /** @@ -80,21 +78,6 @@ public class MetadataVerticalTest extends TestCase { return MetadataVerticalTest.class.getResourceAsStream("Metadata with vertical CRS.xml"); } - /** - * A JUnit {@link Rule} for listening to log events. This field is public because JUnit requires us to - * do so, but should be considered as an implementation details (it should have been a private field). - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.XML); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - /** * Tests the (un)marshalling of a metadata with a vertical CRS. * @@ -200,6 +183,7 @@ public class MetadataVerticalTest extends TestCase { * Now marshal the object and compare with the original file. */ assertMarshalEqualsFile(openTestFile(), metadata, VERSION_2007, "xmlns:*", "xsi:schemaLocation"); + loggings.assertNoUnexpectedLog(); } /** diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_GeneralOperationParameterTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_GeneralOperationParameterTest.java index 996e601f9d..8b04e2732c 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_GeneralOperationParameterTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/xml/bind/referencing/CC_GeneralOperationParameterTest.java @@ -30,10 +30,7 @@ import org.apache.sis.xml.Namespaces; // Test dependencies import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.apache.sis.test.LoggingWatcher; import org.apache.sis.xml.test.TestCase; @@ -44,30 +41,12 @@ import org.apache.sis.xml.test.TestCase; * * @see <a href="http://issues.apache.org/jira/browse/SIS-290">SIS-290</a> */ -public final class CC_GeneralOperationParameterTest extends TestCase { - /** - * A JUnit rule for listening to log events emitted during execution of {@link #testGroupMergeBecauseExtraParameter()}. - * This rule is used by test methods for verifying that the log message contains the expected information. - * The expected message is something like "No parameter named "Parameter B" was found". - * - * <p>This field is public because JUnit requires us to do so, but should be considered as an implementation details - * (it should have been a private field).</p> - */ - @RegisterExtension - public final LoggingWatcher loggings = new LoggingWatcher(Loggers.XML); - - /** - * Verifies that no unexpected warning has been emitted in any test defined in this class. - */ - @AfterEach - public void assertNoUnexpectedLog() { - loggings.assertNoUnexpectedLog(); - } - +public final class CC_GeneralOperationParameterTest extends TestCase.WithLogs { /** * Creates a new test case. */ public CC_GeneralOperationParameterTest() { + super(Loggers.XML); } /** @@ -143,6 +122,8 @@ public final class CC_GeneralOperationParameterTest extends TestCase { provided = unmarshal("Optional parameter", "More details here."); assertSame(complete, CC_GeneralOperationParameter.merge(provided, complete)); + + loggings.assertNoUnexpectedLog(); } /** @@ -175,6 +156,7 @@ public final class CC_GeneralOperationParameterTest extends TestCase { assertEquals (1, merged.getMaximumOccurs()); assertEquals (Integer.class, merged.getValueClass()); assertSame (provided.getRemarks(), merged.getRemarks()); + loggings.assertNoUnexpectedLog(); } /** @@ -200,6 +182,7 @@ public final class CC_GeneralOperationParameterTest extends TestCase { create("Parameter D", "Remarks D.", false, 6)); assertSame(complete, CC_GeneralOperationParameter.merge(provided, complete)); + loggings.assertNoUnexpectedLog(); } /** @@ -240,6 +223,7 @@ public final class CC_GeneralOperationParameterTest extends TestCase { verifyParameter(itc.next(), itm.next(), false, "Remarks C."); // Not same because different remarks. assertTrue (itc.hasNext()); assertFalse(itm.hasNext()); + loggings.assertNoUnexpectedLog(); } /** @@ -281,6 +265,7 @@ public final class CC_GeneralOperationParameterTest extends TestCase { verifyParameter(itc.next(), itm.next(), true, null); assertFalse(itc.hasNext()); assertFalse(itm.hasNext()); + loggings.assertNoUnexpectedLog(); } /** @@ -326,6 +311,7 @@ public final class CC_GeneralOperationParameterTest extends TestCase { verifyParameter(itc.next(), itm.next(), true, "Remarks C."); assertFalse(itc.hasNext()); assertFalse(itm.hasNext()); + loggings.assertNoUnexpectedLog(); } /** 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..a756e706c7 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,6 +18,7 @@ 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; @@ -44,4 +45,32 @@ 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 0ec8858a5e..5c3697cf6e 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 @@ -19,11 +19,13 @@ package org.apache.sis.test; import java.util.Queue; import java.util.LinkedList; import java.util.ConcurrentModificationException; +import java.util.Objects; import java.util.logging.Filter; 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.*; @@ -37,8 +39,11 @@ import org.junit.jupiter.api.extension.ExtensionContext; * For using, create a rule in the JUnit test class like below: * * {@snippet lang="java" : - * @RegisterExtension - * public final LoggingWatcher loggings = new LoggingWatcher(Logger.getLogger(Loggers.XML)); + * @ResourceLock(LoggingWatcher.LOCK) + * public MyTest { + * @RegisterExtension + * public final LoggingWatcher loggings = new LoggingWatcher(Logger.getLogger(Loggers.XML)); + * } * } * * Recommended but not mandatory, ensure that there is no unexpected logging in any tests: @@ -58,9 +63,38 @@ import org.junit.jupiter.api.extension.ExtensionContext; * loggings.assertNoUnexpectedLog(); * } * + * <h2>Multi-threading</h2> + * By default, {@code LoggingWatcher} handles only the log records emitted by the same thread as the one + * that constructed this watcher. If tests are running in parallel, log records emitted by other threads + * will not interfere. For disabling this filtering (which is necessary if the test starts many threads), + * invoke {@link #setMultiThread()}. + * * @author Martin Desruisseaux (Geomatys) */ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallback, Filter { + /** + * Name of the lock to use in a JUnit {@code ResourceLock} annotation. + * Tests that are executed in a single thread can be run in parallel + * and should be declared with the following annotation: + * + * {@snippet lang="java" : + * @ResourceLock(value=LoggingWatcher.LOCK, mode=ResourceAccessMode.READ) + * public class MyTest { + * } + * } + * + * Tests that may start worker threads shall be executed in isolation with other tests + * that may do logging. These tests should have the following annotations: + * + * {@snippet lang="java" : + * @ResourceLock(value=LoggingWatcher.LOCK, mode=ResourceAccessMode.READ_WRITE) + * @Execution(ExecutionMode.SAME_THREAD) + * public class MyTest { + * } + * } + */ + public static final String LOCK = "Logging"; + /** * The logged messages. */ @@ -77,13 +111,33 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba */ private final SimpleFormatter formatter = new SimpleFormatter(); + /** + * Identifier of the thread to watch. + */ + private final long threadId = Thread.currentThread().getId(); + + /** + * Whether the test will be multi-threaded. If this flag is set to {@code true}, + * then this {@code LoggingWatcher} will intercept all logs, not only the ones + * produced by the thread that initialized this instance. + * + * @see #setMultiThread() + */ + private boolean isMultiThread; + + /** + * 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; + /** * Creates a new watcher for the given logger. * * @param logger the logger to watch. */ public LoggingWatcher(final Logger logger) { - this.logger = logger; + this.logger = Objects.requireNonNull(logger); } /** @@ -95,6 +149,26 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba this.logger = Logger.getLogger(logger); } + /** + * Notifies that the test will be multi-threaded. If this method is invoked, + * then this {@code LoggingWatcher} will intercept all logs, not only the ones + * produced by the thread that initialized this instance. + * + * <p>If this method is invoked, then the caller is responsible to ensure that + * no there test running in parallel may do logging. It can be done with JUnit + * {@code Execution} and {@code ResourceLock} annotations. Example:</p> + * + * {@snippet lang="java" : + * @ResourceLock(value=LoggingWatcher.LOCK, mode=ResourceAccessMode.READ_WRITE) + * @Execution(value=ExecutionMode.SAME_THREAD, reason="For verification of logs emitted by worker threads.") + * public class MyTest extends TestCase { + * } + * } + */ + final void setMultiThread() { + isMultiThread = true; + } + /** * Invoked when a test is about to start. This method installs this {@link Filter} * for the log messages before the tests are run. This installation will cause the @@ -106,8 +180,21 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba */ @Override public final void beforeEach(final ExtensionContext description) { - assertNull(logger.getFilter()); - logger.setFilter(this); + synchronized (logger) { + assertNull(allFilters); + final Filter current = logger.getFilter(); + if (current instanceof LoggingWatcher) { + allFilters = ((LoggingWatcher) current).allFilters; + assertNotNull(allFilters); + } else { + allFilters = new LinkedList<>(); + if (current != null) { + allFilters.add(current); + } + logger.setFilter(this); + } + allFilters.add(this); + } } /** @@ -118,7 +205,11 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba */ @Override public final void afterEach(final ExtensionContext description) { - logger.setFilter(null); + synchronized (logger) { + assertTrue(allFilters.remove(this)); + logger.setFilter(allFilters.peek()); + allFilters = null; + } } /** @@ -130,8 +221,29 @@ 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)) { + owner = w; + break; + } + } else if (!filter.isLoggable(record)) { + return false; + } + } + } if (record.getLevel().intValue() >= Level.INFO.intValue()) { - messages.add(formatter.formatMessage(record)); + synchronized (owner) { + owner.messages.add(owner.formatter.formatMessage(record)); + } } return TestCase.VERBOSE; } @@ -145,7 +257,7 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba * if that log message has been emitted. */ @SuppressWarnings("StringEquality") - public void skipNextLogIfContains(final String... keywords) { + public synchronized void skipNextLogIfContains(final String... keywords) { final String message = messages.peek(); if (message != null) { for (final String word : keywords) { @@ -166,7 +278,7 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba * @param keywords the keywords that are expected to exist in the next log message. * May be an empty array for requesting only the existence of a log with any message. */ - public void assertNextLogContains(final String... keywords) { + public synchronized void assertNextLogContains(final String... keywords) { if (messages.isEmpty()) { fail("Expected a logging messages but got no more."); } @@ -181,7 +293,7 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba /** * Verifies that there is no more log message. */ - public void assertNoUnexpectedLog() { + public synchronized void assertNoUnexpectedLog() { final String message = messages.peek(); if (message != null) { fail("Unexpected logging message: " + message); @@ -191,7 +303,7 @@ public final class LoggingWatcher implements BeforeEachCallback, AfterEachCallba /** * Discards all logging messages. */ - public void clear() { + public synchronized void clear() { messages.clear(); } } diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestCaseWithLogs.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestCaseWithLogs.java new file mode 100644 index 0000000000..abe5ad37e9 --- /dev/null +++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestCaseWithLogs.java @@ -0,0 +1,76 @@ +/* + * 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.test; + +import java.util.logging.Logger; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.api.parallel.ResourceLock; +import org.junit.jupiter.api.parallel.ResourceAccessMode; +import org.junit.jupiter.api.extension.RegisterExtension; + + +/** + * Base class of Apache SIS tests (except the ones that extend GeoAPI tests) that may emit logs. + * This is a convenience class applying the JUnit extension documented in {@link LoggingWatcher}. + * + * @author Martin Desruisseaux (Geomatys) + */ +@ResourceLock(value=LoggingWatcher.LOCK, mode=ResourceAccessMode.READ) +public abstract class TestCaseWithLogs extends TestCase { + /** + * A JUnit extension for listening to log events. + */ + @RegisterExtension + public final LoggingWatcher loggings; + + /** + * Creates a new test case which will listen to logs emitted by the given logger. + * + * @param logger the logger to listen to. + */ + protected TestCaseWithLogs(final Logger logger) { + loggings = new LoggingWatcher(logger); + } + + /** + * Creates a new test case which will listen to logs emitted by the logger of the given name. + * + * @param logger name of the logger to listen. + */ + protected TestCaseWithLogs(final String logger) { + loggings = new LoggingWatcher(logger); + } + + /** + * Base class of Apache SIS tests in which many workers may emit logs. + * Such tests should be executed in isolation of other tests that may emit logs. + */ + @Execution(value=ExecutionMode.SAME_THREAD, reason="For verification of logs emitted by worker threads.") + @ResourceLock(value=LoggingWatcher.LOCK, mode=ResourceAccessMode.READ_WRITE) + public static abstract class Isolated extends TestCaseWithLogs { + /** + * Creates a new test case which will listen to logs emitted by the logger of the given name. + * + * @param logger name of the logger to listen. + */ + protected Isolated(final String logger) { + super(logger); + loggings.setMultiThread(); + } + } +}
