Coverage Report - org.apache.onami.test.OnamiRunner
 
Classes in this File Line Coverage Branch Coverage Complexity
OnamiRunner
94%
101/107
90%
45/50
3.818
OnamiRunner$1
100%
6/6
100%
4/4
3.818
OnamiRunner$2
100%
3/3
N/A
3.818
 
 1  
 package org.apache.onami.test;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.lang.reflect.Field;
 23  
 import java.lang.reflect.Modifier;
 24  
 import java.util.ArrayList;
 25  
 import java.util.HashMap;
 26  
 import java.util.LinkedList;
 27  
 import java.util.List;
 28  
 import java.util.Map;
 29  
 import java.util.Map.Entry;
 30  
 import java.util.logging.Level;
 31  
 import java.util.logging.Logger;
 32  
 
 33  
 import org.apache.onami.test.annotation.GuiceModules;
 34  
 import org.apache.onami.test.annotation.GuiceProvidedModules;
 35  
 import org.apache.onami.test.annotation.Mock;
 36  
 import org.apache.onami.test.annotation.MockFramework;
 37  
 import org.apache.onami.test.annotation.MockType;
 38  
 import org.apache.onami.test.handler.GuiceInjectableClassHandler;
 39  
 import org.apache.onami.test.handler.GuiceModuleHandler;
 40  
 import org.apache.onami.test.handler.GuiceProvidedModuleHandler;
 41  
 import org.apache.onami.test.handler.MockFrameworkHandler;
 42  
 import org.apache.onami.test.handler.MockHandler;
 43  
 import org.apache.onami.test.mock.MockEngine;
 44  
 import org.apache.onami.test.mock.guice.MockTypeListener;
 45  
 import org.apache.onami.test.reflection.ClassVisitor;
 46  
 import org.apache.onami.test.reflection.HandleException;
 47  
 import org.junit.runner.notification.RunNotifier;
 48  
 import org.junit.runners.BlockJUnit4ClassRunner;
 49  
 import org.junit.runners.model.FrameworkMethod;
 50  
 import org.junit.runners.model.InitializationError;
 51  
 
 52  
 import com.google.inject.AbstractModule;
 53  
 import com.google.inject.Guice;
 54  
 import com.google.inject.Inject;
 55  
 import com.google.inject.Injector;
 56  
 import com.google.inject.Module;
 57  
 import com.google.inject.matcher.Matchers;
 58  
 import com.google.inject.util.Modules;
 59  
 
 60  
 /**
 61  
  * <p>
 62  
  * It's a {@link BlockJUnit4ClassRunner} runner.
 63  
  * </p>
 64  
  * <p>
 65  
  * This class creates a Google Guice {@link Injector} configured by {@link GuiceModules} annotation (only fr modules
 66  
  * with default constructor) and {@link GuiceProvidedModules} annotation and {@link Mock}.
 67  
  * </p>
 68  
  * <p>
 69  
  * <b>Example #1:</b> <br>
 70  
  * 
 71  
  * <pre>
 72  
  * 
 73  
  * &#064;org.junit.runner.RunWith( OnamiRunner.class )
 74  
  * &#064;GuiceModules( SimpleModule.class )
 75  
  * public class AcmeTestCase
 76  
  * {
 77  
  * 
 78  
  *     &#064;GuiceProvidedModules
 79  
  *     static public Module getProperties()
 80  
  *     {
 81  
  *         ...
 82  
  *         return Modules.combine(new ComplexModule( loadProperies() ), ...  );
 83  
  *     }
 84  
  * 
 85  
  * </pre>
 86  
  * 
 87  
  * </p>
 88  
  * <p>
 89  
  * <b>Example #2:</b> <br>
 90  
  * 
 91  
  * <pre>
 92  
  * 
 93  
  * &#064;org.junit.runner.RunWith( OnamiRunner.class )
 94  
  * public class AcmeTestCase
 95  
  *     extends com.google.inject.AbstractModule
 96  
  * {
 97  
  * 
 98  
  *     public void configure()
 99  
  *     {
 100  
  *         // Configure your proper modules
 101  
  *         ...
 102  
  *         bind( Service.class ).annotatedWith( TestAnnotation.class ).to( ServiceTestImpl.class );
 103  
  *         ...
 104  
  *     }
 105  
  * 
 106  
  *     &#064;Mock
 107  
  *     private AnotherService serviceMock;
 108  
  * 
 109  
  *     &#064;Inject
 110  
  *     private Service serviceTest;
 111  
  * 
 112  
  *     &#064;org.junit.Test
 113  
  *     public void test()
 114  
  *     {
 115  
  *         assertNotNull( serviceMock );
 116  
  *         assertNotNull( serviceTest );
 117  
  *     }
 118  
  * </pre>
 119  
  * 
 120  
  * </p>
 121  
  * 
 122  
  * @see GuiceMockModule
 123  
  */
 124  25
 public class OnamiRunner
 125  
     extends BlockJUnit4ClassRunner
 126  
 {
 127  
 
 128  1
     private static final Logger LOGGER = Logger.getLogger( OnamiRunner.class.getName() );
 129  
 
 130  
     private Injector injector;
 131  
 
 132  
     private final List<Module> allModules;
 133  
 
 134  12
     private final Map<Field, Object> mocked = new HashMap<Field, Object>( 1 );
 135  
 
 136  12
     private MockType mockFramework = MockType.EASY_MOCK;
 137  
 
 138  
     /**
 139  
      * OnamiRunner constructor to create the core JUnice class.
 140  
      * 
 141  
      * @see org.junit.runner.RunWith
 142  
      * @param klass The test case class to run.
 143  
      * @throws org.junit.runners.model.InitializationError if any error occurs.
 144  
      */
 145  
     public OnamiRunner( Class<?> klass )
 146  
         throws InitializationError
 147  
     {
 148  12
         super( klass );
 149  
 
 150  
         try
 151  
         {
 152  12
             if ( LOGGER.isLoggable( Level.FINER ) )
 153  
             {
 154  8
                 LOGGER.finer( "Inizializing injector for test class: " + klass.getName() );
 155  
             }
 156  
 
 157  12
             this.allModules = inizializeInjector( klass );
 158  
 
 159  12
             if ( LOGGER.isLoggable( Level.FINER ) )
 160  
             {
 161  8
                 LOGGER.finer( "done..." );
 162  
             }
 163  
         }
 164  0
         catch ( Exception e )
 165  
         {
 166  0
             final List<Throwable> throwables = new LinkedList<Throwable>();
 167  0
             throwables.add( e );
 168  0
             throw new InitializationError( throwables );
 169  12
         }
 170  12
     }
 171  
 
 172  
     /**
 173  
      * {@inheritDoc}
 174  
      */
 175  
     public void run( final RunNotifier notifier )
 176  
     {
 177  12
         if ( LOGGER.isLoggable( Level.FINER ) )
 178  
         {
 179  8
             LOGGER.finer( " ### Run test case: " + getTestClass().getJavaClass() + " ### " );
 180  8
             LOGGER.finer( " #### Creating injector ####" );
 181  
         }
 182  
 
 183  12
         this.injector = createInjector( allModules );
 184  12
         super.run( notifier );
 185  12
         this.flush();
 186  
 
 187  12
         if ( LOGGER.isLoggable( Level.FINER ) )
 188  
         {
 189  9
             LOGGER.finer( " ### End test case: " + getTestClass().getJavaClass().getName() + " ### " );
 190  
         }
 191  12
     }
 192  
 
 193  
     /**
 194  
      * {@inheritDoc}
 195  
      */
 196  
     private void flush()
 197  
     {
 198  12
         this.injector = null;
 199  12
         this.allModules.clear();
 200  12
         this.mocked.clear();
 201  12
     }
 202  
 
 203  
     @Override
 204  
     protected void runChild( FrameworkMethod method, RunNotifier notifier )
 205  
     {
 206  18
         if ( LOGGER.isLoggable( Level.FINER ) )
 207  
         {
 208  13
             LOGGER.finer( " +++ invoke test method: " + method.getName() + " +++ " );
 209  
         }
 210  
 
 211  18
         super.runChild( method, notifier );
 212  18
         resetAllResetAfterMocks();
 213  
 
 214  18
         if ( LOGGER.isLoggable( Level.FINER ) )
 215  
         {
 216  13
             LOGGER.finer( " --- end test method: " + method.getName() + " --- " );
 217  
         }
 218  18
     }
 219  
 
 220  
     /**
 221  
      * Creates test instance via Google-Guice to inject all not-static dependencies.
 222  
      * @return The instance of the test case.
 223  
      * @throws Exception when an error occurs.
 224  
      */
 225  
     protected Object createTest()
 226  
         throws Exception
 227  
     {
 228  18
         if ( LOGGER.isLoggable( Level.FINER ) )
 229  
         {
 230  13
             LOGGER.finer( " Create and inject test class: " + getTestClass().getJavaClass() );
 231  
         }
 232  18
         return this.injector.getInstance( getTestClass().getJavaClass() );
 233  
     }
 234  
 
 235  
     /**
 236  
      * Shortcut to create the Injector given a list of Modules.
 237  
      *
 238  
      * @param modules the list of modules have to be load
 239  
      * @return an Injector instance built using the input Module list
 240  
      */
 241  
     protected Injector createInjector( List<Module> modules )
 242  
     {
 243  12
         return Guice.createInjector( modules );
 244  
     }
 245  
 
 246  
     /**
 247  
      * This method collects modules from {@link GuiceModules}, {@link GuiceProvidedModules}, {@link Mock}.
 248  
      *
 249  
      * @param <T> whatever input type is accepted
 250  
      * @param clazz the input class has to be analyzed
 251  
      * @return a List of Guice Modules built after input class analysis.
 252  
      * @throws IllegalAccessException when a n error occurs.
 253  
      * @throws InstantiationException when a n error occurs.
 254  
      * @throws HandleException when a n error occurs.
 255  
      */
 256  
     protected <T> List<Module> inizializeInjector( Class<T> clazz )
 257  
         throws HandleException, InstantiationException, IllegalAccessException
 258  
     {
 259  12
         final List<Module> modules = new ArrayList<Module>();
 260  12
         Module m = visitClass( clazz );
 261  12
         if ( m != null )
 262  
         {
 263  12
             modules.add( m );
 264  
         }
 265  12
         return modules;
 266  
     }
 267  
 
 268  
     private void resetAllResetAfterMocks()
 269  
     {
 270  18
         for ( Entry<Field, Object> entry : mocked.entrySet() )
 271  
         {
 272  21
             final Mock mockAnnotation = entry.getKey().getAnnotation( Mock.class );
 273  21
             if ( mockAnnotation.resetAfter() )
 274  
             {
 275  21
                 MockEngine mockEngine = MockEngineFactory.getMockEngine( mockFramework );
 276  21
                 mockEngine.resetMock( entry.getValue() );
 277  
             }
 278  21
         }
 279  18
     }
 280  
 
 281  
     /**
 282  
      * @throws HandleException
 283  
      * @throws IllegalAccessException
 284  
      * @throws InstantiationException
 285  
      */
 286  
     private <T> Module visitClass( final Class<T> clazz )
 287  
         throws HandleException, InstantiationException, IllegalAccessException
 288  
     {
 289  
         try
 290  
         {
 291  12
             if ( LOGGER.isLoggable( Level.FINER ) )
 292  
             {
 293  8
                 LOGGER.finer( "  Start introspecting class: " + clazz.getName() );
 294  
             }
 295  12
             final List<Module> allModules = new ArrayList<Module>();
 296  
 
 297  
             // Setup the handlers
 298  12
             final GuiceProvidedModuleHandler guiceProvidedModuleHandler = new GuiceProvidedModuleHandler();
 299  12
             final GuiceModuleHandler guiceModuleHandler = new GuiceModuleHandler();
 300  12
             final GuiceInjectableClassHandler<Inject> guiceInjectableClassHandler = new GuiceInjectableClassHandler<Inject>();
 301  12
             final GuiceInjectableClassHandler<javax.inject.Inject> jsr330InjectableClassHandler = new GuiceInjectableClassHandler<javax.inject.Inject>();
 302  
 
 303  12
             final MockHandler mockHandler = new MockHandler();
 304  12
             final MockFrameworkHandler mockFrameworkHandler = new MockFrameworkHandler();
 305  
 
 306  
             // Visit class and super-classes
 307  12
             new ClassVisitor()
 308  
             .registerHandler( GuiceProvidedModules.class, guiceProvidedModuleHandler )
 309  
             .registerHandler( GuiceModules.class, guiceModuleHandler )
 310  
             .registerHandler( Mock.class, mockHandler )
 311  
             .registerHandler( MockFramework.class, mockFrameworkHandler )
 312  
             .registerHandler( Inject.class, guiceInjectableClassHandler )
 313  
             .registerHandler( javax.inject.Inject.class, jsr330InjectableClassHandler )
 314  
             .visit( clazz );
 315  
 
 316  
             // Retrieve mock framework
 317  12
             if ( mockFrameworkHandler.getMockType() != null )
 318  
             {
 319  1
                 this.mockFramework = mockFrameworkHandler.getMockType();
 320  
             }
 321  
 
 322  
             // retrieve the modules founded
 323  12
             allModules.addAll( guiceProvidedModuleHandler.getModules() );
 324  12
             allModules.addAll( guiceModuleHandler.getModules() );
 325  12
             MockEngine engine = MockEngineFactory.getMockEngine( this.mockFramework );
 326  12
             this.mocked.putAll( mockHandler.getMockedObject( engine ) );
 327  12
             if ( !this.mocked.isEmpty() )
 328  
             {
 329  
                 // Replace all real module binding with Mocked moduled.
 330  7
                 Module m = Modules.override( allModules ).with( new GuiceMockModule( this.mocked ) );
 331  7
                 allModules.clear();
 332  7
                 allModules.add( m );
 333  
             }
 334  
 
 335  
             // Add only clasess that have got the Inject annotation
 336  12
              final Class<?>[] guiceInjectableClasses = guiceInjectableClassHandler.getClasses();
 337  12
              final Class<?>[] jsr330InjectableClasses = jsr330InjectableClassHandler.getClasses();
 338  
 
 339  12
             final AbstractModule statcInjector = new AbstractModule()
 340  12
             {
 341  
                 @Override
 342  
                 protected void configure()
 343  
                 {
 344  
                     // inject all STATIC dependencies
 345  10
                     if ( guiceInjectableClasses.length != 0 )
 346  
                     {
 347  9
                         requestStaticInjection( guiceInjectableClasses );
 348  
                     }
 349  
                     
 350  10
                     if ( jsr330InjectableClasses.length != 0 )
 351  
                     {
 352  1
                         requestStaticInjection( jsr330InjectableClasses );
 353  
                     }
 354  
 
 355  
                     
 356  10
                 }
 357  
             };
 358  12
             if ( guiceInjectableClasses.length != 0 || jsr330InjectableClasses.length != 0 )
 359  
             {
 360  10
                 allModules.add( statcInjector );
 361  
             }
 362  
 
 363  
             // Check if the class is itself a Google Module.
 364  12
             if ( Module.class.isAssignableFrom( getTestClass().getJavaClass() ) )
 365  
             {
 366  2
                 if ( LOGGER.isLoggable( Level.FINER ) )
 367  
                 {
 368  2
                     LOGGER.finer( "   creating module from test class " + getTestClass().getJavaClass() );
 369  
                 }
 370  2
                 final Module classModule = (Module) getTestClass().getJavaClass().newInstance();
 371  2
                 allModules.add( classModule );
 372  
             }
 373  
 
 374  
             // create MockTypeListenerModule
 375  12
             if ( this.mocked.size() != 0 )
 376  
             {
 377  7
                 final AbstractModule mockTypeListenerModule = new AbstractModule()
 378  7
                 {
 379  
                     @Override
 380  
                     protected void configure()
 381  
                     {
 382  7
                         bindListener( Matchers.any(), new MockTypeListener( mocked ) );
 383  7
                     }
 384  
                 };
 385  
 
 386  
                 // BEGIN patch for issue: google-guice: #452
 387  7
                 for ( Entry<Field, Object> entry : mocked.entrySet() )
 388  
                 {
 389  11
                     final Field field = entry.getKey();
 390  11
                     final Object mock = entry.getValue();
 391  11
                     if ( Modifier.isStatic( field.getModifiers() ) )
 392  
                     {
 393  2
                         if ( LOGGER.isLoggable( Level.FINER ) )
 394  
                         {
 395  0
                             LOGGER.finer( "   inject static mock field: " + field.getName() );
 396  
                         }
 397  
 
 398  2
                         field.setAccessible( true );
 399  2
                         field.set( field.getDeclaringClass(), mock );
 400  
                     }
 401  11
                 }
 402  
                 // END patch for issue: google-guice: #452
 403  
 
 404  7
                 allModules.add( mockTypeListenerModule );
 405  
             }
 406  
 
 407  12
             if ( allModules.size() != 0 )
 408  
             {
 409  12
                 if ( LOGGER.isLoggable( Level.FINER ) )
 410  
                 {
 411  8
                     StringBuilder builder = new StringBuilder();
 412  8
                     builder.append( " Collected modules: " );
 413  8
                     builder.append( "\n" );
 414  8
                     for ( Module module : allModules )
 415  
                     {
 416  19
                         builder.append( "    " + module );
 417  19
                         builder.append( "\n" );
 418  
                     }
 419  8
                     LOGGER.finer( builder.toString() );
 420  
                 }
 421  12
                 return Modules.combine( allModules );
 422  
             }
 423  0
             return null;
 424  
         }
 425  
         finally
 426  
         {
 427  12
             if ( LOGGER.isLoggable( Level.FINER ) )
 428  
             {
 429  8
                 LOGGER.finer( " ...done" );
 430  
             }
 431  
         }
 432  
     }
 433  
 
 434  
 }