Better support for integration testing:
  - easy way of enabling remote debugging in a single class
  - some more utility methods
  - create jar file copy of scr bundle for integration test

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@806503 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/pom.xml b/scr/pom.xml
index ce4b849..b4487f5 100644
--- a/scr/pom.xml
+++ b/scr/pom.xml
@@ -160,6 +160,38 @@
                 </configuration>
             </plugin>
 
+            <!-- Provide bundle for integration tests -->
+            <plugin>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <version>1.3</version>
+                <executions>
+                    <execution>
+                        <id>configadmin-file-create</id>
+                        <phase>pre-integration-test</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <configuration>
+                            <tasks>
+                                <copy file="${project.build.directory}/${project.build.finalName}.jar" tofile="${project.build.directory}/scr.jar" />
+                            </tasks>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>configadmin-file-remove</id>
+                        <phase>post-integration-test</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <configuration>
+                            <tasks>
+                                <delete file="${project.build.directory}/configadmin.jar" />
+                            </tasks>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
             <!--
                 Exclude Integration tests in (default) unit tests and
                 conversely enable integration tests for integration testing
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/ComponentConfigurationTest.java b/scr/src/test/java/org/apache/felix/scr/integration/ComponentConfigurationTest.java
index aa589cd..4187f4f 100644
--- a/scr/src/test/java/org/apache/felix/scr/integration/ComponentConfigurationTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/integration/ComponentConfigurationTest.java
@@ -27,12 +27,17 @@
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.junit.JUnit4TestRunner;
 import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
 
 
 @RunWith(JUnit4TestRunner.class)
 public class ComponentConfigurationTest extends ComponentTestBase
 {
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
+
 
     @Test
     public void test_SimpleComponent_configuration_ignore()
@@ -65,7 +70,79 @@
     @Test
     public void test_SimpleComponent_factory_configuration()
     {
-        test_factory_configuration( "FactoryConfigurationComponent" );
+        final String factoryPid = "FactoryConfigurationComponent";
+
+        deleteFactoryConfigurations( factoryPid );
+        delay();
+
+        // one single component exists without configuration
+        final Component[] noConfigurations = findComponentsByName( factoryPid );
+        TestCase.assertNotNull( noConfigurations );
+        TestCase.assertEquals( 1, noConfigurations.length );
+        TestCase.assertEquals( Component.STATE_DISABLED, noConfigurations[0].getState() );
+        TestCase.assertTrue( SimpleComponent.INSTANCES.isEmpty() );
+
+        // enable the component, configuration required, hence unsatisfied
+        noConfigurations[0].enable();
+        delay();
+
+        final Component[] enabledNoConfigs = findComponentsByName( factoryPid );
+        TestCase.assertNotNull( enabledNoConfigs );
+        TestCase.assertEquals( 1, enabledNoConfigs.length );
+        TestCase.assertEquals( Component.STATE_UNSATISFIED, enabledNoConfigs[0].getState() );
+        TestCase.assertTrue( SimpleComponent.INSTANCES.isEmpty() );
+
+        // create two factory configurations expecting two components
+        final String pid0 = createFactoryConfiguration( factoryPid );
+        final String pid1 = createFactoryConfiguration( factoryPid );
+        delay();
+
+        // expect two components, only first is active, second is disabled
+        final Component[] twoConfigs = findComponentsByName( factoryPid );
+        TestCase.assertNotNull( twoConfigs );
+        TestCase.assertEquals( 2, twoConfigs.length );
+        TestCase.assertEquals( Component.STATE_ACTIVE, twoConfigs[0].getState() );
+        TestCase.assertEquals( Component.STATE_DISABLED, twoConfigs[1].getState() );
+        TestCase.assertEquals( 1, SimpleComponent.INSTANCES.size() );
+        TestCase.assertTrue( SimpleComponent.INSTANCES.containsKey( twoConfigs[0].getId() ) );
+        TestCase.assertFalse( SimpleComponent.INSTANCES.containsKey( twoConfigs[1].getId() ) );
+
+        // enable second component
+        twoConfigs[1].enable();
+        delay();
+
+        // ensure both components active
+        TestCase.assertEquals( Component.STATE_ACTIVE, twoConfigs[0].getState() );
+        TestCase.assertEquals( Component.STATE_ACTIVE, twoConfigs[1].getState() );
+        TestCase.assertEquals( 2, SimpleComponent.INSTANCES.size() );
+        TestCase.assertTrue( SimpleComponent.INSTANCES.containsKey( twoConfigs[0].getId() ) );
+        TestCase.assertTrue( SimpleComponent.INSTANCES.containsKey( twoConfigs[1].getId() ) );
+
+        // delete a configuration
+        deleteConfig( pid0 );
+        delay();
+
+        // expect one component
+        final Component[] oneConfig = findComponentsByName( factoryPid );
+        TestCase.assertNotNull( oneConfig );
+        TestCase.assertEquals( 1, oneConfig.length );
+        TestCase.assertEquals( Component.STATE_ACTIVE, oneConfig[0].getState() );
+        TestCase.assertEquals( 1, SimpleComponent.INSTANCES.size() );
+        TestCase.assertFalse( SimpleComponent.INSTANCES.containsKey( twoConfigs[0].getId() ) );
+        TestCase.assertTrue( SimpleComponent.INSTANCES.containsKey( twoConfigs[1].getId() ) );
+
+        // delete second configuration
+        deleteConfig( pid1 );
+        delay();
+
+        // expect a single unsatisifed component
+        final Component[] configsDeleted = findComponentsByName( factoryPid );
+        TestCase.assertNotNull( configsDeleted );
+        TestCase.assertEquals( 1, configsDeleted.length );
+        TestCase.assertEquals( Component.STATE_UNSATISFIED, configsDeleted[0].getState() );
+        TestCase.assertEquals( 0, SimpleComponent.INSTANCES.size() );
+        TestCase.assertFalse( SimpleComponent.INSTANCES.containsKey( twoConfigs[0].getId() ) );
+        TestCase.assertFalse( SimpleComponent.INSTANCES.containsKey( twoConfigs[1].getId() ) );
     }
 
 
@@ -251,129 +328,4 @@
         TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
         TestCase.assertNull( SimpleComponent.INSTANCE );
     }
