You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/02/01 14:37:43 UTC

[sis] branch geoapi-4.0 updated: Following fix in the previous commit, refactor the exception handling for trying to make the origin of the error easier to identify.

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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 5fa4d63663 Following fix in the previous commit, refactor the exception handling for trying to make the origin of the error easier to identify.
5fa4d63663 is described below

commit 5fa4d63663e1ac2e01119dfedddb2f418e93ec32
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Feb 1 15:21:35 2023 +0100

    Following fix in the previous commit, refactor the exception handling
    for trying to make the origin of the error easier to identify.
---
 .../referencing/provider/DatumShiftGridFile.java   | 16 +++-----
 .../referencing/provider/DatumShiftGridLoader.java | 45 ++++++++++------------
 .../provider/FranceGeocentricInterpolation.java    | 27 ++++++++-----
 .../sis/internal/referencing/provider/NADCON.java  | 25 +++++++++---
 .../sis/internal/referencing/provider/NTv2.java    | 16 ++++++--
 .../FranceGeocentricInterpolationTest.java         |  6 +--
 .../internal/referencing/provider/NADCONTest.java  | 17 +++-----
 .../internal/referencing/provider/NTv2Test.java    | 24 +++++-------
 .../sis/test/integration/DatumShiftTest.java       | 15 ++------
 9 files changed, 95 insertions(+), 96 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
index 1075976a75..78c8a7ed04 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
@@ -49,7 +49,6 @@ import org.apache.sis.util.collection.Containers;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.parameter.Parameters;
 import org.apache.sis.referencing.datum.DatumShiftGrid;
-import org.apache.sis.referencing.factory.FactoryDataException;
 import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.transform.InterpolatedTransform;
@@ -258,19 +257,16 @@ abstract class DatumShiftGridFile<C extends Quantity<C>, T extends Quantity<T>>
      * @param  f2      a second file to load, or {@code null} if none.
      * @param  loader  the loader to execute if the grid is not in the cache.
      * @return the cached or loaded grid.
-     * @throws FactoryException if an error occurred while loading the grid.
+     * @throws Exception if an error occurred while loading the grid.
+     *         Caller should handle the exception with {@code canNotLoad(…)}.
+     *
+     * @see DatumShiftGridLoader#canNotLoad(String, URI, Exception)
      */
     static DatumShiftGridFile<?,?> getOrLoad(final URI f1, final URI f2, final Callable<DatumShiftGridFile<?,?>> loader)
-            throws FactoryException
+            throws Exception
     {
         final Object key = (f2 != null) ? new AbstractMap.SimpleImmutableEntry<>(f1, f2) : f1;
-        try {
-            return CACHE.getOrCreate(key, loader);
-        } catch (FactoryException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new FactoryDataException(Errors.format(Errors.Keys.CanNotRead_1, f1), e);
-        }
+        return CACHE.getOrCreate(key, loader);
     }
 
     /**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridLoader.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridLoader.java
index 00968b55a6..ee7e0e31d7 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridLoader.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridLoader.java
@@ -22,7 +22,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.io.EOFException;
 import java.io.IOException;
 import java.net.URI;
-import java.net.URL;
 import java.nio.ByteBuffer;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -33,6 +32,7 @@ import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.Channels;
 import org.opengis.util.FactoryException;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.resources.Messages;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.internal.system.DataDirectory;
 import org.apache.sis.internal.referencing.Resources;
@@ -155,34 +155,31 @@ abstract class DatumShiftGridLoader {
      *
      * @param  path  the URI to make absolute.
      * @return an absolute (if possible) URI to the data.
+     * @throws NoSuchFileException if the path can not be made absolute.
+     *         This exception is necessary for letting the caller know that the coordinate operation is
+     *         probably valid but can not be constructed because an optional configuration is missing.
+     *         It is typically because the {@code SIS_DATA} environment variable has not been set.
      */
