You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2013/10/08 12:00:39 UTC

svn commit: r1530204 - in /felix/trunk/dependencymanager/test2: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/felix/ src/main/java/org/apache/felix/dependencymanager/ src/main/java/org/apache/fel...

Author: pderop
Date: Tue Oct  8 10:00:38 2013
New Revision: 1530204

URL: http://svn.apache.org/r1530204
Log:
FELIX-3910: pax-exam 3.0.0 migration tests (work in progress). Added new AspectRaceTest for
the FELIX-3910 issue.

Added:
    felix/trunk/dependencymanager/test2/
    felix/trunk/dependencymanager/test2/pom.xml   (with props)
    felix/trunk/dependencymanager/test2/src/
    felix/trunk/dependencymanager/test2/src/main/
    felix/trunk/dependencymanager/test2/src/main/java/
    felix/trunk/dependencymanager/test2/src/main/java/org/
    felix/trunk/dependencymanager/test2/src/main/java/org/apache/
    felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/
    felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/
    felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/
    felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/
    felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/Ensure.java
    felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/SimpleAnnotations.java
    felix/trunk/dependencymanager/test2/src/test/
    felix/trunk/dependencymanager/test2/src/test/java/
    felix/trunk/dependencymanager/test2/src/test/java/org/
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/annotations/
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/annotations/SimpleAnnotationsTest.java
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/api/
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/api/AspectRaceTest.java
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/common/
    felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/common/TestBase.java

Added: felix/trunk/dependencymanager/test2/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/test2/pom.xml?rev=1530204&view=auto
==============================================================================
Binary file - no diff available.

Propchange: felix/trunk/dependencymanager/test2/pom.xml
------------------------------------------------------------------------------
    svn:mime-type = application/xml

Added: felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/Ensure.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/Ensure.java?rev=1530204&view=auto
==============================================================================
--- felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/Ensure.java (added)
+++ felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/Ensure.java Tue Oct  8 10:00:38 2013
@@ -0,0 +1,172 @@
+/*
+ * 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.dependencymanager.test2.components;
+
+import java.io.PrintStream;
+
+import junit.framework.Assert;
+
+/**
+ * Helper class to make sure that steps in a test happen in the correct order. Instantiate
+ * this class and subsequently invoke <code>step(nr)</code> with steps starting at 1. You
+ * can also have threads wait until you arrive at a certain step.
+ */
+public class Ensure {
+    private final boolean DEBUG;
+    private static long INSTANCE = 0;
+    private static final int RESOLUTION = 100;
+    private static PrintStream STREAM = System.out;
+    int step = 0;
+    private Throwable m_throwable;
+    
+    public Ensure() {
+        this(true);
+    }
+    
+    public Ensure(boolean debug) {
+        DEBUG = debug;
+        if (DEBUG) {
+            INSTANCE++;
+        }
+    }
+
+    public void setStream(PrintStream output) {
+        STREAM = output;
+    }
+    
+    /**
+     * Mark this point as step <code>nr</code>.
+     * 
+     * @param nr the step we are in
+     */
+    public synchronized void step(int nr) {
+        step ++;
+        Assert.assertEquals(nr, step);
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] step " + step + " [" + currentThread() + "] " + info);
+        }
+        notifyAll();
+    }
+
+    private String getLineInfo(int depth) {
+        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+        String info = trace[depth].getClassName() + "." + trace[depth].getMethodName() + ":" + trace[depth].getLineNumber();
+        return info;
+    }
+    
+    /**
+     * Mark this point as the next step.
+     */
+    public synchronized void step() {
+        step++;
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] next step " + step + " [" + currentThread() + "] " + info);
+        }
+        notifyAll();
+    }
+
+    /**
+     * Wait until we arrive at least at step <code>nr</code> in the process, or fail if that
+     * takes more than <code>timeout</code> milliseconds. If you invoke wait on a thread,
+     * you are effectively assuming some other thread will invoke the <code>step(nr)</code>
+     * method.
+     * 
+     * @param nr the step to wait for
+     * @param timeout the number of milliseconds to wait
+     */
+    public synchronized void waitForStep(int nr, int timeout) {
+        final int initialTimeout = timeout;
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] waiting for step " + nr + " [" + currentThread() + "] " + info);
+        }
+        while (step < nr && timeout > 0) {
+            try {
+                wait(RESOLUTION);
+                timeout -= RESOLUTION;
+            }
+            catch (InterruptedException e) {}
+        }
+        if (step < nr) {
+            throw new IllegalStateException("Timed out waiting for " + initialTimeout + " ms for step " + nr + ", we are still at step " + step);
+        }
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] arrived at step " + nr + " [" + currentThread() + "] " + info);
+        }
+    }
+    
+    private String currentThread() {
+        Thread thread = Thread.currentThread();
+        return thread.getId() + " " + thread.getName();
+    }
+    
+    public static Runnable createRunnableStep(final Ensure ensure, final int nr) {
+        return new Runnable() { public void run() { ensure.step(nr); }};
+    }
+    
+    public synchronized void steps(Steps steps) {
+        steps.next(this);
+    }
+    
+    /** 
+     * Helper class for naming a list of step numbers. If used with the steps(Steps) method
+     * you can define at which steps in time this point should be passed. That means you can
+     * check methods that will get invoked multiple times during a test.
+     */
+    public static class Steps {
+        private final int[] m_steps;
+        private int m_stepIndex;
+
+        /** 
+         * Create a list of steps and initialize the step counter to zero.
+         */
+        public Steps(int... steps) {
+            m_steps = steps;
+            m_stepIndex = 0;
+        }
+
+        /**
+         * Ensure we're at the right step. Will throw an index out of bounds exception if we enter this step more often than defined.
+         */
+        public void next(Ensure ensure) {
+            ensure.step(m_steps[m_stepIndex++]);
+        }
+    }
+
+    /**
+     * Saves a thrown exception that occurred in a different thread. You can only save one exception
+     * at a time this way.
+     */
+    public synchronized void throwable(Throwable throwable) {
+        m_throwable = throwable;
+    }
+
+    /**
+     * Throws a <code>Throwable</code> if one occurred in a different thread and that thread saved it
+     * using the <code>throwable()</code> method.
+     */
+    public synchronized void ensure() throws Throwable {
+        if (m_throwable != null) {
+            throw m_throwable;
+        }
+    }
+}

