Consolidation of the way we check if some tests are enabled.
diff --git a/core/sis-metadata/src/test/java/org/apache/sis/metadata/xml/SchemaComplianceTest.java b/core/sis-metadata/src/test/java/org/apache/sis/metadata/xml/SchemaComplianceTest.java
index be775fc..e03a2ac 100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/metadata/xml/SchemaComplianceTest.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/metadata/xml/SchemaComplianceTest.java
@@ -17,16 +17,15 @@
 package org.apache.sis.metadata.xml;
 
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.nio.file.Files;
 import org.apache.sis.metadata.iso.ISOMetadata;
 import org.apache.sis.internal.system.DataDirectory;
+import org.apache.sis.test.ProjectDirectories;
 import org.apache.sis.test.xml.SchemaCompliance;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.junit.Assume.*;
-import static org.junit.Assert.*;
 
 
 /**
@@ -36,7 +35,7 @@
  * Content can be downloaded as ZIP files from <a href="https://standards.iso.org/iso/19115/">ISO portal</a>.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
@@ -58,22 +57,9 @@
          * Locate the root of metadata class directory. In a Maven build:
          * "core/sis-metadata/target/classes/org/apache/sis/metadata/iso"
          */
-        final Path mdp = Paths.get(ISOMetadata.class.getResource("ISOMetadata.class").toURI()).getParent();
-        final Path cp = getParent(mdp, "org", "apache", "sis", "metadata", "iso");
-        final SchemaCompliance checker = new SchemaCompliance(cp, directory);
+        final ProjectDirectories dir = new ProjectDirectories(ISOMetadata.class);
+        final SchemaCompliance checker = new SchemaCompliance(dir.classesRootDirectory, directory);
         checker.loadDefaultSchemas();
-        checker.verify(mdp);
-    }
-
-    /**
-     * Returns the parent directory. The expected names of skipped directories are given in argument;
-     * they will be verified.
-     */
-    private static Path getParent(Path cp, final String... parents) {
-        for (int i=parents.length; --i >= 0;) {
-            assertTrue(cp.endsWith(parents[i]));
-            cp = cp.getParent();
-        }
-        return cp;
+        checker.verify(dir.classesPackageDirectory);
     }
 }
diff --git a/core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java b/core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java
index afa93ea..74f6667 100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java
@@ -45,6 +45,8 @@
 import org.apache.sis.internal.xml.LegacyNamespaces;
 import org.apache.sis.xml.Namespaces;
 
+import static org.apache.sis.test.TestCase.PENDING_FUTURE_SIS_VERSION;
+
 
 /**
  * Verify JAXB annotations in a single package. A new instance of this class is created by
@@ -447,11 +449,11 @@
              */
             if (isCollection) {
                 if (!info.isCollection) {
-                    if (false)  // Temporarily disabled because require GeoAPI modifications.
+                    if (PENDING_FUTURE_SIS_VERSION)  // Temporarily disabled because require GeoAPI modifications.
                     throw new SchemaException(errorInClassMember(javaName).append("Value should be a singleton.").toString());
                 }
             } else if (info.isCollection) {
-                if (false)  // Temporarily disabled because require GeoAPI modifications.
+                if (PENDING_FUTURE_SIS_VERSION)  // Temporarily disabled because require GeoAPI modifications.
                 throw new SchemaException(errorInClassMember(javaName).append("Value should be a collection.").toString());
             }
             if (valueType != null) {
@@ -462,7 +464,7 @@
                     expected = TYPE_EQUIVALENCES.getOrDefault(expected, expected);
                     actual   = TYPE_EQUIVALENCES.getOrDefault(actual,   actual);
                     if (!expected.equals(actual)) {
-                        if (false)  // Temporarily disabled because require GeoAPI modifications.
+                        if (PENDING_FUTURE_SIS_VERSION)  // Temporarily disabled because require GeoAPI modifications.
                         throw new SchemaException(errorInClassMember(javaName)
                                 .append("Declared value type: ").append(actual).append(System.lineSeparator())
                                 .append("Expected value type: ").append(expected).toString());
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
index 90344b0..f886b3f 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
@@ -36,7 +36,7 @@
  * This file will be deleted on the SIS JDK9 branch.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @since   1.0
+ * @since   1.1
  * @version 0.8
  * @module
  */
@@ -110,4 +110,21 @@
         if (b instanceof DoubleBuffer) return ((DoubleBuffer) b).duplicate();
         throw new IllegalArgumentException();
     }
+
+    /**
+     * Place holder for {@code Class.getPackageName()}.
+     *
+     * @param  c  the class for which to get the package name.
+     * @return the name of the package.
+     */
+    public static String getPackageName(Class<?> c) {
+        Class<?> outer;
+        while ((outer = c.getEnclosingClass()) != null) {
+            c = outer;
+        }
+        String name = c.getName();
+        final int separator = name.lastIndexOf('.');
+        name = (separator >= 1) ? name.substring(0, separator) : "";
+        return name;
+    }
 }
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java
index 2e9df6d..a396f43 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java
@@ -24,7 +24,7 @@
  * may change in incompatible ways in any future version without notice.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @since   0.8
