You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by bl...@apache.org on 2003/06/06 22:33:15 UTC

cvs commit: avalon-excalibur/fortress/src/tools/org/apache/avalon/fortress/testcase FortressTestCase.java

bloritsch    2003/06/06 13:33:15

  Added:       fortress/src/tools/org/apache/avalon/fortress/testcase
                        FortressTestCase.java
  Log:
  applypatch 20564
  
  Revision  Changes    Path
  1.1                  avalon-excalibur/fortress/src/tools/org/apache/avalon/fortress/testcase/FortressTestCase.java
  
  Index: FortressTestCase.java
  ===================================================================
  /*
  
   ============================================================================
  				   The Apache Software License, Version 1.1
   ============================================================================
   
   Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
   
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
   
   1. Redistributions of  source code must  retain the above copyright  notice,
  	this list of conditions and the following disclaimer.
   
   2. Redistributions in binary form must reproduce the above copyright notice,
  	this list of conditions and the following disclaimer in the documentation
  	and/or other materials provided with the distribution.
   
   3. The end-user documentation included with the redistribution, if any, must
  	include  the following  acknowledgment:  "This product includes  software
  	developed  by the  Apache Software Foundation  (http://www.apache.org/)."
  	Alternately, this  acknowledgment may  appear in the software itself,  if
  	and wherever such third-party acknowledgments normally appear.
   
   4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation"  
  	must not be used to endorse or promote products derived from this  software 
  	without  prior written permission. For written permission, please contact 
  	apache@apache.org.
   
   5. Products  derived from this software may not  be called "Apache", nor may
  	"Apache" appear  in their name,  without prior written permission  of the
  	Apache Software Foundation.
   
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation. For more  information on the 
   Apache Software Foundation, please see <http://www.apache.org/>.
   
  */
  package org.apache.avalon.fortress.testcase;
  
  import java.io.InputStream;
  import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Iterator;
  
  import org.apache.avalon.excalibur.logger.LoggerManager;
  import org.apache.avalon.fortress.impl.DefaultContainer;
  import org.apache.avalon.fortress.impl.DefaultContainerManager;
  import org.apache.avalon.fortress.util.FortressConfig;
  import org.apache.avalon.fortress.util.OverridableContext;
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
  import org.apache.avalon.framework.container.ContainerUtil;
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.context.DefaultContext;
  import org.apache.avalon.framework.logger.Logger;
  import org.apache.avalon.framework.logger.LogKitLogger;
  import org.apache.avalon.framework.service.ServiceException;
  import org.apache.avalon.framework.service.ServiceManager;
  
  import junit.framework.AssertionFailedError;
  import junit.framework.TestCase;
  import junit.framework.TestResult;
  
  import org.apache.log.Hierarchy;
  import org.apache.log.LogTarget;
  import org.apache.log.Priority;
  import org.apache.log.format.PatternFormatter;
  import org.apache.log.output.io.StreamTarget;
  
  /**
   * JUnit TestCase for Avalon Components in Fortress.
   * <p>
   *   This class extends the JUnit TestCase class to setup an environment which
   *   makes it possible to easily test Avalon Components. The following methods
   *   and instance variables are exposed for convenience testing:
   * </p>
   * <dl>
   *   <dt>m_serviceManager</dt>
   *   <dd>
   *     This instance variable contains an initialized ServiceLocator which
   *     can be used to lookup Components configured in the test configuration
   *     file. (see below)
   *   </dd>
   *   <dt>getLogger()</dt>
   *   <dd>
   *     This method returns the default logger for this test case
   *   </dd>
   * </dl>
   * <p>
   *   The following test case configuration can be used as a basis for new tests.
   *   Detailed are explanations of the configuration elements can be found after
   *   the example.  The example will log all logger output to the console and to
   *   a log file.
   * </p>
   * <pre>
   *   &lt;testcase&gt;
   *     &lt;annotation&gt;
   *       &lt;![CDATA[
   *         &lt;title&gt;{Name of test}&lt;/title&gt;
   *         &lt;para&gt;
   *           {Description of test}
   *           The configuration is specified in the file located in
   *           &lt;parameter&gt;avalon-excalibur/src/test/{path and name of conf file}.xtext&lt;/parameter&gt;.
   *         &lt;/para&gt;
   *       ]]&gt;
   *     &lt;/annotation&gt;
   *
   *     &lt;logger log-level="INFO"&gt;
   *       &lt;factories&gt;
   *         &lt;factory type="stream" class="org.apache.avalon.excalibur.logger.factory.StreamTargetFactory"/&gt;
   *         &lt;factory type="file" class="org.apache.avalon.excalibur.logger.factory.FileTargetFactory"/&gt;
   *       &lt;/factories&gt;
   *
   *       &lt;targets&gt;
   *         &lt;stream id="console"&gt;
   *           &lt;stream&gt;System.out&lt;/stream&gt;
   *           &lt;format type="avalon"&gt;
   *             %7.7{priority} %23.23{time:yyyy-MM-dd' 'HH:mm:ss.SSS} [%30.30{category}] (%{context}): %{message}\n%{throwable}
   *           &lt;/format&gt;
   *         &lt;/stream&gt;
   *         &lt;file id="log-file"&gt;
   *           &lt;filename&gt;TEST-{full test class name}.log&lt;/filename&gt;
   *           &lt;format type="avalon"&gt;
   *             %7.7{priority} %23.23{time:yyyy-MM-dd' 'HH:mm:ss.SSS} [%30.30{category}] (%{context}): %{message}\n%{throwable}
   *           &lt;/format&gt;
   *         &lt;/file&gt;
   *       &lt;/targets&gt;
   *
   *       &lt;categories&gt;
   *         &lt;category name="test" log-level="INFO"&gt;
   *           &lt;log-target id-ref="console"/&gt;
   *           &lt;log-target id-ref="log-file"/&gt;
   *         &lt;/category&gt;
   *         &lt;category name="jdbc" log-level="INFO"&gt;
   *           &lt;log-target id-ref="console"/&gt;
   *           &lt;log-target id-ref="log-file"/&gt;
   *         &lt;/category&gt;
   *       &lt;/categories&gt;
   *     &lt;/logger&gt;
   *
   *     &lt;context&gt;
   *       &lt;entry name="foo" value="bar"/&gt;
   *       &lt;entry name="baz" class="my.context.Class"/&gt;
   *     &lt;/context&gt;
   *
   *     &lt;roles&gt;
   *       &lt;role name="org.apache.avalon.excalibur.datasource.DataSourceComponent"
   *             &lt;component shorthand="jdbc"
   *             class="org.apache.avalon.excalibur.datasource.JdbcDataSource"
   *             handler="org.apache.avalon.fortress.impl.handler.ThreadSafeComponentHandler"/&gt;
   *       &lt;/role&gt;
   *     &lt;/roles&gt;
   *
   *     &lt;components&gt;
   *         &lt;jdbc name="personell" logger="jdbc"&gt;
   *           &lt;pool-controller min="5" max="10"/&gt;
   *           &lt;jdbc name="personnel"/&gt;
   *           &lt;dburl&gt;jdbc:odbc:test&lt;/dburl&gt;
   *           &lt;user&gt;test&lt;/user&gt;
   *           &lt;password&gt;test&lt;/password&gt;
   *           &lt;driver&gt;sun.jdbc.odbc.JdbcOdbcDriver&lt;/driver&gt;
   *         &lt;/jdbc&gt;
   *     &lt;/components&gt;
   *   &lt;/testcase&gt;
   * </pre>
   * <p>
   * Element Explanation:
   * <dl>
   * <dt>testcase</dt>
   * <dd>Defines a test case configuration.  Must contain one each of the
   *  following elements: <code>annotation</code>, <code>logger</code>,
   *  <code>context</code>, <code>roles</code>, and <code>components</code>
   *  </dd>.
   *
   * <dt>annotation</dt>
   * <dd>Defines a test annotation.  This element should define a block of
   *  XML enclosed within a CDATA element.  The XML should be made up of a
   *  <code>title</code> element, naming the test, and a <code>para</code>
   *  element which is used to describe the test.</dd>
   *
   * <dt>logger</dt>
   * <dd>Configures the logger used by the test cases and the components used
   *  by the tests.  The <code>logger</code> element takes two optional
   *  attributes:
   *      <dl>
   *      <dt>logger</dt><dd>Uses to name the logger which is used to bootstrap
   *       the LogKit logger.  (Defaults to <code>"lm"</code>)</dd>
   *      <dt>log-level</dt><dd>Because the logger used by the LogKit must be
   *       created before the Log Kit Manager is initialized, it must be fully
   *       configured before the <code>logger</code> element is parsed.  This
   *       attribute allows the Log Kit's log priority to be set.  This log
   *       level will also become the default for the Role Manager, Service
   *       Manager, and all components if they do not have <code>category</code>
   *       elements declated in the <code>logger</code> element.
   *       (Defaults to "INFO")</dd>
   *      </dl>
   *  The loggers used by test cases and components can be easily configured
   *  from within this file.  The default test configuration, shown above,
   *  includes a "test" category.  This category is used to configure the
   *  default logger for all test cases.  If it is set to "DEBUG", then all
   *  test debug logging will be enabled.  To enalble debug logging for a
   *  single test case, a child category must be defined for the
   *  "testCheckTotals" test case as follows:
   *  <pre>
   *       &lt;categories&gt;
   *         &lt;category name="test" log-level="INFO"&gt;
   *           &lt;log-target id-ref="console"/&gt;
   *           &lt;log-target id-ref="log-file"/&gt;
   *
   *           &lt;category name="testCheckTotals" log-level="DEBUG"&gt;
   *             &lt;log-target id-ref="console"/&gt;
   *             &lt;log-target id-ref="log-file"/&gt;
   *           &lt;/category&gt;
   *         &lt;/category&gt;
   *       &lt;/categories&gt;
   *  </pre>
   *  For general information on how to configure the Logger Manager, please
   *  refer to the Log Kit documentation.
   * </dd>
   *
   * <dt>context</dt>
   * <dd>Allows context properties to be set in the context passed to any
   *  Contextualizable components.</dd>
   *
   * <dt>roles</dt>
   * <dd>Roles configuration for the Components configured in the
   *  <code>components</code> element.  The logger used by the RoleManager
   *  can be configured using a <code>logger</code> attribute, which defaults
   *  to "rm".  By default this logger will have the same log level and
   *  formatting as the LogKit logger.  It can be configured by adding a
   *  <code>category</code> within the <code>logger</code> element.</dd>
   *
   * <dt>components</dt>
   * <dd>Used to configure any Components used by the test cases.  The logger
   *  used by the ServiceLocator can be configured using a <code>logger</code>
   *  attribute, which defaults to "cm".  By default this logger will have the
   *  same log level and formatting as the LogKit logger.  It can be configured
   *  by adding a <code>category</code> within the <code>logger</code> element.
   *  </dd>
   *
   * </dl>
   *
   * @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
   * @author unico
   */
  public class FortressTestCase extends TestCase
  {
  
  	///Format of default formatter
  	private static final String FORMAT =
  		"%7.7{priority} %23.23{time:yyyy-MM-dd' 'HH:mm:ss.SSS} [%30.30{category}] " +
  		"(%{context}): %{message}\n%{throwable}";
  
  
  	//The default logger
  	private Logger m_logger;
  	
  	// The container manager
  	private DefaultContainerManager m_containerManager;
  	
  	// The container itself
  	private DefaultContainer m_container;
  	
  	private LoggerManager m_loggerManager;
  	private ServiceManager m_serviceManager;
  	
  
  	private static HashMap m_tests = new HashMap();
  	
  	
      public FortressTestCase( final String name )
      {
          super( name );
  
  		ArrayList methodList = (ArrayList)FortressTestCase.m_tests.get( getClass() );
  
  		Method[] methods = getClass().getMethods();
  
  		if( null == methodList )
  		{
  			methodList = new ArrayList( methods.length );
  
  			for( int i = 0; i < methods.length; i++ )
  			{
  				String methodName = methods[ i ].getName();
  				if( methodName.startsWith( "test" ) &&
  					( Modifier.isPublic( methods[ i ].getModifiers() ) ) &&
  					( methods[ i ].getReturnType().equals( Void.TYPE ) ) &&
  					( methods[ i ].getParameterTypes().length == 0 ) )
  				{
  					methodList.add( methodName );
  				}
  			}
  
  			FortressTestCase.m_tests.put( getClass(), methodList );
  		}
      }
      
      protected final boolean hasService( final String key )
      {
      	return m_serviceManager.hasService( key );
      }
      
  	protected final Object lookup( final String key )
  		throws ServiceException
  	{
  		return m_serviceManager.lookup( key );
  	}
  
  	protected final void release( final Object object )
  	{
  		m_serviceManager.release( object );
  	}
  
  	/** Return the logger */
  	protected Logger getLogger()
  	{
  		return m_logger;
  	}
  
      
  	/**
  	 * Override <code>run</code> so that we can have code that is run once.
  	 */
  	public final void run( TestResult result )
  	{
  		ArrayList methodList = (ArrayList)FortressTestCase.m_tests.get( getClass() );
  
  		if( null == methodList || methodList.isEmpty() )
  		{
  			return; // The test was already run!  NOTE: this is a hack.
  		}
  
  		// Set the logger for the initialization phase.
  		setCurrentLogger( getBaseClassName( getClass() ) );
  
  		try
  		{
  			
  			prepare();
  			
  			if( this instanceof Initializable )
  			{
  				( (Initializable)this ).initialize();
  			}
  
  			Iterator tests = methodList.iterator();
  
  			while( tests.hasNext() )
  			{
  				String methodName = (String)tests.next();
  				setName( methodName );
  				setCurrentLogger( methodName );
  
  				if( getLogger().isDebugEnabled() )
  				{
  					getLogger().debug( "" );
  					getLogger().debug( "========================================" );
  					getLogger().debug( "  begin test: " + methodName );
  					getLogger().debug( "========================================" );
  				}
  
  				super.run( result );
  
  				if( getLogger().isDebugEnabled() )
  				{
  					getLogger().debug( "========================================" );
  					getLogger().debug( "  end test: " + methodName );
  					getLogger().debug( "========================================" );
  					getLogger().debug( "" );
  				}
  			}
  
  		}
  		catch( Exception e )
  		{
  			System.out.println( e );
  			e.printStackTrace();
  			result.addError( this, e );
  		}
  		finally
  		{
  			done();
  
  			if( this instanceof Disposable )
  			{
  				try
  				{
  					( (Disposable)this ).dispose();
  				}
  				catch( Exception e )
  				{
  					result.addFailure( this, new AssertionFailedError( "Disposal Error" ) );
  				}
  			}
  		}
  
  		methodList.clear();
  		FortressTestCase.m_tests.put( getClass(), methodList );
  	}
  
  	/**
  	 * Initializes Fortress.
  	 *
  	 * The configuration file is determined by the class name plus .xtest appended,
  	 * all '.' replaced by '/' and loaded as a resource via classpath
  	 */
  	protected void prepare() throws Exception
  	{
  		setCurrentLogger( "prepare" );
  		
  		final String resourceName = getClass().getName().replace( '.', '/' ) + ".xtest";
  		URL resource = getClass().getClassLoader().getResource( resourceName );
  		
  		if( resource != null )
  		{
  			getLogger().debug( "Loading resource " + resourceName );
  			prepare( resource.openStream() );
  		}
  		else
  		{
  			getLogger().warn( "Resource not found " + resourceName );
  		}		
  	}
  
  	/**
  	 * Initializes Fortress.
  	 *
  	 * @param testconf The configuration file is passed as an <code>InputStream</code>
  	 *
  	 * A common way to supply a InputStream is to overide the prepare() method
  	 * in the sub class, do there whatever is needed to get the right InputStream object
  	 * supplying a conformant xtest configuartion and pass it to this prepare method.
  	 * the mentioned initialize method is also the place to set a different logging priority
  	 * to the member variable m_logPriority.
  	 */
  	protected final void prepare( final InputStream testconf )
  		throws Exception
  	{
  		getLogger().debug( "FortressTestCase.initialize" );
  
  		final DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
  		final Configuration conf = builder.build( testconf );
  
  		String annotation = conf.getChild( "annotation" ).getValue( null );
  
  		if( ( null != annotation ) && !( "".equals( annotation ) ) )
  		{
  			m_logger.info( annotation );
  		}
  
  		final FortressConfig config = new FortressConfig();
  		config.setContainerClass( DefaultContainer.class );
  
  		config.setContextDirectory( "./" );
  		config.setWorkDirectory( "./" );
  		config.setLoggerCategory( "fortress" );
  		
  		config.setLoggerManagerConfiguration( conf.getChild( "logger" ) );
  		config.setRoleManagerConfiguration( conf.getChild( "roles" ) );
  		config.setContainerConfiguration( conf.getChild( "components" ) );
  		
  		m_containerManager = new DefaultContainerManager( 
  			setupContext( conf.getChild( "context", true ), config.getContext() ) );
  		ContainerUtil.initialize( m_containerManager );
  		
  		m_container = (DefaultContainer) m_containerManager.getContainer();
  		m_serviceManager = m_container.getServiceManager();
  		m_loggerManager = (LoggerManager) m_serviceManager.lookup( LoggerManager.class.getName() );
  	}
  
  	/**
  	 * Sets the logger which will be returned by getLogger and getLogEnabledLogger
  	 */
  	private void setCurrentLogger( String name )
  	{
  		if( m_loggerManager == null )
  		{
  			org.apache.log.Logger logger;
  			
  			// Logger for the portion of the configuration has been loaded.
  			logger = Hierarchy.getDefaultHierarchy().getLoggerFor( name );
  			logger.setPriority( Priority.INFO );
  
  			PatternFormatter formatter = new PatternFormatter( FORMAT );
  			StreamTarget target = new StreamTarget( System.out, formatter );
  			logger.setLogTargets( new LogTarget[]{target} );
  			m_logger = new LogKitLogger( logger );
  		}
  		else
  		{
  			m_logger = m_loggerManager.getLoggerForCategory( "test." + name );
  		}
  	}
  
  	/**
  	 * Set up a context according to the xtest configuration specifications context
  	 * element.
  	 *
  	 * A method addContext(DefaultContext context) is called here to enable subclasses
  	 * to put additional objects into the context programmatically.
  	 */
  	private Context setupContext( final Configuration conf, final Context parentContext )
  		throws Exception
  	{
  		//FIXME(GP): This method should setup the Context object according to the
  		//           configuration spec.
  		final DefaultContext context = new OverridableContext( parentContext );
  		final Configuration[] confs = conf.getChildren( "entry" );
  		for( int i = 0; i < confs.length; i++ )
  		{
  			final String key = confs[ i ].getAttribute( "name" );
  			final String value = confs[ i ].getAttribute( "value", null );
  			if( value == null )
  			{
  				String clazz = confs[ i ].getAttribute( "class" );
  				Object obj = getClass().getClassLoader().loadClass( clazz ).newInstance();
  				context.put( key, obj );
  				if( getLogger().isInfoEnabled() )
  				{
  					getLogger().info( "FortressTestCase: added an instance of class " 
  						+ clazz + " to context entry " + key );
  				}
  
  			}
  			else
  			{
  				context.put( key, value );
  				if( getLogger().isInfoEnabled() )
  				{
  					getLogger().info( "FortressTestCase: added value \"" + value 
  						+ "\" to context entry " + key );
  				}
  
  			}
  		}
  		addContext( context );
  		context.makeReadOnly();
  		return context;
  	}
  
  	/**
  	 * This method may be overwritten by subclasses to put additional objects
  	 * into the context programmatically.
  	 */
  	protected void addContext( DefaultContext context )
  	{
  	}
  
  
  	/**
  	 * Exctract the base class name of a class.
  	 */
  	private String getBaseClassName( Class clazz )
  	{
  		String name = clazz.getName();
  		int pos = name.lastIndexOf( '.' );
  		if( pos >= 0 )
  		{
  			name = name.substring( pos + 1 );
  		}
  		return name;
  	}
  
  
  	/**
  	 * Disposes Fortress
  	 */
  	private void done()
  	{
  		ContainerUtil.dispose( m_containerManager );
  	}
  
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: cvs-unsubscribe@avalon.apache.org
For additional commands, e-mail: cvs-help@avalon.apache.org