-    static URI toAbsolutePath(final URI path) {
-        if (!path.isAbsolute() && !path.isOpaque()) {
+    static URI toAbsolutePath(final URI path) throws NoSuchFileException {
+        if (path.isAbsolute()) {
+            return path;
+        }
+        String message;
+        if (path.isOpaque()) {
+            message = Errors.format(Errors.Keys.CanNotOpen_1, path);
+        } else {
             final Path dir = DataDirectory.DATUM_CHANGES.getDirectory();
             if (dir != null) {
                 return dir.resolve(path.getPath()).toUri();
             }
+            final String env = DataDirectory.getenv();
+            if (env == null) {
+                message = Messages.format(Messages.Keys.DataDirectoryNotSpecified_1, DataDirectory.ENV);
+            } else {
+                message = Messages.format(Messages.Keys.DataDirectoryNotReadable_2, DataDirectory.ENV, env);
+            }
         }
-        return path;
-    }
-
-    /**
-     * Converts the given path to a URL, throwing a {@link NoSuchFileException} if the URL is not absolute.
-     * This specific exception type is necessary for letting the caller know that the coordinate operation is
-     * probably valid but can not be constructed because an optional configuration is missing.
-     * It is typically because the {@code SIS_DATA} environment variable has not been set.
-     *
-     * @param  path  the path to convert to a URL.
-     * @return the given path as an URL.
-     * @throws NoSuchFileException if the URI is not absolute.
-     * @throws java.net.MalformedURLException if some error occurred during the conversion.
-     */
-    static URL toURL(final URI path) throws IOException {
-        try {
-            return path.toURL();
-        } catch (IllegalArgumentException e) {
-            throw new NoSuchFileException(path.toString(), null, e.getMessage());
-        }
+        throw new NoSuchFileException(path.toString(), null, message);
     }
 
     /**
@@ -198,7 +195,7 @@ abstract class DatumShiftGridLoader {
         } catch (FileSystemNotFoundException e) {
             Logging.ignorableException(AbstractProvider.LOGGER, DatumShiftGridLoader.class, "newByteChannel", e);
         }
-        return Channels.newChannel(toURL(path).openStream());
+        return Channels.newChannel(path.toURL().openStream());
     }
 
     /**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java
index f6105ef4cc..addfa28b61 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java
@@ -326,9 +326,13 @@ public class FranceGeocentricInterpolation extends GeodeticOperation {
                             Errors.Keys.IllegalArgumentValue_2, DIM, dim), DIM, dim);
         }
         final URI file = pg.getMandatoryValue(FILE);
-        final DatumShiftGridFile<Angle,Length> grid = getOrLoad(file,
-                isRecognized(file) ? new double[] {TX, TY, TZ} : null, PRECISION);
-
+        final DatumShiftGridFile<Angle,Length> grid;
+        try {
+            grid = getOrLoad(file, isRecognized(file) ? new double[] {TX, TY, TZ} : null, PRECISION);
+        } catch (Exception e) {
+            // NumberFormatException, ArithmeticException, NoSuchElementException, and more.
+            throw DatumShiftGridLoader.canNotLoad(HEADER, file, e);
+        }
         MathTransform tr = createGeodeticTransformation(factory,
                 createEllipsoid(pg, Molodensky.TGT_SEMI_MAJOR,
                                     Molodensky.TGT_SEMI_MINOR, CommonCRS.ETRS89.ellipsoid()),   // GRS 1980 ellipsoid
@@ -363,9 +367,13 @@ public class FranceGeocentricInterpolation extends GeodeticOperation {
      * @param  file      an absolute or relative reference to the datum shift grid file to load.
      * @param  averages  an "average" value for the offset in each dimension, or {@code null} if unknown.
      * @param  scale     the factor by which to multiply each compressed value before to add to the average value.
+     * @throws Exception if an error occurred while loading the grid.
+     *         Caller should handle the exception with {@code canNotLoad(…)}.
+     *
+     * @see DatumShiftGridLoader#canNotLoad(String, URI, Exception)
      */
     static DatumShiftGridFile<Angle,Length> getOrLoad(final URI file, final double[] averages, final double scale)
-            throws FactoryException
+            throws Exception
     {
         final URI resolved = DatumShiftGridLoader.toAbsolutePath(file);
         return DatumShiftGridFile.getOrLoad(resolved, null, new Loader(resolved, averages, scale))
@@ -395,7 +403,7 @@ public class FranceGeocentricInterpolation extends GeodeticOperation {
 
         /** Returns the reader for the specified URI. */
         static BufferedReader newBufferedReader(final URI file) throws IOException {
-            return new BufferedReader(new InputStreamReader(DatumShiftGridLoader.toURL(file).openStream()));
+            return new BufferedReader(new InputStreamReader(file.toURL().openStream()));
         }
 
         /**
@@ -403,18 +411,17 @@ public class FranceGeocentricInterpolation extends GeodeticOperation {
          * This method load grid data from the file specified at construction time.
          *
          * @return the loaded grid data.
-         * @throws FactoryException if an error occurred while loading the grid data.
+         * @throws Exception if an error occurred while loading the grid data.
+         *         May be {@link IOException}, {@link NumberFormatException}, {@link ArithmeticException},
+         *         {@link NoSuchElementException}, {@link NoninvertibleTransformException}, <i>etc</i>.
          */
         @Override
-        public DatumShiftGridFile<?,?> call() throws FactoryException {
+        public DatumShiftGridFile<?,?> call() throws Exception {
             final DatumShiftGridFile<?,?> grid;
             try (BufferedReader in = newBufferedReader(file)) {
                 DatumShiftGridLoader.startLoading(FranceGeocentricInterpolation.class, file);
                 final DatumShiftGridFile.Float<Angle,Length> g = load(in, file);
                 grid = DatumShiftGridCompressed.compress(g, averages, scale);
-            } catch (IOException | NoninvertibleTransformException | RuntimeException e) {
-                // NumberFormatException, ArithmeticException, NoSuchElementException, possibly other.
-                throw DatumShiftGridLoader.canNotLoad(HEADER, file, e);
             }
             return grid.useSharedData();
         }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NADCON.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NADCON.java
index e50e10723d..e1f75bfeed 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NADCON.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NADCON.java
@@ -21,6 +21,7 @@ import java.net.URI;
 import java.nio.ByteOrder;
 import java.nio.ByteBuffer;
 import java.nio.FloatBuffer;
+import java.nio.file.NoSuchFileException;
 import java.nio.channels.ReadableByteChannel;
 import javax.xml.bind.annotation.XmlTransient;
 import javax.measure.quantity.Angle;
@@ -34,6 +35,7 @@ import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.operation.Transformation;
 import org.opengis.referencing.operation.NoninvertibleTransformException;
+import org.apache.sis.referencing.factory.MissingFactoryResourceException;
 import org.apache.sis.parameter.ParameterBuilder;
 import org.apache.sis.parameter.Parameters;
 import org.apache.sis.util.CharSequences;
@@ -138,20 +140,27 @@ public final class NADCON extends AbstractProvider {
     public MathTransform createMathTransform(final MathTransformFactory factory, final ParameterValueGroup values)
             throws ParameterNotFoundException, FactoryException
     {
-        final Parameters pg  = Parameters.castOrWrap(values);
-        return DatumShiftGridFile.createGeodeticTransformation(NADCON.class, factory,
-                getOrLoad(pg.getMandatoryValue(LATITUDE), pg.getMandatoryValue(LONGITUDE)));
+        final Parameters pg = Parameters.castOrWrap(values);
+        try {
+            return DatumShiftGridFile.createGeodeticTransformation(NADCON.class, factory,
+                    getOrLoad(pg.getMandatoryValue(LATITUDE), pg.getMandatoryValue(LONGITUDE)));
+        } catch (NoSuchFileException e) {
+            throw new MissingFactoryResourceException(e.getMessage(), e);
+        } catch (Exception e) {
+            throw new FactoryException(e);
+        }
     }
 
     /**
      * Returns the grid of the given name.
      * This method returns the cached instance if it still exists, or load the grid otherwise.
      *
-     * @param latitudeShifts   relative or absolute path of the grid file for latitude shifts.
-     * @param longitudeShifts  relative or absolute path name of the grid file for longitude shifts.
+     * @param  latitudeShifts   relative or absolute path of the grid file for latitude shifts.
+     * @param  longitudeShifts  relative or absolute path name of the grid file for longitude shifts.
+     * @throws Exception if an error occurred while loading the grid.
      */
     static DatumShiftGridFile<Angle,Angle> getOrLoad(final URI latitudeShifts, final URI longitudeShifts)
-            throws FactoryException
+            throws Exception
     {
         final URI rlat = Loader.toAbsolutePath(latitudeShifts);
         final URI rlon = Loader.toAbsolutePath(longitudeShifts);
@@ -175,6 +184,10 @@ public final class NADCON extends AbstractProvider {
                     new Loader(in, buffer, file).readGrid(fb, loader, null);
                 }
             } catch (IOException | NoninvertibleTransformException | RuntimeException e) {
+                /*
+                 * Handle the exception here instead of by the caller
+                 * because we know which of the 2 files is problematic.
+                 */
                 throw DatumShiftGridLoader.canNotLoad("NADCON", file, e);
             }
             grid = DatumShiftGridCompressed.compress(loader.grid, null, loader.grid.accuracy);
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java
index a44fb3a054..df07c15d12 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java
@@ -143,7 +143,13 @@ public final class NTv2 extends AbstractProvider {
             throws ParameterNotFoundException, FactoryException
     {
         final Parameters pg = Parameters.castOrWrap(values);
-        final DatumShiftGridFile<Angle,Angle> grid = getOrLoad(provider, pg.getMandatoryValue(FILE), version);
+        final URI file = pg.getMandatoryValue(FILE);
+        final DatumShiftGridFile<Angle,Angle> grid;
+        try {
+            grid = getOrLoad(provider, file, version);
+        } catch (Exception e) {
+            throw DatumShiftGridLoader.canNotLoad(provider.getSimpleName(), file, e);
+        }
         return DatumShiftGridFile.createGeodeticTransformation(provider, factory, grid);
     }
 
@@ -154,9 +160,13 @@ public final class NTv2 extends AbstractProvider {
      * @param  provider  the provider which is creating a transform.
      * @param  file      relative or absolute path of the datum shift grid file to load.
      * @param  version   the expected version (1 or 2).
+     * @throws Exception if an error occurred while loading the grid.
+     *         Caller should handle the exception with {@code canNotLoad(…)}.
+     *
+     * @see DatumShiftGridLoader#canNotLoad(String, URI, Exception)
      */
     static DatumShiftGridFile<Angle,Angle> getOrLoad(final Class<? extends AbstractProvider> provider,
-            final URI file, final int version) throws FactoryException
+            final URI file, final int version) throws Exception
     {
         final URI resolved = Loader.toAbsolutePath(file);
         return DatumShiftGridFile.getOrLoad(resolved, null, () -> {
@@ -166,8 +176,6 @@ public final class NTv2 extends AbstractProvider {
                 final Loader loader = new Loader(in, file, version);
                 grid = loader.readAllGrids();
                 loader.report(provider);
-            } catch (IOException | NoninvertibleTransformException | RuntimeException e) {
-                throw DatumShiftGridLoader.canNotLoad(provider.getSimpleName(), file, e);
             }
             return grid.useSharedData();
         }).castTo(Angle.class, Angle.class);
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolationTest.java b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolationTest.java
index 2dc409a8d5..fdd0206c95 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolationTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolationTest.java
@@ -221,13 +221,11 @@ public final class FranceGeocentricInterpolationTest extends DatumShiftTestCase
     /**
      * Tests the {@link FranceGeocentricInterpolation#getOrLoad(URI, double[], double)} method and its cache.
      *
-     * @throws URISyntaxException if the URL to the test file is not valid.
-     * @throws FactoryException if an error occurred while computing the grid.
-     * @throws TransformException if an error occurred while computing the envelope.
+     * @throws Exception if an error occurred while loading or computing the grid, or while testing transformations.
      */
     @Test
     @DependsOnMethod("testGrid")
-    public void testGetOrLoad() throws URISyntaxException, FactoryException, TransformException {
+    public void testGetOrLoad() throws Exception {
         final DatumShiftGridFile<Angle,Length> grid = FranceGeocentricInterpolation.getOrLoad(
                 getResource(TEST_FILE), new double[] {
                         FranceGeocentricInterpolation.TX,
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java
index c159b4e9f4..8f87a35bf8 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NADCONTest.java
@@ -20,12 +20,10 @@ import java.util.Locale;
 import java.io.BufferedWriter;
 import java.io.IOException;
 import java.net.URI;
-import java.net.URISyntaxException;
 import java.nio.file.Path;
 import java.nio.file.Files;
 import javax.measure.quantity.Angle;
 import org.opengis.geometry.Envelope;
-import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.referencing.operation.matrix.Matrix3;
 import org.apache.sis.geometry.Envelope2D;
@@ -92,12 +90,10 @@ public final class NADCONTest extends DatumShiftTestCase {
      * Tests loading a grid file and interpolating a sample point.
      * The point used for this test is given by {@link #samplePoint(int)}.
      *
-     * @throws URISyntaxException if the URL to the test file is not valid.
-     * @throws FactoryException if an error occurred while loading or computing the grid.
-     * @throws TransformException if an error occurred while computing the envelope or testing the point.
+     * @throws Exception if an error occurred while loading or computing the grid, or while testing transformations.
      */
     @Test
-    public void testLoader() throws URISyntaxException, FactoryException, TransformException {
+    public void testLoader() throws Exception {
         testNADCON(getResource(TEST_FILE + ".laa"),     // Latitude shifts
                    getResource(TEST_FILE + ".loa"),     // Longitude shifts
                    -99.75, -98.0, 37.5, 39.75);
@@ -111,12 +107,9 @@ public final class NADCONTest extends DatumShiftTestCase {
      *
      * @param  latitudeShifts   path to the official {@code "conus.las"} file.
      * @param  longitudeShifts  path to the official {@code "conus.los"} file.
-     * @throws FactoryException if an error occurred while loading or computing the grid.
-     * @throws TransformException if an error occurred while computing the envelope or testing the point.
+     * @throws Exception if an error occurred while loading or computing the grid, or while testing transformations.
      */
-    public static void testNADCON(final URI latitudeShifts, final URI longitudeShifts)
-            throws FactoryException, TransformException
-    {
+    public static void testNADCON(final URI latitudeShifts, final URI longitudeShifts) throws Exception {
         testNADCON(latitudeShifts, longitudeShifts, -131, -63, 20, 50);
     }
 
@@ -130,7 +123,7 @@ public final class NADCONTest extends DatumShiftTestCase {
      */
     private static void testNADCON(final URI latitudeShifts, final URI longitudeShifts,
             final double xmin, final double xmax, final double ymin, final double ymax)
-            throws FactoryException, TransformException
+            throws Exception
     {
         final DatumShiftGridFile<Angle,Angle> grid = NADCON.getOrLoad(latitudeShifts, longitudeShifts);
         assertInstanceOf("Should not be compressed.", DatumShiftGridFile.Float.class, grid);
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NTv2Test.java b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NTv2Test.java
index 82a2b93f1a..2248734b4f 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NTv2Test.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/NTv2Test.java
@@ -17,7 +17,6 @@
 package org.apache.sis.internal.referencing.provider;
 
 import java.net.URI;
-import java.net.URISyntaxException;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -29,13 +28,13 @@ import java.nio.channels.WritableByteChannel;
 import java.nio.charset.StandardCharsets;
 import javax.measure.quantity.Angle;
 import org.opengis.geometry.Envelope;
-import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.referencing.operation.matrix.Matrix3;
 import org.apache.sis.geometry.Envelope2D;
 import org.apache.sis.geometry.Envelopes;
 import org.apache.sis.measure.Units;
 import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.internal.system.DataDirectory;
 import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
@@ -80,13 +79,10 @@ public final class NTv2Test extends DatumShiftTestCase {
      * Tests loading a grid file and interpolating a sample point. The point used for
      * this test is given by {@link FranceGeocentricInterpolationTest#samplePoint(int)}.
      *
-     * @throws URISyntaxException if the URL to the test file is not valid.
-     * @throws IOException if an error occurred while loading the grid.
-     * @throws FactoryException if an error occurred while computing the grid.
-     * @throws TransformException if an error occurred while computing the envelope or testing the point.
+     * @throws Exception if an error occurred while loading or computing the grid, or while testing transformations.
      */
     @Test
-    public void testLoader() throws URISyntaxException, IOException, FactoryException, TransformException {
+    public void testLoader() throws Exception {
         testRGF93(getResource(TEST_FILE),
                  36000 - 360 * (72 + 5),    // Subgrid of RGF93 beginning at gridX = 72
                  36000 - 360 * (72),        // Subgrid uses 6 cells along longitude axis
@@ -101,10 +97,9 @@ public final class NTv2Test extends DatumShiftTestCase {
      * explicitly if they can provide a path to the {@code "NTF_R93.gsb"} file.
      *
      * @param  file  path to the official {@code "NTF_R93.gsb"} file.
-     * @throws FactoryException if an error occurred while loading or computing the grid.
-     * @throws TransformException if an error occurred while computing the envelope or testing the point.
+     * @throws Exception if an error occurred while loading or computing the grid, or while testing transformations.
      */
-    public static void testRGF93(final URI file) throws FactoryException, TransformException {
+    public static void testRGF93(final URI file) throws Exception {
         testRGF93(file, -19800, 36000, 147600, 187200);
     }
 
@@ -117,7 +112,7 @@ public final class NTv2Test extends DatumShiftTestCase {
      * @param  ymax  value of the {@code "N_LAT"} record.
      */
     private static void testRGF93(final URI file, final double xmin, final double xmax,
-            final double ymin, final double ymax) throws FactoryException, TransformException
+            final double ymin, final double ymax) throws Exception
     {
         final double cellSize = 360;
         final DatumShiftGridFile<Angle,Angle> grid = NTv2.getOrLoad(NTv2.class, file, 2);
@@ -177,13 +172,12 @@ public final class NTv2Test extends DatumShiftTestCase {
      * to be present in the {@code $SIS_DATA/DatumChanges} directory. This test is executed only if the
      * {@link #RUN_EXTENSIVE_TESTS} flag is set.
      *
-     * @throws URISyntaxException if the URL to the test file is not valid.
-     * @throws FactoryException if an error occurred while loading or computing the grid.
-     * @throws TransformException if an error occurred while computing the envelope or testing the point.
+     * @throws Exception if an error occurred while loading or computing the grid, or while testing transformations.
      */
     @Test
-    public void testMultiGrids() throws URISyntaxException, FactoryException, TransformException {
+    public void testMultiGrids() throws Exception {
         assumeTrue(RUN_EXTENSIVE_TESTS);
+        assumeTrue(DataDirectory.getenv() != null);
         final URI file = DatumShiftGridLoader.toAbsolutePath(new URI(MULTIGRID_TEST_FILE));
         assumeTrue(Files.exists(Paths.get(file)));
         final DatumShiftGridFile<Angle,Angle> grid = NTv2.getOrLoad(NTv2.class, file, 2);
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/DatumShiftTest.java b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/DatumShiftTest.java
index 38b116d3e8..3ee02e4f98 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/DatumShiftTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/DatumShiftTest.java
@@ -17,9 +17,6 @@
 package org.apache.sis.test.integration;
 
 import java.net.URI;
-import java.io.IOException;
-import org.opengis.util.FactoryException;
-import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.internal.referencing.provider.NTv2Test;
 import org.apache.sis.internal.referencing.provider.NADCONTest;
 import org.apache.sis.internal.referencing.provider.FranceGeocentricInterpolationTest;
@@ -50,12 +47,10 @@ public final class DatumShiftTest extends TestCase {
      * Tests loading an official {@code "ntf_r93.gsb"} datum shift grid file
      * and interpolating the sample point tested by {@link FranceGeocentricInterpolationTest}.
      *
-     * @throws IOException if the grid exists but an error occurred while loading it.
-     * @throws FactoryException if an error occurred while computing the grid.
-     * @throws TransformException if an error occurred while computing the envelope or testing the point.
+     * @throws Exception if an error occurred while loading or computing the grid, or while testing transformations.
      */
     @Test
-    public void testRGF93() throws IOException, FactoryException, TransformException {
+    public void testRGF93() throws Exception {
         final URI file = assumeDataExists(DataDirectory.DATUM_CHANGES, "ntf_r93.gsb");
         NTv2Test.testRGF93(file);
     }
@@ -64,12 +59,10 @@ public final class DatumShiftTest extends TestCase {
      * Tests loading the official {@code "conus.las"} and {@code "conus.los"} datum shift grid files
      * and interpolating a sample point tested by {@link NADCONTest}.
      *
-     * @throws IOException if the grids exist but an error occurred while loading them.
-     * @throws FactoryException if an error occurred while computing the grid.
-     * @throws TransformException if an error occurred while computing the envelope or testing the point.
+     * @throws Exception if an error occurred while loading or computing the grid, or while testing transformations.
      */
     @Test
-    public void testNADCON() throws IOException, FactoryException, TransformException {
+    public void testNADCON() throws Exception {
         final URI latitudeShifts  = assumeDataExists(DataDirectory.DATUM_CHANGES, "conus.las");
         final URI longitudeShifts = assumeDataExists(DataDirectory.DATUM_CHANGES, "conus.los");
         NADCONTest.testNADCON(latitudeShifts, longitudeShifts);