-
-
-    private void test_factory_configuration( final String componentName )
-    {
-        final String factoryPid = componentName;
-
-        deleteFactoryConfigurations( factoryPid );
-        delay();
-
-        // one single component exists without configuration
-        final Component[] noConfigurations = findComponentsByName( factoryPid );
-        TestCase.assertNotNull( noConfigurations );
-        TestCase.assertEquals( 1, noConfigurations.length );
-        TestCase.assertEquals( Component.STATE_DISABLED, noConfigurations[0].getState() );
-        TestCase.assertTrue( SimpleComponent.INSTANCES.isEmpty() );
-
-        // enable the component, configuration required, hence unsatisfied
-        noConfigurations[0].enable();
-        delay();
-
-        final Component[] enabledNoConfigs = findComponentsByName( factoryPid );
-        TestCase.assertNotNull( enabledNoConfigs );
-        TestCase.assertEquals( 1, enabledNoConfigs.length );
-        TestCase.assertEquals( Component.STATE_UNSATISFIED, enabledNoConfigs[0].getState() );
-        TestCase.assertTrue( SimpleComponent.INSTANCES.isEmpty() );
-
-        // create two factory configurations expecting two components
-        final String pid0 = createFactoryConfiguration( factoryPid );
-        final String pid1 = createFactoryConfiguration( factoryPid );
-        delay();
-
-        // expect two components, only first is active, second is disabled
-        final Component[] twoConfigs = findComponentsByName( factoryPid );
-        TestCase.assertNotNull( twoConfigs );
-        TestCase.assertEquals( 2, twoConfigs.length );
-        TestCase.assertEquals( Component.STATE_ACTIVE, twoConfigs[0].getState() );
-        TestCase.assertEquals( Component.STATE_DISABLED, twoConfigs[1].getState() );
-        TestCase.assertEquals( 1, SimpleComponent.INSTANCES.size() );
-        TestCase.assertTrue( SimpleComponent.INSTANCES.containsKey( twoConfigs[0].getId() ) );
-        TestCase.assertFalse( SimpleComponent.INSTANCES.containsKey( twoConfigs[1].getId() ) );
-
-        // enable second component
-        twoConfigs[1].enable();
-        delay();
-
-        // ensure both components active
-        TestCase.assertEquals( Component.STATE_ACTIVE, twoConfigs[0].getState() );
-        TestCase.assertEquals( Component.STATE_ACTIVE, twoConfigs[1].getState() );
-        TestCase.assertEquals( 2, SimpleComponent.INSTANCES.size() );
-        TestCase.assertTrue( SimpleComponent.INSTANCES.containsKey( twoConfigs[0].getId() ) );
-        TestCase.assertTrue( SimpleComponent.INSTANCES.containsKey( twoConfigs[1].getId() ) );
-
-        // delete a configuration
-        deleteConfig( pid0 );
-        delay();
-
-        // expect one component
-        final Component[] oneConfig = findComponentsByName( factoryPid );
-        TestCase.assertNotNull( oneConfig );
-        TestCase.assertEquals( 1, oneConfig.length );
-        TestCase.assertEquals( Component.STATE_ACTIVE, oneConfig[0].getState() );
-        TestCase.assertEquals( 1, SimpleComponent.INSTANCES.size() );
-        TestCase.assertFalse( SimpleComponent.INSTANCES.containsKey( twoConfigs[0].getId() ) );
-        TestCase.assertTrue( SimpleComponent.INSTANCES.containsKey( twoConfigs[1].getId() ) );
-
-        // delete second configuration
-        deleteConfig( pid1 );
-        delay();
-
-        // expect a single unsatisifed component
-        final Component[] configsDeleted = findComponentsByName( factoryPid );
-        TestCase.assertNotNull( configsDeleted );
-        TestCase.assertEquals( 1, configsDeleted.length );
-        TestCase.assertEquals( Component.STATE_UNSATISFIED, configsDeleted[0].getState() );
-        TestCase.assertEquals( 0, SimpleComponent.INSTANCES.size() );
-        TestCase.assertFalse( SimpleComponent.INSTANCES.containsKey( twoConfigs[0].getId() ) );
-        TestCase.assertFalse( SimpleComponent.INSTANCES.containsKey( twoConfigs[1].getId() ) );
-
-    }
-
-
-    private void test_service( final String componentName )
-    {
-        final String pid = componentName;
-
-        // one single component exists without configuration
-        final Component component = findComponentByName( pid );
-        TestCase.assertNotNull( component );
-        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
-
-        component.enable();
-        delay();
-
-        final SimpleComponent instance = SimpleComponent.INSTANCE;
-        TestCase.assertEquals( Component.STATE_ACTIVE, component.getState() );
-        TestCase.assertNotNull( instance );
-
-        // assert component properties (all !)
-        TestCase.assertEquals( "required", instance.getProperty( "prop.public" ) );
-        TestCase.assertEquals( "private", instance.getProperty( ".prop.private" ) );
-
-        // get the service
-        ServiceReference reference = bundleContext.getServiceReference( "java.lang.Object" );
-        TestCase.assertNotNull( reference );
-        try
-        {
-            TestCase.assertEquals( instance, bundleContext.getService( reference ) );
-        }
-        finally
-        {
-            bundleContext.ungetService( reference );
-        }
-
-        // check service properties
-        TestCase.assertEquals( "required", reference.getProperty( "prop.public" ) );
-        TestCase.assertNull( reference.getProperty( ".prop.private" ) );
-
-        // check property keys do not contain private keys
-        for ( String propKey : reference.getPropertyKeys() )
-        {
-            TestCase.assertTrue( "Property key [" + propKey
-                + "] must have at least one character and not start with a dot", propKey.length() > 0
-                && !propKey.startsWith( "." ) );
-        }
-    }
 }
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/ComponentTestBase.java b/scr/src/test/java/org/apache/felix/scr/integration/ComponentTestBase.java
index 6448a94..89c054e 100644
--- a/scr/src/test/java/org/apache/felix/scr/integration/ComponentTestBase.java
+++ b/scr/src/test/java/org/apache/felix/scr/integration/ComponentTestBase.java
@@ -22,11 +22,12 @@
 import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
 import static org.ops4j.pax.exam.CoreOptions.options;
 import static org.ops4j.pax.exam.CoreOptions.provision;
