FELIX-4403 Add integration test for configure-by-annotations

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1616081 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/AnnoConfigTest.java b/scr/src/test/java/org/apache/felix/scr/integration/AnnoConfigTest.java
new file mode 100644
index 0000000..2b034e4
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/AnnoConfigTest.java
@@ -0,0 +1,256 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.felix.scr.integration;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.scr.integration.components.annoconfig.AnnoComponent;
+import org.apache.felix.scr.integration.components.annoconfig.AnnoComponent.A1;
+import org.apache.felix.scr.integration.components.annoconfig.AnnoComponent.A1Arrays;
+import org.apache.felix.scr.integration.components.annoconfig.AnnoComponent.E1;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.component.runtime.dto.ComponentConfigurationDTO;
+
+@RunWith(JUnit4TestRunner.class)
+public class AnnoConfigTest extends ComponentTestBase
+{
+
+    static
+    {
+        // uncomment to enable debugging of this test class
+//        paxRunnerVmOption = DEBUG_VM_OPTION;
+
+        descriptorFile = "/integration_test_annoconfig.xml";
+        COMPONENT_PACKAGE = COMPONENT_PACKAGE + ".annoconfig";
+   }
+    
+    @Test
+    public void testAnnoConfig() throws Exception
+    {
+        String name = "org.apache.felix.scr.integration.components.annoconfig";
+        ComponentConfigurationDTO dto = findComponentConfigurationByName(name, ComponentConfigurationDTO.SATISFIED);
+        AnnoComponent ac = getServiceFromConfiguration(dto, AnnoComponent.class);
+        checkA1NoValues(ac.m_a1_activate);
+        checkA1ArraysNoValues(ac.m_a1Arrays_activate);
+        
+        Configuration c = configure(name, null, allValues());
+        delay();
+        
+        checkA1(ac.m_a1_modified);
+        checkA1Array(ac.m_a1Arrays_modified);
+
+        ungetServiceFromConfiguration(dto, AnnoComponent.class);
+        checkA1(ac.m_a1_deactivate);
+        checkA1Array(ac.m_a1Arrays_deactivate);
+        ac = getServiceFromConfiguration(dto, AnnoComponent.class);
+        checkA1(ac.m_a1_activate);
+        checkA1Array(ac.m_a1Arrays_activate);
+        
+        c.delete();
+        delay();
+        
+        checkA1NoValues(ac.m_a1_modified);
+        checkA1ArraysNoValues(ac.m_a1Arrays_modified);
+        
+        c = configure(name, null, arrayValues());
+        delay();
+        
+        checkA1FromArray(ac.m_a1_modified);
+        checkA1ArrayFromArray(ac.m_a1Arrays_modified, false);
+        
+        c.delete();
+        delay();
+        
+        checkA1NoValues(ac.m_a1_modified);
+        checkA1ArraysNoValues(ac.m_a1Arrays_modified);
+        
+        c = configure(name, null, collectionValues());
+        delay();
+        
+        checkA1FromArray(ac.m_a1_modified);
+        checkA1ArrayFromArray(ac.m_a1Arrays_modified, true);
+
+    }
+    
+    private Hashtable<String, Object> allValues()
+    {
+        Hashtable<String, Object> values = new Hashtable<String, Object>();
+        values.put("bool", "true");
+        values.put("byt", 12l);
+        values.put("clas", String.class.getName());
+        values.put("e1", E1.a.toString());
+        values.put("doubl", "3.14");
+        values.put("floa", 500l);
+        values.put("integer", 3.0d);
+        values.put("lon", "12345678");
+        values.put("shor", 3l);
+        values.put("string", 3);
+        return values;
+    }
+
+    private Hashtable<String, Object> arrayValues()
+    {
+        Hashtable<String, Object> values = new Hashtable<String, Object>();
+        values.put("bool", new boolean[] {true, false});
+        values.put("byt", new byte[] {12, 3});
+        values.put("clas", new String[] {String.class.getName(), Integer.class.getName()});
+        values.put("e1", new String[] {E1.a.name(), E1.b.name()});
+        values.put("doubl", new double[] {3.14, 2.78, 9});
+        values.put("floa", new float[] {500, 37.44f});
+        values.put("integer", new int[] {3, 6, 9});
+        values.put("lon", new long[] {12345678l, -1});
+        values.put("shor", new short[] {3, 88});
+        values.put("string", new String[] {});
+        return values;
+    }
+    
+    private Hashtable<String, Object> collectionValues()
+    {
+        Hashtable<String, Object> values = arrayValues();
+        Hashtable<String, Object> collectionValues = new Hashtable<String, Object>();
+        for (Map.Entry<String, Object> entry: values.entrySet())
+        {
+            collectionValues.put(entry.getKey(), toList(entry.getValue()));
+        }
+        //yuck
+        collectionValues.remove("string");
+        return collectionValues;
+    }
+    
+    private List<?> toList(Object value)
+    {
+        List result = new ArrayList();
+        for (int i = 0; i < Array.getLength(value); i++)
+        {
+            result.add(Array.get(value, i));
+        }
+        return result;
+    }
+
+    private void checkA1(A1 a)
+    {
+        TestCase.assertEquals(true, a.bool());
+        TestCase.assertEquals((byte)12, a.byt());
+        TestCase.assertEquals(String.class, a.clas());
+        TestCase.assertEquals(E1.a, a.e1());
+        TestCase.assertEquals(3.14d, a.doubl());
+        TestCase.assertEquals(500f, a.floa());
+        TestCase.assertEquals(3, a.integer());
+        TestCase.assertEquals(12345678l,  a.lon());
+        TestCase.assertEquals((short)3, a.shor());
+        TestCase.assertEquals("3", a.string());
+    }
+
+
+    private void checkA1FromArray(A1 a)
+    {        
+        TestCase.assertEquals(true, a.bool());
+        TestCase.assertEquals((byte)12, a.byt());
+        TestCase.assertEquals(String.class, a.clas());
+        TestCase.assertEquals(E1.a, a.e1());
+        TestCase.assertEquals(3.14d, a.doubl());
+        TestCase.assertEquals(500f, a.floa());
+        TestCase.assertEquals(3, a.integer());
+        TestCase.assertEquals(12345678l,  a.lon());
+        TestCase.assertEquals((short)3, a.shor());
+        TestCase.assertEquals(null, a.string());
+    }
+    
+    private void checkA1Array(A1Arrays a)
+    {
+        assertArrayEquals(new boolean[] {true}, a.bool());
+        assertArrayEquals(new byte[] {(byte)12}, a.byt());
+        assertArrayEquals(new Class<?>[] {String.class}, a.clas());
+        assertArrayEquals(new E1[] {E1.a}, a.e1());
+        assertArrayEquals(new double[] {3.14d}, a.doubl());
+        assertArrayEquals(new float[] {500f}, a.floa());
+        assertArrayEquals(new int[] {3}, a.integer());
+        assertArrayEquals(new long[] {12345678l},  a.lon());
+        assertArrayEquals(new short[] {(short)3}, a.shor());
+        assertArrayEquals(new String[] {"3"}, a.string());
+    }
+    
+    private void checkA1ArrayFromArray(A1Arrays a, boolean caBug)
+    {
+        assertArrayEquals(new boolean[] {true, false}, a.bool());
+        assertArrayEquals(new byte[] {12, 3}, a.byt());
+        assertArrayEquals(new Class<?>[] {String.class, Integer.class}, a.clas());
+        assertArrayEquals(new E1[] {E1.a, E1.b}, a.e1());
+        assertArrayEquals(new double[] {3.14, 2.78, 9}, a.doubl());
+        assertArrayEquals(new float[] {500f, 37.44f}, a.floa());
+        assertArrayEquals(new int[] {3, 6, 9}, a.integer());
+        assertArrayEquals(new long[] {12345678l, -1},  a.lon());
+        assertArrayEquals(new short[] {(short)3, 88}, a.shor());
+        if (!caBug)
+        {
+            assertArrayEquals(new String[] {}, a.string());
+        }
+    }
+    
+    private void assertArrayEquals(Object a, Object b)
+    {
+        TestCase.assertTrue(a.getClass().isArray());
+        TestCase.assertTrue(b.getClass().isArray());
+        TestCase.assertEquals("wrong length", Array.getLength(a), Array.getLength(b));
+        TestCase.assertEquals("wrong type", a.getClass().getComponentType(), b.getClass().getComponentType());
+        for (int i = 0; i < Array.getLength(a); i++)
+        {
+            TestCase.assertEquals("different value at " + i, Array.get(a, i), Array.get(b, i));
+        }
+        
+    }
+    
+    private void checkA1NoValues(A1 a)
+    {
+        TestCase.assertEquals(false, a.bool());
+        TestCase.assertEquals((byte)0, a.byt());
+        TestCase.assertEquals(null, a.clas());
+        TestCase.assertEquals(null, a.e1());
+        TestCase.assertEquals(0d, a.doubl());
+        TestCase.assertEquals(0f, a.floa());
+        TestCase.assertEquals(0, a.integer());
+        TestCase.assertEquals(0l,  a.lon());
+        TestCase.assertEquals((short)0, a.shor());
+        TestCase.assertEquals(null, a.string());
+    }
+
+    private void checkA1ArraysNoValues(A1Arrays a)
+    {
+        TestCase.assertEquals(null, a.bool());
+        TestCase.assertEquals(null, a.byt());
+        TestCase.assertEquals(null, a.clas());
+        TestCase.assertEquals(null, a.e1());
+        TestCase.assertEquals(null, a.doubl());
+        TestCase.assertEquals(null, a.floa());
+        TestCase.assertEquals(null, a.integer());
+        TestCase.assertEquals(null,  a.lon());
+        TestCase.assertEquals(null, a.shor());
+        TestCase.assertEquals(null, a.string());
+    }
+}
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 af5f978..37eb492 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
@@ -110,7 +110,7 @@
     protected static final String BUNDLE_JAR_DEFAULT = "target/scr.jar";
 
     protected static final String PROP_NAME = "theValue";
