KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > fortress > testcase > FortressTestCase


1 /*
2  * Copyright 2003-2004 The Apache Software Foundation
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  *
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.avalon.fortress.testcase;
19
20 import java.io.InputStream JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.lang.reflect.Modifier JavaDoc;
23 import java.net.URL JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.Iterator JavaDoc;
27
28 import org.apache.avalon.excalibur.logger.LoggerManager;
29 import org.apache.avalon.fortress.impl.DefaultContainer;
30 import org.apache.avalon.fortress.impl.DefaultContainerManager;
31 import org.apache.avalon.fortress.util.FortressConfig;
32 import org.apache.avalon.fortress.util.OverridableContext;
33 import org.apache.avalon.framework.activity.Disposable;
34 import org.apache.avalon.framework.activity.Initializable;
35 import org.apache.avalon.framework.configuration.Configuration;
36 import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
37 import org.apache.avalon.framework.container.ContainerUtil;
38 import org.apache.avalon.framework.context.Context;
39 import org.apache.avalon.framework.context.DefaultContext;
40 import org.apache.avalon.framework.logger.Logger;
41 import org.apache.avalon.framework.logger.LogKitLogger;
42 import org.apache.avalon.framework.service.ServiceException;
43 import org.apache.avalon.framework.service.ServiceManager;
44
45 import junit.framework.AssertionFailedError;
46 import junit.framework.TestCase;
47 import junit.framework.TestResult;
48
49 import org.apache.log.Hierarchy;
50 import org.apache.log.LogTarget;
51 import org.apache.log.Priority;
52 import org.apache.log.format.PatternFormatter;
53 import org.apache.log.output.io.StreamTarget;
54
55 /**
56  * JUnit TestCase for Avalon Components in Fortress.
57  * <p>
58  * This class extends the JUnit TestCase class to setup an environment which
59  * makes it possible to easily test Avalon Components. The following methods
60  * and instance variables are exposed for convenience testing:
61  * </p>
62  * <dl>
63  * <dt>m_serviceManager</dt>
64  * <dd>
65  * This instance variable contains an initialized ServiceLocator which
66  * can be used to lookup Components configured in the test configuration
67  * file. (see below)
68  * </dd>
69  * <dt>getLogger()</dt>
70  * <dd>
71  * This method returns the default logger for this test case
72  * </dd>
73  * </dl>
74  * <p>
75  * The following test case configuration can be used as a basis for new tests.
76  * Detailed are explanations of the configuration elements can be found after
77  * the example. The example will log all logger output to the console and to
78  * a log file.
79  * </p>
80  * <pre>
81  * &lt;testcase&gt;
82  * &lt;annotation&gt;
83  * &lt;![CDATA[
84  * &lt;title&gt;{Name of test}&lt;/title&gt;
85  * &lt;para&gt;
86  * {Description of test}
87  * The configuration is specified in the file located in
88  * &lt;parameter&gt;avalon-excalibur/src/test/{path and name of conf file}.xtext&lt;/parameter&gt;.
89  * &lt;/para&gt;
90  * ]]&gt;
91  * &lt;/annotation&gt;
92  *
93  * &lt;logger log-level="INFO"&gt;
94  * &lt;factories&gt;
95  * &lt;factory type="stream" class="org.apache.avalon.excalibur.logger.factory.StreamTargetFactory"/&gt;
96  * &lt;factory type="file" class="org.apache.avalon.excalibur.logger.factory.FileTargetFactory"/&gt;
97  * &lt;/factories&gt;
98  *
99  * &lt;targets&gt;
100  * &lt;stream id="console"&gt;
101  * &lt;stream&gt;System.out&lt;/stream&gt;
102  * &lt;format type="avalon"&gt;
103  * %7.7{priority} %23.23{time:yyyy-MM-dd' 'HH:mm:ss.SSS} [%30.30{category}] (%{context}): %{message}\n%{throwable}
104  * &lt;/format&gt;
105  * &lt;/stream&gt;
106  * &lt;file id="log-file"&gt;
107  * &lt;filename&gt;TEST-{full test class name}.log&lt;/filename&gt;
108  * &lt;format type="avalon"&gt;
109  * %7.7{priority} %23.23{time:yyyy-MM-dd' 'HH:mm:ss.SSS} [%30.30{category}] (%{context}): %{message}\n%{throwable}
110  * &lt;/format&gt;
111  * &lt;/file&gt;
112  * &lt;/targets&gt;
113  *
114  * &lt;categories&gt;
115  * &lt;category name="test" log-level="INFO"&gt;
116  * &lt;log-target id-ref="console"/&gt;
117  * &lt;log-target id-ref="log-file"/&gt;
118  * &lt;/category&gt;
119  * &lt;category name="jdbc" log-level="INFO"&gt;
120  * &lt;log-target id-ref="console"/&gt;
121  * &lt;log-target id-ref="log-file"/&gt;
122  * &lt;/category&gt;
123  * &lt;/categories&gt;
124  * &lt;/logger&gt;
125  *
126  * &lt;context&gt;
127  * &lt;entry name="foo" value="bar"/&gt;
128  * &lt;entry name="baz" class="my.context.Class"/&gt;
129  * &lt;/context&gt;
130  *
131  * &lt;roles&gt;
132  * &lt;role name="org.apache.avalon.excalibur.datasource.DataSourceComponent"
133  * &lt;component shorthand="jdbc"
134  * class="org.apache.avalon.excalibur.datasource.JdbcDataSource"
135  * handler="org.apache.avalon.fortress.impl.handler.ThreadSafeComponentHandler"/&gt;
136  * &lt;/role&gt;
137  * &lt;/roles&gt;
138  *
139  * &lt;components&gt;
140  * &lt;jdbc name="personell" logger="jdbc"&gt;
141  * &lt;pool-controller min="5" max="10"/&gt;
142  * &lt;jdbc name="personnel"/&gt;
143  * &lt;dburl&gt;jdbc:odbc:test&lt;/dburl&gt;
144  * &lt;user&gt;test&lt;/user&gt;
145  * &lt;password&gt;test&lt;/password&gt;
146  * &lt;driver&gt;sun.jdbc.odbc.JdbcOdbcDriver&lt;/driver&gt;
147  * &lt;/jdbc&gt;
148  * &lt;/components&gt;
149  * &lt;/testcase&gt;
150  * </pre>
151  * <p>
152  * Element Explanation:
153  * <dl>
154  * <dt>testcase</dt>
155  * <dd>Defines a test case configuration. Must contain one each of the
156  * following elements: <code>annotation</code>, <code>logger</code>,
157  * <code>context</code>, <code>roles</code>, and <code>components</code>
158  * </dd>.
159  *
160  * <dt>annotation</dt>
161  * <dd>Defines a test annotation. This element should define a block of
162  * XML enclosed within a CDATA element. The XML should be made up of a
163  * <code>title</code> element, naming the test, and a <code>para</code>
164  * element which is used to describe the test.</dd>
165  *
166  * <dt>logger</dt>
167  * <dd>Configures the logger used by the test cases and the components used
168  * by the tests. The <code>logger</code> element takes two optional
169  * attributes:
170  * <dl>
171  * <dt>logger</dt><dd>Uses to name the logger which is used to bootstrap
172  * the LogKit logger. (Defaults to <code>"lm"</code>)</dd>
173  * <dt>log-level</dt><dd>Because the logger used by the LogKit must be
174  * created before the Log Kit Manager is initialized, it must be fully
175  * configured before the <code>logger</code> element is parsed. This
176  * attribute allows the Log Kit's log priority to be set. This log
177  * level will also become the default for the Role Manager, Service
178  * Manager, and all components if they do not have <code>category</code>
179  * elements declated in the <code>logger</code> element.
180  * (Defaults to "INFO")</dd>
181  * </dl>
182  * The loggers used by test cases and components can be easily configured
183  * from within this file. The default test configuration, shown above,
184  * includes a "test" category. This category is used to configure the
185  * default logger for all test cases. If it is set to "DEBUG", then all
186  * test debug logging will be enabled. To enalble debug logging for a
187  * single test case, a child category must be defined for the
188  * "testCheckTotals" test case as follows:
189  * <pre>
190  * &lt;categories&gt;
191  * &lt;category name="test" log-level="INFO"&gt;
192  * &lt;log-target id-ref="console"/&gt;
193  * &lt;log-target id-ref="log-file"/&gt;
194  *
195  * &lt;category name="testCheckTotals" log-level="DEBUG"&gt;
196  * &lt;log-target id-ref="console"/&gt;
197  * &lt;log-target id-ref="log-file"/&gt;
198  * &lt;/category&gt;
199  * &lt;/category&gt;
200  * &lt;/categories&gt;
201  * </pre>
202  * For general information on how to configure the Logger Manager, please
203  * refer to the Log Kit documentation.
204  * </dd>
205  *
206  * <dt>context</dt>
207  * <dd>Allows context properties to be set in the context passed to any
208  * Contextualizable components.</dd>
209  *
210  * <dt>roles</dt>
211  * <dd>Roles configuration for the Components configured in the
212  * <code>components</code> element. The logger used by the RoleManager
213  * can be configured using a <code>logger</code> attribute, which defaults
214  * to "rm". By default this logger will have the same log level and
215  * formatting as the LogKit logger. It can be configured by adding a
216  * <code>category</code> within the <code>logger</code> element.</dd>
217  *
218  * <dt>components</dt>
219  * <dd>Used to configure any Components used by the test cases. The logger
220  * used by the ServiceLocator can be configured using a <code>logger</code>
221  * attribute, which defaults to "cm". By default this logger will have the
222  * same log level and formatting as the LogKit logger. It can be configured
223  * by adding a <code>category</code> within the <code>logger</code> element.
224  * </dd>
225  *
226  * </dl>
227  *
228  * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
229  */

