/* * 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(); } }