-import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.scanDir;
 import static org.ops4j.pax.swissbox.tinybundles.core.TinyBundles.withBnd;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.Hashtable;
@@ -38,8 +39,11 @@
 import org.apache.felix.scr.integration.components.MyTinyBundle;
 import org.junit.After;
 import org.junit.Before;
+import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.Inject;
 import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.exam.container.def.PaxRunnerOptions;
 import org.ops4j.pax.exam.junit.Configuration;
 import org.ops4j.pax.swissbox.tinybundles.core.TinyBundles;
 import org.osgi.framework.Bundle;
@@ -66,6 +70,14 @@
     protected static final String PROP_NAME = "theValue";
     protected static final Dictionary<String, String> theConfig;
 
+    // the JVM option to set to enable remote debugging
+    protected static final String DEBUG_VM_OPTION = "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=30303";
+
+    // the actual JVM option set, extensions may implement a static
+    // initializer overwriting this value to have the configuration()
+    // method include it when starting the OSGi framework JVM
+    protected static String paxRunnerVmOption = null;
+
     static
     {
         theConfig = new Hashtable<String, String>();
@@ -76,14 +88,15 @@
     @Configuration
     public static Option[] configuration()
     {
-        return options(
+        final Option[] base = options(
             provision(
-                scanDir( "target" ).filter( "*.jar" ),
+                CoreOptions.bundle( new File("target/scr.jar").toURI().toString() ),
                 mavenBundle( "org.ops4j.pax.swissbox", "pax-swissbox-tinybundles", "1.0.0" ),
                 mavenBundle( "org.apache.felix", "org.apache.felix.configadmin", "1.0.10" )
              )
-//             , PaxRunnerOptions.vmOption( "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=30303" )
         );
+        final Option vmOption = ( paxRunnerVmOption != null ) ? PaxRunnerOptions.vmOption( paxRunnerVmOption ) : null;
+        return OptionUtils.combine( base, vmOption );
     }
 
 
@@ -266,4 +279,43 @@
             TestCase.fail( "Failed deleting configurations " + factoryPid + ": " + ioe.toString() );
         }
     }
