You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by st...@apache.org on 2015/10/20 16:12:34 UTC

svn commit: r1709601 [6/11] - in /sling/trunk/bundles/extensions/discovery: base/ base/src/ base/src/main/ base/src/main/java/ base/src/main/java/org/ base/src/main/java/org/apache/ base/src/main/java/org/apache/sling/ base/src/main/java/org/apache/sli...

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/AbstractTopologyEventTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/AbstractTopologyEventTest.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/AbstractTopologyEventTest.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/AbstractTopologyEventTest.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,252 @@
+/*
+ * 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.sling.discovery.base.its;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEvent.Type;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.base.its.setup.VirtualInstance;
+import org.apache.sling.discovery.base.its.setup.VirtualInstanceBuilder;
+import org.apache.sling.discovery.base.its.setup.mock.AssertingTopologyEventListener;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test class covering correct sending of TopologyEvents
+ * in various scenarios (which are not covered in other tests already).
+ */
+public abstract class AbstractTopologyEventTest {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private VirtualInstance instance1;
+    private VirtualInstance instance2;
+
+    private Level logLevel;
+    
+    @Before
+    public void setup() throws Exception {
+        final org.apache.log4j.Logger discoveryLogger = LogManager.getRootLogger().getLogger("org.apache.sling.discovery");
+        logLevel = discoveryLogger.getLevel();
+        discoveryLogger.setLevel(Level.DEBUG);
+    }
+    
+    @After
+    public void tearDown() throws Throwable {
+        if (instance1!=null) {
+            instance1.stopViewChecker();
+            instance1.stop();
+            instance1 = null;
+        }
+        if (instance2!=null) {
+            instance2.stopViewChecker();
+            instance2.stop();
+            instance2 = null;
+        }
+        final org.apache.log4j.Logger discoveryLogger = LogManager.getRootLogger().getLogger("org.apache.sling.discovery");
+        discoveryLogger.setLevel(logLevel);
+    }
+    
+    public abstract VirtualInstanceBuilder newBuilder();
+    
+    /**
+     * Tests the fact that the INIT event is delayed until voting has succeeded
+     * (which is the default with SLIGN-5030 and SLING-4959
+     * @throws Throwable 
+     */
+    @Test
+    public void testDelayedInitEvent() throws Throwable {
+        logger.info("testDelayedInitEvent: start");
+        instance1 = newBuilder().setDebugName("firstInstanceA")
+                .newRepository("/var/discovery/impl/", true)
+                .setConnectorPingTimeout(3 /* heartbeat-timeout */)
+                .setMinEventDelay(3 /*min event delay*/).build();
+        AssertingTopologyEventListener l1 = new AssertingTopologyEventListener("instance1.l1");
+        instance1.bindTopologyEventListener(l1);
+        logger.info("testDelayedInitEvent: instance1 created, no events expected yet. slingId="+instance1.slingId);
+        
+        // should not have received any events yet
+        assertEquals(0, l1.getEvents().size());
+        assertEquals(0, l1.getUnexpectedCount());
+
+        // one heartbeat doesn't make a day yet - and is 2sec too early for the init
+        instance1.heartbeatsAndCheckView();
+        Thread.sleep(1200);
+        logger.info("testDelayedInitEvent: even after 500ms no events expected, as it needs more than 1 heartbeat");
+        // should not have received any events yet
+        assertEquals(0, l1.getEvents().size());
+        assertEquals(0, l1.getUnexpectedCount());
+        
+        // but two are a good start
+        l1.addExpected(Type.TOPOLOGY_INIT);
+        instance1.heartbeatsAndCheckView();
+        Thread.sleep(1200);
+        instance1.heartbeatsAndCheckView();
+        Thread.sleep(1200);
+        logger.info("testDelayedInitEvent: 2nd/3rd heartbeat sent - now expecting a TOPOLOGY_INIT");
+        instance1.dumpRepo();
+        assertEquals(1, l1.getEvents().size()); // one event
+        assertEquals(0, l1.getRemainingExpectedCount()); // the expected one
+        assertEquals(0, l1.getUnexpectedCount());
+        
+        logger.info("testDelayedInitEvent: creating instance2");
+        instance2 = newBuilder().setDebugName("secondInstanceB")
+                .useRepositoryOf(instance1)
+                .setConnectorPingTimeout(20)
+                .setMinEventDelay(3).build();
+        logger.info("testDelayedInitEvent: instance2 created with slingId="+instance2.slingId);
+        AssertingTopologyEventListener l2 = new AssertingTopologyEventListener("instance2.l2");
+        instance2.bindTopologyEventListener(l2);
+        logger.info("testDelayedInitEvent: listener instance2.l2 added - it should not get any events though");
+        AssertingTopologyEventListener l1Two = new AssertingTopologyEventListener("instance1.l1Two");
+        l1Two.addExpected(Type.TOPOLOGY_INIT);
+        logger.info("testDelayedInitEvent: listener instance1.l1Two added - it expects an INIT now");
+        instance1.bindTopologyEventListener(l1Two);
+        
+        Thread.sleep(500); // SLING-4755: async event sending requires some minimal wait time nowadays
+
+        // just because instance2 is started doesn't kick off any events yet 
+        // since instance2 didn't send heartbeats yet
+        assertEquals(1, l1.getEvents().size()); // one event
+        assertEquals(0, l1.getRemainingExpectedCount()); // the expected one
+        assertEquals(0, l1.getUnexpectedCount());
+        assertEquals(0, l2.getEvents().size());
+        assertEquals(0, l2.getUnexpectedCount());
+        assertEquals(1, l1Two.getEvents().size());
+        assertEquals(0, l1Two.getRemainingExpectedCount()); // the expected one
+        assertEquals(0, l1Two.getUnexpectedCount());
+        
+        
+        // the second & third heartbeat though triggers the voting etc
+        logger.info("testDelayedInitEvent: two more heartbeats should trigger events");
+        l1.addExpected(Type.TOPOLOGY_CHANGING);
+        l1Two.addExpected(Type.TOPOLOGY_CHANGING);
+        Thread.sleep(500);
+        l2.addExpected(Type.TOPOLOGY_INIT);
+        instance1.heartbeatsAndCheckView();
+        instance2.heartbeatsAndCheckView();
+        Thread.sleep(500);
+        instance1.heartbeatsAndCheckView();
+        instance2.heartbeatsAndCheckView();
+        Thread.sleep(500);
+        instance1.heartbeatsAndCheckView();
+        instance2.heartbeatsAndCheckView();
+        logger.info("testDelayedInitEvent: instance1: "+instance1.slingId);
+        logger.info("testDelayedInitEvent: instance2: "+instance2.slingId);
+        instance1.dumpRepo();
+        assertEquals(0, l1.getUnexpectedCount());
+        assertEquals(2, l1.getEvents().size());
+        assertEquals(0, l2.getUnexpectedCount());
+        assertEquals(1, l2.getEvents().size());
+        assertEquals(0, l1Two.getUnexpectedCount());
+        assertEquals(2, l1Two.getEvents().size());
+
+        // wait until CHANGED is sent - which is 3 sec after CHANGING
+        l1.addExpected(Type.TOPOLOGY_CHANGED);
+        l1Two.addExpected(Type.TOPOLOGY_CHANGED);
+        Thread.sleep(4000);
+        assertEquals(0, l1.getUnexpectedCount());
+        assertEquals(3, l1.getEvents().size()); // one event
+        assertEquals(0, l2.getUnexpectedCount());
+        assertEquals(1, l2.getEvents().size());
+        assertEquals(0, l1Two.getUnexpectedCount());
+        assertEquals(3, l1Two.getEvents().size());
+        logger.info("testDelayedInitEvent: end");
+    }
+    
+    @Test
+    public void testGetDuringDelay() throws Throwable {
+        instance1 = newBuilder().setDebugName("firstInstanceA")
+                .newRepository("/var/discovery/impl/", true)
+                .setConnectorPingTimeout(20 /* heartbeat-timeout */)
+                .setMinEventDelay(6 /* min event delay */).build();
+        AssertingTopologyEventListener l1 = new AssertingTopologyEventListener("instance1.l1");
+        l1.addExpected(TopologyEvent.Type.TOPOLOGY_INIT);
+        instance1.bindTopologyEventListener(l1);
+        
+        TopologyView earlyTopo = instance1.getDiscoveryService().getTopology();
+        assertNotNull(earlyTopo);
+        assertFalse(earlyTopo.isCurrent());
+        assertEquals(1, earlyTopo.getInstances().size());
+        
+        for(int i=0; i<4; i++) {
+            instance1.heartbeatsAndCheckView();
+            Thread.sleep(125);
+        }
+        TopologyView secondTopo = instance1.getDiscoveryService().getTopology();
+        assertEquals(1, secondTopo.getInstances().size());
+        assertEquals(instance1.getSlingId(), secondTopo.getInstances().iterator().next().getSlingId());
+        assertTrue(secondTopo.isCurrent());
+        instance1.dumpRepo();
+        
+        assertEarlyAndFirstClusterViewIdMatches(earlyTopo, secondTopo);
+
+        Thread.sleep(500);
+        // should have gotten the INIT, hence 0 remaining expected events
+        assertEquals(0, l1.getRemainingExpectedCount());
+        assertEquals(0, l1.getUnexpectedCount());
+        
+        l1.addExpected(TopologyEvent.Type.TOPOLOGY_CHANGING);
+        instance2 = newBuilder().setDebugName("secondInstanceB")
+                .useRepositoryOf(instance1)
+                .setConnectorPingTimeout(20)
+                .setMinEventDelay(1).build();
+        AssertingTopologyEventListener l2 = new AssertingTopologyEventListener("instance2.l1");
+        l2.addExpected(TopologyEvent.Type.TOPOLOGY_INIT);
+        instance2.bindTopologyEventListener(l2);
+
+        for(int i=0; i<4; i++) {
+            instance2.heartbeatsAndCheckView();
+            instance1.heartbeatsAndCheckView();
+            Thread.sleep(750);
+        }
+        
+        assertEquals(0, l1.getUnexpectedCount());
+        TopologyView topo2 = instance2.getDiscoveryService().getTopology();
+        assertTrue(topo2.isCurrent());
+        assertEquals(2, topo2.getInstances().size());
+        TopologyView topo1 = instance1.getDiscoveryService().getTopology();
+        assertTrue(topo1.isCurrent());
+        assertEquals(2, topo1.getInstances().size());
+        
+        l1.addExpected(TopologyEvent.Type.TOPOLOGY_CHANGED);
+        Thread.sleep(5000);
+        assertEquals(0, l1.getRemainingExpectedCount());
+        assertEquals(0, l1.getUnexpectedCount());
+        assertEquals(0, l2.getRemainingExpectedCount());
+        assertEquals(0, l2.getUnexpectedCount());
+        assertTrue(instance2.getDiscoveryService().getTopology().isCurrent());
+        assertEquals(2, instance2.getDiscoveryService().getTopology().getInstances().size());
+        assertTrue(instance1.getDiscoveryService().getTopology().isCurrent());
+        assertEquals(2, instance1.getDiscoveryService().getTopology().getInstances().size());
+    }
+
+    public abstract void assertEarlyAndFirstClusterViewIdMatches(TopologyView earlyTopo, TopologyView secondTopo);
+    
+}
\ No newline at end of file

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/AbstractTopologyEventTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/TopologyTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/TopologyTest.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/TopologyTest.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/TopologyTest.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,150 @@
+/*
+ * 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.sling.discovery.base.its;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.base.connectors.DummyVirtualInstanceBuilder;
+import org.apache.sling.discovery.base.connectors.announcement.Announcement;
+import org.apache.sling.discovery.base.its.setup.TopologyHelper;
+import org.apache.sling.discovery.base.its.setup.VirtualConnector;
+import org.apache.sling.discovery.base.its.setup.VirtualInstance;
+import org.apache.sling.discovery.base.its.setup.VirtualInstanceBuilder;
+import org.junit.After;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TopologyTest {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private final List<VirtualInstance> instances = new LinkedList<VirtualInstance>();
+    
+    private VirtualInstanceBuilder newBuilder() {
+        return new DummyVirtualInstanceBuilder();
+    }
+    
+    @After
+    public void tearDown() throws Exception {
+        for (Iterator<VirtualInstance> it = instances.iterator(); it.hasNext();) {
+            final VirtualInstance instance = it.next();
+            instance.stop();
+        }
+    }
+    
+    @Test
+    public void testTwoNodes() throws Throwable {
+        VirtualInstanceBuilder builder1 = newBuilder()
+                .newRepository("/var/discovery/impl/", true)
+                .setDebugName("instance1")
+                .setConnectorPingInterval(20)
+                .setConnectorPingTimeout(200);
+        VirtualInstance instance1 = builder1.build();
+        instances.add(instance1);
+        VirtualInstanceBuilder builder2 = newBuilder()
+                .useRepositoryOf(builder1)
+                .setDebugName("instance2")
+                .setConnectorPingInterval(20)
+                .setConnectorPingTimeout(200);
+        VirtualInstance instance2 = builder2.build();
+        instances.add(instance2);
+        instance1.getConfig().setViewCheckTimeout(8);
+        instance1.getConfig().setViewCheckInterval(1);
+        instance2.getConfig().setViewCheckTimeout(2);
+        instance2.getConfig().setViewCheckInterval(1);
+        
+        for(int i=0; i<5; i++) {
+            instance1.heartbeatsAndCheckView();
+            instance2.heartbeatsAndCheckView();
+            Thread.sleep(500);
+        }
+        
+        Set<InstanceDescription> instances1 = instance1.getDiscoveryService().getTopology().getInstances();
+        Set<InstanceDescription> instances2 = instance2.getDiscoveryService().getTopology().getInstances();
+        
+        assertEquals(1, instances1.size());
+        assertEquals(1, instances2.size());
+        assertEquals(instance1.getSlingId(), instances1.iterator().next().getSlingId());
+        assertEquals(instance2.getSlingId(), instances2.iterator().next().getSlingId());
+        
+        new VirtualConnector(instance1, instance2);
+        
+        // check instance 1's announcements
+        Collection<Announcement> instance1LocalAnnouncements = 
+                instance1.getAnnouncementRegistry().listLocalAnnouncements();
+        assertEquals(1, instance1LocalAnnouncements.size());
+        Announcement instance1LocalAnnouncement = instance1LocalAnnouncements.iterator().next();
+        assertEquals(instance2.getSlingId(), instance1LocalAnnouncement.getOwnerId());
+        assertEquals(true, instance1LocalAnnouncement.isInherited());
+
+        // check instance 2's announcements
+        Collection<Announcement> instance2LocalAnnouncements = 
+                instance2.getAnnouncementRegistry().listLocalAnnouncements();
+        assertEquals(1, instance2LocalAnnouncements.size());
+        Announcement instance2LocalAnnouncement = instance2LocalAnnouncements.iterator().next();
+        assertEquals(instance1.getSlingId(), instance2LocalAnnouncement.getOwnerId());
+        assertEquals(false, instance2LocalAnnouncement.isInherited());
+        
+        // check topology
+        TopologyHelper.assertTopologyConsistsOf(instance1.getDiscoveryService().getTopology(), instance1.getSlingId(), instance2.getSlingId());
+        TopologyHelper.assertTopologyConsistsOf(instance2.getDiscoveryService().getTopology(), instance1.getSlingId(), instance2.getSlingId());
+
+        instance1LocalAnnouncements = 
+                instance1.getAnnouncementRegistry().listLocalAnnouncements();
+        assertEquals(1, instance1LocalAnnouncements.size());
+        instance2LocalAnnouncements = 
+                instance2.getAnnouncementRegistry().listLocalAnnouncements();
+        assertEquals(1, instance2LocalAnnouncements.size());
+
+        Thread.sleep(2200); // sleep of 2.2sec ensures instance2's heartbeat timeout (which is 2sec) hits
+        
+        instance1LocalAnnouncements = 
+                instance1.getAnnouncementRegistry().listLocalAnnouncements();
+        assertEquals(1, instance1LocalAnnouncements.size());
+        instance2LocalAnnouncements = 
+                instance2.getAnnouncementRegistry().listLocalAnnouncements();
+        assertEquals(0, instance2LocalAnnouncements.size());
+
+        logger.info("testTwoNodes: instance1: "+instance1.getSlingId());
+        instance1.dumpRepo();
+        logger.info("testTwoNodes: instance2: "+instance2.getSlingId());
+        instance2.dumpRepo();
+        TopologyHelper.assertTopologyConsistsOf(instance1.getDiscoveryService().getTopology(), instance1.getSlingId(), instance2.getSlingId());
+        TopologyHelper.assertTopologyConsistsOf(instance2.getDiscoveryService().getTopology(), instance2.getSlingId());
+        
+        Thread.sleep(6000); // another sleep 6s (2.2+6 = 8.2sec) ensures instance1's heartbeat timeout (which is 8sec) hits as well
+        instance1LocalAnnouncements = 
+                instance1.getAnnouncementRegistry().listLocalAnnouncements();
+        assertEquals(0, instance1LocalAnnouncements.size());
+        instance2LocalAnnouncements = 
+                instance2.getAnnouncementRegistry().listLocalAnnouncements();
+        assertEquals(0, instance2LocalAnnouncements.size());
+
+        TopologyHelper.assertTopologyConsistsOf(instance1.getDiscoveryService().getTopology(), instance1.getSlingId());
+        TopologyHelper.assertTopologyConsistsOf(instance2.getDiscoveryService().getTopology(), instance2.getSlingId());
+    }
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/TopologyTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/ModifiableTestBaseConfig.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/ModifiableTestBaseConfig.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/ModifiableTestBaseConfig.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/ModifiableTestBaseConfig.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,37 @@
+/*
+ * 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.sling.discovery.base.its.setup;
+
+import org.apache.sling.discovery.base.connectors.BaseConfig;
+
+/**
+ * test extension of the BaseConfig that allows setting some
+ * parameters in test classes
+ */
+public interface ModifiableTestBaseConfig extends BaseConfig {
+
+    void addTopologyConnectorWhitelistEntry(String string);
+
+    void setMinEventDelay(int minEventDelay);
+
+    void setViewCheckTimeout(int viewCheckTimeout);
+
+    void setViewCheckInterval(int viewCheckInterval);
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/ModifiableTestBaseConfig.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/OSGiMock.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/OSGiMock.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/OSGiMock.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/OSGiMock.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,124 @@
+/*
+ * 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.sling.discovery.base.its.setup;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sling.discovery.base.its.setup.mock.MockFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OSGiMock {
+
+    private static final Logger logger = LoggerFactory.getLogger(OSGiMock.class);
+
+    private final List<Object> services = new LinkedList<Object>();
+
+    public void addService(Object service) {
+        if (service==null) {
+            throw new IllegalArgumentException("service must not be null");
+        }
+        services.add(service);
+    }
+    
+    public void activateAll() throws Exception {
+        for (@SuppressWarnings("rawtypes")
+        Iterator it = services.iterator(); it.hasNext();) {
+            Object aService = it.next();
+
+            activate(aService);
+        }
+    }
+
+	public static void activate(Object aService) throws IllegalAccessException,
+			InvocationTargetException {
+	    Class<?> clazz = aService.getClass();
+	    while (clazz != null) {
+	        Method[] methods = clazz.getDeclaredMethods();
+	        for (int i = 0; i < methods.length; i++) {
+	            Method method = methods[i];
+	            if (method.getName().equals("activate")) {
+	                method.setAccessible(true);
+	                if ( method.getParameterTypes().length == 0 ) {
+	                    logger.info("activate: activating "+aService+"...");
+	                    method.invoke(aService, null);
+	                    logger.info("activate: activating "+aService+" done.");
+	                } else if (method.getParameterTypes().length==1 && (method.getParameterTypes()[0]==ComponentContext.class)){
+	                    logger.info("activate: activating "+aService+"...");
+	                    method.invoke(aService, MockFactory.mockComponentContext());
+	                    logger.info("activate: activating "+aService+" done.");
+	                } else if (method.getParameterTypes().length==1 && (method.getParameterTypes()[0]==BundleContext.class)){
+	                    logger.info("activate: activating "+aService+"...");
+	                    method.invoke(aService, MockFactory.mockBundleContext());
+	                    logger.info("activate: activating "+aService+" done.");
+	                } else {
+	                    throw new IllegalStateException("unsupported activate variant: "+method);
+	                }
+	                return;
+	            }
+	        }
+	        clazz = clazz.getSuperclass();
+	    }
+	}
+
+	public void deactivateAll() throws Exception {
+        for (@SuppressWarnings("rawtypes")
+        Iterator it = services.iterator(); it.hasNext();) {
+            Object aService = it.next();
+
+            deactivate(aService);
+        }
+	}
+
+	public static void deactivate(Object aService) throws IllegalAccessException,
+			InvocationTargetException {
+        Class<?> clazz = aService.getClass();
+        while (clazz != null) {
+    		Method[] methods = clazz.getDeclaredMethods();
+    		for (int i = 0; i < methods.length; i++) {
+    		    Method method = methods[i];
+    		    if (method.getName().equals("deactivate")) {
+    		        method.setAccessible(true);
+    		        if ( method.getParameterTypes().length == 0 ) {
+    		            method.invoke(aService, null);
+    		        } else {
+    		            method.invoke(aService, MockFactory.mockComponentContext());
+    		        }
+    		        return;
+    		    }
+    		}
+            clazz = clazz.getSuperclass();
+        }
+	}
+
+    public void addServices(Object[] additionalServices) {
+        if (additionalServices==null) {
+            return;
+        }
+        for (Object additionalService : additionalServices) {
+            addService(additionalService);
+        }
+    }
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/OSGiMock.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/TopologyHelper.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/TopologyHelper.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/TopologyHelper.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/TopologyHelper.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,145 @@
+/*
+ * 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.sling.discovery.base.its.setup;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.base.commons.DefaultTopologyView;
+import org.apache.sling.discovery.commons.providers.DefaultClusterView;
+import org.apache.sling.discovery.commons.providers.DefaultInstanceDescription;
+
+import junitx.util.PrivateAccessor;
+
+public class TopologyHelper {
+
+    public static DefaultInstanceDescription createInstanceDescription(
+            ClusterView clusterView) {
+        return createInstanceDescription(UUID.randomUUID().toString(), false, clusterView);
+    }
+    
+    public static DefaultInstanceDescription createInstanceDescription(
+            String instanceId, boolean isLocal, ClusterView clusterView) {
+        if (!(clusterView instanceof DefaultClusterView)) {
+            throw new IllegalArgumentException(
+                    "Must pass a clusterView of type "
+                            + DefaultClusterView.class);
+        }
+        DefaultInstanceDescription i = new DefaultInstanceDescription(
+                (DefaultClusterView) clusterView, false, isLocal, instanceId, new HashMap<String, String>());
+        return i;
+    }
+
+    public static DefaultTopologyView createTopologyView(String clusterViewId,
+            String slingId) {
+        DefaultTopologyView t = new DefaultTopologyView();
+        DefaultClusterView c = new DefaultClusterView(clusterViewId);
+        DefaultInstanceDescription i = new DefaultInstanceDescription(
+                c, true, false, slingId, new HashMap<String, String>());
+        Collection<InstanceDescription> instances = new LinkedList<InstanceDescription>();
+        instances.add(i);
+        t.addInstances(instances);
+        return t;
+    }
+
+    public static DefaultTopologyView cloneTopologyView(DefaultTopologyView original) {
+        DefaultTopologyView t = new DefaultTopologyView();
+        Iterator<ClusterView> it = original.getClusterViews().iterator();
+        while (it.hasNext()) {
+            DefaultClusterView c = (DefaultClusterView) it.next();
+            t.addInstances(clone(c).getInstances());
+        }
+        return t;
+    }
+
+    public static DefaultClusterView clone(DefaultClusterView original) {
+        DefaultClusterView c = new DefaultClusterView(original.getId());
+        Iterator<InstanceDescription> it = original.getInstances().iterator();
+        while (it.hasNext()) {
+            DefaultInstanceDescription id = (DefaultInstanceDescription) it
+                    .next();
+            c.addInstanceDescription(cloneWOClusterView(id));
+        }
+        return c;
+    }
+
+    public static DefaultInstanceDescription cloneWOClusterView(
+            DefaultInstanceDescription original) {
+        DefaultInstanceDescription id = new DefaultInstanceDescription(
+                null, original.isLeader(), original.isLocal(),
+                original.getSlingId(), new HashMap<String, String>(
+                        original.getProperties()));
+        return id;
+    }
+
+    public static DefaultInstanceDescription createAndAddInstanceDescription(
+            DefaultTopologyView newView, ClusterView clusterView) {
+        DefaultInstanceDescription i = createInstanceDescription(clusterView);
+        return addInstanceDescription(newView, i);
+    }
+
+    public static DefaultInstanceDescription addInstanceDescription(
+            DefaultTopologyView newView, DefaultInstanceDescription i) {
+        Collection<InstanceDescription> instances = new LinkedList<InstanceDescription>();
+        instances.add(i);
+        newView.addInstances(instances);
+        return i;
+    }
+
+    public static DefaultTopologyView cloneTopologyView(DefaultTopologyView view,
+            String newLeader) throws NoSuchFieldException {
+        final DefaultTopologyView clone = cloneTopologyView(view);
+        final DefaultClusterView cluster = (DefaultClusterView) clone.getClusterViews().iterator().next();
+        for (Iterator it = cluster.getInstances().iterator(); it.hasNext();) {
+            DefaultInstanceDescription id = (DefaultInstanceDescription) it.next();
+            PrivateAccessor.setField(id, "isLeader", id.getSlingId().equals(newLeader));
+        }
+        return clone;
+    }
+
+    public static void assertTopologyConsistsOf(TopologyView topology, String... slingIds) {
+        assertNotNull(topology);
+        assertEquals(slingIds.length, topology.getInstances().size());
+        for(int i=0; i<slingIds.length; i++) {
+            final String aSlingId = slingIds[i];
+            final Set<?> instances = topology.getInstances();
+            boolean found = false;
+            for (Iterator<?> it = instances.iterator(); it.hasNext();) {
+                InstanceDescription anInstance = (InstanceDescription) it.next();
+                if (anInstance.getSlingId().equals(aSlingId)) {
+                    found = true;
+                    break;
+                }
+            }
+            assertTrue(found);
+        }
+    }
+    
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/TopologyHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualConnector.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualConnector.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualConnector.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualConnector.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,39 @@
+/*
+ * 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.sling.discovery.base.its.setup;
+
+import org.apache.sling.discovery.base.connectors.ping.TopologyConnectorClientInformation;
+
+public class VirtualConnector {
+    @SuppressWarnings("unused")
+    private final VirtualInstance from;
+    @SuppressWarnings("unused")
+    private final VirtualInstance to;
+    private final int jettyPort;
+    @SuppressWarnings("unused")
+    private final TopologyConnectorClientInformation connectorInfo;
+
+    public VirtualConnector(VirtualInstance from, VirtualInstance to) throws Throwable {
+        this.from = from;
+        this.to = to;
+        to.startJetty();
+        this.jettyPort = to.getJettyPort();
+        this.connectorInfo = from.connectTo("http://localhost:"+jettyPort+"/system/console/topology/connector");
+    }
+}
\ No newline at end of file

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualConnector.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstance.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstance.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstance.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstance.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,374 @@
+/*
+ * 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.sling.discovery.base.its.setup;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Date;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.jcr.Session;
+import javax.servlet.Servlet;
+
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.commons.testing.jcr.RepositoryProvider;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.PropertyProvider;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.discovery.base.commons.BaseDiscoveryService;
+import org.apache.sling.discovery.base.commons.ClusterViewService;
+import org.apache.sling.discovery.base.commons.ViewChecker;
+import org.apache.sling.discovery.base.commons.UndefinedClusterViewException;
+import org.apache.sling.discovery.base.connectors.announcement.AnnouncementRegistry;
+import org.apache.sling.discovery.base.connectors.ping.ConnectorRegistry;
+import org.apache.sling.discovery.base.connectors.ping.TopologyConnectorClientInformation;
+import org.apache.sling.discovery.base.connectors.ping.TopologyConnectorServlet;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import junitx.util.PrivateAccessor;
+
+public class VirtualInstance {
+
+    protected final static Logger logger = LoggerFactory.getLogger(VirtualInstance.class);
+
+    public final String slingId;
+
+    ClusterViewService clusterViewService;
+
+    private final ResourceResolverFactory resourceResolverFactory;
+
+    private final OSGiMock osgiMock;
+
+    private final BaseDiscoveryService discoveryService;
+
+    private final AnnouncementRegistry announcementRegistry;
+
+    private final ConnectorRegistry connectorRegistry;
+
+    protected final String debugName;
+
+    private ResourceResolver resourceResolver;
+
+    private int serviceId = 999;
+
+    private ViewCheckerRunner viewCheckerRunner = null;
+
+    private ServletContextHandler servletContext;
+
+    private Server jettyServer;
+
+    private ModifiableTestBaseConfig config;
+
+    private ViewChecker viewChecker;
+
+    private final VirtualInstanceBuilder builder;
+
+    private class ViewCheckerRunner implements Runnable {
+
+    	private final int intervalInSeconds;
+
+    	private boolean stopped_ = false;
+
+		public ViewCheckerRunner(int intervalInSeconds) {
+    		this.intervalInSeconds = intervalInSeconds;
+    	}
+
+		public synchronized void stop() {
+			logger.info("Stopping Instance ["+slingId+"]");
+			stopped_ = true;
+		}
+
+		public void run() {
+			while(true) {
+				synchronized(this) {
+					if (stopped_) {
+						logger.info("Instance ["+slingId+"] stopps.");
+						return;
+					}
+				}
+				try{
+				    heartbeatsAndCheckView();
+				} catch(Exception e) {
+				    logger.error("run: ping connector for slingId="+slingId+" threw exception: "+e, e);
+				}
+				try {
+					Thread.sleep(intervalInSeconds*1000);
+				} catch (InterruptedException e) {
+					e.printStackTrace();
+					return;
+				}
+			}
+		}
+
+    }
+    
+    public VirtualInstance(VirtualInstanceBuilder builder) throws Exception {
+        this.builder = builder;
+    	this.slingId = builder.getSlingId();
+        this.debugName = builder.getDebugName();
+        logger.info("<init>: starting slingId="+slingId+", debugName="+debugName);
+
+        osgiMock = new OSGiMock();
+
+        this.resourceResolverFactory = builder.getResourceResolverFactory();
+
+        config = builder.getConnectorConfig();
+        config.addTopologyConnectorWhitelistEntry("127.0.0.1");
+        config.setMinEventDelay(builder.getMinEventDelay());
+
+        clusterViewService = builder.getClusterViewService();
+        announcementRegistry = builder.getAnnouncementRegistry();
+        connectorRegistry = builder.getConnectorRegistry();
+        viewChecker = builder.getViewChecker();
+		discoveryService = builder.getDiscoverService();
+
+        osgiMock.addService(clusterViewService);
+        osgiMock.addService(announcementRegistry);
+        osgiMock.addService(connectorRegistry);
+        osgiMock.addService(viewChecker);
+        osgiMock.addService(discoveryService);
+        osgiMock.addServices(builder.getAdditionalServices(this));
+
+        resourceResolver = resourceResolverFactory
+                .getAdministrativeResourceResolver(null);
+
+        if (builder.isResetRepo()) {
+            //SLING-4587 : do resetRepo before creating the observationListener
+            // otherwise it will get tons of events from the deletion of /var
+            // which the previous test could have left over.
+            // Doing it before addEventListener should prevent that.
+            builder.resetRepo();
+        }
+
+        osgiMock.activateAll();
+    }
+    
+    @Override
+    public String toString() {
+        return "a [Test]Instance[slingId="+slingId+", debugName="+debugName+"]";
+    }
+
+    public void bindPropertyProvider(PropertyProvider propertyProvider,
+            String... propertyNames) throws Throwable {
+        Map<String, Object> props = new HashMap<String, Object>();
+        props.put(Constants.SERVICE_ID, (long) serviceId++);
+        props.put(PropertyProvider.PROPERTY_PROPERTIES, propertyNames);
+
+        PrivateAccessor.invoke(discoveryService, "bindPropertyProvider",
+                new Class[] { PropertyProvider.class, Map.class },
+                new Object[] { propertyProvider, props });
+    }
+
+    public String getSlingId() {
+        return slingId;
+    }
+
+    public ClusterViewService getClusterViewService() {
+        return clusterViewService;
+    }
+
+    public BaseDiscoveryService getDiscoveryService() {
+        return discoveryService;
+    }
+
+    public AnnouncementRegistry getAnnouncementRegistry() {
+        return announcementRegistry;
+    }
+
+    public synchronized void startJetty() throws Throwable {
+        if (jettyServer!=null) {
+            return;
+        }
+        servletContext = new ServletContextHandler(ServletContextHandler.NO_SECURITY);
+        servletContext.setContextPath("/");
+
+        TopologyConnectorServlet servlet = new TopologyConnectorServlet();
+        PrivateAccessor.setField(servlet, "config", config);
+        PrivateAccessor.setField(servlet, "clusterViewService", clusterViewService);
+        PrivateAccessor.setField(servlet, "announcementRegistry", announcementRegistry);
+
+        Mockery context = new JUnit4Mockery();
+        final HttpService httpService = context.mock(HttpService.class);
+        context.checking(new Expectations() {
+            {
+                allowing(httpService).registerServlet(with(any(String.class)),
+                        with(any(Servlet.class)),
+                        with(any(Dictionary.class)),
+                        with(any(HttpContext.class)));
+            }
+        });
+        PrivateAccessor.setField(servlet, "httpService", httpService);
+        ComponentContext cc = null;
+        PrivateAccessor.invoke(servlet, "activate", new Class[] {ComponentContext.class}, new Object[] {cc});
+
+        ServletHolder holder =
+                new ServletHolder(servlet);
+
+        servletContext.addServlet(holder, "/system/console/topology/*");
+
+        jettyServer = new Server();
+        jettyServer.setHandler(servletContext);
+        Connector connector=new SelectChannelConnector();
+        jettyServer.setConnectors(new Connector[]{connector});
+        jettyServer.start();
+    }
+
+    public synchronized int getJettyPort() {
+        if (jettyServer==null) {
+            throw new IllegalStateException("jettyServer not started");
+        }
+        final Connector[] connectors = jettyServer.getConnectors();
+        return connectors[0].getLocalPort();
+    }
+
+    public TopologyConnectorClientInformation connectTo(String url) throws MalformedURLException {
+        return connectorRegistry.registerOutgoingConnector(clusterViewService, new URL(url));
+    }
+
+    public InstanceDescription getLocalInstanceDescription() throws UndefinedClusterViewException {
+    	final Iterator<InstanceDescription> it = getClusterViewService().getLocalClusterView().getInstances().iterator();
+    	while(it.hasNext()) {
+    		final InstanceDescription id = it.next();
+    		if (slingId.equals(id.getSlingId())) {
+    			return id;
+    		}
+    	}
+    	fail("no local instanceDescription found");
+    	// never called:
+    	return null;
+    }
+
+    public void heartbeatsAndCheckView() {
+    	logger.info("Instance ["+slingId+"] issues a pulse now "+new Date());
+        viewChecker.heartbeatAndCheckView();
+    }
+
+    public void startViewChecker(int intervalInSeconds) throws IllegalAccessException, InvocationTargetException {
+    	logger.info("startViewChecker: intervalInSeconds="+intervalInSeconds);
+    	if (viewCheckerRunner!=null) {
+    		logger.info("startViewChecker: stopping first...");
+    		viewCheckerRunner.stop();
+    		logger.info("startViewChecker: stopped.");
+    	}
+		logger.info("startViewChecker: activating...");
+    	try{
+    		OSGiMock.activate(viewChecker);
+    	} catch(Error er) {
+    		er.printStackTrace(System.out);
+    		throw er;
+    	} catch(RuntimeException re) {
+    		re.printStackTrace(System.out);
+    	}
+		logger.info("startViewChecker: initializing...");
+    	viewCheckerRunner = new ViewCheckerRunner(intervalInSeconds);
+    	Thread th = new Thread(viewCheckerRunner, "Test-ViewCheckerRunner ["+debugName+"]");
+    	th.setDaemon(true);
+		logger.info("startViewChecker: starting thread...");
+    	th.start();
+		logger.info("startViewChecker: done.");
+    }
+
+	public boolean isViewCheckerRunning() {
+		return (viewCheckerRunner!=null);
+	}
+
+    public void stopViewChecker() throws Throwable {
+    	if (viewCheckerRunner!=null) {
+    		viewCheckerRunner.stop();
+    		viewCheckerRunner = null;
+    	}
+        try{
+            OSGiMock.deactivate(viewChecker);
+        } catch(Error er) {
+            er.printStackTrace(System.out);
+            throw er;
+        } catch(RuntimeException re) {
+            re.printStackTrace(System.out);
+            throw re;
+        }
+    }
+
+    public void dumpRepo() throws Exception {
+        VirtualInstanceHelper.dumpRepo(resourceResolverFactory);
+    }
+    
+    public ResourceResolverFactory getResourceResolverFactory() {
+        return resourceResolverFactory;
+    }
+
+    public void stop() throws Exception {
+        logger.info("stop: stopping slingId="+slingId+", debugName="+debugName);
+        try {
+            stopViewChecker();
+        } catch (Throwable e) {
+            throw new Exception("Caught Throwable in stopConnectorPinger: "+e, e);
+        }
+
+        if (resourceResolver != null) {
+            resourceResolver.close();
+        }
+        osgiMock.deactivateAll();
+        logger.info("stop: stopped slingId="+slingId+", debugName="+debugName);
+    }
+
+    public void bindTopologyEventListener(TopologyEventListener eventListener)
+            throws Throwable {
+        PrivateAccessor.invoke(discoveryService, "bindTopologyEventListener",
+                new Class[] { TopologyEventListener.class },
+                new Object[] { eventListener });
+    }
+
+    public ModifiableTestBaseConfig getConfig() {
+        return config;
+    }
+
+    public ViewChecker getViewChecker() {
+        return viewChecker;
+    }
+
+    public void assertEstablishedView() {
+        assertTrue(getDiscoveryService().getTopology().isCurrent());
+    }
+
+    public VirtualInstanceBuilder getBuilder() {
+        return builder;
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstance.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstanceBuilder.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstanceBuilder.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstanceBuilder.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstanceBuilder.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,238 @@
+/*
+ * 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.sling.discovery.base.its.setup;
+
+import java.util.UUID;
+
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.commons.scheduler.Scheduler;
+import org.apache.sling.commons.scheduler.impl.QuartzScheduler;
+import org.apache.sling.commons.threads.ThreadPoolManager;
+import org.apache.sling.commons.threads.impl.DefaultThreadPoolManager;
+import org.apache.sling.discovery.base.commons.BaseDiscoveryService;
+import org.apache.sling.discovery.base.commons.ClusterViewService;
+import org.apache.sling.discovery.base.commons.ViewChecker;
+import org.apache.sling.discovery.base.connectors.announcement.AnnouncementRegistry;
+import org.apache.sling.discovery.base.connectors.announcement.AnnouncementRegistryImpl;
+import org.apache.sling.discovery.base.connectors.ping.ConnectorRegistry;
+import org.apache.sling.discovery.base.connectors.ping.ConnectorRegistryImpl;
+import org.apache.sling.discovery.base.its.setup.mock.FailingScheduler;
+import org.apache.sling.discovery.commons.providers.spi.impl.DummySlingSettingsService;
+import org.apache.sling.settings.SlingSettingsService;
+
+import junitx.util.PrivateAccessor;
+
+public abstract class VirtualInstanceBuilder {
+
+    private static Scheduler singletonScheduler = null;
+    
+    public static Scheduler getSingletonScheduler() throws Exception {
+        if (singletonScheduler!=null) {
+            return singletonScheduler;
+        }
+        final Scheduler newscheduler = new QuartzScheduler();
+        final ThreadPoolManager tpm = new DefaultThreadPoolManager(null, null);
+        try {
+            PrivateAccessor.invoke(newscheduler, "bindThreadPoolManager",
+                    new Class[] { ThreadPoolManager.class },
+                    new Object[] { tpm });
+        } catch (Throwable e1) {
+            org.junit.Assert.fail(e1.toString());
+        }
+        OSGiMock.activate(newscheduler);
+        singletonScheduler = newscheduler;
+        return singletonScheduler;
+    }
+
+    private String debugName;
+    protected ResourceResolverFactory factory;
+    private boolean resetRepo;
+    private String slingId = UUID.randomUUID().toString();
+    private ClusterViewService clusterViewService;
+    protected ViewChecker viewChecker;
+    private AnnouncementRegistry announcementRegistry;
+    private ConnectorRegistry connectorRegistry;
+    private Scheduler scheduler;
+    private BaseDiscoveryService discoveryService;
+    private SlingSettingsService slingSettingsService;
+    protected boolean ownRepository;
+    private int minEventDelay = 1;
+    protected VirtualInstanceBuilder hookedToBuilder;
+
+    public VirtualInstanceBuilder() {
+    }
+    
+    public VirtualInstanceBuilder newRepository(String path, boolean resetRepo) throws Exception {
+        createNewRepository();
+        ownRepository = true;
+        this.resetRepo = resetRepo;
+        setPath(path);
+        return this;
+    }
+    
+    public abstract VirtualInstanceBuilder createNewRepository() throws Exception;
+    
+    public VirtualInstanceBuilder useRepositoryOf(VirtualInstance other) throws Exception {
+        return useRepositoryOf(other.getBuilder());
+    }
+    
+    public VirtualInstanceBuilder useRepositoryOf(VirtualInstanceBuilder other) throws Exception {
+        factory = other.factory;
+        hookedToBuilder = other;
+        ownRepository = false;
+        return this;
+    }
+
+    public VirtualInstanceBuilder setConnectorPingTimeout(int connectorPingTimeout) {
+        getConnectorConfig().setViewCheckTimeout(connectorPingTimeout);
+        return this;
+    }
+    
+    public VirtualInstanceBuilder setConnectorPingInterval(int connectorPingInterval) {
+        getConnectorConfig().setViewCheckInterval(connectorPingInterval);
+        return this;
+    }
+
+    public boolean isResetRepo() {
+        return resetRepo;
+    }
+
+    public String getSlingId() {
+        return slingId;
+    }
+
+    public String getDebugName() {
+        return debugName;
+    }
+
+    public ResourceResolverFactory getResourceResolverFactory() {
+        return factory;
+    }
+
+    public ClusterViewService getClusterViewService() {
+        if (clusterViewService==null) {
+            clusterViewService = createClusterViewService();
+        }
+        return clusterViewService;
+    }
+    
+    protected abstract ClusterViewService createClusterViewService();
+
+    public ViewChecker getViewChecker() throws Exception {
+        if (viewChecker==null) {
+            viewChecker = createViewChecker();
+        }
+        return viewChecker;
+    }
+    
+    public AnnouncementRegistry getAnnouncementRegistry() {
+        if (announcementRegistry==null) {
+            announcementRegistry = createAnnouncementRegistry();
+        }
+        return announcementRegistry;
+    }
+    
+    protected AnnouncementRegistry createAnnouncementRegistry() {
+        return AnnouncementRegistryImpl.testConstructor( 
+                getResourceResolverFactory(), getSlingSettingsService(), getConnectorConfig());
+    }
+
+    public ConnectorRegistry getConnectorRegistry() {
+        if (connectorRegistry==null) {
+            connectorRegistry = createConnectorRegistry();
+        }
+        return connectorRegistry;
+    }
+    
+    protected ConnectorRegistry createConnectorRegistry() {
+        return ConnectorRegistryImpl.testConstructor(announcementRegistry, getConnectorConfig());
+    }
+
+    protected abstract ViewChecker createViewChecker() throws Exception;
+
+    protected abstract VirtualInstanceBuilder setPath(String string);
+
+    public VirtualInstanceBuilder setDebugName(String debugName) {
+        this.debugName = debugName;
+        return this;
+    }
+
+    public abstract ModifiableTestBaseConfig getConnectorConfig();
+
+    public void setScheduler(Scheduler singletonScheduler) {
+        this.scheduler = singletonScheduler;
+    }
+    
+    public Scheduler getScheduler() throws Exception {
+        if (scheduler == null) {
+            scheduler = getSingletonScheduler();
+        }
+        return scheduler;
+    }
+
+    public BaseDiscoveryService getDiscoverService() throws Exception {
+        if (discoveryService==null) {
+            discoveryService = createDiscoveryService();
+        }
+        return discoveryService;
+    }
+
+    protected abstract BaseDiscoveryService createDiscoveryService() throws Exception;
+    
+    protected SlingSettingsService getSlingSettingsService() {
+        if (slingSettingsService==null) {
+            slingSettingsService = createSlingSettingsService();
+        }
+        return slingSettingsService;
+    }
+
+    protected SlingSettingsService createSlingSettingsService() {
+        return new DummySlingSettingsService(getSlingId());
+    }
+
+    public abstract Object[] getAdditionalServices(VirtualInstance instance) throws Exception;
+
+    public VirtualInstanceBuilder setMinEventDelay(int minEventDelay) {
+        this.minEventDelay = minEventDelay;
+        return this;
+    }
+
+    public int getMinEventDelay() {
+        return minEventDelay;
+    }
+
+    public VirtualInstance build() throws Exception {
+        return new VirtualInstance(this);
+    }
+
+    public VirtualInstanceBuilder setSlingId(String slingId) {
+        this.slingId = slingId;
+        return this;
+    }
+    
+    public VirtualInstanceBuilder withFailingScheduler(boolean useFailingScheduler) {
+        if (useFailingScheduler) {
+            this.scheduler = new FailingScheduler();
+        }
+        return this;
+    }
+
+    protected abstract void resetRepo() throws Exception;
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstanceBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstanceHelper.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstanceHelper.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstanceHelper.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstanceHelper.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,88 @@
+/*
+ * 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.sling.discovery.base.its.setup;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class VirtualInstanceHelper {
+
+    private final static Logger logger = LoggerFactory.getLogger(VirtualInstanceHelper.class);
+
+    public static void dumpRepo(ResourceResolverFactory resourceResolverFactory) throws Exception {
+        Session session = resourceResolverFactory
+                .getAdministrativeResourceResolver(null).adaptTo(Session.class);
+        logger.info("dumpRepo: ====== START =====");
+        logger.info("dumpRepo: repo = " + session.getRepository());
+    
+        dump(session.getRootNode());
+    
+        // session.logout();
+        logger.info("dumpRepo: ======  END  =====");
+    
+        session.logout();
+    }
+
+    public static void dump(Node node) throws RepositoryException {
+        if (node.getPath().equals("/jcr:system")
+                || node.getPath().equals("/rep:policy")) {
+            // ignore that one
+            return;
+        }
+    
+        PropertyIterator pi = node.getProperties();
+        StringBuilder sb = new StringBuilder();
+        while (pi.hasNext()) {
+            Property p = pi.nextProperty();
+            sb.append(" ");
+            sb.append(p.getName());
+            sb.append("=");
+            if (p.getType() == PropertyType.BOOLEAN) {
+                sb.append(p.getBoolean());
+            } else if (p.getType() == PropertyType.STRING) {
+                sb.append(p.getString());
+            } else if (p.getType() == PropertyType.DATE) {
+                sb.append(p.getDate().getTime());
+            } else {
+                sb.append("<unknown type=" + p.getType() + "/>");
+            }
+        }
+    
+        StringBuffer depth = new StringBuffer();
+        for(int i=0; i<node.getDepth(); i++) {
+            depth.append(" ");
+        }
+        logger.info(depth + "/" + node.getName() + " -- " + sb);
+        NodeIterator it = node.getNodes();
+        while (it.hasNext()) {
+            Node child = it.nextNode();
+            dump(child);
+        }
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualInstanceHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualRepository.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualRepository.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualRepository.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualRepository.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,27 @@
+/*
+ * 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.sling.discovery.base.its.setup;
+
+import org.apache.sling.api.resource.ResourceResolverFactory;
+
+public interface VirtualRepository {
+
+    ResourceResolverFactory getResourceResolverFactory();
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/VirtualRepository.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/WithholdingAppender.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/WithholdingAppender.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/WithholdingAppender.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/WithholdingAppender.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,82 @@
+/*
+ * 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 SF 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.sling.discovery.base.its.setup;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.URL;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.WriterAppender;
+
+public class WithholdingAppender extends WriterAppender {
+
+    private final ByteArrayOutputStream baos;
+    private final Writer writer;
+
+    /**
+     * Install the WithholdingAppender, essentially muting all logging 
+     * and withholding it until release() is called
+     * @return the WithholdingAppender that can be used to get the 
+     * withheld log output
+     */
+    public static WithholdingAppender install() {
+        LogManager.getRootLogger().removeAllAppenders();
+        final WithholdingAppender withholdingAppender = new WithholdingAppender(
+                new PatternLayout("%d{dd.MM.yyyy HH:mm:ss} *%-5p* [%t] %c{1}: %m\n"));
+        LogManager.getRootLogger().addAppender(withholdingAppender);
+        return withholdingAppender;
+    }
+    
+    /**
+     * Release this WithholdingAppender and optionally dump what was
+     * withheld (eg in case of an exception)
+     * @param dumpToSysout
+     */
+    public void release(boolean dumpToSysout) {
+        LogManager.resetConfiguration();
+        URL log4jPropertiesFile = getClass().getResource("/log4j.properties");
+        PropertyConfigurator.configure(log4jPropertiesFile);
+        if (dumpToSysout) {
+            String withheldLogoutput = getBuffer();
+            System.out.println(withheldLogoutput);
+        }
+    }
+    
+    public WithholdingAppender(Layout layout) {
+        this.layout = layout;
+        this.baos = new ByteArrayOutputStream();
+        this.writer = new BufferedWriter(new OutputStreamWriter(baos));
+        this.setWriter(writer);
+    }
+    
+    public String getBuffer() {
+        try{
+            writer.flush();
+        } catch(IOException e) {
+            // ignore
+        }
+        return baos.toString();
+    }
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/WithholdingAppender.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AcceptsMultiple.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AcceptsMultiple.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AcceptsMultiple.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AcceptsMultiple.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,65 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEvent.Type;
+
+public class AcceptsMultiple implements TopologyEventAsserter {
+
+    private final Type[] acceptedTypes;
+
+    private final Map<Type, Integer> counts = new HashMap<Type, Integer>();
+
+    public AcceptsMultiple(Type... acceptedTypes) {
+        this.acceptedTypes = acceptedTypes;
+    }
+
+    public synchronized void assertOk(TopologyEvent event) {
+        for (int i = 0; i < acceptedTypes.length; i++) {
+            Type aType = acceptedTypes[i];
+            if (aType == event.getType()) {
+                // perfect
+                Integer c = counts.remove(aType);
+                if (c == null) {
+                    counts.put(aType, new Integer(1));
+                } else {
+                    counts.put(aType, new Integer(c + 1));
+                }
+                return;
+            }
+        }
+
+        throw new IllegalStateException("Got an Event which I did not expect: "
+                + event.getType());
+    }
+
+    public synchronized int getEventCnt(Type type) {
+        Integer i = counts.get(type);
+        if (i!=null) {
+            return i;
+        } else {
+            return 0;
+        }
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AcceptsMultiple.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AcceptsParticularTopologyEvent.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AcceptsParticularTopologyEvent.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AcceptsParticularTopologyEvent.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AcceptsParticularTopologyEvent.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,50 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEvent.Type;
+
+public class AcceptsParticularTopologyEvent implements TopologyEventAsserter {
+
+    private final Type particularType;
+
+    private int eventCnt = 0;
+
+    /**
+     * @param singleInstanceTest
+     */
+    public AcceptsParticularTopologyEvent(Type particularType) {
+        this.particularType = particularType;
+    }
+
+    public void assertOk(TopologyEvent event) {
+        if (event.getType() == particularType) {
+            // fine
+            eventCnt++;
+        } else {
+            throw new IllegalStateException("expected " + particularType
+                    + ", got " + event.getType());
+        }
+    }
+
+    public int getEventCnt() {
+        return eventCnt;
+    }
+}
\ No newline at end of file

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AcceptsParticularTopologyEvent.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AssertingTopologyEventListener.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AssertingTopologyEventListener.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AssertingTopologyEventListener.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AssertingTopologyEventListener.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,160 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEvent.Type;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AssertingTopologyEventListener implements TopologyEventListener {
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+    private final List<TopologyEventAsserter> expectedEvents = new LinkedList<TopologyEventAsserter>();
+
+    private String debugInfo = null;
+    
+    private String errorMsg = null;
+    
+    public AssertingTopologyEventListener() {
+    }
+
+    public AssertingTopologyEventListener(String debugInfo) {
+        this.debugInfo = debugInfo;
+    }
+    
+    @Override
+    public String toString() {
+        return super.toString()+"-[debugInfo="+debugInfo+"]";
+    }
+    
+    private List<TopologyEvent> events_ = new LinkedList<TopologyEvent>();
+
+    private List<TopologyEvent> unexpectedEvents_ = new LinkedList<TopologyEvent>();
+
+    public void handleTopologyEvent(TopologyEvent event) {
+        final String logPrefix = "handleTopologyEvent["+(debugInfo!=null ? debugInfo : "this="+this) +"] ";
+        logger.info(logPrefix + "got event=" + event);
+        TopologyEventAsserter asserter = null;
+        synchronized (expectedEvents) {
+            if (expectedEvents.size() == 0) {
+                unexpectedEvents_.add(event);
+                throw new IllegalStateException(
+                        "no expected events anymore. But got: " + event);
+            }
+            asserter = expectedEvents.remove(0);
+        }
+        if (asserter == null) {
+            throw new IllegalStateException("this should not occur");
+        }
+        try{
+            asserter.assertOk(event);
+            logger.info(logPrefix + "event matched expectations (" + event+")");
+        } catch(RuntimeException re) {
+            synchronized(expectedEvents) {
+                unexpectedEvents_.add(event);
+            }
+            throw re;
+        } catch(Error er) {
+            synchronized(expectedEvents) {
+                unexpectedEvents_.add(event);
+            }
+            throw er;
+        }
+        try{
+        switch(event.getType()) {
+        case PROPERTIES_CHANGED: {
+            assertNotNull(event.getOldView());
+            assertNotNull(event.getNewView());
+            assertTrue(event.getNewView().isCurrent());
+            assertFalse(event.getOldView().isCurrent());
+            break;
+        }
+        case TOPOLOGY_CHANGED: {
+            assertNotNull(event.getOldView());
+            assertNotNull(event.getNewView());
+            assertTrue(event.getNewView().isCurrent());
+            assertFalse(event.getOldView().isCurrent());
+            break;
+        }
+        case TOPOLOGY_CHANGING: {
+            assertNotNull(event.getOldView());
+            assertNull(event.getNewView());
+            assertFalse(event.getOldView().isCurrent());
+            break;
+        }
+        case TOPOLOGY_INIT: {
+            assertNull(event.getOldView());
+            assertNotNull(event.getNewView());
+            // cannot make any assertions on event.getNewView().isCurrent()
+            // as that can be true or false
+            break;
+        }
+        }
+        } catch(RuntimeException re) {
+            logger.error("RuntimeException: "+re, re);
+            throw re;
+        } catch(AssertionError e) {
+            logger.error("AssertionError: "+e, e);
+            throw e;
+        }
+        events_.add(event);
+    }
+
+    public List<TopologyEvent> getEvents() {
+        return events_;
+    }
+
+    public void addExpected(Type expectedType) {
+        addExpected(new AcceptsParticularTopologyEvent(expectedType));
+    }
+
+    public void addExpected(TopologyEventAsserter topologyEventAsserter) {
+        expectedEvents.add(topologyEventAsserter);
+    }
+
+    public int getRemainingExpectedCount() {
+        return expectedEvents.size();
+    }
+    
+    public int getUnexpectedCount() {
+        return unexpectedEvents_.size();
+    }
+
+    public void dump() {
+        StringBuffer ue = new StringBuffer();
+        if (unexpectedEvents_.size()>0) {
+            for (Iterator<TopologyEvent> it = unexpectedEvents_.iterator(); it.hasNext();) {
+                TopologyEvent topologyEvent = it.next();
+                ue.append(topologyEvent+", ");
+            }
+            unexpectedEvents_.iterator();
+        }
+        logger.info("dump: got "+events_.size()+" events, "+unexpectedEvents_.size()+" (details: "+ue+") thereof unexpected. My list of expected events contains "+expectedEvents.size());
+    }
+}
\ No newline at end of file

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/AssertingTopologyEventListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/DummyViewChecker.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/DummyViewChecker.java?rev=1709601&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/DummyViewChecker.java (added)
+++ sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/DummyViewChecker.java Tue Oct 20 14:12:31 2015
@@ -0,0 +1,48 @@
+/*
+ * 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.sling.discovery.base.its.setup.mock;
+
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.commons.scheduler.Scheduler;
+import org.apache.sling.discovery.base.commons.BaseViewChecker;
+import org.apache.sling.discovery.base.connectors.BaseConfig;
+import org.apache.sling.discovery.base.connectors.announcement.AnnouncementRegistry;
+import org.apache.sling.discovery.base.connectors.ping.ConnectorRegistry;
+import org.apache.sling.settings.SlingSettingsService;
+
+public class DummyViewChecker extends BaseViewChecker {
+    
+    public static DummyViewChecker testConstructor(
+            SlingSettingsService slingSettingsService,
+            ResourceResolverFactory resourceResolverFactory,
+            ConnectorRegistry connectorRegistry,
+            AnnouncementRegistry announcementRegistry,
+            Scheduler scheduler,
+            BaseConfig connectorConfig) {
+        DummyViewChecker pinger = new DummyViewChecker();
+        pinger.slingSettingsService = slingSettingsService;
+        pinger.resourceResolverFactory = resourceResolverFactory;
+        pinger.connectorRegistry = connectorRegistry;
+        pinger.announcementRegistry = announcementRegistry;
+        pinger.scheduler = scheduler;
+        pinger.connectorConfig = connectorConfig;
+        return pinger;
+    }
+
+}

Propchange: sling/trunk/bundles/extensions/discovery/base/src/test/java/org/apache/sling/discovery/base/its/setup/mock/DummyViewChecker.java
------------------------------------------------------------------------------
    svn:eol-style = native