/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.test;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.geoserver.data.test.TestData;
import org.geotools.factory.Hints;
import org.geotools.feature.NameImpl;
import org.geotools.referencing.CRS;
import org.geotools.util.logging.Logging;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.opengis.feature.type.Name;
/**
* Base test support class for GeoServer test cases.
* <h2>Test Setup Lifecycle</h2>
* <p>
* This class provides a number of hooks for subclasses that are called throughout the life cycle
* of the test. These include:
* <ul>
* <li>{@link #createTestData()} - The first subclass hook called to created the {@link TestData}
* for the test</li>
* <li>{@link #setUp(TestData)} - Called after the test data setup has been completed and provides
* subclass with a chance to any setup it requires</li>
* <li>{@link #tearDown(TestData)} - Called after the test has run and before the test setup
* tear down.</li>
* </ul>
* </p>
* <p>
* Additionally a test class may use the standard JUnit annotations such as {@link Before},
* {@link BeforeClass}, {@link After}, {@link AfterClass} to define additional life cycle setup
* and tear down methods. Generally these methods will execute after methods of the super class with
* the same annotation.
* </p>
* <h2>Test Setup Frequency</h2>
* <p>
* The {@link TestSetup} annotation is used to control the frequency at which the test setup will
* occur over the life of the test class. It controls whether the test setup is run repeatedly for
* each test method or once for the all the test methods of the class.
* </p>
* <p>
* The annotation is defined at the class level. For example, to define a single (one-time) setup:
* <code>
* <pre>
* {@literal @}TestSetup(run=TestSetupFrequency.ONCE)
* public class MyTest extends GeoServerBaseTestSupport {
* }
* </pre>
* </code>
*
* </p>
* @author Justin Deoliveira, OpenGeo
*
* @param <T>
*/
public abstract class GeoServerBaseTestSupport<T extends TestData> {
/**
* Common logger for test cases
*/
protected static final Logger LOGGER =
org.geotools.util.logging.Logging.getLogger("org.geoserver.test");
/**
* test data
*/
protected static TestData testData;
/**
* test instance, used to give subclass hooks for one time setup/teardown
*/
protected static GeoServerBaseTestSupport test;
/**
* Controls the frequency of the test setup
*/
protected static TestSetupFrequency testSetupFrequency = null;
// @Rule
// public TestWatcher watcher = new TestWatcher() {
// protected void finished(org.junit.runner.Description description) {
// System.out.println(description);
// };
// };
@Rule
public TestRule runSetup = new TestRule() {
@Override
public Statement apply(Statement base, Description description) {
if (description.getAnnotation(RunTestSetup.class) != null) {
try {
doTearDownClass();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return base;
}
};
/**
* Checks for existence of a system property named "quietTests".
*/
public static boolean isQuietTests() {
String quietTests = System.getProperty("quietTests");
return quietTests != null && !"false".equalsIgnoreCase(quietTests);
}
@BeforeClass
public final static void setUpLogging() throws Exception {
if (isQuietTests()) {
Logging.getLogger("org.geoserver").setLevel(Level.SEVERE);
Logging.getLogger("org.vfny.geoserver").setLevel(Level.SEVERE);
Logging.getLogger("org.geotools").setLevel(Level.SEVERE);
}
}
@BeforeClass
public final static void setUpReferencing() throws Exception {
// do we need to reset the referencing subsystem and reorient it with lon/lat order?
if (System.getProperty("org.geotools.referencing.forceXY") == null
|| !"http".equals(Hints.getSystemDefault(Hints.FORCE_AXIS_ORDER_HONORING))) {
System.setProperty("org.geotools.referencing.forceXY", "true");
Hints.putSystemDefault(Hints.FORCE_AXIS_ORDER_HONORING, "http");
CRS.reset("all");
}
}
@Before
public final void doSetup() throws Exception {
if (testData == null) {
test = this;
testData = createTestData();
testData.setUp();
setUp((T) testData);
}
}
protected T getTestData() {
return (T) testData;
}
/**
* Creates the {@link TestData} implementation for this test.
* <p>
* If the concrete {@link TestData} class provides any configurable options that control how
* its setup will operate they should be set/unset in this method before turning the new
* instance.
* </p>
*/
protected abstract T createTestData() throws Exception;
/**
* Subclass hook for set up before the test run.
* <p>
* This methods should be used for setup that occurs after the {@link TestData} instance has
* been setup.
* </p>
*/
protected void setUp(T testData) throws Exception {
}
@After
public final void doTearDown() throws Exception {
if (testSetupFrequency == null) {
testSetupFrequency = lookupTestSetupPolicy();
}
if (testSetupFrequency != TestSetupFrequency.ONCE) {
doTearDownClass();
}
}
private TestSetupFrequency lookupTestSetupPolicy() {
Class clazz = getClass();
while(clazz != null && !Object.class.equals(clazz)) {
TestSetup testSetup = (TestSetup) clazz.getAnnotation(TestSetup.class);
if (testSetup != null) {
return testSetup.run();
}
clazz = clazz.getSuperclass();
}
return TestSetupFrequency.REPEAT;
}
@AfterClass
public final static void doTearDownClass() throws Exception {
if (testData != null) {
try {
test.tearDown(testData);
testData.tearDown();
} finally {
// clean up the static variables anyways, otherwise a failure
// to tear down will pullute the test and test data used by subsequent tests
testData = null;
test = null;
}
}
}
@AfterClass
public static void clearTestSetupFrequency() {
testSetupFrequency = null;
}
/**
* Subclass hook for set up before the test run.
* <p>
* This methods should be used for setup that occurs after the {@link TestData} instance has
* been setup.
* </p>
*/
protected void tearDown(T testData) throws Exception {
}
//common convenience methods
/**
* Returns a qualified name into a string of the form "[<prefix>:]<localPart>".
*/
protected String toString(QName qName) {
if(qName.getPrefix() != null) {
return qName.getPrefix() + ":" + qName.getLocalPart();
}
else {
return qName.getLocalPart();
}
}
/**
* Returns a qualified name into a GeoTools type name.
*/
protected Name toName(QName qName) {
return qName.getNamespaceURI() != null ?
new NameImpl(qName.getNamespaceURI(), qName.getLocalPart()) : new NameImpl(qName.getLocalPart());
}
}