230 public class FortressTestCase extends TestCase
231 {
232
233     ///Format of default formatter
234
private static final String JavaDoc FORMAT =
235         "%7.7{priority} %23.23{time:yyyy-MM-dd' 'HH:mm:ss.SSS} [%30.30{category}] " +
236         "(%{context}): %{message}\n%{throwable}";
237
238
239     //The default logger
240
private Logger m_logger;
241     
242     // The container manager
243
private DefaultContainerManager m_containerManager;
244     
245     // The container itself
246
private DefaultContainer m_container;
247     
248     private LoggerManager m_loggerManager;
249     private ServiceManager m_serviceManager;
250     
251
252     private static HashMap JavaDoc m_tests = new HashMap JavaDoc();
253     
254     
255     public FortressTestCase( final String JavaDoc name )
256     {
257         super( name );
258
259         ArrayList JavaDoc methodList = (ArrayList JavaDoc)FortressTestCase.m_tests.get( getClass() );
260
261         Method JavaDoc[] methods = getClass().getMethods();
262
263         if( null == methodList )
264         {
265             methodList = new ArrayList JavaDoc( methods.length );
266
267             for( int i = 0; i < methods.length; i++ )
268             {
269                 String JavaDoc methodName = methods[ i ].getName();
270                 if( methodName.startsWith( "test" ) &&
271                     ( Modifier.isPublic( methods[ i ].getModifiers() ) ) &&
272                     ( methods[ i ].getReturnType().equals( Void.TYPE ) ) &&
273                     ( methods[ i ].getParameterTypes().length == 0 ) )
274                 {
275                     methodList.add( methodName );
276                 }
277             }
278
279             FortressTestCase.m_tests.put( getClass(), methodList );
280         }
281     }
282     
283     protected final boolean hasService( final String JavaDoc key )
284     {
285         return m_serviceManager.hasService( key );
286     }
287     
288     protected final Object JavaDoc lookup( final String JavaDoc key )
289         throws ServiceException
290     {
291         return m_serviceManager.lookup( key );
292     }
293
294     protected final void release( final Object JavaDoc object )
295     {
296         m_serviceManager.release( object );
297     }
298
299     /** Return the logger */
300     protected Logger getLogger()
301     {
302         return m_logger;
303     }
304
305     
306     /**
307      * Override <code>run</code> so that we can have code that is run once.
308      */

309     public final void run( TestResult result )
310     {
311         ArrayList JavaDoc methodList = (ArrayList JavaDoc)FortressTestCase.m_tests.get( getClass() );
312
313         if( null == methodList || methodList.isEmpty() )
314         {
315             return; // The test was already run! NOTE: this is a hack.
316
}
317
318         // Set the logger for the initialization phase.
319
setCurrentLogger( getBaseClassName( getClass() ) );
320
321         try
322         {
323             
324             prepare();
325             
326             if( this instanceof Initializable )
327             {
328                 ( (Initializable)this ).initialize();
329             }
330
331             Iterator JavaDoc tests = methodList.iterator();
332
333             while( tests.hasNext() )
334             {
335                 String JavaDoc methodName = (String JavaDoc)tests.next();
336                 setName( methodName );
337                 setCurrentLogger( methodName );
338
339                 if( getLogger().isDebugEnabled() )
340                 {
341                     getLogger().debug( "" );
342                     getLogger().debug( "========================================" );
343                     getLogger().debug( " begin test: " + methodName );
344                     getLogger().debug( "========================================" );
345                 }
346
347                 super.run( result );
348
349                 if( getLogger().isDebugEnabled() )
350                 {
351                     getLogger().debug( "========================================" );
352                     getLogger().debug( " end test: " + methodName );
353                     getLogger().debug( "========================================" );
354                     getLogger().debug( "" );
355                 }
356             }
357
358         }
359         catch( Exception JavaDoc e )
360         {
361             System.out.println( e );
362             e.printStackTrace();
363             result.addError( this, e );
364         }
365         finally
366         {
367             done();
368
369             if( this instanceof Disposable )
370             {
371                 try
372                 {
373                     ( (Disposable)this ).dispose();
374                 }
375                 catch( Exception JavaDoc e )
376                 {
377                     result.addFailure( this, new AssertionFailedError( "Disposal Error" ) );
378                 }
379             }
380         }
381
382         methodList.clear();
383         FortressTestCase.m_tests.put( getClass(), methodList );
384     }
385
386     /**
387      * Initializes Fortress.
388      *
389      * The configuration file is determined by the class name plus .xtest appended,
390      * all '.' replaced by '/' and loaded as a resource via classpath
391      */

392     protected void prepare() throws Exception JavaDoc
393     {
394         setCurrentLogger( "prepare" );
395         
396         final String JavaDoc resourceName = getClass().getName().replace( '.', '/' ) + ".xtest";
397         URL JavaDoc resource = getClass().getClassLoader().getResource( resourceName );
398         
399         if( resource != null )
400         {
401             getLogger().debug( "Loading resource " + resourceName );
402             prepare( resource.openStream() );
403         }
404         else
405         {
406             getLogger().warn( "Resource not found " + resourceName );
407         }
408     }
409
410     /**
411      * Initializes Fortress.
412      *
413      * @param testconf The configuration file is passed as an <code>InputStream</code>
414      *
415      * A common way to supply a InputStream is to overide the prepare() method
416      * in the sub class, do there whatever is needed to get the right InputStream object
417      * supplying a conformant xtest configuartion and pass it to this prepare method.
418      * the mentioned initialize method is also the place to set a different logging priority
419      * to the member variable m_logPriority.
420      */

421     protected final void prepare( final InputStream JavaDoc testconf )
422         throws Exception JavaDoc
423     {
424         getLogger().debug( "FortressTestCase.initialize" );
425
426         final DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
427         final Configuration conf = builder.build( testconf );
428
429         String JavaDoc annotation = conf.getChild( "annotation" ).getValue( null );
430
431         if( ( null != annotation ) && !( "".equals( annotation ) ) )
432         {
433             m_logger.info( annotation );
434         }
435
436         final FortressConfig config = new FortressConfig();
437         config.setContainerClass( DefaultContainer.class );
438
439         config.setContextDirectory( "./" );
440         config.setWorkDirectory( "./" );
441         config.setLoggerCategory( "fortress" );
442         
443         config.setLoggerManagerConfiguration( conf.getChild( "logger" ) );
444         config.setRoleManagerConfiguration( conf.getChild( "roles" ) );
445         config.setContainerConfiguration( conf.getChild( "components" ) );
446         
447         m_containerManager = new DefaultContainerManager(
448             setupContext( conf.getChild( "context", true ), config.getContext() ) );
449         ContainerUtil.initialize( m_containerManager );
450         
451         m_container = (DefaultContainer) m_containerManager.getContainer();
452         m_serviceManager = m_container.getServiceManager();
453         m_loggerManager = (LoggerManager) m_serviceManager.lookup( LoggerManager.class.getName() );
454     }
455
456     /**
457      * Sets the logger which will be returned by getLogger and getLogEnabledLogger
458      */

459     private void setCurrentLogger( String JavaDoc name )
460     {
461         if( m_loggerManager == null )
462         {
463             org.apache.log.Logger logger;
464             
465             // Logger for the portion of the configuration has been loaded.
466
logger = Hierarchy.getDefaultHierarchy().getLoggerFor( name );
467             logger.setPriority( Priority.INFO );
468
469             PatternFormatter formatter = new PatternFormatter( FORMAT );
470             StreamTarget target = new StreamTarget( System.out, formatter );
471             logger.setLogTargets( new LogTarget[]{target} );
472             m_logger = new LogKitLogger( logger );
473         }
474         else
475         {
476             m_logger = m_loggerManager.getLoggerForCategory( "test." + name );
477         }
478     }
479
480     /**
481      * Set up a context according to the xtest configuration specifications context
482      * element.
483      *
484      * A method addContext(DefaultContext context) is called here to enable subclasses
485      * to put additional objects into the context programmatically.
486      */

487     private Context setupContext( final Configuration conf, final Context parentContext )
488         throws Exception JavaDoc
489     {
490         //FIXME(GP): This method should setup the Context object according to the
491
// configuration spec.
492
final DefaultContext context = new OverridableContext( parentContext );
493         final Configuration[] confs = conf.getChildren( "entry" );
494         for( int i = 0; i < confs.length; i++ )
495         {
496             final String JavaDoc key = confs[ i ].getAttribute( "name" );
497             final String JavaDoc value = confs[ i ].getAttribute( "value", null );
498             if( value == null )
499             {
500                 String JavaDoc clazz = confs[ i ].getAttribute( "class" );
501                 Object JavaDoc obj = getClass().getClassLoader().loadClass( clazz ).newInstance();
502                 context.put( key, obj );
503                 if( getLogger().isInfoEnabled() )
504                 {
505                     getLogger().info( "FortressTestCase: added an instance of class "
506                         + clazz + " to context entry " + key );
507                 }
508
509             }
510             else
511             {
512                 context.put( key, value );
513                 if( getLogger().isInfoEnabled() )
514                 {
515                     getLogger().info( "FortressTestCase: added value \"" + value
516                         + "\" to context entry " + key );
517                 }
518
519             }
520         }
521         addContext( context );
522         context.makeReadOnly();
523         return context;
524     }
525
526     /**
527      * This method may be overwritten by subclasses to put additional objects
528      * into the context programmatically.
529      */

530     protected void addContext( DefaultContext context )
531     {
532     }
533
534
535     /**
536      * Exctract the base class name of a class.
537      */

538     private String JavaDoc getBaseClassName( Class JavaDoc clazz )
539     {
540         String JavaDoc name = clazz.getName();
541         int pos = name.lastIndexOf( '.' );
542         if( pos >= 0 )
543         {
544             name = name.substring( pos + 1 );
545         }
546         return name;
547     }
548
549
550     /**
551      * Disposes Fortress
552      */

553     private void done()
554     {
555         ContainerUtil.dispose( m_containerManager );
556     }
557
558 }
559
Popular Tags