+
+
+    protected static Class<?> getType( Object object, String desiredName )
+    {
+        Class<?> ccImpl = object.getClass();
+        while ( ccImpl != null && !desiredName.equals( ccImpl.getSimpleName() ) )
+        {
+            ccImpl = ccImpl.getSuperclass();
+        }
+        if ( ccImpl == null )
+        {
+            TestCase.fail( "ComponentContext " + object + " is not a " + desiredName );
+        }
+
+        return ccImpl;
+    }
+
+
+    protected static Object getFieldValue( Object object, String fieldName )
+    {
+        try
+        {
+            final Field m_componentsField = getField( object.getClass(), fieldName );
+            return m_componentsField.get( object );
+        }
+        catch ( Throwable t )
+        {
+            TestCase.fail( "Cannot get " + fieldName + " from " + object + ": " + t );
+            return null; // keep the compiler happy
+        }
+    }
+
+
+    protected static Field getField( Class<?> type, String fieldName ) throws NoSuchFieldException
+    {
+        Field field = type.getDeclaredField( fieldName );
+        field.setAccessible( true );
+        return field;
+    }
 }
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/ServiceComponentTest.java b/scr/src/test/java/org/apache/felix/scr/integration/ServiceComponentTest.java
index 94d4dc8..c5393c0 100644
--- a/scr/src/test/java/org/apache/felix/scr/integration/ServiceComponentTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/integration/ServiceComponentTest.java
@@ -32,6 +32,12 @@
 @RunWith(JUnit4TestRunner.class)
 public class ServiceComponentTest extends ComponentTestBase
 {
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
+
 
     @Test
     public void test_SimpleComponent_service()