blob: 4293eb1409efac619bc649281cdf6a54d28b224a [file] [log] [blame]
/*
* 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.directory.server.core.integ.state;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import javax.naming.NamingException;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.core.integ.InheritableSettings;
import org.junit.internal.runners.MethodRoadie;
import org.junit.internal.runners.TestClass;
import org.junit.internal.runners.TestMethod;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The context for managing the state of an integration test service.
* Each thread of execution driving tests manages it's own service context.
* Hence parallelism can be achieved while running integration tests.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$, $Date$
*/
public class TestServiceContext
{
/** The logger */
private static final Logger LOG = LoggerFactory.getLogger( TestServiceContext.class );
/** The ThreadLocal containing the contexts */
private static final ThreadLocal<TestServiceContext> CONTEXTS = new ThreadLocal<TestServiceContext>();
/** The NonExistant state instance */
private final TestServiceState nonExistentState = new NonExistentState( this );
/** The StartedPristine state instance */
private final TestServiceState startedPristineState = new StartedPristineState( this );
/** The StartedNormal state instance */
private final TestServiceState startedNormalState = new StartedNormalState( this );
/** current service state with respect to the testing life cycle */
private TestServiceState state = nonExistentState;
/** the core directory service managed by this context */
private DirectoryService service;
/**
* A private constructor, the clas contains only static methods,
* no need to construct an instance.
*/
private TestServiceContext()
{
}
/**
* Gets the TestServiceContext associated with the current thread of
* execution. If one does not yet exist it will be created.
*
* @return the context associated with the calling thread
*/
public static TestServiceContext get()
{
TestServiceContext context = CONTEXTS.get();
if ( context == null )
{
context = new TestServiceContext();
CONTEXTS.set( context );
}
return context;
}
/**
* Sets the TestServiceContext for this current thread
*
* @param context the context associated with the calling thread
*/
public static void set( TestServiceContext context )
{
CONTEXTS.set( context );
}
/**
* Action where an attempt is made to create the service. Service
* creation in this system is the combined instantiation and
* configuration which takes place when the factory is used to get
* a new instance of the service.
*
* @param settings the settings for this test
* @throws NamingException if we can't create the service
*/
public static void create( InheritableSettings settings ) throws NamingException
{
get().state.create( settings );
}
/**
* Action where an attempt is made to destroy the service. This
* entails nulling out reference to it and triggering garbage
* collection.
*/
public static void destroy()
{
get().state.destroy();
}
/**
* Action where an attempt is made to erase the contents of the
* working directory used by the service for various files including
* partition database files.
*
* @throws IOException on errors while deleting the working directory
*/
public static void cleanup() throws IOException
{
get().state.cleanup();
}
/**
* Action where an attempt is made to start up the service.
*
* @throws NamingException on failures to start the core directory service
*/
public static void startup() throws NamingException
{
get().state.startup();
}
/**
* Action where an attempt is made to shutdown the service.
*
* @throws NamingException on failures to stop the core directory service
*/
public static void shutdown() throws NamingException
{
get().state.shutdown();
}
/**
* Action where an attempt is made to run a test against the service.
*
* @param testClass the class whose test method is to be run
* @param testMethod the test method which is to be run
* @param notifier a notifier to report failures to
* @param settings the inherited settings and annotations associated with
* the test method
*/
public static void test( TestClass testClass, TestMethod testMethod, RunNotifier notifier,
InheritableSettings settings )
{
LOG.debug( "calling test(): {}", settings.getDescription().getDisplayName() );
get().getState().test( testClass, testMethod, notifier, settings );
}
/**
* Action where an attempt is made to revert the service to it's
* initial start up state by using a previous snapshot.
*
* @throws NamingException on failures to revert the state of the core
* directory service
*/
public static void revert() throws NamingException
{
get().state.revert();
}
static void invokeTest( TestClass testClass, TestMethod testMethod, RunNotifier notifier, Description description )
{
try
{
Object test = testClass.getConstructor().newInstance();
Field field = testClass.getJavaClass().getDeclaredField( "service" );
field.set( testClass.getJavaClass(), get().getService() );
new MethodRoadie( test, testMethod, notifier, description ).run();
}
catch ( InvocationTargetException e )
{
LOG.error( "Failed to invoke test method: " + description.getDisplayName(), e.getCause() );
notifier.testAborted( description, e.getCause() );
return;
}
catch ( InstantiationException ie )
{
LOG.error( "Failed to invoke test method: " + description.getDisplayName(), ie );
notifier.testAborted( description, ie );
return;
}
catch ( IllegalAccessException iae )
{
LOG.error( "Failed to invoke test method: " + description.getDisplayName(), iae );
notifier.testAborted( description, iae );
return;
}
catch ( NoSuchMethodException nsme )
{
LOG.error( "Failed to invoke test method: " + description.getDisplayName(), nsme );
notifier.testAborted( description, nsme );
return;
}
catch ( NoSuchFieldException nsfe )
{
LOG.error( "Failed to invoke test method: " + description.getDisplayName(), nsfe );
notifier.testAborted( description, nsfe );
return;
}
}
// -----------------------------------------------------------------------
// Package Friendly Instance Methods
// -----------------------------------------------------------------------
void setState( TestServiceState state )
{
this.state = state;
}
TestServiceState getState()
{
return state;
}
TestServiceState getNonExistentState()
{
return nonExistentState;
}
TestServiceState getStartedPristineState()
{
return startedPristineState;
}
TestServiceState getStartedNormalState()
{
return startedNormalState;
}
DirectoryService getService()
{
return service;
}
void setService( DirectoryService service )
{
this.service = service;
}
}