sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: Allow to run `CoverageReadConsistency` for benchmarks instead than tests.
Date Mon, 12 Jul 2021 15:52:00 GMT
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 d853a03c252a0bcbe2399ed01e5cb24553e150c8
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Mon Jul 12 17:45:21 2021 +0200

    Allow to run `CoverageReadConsistency` for benchmarks instead than tests.
---
 .../test/java/org/apache/sis/test/TestCase.java    |   2 +-
 .../sis/internal/storage/TiledGridCoverage.java    |  14 ---
 .../sis/test/storage/CoverageReadConsistency.java  | 107 +++++++++++++++++----
 3 files changed, 87 insertions(+), 36 deletions(-)

diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/TestCase.java b/core/sis-utility/src/test/java/org/apache/sis/test/TestCase.java
index e66bc0d..46f841b 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/test/TestCase.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/TestCase.java
@@ -101,7 +101,7 @@ public abstract strictfp class TestCase {
      * the {@value org.apache.sis.test.TestConfiguration#OUTPUT_ENCODING_KEY} system
      * property has been set to a different value.
      *
-     * @see org.apache.sis.test
+     * @see org.apache.sis.test.TestUtilities#forceFlushOutput()
      */
     public static final PrintWriter out;
 
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
index 6d7ca29..7ea234e 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
@@ -343,7 +343,6 @@ public abstract class TiledGridCoverage extends GridCoverage {
             throw new CannotEvaluateException(Resources.forLocale(getLocale()).getString(
                     Resources.Keys.CanNotRenderImage_1, getDisplayName()), e);
         }
-        assert validate(image);
         return image;
     }
 
@@ -658,17 +657,4 @@ public abstract class TiledGridCoverage extends GridCoverage {
      *         (too many exception types to list them all).
      */
     protected abstract WritableRaster[] readTiles(AOI iterator) throws IOException, DataStoreException;
-
-    /**
-     * Verifies if the given image is consistent. This method tolerates smaller width or
height compared to
-     * what we would expect from an integer amount of tiles, because this may get smaller
height from images
-     * encoded as strips.
-     */
-    private static boolean validate(final TiledImage image) {
-        final String error = image.verify();
-        if (error == null || error.equals("width") || error.equals("height")) {
-            return true;
-        }
-        throw new AssertionError(error);
-    }
 }
diff --git a/storage/sis-storage/src/test/java/org/apache/sis/test/storage/CoverageReadConsistency.java
b/storage/sis-storage/src/test/java/org/apache/sis/test/storage/CoverageReadConsistency.java
index ea1de7d..1d4424a 100644
--- a/storage/sis-storage/src/test/java/org/apache/sis/test/storage/CoverageReadConsistency.java
+++ b/storage/sis-storage/src/test/java/org/apache/sis/test/storage/CoverageReadConsistency.java
@@ -27,7 +27,9 @@ import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.internal.util.StandardDateFormat;
 import org.apache.sis.image.PixelIterator;
+import org.apache.sis.math.Statistics;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
@@ -66,6 +68,7 @@ public strictfp class CoverageReadConsistency extends TestCase {
     /**
      * The coverage at full extent, full resolution and with all bands.
      * This coverage will be used as a reference for verifying values read in sub-domains.
+     * Can be {@code null} if unavailable (for example because the image is too large).
      */
     private final GridCoverage full;
 
@@ -75,27 +78,62 @@ public strictfp class CoverageReadConsistency extends TestCase {
     private final Random random;
 
     /**
+     * Number of random sub-regions to read.
+     */
+    private final int numIterations;
+
+    /**
+     * Whether mismatched pixel values should cause a test failure. The default value is
{@code true}.
+     * If {@code false}, then this class will only counts the failures and reports them as
statistics.
+     */
+    private final boolean failOnMismatch;
+
+    /**
      * Creates a new tester. This constructor reads immediately the coverage at full extent
and full resolution.
-     * That full coverage will be used as a reference for verifying values read in sub-domains.
+     * That full coverage will be used as a reference for verifying the pixel values read
in sub-domains.
+     * Any mismatch in pixel values will cause immediate test failure.
      *
-     * @param  resource  the resource to test.
+     * @param  tested  the resource to test.
      * @throws DataStoreException if the full coverage can not be read.
      */
-    public CoverageReadConsistency(final GridCoverageResource resource) throws DataStoreException
{
-        this.resource = resource;
-        full = resource.read(null, null);
-        random = TestUtilities.createRandomNumberGenerator();
+    public CoverageReadConsistency(final GridCoverageResource tested) throws DataStoreException
{
+        resource       = tested;
+        full           = tested.read(null, null);
+        random         = TestUtilities.createRandomNumberGenerator();
+        numIterations  = 100;
+        failOnMismatch = true;
+    }
+
+    /**
+     * Creates a new tester with specified configuration.
+     * This tester may be used for benchmarking instead of JUnit tests.
+     * Mismatched pixel values will be reported in statistics instead than causing test failure.
+     *
+     * @param  tested     the resource to test.
+     * @param  reference  full coverage read from the {@code resource}, or {@code null} if
none.
+     * @param  seed       seed for random number generator. Used for reproducible "random"
values.
+     * @param  readCount  number of read operations to perform.
+     */
+    public CoverageReadConsistency(final GridCoverageResource tested, final GridCoverage
reference,
+                                   final long seed, final int readCount)
+    {
+        resource       = tested;
+        full           = reference;
+        random         = TestUtilities.createRandomNumberGenerator(seed);
+        numIterations  = readCount;
+        failOnMismatch = false;
     }
 
     /**
      * Tests reading in random sub-regions starting at coordinates (0,0).
-     * Data are read at full resolution (no-subsampling) and all bands are read. This is
the simplest test.
+     * Data are read at full resolution (no-subsampling) and all bands are read.
+     * This is the simplest test.
      *
      * @throws DataStoreException if an error occurred while using the resource.
      */
     @Test
     public void testSubRegionAtOrigin() throws DataStoreException {
-        readAndCompareRandomRegions(10, false, false);
+        readAndCompareRandomRegions(false, false);
     }
 
     /**
@@ -107,7 +145,7 @@ public strictfp class CoverageReadConsistency extends TestCase {
     @Test
     @DependsOnMethod("testSubRegionAtOrigin")
     public void testSubRegionsAnywhere() throws DataStoreException {
-        readAndCompareRandomRegions(10, true, false);
+        readAndCompareRandomRegions(true, false);
     }
 
     /**
@@ -119,7 +157,7 @@ public strictfp class CoverageReadConsistency extends TestCase {
     @Test
     @DependsOnMethod("testSubRegionAtOrigin")
     public void testSubsamplingAtOrigin() throws DataStoreException {
-        readAndCompareRandomRegions(10, false, true);
+        readAndCompareRandomRegions(false, true);
     }
 
     /**
@@ -131,18 +169,17 @@ public strictfp class CoverageReadConsistency extends TestCase {
     @Test
     @DependsOnMethod({"testSubsamplingAtOrigin", "testSubRegionsAnywhere"})
     public void testSubsamplingAnywhere() throws DataStoreException {
-        readAndCompareRandomRegions(100, true, true);
+        readAndCompareRandomRegions(true, true);
     }
 
     /**
      * Implementation of methods testing reading in random sub-regions with random sub-samplings.
      *
-     * @param  numIterations      number of random sub-regions to test.
      * @param  allowOffsets       whether to allow sub-regions to start elsewhere than (0,0).
      * @param  allowSubsamplings  whether to use random subsamplings.
      * @throws DataStoreException if an error occurred while using the resource.
      */
-    private void readAndCompareRandomRegions(int numIterations, final boolean allowOffsets,
final boolean allowSubsamplings)
+    private void readAndCompareRandomRegions(final boolean allowOffsets, final boolean allowSubsamplings)
             throws DataStoreException
     {
         final GridGeometry gg = resource.getGridGeometry();
@@ -152,7 +189,13 @@ public strictfp class CoverageReadConsistency extends TestCase {
         final long[] high        = new long[dimension];
         final int [] subsampling = new int [dimension];
         final int [] subOffsets  = new int [dimension];
-        while (--numIterations >= 0) {
+        /*
+         * We will collect statistics on execution time only if the
+         * test is executed in a more verbose mode than the default.
+         */
+        final Statistics durations = (VERBOSE || !failOnMismatch) ? new Statistics("time
(ms)") : null;
+        int failuresCount = 0;
+        for (int it=0; it < numIterations; it++) {
             /*
              * Create a random domain to be used as a query on the `GridCoverageResource`.
              */
@@ -175,20 +218,24 @@ public strictfp class CoverageReadConsistency extends TestCase {
              * more data than requested. The extent actually read is `actualReadExtent`.
It shall contain
              * fully the requested `domain`.
              */
+            final long startTime = System.nanoTime();
             final GridCoverage subset = resource.read(domain, null);
             final GridExtent actualReadExtent = subset.getGridGeometry().getExtent();
-            assertEquals("Unexpected number of dimensions.", dimension, actualReadExtent.getDimension());
-            for (int d=0; d<dimension; d++) {
-                if (subsampling[d] == 1) {
-                    assertTrue("Actual extent is too small.", actualReadExtent.getLow (d)
<= low [d]);
-                    assertTrue("Actual extent is too small.", actualReadExtent.getHigh(d)
>= high[d]);
+            if (failOnMismatch) {
+                assertEquals("Unexpected number of dimensions.", dimension, actualReadExtent.getDimension());
+                for (int d=0; d<dimension; d++) {
+                    if (subsampling[d] == 1) {
+                        assertTrue("Actual extent is too small.", actualReadExtent.getSize(d)
> high[d] - low[d]);
+                        assertTrue("Actual extent is too small.", actualReadExtent.getLow
(d) <= low [d]);
+                        assertTrue("Actual extent is too small.", actualReadExtent.getHigh(d)
>= high[d]);
+                    }
                 }
             }
             /*
              * If subsampling was enabled, the factors selected by the reader may be different
than
              * the subsampling factors that we specified. The following block updates those
values.
              */
-            if (allowSubsamplings) {
+            if (allowSubsamplings && full != null) {
                 final GridDerivation change = full.getGridGeometry().derive().subgrid(subset.getGridGeometry());
                 System.arraycopy(change.getSubsampling(),        0, subsampling, 0, dimension);
                 System.arraycopy(change.getSubsamplingOffsets(), 0, subOffsets,  0, dimension);
@@ -204,6 +251,9 @@ nextSlice:  for (;;) {
                 System.arraycopy(sliceMin, BIDIMENSIONAL, sliceMax, BIDIMENSIONAL, dimension
- BIDIMENSIONAL);
                 final PixelIterator itr = iterator(full,   sliceMin, sliceMax, subsampling,
subOffsets, allowSubsamplings);
                 final PixelIterator itc = iterator(subset, sliceMin, sliceMax, subsampling,
subOffsets, false);
+                if (durations != null) {
+                    durations.accept((System.nanoTime() - startTime) / (double) StandardDateFormat.NANOS_PER_MILLISECOND);
+                }
                 if (itr != null) {
                     assertEquals(itr.getDomain().getSize(), itc.getDomain().getSize());
                     double[] expected = null, actual = null;
@@ -212,6 +262,8 @@ nextSlice:  for (;;) {
                         expected = itr.getPixel(expected);
                         actual   = itc.getPixel(actual);
                         if (!Arrays.equals(expected, actual)) {
+                            failuresCount++;
+                            if (!failOnMismatch) break;
                             final Point pr = itr.getPosition();
                             final Point pc = itc.getPosition();
                             assertArrayEquals("Mismatch at position (" + pr.x + ", " + pr.y
+ ") in full image " +
@@ -238,6 +290,16 @@ nextSlice:  for (;;) {
                 break;
             }
         }
+        /*
+         * Show statistics only if the test are executed with the `VERBOSE` flag set,
+         * or if this `CoverageReadConsistency` is used for benchmark.
+         */
+        if (durations != null) {
+            out.print(durations);
+            final int totalCount = durations.count();
+            out.println("Number of failures: " + failuresCount + " / " + totalCount
+                        + " (" + (failuresCount / (totalCount / 100f)) + "%)");
+        }
     }
 
     /**
@@ -246,7 +308,7 @@ nextSlice:  for (;;) {
      * This method returns {@code null} if the arguments are valid but the image can not
be created
      * because of a restriction in {@code PixelInterleavedSampleModel} constructor.
      *
-     * @param  coverage     the coverage from which to get the iterator.
+     * @param  coverage     the coverage from which to get the iterator, or {@code null}
if unavailable.
      * @param  sliceMin     lower bounds of the <var>n</var>-dimensional region
of the coverage for which to get an iterator.
      * @param  sliceMax     upper bounds of the <var>n</var>-dimensional region
of the coverage for which to get an iterator.
      * @param  subsampling  subsampling factors to apply on the image.
@@ -256,6 +318,9 @@ nextSlice:  for (;;) {
     private static PixelIterator iterator(final GridCoverage coverage, long[] sliceMin, long[]
sliceMax,
             final int[] subsampling, final int[] subOffsets, final boolean allowSubsamplings)
     {
+        if (coverage == null) {
+            return null;
+        }
         /*
          * Same extent than `areaOfInterest` but in two dimensions and with (0,0) origin.
          * We use that for clipping iteration to the area that we requested even if the

Mime
View raw message