Added: felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/SimpleAnnotations.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/SimpleAnnotations.java?rev=1530204&view=auto
==============================================================================
--- felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/SimpleAnnotations.java (added)
+++ felix/trunk/dependencymanager/test2/src/main/java/org/apache/felix/dependencymanager/test2/components/SimpleAnnotations.java Tue Oct  8 10:00:38 2013
@@ -0,0 +1,176 @@
+/*
+ * 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.dependencymanager.test2.components;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Inject;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.Registered;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.annotation.api.Stop;
+import org.apache.felix.dm.annotation.api.Unregistered;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+
+public class SimpleAnnotations {
+    /**
+     * Provides a <code>Runnable</code> service, which is required by the
+     * {@link Consumer} class.
+     */
+    @Component(properties = {@Property(name = "foo", value = "bar")})
+    public static class Producer implements Runnable {
+        @ServiceDependency(filter = "(name=simple.annotations.producer)")
+        volatile Ensure _ensure;
+
+        @ServiceDependency
+        volatile LogService _logService;
+
+        @Inject
+        volatile BundleContext _ctx;
+
+        @Init
+        protected void init() {
+            _logService.log(LogService.LOG_INFO, "producer.init");
+            // Our component is initializing (at this point: all required
+            // dependencies are injected).
+            _ensure.step(1);
+        }
+
+        @Start
+        protected void start() {
+            // We are about to be registered in the OSGi registry.
+            _ensure.step(2);
+        }
+
+        @Registered
+        protected void registered(ServiceRegistration sr) {
+            _logService.log(LogService.LOG_INFO, "Registered");
+            if (sr == null) {
+                _ensure.throwable(new Exception("ServiceRegistration is null"));
+            }
+            if (!"bar".equals(sr.getReference().getProperty("foo"))) {
+                _ensure.throwable(new Exception("Invalid Service Properties"));
+            }
+            _ensure.step(3);
+        }
+
+        public void run() {
+            _ensure.step(5);
+        }
+
+        @Stop
+        protected void stop() {
+            // We are about to be unregistered from the OSGi registry, and we
+            // must stop.
+            _ensure.step(8);
+        }
+
+        @Unregistered
+        protected void stopped() {
+            // We are unregistered from the OSGi registry.
+            _ensure.step(9);
+        }
+
+        @Destroy
+        public void destroy() {
+            // Our component is shutting down.
+            _ensure.step(10);
+        }
+    }
+
+    /**
+     * Consumes a service which is provided by the {@link Producer} class.
+     */
+    @Component
+    public static class Consumer {
+        @ServiceDependency
+        volatile LogService _logService;
+
+        @ServiceDependency
+        volatile Runnable _runnable;
+
+        @ServiceDependency(filter = "(name=simple.annotations.consumer)")
+        volatile Ensure _ensure;
+
+        @Inject
+        volatile BundleContext _bc;
+        BundleContext _bcNotInjected;
+
+        @Inject
+        volatile DependencyManager _dm;
+        DependencyManager _dmNotInjected;
+
+        @Inject
+        volatile org.apache.felix.dm.Component _component;
+        org.apache.felix.dm.Component _componentNotInjected;
+
+        @Start
+        protected void start() {
+            _logService.log(LogService.LOG_INFO, "Consumer.START: ");
+            checkInjectedFields();
+            _ensure.step(4);
+            _runnable.run();
+        }
+        
+        private void checkInjectedFields() {
+            if (_bc == null) {
+                _ensure.throwable(new Exception("Bundle Context not injected"));
+                return;
+            }
+            if (_bcNotInjected != null) {
+                _ensure.throwable(new Exception("Bundle Context must not be injected"));
+                return;
+            }
+
+            if (_dm == null) {
+                _ensure.throwable(new Exception("DependencyManager not injected"));
+                return;
+            }
+            if (_dmNotInjected != null) {
+                _ensure.throwable(new Exception("DependencyManager must not be injected"));
+                return;
+            }
+
+            if (_component == null) {
+                _ensure.throwable(new Exception("Component not injected"));
+                return;
+            }
+            if (_componentNotInjected != null) {
+                _ensure.throwable(new Exception("Component must not be injected"));
+                return;
+            }
+        }
+
+        @Stop
+        protected void stop() {
+            _ensure.step(6);
+        }
+        
+        @Destroy
+        void destroy() {
+            _ensure.step(7);
+        }
+    }
+}