-    protected static final Dictionary<String, String> theConfig;
+    protected static final Dictionary<String, Object> 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";
@@ -144,14 +144,15 @@
 
     static
     {
-        theConfig = new Hashtable<String, String>();
+        theConfig = new Hashtable<String, Object>();
         theConfig.put( PROP_NAME, PROP_NAME );
     }
 
     @ProbeBuilder
     public TestProbeBuilder extendProbe(TestProbeBuilder builder) {
         builder.setHeader("Export-Package", "org.apache.felix.scr.integration.components," +
-                                            "org.apache.felix.scr.integration.components.activatesignature," +
+            "org.apache.felix.scr.integration.components.activatesignature," +
+            "org.apache.felix.scr.integration.components.annoconfig," +
                                             "org.apache.felix.scr.integration.components.circular," +
                                             "org.apache.felix.scr.integration.components.circularFactory," +
                                             "org.apache.felix.scr.integration.components.concurrency," +
@@ -371,20 +372,35 @@
     
     protected <S> S getServiceFromConfiguration( ComponentConfigurationDTO dto, Class<S> clazz )
     {
-    	long id = dto.id;
-    	String filter = "(component.id=" + id + ")";
-    	Collection<ServiceReference<S>> srs;
-		try {
-			srs = bundleContext.getServiceReferences(clazz, filter);
-	    	Assert.assertEquals(1, srs.size());
-	    	ServiceReference<S> sr = srs.iterator().next();
-	    	S s = bundleContext.getService(sr);
-	    	Assert.assertNotNull(s);
-	    	return s;
-		} catch (InvalidSyntaxException e) {
-			TestCase.fail(e.getMessage());
-			return null;//unreachable in fact
-		}
+        long id = dto.id;
+        String filter = "(component.id=" + id + ")";
+        Collection<ServiceReference<S>> srs;
+        try {
+            srs = bundleContext.getServiceReferences(clazz, filter);
+            Assert.assertEquals(1, srs.size());
+            ServiceReference<S> sr = srs.iterator().next();
+            S s = bundleContext.getService(sr);
+            Assert.assertNotNull(s);
+            return s;
+        } catch (InvalidSyntaxException e) {
+            TestCase.fail(e.getMessage());
+            return null;//unreachable in fact
+        }
+    }
+    
+    protected <S> void ungetServiceFromConfiguration( ComponentConfigurationDTO dto, Class<S> clazz )
+    {
+        long id = dto.id;
+        String filter = "(component.id=" + id + ")";
+        Collection<ServiceReference<S>> srs;
+        try {
+            srs = bundleContext.getServiceReferences(clazz, filter);
+            Assert.assertEquals(1, srs.size());
+            ServiceReference<S> sr = srs.iterator().next();
+            bundleContext.ungetService(sr);
+        } catch (InvalidSyntaxException e) {
+            TestCase.fail(e.getMessage());
+        }
     }
     
     protected void enableAndCheck( ComponentDescriptionDTO cd ) throws InvocationTargetException, InterruptedException
@@ -461,6 +477,12 @@
 
     protected org.osgi.service.cm.Configuration configure( String pid, String bundleLocation )
     {
+        return configure(pid, bundleLocation, theConfig);
+    }
+
+    protected org.osgi.service.cm.Configuration configure(String pid,
+        String bundleLocation, Dictionary<String, Object> props)
+    {
         ConfigurationAdmin ca = getConfigurationAdmin();
         try
         {
@@ -469,7 +491,7 @@
             {
                 config.setBundleLocation( bundleLocation );
             }
-            config.update( theConfig );
+            config.update( props );
             return config;
         }
         catch ( IOException ioe )
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/annoconfig/AnnoComponent.java b/scr/src/test/java/org/apache/felix/scr/integration/components/annoconfig/AnnoComponent.java
new file mode 100644
index 0000000..db5770b
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/annoconfig/AnnoComponent.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.felix.scr.integration.components.annoconfig;
+
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.scr.impl.helper.AnnotationTest.E1;
+import org.osgi.service.component.ComponentConstants;
+import org.osgi.service.component.ComponentContext;
+
+
+public class AnnoComponent
+{
+
+    public enum E1 {a, b, c}
+    
+    public @interface A1 {
+        boolean bool();
+        byte byt();
+        Class<?> clas();
+        E1 e1();
+        double doubl();
+        float floa();
+        int integer();
+        long lon();
+        short shor();
+        String string();
+    }
+    
+    public @interface A1Arrays {
+        boolean[] bool();
+        byte[] byt();
+        Class<?>[] clas();
+        E1[] e1();
+        double[] doubl();
+        float[] floa();
+        int[] integer();
+        long[] lon();
+        short[] shor();
+        String[] string();
+    }
+    
+    public A1 m_a1_activate;
+    public A1Arrays m_a1Arrays_activate;
+    public A1 m_a1_modified;
+    public A1Arrays m_a1Arrays_modified;
+    public A1 m_a1_deactivate;
+    public A1Arrays m_a1Arrays_deactivate;
+    
+
+    @SuppressWarnings("unused")
+    private void activate( ComponentContext activateContext, A1 a1, A1Arrays a1Arrays, Map<?, ?> config )
+    {
+        m_a1_activate = a1;
+        m_a1Arrays_activate = a1Arrays;
+    }
+
+
+
+    @SuppressWarnings("unused")
+    private void modified( ComponentContext context, A1 a1, A1Arrays a1Arrays)
+    {
+        m_a1_modified = a1;
+        m_a1Arrays_modified = a1Arrays;
+    }
+    
+    @SuppressWarnings("unused")
+    private void deactivate( A1 a1, A1Arrays a1Arrays )
+    {
+        m_a1_deactivate = a1;
+        m_a1Arrays_deactivate = a1Arrays;
+    }
+
+
+}
diff --git a/scr/src/test/resources/integration_test_annoconfig.xml b/scr/src/test/resources/integration_test_annoconfig.xml
new file mode 100644
index 0000000..6511e2d
--- /dev/null
+++ b/scr/src/test/resources/integration_test_annoconfig.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
+	license agreements. See the NOTICE file distributed with this work for additional 
+	information regarding copyright ownership. The ASF licenses this file to 
+	you 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. -->
+<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.3.0">
+	<scr:component
+		name='org.apache.felix.scr.integration.components.annoconfig'
+		pid='org.apache.felix.scr.integration.components.annoconfig'
+		modified='modified'
+		>
+		<implementation
+			class='org.apache.felix.scr.integration.components.annoconfig.AnnoComponent' />
+		<service>
+			<provide interface='org.apache.felix.scr.integration.components.annoconfig.AnnoComponent' />
+		</service>
+	</scr:component>
+
+
+</components>