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



Reply via email to