/* * 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.commons.providers.base; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import java.util.HashMap; import java.util.Random; import java.util.UUID; import org.apache.sling.discovery.TopologyEvent; import org.apache.sling.discovery.commons.providers.BaseTopologyView; import org.apache.sling.discovery.commons.providers.DefaultClusterView; import org.apache.sling.discovery.commons.providers.DefaultInstanceDescription; import org.apache.sling.discovery.commons.providers.DummyTopologyView; import org.apache.sling.discovery.commons.providers.EventHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestHelper { private static final Logger logger = LoggerFactory.getLogger(TestHelper.class); public static void assertEvents(ViewStateManagerImpl mgr, DummyListener listener, TopologyEvent... events) { waitForAsyncEvents(mgr); assertEquals(events.length, listener.countEvents()); for (int i = 0; i < events.length; i++) { TopologyEvent e = events[i]; assertEquals(e.getType(), listener.getEvents().get(i).getType()); switch(e.getType()) { case TOPOLOGY_INIT: { assertNull(listener.getEvents().get(i).getOldView()); assertEquals(e.getNewView(), listener.getEvents().get(i).getNewView()); break; } case TOPOLOGY_CHANGING: { assertEquals(e.getOldView(), listener.getEvents().get(i).getOldView()); assertNull(listener.getEvents().get(i).getNewView()); break; } case PROPERTIES_CHANGED: case TOPOLOGY_CHANGED: { assertEquals(e.getOldView(), listener.getEvents().get(i).getOldView()); assertEquals(e.getNewView(), listener.getEvents().get(i).getNewView()); break; } default: { fail("no other type supported yet"); } } } listener.clearEvents(); } public static void waitForAsyncEvents(ViewStateManagerImpl mgr) { int sleep = 1; while(true) { if (!mgr.getAsyncEventSender().hasInFlightEvent()) { return; } // sleep outside of synchronized to keep test-influence // to a minimum try { Thread.sleep(sleep); } catch (InterruptedException e) { logger.error("waitForFlush: got interrupted: "+e, e); } // minor back-off up until 20ms sleep = Math.min(20, sleep+1); } } public static void assertNoEvents(DummyListener listener) { assertEquals(0, listener.countEvents()); } /** does couple loops randomly calling handleChanging() (or not) and then handleNewView(). * Note: random is passed to allow customizing and not hardcoding this method to a particular random * @throws InterruptedException **/ public static void randomEventLoop(ViewStateManagerImpl mgr, DummyDiscoveryService sds, int loopSize, int delayInMillis, final Random random, DummyListener... listeners) throws InterruptedException { for(int i=0; i<loopSize; i++) { final boolean shouldCallChanging = random.nextBoolean(); if (shouldCallChanging) { // dont always do a changing logger.debug("randomEventLoop: calling handleChanging..."); mgr.handleChanging(); // must first wait for async events to have been processed - as otherwise // the 'getLastView()' might not return the correct view logger.debug("randomEventLoop: waiting for async events...."); waitForAsyncEvents(mgr); logger.debug("randomEventLoop: asserting CHANGING event was sent..."); for(int j=0; j<listeners.length; j++) { assertEvents(mgr, listeners[j], EventHelper.newChangingEvent(listeners[j].getLastView())); } } else { logger.debug("randomEventLoop: asserting no events..."); for(int j=0; j<listeners.length; j++) { assertNoEvents(listeners[j]); } } final DummyTopologyView view = new DummyTopologyView().addInstance(); BaseTopologyView[] lastViews = new BaseTopologyView[listeners.length]; for(int j=0; j<listeners.length; j++) { lastViews[j] = listeners[j].getLastView(); } logger.debug("randomEventLoop: calling handleNewView"); if (sds!=null) { sds.setTopoology(view); } DummyTopologyView clonedView = view.clone(); mgr.handleNewView(view); if (delayInMillis>0) { logger.debug("randomEventLoop: waiting "+delayInMillis+"ms ..."); Thread.sleep(delayInMillis); logger.debug("randomEventLoop: waiting "+delayInMillis+"ms done."); } assertEquals(0, mgr.waitForAsyncEvents(500)); if (!shouldCallChanging) { // in that case I should still get a CHANGING - by contract logger.debug("randomEventLoop: asserting CHANGING, CHANGED events were sent"); for(int j=0; j<listeners.length; j++) { assertEvents(mgr, listeners[j], EventHelper.newChangingEvent(lastViews[j]), EventHelper.newChangedEvent(lastViews[j], view)); } } else { logger.debug("randomEventLoop: asserting CHANGED event was sent"); for(int j=0; j<listeners.length; j++) { assertEvents(mgr, listeners[j], EventHelper.newChangedEvent(lastViews[j], clonedView)); } } } } public static DummyTopologyView newView(boolean isCurrent, String leaderId, String localId, String... slingIds) { return newView(UUID.randomUUID().toString(), UUID.randomUUID().toString(), isCurrent, leaderId, localId, slingIds); } public static DummyTopologyView newView(String syncId, String clusterId, boolean isCurrent, String leaderId, String localId, String... slingIds) { DummyTopologyView topology = new DummyTopologyView(syncId); DefaultClusterView cluster = new DefaultClusterView(clusterId); for (String slingId : slingIds) { DefaultInstanceDescription id = new DefaultInstanceDescription(cluster, slingId.equals(leaderId), slingId.equals(localId), slingId, new HashMap<String, String>()); topology.addInstanceDescription(id); } if (!isCurrent) { topology.setNotCurrent(); } return topology; } }