http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/followthesun/FollowTheSunPolicySoakTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/followthesun/FollowTheSunPolicySoakTest.java b/policy/src/test/java/brooklyn/policy/followthesun/FollowTheSunPolicySoakTest.java deleted file mode 100644 index a67e24d..0000000 --- a/policy/src/test/java/brooklyn/policy/followthesun/FollowTheSunPolicySoakTest.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * 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 brooklyn.policy.followthesun; - -import static org.testng.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.basic.EntityLocal; -import org.apache.brooklyn.api.location.Location; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.Test; - -import brooklyn.entity.basic.Entities; - -import org.apache.brooklyn.location.basic.SimulatedLocation; - -import brooklyn.policy.loadbalancing.BalanceableContainer; -import brooklyn.policy.loadbalancing.MockContainerEntity; -import brooklyn.policy.loadbalancing.MockItemEntity; -import brooklyn.policy.loadbalancing.MockItemEntityImpl; -import brooklyn.policy.loadbalancing.Movable; -import brooklyn.test.Asserts; -import brooklyn.util.collections.MutableMap; - -import com.google.common.base.Function; -import com.google.common.base.Predicates; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; - -public class FollowTheSunPolicySoakTest extends AbstractFollowTheSunPolicyTest { - - private static final Logger LOG = LoggerFactory.getLogger(FollowTheSunPolicySoakTest.class); - - private static final long TIMEOUT_MS = 10*1000; - - @Test - public void testFollowTheSunQuickTest() { - RunConfig config = new RunConfig(); - config.numCycles = 1; - config.numLocations=3; - config.numContainersPerLocation = 5; - config.numLockedItemsPerLocation = 2; - config.numMovableItems = 10; - - runFollowTheSunSoakTest(config); - } - - @Test - public void testLoadBalancingManyItemsQuickTest() { - RunConfig config = new RunConfig(); - config.numCycles = 1; - config.numLocations=2; - config.numContainersPerLocation = 3; - config.numLockedItemsPerLocation = 2; - config.numMovableItems = 10; - config.numContainerStopsPerCycle = 1; - config.numItemStopsPerCycle = 1; - - runFollowTheSunSoakTest(config); - } - - @Test(groups={"Integration"}) // takes ~2s - public void testLoadBalancingManyItemsNotTooLongTest() { - RunConfig config = new RunConfig(); - config.numCycles = 1; - config.numLocations=3; - config.numContainersPerLocation = 5; - config.numLockedItemsPerLocation = 2; - config.numMovableItems = 500; - config.numContainerStopsPerCycle = 1; - config.numItemStopsPerCycle = 1; - - runFollowTheSunSoakTest(config); - } - - @Test(groups={"Integration","Acceptance"}) // integration group, because it's slow to run many cycles - public void testLoadBalancingSoakTest() { - RunConfig config = new RunConfig(); - config.numCycles = 100; - config.numLocations=3; - config.numContainersPerLocation = 5; - config.numLockedItemsPerLocation = 2; - config.numMovableItems = 10; - - runFollowTheSunSoakTest(config); - } - - @Test(groups={"Integration","Acceptance"}) // integration group, because it's slow to run many cycles - public void testLoadBalancingManyItemsSoakTest() { - RunConfig config = new RunConfig(); - config.numCycles = 100; - config.numLocations=3; - config.numContainersPerLocation = 5; - config.numLockedItemsPerLocation = 2; - config.numMovableItems = 100; - config.numContainerStopsPerCycle = 3; - config.numItemStopsPerCycle = 10; - - runFollowTheSunSoakTest(config); - } - - @Test(groups={"Integration","Acceptance"}) // integration group, because it's slow to run many cycles - public void testLoadBalancingManyManyItemsTest() { - RunConfig config = new RunConfig(); - config.numCycles = 1; - config.numLocations=10; - config.numContainersPerLocation = 5; - config.numLockedItemsPerLocation = 100; - config.numMovableItems = 1000; - config.numContainerStopsPerCycle = 0; - config.numItemStopsPerCycle = 0; - config.timeout_ms = 30*1000; - config.verbose = false; - - runFollowTheSunSoakTest(config); - } - - private void runFollowTheSunSoakTest(RunConfig config) { - int numCycles = config.numCycles; - int numLocations = config.numLocations; - int numContainersPerLocation = config.numContainersPerLocation; - int numLockedItemsPerLocation = config.numLockedItemsPerLocation; - int numMovableItems = config.numMovableItems; - - int numContainerStopsPerCycle = config.numContainerStopsPerCycle; - int numItemStopsPerCycle = config.numItemStopsPerCycle; - long timeout_ms = config.timeout_ms; - final boolean verbose = config.verbose; - - MockItemEntityImpl.totalMoveCount.set(0); - - List<Location> locations = new ArrayList<Location>(); - Multimap<Location,MockContainerEntity> containers = HashMultimap.<Location,MockContainerEntity>create(); - Multimap<Location,MockItemEntity> lockedItems = HashMultimap.<Location,MockItemEntity>create(); - final List<MockItemEntity> movableItems = new ArrayList<MockItemEntity>(); - - for (int i = 1; i <= numLocations; i++) { - String locName = "loc"+i; - Location loc = new SimulatedLocation(MutableMap.of("name",locName)); - locations.add(loc); - - for (int j = 1; j <= numContainersPerLocation; j++) { - MockContainerEntity container = newContainer(app, loc, "container-"+locName+"-"+j); - containers.put(loc, container); - } - for (int j = 1; j <= numLockedItemsPerLocation; j++) { - MockContainerEntity container = Iterables.get(containers.get(loc), j%numContainersPerLocation); - MockItemEntity item = newLockedItem(app, container, "item-locked-"+locName+"-"+j); - lockedItems.put(loc, item); - } - } - - for (int i = 1; i <= numMovableItems; i++) { - MockContainerEntity container = Iterables.get(containers.values(), i%containers.size()); - MockItemEntity item = newItem(app, container, "item-movable"+i); - movableItems.add(item); - } - - for (int i = 1; i <= numCycles; i++) { - LOG.info("{}: cycle {}", FollowTheSunPolicySoakTest.class.getSimpleName(), i); - - // Stop movable items, and start others - for (int j = 1; j <= numItemStopsPerCycle; j++) { - int itemIndex = random.nextInt(numMovableItems); - MockItemEntity itemToStop = movableItems.get(itemIndex); - itemToStop.stop(); - LOG.debug("Unmanaging item {}", itemToStop); - Entities.unmanage(itemToStop); - movableItems.set(itemIndex, newItem(app, Iterables.get(containers.values(), 0), "item-movable"+itemIndex)); - } - - // Choose a location to be busiest - int locIndex = random.nextInt(numLocations); - final Location busiestLocation = locations.get(locIndex); - - // Repartition the load across the items - for (int j = 0; j < numMovableItems; j++) { - MockItemEntity item = movableItems.get(j); - Map<Entity, Double> workrates = Maps.newLinkedHashMap(); - - for (Map.Entry<Location,MockItemEntity> entry : lockedItems.entries()) { - Location location = entry.getKey(); - MockItemEntity source = entry.getValue(); - double baseWorkrate = (location == busiestLocation ? 1000 : 100); - double jitter = 10; - double jitteredWorkrate = Math.max(0, baseWorkrate + (random.nextDouble()*jitter*2 - jitter)); - workrates.put(source, jitteredWorkrate); - } - ((EntityLocal)item).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, workrates); - } - - // Stop containers, and start others - // This offloads the "immovable" items to other containers in the same location! - for (int j = 1; j <= numContainerStopsPerCycle; j++) { - int containerIndex = random.nextInt(containers.size()); - MockContainerEntity containerToStop = Iterables.get(containers.values(), containerIndex); - Location location = Iterables.get(containerToStop.getLocations(), 0); - MockContainerEntity otherContainerInLocation = Iterables.find(containers.get(location), Predicates.not(Predicates.equalTo(containerToStop)), null); - containerToStop.offloadAndStop(otherContainerInLocation); - LOG.debug("Unmanaging container {}", containerToStop); - Entities.unmanage(containerToStop); - containers.remove(location, containerToStop); - - MockContainerEntity containerToAdd = newContainer(app, location, "container-"+location.getDisplayName()+"-new."+i+"."+j); - containers.put(location, containerToAdd); - } - - // Assert that the items all end up in the location with maximum load-generation - Asserts.succeedsEventually(MutableMap.of("timeout", timeout_ms), new Runnable() { - public void run() { - Iterable<Location> itemLocs = Iterables.transform(movableItems, new Function<MockItemEntity, Location>() { - public Location apply(MockItemEntity input) { - BalanceableContainer<?> container = input.getAttribute(Movable.CONTAINER); - Collection<Location> locs = (container != null) ? container.getLocations(): null; - return (locs != null && locs.size() > 0) ? Iterables.get(locs, 0) : null; - }}); - - Iterable<String> itemLocNames = Iterables.transform(itemLocs, new Function<Location, String>() { - public String apply(Location input) { - return (input != null) ? input.getDisplayName() : null; - }}); - String errMsg; - if (verbose) { - errMsg = verboseDumpToString()+"; itemLocs="+itemLocNames; - } else { - Set<String> locNamesInUse = Sets.newLinkedHashSet(itemLocNames); - errMsg = "locsInUse="+locNamesInUse+"; totalMoves="+MockItemEntityImpl.totalMoveCount; - } - - assertEquals(ImmutableList.copyOf(itemLocs), Collections.nCopies(movableItems.size(), busiestLocation), errMsg); - }}); - } - } - - static class RunConfig { - int numCycles = 1; - int numLocations = 3; - int numContainersPerLocation = 5; - int numLockedItemsPerLocation = 5; - int numMovableItems = 5; - int numContainerStopsPerCycle = 0; - int numItemStopsPerCycle = 0; - long timeout_ms = TIMEOUT_MS; - boolean verbose = true; - } -}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/followthesun/FollowTheSunPolicyTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/followthesun/FollowTheSunPolicyTest.java b/policy/src/test/java/brooklyn/policy/followthesun/FollowTheSunPolicyTest.java deleted file mode 100644 index 1f5e5db..0000000 --- a/policy/src/test/java/brooklyn/policy/followthesun/FollowTheSunPolicyTest.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * 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 brooklyn.policy.followthesun; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -import java.util.Collections; -import java.util.List; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.basic.EntityLocal; -import org.apache.brooklyn.api.event.SensorEvent; -import org.apache.brooklyn.api.event.SensorEventListener; -import org.apache.brooklyn.api.location.Location; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.Test; - -import brooklyn.entity.basic.Entities; - -import org.apache.brooklyn.location.basic.SimulatedLocation; - -import brooklyn.policy.loadbalancing.MockContainerEntity; -import brooklyn.policy.loadbalancing.MockItemEntity; -import brooklyn.policy.loadbalancing.Movable; -import brooklyn.test.Asserts; -import brooklyn.util.collections.MutableMap; - -import com.google.common.base.Function; -import com.google.common.base.Stopwatch; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; - -public class FollowTheSunPolicyTest extends AbstractFollowTheSunPolicyTest { - - private static final Logger LOG = LoggerFactory.getLogger(FollowTheSunPolicyTest.class); - - @Test - public void testPolicyUpdatesModel() { - final MockContainerEntity containerA = newContainer(app, loc1, "A"); - final MockItemEntity item1 = newItem(app, containerA, "1"); - final MockItemEntity item2 = newItem(app, containerA, "2"); - ((EntityLocal)item2).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item1, 11d)); - - Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { - @Override public void run() { - assertEquals(ImmutableSet.of(item1, item2), model.getItems()); - assertEquals(model.getItemContainer(item1), containerA); - assertEquals(model.getItemLocation(item1), loc1); - assertEquals(model.getContainerLocation(containerA), loc1); - assertEquals(model.getDirectSendsToItemByLocation(), ImmutableMap.of(item2, ImmutableMap.of(loc1, 11d))); - }}); - } - - @Test - public void testPolicyAcceptsLocationFinder() { - pool.removePolicy(policy); - - Function<Entity, Location> customLocationFinder = new Function<Entity, Location>() { - @Override public Location apply(Entity input) { - return new SimulatedLocation(MutableMap.of("name", "custom location for "+input)); - }}; - - FollowTheSunPolicy customPolicy = new FollowTheSunPolicy( - MutableMap.of("minPeriodBetweenExecs", 0, "locationFinder", customLocationFinder), - MockItemEntity.ITEM_USAGE_METRIC, - model, - FollowTheSunParameters.newDefault()); - - pool.addPolicy(customPolicy); - - final MockContainerEntity containerA = newContainer(app, loc1, "A"); - - Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { - @Override public void run() { - assertEquals(model.getContainerLocation(containerA).getDisplayName(), "custom location for "+containerA); - }}); - } - - @Test - public void testNoopBalancing() throws Exception { - // Set-up containers and items. - MockContainerEntity containerA = newContainer(app, loc1, "A"); - MockContainerEntity containerB = newContainer(app, loc2, "B"); - MockItemEntity item1 = newItem(app, containerA, "1", Collections.<Entity, Double>emptyMap()); - MockItemEntity item2 = newItem(app, containerB, "2", Collections.<Entity, Double>emptyMap()); - - Thread.sleep(SHORT_WAIT_MS); - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item1), containerB, ImmutableList.of(item2))); - } - - @Test - public void testMovesItemToFollowDemand() throws Exception { - // Set-up containers and items. - MockContainerEntity containerA = newContainer(app, loc1, "A"); - MockContainerEntity containerB = newContainer(app, loc2, "B"); - MockItemEntity item1 = newItem(app, containerA, "1"); - MockItemEntity item2 = newItem(app, containerB, "2"); - - ((EntityLocal)item1).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item2, 100d)); - - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.<MockItemEntity>of(), containerB, ImmutableList.of(item1, item2))); - } - - @Test - public void testNoopIfDemandIsTiny() throws Exception { - // Set-up containers and items. - MockContainerEntity containerA = newContainer(app, loc1, "A"); - MockContainerEntity containerB = newContainer(app, loc2, "B"); - MockItemEntity item1 = newItem(app, containerA, "1"); - MockItemEntity item2 = newItem(app, containerB, "2"); - - ((EntityLocal)item1).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item2, 0.1d)); - - Thread.sleep(SHORT_WAIT_MS); - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item1), containerB, ImmutableList.of(item2))); - } - - @Test - public void testNoopIfDemandIsSimilarToCurrentLocation() throws Exception { - // Set-up containers and items. - MockContainerEntity containerA = newContainer(app, loc1, "A"); - MockContainerEntity containerB = newContainer(app, loc2, "B"); - MockItemEntity item1 = newItem(app, containerA, "1"); - MockItemEntity item2 = newItem(app, containerA, "2"); - MockItemEntity item3 = newItem(app, containerB, "3"); - - ((EntityLocal)item1).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item2, 100d, item3, 100.1d)); - - Thread.sleep(SHORT_WAIT_MS); - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item1, item2), containerB, ImmutableList.of(item3))); - } - - @Test - public void testMoveDecisionIgnoresDemandFromItself() throws Exception { - // Set-up containers and items. - MockContainerEntity containerA = newContainer(app, loc1, "A"); - MockContainerEntity containerB = newContainer(app, loc2, "B"); - MockItemEntity item1 = newItem(app, containerA, "1"); - MockItemEntity item2 = newItem(app, containerB, "2"); - - ((EntityLocal)item1).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item1, 100d)); - ((EntityLocal)item2).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item2, 100d)); - - Thread.sleep(SHORT_WAIT_MS); - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item1), containerB, ImmutableList.of(item2))); - } - - @Test - public void testItemRemovedCausesRecalculationOfOptimalLocation() { - // Set-up containers and items. - MockContainerEntity containerA = newContainer(app, loc1, "A"); - MockContainerEntity containerB = newContainer(app, loc2, "B"); - MockItemEntity item1 = newItem(app, containerA, "1"); - MockItemEntity item2 = newItem(app, containerA, "2"); - MockItemEntity item3 = newItem(app, containerB, "3"); - - ((EntityLocal)item1).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item2, 100d, item3, 1000d)); - - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item2), containerB, ImmutableList.of(item1, item3))); - - item3.stop(); - Entities.unmanage(item3); - - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item1, item2), containerB, ImmutableList.<MockItemEntity>of())); - } - - @Test - public void testItemMovedCausesRecalculationOfOptimalLocationForOtherItems() { - // Set-up containers and items. - MockContainerEntity containerA = newContainer(app, loc1, "A"); - MockContainerEntity containerB = newContainer(app, loc2, "B"); - MockItemEntity item1 = newItem(app, containerA, "1"); - MockItemEntity item2 = newItem(app, containerA, "2"); - MockItemEntity item3 = newItem(app, containerB, "3"); - - ((EntityLocal)item1).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item2, 100d, item3, 1000d)); - - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item2), containerB, ImmutableList.of(item1, item3))); - - item3.move(containerA); - - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item1, item2, item3), containerB, ImmutableList.<MockItemEntity>of())); - } - - @Test - public void testImmovableItemIsNotMoved() { - MockContainerEntity containerA = newContainer(app, loc1, "A"); - MockContainerEntity containerB = newContainer(app, loc2, "B"); - MockItemEntity item1 = newLockedItem(app, containerA, "1"); - MockItemEntity item2 = newItem(app, containerB, "2"); - - ((EntityLocal)item1).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item2, 100d)); - - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item1), containerB, ImmutableList.of(item2))); - } - - @Test - public void testImmovableItemContributesTowardsLoad() { - MockContainerEntity containerA = newContainer(app, loc1, "A"); - MockContainerEntity containerB = newContainer(app, loc2, "B"); - MockItemEntity item1 = newLockedItem(app, containerA, "1"); - MockItemEntity item2 = newItem(app, containerA, "2"); - - ((EntityLocal)item1).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item1, 100d)); - - assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item1, item2), containerB, ImmutableList.<MockItemEntity>of())); - } - - // Marked as "Acceptance" due to time-sensitive nature :-( - @Test(groups={"Integration", "Acceptance"}, invocationCount=20) - public void testRepeatedRespectsMinPeriodBetweenExecs() throws Exception { - testRespectsMinPeriodBetweenExecs(); - } - - @Test(groups="Integration") - public void testRespectsMinPeriodBetweenExecs() throws Exception { - // Failed in jenkins several times, e.g. with event times [2647, 2655] and [1387, 2001]. - // Aled's guess is that there was a delay notifying the listener the first time - // (which happens async), causing the listener to be notified in rapid - // succession. The policy execs probably did happen with a 1000ms separation. - // - // Therefore try up to three times to see if we get the desired separation. If the - // minPeriodBetweenExecs wasn't being respected, we'd expect the default 100ms; this - // test would therefore hardly ever pass. - final int MAX_ATTEMPTS = 3; - - final long minPeriodBetweenExecs = 1000; - final long timePrecision = 250; - - pool.removePolicy(policy); - - MockContainerEntity containerA = newContainer(app, loc1, "A"); - MockContainerEntity containerB = newContainer(app, loc2, "B"); - MockItemEntity item1 = newItem(app, containerA, "1"); - MockItemEntity item2 = newItem(app, containerB, "2"); - MockItemEntity item3 = newItem(app, containerA, "3"); - - FollowTheSunPolicy customPolicy = new FollowTheSunPolicy( - MutableMap.of("minPeriodBetweenExecs", minPeriodBetweenExecs), - MockItemEntity.ITEM_USAGE_METRIC, - model, - FollowTheSunParameters.newDefault()); - - pool.addPolicy(customPolicy); - - // Record times that things are moved, by lisening to the container sensor being set - final Stopwatch stopwatch = Stopwatch.createStarted(); - - final List<Long> eventTimes = Lists.newCopyOnWriteArrayList(); - final Semaphore semaphore = new Semaphore(0); - - app.subscribe(item1, Movable.CONTAINER, new SensorEventListener<Entity>() { - @Override public void onEvent(SensorEvent<Entity> event) { - long eventTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - LOG.info("Received {} at {}", event, eventTime); - eventTimes.add(eventTime); - semaphore.release(); - }}); - - String errmsg = ""; - for (int i = 0; i < MAX_ATTEMPTS; i++) { - // Set the workrate, causing the policy to move item1 to item2's location, and wait for it to happen - ((EntityLocal)item1).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item2, 100d)); - assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - assertEquals(item1.getAttribute(Movable.CONTAINER), containerB); - - // now cause item1 to be moved to item3's location, and wait for it to happen - ((EntityLocal)item1).setAttribute(MockItemEntity.ITEM_USAGE_METRIC, ImmutableMap.<Entity,Double>of(item3, 100d)); - assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - assertEquals(item1.getAttribute(Movable.CONTAINER), containerA); - - LOG.info("testRepeatedRespectsMinPeriodBetweenExecs event times: "+eventTimes); - assertEquals(eventTimes.size(), 2); - if (eventTimes.get(1) - eventTimes.get(0) > (minPeriodBetweenExecs-timePrecision)) { - return; // success - } else { - errmsg += eventTimes; - eventTimes.clear(); - } - } - - fail("Event times never had sufficient gap: "+errmsg); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/ha/ConnectionFailureDetectorTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/ha/ConnectionFailureDetectorTest.java b/policy/src/test/java/brooklyn/policy/ha/ConnectionFailureDetectorTest.java deleted file mode 100644 index bb2a2ec..0000000 --- a/policy/src/test/java/brooklyn/policy/ha/ConnectionFailureDetectorTest.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * 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 brooklyn.policy.ha; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -import java.io.IOException; -import java.net.ServerSocket; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.apache.brooklyn.api.event.Sensor; -import org.apache.brooklyn.api.event.SensorEvent; -import org.apache.brooklyn.api.event.SensorEventListener; -import org.apache.brooklyn.api.management.ManagementContext; -import org.apache.brooklyn.api.policy.PolicySpec; -import org.apache.brooklyn.test.entity.LocalManagementContextForTests; -import org.apache.brooklyn.test.entity.TestApplication; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import brooklyn.entity.basic.ApplicationBuilder; -import brooklyn.entity.basic.Entities; -import brooklyn.policy.ha.HASensors.FailureDescriptor; -import brooklyn.test.Asserts; -import brooklyn.util.collections.MutableMap; -import brooklyn.util.time.Duration; - -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableMap; -import com.google.common.net.HostAndPort; - -public class ConnectionFailureDetectorTest { - - private static final int TIMEOUT_MS = 30*1000; - private static final int OVERHEAD = 250; - private static final int POLL_PERIOD = 100; - - private ManagementContext managementContext; - private TestApplication app; - - private List<SensorEvent<FailureDescriptor>> events; - - private ServerSocket serverSocket; - private HostAndPort serverSocketAddress; - - @BeforeMethod(alwaysRun=true) - public void setUp() throws Exception { - events = new CopyOnWriteArrayList<SensorEvent<FailureDescriptor>>(); - - managementContext = new LocalManagementContextForTests(); - app = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext); - - app.getManagementContext().getSubscriptionManager().subscribe( - app, - HASensors.CONNECTION_FAILED, - new SensorEventListener<FailureDescriptor>() { - @Override public void onEvent(SensorEvent<FailureDescriptor> event) { - events.add(event); - } - }); - app.getManagementContext().getSubscriptionManager().subscribe( - app, - HASensors.CONNECTION_RECOVERED, - new SensorEventListener<FailureDescriptor>() { - @Override public void onEvent(SensorEvent<FailureDescriptor> event) { - events.add(event); - } - }); - - serverSocketAddress = startServerSocket(); - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - stopServerSocket(); - if (managementContext != null) Entities.destroyAll(managementContext); - } - - private HostAndPort startServerSocket() throws Exception { - if (serverSocketAddress != null) { - serverSocket = new ServerSocket(serverSocketAddress.getPort()); - } else { - for (int i = 40000; i < 40100; i++) { - try { - serverSocket = new ServerSocket(i); - } catch (IOException e) { - // try next port - } - } - assertNotNull(serverSocket, "Failed to create server socket; no ports free in range!"); - serverSocketAddress = HostAndPort.fromParts(serverSocket.getInetAddress().getHostAddress(), serverSocket.getLocalPort()); - } - return serverSocketAddress; - } - - private void stopServerSocket() throws Exception { - if (serverSocket != null) serverSocket.close(); - } - - @Test(groups="Integration") // Has a 1 second wait - public void testNotNotifiedOfFailuresForHealthy() throws Exception { - // Create members before and after the policy is registered, to test both scenarios - - app.addPolicy(PolicySpec.create(ConnectionFailureDetector.class) - .configure(ConnectionFailureDetector.ENDPOINT, serverSocketAddress)); - - assertNoEventsContinually(); - } - - @Test - public void testNotifiedOfFailure() throws Exception { - app.addPolicy(PolicySpec.create(ConnectionFailureDetector.class) - .configure(ConnectionFailureDetector.ENDPOINT, serverSocketAddress)); - - stopServerSocket(); - - assertHasEventEventually(HASensors.CONNECTION_FAILED, Predicates.<Object>equalTo(app), null); - assertEquals(events.size(), 1, "events="+events); - } - - @Test - public void testNotifiedOfRecovery() throws Exception { - app.addPolicy(PolicySpec.create(ConnectionFailureDetector.class) - .configure(ConnectionFailureDetector.ENDPOINT, serverSocketAddress)); - - stopServerSocket(); - assertHasEventEventually(HASensors.CONNECTION_FAILED, Predicates.<Object>equalTo(app), null); - - // make the connection recover - startServerSocket(); - assertHasEventEventually(HASensors.CONNECTION_RECOVERED, Predicates.<Object>equalTo(app), null); - assertEquals(events.size(), 2, "events="+events); - } - - @Test - public void testReportsFailureWhenAlreadyDownOnRegisteringPolicy() throws Exception { - stopServerSocket(); - - app.addPolicy(PolicySpec.create(ConnectionFailureDetector.class) - .configure(ConnectionFailureDetector.ENDPOINT, serverSocketAddress)); - - assertHasEventEventually(HASensors.CONNECTION_FAILED, Predicates.<Object>equalTo(app), null); - } - - @Test(groups="Integration") // Because slow - public void testNotNotifiedOfTemporaryFailuresDuringStabilisationDelay() throws Exception { - app.addPolicy(PolicySpec.create(ConnectionFailureDetector.class) - .configure(ConnectionFailureDetector.ENDPOINT, serverSocketAddress) - .configure(ConnectionFailureDetector.CONNECTION_FAILED_STABILIZATION_DELAY, Duration.ONE_MINUTE)); - - stopServerSocket(); - Thread.sleep(100); - startServerSocket(); - - assertNoEventsContinually(); - } - - @Test(groups="Integration") // Because slow - public void testNotifiedOfFailureAfterStabilisationDelay() throws Exception { - final int stabilisationDelay = 1000; - - app.addPolicy(PolicySpec.create(ConnectionFailureDetector.class) - .configure(ConnectionFailureDetector.ENDPOINT, serverSocketAddress) - .configure(ConnectionFailureDetector.CONNECTION_FAILED_STABILIZATION_DELAY, Duration.of(stabilisationDelay))); - - stopServerSocket(); - - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - assertHasEventEventually(HASensors.CONNECTION_FAILED, Predicates.<Object>equalTo(app), null); - } - - @Test(groups="Integration") // Because slow - public void testFailuresThenUpDownResetsStabilisationCount() throws Exception { - final long stabilisationDelay = 1000; - - app.addPolicy(PolicySpec.create(ConnectionFailureDetector.class) - .configure(ConnectionFailureDetector.ENDPOINT, serverSocketAddress) - .configure(ConnectionFailureDetector.CONNECTION_FAILED_STABILIZATION_DELAY, Duration.of(stabilisationDelay))); - - stopServerSocket(); - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - - startServerSocket(); - Thread.sleep(POLL_PERIOD+OVERHEAD); - stopServerSocket(); - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - - assertHasEventEventually(HASensors.CONNECTION_FAILED, Predicates.<Object>equalTo(app), null); - } - - @Test(groups="Integration") // Because slow - public void testNotNotifiedOfTemporaryRecoveryDuringStabilisationDelay() throws Exception { - final long stabilisationDelay = 1000; - - app.addPolicy(PolicySpec.create(ConnectionFailureDetector.class) - .configure(ConnectionFailureDetector.ENDPOINT, serverSocketAddress) - .configure(ConnectionFailureDetector.CONNECTION_RECOVERED_STABILIZATION_DELAY, Duration.of(stabilisationDelay))); - - stopServerSocket(); - assertHasEventEventually(HASensors.CONNECTION_FAILED, Predicates.<Object>equalTo(app), null); - events.clear(); - - startServerSocket(); - Thread.sleep(POLL_PERIOD+OVERHEAD); - stopServerSocket(); - - assertNoEventsContinually(Duration.of(stabilisationDelay + OVERHEAD)); - } - - @Test(groups="Integration") // Because slow - public void testNotifiedOfRecoveryAfterStabilisationDelay() throws Exception { - final int stabilisationDelay = 1000; - - app.addPolicy(PolicySpec.create(ConnectionFailureDetector.class) - .configure(ConnectionFailureDetector.ENDPOINT, serverSocketAddress) - .configure(ConnectionFailureDetector.CONNECTION_RECOVERED_STABILIZATION_DELAY, Duration.of(stabilisationDelay))); - - stopServerSocket(); - assertHasEventEventually(HASensors.CONNECTION_FAILED, Predicates.<Object>equalTo(app), null); - events.clear(); - - startServerSocket(); - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - assertHasEventEventually(HASensors.CONNECTION_RECOVERED, Predicates.<Object>equalTo(app), null); - } - - @Test(groups="Integration") // Because slow - public void testRecoversThenDownUpResetsStabilisationCount() throws Exception { - final long stabilisationDelay = 1000; - - app.addPolicy(PolicySpec.create(ConnectionFailureDetector.class) - .configure(ConnectionFailureDetector.ENDPOINT, serverSocketAddress) - .configure(ConnectionFailureDetector.CONNECTION_RECOVERED_STABILIZATION_DELAY, Duration.of(stabilisationDelay))); - - stopServerSocket(); - assertHasEventEventually(HASensors.CONNECTION_FAILED, Predicates.<Object>equalTo(app), null); - events.clear(); - - startServerSocket(); - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - - stopServerSocket(); - Thread.sleep(POLL_PERIOD+OVERHEAD); - startServerSocket(); - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - - assertHasEventEventually(HASensors.CONNECTION_RECOVERED, Predicates.<Object>equalTo(app), null); - } - - private void assertHasEvent(Sensor<?> sensor, Predicate<Object> componentPredicate, Predicate<? super CharSequence> descriptionPredicate) { - for (SensorEvent<FailureDescriptor> event : events) { - if (event.getSensor().equals(sensor) && - (componentPredicate == null || componentPredicate.apply(event.getValue().getComponent())) && - (descriptionPredicate == null || descriptionPredicate.apply(event.getValue().getDescription()))) { - return; - } - } - fail("No matching "+sensor+" event found; events="+events); - } - - private void assertHasEventEventually(final Sensor<?> sensor, final Predicate<Object> componentPredicate, final Predicate<? super CharSequence> descriptionPredicate) { - Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { - @Override public void run() { - assertHasEvent(sensor, componentPredicate, descriptionPredicate); - }}); - } - - private void assertNoEventsContinually(Duration duration) { - Asserts.succeedsContinually(ImmutableMap.of("timeout", duration), new Runnable() { - @Override public void run() { - assertTrue(events.isEmpty(), "events="+events); - }}); - } - - private void assertNoEventsContinually() { - Asserts.succeedsContinually(new Runnable() { - @Override public void run() { - assertTrue(events.isEmpty(), "events="+events); - }}); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/ha/HaPolicyRebindTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/ha/HaPolicyRebindTest.java b/policy/src/test/java/brooklyn/policy/ha/HaPolicyRebindTest.java deleted file mode 100644 index e6d01d0..0000000 --- a/policy/src/test/java/brooklyn/policy/ha/HaPolicyRebindTest.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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 brooklyn.policy.ha; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.fail; - -import java.util.List; -import java.util.Set; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.proxying.EntitySpec; -import org.apache.brooklyn.api.event.Sensor; -import org.apache.brooklyn.api.event.SensorEvent; -import org.apache.brooklyn.api.event.SensorEventListener; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.api.location.LocationSpec; -import org.apache.brooklyn.api.policy.EnricherSpec; -import org.apache.brooklyn.api.policy.PolicySpec; -import org.apache.brooklyn.test.entity.TestApplication; -import org.apache.brooklyn.test.entity.TestEntity; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import brooklyn.entity.basic.Entities; -import brooklyn.entity.basic.Lifecycle; -import brooklyn.entity.basic.ServiceStateLogic; -import brooklyn.entity.group.DynamicCluster; -import brooklyn.entity.rebind.RebindTestFixtureWithApp; - -import org.apache.brooklyn.location.basic.SimulatedLocation; - -import brooklyn.policy.ha.HASensors.FailureDescriptor; -import brooklyn.test.Asserts; -import brooklyn.util.collections.MutableMap; - -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - -public class HaPolicyRebindTest extends RebindTestFixtureWithApp { - - private TestEntity origEntity; - private SensorEventListener<FailureDescriptor> eventListener; - private List<SensorEvent<FailureDescriptor>> events; - - @Override - @BeforeMethod(alwaysRun=true) - public void setUp() throws Exception { - super.setUp(); - origEntity = origApp.createAndManageChild(EntitySpec.create(TestEntity.class)); - events = Lists.newCopyOnWriteArrayList(); - eventListener = new SensorEventListener<FailureDescriptor>() { - @Override public void onEvent(SensorEvent<FailureDescriptor> event) { - events.add(event); - } - }; - } - - @Test - public void testServiceRestarterWorksAfterRebind() throws Exception { - origEntity.addPolicy(PolicySpec.create(ServiceRestarter.class) - .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED)); - - TestApplication newApp = rebind(); - final TestEntity newEntity = (TestEntity) Iterables.find(newApp.getChildren(), Predicates.instanceOf(TestEntity.class)); - - newEntity.emit(HASensors.ENTITY_FAILED, new FailureDescriptor(origEntity, "simulate failure")); - - Asserts.succeedsEventually(new Runnable() { - @Override public void run() { - assertEquals(newEntity.getCallHistory(), ImmutableList.of("restart")); - }}); - } - - @Test - public void testServiceReplacerWorksAfterRebind() throws Exception { - Location origLoc = origManagementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)); - DynamicCluster origCluster = origApp.createAndManageChild(EntitySpec.create(DynamicCluster.class) - .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(TestEntity.class)) - .configure(DynamicCluster.INITIAL_SIZE, 3)); - origApp.start(ImmutableList.<Location>of(origLoc)); - - origCluster.addPolicy(PolicySpec.create(ServiceReplacer.class) - .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED)); - - // rebind - TestApplication newApp = rebind(); - final DynamicCluster newCluster = (DynamicCluster) Iterables.find(newApp.getChildren(), Predicates.instanceOf(DynamicCluster.class)); - - // stimulate the policy - final Set<Entity> initialMembers = ImmutableSet.copyOf(newCluster.getMembers()); - final TestEntity e1 = (TestEntity) Iterables.get(initialMembers, 1); - - newApp.getManagementContext().getSubscriptionManager().subscribe(e1, HASensors.ENTITY_FAILED, eventListener); - newApp.getManagementContext().getSubscriptionManager().subscribe(e1, HASensors.ENTITY_RECOVERED, eventListener); - - e1.emit(HASensors.ENTITY_FAILED, new FailureDescriptor(e1, "simulate failure")); - - // Expect e1 to be replaced - Asserts.succeedsEventually(new Runnable() { - @Override public void run() { - Set<Entity> newMembers = Sets.difference(ImmutableSet.copyOf(newCluster.getMembers()), initialMembers); - Set<Entity> removedMembers = Sets.difference(initialMembers, ImmutableSet.copyOf(newCluster.getMembers())); - assertEquals(removedMembers, ImmutableSet.of(e1)); - assertEquals(newMembers.size(), 1); - assertEquals(((TestEntity)Iterables.getOnlyElement(newMembers)).getCallHistory(), ImmutableList.of("start")); - - // TODO e1 not reporting "start" after rebind because callHistory is a field rather than an attribute, so was not persisted - Asserts.assertEqualsIgnoringOrder(e1.getCallHistory(), ImmutableList.of("stop")); - assertFalse(Entities.isManaged(e1)); - }}); - } - - @Test - public void testServiceFailureDetectorWorksAfterRebind() throws Exception { - origEntity.addEnricher(EnricherSpec.create(ServiceFailureDetector.class)); - - // rebind - TestApplication newApp = rebind(); - final TestEntity newEntity = (TestEntity) Iterables.find(newApp.getChildren(), Predicates.instanceOf(TestEntity.class)); - - newApp.getManagementContext().getSubscriptionManager().subscribe(newEntity, HASensors.ENTITY_FAILED, eventListener); - - newEntity.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(newEntity, Lifecycle.RUNNING); - - // trigger the failure - newEntity.setAttribute(TestEntity.SERVICE_UP, false); - - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(newEntity), null); - assertEquals(events.size(), 1, "events="+events); - } - - private void assertHasEventEventually(final Sensor<?> sensor, final Predicate<Object> componentPredicate, final Predicate<? super CharSequence> descriptionPredicate) { - Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { - @Override public void run() { - assertHasEvent(sensor, componentPredicate, descriptionPredicate); - }}); - } - - private void assertHasEvent(Sensor<?> sensor, Predicate<Object> componentPredicate, Predicate<? super CharSequence> descriptionPredicate) { - for (SensorEvent<FailureDescriptor> event : events) { - if (event.getSensor().equals(sensor) && - (componentPredicate == null || componentPredicate.apply(event.getValue().getComponent())) && - (descriptionPredicate == null || descriptionPredicate.apply(event.getValue().getDescription()))) { - return; - } - } - fail("No matching "+sensor+" event found; events="+events); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/ha/ServiceFailureDetectorStabilizationTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/ha/ServiceFailureDetectorStabilizationTest.java b/policy/src/test/java/brooklyn/policy/ha/ServiceFailureDetectorStabilizationTest.java deleted file mode 100644 index b6c5c7b..0000000 --- a/policy/src/test/java/brooklyn/policy/ha/ServiceFailureDetectorStabilizationTest.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 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 brooklyn.policy.ha; - -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.apache.brooklyn.api.entity.proxying.EntitySpec; -import org.apache.brooklyn.api.event.Sensor; -import org.apache.brooklyn.api.event.SensorEvent; -import org.apache.brooklyn.api.event.SensorEventListener; -import org.apache.brooklyn.api.management.ManagementContext; -import org.apache.brooklyn.api.policy.EnricherSpec; -import org.apache.brooklyn.test.entity.LocalManagementContextForTests; -import org.apache.brooklyn.test.entity.TestApplication; -import org.apache.brooklyn.test.entity.TestEntity; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import brooklyn.entity.basic.ApplicationBuilder; -import brooklyn.entity.basic.Entities; -import brooklyn.entity.basic.Lifecycle; -import brooklyn.entity.basic.ServiceStateLogic; -import brooklyn.entity.basic.ServiceStateLogicTest; -import brooklyn.policy.ha.HASensors.FailureDescriptor; -import brooklyn.test.Asserts; -import brooklyn.util.collections.MutableMap; -import brooklyn.util.time.Duration; - -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableMap; - -/** also see more primitive tests in {@link ServiceStateLogicTest} */ -public class ServiceFailureDetectorStabilizationTest { - - private static final Logger LOG = LoggerFactory.getLogger(ServiceFailureDetectorStabilizationTest.class); - - private static final int TIMEOUT_MS = 10*1000; - private static final int OVERHEAD = 250; - - private ManagementContext managementContext; - private TestApplication app; - private TestEntity e1; - - private List<SensorEvent<FailureDescriptor>> events; - - @BeforeMethod(alwaysRun=true) - public void setUp() throws Exception { - events = new CopyOnWriteArrayList<SensorEvent<FailureDescriptor>>(); - - managementContext = new LocalManagementContextForTests(); - app = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext); - e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class)); - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - - app.getManagementContext().getSubscriptionManager().subscribe( - e1, - HASensors.ENTITY_FAILED, - new SensorEventListener<FailureDescriptor>() { - @Override public void onEvent(SensorEvent<FailureDescriptor> event) { - events.add(event); - } - }); - app.getManagementContext().getSubscriptionManager().subscribe( - e1, - HASensors.ENTITY_RECOVERED, - new SensorEventListener<FailureDescriptor>() { - @Override public void onEvent(SensorEvent<FailureDescriptor> event) { - events.add(event); - } - }); - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - if (managementContext != null) Entities.destroyAll(managementContext); - } - - @Test(groups="Integration") // Because slow - public void testNotNotifiedOfTemporaryFailuresDuringStabilisationDelay() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.ENTITY_FAILED_STABILIZATION_DELAY, Duration.ONE_MINUTE)); - - e1.setAttribute(TestEntity.SERVICE_UP, false); - Thread.sleep(100); - e1.setAttribute(TestEntity.SERVICE_UP, true); - - assertNoEventsContinually(); - } - - @Test(groups="Integration") // Because slow - public void testNotifiedOfFailureAfterStabilisationDelay() throws Exception { - final int stabilisationDelay = 1000; - - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.ENTITY_FAILED_STABILIZATION_DELAY, Duration.of(stabilisationDelay))); - - e1.setAttribute(TestEntity.SERVICE_UP, false); - - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - } - - @Test(groups="Integration") // Because slow - public void testFailuresThenUpDownResetsStabilisationCount() throws Exception { - LOG.debug("Running testFailuresThenUpDownResetsStabilisationCount"); - final long stabilisationDelay = 1000; - - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.ENTITY_FAILED_STABILIZATION_DELAY, Duration.of(stabilisationDelay))); - - e1.setAttribute(TestEntity.SERVICE_UP, false); - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - - e1.setAttribute(TestEntity.SERVICE_UP, true); - Thread.sleep(OVERHEAD); - e1.setAttribute(TestEntity.SERVICE_UP, false); - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - } - - @Test(groups="Integration") // Because slow - public void testNotNotifiedOfTemporaryRecoveryDuringStabilisationDelay() throws Exception { - final long stabilisationDelay = 1000; - - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.ENTITY_RECOVERED_STABILIZATION_DELAY, Duration.of(stabilisationDelay))); - - e1.setAttribute(TestEntity.SERVICE_UP, false); - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - events.clear(); - - e1.setAttribute(TestEntity.SERVICE_UP, true); - Thread.sleep(100); - e1.setAttribute(TestEntity.SERVICE_UP, false); - - assertNoEventsContinually(Duration.of(stabilisationDelay + OVERHEAD)); - } - - @Test(groups="Integration") // Because slow - public void testNotifiedOfRecoveryAfterStabilisationDelay() throws Exception { - final int stabilisationDelay = 1000; - - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.ENTITY_RECOVERED_STABILIZATION_DELAY, Duration.of(stabilisationDelay))); - - e1.setAttribute(TestEntity.SERVICE_UP, false); - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - events.clear(); - - e1.setAttribute(TestEntity.SERVICE_UP, true); - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - assertHasEventEventually(HASensors.ENTITY_RECOVERED, Predicates.<Object>equalTo(e1), null); - } - - @Test(groups="Integration") // Because slow - public void testRecoversThenDownUpResetsStabilisationCount() throws Exception { - final long stabilisationDelay = 1000; - - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.ENTITY_RECOVERED_STABILIZATION_DELAY, Duration.of(stabilisationDelay))); - - e1.setAttribute(TestEntity.SERVICE_UP, false); - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - events.clear(); - - e1.setAttribute(TestEntity.SERVICE_UP, true); - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - - e1.setAttribute(TestEntity.SERVICE_UP, false); - Thread.sleep(OVERHEAD); - e1.setAttribute(TestEntity.SERVICE_UP, true); - assertNoEventsContinually(Duration.of(stabilisationDelay - OVERHEAD)); - - assertHasEventEventually(HASensors.ENTITY_RECOVERED, Predicates.<Object>equalTo(e1), null); - } - - private void assertHasEvent(Sensor<?> sensor, Predicate<Object> componentPredicate, Predicate<? super CharSequence> descriptionPredicate) { - for (SensorEvent<FailureDescriptor> event : events) { - if (event.getSensor().equals(sensor) && - (componentPredicate == null || componentPredicate.apply(event.getValue().getComponent())) && - (descriptionPredicate == null || descriptionPredicate.apply(event.getValue().getDescription()))) { - return; - } - } - fail("No matching "+sensor+" event found; events="+events); - } - - private void assertHasEventEventually(final Sensor<?> sensor, final Predicate<Object> componentPredicate, final Predicate<? super CharSequence> descriptionPredicate) { - Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { - @Override public void run() { - assertHasEvent(sensor, componentPredicate, descriptionPredicate); - }}); - } - - private void assertNoEventsContinually(Duration duration) { - Asserts.succeedsContinually(ImmutableMap.of("timeout", duration), new Runnable() { - @Override public void run() { - assertTrue(events.isEmpty(), "events="+events); - }}); - } - - private void assertNoEventsContinually() { - Asserts.succeedsContinually(new Runnable() { - @Override public void run() { - assertTrue(events.isEmpty(), "events="+events); - }}); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/ha/ServiceFailureDetectorTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/ha/ServiceFailureDetectorTest.java b/policy/src/test/java/brooklyn/policy/ha/ServiceFailureDetectorTest.java deleted file mode 100644 index 37355cf..0000000 --- a/policy/src/test/java/brooklyn/policy/ha/ServiceFailureDetectorTest.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * 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 brooklyn.policy.ha; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeUnit; - -import org.apache.brooklyn.api.entity.proxying.EntitySpec; -import org.apache.brooklyn.api.event.Sensor; -import org.apache.brooklyn.api.event.SensorEvent; -import org.apache.brooklyn.api.event.SensorEventListener; -import org.apache.brooklyn.api.management.ManagementContext; -import org.apache.brooklyn.api.policy.EnricherSpec; -import org.apache.brooklyn.test.EntityTestUtils; -import org.apache.brooklyn.test.entity.LocalManagementContextForTests; -import org.apache.brooklyn.test.entity.TestApplication; -import org.apache.brooklyn.test.entity.TestEntity; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import brooklyn.entity.basic.ApplicationBuilder; -import brooklyn.entity.basic.Attributes; -import brooklyn.entity.basic.Entities; -import brooklyn.entity.basic.Lifecycle; -import brooklyn.entity.basic.ServiceStateLogic; -import brooklyn.entity.basic.ServiceStateLogic.ServiceProblemsLogic; -import brooklyn.policy.ha.HASensors.FailureDescriptor; -import brooklyn.test.Asserts; -import brooklyn.util.collections.MutableMap; -import brooklyn.util.time.Duration; -import brooklyn.util.time.Time; - -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableMap; - -public class ServiceFailureDetectorTest { - private static final Logger log = LoggerFactory.getLogger(ServiceFailureDetectorTest.class); - - private static final int TIMEOUT_MS = 10*1000; - - private ManagementContext managementContext; - private TestApplication app; - private TestEntity e1; - - private List<SensorEvent<FailureDescriptor>> events; - private SensorEventListener<FailureDescriptor> eventListener; - - @BeforeMethod(alwaysRun=true) - public void setUp() throws Exception { - events = new CopyOnWriteArrayList<SensorEvent<FailureDescriptor>>(); - eventListener = new SensorEventListener<FailureDescriptor>() { - @Override public void onEvent(SensorEvent<FailureDescriptor> event) { - events.add(event); - } - }; - - managementContext = new LocalManagementContextForTests(); - app = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext); - e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class)); - e1.addEnricher(ServiceStateLogic.newEnricherForServiceStateFromProblemsAndUp()); - - app.getManagementContext().getSubscriptionManager().subscribe(e1, HASensors.ENTITY_FAILED, eventListener); - app.getManagementContext().getSubscriptionManager().subscribe(e1, HASensors.ENTITY_RECOVERED, eventListener); - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - if (managementContext != null) Entities.destroyAll(managementContext); - } - - @Test(groups="Integration") // Has a 1 second wait - public void testNotNotifiedOfFailuresForHealthy() throws Exception { - // Create members before and after the policy is registered, to test both scenarios - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class)); - - assertNoEventsContinually(); - assertEquals(e1.getAttribute(TestEntity.SERVICE_STATE_ACTUAL), Lifecycle.RUNNING); - } - - @Test - public void testNotifiedOfFailure() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class)); - - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - - assertEquals(events.size(), 0, "events="+events); - - e1.setAttribute(TestEntity.SERVICE_UP, false); - - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - assertEquals(events.size(), 1, "events="+events); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - } - - @Test - public void testNotifiedOfFailureOnProblem() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class)); - - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - - assertEquals(events.size(), 0, "events="+events); - - ServiceProblemsLogic.updateProblemsIndicator(e1, "test", "foo"); - - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - assertEquals(events.size(), 1, "events="+events); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - } - - @Test - public void testNotifiedOfFailureOnStateOnFire() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class)); - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.ON_FIRE); - - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - assertEquals(events.size(), 1, "events="+events); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - } - - @Test - public void testNotifiedOfRecovery() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class)); - - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - // Make the entity fail - e1.setAttribute(TestEntity.SERVICE_UP, false); - - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - - // And make the entity recover - e1.setAttribute(TestEntity.SERVICE_UP, true); - assertHasEventEventually(HASensors.ENTITY_RECOVERED, Predicates.<Object>equalTo(e1), null); - assertEquals(events.size(), 2, "events="+events); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); - } - - @Test - public void testNotifiedOfRecoveryFromProblems() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class)); - - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - // Make the entity fail - ServiceProblemsLogic.updateProblemsIndicator(e1, "test", "foo"); - - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - - // And make the entity recover - ServiceProblemsLogic.clearProblemsIndicator(e1, "test"); - assertHasEventEventually(HASensors.ENTITY_RECOVERED, Predicates.<Object>equalTo(e1), null); - assertEquals(events.size(), 2, "events="+events); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); - } - - - @Test(groups="Integration") // Has a 1 second wait - public void testEmitsEntityFailureOnlyIfPreviouslyUp() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class)); - - // Make the entity fail - e1.setAttribute(TestEntity.SERVICE_UP, false); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - assertNoEventsContinually(); - } - - @Test - public void testDisablingPreviouslyUpRequirementForEntityFailed() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.ENTITY_FAILED_ONLY_IF_PREVIOUSLY_UP, false)); - - e1.setAttribute(TestEntity.SERVICE_UP, false); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - } - - @Test - public void testDisablingOnFire() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.SERVICE_ON_FIRE_STABILIZATION_DELAY, Duration.PRACTICALLY_FOREVER)); - - // Make the entity fail - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - EntityTestUtils.assertAttributeEqualsEventually(e1, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); - e1.setAttribute(TestEntity.SERVICE_UP, false); - - assertEquals(e1.getAttribute(TestEntity.SERVICE_STATE_ACTUAL), Lifecycle.RUNNING); - } - - @Test(groups="Integration") // Has a 1 second wait - public void testOnFireAfterDelay() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.SERVICE_ON_FIRE_STABILIZATION_DELAY, Duration.ONE_SECOND)); - - // Make the entity fail - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - EntityTestUtils.assertAttributeEqualsEventually(e1, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); - - e1.setAttribute(TestEntity.SERVICE_UP, false); - - assertEquals(e1.getAttribute(TestEntity.SERVICE_STATE_ACTUAL), Lifecycle.RUNNING); - Time.sleep(Duration.millis(100)); - assertEquals(e1.getAttribute(TestEntity.SERVICE_STATE_ACTUAL), Lifecycle.RUNNING); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - } - - @Test(groups="Integration") // Has a 1 second wait - public void testOnFailureDelayFromProblemAndRecover() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.SERVICE_ON_FIRE_STABILIZATION_DELAY, Duration.ONE_SECOND) - .configure(ServiceFailureDetector.ENTITY_RECOVERED_STABILIZATION_DELAY, Duration.ONE_SECOND)); - - // Set the entity to healthy - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - EntityTestUtils.assertAttributeEqualsEventually(e1, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); - - // Make the entity fail; won't set on-fire for 1s but will publish FAILED immediately. - ServiceStateLogic.ServiceProblemsLogic.updateProblemsIndicator(e1, "test", "foo"); - EntityTestUtils.assertAttributeEqualsContinually(ImmutableMap.of("timeout", 100), e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - assertEquals(e1.getAttribute(TestEntity.SERVICE_STATE_ACTUAL), Lifecycle.RUNNING); - - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - - // Now recover: will publish RUNNING immediately, but has 1s stabilisation for RECOVERED - ServiceStateLogic.ServiceProblemsLogic.clearProblemsIndicator(e1, "test"); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); - - assertEquals(events.size(), 1, "events="+events); - - assertHasEventEventually(HASensors.ENTITY_RECOVERED, Predicates.<Object>equalTo(e1), null); - assertEquals(events.size(), 2, "events="+events); - } - - @Test(groups="Integration") // Has a 1 second wait - public void testAttendsToServiceState() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class)); - - e1.setAttribute(TestEntity.SERVICE_UP, true); - // not counted as failed because not expected to be running - e1.setAttribute(TestEntity.SERVICE_UP, false); - - assertNoEventsContinually(); - } - - @Test(groups="Integration") // Has a 1 second wait - public void testOnlyReportsFailureIfRunning() throws Exception { - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class)); - - // Make the entity fail - ServiceStateLogic.setExpectedState(e1, Lifecycle.STARTING); - e1.setAttribute(TestEntity.SERVICE_UP, true); - e1.setAttribute(TestEntity.SERVICE_UP, false); - - assertNoEventsContinually(); - } - - @Test - public void testReportsFailureWhenAlreadyDownOnRegisteringPolicy() throws Exception { - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - e1.setAttribute(TestEntity.SERVICE_UP, false); - - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.ENTITY_FAILED_ONLY_IF_PREVIOUSLY_UP, false)); - - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - } - - @Test - public void testReportsFailureWhenAlreadyOnFireOnRegisteringPolicy() throws Exception { - ServiceStateLogic.setExpectedState(e1, Lifecycle.ON_FIRE); - - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.ENTITY_FAILED_ONLY_IF_PREVIOUSLY_UP, false)); - - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - } - - @Test(groups="Integration") // Has a 1.5 second wait - public void testRepublishedFailure() throws Exception { - Duration republishPeriod = Duration.millis(100); - - e1.addEnricher(EnricherSpec.create(ServiceFailureDetector.class) - .configure(ServiceFailureDetector.ENTITY_FAILED_REPUBLISH_TIME, republishPeriod)); - - // Set the entity to healthy - e1.setAttribute(TestEntity.SERVICE_UP, true); - ServiceStateLogic.setExpectedState(e1, Lifecycle.RUNNING); - EntityTestUtils.assertAttributeEqualsEventually(e1, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); - - // Make the entity fail; - ServiceStateLogic.ServiceProblemsLogic.updateProblemsIndicator(e1, "test", "foo"); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - assertHasEventEventually(HASensors.ENTITY_FAILED, Predicates.<Object>equalTo(e1), null); - - //wait for at least 10 republish events (~1 sec) - assertEventsSizeEventually(10); - - // Now recover - ServiceStateLogic.ServiceProblemsLogic.clearProblemsIndicator(e1, "test"); - EntityTestUtils.assertAttributeEqualsEventually(e1, TestEntity.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); - assertHasEventEventually(HASensors.ENTITY_RECOVERED, Predicates.<Object>equalTo(e1), null); - - //once recovered check no more failed events emitted periodically - assertEventsSizeContiniually(events.size()); - - SensorEvent<FailureDescriptor> prevEvent = null; - for (SensorEvent<FailureDescriptor> event : events) { - if (prevEvent != null) { - long repeatOffset = event.getTimestamp() - prevEvent.getTimestamp(); - long deviation = Math.abs(repeatOffset - republishPeriod.toMilliseconds()); - if (deviation > republishPeriod.toMilliseconds()/10 && - //warn only if recovered is too far away from the last failure - (!event.getSensor().equals(HASensors.ENTITY_RECOVERED) || - repeatOffset > republishPeriod.toMilliseconds())) { - log.error("The time between failure republish (" + repeatOffset + "ms) deviates too much from the expected " + republishPeriod + ". prevEvent=" + prevEvent + ", event=" + event); - } - } - prevEvent = event; - } - - //make sure no republish takes place after recovered - assertEquals(prevEvent.getSensor(), HASensors.ENTITY_RECOVERED); - } - - private void assertEventsSizeContiniually(final int size) { - Asserts.succeedsContinually(MutableMap.of("timeout", 500), new Runnable() { - @Override - public void run() { - assertTrue(events.size() == size, "assertEventsSizeContiniually expects " + size + " events but found " + events.size() + ": " + events); - } - }); - } - - private void assertEventsSizeEventually(final int size) { - Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { - @Override - public void run() { - assertTrue(events.size() >= size, "assertEventsSizeContiniually expects at least " + size + " events but found " + events.size() + ": " + events); - } - }); - } - - private void assertHasEvent(Sensor<?> sensor, Predicate<Object> componentPredicate, Predicate<? super CharSequence> descriptionPredicate) { - for (SensorEvent<FailureDescriptor> event : events) { - if (event.getSensor().equals(sensor) && - (componentPredicate == null || componentPredicate.apply(event.getValue().getComponent())) && - (descriptionPredicate == null || descriptionPredicate.apply(event.getValue().getDescription()))) { - return; - } - } - fail("No matching "+sensor+" event found; events="+events); - } - - private void assertHasEventEventually(final Sensor<?> sensor, final Predicate<Object> componentPredicate, final Predicate<? super CharSequence> descriptionPredicate) { - Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { - @Override public void run() { - assertHasEvent(sensor, componentPredicate, descriptionPredicate); - }}); - } - - private void assertNoEventsContinually() { - Asserts.succeedsContinually(new Runnable() { - @Override public void run() { - assertTrue(events.isEmpty(), "events="+events); - }}); - } -}
