blob: e597358097b43a85db61f552f04dda11598f61e6 [file] [log] [blame]
package brooklyn.entity.group;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import brooklyn.entity.Entity;
import brooklyn.entity.basic.ApplicationBuilder;
import brooklyn.entity.basic.BasicGroup;
import brooklyn.entity.basic.Entities;
import brooklyn.entity.proxying.EntitySpec;
import brooklyn.entity.trait.Startable;
import brooklyn.event.Sensor;
import brooklyn.location.basic.SimulatedLocation;
import brooklyn.management.EntityManager;
import brooklyn.policy.PolicySpec;
import brooklyn.test.Asserts;
import brooklyn.test.entity.TestApplication;
import brooklyn.test.entity.TestEntity;
import brooklyn.util.collections.MutableMap;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
public class MembershipTrackingPolicyTest {
private static final long TIMEOUT_MS = 10*1000;
SimulatedLocation loc;
EntityManager entityManager;
TestApplication app;
private BasicGroup group;
private RecordingMembershipTrackingPolicy policy;
@BeforeMethod(alwaysRun=true)
public void setUp() {
loc = new SimulatedLocation();
app = ApplicationBuilder.newManagedApp(TestApplication.class);
entityManager = app.getManagementContext().getEntityManager();
group = app.createAndManageChild(EntitySpec.create(BasicGroup.class)
.configure("childrenAsMembers", true));
policy = new RecordingMembershipTrackingPolicy(MutableMap.of("group", group));
group.addPolicy(policy);
policy.setGroup(group);
app.start(ImmutableList.of(loc));
}
@AfterMethod(alwaysRun=true)
public void tearDown() {
if (app != null) Entities.destroyAll(app.getManagementContext());
}
private TestEntity createAndManageChildOf(Entity parent) {
EntityManager entityManager = app.getManagementContext().getEntityManager();
TestEntity result = entityManager.createEntity(EntitySpec.create(TestEntity.class).parent(parent));
Entities.manage(result);
return result;
}
@Test
public void testNotifiedOfMemberAddedAndRemoved() throws Exception {
TestEntity e1 = createAndManageChildOf(group);
assertRecordsEventually(Record.newAdded(e1));
e1.clearParent();
assertRecordsEventually(Record.newAdded(e1), Record.newRemoved(e1));
}
@Test
public void testNotifiedOfMemberChanged() throws Exception {
TestEntity e1 = createAndManageChildOf(group);
e1.setAttribute(Startable.SERVICE_UP, true);
assertRecordsEventually(Record.newAdded(e1), Record.newChanged(e1));
}
@Test
public void testNotNotifiedWhenPolicySuspended() throws Exception {
policy.suspend();
createAndManageChildOf(group);
assertRecordsContinually(new Record[0]);
}
@SuppressWarnings("unchecked")
@Test
public void testNotifiedOfEverythingWhenPolicyResumed() throws Exception {
TestEntity e1 = createAndManageChildOf(group);
assertRecordsEventually(Record.newAdded(e1));
policy.suspend();
TestEntity e2 = createAndManageChildOf(group);
assertRecordsContinually(Record.newAdded(e1));
policy.resume();
// Order of members set is non-deterministic, so could get [e1,e1,e2] or [e1,e2,e1]
assertRecordsEventually(policy, ImmutableList.of(Record.newAdded(e1), Record.newAdded(e1), Record.newAdded(e2)),
ImmutableList.of(Record.newAdded(e1), Record.newAdded(e2), Record.newAdded(e1)));
}
@Test
public void testNotifiedOfSubsequentChangesWhenPolicyResumed() throws Exception {
policy.suspend();
policy.resume();
TestEntity e1 = createAndManageChildOf(group);
assertRecordsEventually(Record.newAdded(e1));
}
@Test
public void testNotifiedOfExtraTrackedSensors() throws Exception {
TestEntity e1 = createAndManageChildOf(group);
RecordingMembershipTrackingPolicy policy2 = new RecordingMembershipTrackingPolicy(MutableMap.of("group", group, "sensorsToTrack", ImmutableSet.of(TestEntity.NAME)));
group.addPolicy(policy2);
policy2.setGroup(group);
e1.setAttribute(TestEntity.NAME, "myname");
assertRecordsEventually(policy2, Record.newAdded(e1), Record.newChanged(e1));
}
@Test
public void testNotNotifiedOfExtraTrackedSensorsIfNonDuplicate() throws Exception {
TestEntity e1 = createAndManageChildOf(group);
PolicySpec<RecordingMembershipTrackingPolicy> nonDuplicateTrackingPolicySpec =
PolicySpec.create(RecordingMembershipTrackingPolicy.class)
.configure(AbstractMembershipTrackingPolicy.SENSORS_TO_TRACK, ImmutableSet.<Sensor<?>>of(TestEntity.NAME))
.configure(AbstractMembershipTrackingPolicy.NOTIFY_ON_DUPLICATES, false);
RecordingMembershipTrackingPolicy nonDuplicateTrackingPolicy = group.addPolicy(nonDuplicateTrackingPolicySpec);
group.addPolicy(nonDuplicateTrackingPolicy);
nonDuplicateTrackingPolicy.setGroup(group);
e1.setAttribute(TestEntity.NAME, "myname");
assertRecordsEventually(nonDuplicateTrackingPolicy, Record.newAdded(e1), Record.newChanged(e1));
e1.setAttribute(TestEntity.NAME, "myname");
assertRecordsContinually(nonDuplicateTrackingPolicy, Record.newAdded(e1), Record.newChanged(e1));
e1.setAttribute(TestEntity.NAME, "mynewname");
assertRecordsEventually(nonDuplicateTrackingPolicy, Record.newAdded(e1), Record.newChanged(e1), Record.newChanged(e1));
}
private void assertRecordsEventually(final Record... expected) {
assertRecordsEventually(policy, expected);
}
@SuppressWarnings("unchecked")
private void assertRecordsEventually(final RecordingMembershipTrackingPolicy policy, final Record... expected) {
assertRecordsEventually(policy, ImmutableList.copyOf(expected));
}
private void assertRecordsEventually(final RecordingMembershipTrackingPolicy policy, final List<Record>... validExpecteds) {
Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
public void run() {
for (List<Record> validExpected : validExpecteds) {
if (policy.records.equals(validExpected)) return;
}
fail("actual="+policy.records+"; valid: "+validExpecteds);
}});
}
private void assertRecordsContinually(final Record... expected) {
assertRecordsContinually(policy, expected);
}
private void assertRecordsContinually(final RecordingMembershipTrackingPolicy policy, final Record... expected) {
Asserts.succeedsContinually(ImmutableMap.of("timeout", 100), new Runnable() {
public void run() {
assertEquals(policy.records, ImmutableList.copyOf(expected), "actual="+policy.records);
}});
}
// Needs to be public when instantiated from a spec (i.e. by InternalPolicyFactory)
public static class RecordingMembershipTrackingPolicy extends AbstractMembershipTrackingPolicy {
final List<Record> records = new CopyOnWriteArrayList<Record>();
public RecordingMembershipTrackingPolicy() {
super();
}
public RecordingMembershipTrackingPolicy(MutableMap<String, ?> flags) {
super(flags);
}
@Override protected void onEntityChange(Entity member) {
records.add(Record.newChanged(member));
}
@Override protected void onEntityAdded(Entity member) {
records.add(Record.newAdded(member));
}
@Override protected void onEntityRemoved(Entity member) {
records.add(Record.newRemoved(member));
}
}
static class Record {
final String action;
final Entity member;
static Record newChanged(Entity member) {
return new Record("change", member);
}
static Record newAdded(Entity member) {
return new Record("added", member);
}
static Record newRemoved(Entity member) {
return new Record("removed", member);
}
static Record newChangeRecord(Entity member) {
return new Record("change", member);
}
private Record(String action, Entity member) {
this.action = action;
this.member = member;
}
@Override
public String toString() {
return action+"("+member+")";
}
@Override
public int hashCode() {
return Objects.hashCode(action, member);
}
@Override
public boolean equals(Object other) {
return other instanceof Record && Objects.equal(action, ((Record)other).action) &&
Objects.equal(member, ((Record)other).member);
}
}
}