http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/ha/ServiceReplacerTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/ha/ServiceReplacerTest.java b/policy/src/test/java/brooklyn/policy/ha/ServiceReplacerTest.java deleted file mode 100644 index c679224..0000000 --- a/policy/src/test/java/brooklyn/policy/ha/ServiceReplacerTest.java +++ /dev/null @@ -1,340 +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.assertNotEquals; -import static org.testng.Assert.assertTrue; - -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.proxying.EntitySpec; -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.management.ManagementContext; -import org.apache.brooklyn.api.policy.PolicySpec; -import org.apache.brooklyn.core.util.config.ConfigBag; -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.Assert; -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.EntityInternal; -import brooklyn.entity.basic.Lifecycle; -import brooklyn.entity.basic.QuorumCheck; -import brooklyn.entity.basic.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers; -import brooklyn.entity.group.DynamicCluster; -import brooklyn.entity.trait.FailingEntity; - -import org.apache.brooklyn.location.basic.SimulatedLocation; - -import brooklyn.policy.ha.HASensors.FailureDescriptor; -import brooklyn.test.Asserts; -import brooklyn.util.javalang.JavaClassNames; - -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 ServiceReplacerTest { - - private static final Logger log = LoggerFactory.getLogger(ServiceReplacerTest.class); - - private ManagementContext managementContext; - private TestApplication app; - private SimulatedLocation loc; - private SensorEventListener<Object> eventListener; - private List<SensorEvent<?>> events; - - @BeforeMethod(alwaysRun=true) - public void setUp() throws Exception { - managementContext = new LocalManagementContextForTests(); - app = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext); - loc = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)); - events = Lists.newCopyOnWriteArrayList(); - eventListener = new SensorEventListener<Object>() { - @Override public void onEvent(SensorEvent<Object> event) { - events.add(event); - } - }; - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - if (managementContext != null) Entities.destroyAll(managementContext); - } - - @Test - public void testReplacesFailedMember() throws Exception { - final DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class) - .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(TestEntity.class)) - .configure(DynamicCluster.INITIAL_SIZE, 3)); - app.start(ImmutableList.<Location>of(loc)); - - ServiceReplacer policy = new ServiceReplacer(new ConfigBag().configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED)); - cluster.addPolicy(policy); - - final Set<Entity> initialMembers = ImmutableSet.copyOf(cluster.getMembers()); - final TestEntity e1 = (TestEntity) Iterables.get(initialMembers, 1); - - 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(cluster.getMembers()), initialMembers); - Set<Entity> removedMembers = Sets.difference(initialMembers, ImmutableSet.copyOf(cluster.getMembers())); - assertEquals(removedMembers, ImmutableSet.of(e1)); - assertEquals(newMembers.size(), 1); - assertEquals(((TestEntity)Iterables.getOnlyElement(newMembers)).getCallHistory(), ImmutableList.of("start")); - assertEquals(e1.getCallHistory(), ImmutableList.of("start", "stop")); - assertFalse(Entities.isManaged(e1)); - }}); - } - - @Test(invocationCount=100) - public void testSetsOnFireWhenFailToReplaceMemberManyTimes() throws Exception { - testSetsOnFireWhenFailToReplaceMember(); - } - - // fails the startup of the replacement entity (but not the original). - @Test - public void testSetsOnFireWhenFailToReplaceMember() throws Exception { - app.subscribe(null, ServiceReplacer.ENTITY_REPLACEMENT_FAILED, eventListener); - - final DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class) - .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(FailingEntity.class) - .configure(FailingEntity.FAIL_ON_START_CONDITION, predicateOnlyTrueForCallAtOrAfter(2))) - .configure(DynamicCluster.INITIAL_SIZE, 1) - .configure(DynamicCluster.QUARANTINE_FAILED_ENTITIES, true) - .configure(ComputeServiceIndicatorsFromChildrenAndMembers.UP_QUORUM_CHECK, QuorumCheck.QuorumChecks.alwaysTrue()) - .configure(ComputeServiceIndicatorsFromChildrenAndMembers.RUNNING_QUORUM_CHECK, QuorumCheck.QuorumChecks.alwaysTrue())); - app.start(ImmutableList.<Location>of(loc)); - - // should not be on fire - Assert.assertNotEquals(cluster.getAttribute(Attributes.SERVICE_STATE_ACTUAL), Lifecycle.ON_FIRE); - // and should eventually be running - EntityTestUtils.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); - - log.info("started "+app+" for "+JavaClassNames.niceClassAndMethod()); - - ServiceReplacer policy = new ServiceReplacer(new ConfigBag().configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED)); - cluster.addPolicy(policy); - - final Set<Entity> initialMembers = ImmutableSet.copyOf(cluster.getMembers()); - final TestEntity e1 = (TestEntity) Iterables.get(initialMembers, 0); - - e1.emit(HASensors.ENTITY_FAILED, new FailureDescriptor(e1, "simulate failure")); - - // Expect cluster to go on-fire when fails to start replacement - // Note that we've set up-quorum and running-quorum to be "alwaysTrue" so that we don't get a transient onFire - // when the failed node fails to start (but before it has been removed from the group to be put in quarantine). - EntityTestUtils.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); - - // Expect to have the second failed entity still kicking around as proof (in quarantine) - // The cluster should NOT go on fire until after the 2nd failure - Iterable<Entity> members = Iterables.filter(managementContext.getEntityManager().getEntities(), Predicates.instanceOf(FailingEntity.class)); - assertEquals(Iterables.size(members), 2); - - // e2 failed to start, so it won't have called stop on e1 - TestEntity e2 = (TestEntity) Iterables.getOnlyElement(Sets.difference(ImmutableSet.copyOf(members), initialMembers)); - assertEquals(e1.getCallHistory(), ImmutableList.of("start"), "e1.history="+e1.getCallHistory()); - assertEquals(e2.getCallHistory(), ImmutableList.of("start"), "e2.history="+e2.getCallHistory()); - - // And will have received notification event about it - assertEventuallyHasEntityReplacementFailedEvent(cluster); - } - - @Test(groups="Integration") // has a 1 second wait - public void testDoesNotOnFireWhenFailToReplaceMember() throws Exception { - app.subscribe(null, ServiceReplacer.ENTITY_REPLACEMENT_FAILED, eventListener); - - final DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class) - .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(FailingEntity.class) - .configure(FailingEntity.FAIL_ON_START_CONDITION, predicateOnlyTrueForCallAtOrAfter(2))) - .configure(DynamicCluster.INITIAL_SIZE, 1) - .configure(DynamicCluster.QUARANTINE_FAILED_ENTITIES, true)); - app.start(ImmutableList.<Location>of(loc)); - - ServiceReplacer policy = new ServiceReplacer(new ConfigBag() - .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED) - .configure(ServiceReplacer.SET_ON_FIRE_ON_FAILURE, false)); - cluster.addPolicy(policy); - - final Set<Entity> initialMembers = ImmutableSet.copyOf(cluster.getMembers()); - final TestEntity e1 = (TestEntity) Iterables.get(initialMembers, 0); - - e1.emit(HASensors.ENTITY_FAILED, new FailureDescriptor(e1, "simulate failure")); - - // Configured to not mark cluster as on fire - Asserts.succeedsContinually(new Runnable() { - @Override public void run() { - assertNotEquals(cluster.getAttribute(Attributes.SERVICE_STATE_ACTUAL), Lifecycle.ON_FIRE); - }}); - - // And will have received notification event about it - assertEventuallyHasEntityReplacementFailedEvent(cluster); - } - - @Test(groups="Integration") // 1s wait - public void testStopFailureOfOldEntityDoesNotSetClusterOnFire() throws Exception { - app.subscribe(null, ServiceReplacer.ENTITY_REPLACEMENT_FAILED, eventListener); - - final DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class) - .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(FailingEntity.class) - .configure(FailingEntity.FAIL_ON_STOP_CONDITION, predicateOnlyTrueForCallAt(1))) - .configure(DynamicCluster.INITIAL_SIZE, 2)); - app.start(ImmutableList.<Location>of(loc)); - - cluster.addPolicy(PolicySpec.create(ServiceReplacer.class) - .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED)); - - final Set<Entity> initialMembers = ImmutableSet.copyOf(cluster.getMembers()); - final TestEntity e1 = (TestEntity) Iterables.get(initialMembers, 0); - - 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(cluster.getMembers()), initialMembers); - Set<Entity> removedMembers = Sets.difference(initialMembers, ImmutableSet.copyOf(cluster.getMembers())); - assertEquals(removedMembers, ImmutableSet.of(e1)); - assertEquals(newMembers.size(), 1); - assertEquals(((TestEntity)Iterables.getOnlyElement(newMembers)).getCallHistory(), ImmutableList.of("start")); - assertEquals(e1.getCallHistory(), ImmutableList.of("start", "stop")); - assertFalse(Entities.isManaged(e1)); - }}); - - // Failure to stop the failed member should not cause "on-fire" of cluster - Asserts.succeedsContinually(new Runnable() { - @Override public void run() { - assertNotEquals(cluster.getAttribute(Attributes.SERVICE_STATE_ACTUAL), Lifecycle.ON_FIRE); - }}); - } - - /** - * If we keep on getting failure reports, never managing to replace the failed node, then don't keep trying - * (i.e. avoid infinite loop). - * - * TODO This code + configuration needs some work; it's not testing quite the scenarios that I - * was thinking of! - * I saw problem where a node failed, and the replacements failed, and we ended up trying thousands of times. - * (describing this scenario is made more complex by me having temporarily disabled the cluster from - * removing failed members, for debugging purposes!) - * Imagine these two scenarios: - * <ol> - * <li>Entity fails during call to start(). - * Here, the cluster removes it as a member (either unmanages it or puts it in quarantine) - * So the ENTITY_FAILED is ignored because the entity is not a member at that point. - * <li>Entity returns from start(), but quickly goes to service-down. - * Here we'll keep trying to replace that entity. Depending how long that takes, we'll either - * enter a horrible infinite loop, or we'll just provision a huge number of VMs over a long - * time period. - * Unfortunately this scenario is not catered for in the code yet. - * </ol> - */ - @Test(groups="Integration") // because takes 1.2 seconds - public void testAbandonsReplacementAfterNumFailures() throws Exception { - app.subscribe(null, ServiceReplacer.ENTITY_REPLACEMENT_FAILED, eventListener); - - final DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class) - .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(FailingEntity.class) - .configure(FailingEntity.FAIL_ON_START_CONDITION, predicateOnlyTrueForCallAtOrAfter(11))) - .configure(DynamicCluster.INITIAL_SIZE, 10) - .configure(DynamicCluster.QUARANTINE_FAILED_ENTITIES, true)); - app.start(ImmutableList.<Location>of(loc)); - - ServiceReplacer policy = new ServiceReplacer(new ConfigBag() - .configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED) - .configure(ServiceReplacer.FAIL_ON_NUM_RECURRING_FAILURES, 3)); - cluster.addPolicy(policy); - - final Set<Entity> initialMembers = ImmutableSet.copyOf(cluster.getMembers()); - for (int i = 0; i < 5; i++) { - final int counter = i+1; - EntityInternal entity = (EntityInternal) Iterables.get(initialMembers, i); - entity.emit(HASensors.ENTITY_FAILED, new FailureDescriptor(entity, "simulate failure")); - if (i <= 3) { - Asserts.succeedsEventually(new Runnable() { - @Override public void run() { - Set<FailingEntity> all = ImmutableSet.copyOf(Iterables.filter(managementContext.getEntityManager().getEntities(), FailingEntity.class)); - Set<FailingEntity> replacements = Sets.difference(all, initialMembers); - Set<?> replacementMembers = Sets.intersection(ImmutableSet.of(cluster.getMembers()), replacements); - assertTrue(replacementMembers.isEmpty()); - assertEquals(replacements.size(), counter); - }}); - } else { - Asserts.succeedsContinually(new Runnable() { - @Override public void run() { - Set<FailingEntity> all = ImmutableSet.copyOf(Iterables.filter(managementContext.getEntityManager().getEntities(), FailingEntity.class)); - Set<FailingEntity> replacements = Sets.difference(all, initialMembers); - assertEquals(replacements.size(), 4); - }}); - } - } - } - - - private Predicate<Object> predicateOnlyTrueForCallAt(final int callNumber) { - return predicateOnlyTrueForCallRange(callNumber, callNumber); - } - - private Predicate<Object> predicateOnlyTrueForCallAtOrAfter(final int callLowerNumber) { - return predicateOnlyTrueForCallRange(callLowerNumber, Integer.MAX_VALUE); - } - - private Predicate<Object> predicateOnlyTrueForCallRange(final int callLowerNumber, final int callUpperNumber) { - return new Predicate<Object>() { - private final AtomicInteger counter = new AtomicInteger(0); - @Override public boolean apply(Object input) { - int num = counter.incrementAndGet(); - return num >= callLowerNumber && num <= callUpperNumber; - } - }; - } - - private void assertEventuallyHasEntityReplacementFailedEvent(final Entity expectedCluster) { - Asserts.succeedsEventually(new Runnable() { - @Override public void run() { - assertEquals(Iterables.getOnlyElement(events).getSensor(), ServiceReplacer.ENTITY_REPLACEMENT_FAILED, "events="+events); - assertEquals(Iterables.getOnlyElement(events).getSource(), expectedCluster, "events="+events); - assertEquals(((FailureDescriptor)Iterables.getOnlyElement(events).getValue()).getComponent(), expectedCluster, "events="+events); - }}); - } -}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/ha/ServiceRestarterTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/ha/ServiceRestarterTest.java b/policy/src/test/java/brooklyn/policy/ha/ServiceRestarterTest.java deleted file mode 100644 index 81cff78..0000000 --- a/policy/src/test/java/brooklyn/policy/ha/ServiceRestarterTest.java +++ /dev/null @@ -1,190 +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.assertNotEquals; -import static org.testng.Assert.assertTrue; - -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.apache.brooklyn.api.entity.proxying.EntitySpec; -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.core.util.config.ConfigBag; -import org.apache.brooklyn.test.entity.LocalManagementContextForTests; -import org.apache.brooklyn.test.entity.TestApplication; -import org.apache.brooklyn.test.entity.TestEntity; -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.trait.FailingEntity; -import brooklyn.policy.ha.HASensors.FailureDescriptor; -import brooklyn.test.Asserts; -import brooklyn.util.exceptions.Exceptions; - -import com.google.common.base.Function; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - -public class ServiceRestarterTest { - - private static final int TIMEOUT_MS = 10*1000; - - private ManagementContext managementContext; - private TestApplication app; - private TestEntity e1; - private ServiceRestarter policy; - private SensorEventListener<Object> eventListener; - private List<SensorEvent<?>> events; - - @BeforeMethod(alwaysRun=true) - public void setUp() throws Exception { - managementContext = new LocalManagementContextForTests(); - app = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext); - e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class)); - events = Lists.newCopyOnWriteArrayList(); - eventListener = new SensorEventListener<Object>() { - @Override public void onEvent(SensorEvent<Object> event) { - events.add(event); - } - }; - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - if (managementContext != null) Entities.destroyAll(managementContext); - } - - @Test - public void testRestartsOnFailure() throws Exception { - policy = new ServiceRestarter(new ConfigBag().configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED)); - e1.addPolicy(policy); - - e1.emit(HASensors.ENTITY_FAILED, new FailureDescriptor(e1, "simulate failure")); - - Asserts.succeedsEventually(new Runnable() { - @Override public void run() { - assertEquals(e1.getCallHistory(), ImmutableList.of("restart")); - }}); - } - - @Test(groups="Integration") // Has a 1 second wait - public void testDoesNotRestartsWhenHealthy() throws Exception { - policy = new ServiceRestarter(new ConfigBag().configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED)); - e1.addPolicy(policy); - - e1.emit(HASensors.ENTITY_RECOVERED, new FailureDescriptor(e1, "not a failure")); - - Asserts.succeedsContinually(new Runnable() { - @Override public void run() { - assertEquals(e1.getCallHistory(), ImmutableList.of()); - }}); - } - - @Test - public void testEmitsFailureEventWhenRestarterFails() throws Exception { - final FailingEntity e2 = app.createAndManageChild(EntitySpec.create(FailingEntity.class) - .configure(FailingEntity.FAIL_ON_RESTART, true)); - app.subscribe(e2, ServiceRestarter.ENTITY_RESTART_FAILED, eventListener); - - policy = new ServiceRestarter(new ConfigBag().configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED)); - e2.addPolicy(policy); - - e2.emit(HASensors.ENTITY_FAILED, new FailureDescriptor(e2, "simulate failure")); - - Asserts.succeedsEventually(new Runnable() { - @Override public void run() { - assertEquals(Iterables.getOnlyElement(events).getSensor(), ServiceRestarter.ENTITY_RESTART_FAILED, "events="+events); - assertEquals(Iterables.getOnlyElement(events).getSource(), e2, "events="+events); - assertEquals(((FailureDescriptor)Iterables.getOnlyElement(events).getValue()).getComponent(), e2, "events="+events); - }}); - - assertEquals(e2.getAttribute(Attributes.SERVICE_STATE_ACTUAL), Lifecycle.ON_FIRE); - } - - @Test - public void testDoesNotSetOnFireOnFailure() throws Exception { - final FailingEntity e2 = app.createAndManageChild(EntitySpec.create(FailingEntity.class) - .configure(FailingEntity.FAIL_ON_RESTART, true)); - app.subscribe(e2, ServiceRestarter.ENTITY_RESTART_FAILED, eventListener); - - policy = new ServiceRestarter(new ConfigBag() - .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED) - .configure(ServiceRestarter.SET_ON_FIRE_ON_FAILURE, false)); - e2.addPolicy(policy); - - e2.emit(HASensors.ENTITY_FAILED, new FailureDescriptor(e2, "simulate failure")); - - Asserts.succeedsContinually(new Runnable() { - @Override public void run() { - assertNotEquals(e2.getAttribute(Attributes.SERVICE_STATE_ACTUAL), Lifecycle.ON_FIRE); - }}); - } - - // Previously RestarterPolicy called entity.restart inside the event-listener thread. - // That caused all other events for that entity's subscriptions to be queued until that - // entity's single event handler thread was free again. - @Test - public void testRestartDoesNotBlockOtherSubscriptions() throws Exception { - final CountDownLatch inRestartLatch = new CountDownLatch(1); - final CountDownLatch continueRestartLatch = new CountDownLatch(1); - - final FailingEntity e2 = app.createAndManageChild(EntitySpec.create(FailingEntity.class) - .configure(FailingEntity.FAIL_ON_RESTART, true) - .configure(FailingEntity.EXEC_ON_FAILURE, new Function<Object, Void>() { - @Override public Void apply(Object input) { - inRestartLatch.countDown(); - try { - continueRestartLatch.await(); - } catch (InterruptedException e) { - throw Exceptions.propagate(e); - } - return null; - }})); - - e2.addPolicy(PolicySpec.create(ServiceRestarter.class) - .configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, HASensors.ENTITY_FAILED)); - e2.subscribe(e2, TestEntity.SEQUENCE, eventListener); - - // Cause failure, and wait for entity.restart to be blocking - e2.emit(HASensors.ENTITY_FAILED, new FailureDescriptor(e1, "simulate failure")); - assertTrue(inRestartLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - - // Expect other notifications to continue to get through - e2.setAttribute(TestEntity.SEQUENCE, 1); - Asserts.succeedsEventually(new Runnable() { - @Override public void run() { - assertEquals(Iterables.getOnlyElement(events).getValue(), 1); - }}); - - // Allow restart to finish - continueRestartLatch.countDown(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/loadbalancing/AbstractLoadBalancingPolicyTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/loadbalancing/AbstractLoadBalancingPolicyTest.java b/policy/src/test/java/brooklyn/policy/loadbalancing/AbstractLoadBalancingPolicyTest.java deleted file mode 100644 index 68db44c..0000000 --- a/policy/src/test/java/brooklyn/policy/loadbalancing/AbstractLoadBalancingPolicyTest.java +++ /dev/null @@ -1,253 +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.loadbalancing; - -import static org.testng.Assert.assertEquals; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.Group; -import org.apache.brooklyn.api.entity.basic.EntityLocal; -import org.apache.brooklyn.api.entity.proxying.EntitySpec; -import org.apache.brooklyn.api.event.AttributeSensor; -import org.apache.brooklyn.test.entity.TestApplication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; - -import brooklyn.config.ConfigKey; -import brooklyn.entity.basic.DynamicGroup; -import brooklyn.entity.basic.Entities; -import brooklyn.event.basic.BasicConfigKey; -import brooklyn.event.basic.Sensors; -import org.apache.brooklyn.location.basic.SimulatedLocation; -import brooklyn.test.Asserts; -import brooklyn.util.collections.MutableMap; -import brooklyn.util.time.Time; - -import com.google.common.base.Function; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -public class AbstractLoadBalancingPolicyTest { - - private static final Logger LOG = LoggerFactory.getLogger(AbstractLoadBalancingPolicyTest.class); - - protected static final long TIMEOUT_MS = 10*1000; - protected static final long SHORT_WAIT_MS = 250; - - protected static final long CONTAINER_STARTUP_DELAY_MS = 100; - - public static final AttributeSensor<Integer> TEST_METRIC = - Sensors.newIntegerSensor("test.metric", "Dummy workrate for test entities"); - - public static final ConfigKey<Double> LOW_THRESHOLD_CONFIG_KEY = new BasicConfigKey<Double>(Double.class, TEST_METRIC.getName()+".threshold.low", "desc", 0.0); - public static final ConfigKey<Double> HIGH_THRESHOLD_CONFIG_KEY = new BasicConfigKey<Double>(Double.class, TEST_METRIC.getName()+".threshold.high", "desc", 0.0); - - protected TestApplication app; - protected SimulatedLocation loc; - protected BalanceableWorkerPool pool; - protected DefaultBalanceablePoolModel<Entity, Entity> model; - protected LoadBalancingPolicy policy; - protected Group containerGroup; - protected Group itemGroup; - protected Random random = new Random(); - - @BeforeMethod(alwaysRun=true) - public void before() { - LOG.debug("In AbstractLoadBalancingPolicyTest.before()"); - - MockItemEntityImpl.totalMoveCount.set(0); - MockItemEntityImpl.lastMoveTime.set(0); - - loc = new SimulatedLocation(MutableMap.of("name", "loc")); - - model = new DefaultBalanceablePoolModel<Entity, Entity>("pool-model"); - - app = TestApplication.Factory.newManagedInstanceForTests(); - containerGroup = app.createAndManageChild(EntitySpec.create(DynamicGroup.class) - .displayName("containerGroup") - .configure(DynamicGroup.ENTITY_FILTER, Predicates.instanceOf(MockContainerEntity.class))); - itemGroup = app.createAndManageChild(EntitySpec.create(DynamicGroup.class) - .displayName("itemGroup") - .configure(DynamicGroup.ENTITY_FILTER, Predicates.instanceOf(MockItemEntity.class))); - pool = app.createAndManageChild(EntitySpec.create(BalanceableWorkerPool.class)); - pool.setContents(containerGroup, itemGroup); - policy = new LoadBalancingPolicy(MutableMap.of("minPeriodBetweenExecs", 1), TEST_METRIC, model); - pool.addPolicy(policy); - app.start(ImmutableList.of(loc)); - } - - @AfterMethod(alwaysRun=true) - public void after() { - if (policy != null) policy.destroy(); - if (app != null) Entities.destroyAll(app.getManagementContext()); - } - - // Using this utility, as it gives more info about the workrates of all containers rather than just the one that differs - protected void assertWorkrates(Collection<MockContainerEntity> containers, Collection<Double> expectedC, double precision) { - Iterable<Double> actual = Iterables.transform(containers, new Function<MockContainerEntity, Double>() { - public Double apply(MockContainerEntity input) { - return getContainerWorkrate(input); - }}); - - List<Double> expected = Lists.newArrayList(expectedC); - String errMsg = "actual="+actual+"; expected="+expected; - assertEquals(containers.size(), expected.size(), errMsg); - for (int i = 0; i < containers.size(); i++) { - assertEquals(Iterables.get(actual, i), expected.get(i), precision, errMsg); - } - } - - protected void assertWorkratesEventually(Collection<MockContainerEntity> containers, Iterable<? extends Movable> items, Collection<Double> expected) { - assertWorkratesEventually(containers, items, expected, 0d); - } - - /** - * Asserts that the given container have the given expected workrates (by querying the containers directly). - * Accepts an accuracy of "precision" for each container's workrate. - */ - protected void assertWorkratesEventually(final Collection<MockContainerEntity> containers, final Iterable<? extends Movable> items, final Collection<Double> expected, final double precision) { - try { - Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { - public void run() { - assertWorkrates(containers, expected, precision); - }}); - } catch (AssertionError e) { - String errMsg = e.getMessage()+"; "+verboseDumpToString(containers, items); - throw new RuntimeException(errMsg, e); - } - } - - // Using this utility, as it gives more info about the workrates of all containers rather than just the one that differs - protected void assertWorkratesContinually(List<MockContainerEntity> containers, Iterable<? extends Movable> items, List<Double> expected) { - assertWorkratesContinually(containers, items, expected, 0d); - } - - /** - * Asserts that the given containers have the given expected workrates (by querying the containers directly) - * continuously for SHORT_WAIT_MS. - * Accepts an accuracy of "precision" for each container's workrate. - */ - protected void assertWorkratesContinually(final List<MockContainerEntity> containers, Iterable<? extends Movable> items, final List<Double> expected, final double precision) { - try { - Asserts.succeedsContinually(MutableMap.of("timeout", SHORT_WAIT_MS), new Runnable() { - public void run() { - assertWorkrates(containers, expected, precision); - }}); - } catch (AssertionError e) { - String errMsg = e.getMessage()+"; "+verboseDumpToString(containers, items); - throw new RuntimeException(errMsg, e); - } - } - - protected String verboseDumpToString(Iterable<MockContainerEntity> containers, Iterable<? extends Movable> items) { - Iterable<Double> containerRates = Iterables.transform(containers, new Function<MockContainerEntity, Double>() { - @Override public Double apply(MockContainerEntity input) { - return (double) input.getWorkrate(); - }}); - - Map<MockContainerEntity, Set<Movable>> itemDistributionByContainer = Maps.newLinkedHashMap(); - for (MockContainerEntity container : containers) { - itemDistributionByContainer.put(container, container.getBalanceableItems()); - } - - Map<Movable, BalanceableContainer<?>> itemDistributionByItem = Maps.newLinkedHashMap(); - for (Movable item : items) { - itemDistributionByItem.put(item, item.getAttribute(Movable.CONTAINER)); - } - - String modelItemDistribution = model.itemDistributionToString(); - return "containers="+containers+"; containerRates="+containerRates - +"; itemDistributionByContainer="+itemDistributionByContainer - +"; itemDistributionByItem="+itemDistributionByItem - +"; model="+modelItemDistribution - +"; totalMoves="+MockItemEntityImpl.totalMoveCount - +"; lastMoveTime="+Time.makeDateString(MockItemEntityImpl.lastMoveTime.get()); - } - - protected MockContainerEntity newContainer(TestApplication app, String name, double lowThreshold, double highThreshold) { - return newAsyncContainer(app, name, lowThreshold, highThreshold, 0); - } - - /** - * Creates a new container that will take "delay" millis to complete its start-up. - */ - protected MockContainerEntity newAsyncContainer(TestApplication app, String name, double lowThreshold, double highThreshold, long delay) { - MockContainerEntity container = app.createAndManageChild(EntitySpec.create(MockContainerEntity.class) - .displayName(name) - .configure(MockContainerEntity.DELAY, delay) - .configure(LOW_THRESHOLD_CONFIG_KEY, lowThreshold) - .configure(HIGH_THRESHOLD_CONFIG_KEY, highThreshold)); - LOG.debug("Managed new container {}", container); - container.start(ImmutableList.of(loc)); - return container; - } - - protected static MockItemEntity newItem(TestApplication app, MockContainerEntity container, String name, double workrate) { - MockItemEntity item = app.createAndManageChild(EntitySpec.create(MockItemEntity.class) - .displayName(name)); - LOG.debug("Managing new item {} on container {}", item, container); - item.move(container); - ((EntityLocal)item).setAttribute(TEST_METRIC, (int)workrate); - return item; - } - - protected static MockItemEntity newLockedItem(TestApplication app, MockContainerEntity container, String name, double workrate) { - MockItemEntity item = app.createAndManageChild(EntitySpec.create(MockItemEntity.class) - .displayName(name) - .configure(Movable.IMMOVABLE, true)); - LOG.debug("Managed new item {} on container {}", item, container); - item.move(container); - ((EntityLocal)item).setAttribute(TEST_METRIC, (int)workrate); - return item; - } - - /** - * Asks the item directly for its workrate. - */ - protected static double getItemWorkrate(MockItemEntity item) { - Object result = item.getAttribute(TEST_METRIC); - return (result == null ? 0 : ((Number) result).doubleValue()); - } - - /** - * Asks the container for its items, and then each of those items directly for their workrates; returns the total. - */ - protected static double getContainerWorkrate(MockContainerEntity container) { - double result = 0.0; - Preconditions.checkNotNull(container, "container"); - for (Movable item : container.getBalanceableItems()) { - Preconditions.checkNotNull(item, "item in container"); - assertEquals(item.getContainerId(), container.getId()); - result += getItemWorkrate((MockItemEntity)item); - } - return result; - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/loadbalancing/BalanceableWorkerPoolTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/loadbalancing/BalanceableWorkerPoolTest.java b/policy/src/test/java/brooklyn/policy/loadbalancing/BalanceableWorkerPoolTest.java deleted file mode 100644 index e744720..0000000 --- a/policy/src/test/java/brooklyn/policy/loadbalancing/BalanceableWorkerPoolTest.java +++ /dev/null @@ -1,133 +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.loadbalancing; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; - -import org.apache.brooklyn.api.entity.Group; -import org.apache.brooklyn.api.entity.proxying.EntitySpec; -import org.apache.brooklyn.api.entity.proxying.ImplementedBy; -import org.apache.brooklyn.test.entity.TestApplication; -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.AbstractGroup; -import brooklyn.entity.basic.AbstractGroupImpl; -import brooklyn.entity.basic.ApplicationBuilder; -import brooklyn.entity.basic.DynamicGroup; -import brooklyn.entity.basic.Entities; -import brooklyn.entity.trait.Resizable; -import org.apache.brooklyn.location.basic.SimulatedLocation; -import brooklyn.util.collections.MutableMap; -import brooklyn.util.exceptions.Exceptions; - -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableList; - -public class BalanceableWorkerPoolTest { - - private static final Logger LOG = LoggerFactory.getLogger(BalanceableWorkerPoolTest.class); - - protected static final long TIMEOUT_MS = 10*1000; - protected static final long SHORT_WAIT_MS = 250; - - protected static final long CONTAINER_STARTUP_DELAY_MS = 100; - - protected TestApplication app; - protected SimulatedLocation loc; - protected BalanceableWorkerPool pool; - protected Group containerGroup; - protected Group itemGroup; - - @BeforeMethod(alwaysRun=true) - public void before() { - loc = new SimulatedLocation(MutableMap.of("name", "loc")); - - app = ApplicationBuilder.newManagedApp(TestApplication.class); - containerGroup = app.createAndManageChild(EntitySpec.create(DynamicGroup.class) - .displayName("containerGroup") - .configure(DynamicGroup.ENTITY_FILTER, Predicates.instanceOf(MockContainerEntity.class))); - itemGroup = app.createAndManageChild(EntitySpec.create(DynamicGroup.class) - .displayName("itemGroup") - .configure(DynamicGroup.ENTITY_FILTER, Predicates.instanceOf(MockItemEntity.class))); - pool = app.createAndManageChild(EntitySpec.create(BalanceableWorkerPool.class)); - pool.setContents(containerGroup, itemGroup); - - app.start(ImmutableList.of(loc)); - } - - @AfterMethod(alwaysRun=true) - public void after() { - if (app != null) Entities.destroyAll(app.getManagementContext()); - } - - @Test - public void testDefaultResizeFailsIfContainerGroupNotResizable() throws Exception { - try { - pool.resize(1); - fail(); - } catch (Exception e) { - if (Exceptions.getFirstThrowableOfType(e, UnsupportedOperationException.class) == null) throw e; - } - } - - @Test - public void testDefaultResizeCallsResizeOnContainerGroup() { - LocallyResizableGroup resizable = app.createAndManageChild(EntitySpec.create(LocallyResizableGroup.class)); - - BalanceableWorkerPool pool2 = app.createAndManageChild(EntitySpec.create(BalanceableWorkerPool.class)); - pool2.setContents(resizable, itemGroup); - Entities.manage(pool2); - - pool2.resize(123); - assertEquals(resizable.getCurrentSize(), (Integer) 123); - } - - @Test - public void testCustomResizableCalledWhenResizing() { - LocallyResizableGroup resizable = app.createAndManageChild(EntitySpec.create(LocallyResizableGroup.class)); - - pool.setResizable(resizable); - - pool.resize(123); - assertEquals(resizable.getCurrentSize(), (Integer)123); - } - - @ImplementedBy(LocallyResizableGroupImpl.class) - public static interface LocallyResizableGroup extends AbstractGroup, Resizable { - } - - public static class LocallyResizableGroupImpl extends AbstractGroupImpl implements LocallyResizableGroup { - private int size = 0; - - @Override - public Integer resize(Integer newSize) { - size = newSize; - return size; - } - @Override - public Integer getCurrentSize() { - return size; - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/loadbalancing/ItemsInContainersGroupTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/loadbalancing/ItemsInContainersGroupTest.java b/policy/src/test/java/brooklyn/policy/loadbalancing/ItemsInContainersGroupTest.java deleted file mode 100644 index cf47359..0000000 --- a/policy/src/test/java/brooklyn/policy/loadbalancing/ItemsInContainersGroupTest.java +++ /dev/null @@ -1,189 +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.loadbalancing; - -import static org.testng.Assert.assertEquals; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.Group; -import org.apache.brooklyn.api.entity.proxying.EntitySpec; -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.DynamicGroup; -import brooklyn.entity.basic.Entities; -import org.apache.brooklyn.location.basic.SimulatedLocation; -import brooklyn.test.Asserts; -import brooklyn.util.collections.MutableMap; - -import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; - -public class ItemsInContainersGroupTest { - - // all tests are 20ms or less, but use a big timeout just in case very slow machine! - private static final long TIMEOUT_MS = 15000; - - private TestApplication app; - private SimulatedLocation loc; - private Group containerGroup; - private ItemsInContainersGroup itemGroup; - - @BeforeMethod(alwaysRun=true) - public void setUp() throws Exception { - loc = new SimulatedLocation(MutableMap.of("name", "loc")); - - app = ApplicationBuilder.newManagedApp(TestApplication.class); - containerGroup = app.createAndManageChild(EntitySpec.create(DynamicGroup.class) - .displayName("containerGroup") - .configure(DynamicGroup.ENTITY_FILTER, new Predicate<Entity>() { - public boolean apply(Entity input) { - return input instanceof MockContainerEntity && - input.getConfig(MockContainerEntity.MOCK_MEMBERSHIP) == "ingroup"; - }})); - itemGroup = app.createAndManageChild(EntitySpec.create(ItemsInContainersGroup.class) - .displayName("itemGroup")); - itemGroup.setContainers(containerGroup); - - app.start(ImmutableList.of(loc)); - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - if (app != null) Entities.destroyAll(app.getManagementContext()); - } - - @Test - public void testSimpleMembership() throws Exception { - MockContainerEntity containerIn = newContainer(app, "A", "ingroup"); - MockItemEntity item1 = newItem(app, containerIn, "1"); - MockItemEntity item2 = newItem(app, containerIn, "2"); - - assertItemsEventually(item1, item2); - } - - @Test - public void testFilterIsAppliedToItems() throws Exception { - itemGroup.stop(); - Entities.unmanage(itemGroup); - - itemGroup = app.createAndManageChild(EntitySpec.create(ItemsInContainersGroup.class) - .displayName("itemGroupWithDispName2") - .configure(ItemsInContainersGroup.ITEM_FILTER, new Predicate<Entity>() { - public boolean apply(Entity input) { - return "2".equals(input.getDisplayName()); - }})); - itemGroup.setContainers(containerGroup); - - MockContainerEntity containerIn = newContainer(app, "A", "ingroup"); - MockItemEntity item1 = newItem(app, containerIn, "1"); - MockItemEntity item2 = newItem(app, containerIn, "2"); - - assertItemsEventually(item2); // does not include item1 - } - - @Test - public void testItemsInOtherContainersIgnored() throws Exception { - MockContainerEntity containerOut = newContainer(app, "A", "outgroup"); - MockItemEntity item1 = newItem(app, containerOut, "1"); - - assertItemsEventually(); - } - - @Test - public void testItemMovedInIsAdded() throws Exception { - MockContainerEntity containerIn = newContainer(app, "A", "ingroup"); - MockContainerEntity containerOut = newContainer(app, "A", "outgroup"); - MockItemEntity item1 = newItem(app, containerOut, "1"); - item1.move(containerIn); - - assertItemsEventually(item1); - } - - @Test - public void testItemMovedOutIsRemoved() throws Exception { - MockContainerEntity containerIn = newContainer(app, "A", "ingroup"); - MockContainerEntity containerOut = newContainer(app, "A", "outgroup"); - MockItemEntity item1 = newItem(app, containerIn, "1"); - assertItemsEventually(item1); - - item1.move(containerOut); - assertItemsEventually(); - } - - /* - * Previously could fail if... - * ItemsInContainersGroupImpl listener got notified of Movable.CONTAINER after entity was unmanaged - * (because being done in concurrent threads). - * This called ItemsInContainersGroupImpl.onItemMoved, which called addMember to add it back in again. - * In AbstractGroup.addMember, we now check if the entity is still managed, to - * ensure there is synchronization for concurrent calls to add/remove member. - */ - @Test - public void testItemUnmanagedIsRemoved() throws Exception { - MockContainerEntity containerIn = newContainer(app, "A", "ingroup"); - MockItemEntity item1 = newItem(app, containerIn, "1"); - assertItemsEventually(item1); - - Entities.unmanage(item1); - assertItemsEventually(); - } - - // TODO How to test this? Will it be used? - // Adding a new container then adding items to it is tested in many other methods. - @Test(enabled=false) - public void testContainerAddedWillAddItsItems() throws Exception { - } - - @Test - public void testContainerRemovedWillRemoveItsItems() throws Exception { - MockContainerEntity containerA = newContainer(app, "A", "ingroup"); - MockItemEntity item1 = newItem(app, containerA, "1"); - assertItemsEventually(item1); - - Entities.unmanage(containerA); - assertItemsEventually(); - } - - private void assertItemsEventually(final MockItemEntity... expected) { - Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { - public void run() { - assertEquals(ImmutableSet.copyOf(itemGroup.getMembers()), ImmutableSet.copyOf(expected)); - }}); - } - - private MockContainerEntity newContainer(TestApplication app, String name, String membership) { - MockContainerEntity container = app.createAndManageChild(EntitySpec.create(MockContainerEntity.class) - .displayName(name) - .configure(MockContainerEntity.MOCK_MEMBERSHIP, membership)); - container.start(ImmutableList.of(loc)); - return container; - } - - private static MockItemEntity newItem(TestApplication app, MockContainerEntity container, String name) { - MockItemEntity item = app.createAndManageChild(EntitySpec.create(MockItemEntity.class) - .displayName(name)); - item.move(container); - return item; - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingModelTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingModelTest.java b/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingModelTest.java deleted file mode 100644 index cae86e7..0000000 --- a/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingModelTest.java +++ /dev/null @@ -1,115 +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.loadbalancing; - -import static org.testng.Assert.assertEquals; - -import java.util.Collections; - -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import brooklyn.entity.basic.Entities; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; - -public class LoadBalancingModelTest { - - private static final double PRECISION = 0.00001; - - private MockContainerEntity container1 = new MockContainerEntityImpl(); - private MockContainerEntity container2 = new MockContainerEntityImpl(); - private MockItemEntity item1 = new MockItemEntityImpl(); - private MockItemEntity item2 = new MockItemEntityImpl(); - private MockItemEntity item3 = new MockItemEntityImpl(); - - private DefaultBalanceablePoolModel<MockContainerEntity, MockItemEntity> model; - - @BeforeMethod(alwaysRun=true) - public void setUp() throws Exception { - model = new DefaultBalanceablePoolModel<MockContainerEntity, MockItemEntity>("myname"); - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - // nothing to tear down; no management context created - } - - @Test - public void testPoolRatesCorrectlySumContainers() throws Exception { - model.onContainerAdded(container1, 10d, 20d); - model.onContainerAdded(container2, 11d, 22d); - - assertEquals(model.getPoolLowThreshold(), 10d+11d, PRECISION); - assertEquals(model.getPoolHighThreshold(), 20d+22d, PRECISION); - } - - @Test - public void testPoolRatesCorrectlySumItems() throws Exception { - model.onContainerAdded(container1, 10d, 20d); - model.onItemAdded(item1, container1, true); - model.onItemAdded(item2, container1, true); - - model.onItemWorkrateUpdated(item1, 1d); - assertEquals(model.getCurrentPoolWorkrate(), 1d, PRECISION); - - model.onItemWorkrateUpdated(item2, 2d); - assertEquals(model.getCurrentPoolWorkrate(), 1d+2d, PRECISION); - - model.onItemWorkrateUpdated(item2, 4d); - assertEquals(model.getCurrentPoolWorkrate(), 1d+4d, PRECISION); - - model.onItemRemoved(item1); - assertEquals(model.getCurrentPoolWorkrate(), 4d, PRECISION); - } - - @Test - public void testWorkrateUpdateAfterItemRemovalIsNotRecorded() throws Exception { - model.onContainerAdded(container1, 10d, 20d); - model.onItemAdded(item1, container1, true); - model.onItemRemoved(item1); - model.onItemWorkrateUpdated(item1, 123d); - - assertEquals(model.getCurrentPoolWorkrate(), 0d, PRECISION); - assertEquals(model.getContainerWorkrates().get(container1), 0d, PRECISION); - assertEquals(model.getItemWorkrate(item1), null); - } - - @Test - public void testItemMovedWillUpdateContainerWorkrates() throws Exception { - model.onContainerAdded(container1, 10d, 20d); - model.onContainerAdded(container2, 11d, 21d); - model.onItemAdded(item1, container1, false); - model.onItemWorkrateUpdated(item1, 123d); - - model.onItemMoved(item1, container2); - - assertEquals(model.getItemsForContainer(container1), Collections.emptySet()); - assertEquals(model.getItemsForContainer(container2), ImmutableSet.of(item1)); - assertEquals(model.getItemWorkrate(item1), 123d); - assertEquals(model.getTotalWorkrate(container1), 0d); - assertEquals(model.getTotalWorkrate(container2), 123d); - assertEquals(model.getItemWorkrates(container1), Collections.emptyMap()); - assertEquals(model.getItemWorkrates(container2), ImmutableMap.of(item1, 123d)); - assertEquals(model.getContainerWorkrates(), ImmutableMap.of(container1, 0d, container2, 123d)); - assertEquals(model.getCurrentPoolWorkrate(), 123d); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingPolicyConcurrencyTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingPolicyConcurrencyTest.java b/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingPolicyConcurrencyTest.java deleted file mode 100644 index 69d380b..0000000 --- a/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingPolicyConcurrencyTest.java +++ /dev/null @@ -1,211 +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.loadbalancing; - -import java.util.Collections; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import org.apache.brooklyn.api.entity.basic.EntityLocal; -import org.apache.brooklyn.test.entity.TestApplication; -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.Entities; - -import com.google.common.collect.Lists; - -public class LoadBalancingPolicyConcurrencyTest extends AbstractLoadBalancingPolicyTest { - - private static final Logger LOG = LoggerFactory.getLogger(LoadBalancingPolicyConcurrencyTest.class); - - private static final double WORKRATE_JITTER = 2d; - private static final int NUM_CONTAINERS = 20; - private static final int WORKRATE_UPDATE_PERIOD_MS = 1000; - - private ScheduledExecutorService scheduledExecutor; - - @BeforeMethod(alwaysRun=true) - @Override - public void before() { - scheduledExecutor = Executors.newScheduledThreadPool(10); - super.before(); - } - - @AfterMethod(alwaysRun=true) - @Override - public void after() { - if (scheduledExecutor != null) scheduledExecutor.shutdownNow(); - super.after(); - } - - @Test - public void testSimplePeriodicWorkrateUpdates() { - List<MockItemEntity> items = Lists.newArrayList(); - List<MockContainerEntity> containers = Lists.newArrayList(); - - for (int i = 0; i < NUM_CONTAINERS; i++) { - containers.add(newContainer(app, "container"+i, 10, 30)); - } - for (int i = 0; i < NUM_CONTAINERS; i++) { - newItemWithPeriodicWorkrates(app, containers.get(0), "item"+i, 20); - } - - assertWorkratesEventually(containers, items, Collections.nCopies(NUM_CONTAINERS, 20d), WORKRATE_JITTER); - } - - @Test - public void testConcurrentlyAddContainers() { - final Queue<MockContainerEntity> containers = new ConcurrentLinkedQueue<MockContainerEntity>(); - final List<MockItemEntity> items = Lists.newArrayList(); - - containers.add(newContainer(app, "container-orig", 10, 30)); - - for (int i = 0; i < NUM_CONTAINERS; i++) { - items.add(newItemWithPeriodicWorkrates(app, containers.iterator().next(), "item"+i, 20)); - } - for (int i = 0; i < NUM_CONTAINERS-1; i++) { - final int index = i; - scheduledExecutor.submit(new Callable<Void>() { - @Override public Void call() { - containers.add(newContainer(app, "container"+index, 10, 30)); - return null; - }}); - } - - assertWorkratesEventually(containers, items, Collections.nCopies(NUM_CONTAINERS, 20d), WORKRATE_JITTER); - } - - @Test - public void testConcurrentlyAddItems() { - final Queue<MockItemEntity> items = new ConcurrentLinkedQueue<MockItemEntity>(); - final List<MockContainerEntity> containers = Lists.newArrayList(); - - for (int i = 0; i < NUM_CONTAINERS; i++) { - containers.add(newContainer(app, "container"+i, 10, 30)); - } - for (int i = 0; i < NUM_CONTAINERS; i++) { - final int index = i; - scheduledExecutor.submit(new Callable<Void>() { - @Override public Void call() { - items.add(newItemWithPeriodicWorkrates(app, containers.get(0), "item"+index, 20)); - return null; - }}); - } - assertWorkratesEventually(containers, items, Collections.nCopies(NUM_CONTAINERS, 20d), WORKRATE_JITTER); - } - - // TODO Got IndexOutOfBoundsException from containers.last() - @Test(groups="WIP", invocationCount=100) - public void testConcurrentlyRemoveContainers() { - List<MockItemEntity> items = Lists.newArrayList(); - final List<MockContainerEntity> containers = Lists.newArrayList(); - - for (int i = 0; i < NUM_CONTAINERS; i++) { - containers.add(newContainer(app, "container"+i, 15, 45)); - } - for (int i = 0; i < NUM_CONTAINERS; i++) { - items.add(newItemWithPeriodicWorkrates(app, containers.get(i), "item"+i, 20)); - } - - final List<MockContainerEntity> containersToStop = Lists.newArrayList(); - for (int i = 0; i < NUM_CONTAINERS/2; i++) { - containersToStop.add(containers.remove(0)); - } - for (final MockContainerEntity containerToStop : containersToStop) { - scheduledExecutor.submit(new Callable<Void>() { - @Override public Void call() { - try { - containerToStop.offloadAndStop(containers.get(containers.size()-1)); - Entities.unmanage(containerToStop); - } catch (Throwable t) { - LOG.error("Error stopping container "+containerToStop, t); - } - return null; - }}); - } - - assertWorkratesEventually(containers, items, Collections.nCopies((int)(NUM_CONTAINERS/2), 40d), WORKRATE_JITTER*2); - } - - @Test(groups="WIP") - public void testConcurrentlyRemoveItems() { - List<MockItemEntity> items = Lists.newArrayList(); - List<MockContainerEntity> containers = Lists.newArrayList(); - - for (int i = 0; i < NUM_CONTAINERS; i++) { - containers.add(newContainer(app, "container"+i, 15, 45)); - } - for (int i = 0; i < NUM_CONTAINERS*2; i++) { - items.add(newItemWithPeriodicWorkrates(app, containers.get(i%NUM_CONTAINERS), "item"+i, 20)); - } - // should now have item0 and item{0+NUM_CONTAINERS} on container0, etc - - for (int i = 0; i < NUM_CONTAINERS; i++) { - // not removing consecutive items as that would leave it balanced! - int indexToStop = (i < NUM_CONTAINERS/2) ? NUM_CONTAINERS : 0; - final MockItemEntity itemToStop = items.remove(indexToStop); - scheduledExecutor.submit(new Callable<Void>() { - @Override public Void call() { - try { - itemToStop.stop(); - Entities.unmanage(itemToStop); - } catch (Throwable t) { - LOG.error("Error stopping item "+itemToStop, t); - } - return null; - }}); - } - - assertWorkratesEventually(containers, items, Collections.nCopies(NUM_CONTAINERS, 20d), WORKRATE_JITTER); - } - - protected MockItemEntity newItemWithPeriodicWorkrates(TestApplication app, MockContainerEntity container, String name, double workrate) { - MockItemEntity item = newItem(app, container, name, workrate); - scheduleItemWorkrateUpdates(item, workrate, WORKRATE_JITTER); - return item; - } - - private void scheduleItemWorkrateUpdates(final MockItemEntity item, final double workrate, final double jitter) { - final AtomicReference<Future<?>> futureRef = new AtomicReference<Future<?>>(); - Future<?> future = scheduledExecutor.scheduleAtFixedRate( - new Runnable() { - @Override public void run() { - if (item.isStopped() && futureRef.get() != null) { - futureRef.get().cancel(true); - return; - } - double jitteredWorkrate = workrate + (random.nextDouble()*jitter*2 - jitter); - ((EntityLocal)item).setAttribute(TEST_METRIC, (int) Math.max(0, jitteredWorkrate)); - } - }, - 0, WORKRATE_UPDATE_PERIOD_MS, TimeUnit.MILLISECONDS); - futureRef.set(future); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d30ff597/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingPolicySoakTest.java ---------------------------------------------------------------------- diff --git a/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingPolicySoakTest.java b/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingPolicySoakTest.java deleted file mode 100644 index 1bb8dc4..0000000 --- a/policy/src/test/java/brooklyn/policy/loadbalancing/LoadBalancingPolicySoakTest.java +++ /dev/null @@ -1,273 +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.loadbalancing; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.basic.EntityLocal; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.Test; - -import brooklyn.entity.basic.Entities; -import brooklyn.test.Asserts; -import brooklyn.util.collections.MutableMap; - -import com.google.common.base.Function; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - -public class LoadBalancingPolicySoakTest extends AbstractLoadBalancingPolicyTest { - - private static final Logger LOG = LoggerFactory.getLogger(LoadBalancingPolicySoakTest.class); - - private static final long TIMEOUT_MS = 40*1000; - - @Test - public void testLoadBalancingQuickTest() { - RunConfig config = new RunConfig(); - config.numCycles = 1; - config.numContainers = 5; - config.numItems = 5; - config.lowThreshold = 200; - config.highThreshold = 300; - config.totalRate = (int) (config.numContainers*(0.95*config.highThreshold)); - - runLoadBalancingSoakTest(config); - } - - @Test - public void testLoadBalancingManyItemsQuickTest() { - RunConfig config = new RunConfig(); - config.numCycles = 1; - config.numContainers = 5; - config.numItems = 30; - config.lowThreshold = 200; - config.highThreshold = 300; - config.numContainerStopsPerCycle = 1; - config.numItemStopsPerCycle = 1; - config.totalRate = (int) (config.numContainers*(0.95*config.highThreshold)); - - runLoadBalancingSoakTest(config); - } - - @Test(groups={"Integration","Acceptance"}) // acceptance group, because it's slow to run many cycles - public void testLoadBalancingSoakTest() { - RunConfig config = new RunConfig(); - config.numCycles = 100; - config.numContainers = 5; - config.numItems = 5; - config.lowThreshold = 200; - config.highThreshold = 300; - config.totalRate = (int) (config.numContainers*(0.95*config.highThreshold)); - - runLoadBalancingSoakTest(config); - } - - @Test(groups={"Integration","Acceptance"}) // acceptance group, because it's slow to run many cycles - public void testLoadBalancingManyItemsSoakTest() { - RunConfig config = new RunConfig(); - config.numCycles = 100; - config.numContainers = 5; - config.numItems = 30; - config.lowThreshold = 200; - config.highThreshold = 300; - config.totalRate = (int) (config.numContainers*(0.95*config.highThreshold)); - config.numContainerStopsPerCycle = 3; - config.numItemStopsPerCycle = 10; - - runLoadBalancingSoakTest(config); - } - - @Test(groups={"Integration","Acceptance"}) - public void testLoadBalancingManyManyItemsTest() { - RunConfig config = new RunConfig(); - config.numCycles = 1; - config.numContainers = 5; - config.numItems = 1000; - config.lowThreshold = 2000; - config.highThreshold = 3000; - config.numContainerStopsPerCycle = 0; - config.numItemStopsPerCycle = 0; - config.totalRate = (int) (config.numContainers*(0.95*config.highThreshold)); - config.verbose = false; - - runLoadBalancingSoakTest(config); - } - - private void runLoadBalancingSoakTest(RunConfig config) { - final int numCycles = config.numCycles; - final int numContainers = config.numContainers; - final int numItems = config.numItems; - final double lowThreshold = config.lowThreshold; - final double highThreshold = config.highThreshold; - final int totalRate = config.totalRate; - final int numContainerStopsPerCycle = config.numContainerStopsPerCycle; - final int numItemStopsPerCycle = config.numItemStopsPerCycle; - final boolean verbose = config.verbose; - - MockItemEntityImpl.totalMoveCount.set(0); - - final List<MockContainerEntity> containers = new ArrayList<MockContainerEntity>(); - final List<MockItemEntity> items = new ArrayList<MockItemEntity>(); - - for (int i = 1; i <= numContainers; i++) { - MockContainerEntity container = newContainer(app, "container-"+i, lowThreshold, highThreshold); - containers.add(container); - } - for (int i = 1; i <= numItems; i++) { - MockItemEntity item = newItem(app, containers.get(0), "item-"+i, 5); - items.add(item); - } - - for (int i = 1; i <= numCycles; i++) { - LOG.info(LoadBalancingPolicySoakTest.class.getSimpleName()+": cycle "+i); - - // Stop items, and start others - for (int j = 1; j <= numItemStopsPerCycle; j++) { - int itemIndex = random.nextInt(numItems); - MockItemEntity itemToStop = items.get(itemIndex); - itemToStop.stop(); - LOG.debug("Unmanaging item {}", itemToStop); - Entities.unmanage(itemToStop); - items.set(itemIndex, newItem(app, containers.get(0), "item-"+(itemIndex+1)+"."+i+"."+j, 5)); - } - - // Repartition the load across the items - final List<Integer> itemRates = randomlyDivideLoad(numItems, totalRate, 0, (int)highThreshold); - - for (int j = 0; j < numItems; j++) { - MockItemEntity item = items.get(j); - ((EntityLocal)item).setAttribute(MockItemEntity.TEST_METRIC, itemRates.get(j)); - } - - // Stop containers, and start others - for (int j = 1; j <= numContainerStopsPerCycle; j++) { - int containerIndex = random.nextInt(numContainers); - MockContainerEntity containerToStop = containers.get(containerIndex); - containerToStop.offloadAndStop(containers.get((containerIndex+1)%numContainers)); - LOG.debug("Unmanaging container {}", containerToStop); - Entities.unmanage(containerToStop); - - MockContainerEntity containerToAdd = newContainer(app, "container-"+(containerIndex+1)+"."+i+"."+j, lowThreshold, highThreshold); - containers.set(containerIndex, containerToAdd); - } - - // Assert that the items become balanced again - Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() { - @Override public void run() { - Iterable<Double> containerRates = Iterables.transform(containers, new Function<MockContainerEntity, Double>() { - @Override public Double apply(MockContainerEntity input) { - return (double) input.getWorkrate(); - }}); - - String errMsg; - if (verbose) { - errMsg = verboseDumpToString(containers, items)+"; itemRates="+itemRates; - } else { - errMsg = containerRates+"; totalMoves="+MockItemEntityImpl.totalMoveCount; - } - - // Check that haven't lost any items - // (as observed in one jenkins build failure: 2014-03-18; but that could also be - // explained by errMsg generated in the middle of a move) - List<Entity> itemsFromModel = Lists.newArrayList(); - List<Entity> itemsFromContainers = Lists.newArrayList(); - for (Entity container : model.getPoolContents()) { - itemsFromModel.addAll(model.getItemsForContainer(container)); - } - for (MockContainerEntity container : containers) { - itemsFromContainers.addAll(container.getBalanceableItems()); - } - Asserts.assertEqualsIgnoringOrder(itemsFromModel, items, true, errMsg); - Asserts.assertEqualsIgnoringOrder(itemsFromContainers, items, true, errMsg); - - // Check overall container rates are balanced - assertEquals(sum(containerRates), sum(itemRates), errMsg); - for (double containerRate : containerRates) { - assertTrue(containerRate >= lowThreshold, errMsg); - assertTrue(containerRate <= highThreshold, errMsg); - } - }}); - } - } - - private static class RunConfig { - int numCycles = 1; - int numContainers = 5; - int numItems = 5; - double lowThreshold = 200; - double highThreshold = 300; - int totalRate = (int) (5*(0.95*highThreshold)); - int numContainerStopsPerCycle = 1; - int numItemStopsPerCycle = 1; - boolean verbose = true; - } - - // Testing conveniences. - - private double sum(Iterable<? extends Number> vals) { - double total = 0;; - for (Number val : vals) { - total += val.doubleValue(); - } - return total; - } - - /** - * Distributes a given load across a number of items randomly. The variability in load for an item is dictated by the variance, - * but the total will always equal totalLoad. - * - * The distribution of load is skewed: one side of the list will have bigger values than the other. - * Which side is skewed will vary, so when balancing a policy will find that things have entirely changed. - * - * TODO This is not particularly good at distributing load, but it's random and skewed enough to force rebalancing. - */ - private List<Integer> randomlyDivideLoad(int numItems, int totalLoad, int min, int max) { - List<Integer> result = new ArrayList<Integer>(numItems); - int totalRemaining = totalLoad; - int variance = 3; - int skew = 3; - - for (int i = 0; i < numItems; i++) { - int itemsRemaining = numItems-i; - int itemFairShare = (totalRemaining/itemsRemaining); - double skewFactor = ((double)i/numItems)*2 - 1; // a number between -1 and 1, depending how far through the item set we are - int itemSkew = (int) (random.nextInt(skew)*skewFactor); - int itemLoad = itemFairShare + (random.nextInt(variance*2)-variance) + itemSkew; - itemLoad = Math.max(min, itemLoad); - itemLoad = Math.min(totalRemaining, itemLoad); - itemLoad = Math.min(max, itemLoad); - result.add(itemLoad); - totalRemaining -= itemLoad; - } - - if (random.nextBoolean()) Collections.reverse(result); - - assertTrue(sum(result) <= totalLoad, "totalLoad="+totalLoad+"; result="+result); - - return result; - } -}
