001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    http://www.geoapi.org
004 *
005 *    Copyright (C) 2011-2019 Open Geospatial Consortium, Inc.
006 *    All Rights Reserved. http://www.opengeospatial.org/ogc/legal
007 *
008 *    Permission to use, copy, and modify this software and its documentation, with
009 *    or without modification, for any purpose and without fee or royalty is hereby
010 *    granted, provided that you include the following on ALL copies of the software
011 *    and documentation or portions thereof, including modifications, that you make:
012 *
013 *    1. The full text of this NOTICE in a location viewable to users of the
014 *       redistributed or derivative work.
015 *    2. Notice of any changes or modifications to the OGC files, including the
016 *       date changes were made.
017 *
018 *    THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE
019 *    NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
020 *    TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT
021 *    THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
022 *    PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
023 *
024 *    COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
025 *    CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
026 *
027 *    The name and trademarks of copyright holders may NOT be used in advertising or
028 *    publicity pertaining to the software without specific, written prior permission.
029 *    Title to copyright in this software and any associated documentation will at all
030 *    times remain with copyright holders.
031 */
032package org.opengis.test;
033
034import java.util.Arrays;
035import java.util.Objects;
036import java.util.Iterator;
037import java.util.Collection;
038import java.util.ServiceLoader;
039import java.lang.reflect.Array;
040import org.junit.runner.RunWith;
041import org.junit.runners.Suite;
042import org.opengis.util.Factory;
043import org.opengis.referencing.operation.MathTransform;
044
045
046/**
047 * The suite of every tests defined in the GeoAPI conformance module.
048 * The test cases included in this test suite are:
049 *
050 * <ul>
051 *   <li>{@link org.opengis.test.util.NameTest}</li>
052 *   <li>{@link org.opengis.test.referencing.ObjectFactoryTest}</li>
053 *   <li>{@link org.opengis.test.referencing.AffineTransformTest}</li>
054 *   <li>{@link org.opengis.test.referencing.ParameterizedTransformTest}</li>
055 *   <li>{@link org.opengis.test.referencing.AuthorityFactoryTest}</li>
056 *   <li>{@link org.opengis.test.referencing.gigs.GIGS2001}</li>
057 *   <li>{@link org.opengis.test.referencing.gigs.GIGS2002}</li>
058 *   <li>{@link org.opengis.test.referencing.gigs.GIGS2003}</li>
059 *   <li>{@link org.opengis.test.referencing.gigs.GIGS2004}</li>
060 *   <li>{@link org.opengis.test.referencing.gigs.GIGS2005}</li>
061 *   <li>{@link org.opengis.test.referencing.gigs.GIGS2006}</li>
062 *   <li>{@link org.opengis.test.referencing.gigs.GIGS2007}</li>
063 *   <li>{@link org.opengis.test.referencing.gigs.GIGS2008}</li>
064 *   <li>{@link org.opengis.test.referencing.gigs.GIGS2009}</li>
065 *   <li>{@link org.opengis.test.referencing.gigs.GIGS3002}</li>
066 *   <li>{@link org.opengis.test.referencing.gigs.GIGS3003}</li>
067 *   <li>{@link org.opengis.test.referencing.gigs.GIGS3004}</li>
068 *   <li>{@link org.opengis.test.referencing.gigs.GIGS3005}</li>
069 *   <li>{@link org.opengis.test.wkt.CRSParserTest}</li>
070 * </ul>
071 *
072 * This {@code TestSuite} class provides also some static methods for {@linkplain #setFactories specifying
073 * explicitely which factories to use} or {@linkplain #addTestListener being notified of test results}.
074 * Those methods take effect even if the {@link TestCase} are run outside of a {@code TestSuite} context.
075 *
076 * <section class="note">
077 * <h1>How implementations are discovered</h1>
078 * All tests use {@link Factory} instances that are specific to the implementation being tested.
079 * By default {@code TestSuite} fetches the factory implementations with {@link ServiceLoader},
080 * which will scan every <code>META-INF/services/org.opengis.<var>TheFactory</var></code> files
081 * on the classpath. However implementors can override this default mechanism with explicit calls
082 * to the {@link #setFactories(Class, Factory[])} method.
083 *
084 * <p>Implementors can have some control on the tests (factories to use, features to test, tolerance
085 * thresholds) by registering their {@link FactoryFilter} or {@link ImplementationDetails} in the
086 * {@code META-INF/services/} directory. As an alternative, implementors can also extend directly
087 * the various {@link TestCase} subclasses.</p>
088 *
089 * <div class="note"><b>Example:</b>
090 * The test suite below declares that the tolerance threshold for {@code MyProjection}
091 * needs to be relaxed by a factor 10 during inverse projections.
092 *
093 * <blockquote><pre>package org.myproject;
094 *
095 *import org.opengis.test.TestSuite;
096 *import org.opengis.test.CalculationType;
097 *import org.opengis.test.ToleranceModifier;
098 *import org.opengis.test.ToleranceModifiers;
099 *import org.opengis.test.ImplementationDetails;
100 *import org.opengis.referencing.operation.MathTransform;
101 *import org.opengis.util.Factory;
102 *import java.util.Properties;
103 *
104 *public class AllTests extends TestSuite implements {@linkplain ImplementationDetails} {
105 *    &#64;Override
106 *    public Properties {@linkplain ImplementationDetails#configuration configuration}({@linkplain Factory}... factories) {
107 *        return null;
108 *    }
109 *
110 *    &#64;Override
111 *    public {@linkplain ToleranceModifier} {@linkplain ImplementationDetails#tolerance tolerance}({@linkplain MathTransform} transform) {
112 *        if (transform instanceof <var>MyProjection</var>) {
113 *            return {@linkplain ToleranceModifiers#scale ToleranceModifiers.scale}(EnumSet.of({@linkplain CalculationType#INVERSE_TRANSFORM}), 1, 10);
114 *        }
115 *        return null;
116 *    }
117 *}</pre></blockquote>
118 *
119 * The above {@code AllTests} class needs to be registered in the {@code META-INF/services/}
120 * directory if the implementation details shall be honored (otherwise the tests will be run,
121 * but the implementation details will be ignored).
122 * </div>
123 * </section>
124 *
125 * @see ImplementationDetails
126 * @see TestCase
127 * @see Factory
128 *
129 * @author  Martin Desruisseaux (Geomatys)
130 * @version 3.1
131 * @since   3.1
132 */
133@RunWith(Suite.class)
134@Suite.SuiteClasses({
135  org.opengis.test.util.NameTest.class,
136  org.opengis.test.referencing.ObjectFactoryTest.class,
137  org.opengis.test.referencing.AffineTransformTest.class,
138  org.opengis.test.referencing.ParameterizedTransformTest.class,
139  org.opengis.test.referencing.AuthorityFactoryTest.class,
140  org.opengis.test.referencing.gigs.GIGS2001.class,
141  org.opengis.test.referencing.gigs.GIGS2002.class,
142  org.opengis.test.referencing.gigs.GIGS2003.class,
143  org.opengis.test.referencing.gigs.GIGS2004.class,
144  org.opengis.test.referencing.gigs.GIGS2005.class,
145  org.opengis.test.referencing.gigs.GIGS2006.class,
146  org.opengis.test.referencing.gigs.GIGS2007.class,
147  org.opengis.test.referencing.gigs.GIGS2008.class,
148  org.opengis.test.referencing.gigs.GIGS2009.class,
149  org.opengis.test.referencing.gigs.GIGS3002.class,
150  org.opengis.test.referencing.gigs.GIGS3003.class,
151  org.opengis.test.referencing.gigs.GIGS3004.class,
152  org.opengis.test.referencing.gigs.GIGS3005.class,
153  org.opengis.test.wkt.CRSParserTest.class
154})
155public strictfp class TestSuite {
156    /**
157     * Constructor provided for allowing subclassing.
158     * Instances of this class usually don't need to be created.
159     */
160    protected TestSuite() {
161    }
162
163    /**
164     * Sets the class loader to use for loading implementations. A {@code null} value restores
165     * the default {@linkplain Thread#getContextClassLoader() context class loader}.
166     *
167     * @param loader  the class loader to use, or {@code null} for the default.
168     */
169    public static void setClassLoader(final ClassLoader loader) {
170        TestCase.setClassLoader(loader);
171    }
172
173    /**
174     * Specifies the factory implementations to use for the given factory interface.
175     *
176     * @param <T>      the compile-time type of the {@code type} class argument.
177     * @param type     the factory interface for which an implementation is specified.
178     * @param factory  the implementations to use for the given interface.
179     */
180    @SafeVarargs
181    public static <T extends Factory> void setFactories(final Class<T> type, final T... factory) {
182        Objects.requireNonNull(type, "Given 'type' can not be null");
183        final Iterable<? extends Factory> list = Arrays.asList(factory.clone());
184        synchronized (TestCase.FACTORIES) {
185            TestCase.FACTORIES.put(type, list);
186        }
187    }
188
189    /**
190     * Returns the factory implementations explicitely given by the last call to
191     * {@link #setFactories(Class, Factory[])} for the given interface.
192     * This method does not scan the {@code META-INF/services/<T>} entries.
193     *
194     * @param <T>   the compile-time type of the {@code type} class argument.
195     * @param type  the factory interface for which an implementations is desired.
196     * @return      the implementations for the given interface, or {@code null} if none.
197     */
198    @SuppressWarnings("unchecked")
199    public static <T extends Factory> T[] getFactories(final Class<T> type) {
200        Objects.requireNonNull(type, "Given 'type' can not be null");
201        final Iterable<? extends Factory> factories;
202        synchronized (TestCase.FACTORIES) {
203            factories = TestCase.FACTORIES.get(type);
204        }
205        if (factories instanceof Collection<?>) {
206            final Collection<? extends Factory> collection = (Collection<? extends Factory>) factories;
207            return collection.toArray((T[]) Array.newInstance(type, collection.size()));
208        }
209        return null;
210    }
211
212    /**
213     * Adds a listener to be informed every time a test begin or finish, either on success
214     * or failure. This method does not check if the given listener was already registered
215     * (i.e. the same listener may be added more than once).
216     *
217     * @param listener The listener to add. {@code null} values are silently ignored.
218     *
219     * @deprecated To be replaced by JUnit 5 listener mechanism.
220     */
221    @Deprecated
222    public static void addTestListener(final TestListener listener) {
223        TestCase.addTestListener(listener);
224    }
225
226    /**
227     * Removes a previously {@linkplain #addTestListener(TestListener) added} listener. If the
228     * given listener has been added more than once, then only the last occurrence is removed.
229     * If the given listener is not found, then this method does nothing.
230     *
231     * @param listener  the listener to remove. {@code null} values are silently ignored.
232     *
233     * @deprecated To be replaced by JUnit 5 listener mechanism.
234     */
235    @Deprecated
236    public static void removeTestListener(final TestListener listener) {
237        TestCase.removeTestListener(listener);
238    }
239
240    /**
241     * Clears all factories specified to the {@link #setFactories(Class, Factory[])} method, and clears
242     * all {@linkplain ServiceLoader service loader} caches. After this method call, all factories
243     * will be reloaded when first needed. This method is intended for use in situations in which
244     * new factories can be installed or removed into a running Java virtual machine.
245     *
246     * @see ServiceLoader#reload()
247     */
248    public static void clear() {
249        synchronized (TestCase.FACTORIES) {
250            ServiceLoader<?> services = TestCase.getFactoryFilter();
251            synchronized (services) {
252                services.reload();
253            }
254            services = TestCase.getImplementationDetails();
255            synchronized (services) {
256                services.reload();
257            }
258            final Iterator<Iterable<? extends Factory>> it = TestCase.FACTORIES.values().iterator();
259            while (it.hasNext()) {
260                final Iterable<? extends Factory> factories = it.next();
261                if (factories instanceof ServiceLoader<?>) {
262                    ((ServiceLoader<? extends Factory>) factories).reload();
263                } else {
264                    it.remove();
265                }
266            }
267        }
268    }
269}