+ * @since   1.1
  * @version 0.8
  * @module
  */
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java b/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
index 6a9cd42..9539899 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
@@ -26,6 +26,7 @@
 import org.apache.sis.util.Static;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.Classes;
+import org.apache.sis.internal.jdk9.JDK9;
 import org.apache.sis.internal.system.Modules;
 
 
@@ -173,15 +174,9 @@
      *
      * @since 1.0
      */
-    public static Logger getLogger(Class<?> source) {
+    public static Logger getLogger(final Class<?> source) {
         ArgumentChecks.ensureNonNull("source", source);
-        Class<?> outer;
-        while ((outer = source.getEnclosingClass()) != null) {
-            source = outer;
-        }
-        String name = source.getName();
-        final int separator = name.lastIndexOf('.');
-        name = (separator >= 1) ? name.substring(0, separator) : "";
+        String name = JDK9.getPackageName(source);
         if (name.startsWith(Modules.INTERNAL_CLASSNAME_PREFIX)) {
             // Remove the "internal" part from Apache SIS package names.
             name = Modules.CLASSNAME_PREFIX + name.substring(Modules.INTERNAL_CLASSNAME_PREFIX.length());
diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/ProjectDirectories.java b/core/sis-utility/src/test/java/org/apache/sis/test/ProjectDirectories.java
new file mode 100644
index 0000000..acc7cc5
--- /dev/null
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/ProjectDirectories.java
@@ -0,0 +1,104 @@
+/*
+ * 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.net.URL;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.apache.sis.internal.jdk9.JDK9;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Provides methods that depend on assumption on the project directory layout.
+ * We currently use Maven conventions, but this class provide a central place
+ * to revisit if we want to change convention in the future.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public final class ProjectDirectories {
+    /**
+     * The directory where are stored the compiled Java classes for the package of the given class.
+     * The constructor used the given class as a sample member for getting this package directory.
+     */
+    public final Path classesPackageDirectory;
+
+    /**
+     * The root directory where are stored the compiled Java classes.
+     * The constructor used the given class as a sample member for getting this directory.
+     *
+     * <p>If we are running the tests from another environment than Maven (e.g. from NetBeans project),
+     * then this directory may contain all modules instead than only the module of the class given to
+     * the constructor.</p>
+     */
+    public final Path classesRootDirectory;
+
+    /**
+     * Find classes and sources directories using the given class as a member.
+     * In the case of Maven multi-modules project, the directories will apply
+     * only to the module containing the given class.
+     *
+     * @param  c  a sample member of the classes for which to get the directories.
+     */
+    public ProjectDirectories(final Class<?> c) {
+        final URL resource = c.getResource(c.getSimpleName() + ".class");
+        assertNotNull("Class not found.", resource);
+        Path dir;
+        try {
+            dir = Paths.get(resource.toURI()).getParent();
+        } catch (URISyntaxException e) {
+            throw new AssertionError(e);
+        }
+        classesPackageDirectory = dir;
+        String pkg = JDK9.getPackageName(c);
+        int s = pkg.length();
+        do {
+            pkg = pkg.substring(0, s);
+            s = pkg.lastIndexOf('.');
+            assertEquals ("Unexpected directory structure.", pkg.substring(s+1), dir.getFileName().toString());
+            assertNotNull("Unexpected directory structure.", dir = dir.getParent());
+        } while (s >= 0);
+        classesRootDirectory = dir;
+    }
+
+    /**
+     * Returns whether the {@link #classesRootDirectory} is the sub-directory of a tree following Maven conventions.
+     * This method verifies that the parent directories are {@code "target/*classes"} and that the parent directory
+     * contains a {@code pom.xml} file.
+     *
+     * @return whether we are in a Maven module.
+     */
+    public boolean isMavenModule() {
+        Path dir = classesRootDirectory;
+        if (dir.getFileName().toString().endsWith("classes")) {
+            dir = dir.getParent();
+            if (dir != null && dir.getFileName().toString().equals("target")) {
+                dir = dir.getParent();
+                if (dir != null && Files.isRegularFile(dir.resolve("pom.xml"))) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
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 b2ae8f1..e66bc0d 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
@@ -68,6 +68,10 @@
      * The intent is to make easier to identify test cases that fail with the current version
      * of SIS (e.g. because of unsupported operations), but should pass in a future version.
      *
+     * <p>Note: sometime the work is actually pending future GeoAPI development. But we still
+     * use that flag for those cases because the {@code "geoapi"} branches of Apache SIS follow
+     * closely GeoAPI developments.</p>
+     *
      * @since 0.4
      */
     public static final boolean PENDING_FUTURE_SIS_VERSION = false;
diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java b/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java
index d6c40eb..2e98b2a 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java
@@ -25,8 +25,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
-import java.net.URL;
-import java.net.URISyntaxException;
 import org.apache.sis.internal.system.Shutdown;
 import org.apache.sis.internal.system.SystemListener;
 import org.apache.sis.util.logging.MonolineFormatter;
@@ -92,35 +90,12 @@
      */
     protected static void assertNoMissingTest(final Class<? extends TestSuite> suite) {
         if (skipCheckForMissingTests) return;
-        final ClassLoader loader = suite.getClassLoader();
-        final URL url = loader.getResource(suite.getName().replace('.', '/') + ".class");
-        assertNotNull("Test suite class not found.", url);
-        File root;
-        try {
-            root = new File(url.toURI());
-        } catch (URISyntaxException | IllegalArgumentException e) {
-            // If not a file, then it is probably an entry in a JAR file.
-            fail(e.toString());
-            return;
-        }
-        for (File c = new File(suite.getName().replace('.', File.separatorChar)); (c = c.getParentFile()) != null;) {
-            root = root.getParentFile();
-            assertNotNull("Unexpected directory structure.", root);
-            assertEquals("Unexpected directory structure.", c.getName(), root.getName());
-        }
         /*
-         * At this point, we found the root "org" package. Verifies if we are in the Maven target directory.
-         * In some IDE configuration, all the ".class" files are in the same directory, in which case the
-         * verification performed by this method become irrelevant.
+         * Verifies if we are in the Maven target directory. In some IDE configuration, all the ".class" files
+         * are in the same directory, in which case the verification performed by this method become irrelevant.
          */
-        File moduleDir = root;
-        for (int i=0; i<3; i++) {
-            moduleDir = moduleDir.getParentFile();
-            if (moduleDir == null) {
-                return;
-            }
-        }
-        if (!new File(moduleDir, "pom.xml").isFile()) {
+        final ProjectDirectories dir = new ProjectDirectories(suite);
+        if (!dir.isMavenModule()) {
             return;
         }
         /*
@@ -142,6 +117,8 @@
                 it.remove();
             }
         }
+        final ClassLoader loader = suite.getClassLoader();
+        final File root = dir.classesRootDirectory.resolve("org").toFile();
         removeExistingTests(loader, root, new StringBuilder(120).append(root.getName()), tests);
         if (!tests.isEmpty()) {
             fail("Classes not found. Are they defined in an other module? " + tests);
diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/package-info.java b/core/sis-utility/src/test/java/org/apache/sis/test/package-info.java
index 2899f39..48d530c 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/test/package-info.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/package-info.java
@@ -41,7 +41,7 @@
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.3
  * @module
  */