/*
* Copyright 2008 Alin Dreghiciu.
*
* Licensed 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.ops4j.pax.exam.junit.internal;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.internal.runners.TestClass;
import org.junit.internal.runners.TestMethod;
import static org.ops4j.lang.NullArgumentException.*;
import static org.ops4j.pax.exam.Constants.*;
import org.ops4j.pax.exam.Info;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.OptionUtils;
import org.ops4j.pax.exam.junit.extender.CallableTestMethod;
import org.ops4j.pax.exam.junit.extender.Constants;
import org.ops4j.pax.exam.options.FrameworkOption;
import org.ops4j.pax.exam.runtime.PaxExamRuntime;
import org.ops4j.pax.exam.spi.container.TestContainer;
import org.ops4j.pax.exam.spi.container.TestContainerFactory;
/**
* A {@link TestMethod} that upon invokation starts a {@link TestContainer} and executes the test in the test container.
*
* @author Alin Dreghiciu (adreghiciu@gmail.com)
* @since 0.3.0 December 16, 2008
*/
public class JUnit4TestMethod
extends TestMethod
{
/**
* Test flow not yet started.
*/
private static final int NOT_STARTED = 0;
/**
* Test container started.
*/
private static final int CONTAINER_STARTED = 1;
/**
* Test bundle installed.
*/
private static final int PROBE_INSTALLED = 2;
/**
* Test bundle started.
*/
private static final int PROBE_STARTED = 3;
/**
* Test flow ended
*/
private static final int SUCCESFUL = 4;
/**
* JCL logger.
*/
private static final Log LOG = LogFactory.getLog( JUnit4TestMethod.class );
/**
* Test method. Cannot reuse the one from super class as it is not public.
*/
private final Method m_testMethod;
/**
* Configuration options.
*/
private final Option[] m_options;
/**
* Configuration method name (test method name and eventual the framework and framework version)
*/
private final String m_name;
/**
* Test bundle URL.
*/
private final String m_testBundleUrl;
/**
* Constructor.
*
* @param testMethod test method (cannot be null)
* @param testClass test class (cannot be null)
* @param frameworkOption framework option (on which framework the test method should be run) (can be null = default
* framework)
* @param userOptions user options (can be null)
*/
public JUnit4TestMethod( final Method testMethod,
final TestClass testClass,
final FrameworkOption frameworkOption,
final Option... userOptions )
{
super( testMethod, testClass );
validateNotNull( testMethod, "Test method" );
validateNotNull( testClass, "Test class" );
m_testMethod = testMethod;
m_options = OptionUtils.combine( userOptions, frameworkOption );
m_name = calculateName( testMethod.getName(), frameworkOption );
m_testBundleUrl = getTestBundleUrl( testClass.getName(), m_testMethod.getName() );
}
/**
* {@inheritDoc} Starts the test container, installs the test bundle and executes the test within the container.
*/
@Override
public void invoke( Object test )
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
{
Info.showLogo();
final String fullTestName = m_name + "(" + m_testMethod.getDeclaringClass().getName() + ")";
LOG.info( "Starting test " + fullTestName );
int executionState = NOT_STARTED;
final TestContainerFactory containerFactory = PaxExamRuntime.getTestContainerFactory();
TestContainer container = null;
try
{
LOG.trace( "Start test container" );
container = containerFactory.newInstance( m_options );
container.start();
executionState = CONTAINER_STARTED;
LOG.trace( "Install and start test bundle" );
final long bundleId = container.installBundle( m_testBundleUrl );
executionState = PROBE_INSTALLED;
container.setBundleStartLevel( bundleId, START_LEVEL_TEST_BUNDLE );
container.startBundle( bundleId );
executionState = PROBE_STARTED;
LOG.trace( "Execute test [" + m_name + "]" );
final CallableTestMethod callable = container.getService( CallableTestMethod.class );
try
{
LOG.info( "Starting test " + fullTestName );
callable.call();
LOG.info( "Test " + fullTestName + " ended succesfully" );
executionState = SUCCESFUL;
}
catch ( InstantiationException e )
{
throw new InvocationTargetException( e );
}
catch ( ClassNotFoundException e )
{
throw new InvocationTargetException( e );
}
}
finally
{
if ( container != null )
{
// Leave handling of proper stop to container implementation
try
{
container.stop();
}
catch ( RuntimeException ignore )
{
if ( executionState >= SUCCESFUL )
{
// throw catched exception if the test already was successful
// noinspection ThrowFromFinallyBlock
throw ignore;
}
else
{
// Do not throw an exception that could occur during stopping the container in case that an
// exception was already being thrown
LOG.error( "Cannot stop the test container: " + ignore.getMessage() );
}
}
}
}
}
/**
* Getter.
*
* @return test method
*/
public Method getTestMethod()
{
return m_testMethod;
}
/**
* Getter.
*
* @return test method name
*/
public String getName()
{
return m_name;
}
/**
* Computes the test method name out of test method name, framework and framework version.
*
* @param testMethodName test method name
* @param frameworkOption framework option
* @return test method name
*/
private static String calculateName( final String testMethodName,
final FrameworkOption frameworkOption )
{
final StringBuilder name = new StringBuilder();
name.append( testMethodName );
if ( frameworkOption != null )
{
name.append( " [" ).append( frameworkOption.getName() );
final String version = frameworkOption.getVersion();
if ( version != null )
{
name.append( "/" ).append( version );
}
name.append( "]" );
}
return name.toString();
}
/**
* Returns the test bundle url using an Pax URL Dir url.
*
* @param testClassName test class name
* @param testMethodName test method name
* @return test bundle url
*/
private static String getTestBundleUrl( final String testClassName,
final String testMethodName )
{
final StringBuilder url = new StringBuilder();
url.append( "dir:" )
.append( new File( "." ).getAbsolutePath() )
.append( "$" )
.append( "tail=" ).append( testClassName.replace( ".", "/" ) ).append( ".class" )
.append( "&" )
.append( Constants.PROBE_TEST_CLASS ).append( "=" ).append( testClassName )
.append( "&" )
.append( Constants.PROBE_TEST_METHOD ).append( "=" ).append( testMethodName )
.append( "&" )
.append( org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME )
.append( "=" )
.append( Constants.PROBE_SYMBOLICNAME )
.append( "&" )
.append( org.osgi.framework.Constants.DYNAMICIMPORT_PACKAGE )
.append( "=*" )
.append( "&" )
.append( org.osgi.framework.Constants.EXPORT_PACKAGE )
.append( "=!*" );
return url.toString();
}
}