Skip to content

Commit

Permalink
Fix FollowTheSunPolicyTest sensitivity
Browse files Browse the repository at this point in the history
- and some general code tidy, to remove warnings
  • Loading branch information
aledsage committed Mar 14, 2014
1 parent 0b66e66 commit e8ce8fa
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
import brooklyn.entity.basic.EntityLocal;
import brooklyn.entity.proxying.EntitySpec;
import brooklyn.event.AttributeSensor;
import brooklyn.event.basic.BasicAttributeSensor;
import brooklyn.event.basic.Sensors;
import brooklyn.location.Location;
import brooklyn.location.LocationSpec;
import brooklyn.location.basic.SimulatedLocation;
import brooklyn.management.ManagementContext;
import brooklyn.policy.loadbalancing.MockContainerEntity;
import brooklyn.policy.loadbalancing.MockItemEntity;
import brooklyn.policy.loadbalancing.MockItemEntityImpl;
Expand All @@ -36,6 +38,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.reflect.TypeToken;

public class AbstractFollowTheSunPolicyTest {

Expand All @@ -46,10 +49,11 @@ public class AbstractFollowTheSunPolicyTest {

protected static final long CONTAINER_STARTUP_DELAY_MS = 100;

public static final AttributeSensor<Map<Entity, Double>> TEST_METRIC =
new BasicAttributeSensor(Map.class, "test.metric", "Dummy workrate for test entities");
public static final AttributeSensor<Map<Entity, Double>> TEST_METRIC = Sensors.newSensor(
new TypeToken<Map<Entity, Double>>() {}, "test.metric", "Dummy workrate for test entities");

protected TestApplication app;
protected ManagementContext managementContext;
protected SimulatedLocation loc1;
protected SimulatedLocation loc2;
protected FollowTheSunPool pool;
Expand All @@ -60,16 +64,17 @@ public class AbstractFollowTheSunPolicyTest {
protected Random random = new Random();

@BeforeMethod(alwaysRun=true)
public void before() throws Exception {
LOG.debug("In AbstractFollowTheSunPolicyTest.before()");
public void setUp() throws Exception {
LOG.debug("In AbstractFollowTheSunPolicyTest.setUp()");

MockItemEntityImpl.totalMoveCount.set(0);

app = ApplicationBuilder.newManagedApp(TestApplication.class);
managementContext = app.getManagementContext();

loc1 = new SimulatedLocation(MutableMap.of("name", "loc1"));
loc2 = new SimulatedLocation(MutableMap.of("name", "loc2"));

loc1 = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class).configure("name", "loc1"));
loc2 = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class).configure("name", "loc2"));

app = ApplicationBuilder.newManagedApp(TestApplication.class);
containerGroup = app.createAndManageChild(EntitySpec.create(DynamicGroup.class)
.displayName("containerGroup")
.configure(DynamicGroup.ENTITY_FILTER, Predicates.instanceOf(MockContainerEntity.class)));
Expand All @@ -86,7 +91,7 @@ public void before() throws Exception {
}

@AfterMethod(alwaysRun=true)
public void after() {
public void tearDown() {
if (pool != null && policy != null) pool.removePolicy(policy);
if (app != null) Entities.destroyAll(app.getManagementContext());
MockItemEntityImpl.totalMoveCount.set(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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;
Expand Down Expand Up @@ -44,7 +45,7 @@ public void testPolicyUpdatesModel() {
((EntityLocal)item2).setAttribute(TEST_METRIC, ImmutableMap.<Entity,Double>of(item1, 11d));

Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
public void run() {
@Override public void run() {
assertEquals(ImmutableSet.of(item1, item2), model.getItems());
assertEquals(model.getItemContainer(item1), containerA);
assertEquals(model.getItemLocation(item1), loc1);
Expand All @@ -58,7 +59,7 @@ public void testPolicyAcceptsLocationFinder() {
pool.removePolicy(policy);

Function<Entity, Location> customLocationFinder = new Function<Entity, Location>() {
public Location apply(Entity input) {
@Override public Location apply(Entity input) {
return new SimulatedLocation(MutableMap.of("name", "custom location for "+input));
}};

Expand All @@ -73,7 +74,7 @@ public Location apply(Entity input) {
final MockContainerEntity containerA = newContainer(app, loc1, "A");

Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
public void run() {
@Override public void run() {
assertEquals(model.getContainerLocation(containerA).getDisplayName(), "custom location for "+containerA);
}});
}
Expand Down Expand Up @@ -208,11 +209,6 @@ public void testImmovableItemContributesTowardsLoad() {
assertItemDistributionEventually(ImmutableMap.of(containerA, ImmutableList.of(item1, item2), containerB, ImmutableList.<MockItemEntity>of()));
}

// FIXME Failed in jenkins once with event times [2647, 2655]
// My 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 happened with a 1000ms separation.
//
// Marked as "Acceptance" due to time-sensitive nature :-(
@Test(groups={"Integration", "Acceptance"}, invocationCount=20)
public void testRepeatedRespectsMinPeriodBetweenExecs() throws Exception {
Expand All @@ -221,8 +217,18 @@ public void testRepeatedRespectsMinPeriodBetweenExecs() throws Exception {

@Test(groups="Integration")
public void testRespectsMinPeriodBetweenExecs() throws Exception {
long minPeriodBetweenExecs = 1000;
long timePrecision = 250;
// 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);

Expand All @@ -241,10 +247,9 @@ public void testRespectsMinPeriodBetweenExecs() throws Exception {
pool.addPolicy(customPolicy);

// Record times that things are moved, by lisening to the container sensor being set
final Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
final Stopwatch stopwatch = Stopwatch.createStarted();

final List<Long> eventTimes = Lists.newArrayList();
final List<Long> eventTimes = Lists.newCopyOnWriteArrayList();
final Semaphore semaphore = new Semaphore(0);

app.subscribe(item1, Movable.CONTAINER, new SensorEventListener<Entity>() {
Expand All @@ -255,17 +260,28 @@ public void testRespectsMinPeriodBetweenExecs() throws Exception {
semaphore.release();
}});

// Set the workrate, causing the policy to move item1 to item2's location, and wait for it to happen
((EntityLocal)item1).setAttribute(TEST_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(TEST_METRIC, ImmutableMap.<Entity,Double>of(item3, 100d));
assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(item1.getAttribute(Movable.CONTAINER), containerA);

assertEquals(eventTimes.size(), 2);
assertTrue(eventTimes.get(1) - eventTimes.get(0) > (minPeriodBetweenExecs-timePrecision), ""+eventTimes);
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(TEST_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(TEST_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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import brooklyn.entity.Entity;
import brooklyn.entity.proxying.ImplementedBy;
import brooklyn.event.AttributeSensor;
import brooklyn.event.basic.BasicAttributeSensor;
import brooklyn.event.basic.Sensors;

@ImplementedBy(MockItemEntityImpl.class)
public interface MockItemEntity extends Entity, Movable {

public static final AttributeSensor<Integer> TEST_METRIC = new BasicAttributeSensor<Integer>(
Integer.class, "test.metric", "Dummy workrate for test entities");
public static final AttributeSensor<Integer> TEST_METRIC = Sensors.newIntegerSensor(
"test.metric", "Dummy workrate for test entities");

public boolean isStopped();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@
import brooklyn.entity.Entity;
import brooklyn.entity.basic.AbstractEntity;
import brooklyn.event.AttributeSensor;
import brooklyn.event.basic.BasicAttributeSensor;
import brooklyn.util.collections.MutableList;


public class MockItemEntityImpl extends AbstractEntity implements MockItemEntity {

private static final Logger LOG = LoggerFactory.getLogger(MockItemEntityImpl.class);

public static final AttributeSensor<Integer> TEST_METRIC = new BasicAttributeSensor<Integer>(
Integer.class, "test.metric", "Dummy workrate for test entities");

public static AtomicInteger totalMoveCount = new AtomicInteger(0);

private volatile boolean stopped;
Expand Down

0 comments on commit e8ce8fa

Please sign in to comment.