FELIX-3557 tests for circular dependency behavior
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1350816 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/CircularReferenceTest.java b/scr/src/test/java/org/apache/felix/scr/integration/CircularReferenceTest.java
new file mode 100644
index 0000000..59a460c
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/CircularReferenceTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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 junit.framework.TestCase;
+import org.apache.felix.scr.Component;
+import org.apache.felix.scr.integration.components.circular.A;
+import org.apache.felix.scr.integration.components.circular.B;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+/**
+ * @version $Rev:$ $Date:$
+ */
+@RunWith(JUnit4TestRunner.class)
+public class CircularReferenceTest extends ComponentTestBase
+{
+
+ static
+ {
+ // uncomment to enable debugging of this test class
+// paxRunnerVmOption = DEBUG_VM_OPTION;
+
+ descriptorFile = "/integration_test_circular.xml";
+ }
+
+
+ /**
+ * A and B have mandatory dependencies on each other. Neither should start.
+ */
+ @Test
+ public void test_A11_B11()
+ {
+ String componentNameA = "1.A.1.1.dynamic";
+ final Component componentA = findComponentByName( componentNameA );
+ TestCase.assertNotNull( componentA );
+ TestCase.assertEquals( Component.STATE_UNSATISFIED, componentA.getState() );
+
+ String componentNameB = "1.B.1.1.dynamic";
+ final Component componentB = findComponentByName( componentNameB );
+ TestCase.assertNotNull( componentB );
+ TestCase.assertEquals( Component.STATE_UNSATISFIED, componentB.getState() );
+
+
+ }
+
+ /**
+ * A > 1.1 > B > 0..n > A Both should start (A first), but B should not have an A reference.
+ */
+ @Test
+ public void test_A11_B0n_immediate_A_first()
+ {
+ String componentNameA = "2.A.1.1.dynamic";
+ final Component componentA = findComponentByName( componentNameA );
+ TestCase.assertNotNull( componentA );
+ TestCase.assertEquals( Component.STATE_ACTIVE, componentA.getState() );
+ A a = ( A ) componentA.getComponentInstance().getInstance();
+ assertEquals( 1, a.getBs().size());
+
+ String componentNameB = "2.B.0.n.dynamic";
+ final Component componentB = findComponentByName( componentNameB );
+ TestCase.assertNotNull( componentB );
+ TestCase.assertEquals( Component.STATE_ACTIVE, componentB.getState() );
+ B b = ( B ) componentB.getComponentInstance().getInstance();
+ //Currently felix binds A. I think this is wrong.
+// assertEquals( 0, b.getAs().size() );
+ }
+ /**
+ * A > 1.1 > B > 0..n > A Both should start (B first), and B should have an A reference.
+ */
+ @Test
+ public void test_A11_B0n_immediate_B_first()
+ {
+ String componentNameA = "3.A.1.1.dynamic";
+ final Component componentA = findComponentByName( componentNameA );
+ TestCase.assertNotNull( componentA );
+ TestCase.assertEquals( Component.STATE_ACTIVE, componentA.getState() );
+ A a = ( A ) componentA.getComponentInstance().getInstance();
+ assertEquals( 1, a.getBs().size());
+
+ String componentNameB = "3.B.0.n.dynamic";
+ final Component componentB = findComponentByName( componentNameB );
+ TestCase.assertNotNull( componentB );
+ TestCase.assertEquals( Component.STATE_ACTIVE, componentB.getState() );
+ B b = ( B ) componentB.getComponentInstance().getInstance();
+ assertEquals( 1, b.getAs().size() );
+ }
+ /**
+ * A > 1.1 > B > 0..n > A Both should start, but B should not have an A reference.
+ */
+ @Test
+ public void test_A11_B0n_delayed_A_first() throws InvalidSyntaxException
+ {
+ String componentNameA = "4.A.1.1.dynamic";
+ final Component componentA = findComponentByName( componentNameA );
+ TestCase.assertNotNull( componentA );
+ TestCase.assertEquals( Component.STATE_REGISTERED, componentA.getState() );
+
+ String componentNameB = "4.B.0.n.dynamic";
+ final Component componentB = findComponentByName( componentNameB );
+ TestCase.assertNotNull( componentB );
+ TestCase.assertEquals( Component.STATE_REGISTERED, componentB.getState() );
+
+ ServiceReference[] serviceReferences = bundleContext.getServiceReferences( A.class.getName(), "(service.pid=" + componentNameA + ")" );
+ TestCase.assertEquals( 1, serviceReferences.length );
+ ServiceReference serviceReference = serviceReferences[0];
+ Object service = bundleContext.getService( serviceReference );
+ assertNotNull( service );
+
+
+ A a = ( A ) componentA.getComponentInstance().getInstance();
+ assertEquals( 1, a.getBs().size() );
+ B b = ( B ) componentB.getComponentInstance().getInstance();
+ assertEquals( 0, b.getAs().size() );
+ }
+ /**
+ * A > 1.1 > B > 0..n > A Both should start, but B should not have an A reference.
+ */
+ @Test
+ public void test_A11_B0n_delayed_B_first() throws InvalidSyntaxException
+ {
+ String componentNameA = "4.A.1.1.dynamic";
+ final Component componentA = findComponentByName( componentNameA );
+ TestCase.assertNotNull( componentA );
+ TestCase.assertEquals( Component.STATE_REGISTERED, componentA.getState() );
+
+ String componentNameB = "4.B.0.n.dynamic";
+ final Component componentB = findComponentByName( componentNameB );
+ TestCase.assertNotNull( componentB );
+ TestCase.assertEquals( Component.STATE_REGISTERED, componentB.getState() );
+
+ ServiceReference[] serviceReferencesB = bundleContext.getServiceReferences( B.class.getName(), "(service.pid=" + componentNameB + ")" );
+ TestCase.assertEquals( 1, serviceReferencesB.length );
+ ServiceReference serviceReferenceB = serviceReferencesB[0];
+ Object serviceB = bundleContext.getService( serviceReferenceB );
+ assertNotNull( serviceB );
+
+ ServiceReference[] serviceReferencesA = bundleContext.getServiceReferences( A.class.getName(), "(service.pid=" + componentNameA + ")" );
+ TestCase.assertEquals( 1, serviceReferencesA.length );
+ ServiceReference serviceReferenceA = serviceReferencesA[0];
+ Object serviceA = bundleContext.getService( serviceReferenceA );
+ assertNotNull( serviceA );
+
+
+ A a = ( A ) componentA.getComponentInstance().getInstance();
+ assertEquals( 1, a.getBs().size() );
+ B b = ( B ) componentB.getComponentInstance().getInstance();
+ assertEquals( 0, b.getAs().size() );
+
+
+ //disabling (removing the A service registration) and re-enabling will
+ //result in a service event to B, so B will bind A.
+ componentA.disable();
+ delay();
+ componentA.enable();
+ delay();
+ ServiceReference[] serviceReferencesA1 = bundleContext.getServiceReferences( A.class.getName(), "(service.pid=" + componentNameA + ")" );
+ TestCase.assertEquals( 1, serviceReferencesA1.length );
+ ServiceReference serviceReferenceA1 = serviceReferencesA1[0];
+ Object serviceA1 = bundleContext.getService( serviceReferenceA1 );
+ assertNotNull( serviceA1 );
+
+ A a1 = ( A ) componentA.getComponentInstance().getInstance();
+ assertEquals( 1, a1.getBs().size() );
+ B b1 = ( B ) componentB.getComponentInstance().getInstance();
+ assertEquals( 1, b1.getAs().size() );
+
+ }
+}
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 b9d87e7..a9b4f92 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
@@ -98,7 +98,7 @@
@ProbeBuilder
public TestProbeBuilder extendProbe(TestProbeBuilder builder) {
- builder.setHeader("Export-Package", "org.apache.felix.scr.integration.components,org.apache.felix.scr.integration.components.activatesignature");
+ builder.setHeader("Export-Package", "org.apache.felix.scr.integration.components,org.apache.felix.scr.integration.components.activatesignature,org.apache.felix.scr.integration.components.circular");
builder.setHeader("Import-Package", "org.apache.felix.scr,org.apache.felix.scr.component;mandatory:=\"status\"; status=\"provisional\"");
builder.setHeader("Bundle-ManifestVersion", "2");
return builder;
@@ -352,7 +352,7 @@
.set(Constants.BUNDLE_SYMBOLICNAME, "simplecomponent")
.set(Constants.BUNDLE_VERSION, "0.0.11")
.set(Constants.IMPORT_PACKAGE,
- "org.apache.felix.scr.integration.components,org.apache.felix.scr.integration.components.activatesignature")
+ "org.apache.felix.scr.integration.components,org.apache.felix.scr.integration.components.activatesignature,org.apache.felix.scr.integration.components.circular")
.set("Service-Component", "OSGI-INF/components.xml")
.build(withBnd());
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/circular/A.java b/scr/src/test/java/org/apache/felix/scr/integration/components/circular/A.java
new file mode 100644
index 0000000..db9e4ac
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/circular/A.java
@@ -0,0 +1,58 @@
+/*
+ * 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.circular;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.service.component.ComponentContext;
+
+/**
+ * @version $Rev:$ $Date:$
+ */
+public class A
+{
+
+ private List<B> bs = new ArrayList<B>();
+
+ private boolean activated;
+
+ private void activate(ComponentContext cc)
+ {
+ activated = true;
+ }
+
+ private void setB(B b)
+ {
+ bs.add( b );
+ }
+
+ private void unsetB(B b)
+ {
+ bs.remove( b );
+ }
+
+ public List<B> getBs()
+ {
+ return bs;
+ }
+
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/circular/B.java b/scr/src/test/java/org/apache/felix/scr/integration/components/circular/B.java
new file mode 100644
index 0000000..e9dd112
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/circular/B.java
@@ -0,0 +1,58 @@
+/*
+ * 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.circular;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.service.component.ComponentContext;
+
+/**
+ * @version $Rev:$ $Date:$
+ */
+public class B
+{
+
+ private List<A> as = new ArrayList<A>();
+
+ private boolean activated;
+
+ private void activate(ComponentContext cc)
+ {
+ activated = true;
+ }
+
+ private void setA(A b)
+ {
+ as.add( b );
+ }
+
+ private void unsetA(A b)
+ {
+ as.remove( b );
+ }
+
+ public List<A> getAs()
+ {
+ return as;
+ }
+
+}
diff --git a/scr/src/test/resources/integration_test_circular.xml b/scr/src/test/resources/integration_test_circular.xml
new file mode 100644
index 0000000..2a408c8
--- /dev/null
+++ b/scr/src/test/resources/integration_test_circular.xml
@@ -0,0 +1,181 @@
+<?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.1.0">
+
+ <!-- A, B both 1.1 dynamic. Neither should register or activate -->
+ <scr:component name="1.A.1.1.dynamic"
+ enabled="true"
+ configuration-policy="ignore">
+ <implementation class="org.apache.felix.scr.integration.components.circular.A" />
+ <service>
+ <provide interface="org.apache.felix.scr.integration.components.circular.A" />
+ </service>
+ <reference
+ name="b"
+ interface="org.apache.felix.scr.integration.components.circular.B"
+ cardinality="1..1"
+ policy="dynamic"
+ bind="setB"
+ unbind="unsetB"
+ target="(service.pid=1.B.1.1.dynamic)"
+ />
+ <property name="service.pid" value="1.A.1.1.dynamic" />
+ </scr:component>
+ <scr:component name="1.B.1.1.dynamic"
+ enabled="true"
+ configuration-policy="ignore">
+ <implementation class="org.apache.felix.scr.integration.components.circular.B" />
+ <service>
+ <provide interface="org.apache.felix.scr.integration.components.circular.B" />
+ </service>
+ <reference
+ name="a"
+ interface="org.apache.felix.scr.integration.components.circular.A"
+ cardinality="1..1"
+ policy="dynamic"
+ bind="setA"
+ unbind="unsetA"
+ target="(service.pid=1.A.1.1.dynamic)"
+ />
+ <property name="service.pid" value="1.B.1.1.dynamic" />
+ </scr:component>
+
+ <!-- A 1.1 dynamic. B 0..n dynamic both immediate. Both should start (A first) and B should have no reference to A -->
+ <scr:component name="2.A.1.1.dynamic"
+ enabled="true"
+ immediate="true"
+ configuration-policy="ignore">
+ <implementation class="org.apache.felix.scr.integration.components.circular.A" />
+ <service>
+ <provide interface="org.apache.felix.scr.integration.components.circular.A" />
+ </service>
+ <reference
+ name="b"
+ interface="org.apache.felix.scr.integration.components.circular.B"
+ cardinality="1..1"
+ policy="dynamic"
+ bind="setB"
+ unbind="unsetB"
+ target="(service.pid=2.B.0.n.dynamic)"
+ />
+ <property name="service.pid" value="2.A.1.1.dynamic" />
+ </scr:component>
+ <scr:component name="2.B.0.n.dynamic"
+ enabled="true"
+ immediate="true"
+ configuration-policy="ignore">
+ <implementation class="org.apache.felix.scr.integration.components.circular.B" />
+ <service>
+ <provide interface="org.apache.felix.scr.integration.components.circular.B" />
+ </service>
+ <reference
+ name="a"
+ interface="org.apache.felix.scr.integration.components.circular.A"
+ cardinality="0..n"
+ policy="dynamic"
+ bind="setA"
+ unbind="unsetA"
+ target="(service.pid=2.A.1.1.dynamic)"
+ />
+ <property name="service.pid" value="2.B.0.n.dynamic" />
+ </scr:component>
+
+ <!-- A 1.1 dynamic. B 0..n dynamic both immediate. Both should start (B first) and B should have a reference to A -->
+ <scr:component name="3.B.0.n.dynamic"
+ enabled="true"
+ immediate="true"
+ configuration-policy="ignore">
+ <implementation class="org.apache.felix.scr.integration.components.circular.B" />
+ <service>
+ <provide interface="org.apache.felix.scr.integration.components.circular.B" />
+ </service>
+ <reference
+ name="a"
+ interface="org.apache.felix.scr.integration.components.circular.A"
+ cardinality="0..n"
+ policy="dynamic"
+ bind="setA"
+ unbind="unsetA"
+ target="(service.pid=3.A.1.1.dynamic)"
+ />
+ <property name="service.pid" value="3.B.0.n.dynamic" />
+ </scr:component>
+ <scr:component name="3.A.1.1.dynamic"
+ enabled="true"
+ immediate="true"
+ configuration-policy="ignore">
+ <implementation class="org.apache.felix.scr.integration.components.circular.A" />
+ <service>
+ <provide interface="org.apache.felix.scr.integration.components.circular.A" />
+ </service>
+ <reference
+ name="b"
+ interface="org.apache.felix.scr.integration.components.circular.B"
+ cardinality="1..1"
+ policy="dynamic"
+ bind="setB"
+ unbind="unsetB"
+ target="(service.pid=3.B.0.n.dynamic)"
+ />
+ <property name="service.pid" value="3.A.1.1.dynamic" />
+ </scr:component>
+
+ <!-- A 1.1 dynamic. B 0..n dynamic both delayed. Both should start and B should have no reference to A -->
+ <scr:component name="4.A.1.1.dynamic"
+ enabled="true"
+ immediate="false"
+ configuration-policy="ignore">
+ <implementation class="org.apache.felix.scr.integration.components.circular.A" />
+ <service>
+ <provide interface="org.apache.felix.scr.integration.components.circular.A" />
+ </service>
+ <reference
+ name="b"
+ interface="org.apache.felix.scr.integration.components.circular.B"
+ cardinality="1..1"
+ policy="dynamic"
+ bind="setB"
+ unbind="unsetB"
+ target="(service.pid=4.B.0.n.dynamic)"
+ />
+ <property name="service.pid" value="4.A.1.1.dynamic" />
+ </scr:component>
+ <scr:component name="4.B.0.n.dynamic"
+ enabled="true"
+ immediate="false"
+ configuration-policy="ignore">
+ <implementation class="org.apache.felix.scr.integration.components.circular.B" />
+ <service>
+ <provide interface="org.apache.felix.scr.integration.components.circular.B" />
+ </service>
+ <reference
+ name="a"
+ interface="org.apache.felix.scr.integration.components.circular.A"
+ cardinality="0..n"
+ policy="dynamic"
+ bind="setA"
+ unbind="unsetA"
+ target="(service.pid=4.A.1.1.dynamic)"
+ />
+ <property name="service.pid" value="4.B.0.n.dynamic" />
+ </scr:component>
+
+
+</components>