Added: felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/annotations/SimpleAnnotationsTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/annotations/SimpleAnnotationsTest.java?rev=1530204&view=auto
==============================================================================
--- felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/annotations/SimpleAnnotationsTest.java (added)
+++ felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/annotations/SimpleAnnotationsTest.java Tue Oct  8 10:00:38 2013
@@ -0,0 +1,51 @@
+/*
+* 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.dependencymanager.test2.integration.annotations;
+
+import org.apache.felix.dependencymanager.test2.components.Ensure;
+import org.apache.felix.dependencymanager.test2.components.SimpleAnnotations;
+import org.apache.felix.dependencymanager.test2.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Ensure that a Provider can be injected into a Consumer, using simple DM annotations.
+ */
+@RunWith(PaxExam.class)
+public class SimpleAnnotationsTest extends TestBase
+{    
+	@Test
+	public void testSimpleAnnotations() throws Throwable
+    {
+	    Ensure e = new Ensure();
+		ServiceRegistration er = register(e, "simple.annotations.producer");        
+        e.waitForStep(3, 10000); // Producer registered
+        ServiceRegistration er2 = register(e, "simple.annotations.consumer"); 
+
+        er2.unregister(); // stop consumer
+        er.unregister(); // stop provider
+
+        // And check if components have been deactivated orderly.
+        e.waitForStep(10, 10000);
+        e.ensure();
+        stopTestComponentsBundle();
+    }        
+}

Added: felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/api/AspectRaceTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/api/AspectRaceTest.java?rev=1530204&view=auto
==============================================================================
--- felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/api/AspectRaceTest.java (added)
+++ felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/api/AspectRaceTest.java Tue Oct  8 10:00:38 2013
@@ -0,0 +1,341 @@
+/*
+ * 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.dependencymanager.test2.integration.api;
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dependencymanager.test2.components.Ensure;
+import org.apache.felix.dependencymanager.test2.integration.common.TestBase;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+@RunWith(PaxExam.class)
+public class AspectRaceTest extends TestBase {
+	volatile ExecutorService _serviceExec;
+	volatile ExecutorService _aspectExec;
+	volatile DependencyManager _dm;
+	final static int SERVICES = 3;
+	final static int ASPECTS_PER_SERVICE = 10;
+
+	@Inject
+	private static volatile BundleContext _bctx;
+
+	@Test
+	public void testConcurrentAspects() {
+		try {
+			warn("starting aspect race test");
+			int cores = Runtime.getRuntime().availableProcessors();
+			// Used to inject S services
+			_serviceExec = Executors.newFixedThreadPool(cores);
+			// Used to inject S Aspects
+			_aspectExec = Executors.newFixedThreadPool(cores);
+
+			// Setup test components using dependency manager.
+			// We create a Controller which is injected with some S services,
+			// and each S services has some aspects (SAspect).
+
+			_dm = new DependencyManager(_bctx);
+			Controller controller = new Controller();
+			Component c = _dm
+					.createComponent()
+					.setImplementation(controller)
+					.setInterface(Controller.class.getName(), null)
+					.setComposition("getComposition")
+					.add(_dm.createServiceDependency().setService(S.class)
+							.setCallbacks("bind", null, "unbind", "swap")
+							.setRequired(true));
+
+			_dm.add(c);
+
+			for (int loop = 1; loop <= 10000; loop++) {
+				// Perform concurrent injections of "S" service and S aspects
+				// into the Controller component;
+				Factory f = new Factory();
+				f.register();
+
+				controller.check();
+
+				// unregister all services and aspects concurrently
+				f.unregister();
+
+				if ((loop) % 100 == 0) {
+					warn("Performed " + loop + " tests.");
+				}
+			}
+
+			if (super.errorsLogged()) {
+				Assert.assertFalse("Test failed.", false);
+			}
+		}
+
+		catch (Throwable t) {
+			error("Test failed", t);
+			Assert.fail("Test failed: " + t.getMessage());
+		} finally {
+			shutdown(_serviceExec);
+			shutdown(_aspectExec);
+		}
+	}
+
+	void shutdown(ExecutorService exec) {
+		exec.shutdown();
+		try {
+			exec.awaitTermination(5, TimeUnit.SECONDS);
+		} catch (InterruptedException e) {
+		}
+	}
+
+	public interface S {
+		void invoke(Ensure e);
+	}
+
+	public static class SImpl implements S {
+		final int _step;
+
+		SImpl(int step) {
+			_step = step;
+		}
+
+		public void invoke(Ensure e) {
+			e.step(_step);
+		}
+
+		public String toString() {
+			return "SImpl[" + _step + "]";
+		}
+	}
+
+	public static class SAspect implements S {
+		volatile S _next;
+		final int _rank;
+		final int _step;
+
+		SAspect(int rank) {
+			_rank = rank;
+			_step = ASPECTS_PER_SERVICE - rank + 1;
+		}
+
+		public void added(S s) {
+			_next = s;
+		}
+
+		public void swap(S oldS, S newS) {
+			_next = newS;
+		}
+
+		public void invoke(Ensure e) {
+			e.step(_step);
+			_next.invoke(e);
+		}
+
+		public String toString() {
+			return "SAspect[" + _rank + ", " + _step + "]";
+		}
+	}
+
+	class Factory {
+		final ConcurrentLinkedQueue<Component> m_services = new ConcurrentLinkedQueue<Component>();
+		final ConcurrentLinkedQueue<Component> m_aspects = new ConcurrentLinkedQueue<Component>();
+
+		public void register() throws InterruptedException {
+			final CountDownLatch latch = new CountDownLatch(SERVICES
+					+ (ASPECTS_PER_SERVICE * SERVICES));
+
+			for (int i = 1; i <= SERVICES; i++) {
+				final int serviceId = i;
+				_serviceExec.execute(new Runnable() {
+					public void run() {
+						try {
+							Component c = _dm.createComponent();
+							Hashtable<String, String> props = new Hashtable<String, String>();
+							props.put("id", String.valueOf(serviceId));
+							c.setInterface(S.class.getName(), props)
+									.setImplementation(
+											new SImpl(ASPECTS_PER_SERVICE + 1));
+							m_services.add(c);
+							_dm.add(c);
+							latch.countDown();
+						} catch (Throwable e) {
+							error(e);
+						}
+					}
+				});
+
+				for (int j = 1; j <= ASPECTS_PER_SERVICE; j++) {
+					final int rank = j;
+					_aspectExec.execute(new Runnable() {
+						public void run() {
+							try {
+								SAspect sa = new SAspect(rank);
+								Component aspect = _dm.createAspectService(
+										S.class, "(id=" + serviceId + ")",
+										rank, "added", null, null, "swap")
+										.setImplementation(sa);
+								debug("adding aspect " + sa);
+								m_aspects.add(aspect);
+								_dm.add(aspect);
+								latch.countDown();
+							} catch (Throwable e) {
+								error(e);
+							}
+						}
+					});
+				}
+			}
+
+			if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
+				throw new IllegalStateException(
+						"could not register services and aspects timely");
+			}
+
+			debug("all registered: aspects=" + m_aspects);
+			// Thread.sleep(5000);
+		}
+
+		public void unregister() throws InterruptedException,
+				InvalidSyntaxException {
+			final CountDownLatch latch = new CountDownLatch(SERVICES
+					+ (ASPECTS_PER_SERVICE * SERVICES));
+
+			unregisterAspects(latch);
+			unregisterServices(latch);
+
+			if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
+				throw new IllegalStateException(
+						"could not unregister services and aspects timely");
+			}
+
+			if (_bctx.getServiceReference(S.class.getName()) != null) {
+				error("could not unregister some services or aspects !");
+			}
+			debug("unregistered all aspects and services concurrently");
+		}
+
+		public void unregisterAspects(final CountDownLatch latch)
+				throws InterruptedException, InvalidSyntaxException {
+			Component c;
+			debug("unregister: aspects=" + m_aspects);
+
+			while ((c = m_aspects.poll()) != null) {
+				final Component c$ = c;
+				_serviceExec.execute(new Runnable() {
+					public void run() {
+						try {
+							debug("removing service " + c$);
+							_dm.remove(c$);
+							latch.countDown();
+						} catch (Throwable e) {
+							error(e);
+						}
+					}
+				});
+			}
+		}
+
+		public void unregisterServices(final CountDownLatch latch)
+				throws InterruptedException {
+			Component c;
+			debug("unregister: services=" + m_services);
+
+			while ((c = m_services.poll()) != null) {
+				final Component c$ = c;
+				_serviceExec.execute(new Runnable() {
+					public void run() {
+						try {
+							debug("removing service " + c$);
+							_dm.remove(c$);
+							latch.countDown();
+						} catch (Throwable e) {
+							error(e);
+						}
+					}
+				});
+			}
+
+			debug("unregistered all services");
+		}
+	}
+
+	public class Controller {
+		final Composition m_compo = new Composition();
+		final HashSet<S> _services = new HashSet<S>();
+
+		Object[] getComposition() {
+			return new Object[] { this, m_compo };
+		}
+
+		void bind(ServiceReference sr) {
+			S s = (S) sr.getBundle().getBundleContext().getService(sr);
+			if (s == null) {
+				throw new IllegalStateException(
+						"bindA: bundleContext.getService returned null");
+			}
+			debug("bind " + s);
+			synchronized (this) {
+				_services.add(s);
+			}
+		}
+
+		void swap(S previous, S current) {
+			debug("swap: " + previous + "," + current);
+			synchronized (this) {
+				if (!_services.remove(previous)) {
+					error("swap: unknow previous counter: " + previous);
+				}
+				_services.add(current);
+			}
+		}
+
+		void unbind(S a) {
+			debug("unbind " + a);
+			synchronized (this) {
+				_services.remove(a);
+			}
+		}
+
+		void check() {
+			synchronized (this) {
+				for (S s : _services) {
+					debug("checking service: " + s + " ...");
+					Ensure ensure = new Ensure(false);
+					s.invoke(ensure);
+				}
+			}
+		}
+	}
+
+	public static class Composition {
+	}
+}

Added: felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/common/TestBase.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/common/TestBase.java?rev=1530204&view=auto
==============================================================================
--- felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/common/TestBase.java (added)
+++ felix/trunk/dependencymanager/test2/src/test/java/org/apache/felix/dependencymanager/test2/integration/common/TestBase.java Tue Oct  8 10:00:38 2013
@@ -0,0 +1,313 @@
+/*
+ * 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.dependencymanager.test2.integration.common;
+
+import static org.ops4j.pax.exam.CoreOptions.bundle;
+import static org.ops4j.pax.exam.CoreOptions.cleanCaches;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.exam.CoreOptions.systemTimeout;
+import static org.ops4j.pax.exam.CoreOptions.vmOption;
+import static org.ops4j.pax.exam.CoreOptions.workingDirectory;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Hashtable;
+
+import javax.inject.Inject;
+
+import org.apache.felix.dependencymanager.test2.components.Ensure;
+import org.junit.After;
+import org.junit.Before;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.OptionUtils;
+import org.ops4j.pax.exam.ProbeBuilder;
+import org.ops4j.pax.exam.TestProbeBuilder;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+
+public abstract class TestBase implements LogService, FrameworkListener {
+    public static final String OSGI_SPEC_VERSION = "4.2.0";
+    private final static int LOG_LEVEL = LogService.LOG_WARNING;
+    private volatile boolean m_errorsLogged;
+
+    // the name of the system property providing the bundle file to be installed
+    // and tested
+    protected static final String BUNDLE_JAR_SYS_PROP = "project.bundle.file";
+
+    // the default bundle jar file name
+    protected static final String BUNDLE_JAR_DEFAULT = "target/org.apache.felix.dependencymanager.test2-3.0.2-SNAPSHOT.jar";
+
+    // 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;
+
+    @Inject
+    protected BundleContext context;
+
+    protected ServiceRegistration logService;
+
+    @Configuration
+    public static Option[] configuration() {
+        final String bundleFileName = System.getProperty(BUNDLE_JAR_SYS_PROP, BUNDLE_JAR_DEFAULT);
+        final File bundleFile = new File(bundleFileName);
+        if (!bundleFile.canRead()) {
+            throw new IllegalArgumentException("Cannot read from bundle file " + bundleFileName
+                    + " specified in the " + BUNDLE_JAR_SYS_PROP + " system property");
+        }
+
+        final Option[] base = options(
+                workingDirectory("target/paxexam/"),
+                systemProperty("dm.runtime.log").value("false"),
+                systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("WARN"),
+                systemTimeout(5000),
+                cleanCaches(true),
+                junitBundles(),
+                mavenBundle("org.ops4j.pax.tinybundles", "tinybundles", "1.0.0"),
+                mavenBundle("org.apache.felix", "org.apache.felix.dependencymanager",
+                        "3.1.1-SNAPSHOT"),
+                mavenBundle("org.apache.felix", "org.apache.felix.dependencymanager.runtime",
+                        "3.1.1-SNAPSHOT"),
+                mavenBundle("org.apache.felix", "org.apache.felix.configadmin", "1.6.0"),
+                mavenBundle("org.apache.felix", "org.apache.felix.metatype", "1.0.8"),
+                bundle(bundleFile.toURI().toString()));
+        final Option option = (paxRunnerVmOption != null) ? vmOption(paxRunnerVmOption) : null;
+        return OptionUtils.combine(base, option);
+    }
+
+    @ProbeBuilder
+    public TestProbeBuilder buildProbe(TestProbeBuilder builder) {
+        return builder.setHeader(Constants.IMPORT_PACKAGE,
+                "org.apache.felix.dependencymanager.test2.components");
+    }
+
+    @Before
+    public void setUp() {
+        logService = context.registerService(LogService.class.getName(), this, null);
+        context.addFrameworkListener(this);
+    }
+
+    @After
+    public void tearDown() throws BundleException {
+       logService.unregister();
+        context.removeFrameworkListener(this);
+    }
+
+    /**
+     * Create and provide an Ensure object with a name service property.
+     */
+    protected ServiceRegistration register(Ensure e, String name) {
+        Hashtable<String, String> props = new Hashtable<String, String>();
+        props.put("name", name);
+        return context.registerService(Ensure.class.getName(), e, props);
+    }
+
+    /**
+     * Helper method used to stop a given bundle.
+     * 
+     * @param symbolicName
+     *            the symbolic name of the bundle to be stopped.
+     * @param context
+     *            the context used to lookup all installed bundles.
+     */
+    protected void stopBundle(String symbolicName) {
+        // Stop the test.annotation bundle
+        boolean found = false;
+        for (Bundle b : context.getBundles()) {
+            if (b.getSymbolicName().equals(symbolicName)) {
+                try {
+                    found = true;
+                    b.stop();
+                } catch (BundleException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        if (!found) {
+            throw new IllegalStateException("bundle " + symbolicName + " not found");
+        }
+    }
+
+    /**
+     * Stops our test components bundle.
+     */
+    protected void stopTestComponentsBundle() {
+        stopBundle("org.apache.felix.dependencymanager.test2.components");
+    }
+
+    /**
+     * Suspend the current thread for a while.
+     * 
+     * @param n
+     *            the number of milliseconds to wait for.
+     */
+    protected void sleep(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    public void log(int level, String message) {
+        checkError(level, null);
+        if (LOG_LEVEL >= level) {
+            System.out.println(getLevel(level) + " - " + Thread.currentThread().getName() + " : " + message);
+        }
+    }
+
+    public void log(int level, String message, Throwable exception) {
+        checkError(level, exception);
+        if (LOG_LEVEL >= level) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(getLevel(level) + " - " + Thread.currentThread().getName() + " : ");
+            sb.append(message);
+            parse(sb, exception);
+            System.out.println(sb.toString());
+        }
+    }
+
+    public void log(ServiceReference sr, int level, String message) {
+        checkError(level, null);
+        if (LOG_LEVEL >= level) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(getLevel(level) + " - " + Thread.currentThread().getName() + " : ");
+            sb.append(message);
+            System.out.println(sb.toString());
+        }
+    }
+
+    public void log(ServiceReference sr, int level, String message, Throwable exception) {
+        checkError(level, exception);
+        if (LOG_LEVEL >= level) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(getLevel(level) + " - " + Thread.currentThread().getName() + " : ");
+            sb.append(message);
+            parse(sb, exception);
+            System.out.println(sb.toString());
+        }
+    }
+
+    protected boolean errorsLogged() {
+        return m_errorsLogged;
+    }
+
+    private void parse(StringBuilder sb, Throwable t) {
+        if (t != null) {
+            sb.append(" - ");
+            StringWriter buffer = new StringWriter();
+            PrintWriter pw = new PrintWriter(buffer);
+            t.printStackTrace(pw);
+            sb.append(buffer.toString());
+            m_errorsLogged = true;
+        }
+    }
+
+    private String getLevel(int level) {
+        switch (level) {
+            case LogService.LOG_DEBUG :
+                return "DEBUG";
+            case LogService.LOG_ERROR :
+                return "ERROR";
+            case LogService.LOG_INFO :
+                return "INFO";
+            case LogService.LOG_WARNING :
+                return "WARN";
+            default :
+                return "";
+        }
+    }
+
+    private void checkError(int level, Throwable exception) {
+        if (level >= LOG_ERROR) {
+            m_errorsLogged = true;
+        }
+        if (exception != null) {
+            m_errorsLogged = true;
+        }
+    }
+
+    public void frameworkEvent(FrameworkEvent event) {
+        int eventType = event.getType();
+        String msg = getFrameworkEventMessage(eventType);
+        int level = (eventType == FrameworkEvent.ERROR) ? LOG_ERROR : LOG_WARNING;
+        if (msg != null) {
+            log(level, msg, event.getThrowable());
+        } else {
+            log(level, "Unknown fwk event: " + event);
+        }
+    }
+
+    private String getFrameworkEventMessage(int event) {
+        switch (event) {
+            case FrameworkEvent.ERROR :
+                return "FrameworkEvent: ERROR";
+            case FrameworkEvent.INFO :
+                return "FrameworkEvent INFO";
+            case FrameworkEvent.PACKAGES_REFRESHED :
+                return "FrameworkEvent: PACKAGE REFRESHED";
+            case FrameworkEvent.STARTED :
+                return "FrameworkEvent: STARTED";
+            case FrameworkEvent.STARTLEVEL_CHANGED :
+                return "FrameworkEvent: STARTLEVEL CHANGED";
+            case FrameworkEvent.WARNING :
+                return "FrameworkEvent: WARNING";
+            default :
+                return null;
+        }
+    }
+
+    protected void warn(String msg) {
+        log(LogService.LOG_WARNING, "[" + Thread.currentThread().getName() + "] " + msg);
+    }
+
+    protected void info(String msg) {
+        log(LogService.LOG_INFO, "[" + Thread.currentThread().getName() + "] " + msg);
+    }
+
+    protected void debug(String msg) {
+        log(LogService.LOG_DEBUG, "[" + Thread.currentThread().getName() + "] " + msg);
+    }
+
+    protected void error(String msg) {
+        log(LogService.LOG_ERROR, "[" + Thread.currentThread().getName() + "] " + msg);
+    }
+
+    protected void error(String msg, Throwable err) {
+        log(LogService.LOG_ERROR, "[" + Thread.currentThread().getName() + "] " + msg, err);
+    }
+
+    protected void error(Throwable err) {
+        log(LogService.LOG_ERROR, "[" + Thread.currentThread().getName() + "] error:", err);
+    }
+}