You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/08/19 23:20:51 UTC

[17/62] [abbrv] incubator-brooklyn git commit: rename core’s o.a.b.entity to o.a.b.core.entity

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntityConfigTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntityConfigTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntityConfigTest.java
new file mode 100644
index 0000000..2c58f66
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntityConfigTest.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.AbstractEntity;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+public class EntityConfigTest {
+
+    private ManagementContext managementContext;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        managementContext = LocalManagementContextForTests.newInstance();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() {
+        if (managementContext != null) Entities.destroyAll(managementContext);
+    }
+
+    @Test
+    public void testConfigBagContainsMatchesForConfigKeyName() throws Exception {
+        EntityInternal entity = managementContext.getEntityManager().createEntity(EntitySpec.create(MyEntity.class)
+                .configure("myentity.myconfig", "myval1")
+                .configure("myentity.myconfigwithflagname", "myval2"));
+        
+        assertEquals(entity.getAllConfig(), ImmutableMap.of(MyEntity.MY_CONFIG, "myval1", MyEntity.MY_CONFIG_WITH_FLAGNAME, "myval2"));
+        assertEquals(entity.getAllConfigBag().getAllConfig(), ImmutableMap.of("myentity.myconfig", "myval1", "myentity.myconfigwithflagname", "myval2"));
+        assertEquals(entity.getLocalConfigBag().getAllConfig(), ImmutableMap.of("myentity.myconfig", "myval1", "myentity.myconfigwithflagname", "myval2"));
+    }
+
+    @Test
+    public void testConfigBagContainsMatchesForFlagName() throws Exception {
+        // Prefers flag-name, over config-key's name
+        EntityInternal entity = managementContext.getEntityManager().createEntity(EntitySpec.create(MyEntity.class)
+                .configure("myconfigflagname", "myval"));
+        
+        assertEquals(entity.getAllConfig(), ImmutableMap.of(MyEntity.MY_CONFIG_WITH_FLAGNAME, "myval"));
+        assertEquals(entity.getAllConfigBag().getAllConfig(), ImmutableMap.of("myentity.myconfigwithflagname", "myval"));
+        assertEquals(entity.getLocalConfigBag().getAllConfig(), ImmutableMap.of("myentity.myconfigwithflagname", "myval"));
+    }
+
+    // TODO Which way round should it be?!
+    @Test(enabled=false)
+    public void testPrefersFlagNameOverConfigKeyName() throws Exception {
+        EntityInternal entity = managementContext.getEntityManager().createEntity(EntitySpec.create(MyEntity.class)
+                .configure("myconfigflagname", "myval")
+                .configure("myentity.myconfigwithflagname", "shouldIgnoreAndPreferFlagName"));
+        
+        assertEquals(entity.getAllConfig(), ImmutableMap.of(MyEntity.MY_CONFIG_WITH_FLAGNAME, "myval"));
+    }
+
+    @Test
+    public void testConfigBagContainsUnmatched() throws Exception {
+        EntityInternal entity = managementContext.getEntityManager().createEntity(EntitySpec.create(MyEntity.class)
+                .configure("notThere", "notThereVal"));
+        
+        assertEquals(entity.getAllConfig(), ImmutableMap.of(ConfigKeys.newConfigKey(Object.class, "notThere"), "notThereVal"));
+        assertEquals(entity.getAllConfigBag().getAllConfig(), ImmutableMap.of("notThere", "notThereVal"));
+        assertEquals(entity.getLocalConfigBag().getAllConfig(), ImmutableMap.of("notThere", "notThereVal"));
+    }
+    
+    @Test
+    public void testChildConfigBagInheritsUnmatchedAtParent() throws Exception {
+        EntityInternal entity = managementContext.getEntityManager().createEntity(EntitySpec.create(MyEntity.class)
+                .configure("mychildentity.myconfig", "myval1")
+                .configure("mychildconfigflagname", "myval2")
+                .configure("notThere", "notThereVal"));
+
+        EntityInternal child = managementContext.getEntityManager().createEntity(EntitySpec.create(MyChildEntity.class)
+                .parent(entity));
+
+        assertEquals(child.getAllConfig(), ImmutableMap.of(MyChildEntity.MY_CHILD_CONFIG, "myval1", 
+            ConfigKeys.newConfigKey(Object.class, "mychildconfigflagname"), "myval2",
+            ConfigKeys.newConfigKey(Object.class, "notThere"), "notThereVal"));
+        assertEquals(child.getAllConfigBag().getAllConfig(), ImmutableMap.of("mychildentity.myconfig", "myval1", "mychildconfigflagname", "myval2", "notThere", "notThereVal"));
+        assertEquals(child.getLocalConfigBag().getAllConfig(), ImmutableMap.of());
+    }
+    
+    @Test
+    public void testChildInheritsFromParent() throws Exception {
+        EntityInternal entity = managementContext.getEntityManager().createEntity(EntitySpec.create(MyEntity.class)
+                .configure("myentity.myconfig", "myval1"));
+
+        EntityInternal child = managementContext.getEntityManager().createEntity(EntitySpec.create(MyChildEntity.class)
+                .parent(entity));
+
+        assertEquals(child.getAllConfig(), ImmutableMap.of(MyEntity.MY_CONFIG, "myval1"));
+        assertEquals(child.getAllConfigBag().getAllConfig(), ImmutableMap.of("myentity.myconfig", "myval1"));
+        assertEquals(child.getLocalConfigBag().getAllConfig(), ImmutableMap.of());
+    }
+    
+    @Test
+    public void testChildCanOverrideConfigUsingKeyName() throws Exception {
+        EntityInternal entity = managementContext.getEntityManager().createEntity(EntitySpec.create(MyEntity.class)
+                .configure("mychildentity.myconfigwithflagname", "myval")
+                .configure("notThere", "notThereVal"));
+
+        EntityInternal child = managementContext.getEntityManager().createEntity(EntitySpec.create(MyChildEntity.class)
+                .parent(entity)
+                .configure("mychildentity.myconfigwithflagname", "overrideMyval")
+                .configure("notThere", "overrideNotThereVal"));
+
+        assertEquals(child.getAllConfig(), ImmutableMap.of(MyChildEntity.MY_CHILD_CONFIG_WITH_FLAGNAME, "overrideMyval",
+            ConfigKeys.newConfigKey(Object.class, "notThere"), "overrideNotThereVal"));
+        assertEquals(child.getAllConfigBag().getAllConfig(), ImmutableMap.of("mychildentity.myconfigwithflagname", "overrideMyval", "notThere", "overrideNotThereVal"));
+        assertEquals(child.getLocalConfigBag().getAllConfig(), ImmutableMap.of("mychildentity.myconfigwithflagname", "overrideMyval", "notThere", "overrideNotThereVal"));
+    }
+    
+    @Test
+    public void testChildCanOverrideConfigUsingFlagName() throws Exception {
+        EntityInternal entity = managementContext.getEntityManager().createEntity(EntitySpec.create(MyEntity.class)
+                .configure(MyChildEntity.MY_CHILD_CONFIG_WITH_FLAGNAME, "myval"));
+        assertEquals(entity.getAllConfig(), ImmutableMap.of(MyChildEntity.MY_CHILD_CONFIG_WITH_FLAGNAME, "myval"));
+
+        EntityInternal child = managementContext.getEntityManager().createEntity(EntitySpec.create(MyChildEntity.class)
+                .parent(entity)
+                .configure("mychildconfigflagname", "overrideMyval"));
+
+        assertEquals(child.getAllConfig(), ImmutableMap.of(MyChildEntity.MY_CHILD_CONFIG_WITH_FLAGNAME, "overrideMyval"));
+        assertEquals(child.getAllConfigBag().getAllConfig(), ImmutableMap.of("mychildentity.myconfigwithflagname", "overrideMyval"));
+        assertEquals(child.getLocalConfigBag().getAllConfig(), ImmutableMap.of("mychildentity.myconfigwithflagname", "overrideMyval"));
+    }
+    
+    public static class MyEntity extends AbstractEntity {
+        public static final ConfigKey<String> MY_CONFIG = ConfigKeys.newStringConfigKey("myentity.myconfig");
+
+        @SetFromFlag("myconfigflagname")
+        public static final ConfigKey<String> MY_CONFIG_WITH_FLAGNAME = ConfigKeys.newStringConfigKey("myentity.myconfigwithflagname");
+        
+        @Override
+        public void init() {
+            super.init();
+            
+            // Just calling this to prove we can! When config() was changed to return BasicConfigurationSupport,
+            // it broke because BasicConfigurationSupport was private.
+            config().getLocalBag();
+        }
+    }
+    
+    public static class MyChildEntity extends AbstractEntity {
+        public static final ConfigKey<String> MY_CHILD_CONFIG = ConfigKeys.newStringConfigKey("mychildentity.myconfig");
+
+        @SetFromFlag("mychildconfigflagname")
+        public static final ConfigKey<String> MY_CHILD_CONFIG_WITH_FLAGNAME = ConfigKeys.newStringConfigKey("mychildentity.myconfigwithflagname");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntityFunctionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntityFunctionsTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntityFunctionsTest.java
new file mode 100644
index 0000000..b120d98
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntityFunctionsTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.entity.EntityFunctions;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+
+public class EntityFunctionsTest extends BrooklynAppUnitTestSupport {
+
+    private TestEntity entity;
+    private Location loc;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).displayName("mydisplayname"));
+        loc = app.getManagementContext().getLocationRegistry().resolve("localhost");
+    }
+
+    @Test
+    public void testAttribute() throws Exception {
+        entity.setAttribute(TestEntity.NAME, "myname");
+        assertEquals(EntityFunctions.attribute(TestEntity.NAME).apply(entity), "myname");
+        assertNull(EntityFunctions.attribute(TestEntity.SEQUENCE).apply(entity));
+    }
+    
+    @Test
+    public void testConfig() throws Exception {
+        entity.setConfig(TestEntity.CONF_NAME, "myname");
+        assertEquals(EntityFunctions.config(TestEntity.CONF_NAME).apply(entity), "myname");
+        assertNull(EntityFunctions.config(TestEntity.CONF_OBJECT).apply(entity));
+    }
+    
+    @Test
+    public void testDisplayName() throws Exception {
+        assertEquals(EntityFunctions.displayName().apply(entity), "mydisplayname");
+    }
+    
+    @Test
+    public void testId() throws Exception {
+        assertEquals(EntityFunctions.id().apply(entity), entity.getId());
+    }
+    
+    @Test
+    public void testLocationMatching() throws Exception {
+        entity.addLocations(ImmutableList.of(loc));
+        assertEquals(EntityFunctions.locationMatching(Predicates.alwaysTrue()).apply(entity), loc);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntityLocationsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntityLocationsTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntityLocationsTest.java
new file mode 100644
index 0000000..3aa68b6
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntityLocationsTest.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.sensor.SensorEvent;
+import org.apache.brooklyn.core.entity.AbstractEntity;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.test.Asserts;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class EntityLocationsTest extends BrooklynAppUnitTestSupport {
+
+    @Test
+    public void testDuplicateLocationOnlyAddedOnce() {
+        Location l = app.newSimulatedLocation();
+        app.addLocations(Arrays.asList(l, l));
+        app.addLocations(Arrays.asList(l, l));
+        Assert.assertEquals(app.getLocations().size(), 1);
+    }
+    
+    @Test
+    public void testNotifiedOfAddAndRemoveLocations() throws Exception {
+        final Location l = app.newSimulatedLocation();
+        final Location l2 = app.newSimulatedLocation();
+        
+        final RecordingSensorEventListener<Object> addedEvents = new RecordingSensorEventListener<>();
+        final RecordingSensorEventListener<Object> removedEvents = new RecordingSensorEventListener<>();
+        app.subscribe(app, AbstractEntity.LOCATION_ADDED, addedEvents);
+        app.subscribe(app, AbstractEntity.LOCATION_REMOVED, removedEvents);
+
+        // Add first location
+        app.addLocations(ImmutableList.of(l));
+        
+        assertEventValuesEqualsEventually(addedEvents, ImmutableList.of(l));
+        assertEventValuesEquals(removedEvents, ImmutableList.of());
+
+        // Add second location
+        app.addLocations(ImmutableList.of(l2));
+
+        assertEventValuesEqualsEventually(addedEvents, ImmutableList.of(l, l2));
+        assertEventValuesEquals(removedEvents, ImmutableList.of());
+
+        // Remove first location
+        app.removeLocations(ImmutableList.of(l));
+        
+        assertEventValuesEqualsEventually(removedEvents, ImmutableList.of(l));
+        assertEventValuesEquals(addedEvents, ImmutableList.of(l, l2));
+
+        // Remove second location
+        app.removeLocations(ImmutableList.of(l2));
+        
+        assertEventValuesEqualsEventually(removedEvents, ImmutableList.of(l, l2));
+        assertEventValuesEquals(addedEvents, ImmutableList.of(l, l2));
+    }
+    
+    @Test(groups="Integration") // because takes a second
+    public void testNotNotifiedDuplicateAddedLocations() throws Exception {
+        final Location l = app.newSimulatedLocation();
+        
+        final RecordingSensorEventListener<Object> addedEvents = new RecordingSensorEventListener<>();
+        final RecordingSensorEventListener<Object> removedEvents = new RecordingSensorEventListener<>();
+        app.subscribe(app, AbstractEntity.LOCATION_ADDED, addedEvents);
+        app.subscribe(app, AbstractEntity.LOCATION_REMOVED, removedEvents);
+
+        // Add first location
+        app.addLocations(ImmutableList.of(l, l));
+        
+        assertEventValuesEqualsEventually(addedEvents, ImmutableList.of(l));
+        assertEventValuesEquals(removedEvents, ImmutableList.of());
+
+        // Add second location
+        app.addLocations(ImmutableList.of(l));
+
+        assertEventValuesEqualsContinually(addedEvents, ImmutableList.of(l));
+        assertEventValuesEquals(removedEvents, ImmutableList.of());
+    }
+    
+    private void assertEventValuesEqualsEventually(final RecordingSensorEventListener<Object> listener, final List<?> expectedVals) {
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEventValuesEquals(listener, expectedVals);
+            }});
+    }
+    
+    private void assertEventValuesEqualsContinually(final RecordingSensorEventListener<Object> listener, final List<?> expectedVals) {
+        Asserts.succeedsContinually(new Runnable() {
+            @Override public void run() {
+                assertEventValuesEquals(listener, expectedVals);
+            }});
+    }
+
+    private void assertEventValuesEquals(final RecordingSensorEventListener<Object> listener, final List<?> expectedVals) {
+        Iterable<SensorEvent<Object>> events = listener.getEvents();
+        assertEquals(Iterables.size(events), expectedVals.size(), "events=" + events);
+        for (int i = 0; i < expectedVals.size(); i++) {
+            Object expectedVal = expectedVals.get(i);
+            assertEquals(Iterables.get(events, i).getValue(), expectedVal, "events=" + events);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntityPreManagementTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntityPreManagementTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntityPreManagementTest.java
new file mode 100644
index 0000000..bc7ed14
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntityPreManagementTest.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.EntityManager;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.sensor.SensorEvent;
+import org.apache.brooklyn.api.sensor.SensorEventListener;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.policy.core.AbstractPolicy;
+import org.apache.brooklyn.test.TestUtils;
+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;
+
+
+@SuppressWarnings({"rawtypes","unchecked"})
+public class EntityPreManagementTest {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(EntityPreManagementTest.class);
+
+    private ManagementContext managementContext;
+    private EntityManager entityManager;
+    private TestApplication app;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        managementContext = new LocalManagementContextForTests();
+        entityManager = managementContext.getEntityManager();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (managementContext != null) Entities.destroyAll(managementContext);
+    }
+    
+    @Test
+    public void testSetSensorBeforeManaged() {
+        TestEntity e = entityManager.createEntity(EntitySpec.create(TestEntity.class));
+
+        e.setAttribute(Attributes.HOSTNAME, "martian.martian");
+        Assert.assertEquals(e.getAttribute(Attributes.HOSTNAME), "martian.martian");
+        
+        Assert.assertFalse(e.getManagementSupport().isManagementContextReal());
+    }
+    
+    @Test
+    public void testAddPolicyToEntityBeforeManaged() {
+        TestEntity e = entityManager.createEntity(EntitySpec.create(TestEntity.class));
+        final List events = new ArrayList();
+        
+        e.addPolicy(new AbstractPolicy() {
+            @Override
+            public void setEntity(EntityLocal entity) {
+                super.setEntity(entity);
+                subscribe(entity, Attributes.HOSTNAME, new SensorEventListener() {
+                    @Override
+                    public void onEvent(SensorEvent event) {
+                        events.add(event);
+                    }
+                });
+            }
+        });
+        
+        e.setAttribute(Attributes.HOSTNAME, "martian.martian");
+        Assert.assertEquals(e.getAttribute(Attributes.HOSTNAME), "martian.martian");
+        
+        if (!events.isEmpty()) Assert.fail("Shouldn't have events yet: "+events);
+        Assert.assertFalse(e.getManagementSupport().isManagementContextReal());
+        
+        TestApplication app = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext);
+        e.setParent(app);
+        Entities.manage(e);
+        
+        TestUtils.assertEventually(new Runnable() {
+            @Override
+            public void run() {
+                if (events.isEmpty()) Assert.fail("no events received");
+            }});
+        Assert.assertEquals(events.size(), 1, "Expected 1 event; got: "+events);
+    }
+
+    @Test
+    public void testAddPolicyToApplicationBeforeManaged() {
+        app = entityManager.createEntity(EntitySpec.create(TestApplication.class));
+        final List events = new ArrayList();
+        
+        app.addPolicy(new AbstractPolicy() {
+            @Override
+            public void setEntity(EntityLocal entity) {
+                super.setEntity(entity);
+                subscribe(entity, Attributes.HOSTNAME, new SensorEventListener() {
+                    @Override
+                    public void onEvent(SensorEvent event) {
+                        events.add(event);
+                    }
+                });
+            }
+        });
+        
+        app.setAttribute(Attributes.HOSTNAME, "martian.martian");
+        Assert.assertEquals(app.getAttribute(Attributes.HOSTNAME), "martian.martian");
+        
+        if (!events.isEmpty()) Assert.fail("Shouldn't have events yet: "+events);
+        
+        Entities.startManagement(app, managementContext);
+        
+        TestUtils.assertEventually(new Runnable() {
+            @Override
+            public void run() {
+                if (events.isEmpty()) Assert.fail("no events received");
+            }});
+        Assert.assertEquals(events.size(), 1, "Expected 1 event; got: "+events);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntityPredicatesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntityPredicatesTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntityPredicatesTest.java
new file mode 100644
index 0000000..dbd2972
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntityPredicatesTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.core.entity.trait.Changeable;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.entity.group.BasicGroup;
+import org.apache.brooklyn.util.text.StringPredicates;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class EntityPredicatesTest extends BrooklynAppUnitTestSupport {
+
+    private TestEntity entity;
+    private BasicGroup group;
+    private Location loc;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).displayName("mydisplayname"));
+        group = app.createAndManageChild(EntitySpec.create(BasicGroup.class));
+        loc = app.getManagementContext().getLocationRegistry().resolve("localhost");
+    }
+
+    @Test
+    public void testApplicationIdEqualTo() throws Exception {
+        assertTrue(EntityPredicates.applicationIdEqualTo(app.getId()).apply(entity));
+        assertFalse(EntityPredicates.applicationIdEqualTo("wrongid").apply(entity));
+    }
+    
+    @Test
+    public void testIdEqualTo() throws Exception {
+        assertTrue(EntityPredicates.idEqualTo(entity.getId()).apply(entity));
+        assertFalse(EntityPredicates.idEqualTo("wrongid").apply(entity));
+    }
+    
+    @Test
+    public void testAttributeEqualTo() throws Exception {
+        entity.setAttribute(TestEntity.NAME, "myname");
+        assertTrue(EntityPredicates.attributeEqualTo(TestEntity.NAME, "myname").apply(entity));
+        assertFalse(EntityPredicates.attributeEqualTo(TestEntity.NAME, "wrongname").apply(entity));
+    }
+    
+    @Test
+    public void testConfigEqualTo() throws Exception {
+        entity.setConfig(TestEntity.CONF_NAME, "myname");
+        assertTrue(EntityPredicates.configEqualTo(TestEntity.CONF_NAME, "myname").apply(entity));
+        assertFalse(EntityPredicates.configEqualTo(TestEntity.CONF_NAME, "wrongname").apply(entity));
+    }
+    
+    @Test
+    public void testDisplayNameEqualTo() throws Exception {
+        assertTrue(EntityPredicates.displayNameEqualTo("mydisplayname").apply(entity));
+        assertFalse(EntityPredicates.displayNameEqualTo("wrongname").apply(entity));
+    }
+    
+    @Test
+    public void testDisplayNameSatisfies() throws Exception {
+        assertTrue(EntityPredicates.displayNameSatisfies(StringPredicates.matchesRegex("myd.*me")).apply(entity));
+        assertFalse(EntityPredicates.applicationIdEqualTo("wrongname").apply(entity));
+    }
+    
+    @Test
+    public void testIsChildOf() throws Exception {
+        assertTrue(EntityPredicates.isChildOf(app).apply(entity));
+        assertFalse(EntityPredicates.isChildOf(entity).apply(entity));
+        assertFalse(EntityPredicates.isChildOf(entity).apply(app));
+    }
+    
+    @Test
+    public void testIsMemberOf() throws Exception {
+        group.addMember(entity);
+        assertTrue(EntityPredicates.isMemberOf(group).apply(entity));
+        assertFalse(EntityPredicates.isMemberOf(group).apply(app));
+        assertFalse(EntityPredicates.isMemberOf(group).apply(group));
+    }
+    
+    @Test
+    public void testManaged() throws Exception {
+        assertTrue(EntityPredicates.isManaged().apply(entity));
+        Entities.unmanage(entity);
+        assertFalse(EntityPredicates.isManaged().apply(entity));
+    }
+    
+    @Test
+    public void testWithLocation() throws Exception {
+        entity.addLocations(ImmutableList.of(loc));
+        assertTrue(EntityPredicates.locationsIncludes(loc).apply(entity));
+        assertFalse(EntityPredicates.locationsIncludes(loc).apply(app));
+    }
+
+    @Test
+    public void testHasInterfaceMatching() throws Exception {
+        assertTrue(EntityPredicates.hasInterfaceMatching(".*").apply(entity));
+        assertTrue(EntityPredicates.hasInterfaceMatching(".*TestEntity").apply(entity));
+        assertFalse(EntityPredicates.hasInterfaceMatching(".*TestEntity").apply(group));
+        assertTrue(EntityPredicates.hasInterfaceMatching(Changeable.class.getName()).apply(group));
+        assertTrue(EntityPredicates.hasInterfaceMatching(".*C.*able").apply(group));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntityRegistrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntityRegistrationTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntityRegistrationTest.java
new file mode 100644
index 0000000..018b82c
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntityRegistrationTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.sensor.SensorEvent;
+import org.apache.brooklyn.api.sensor.SensorEventListener;
+import org.apache.brooklyn.core.entity.AbstractEntity;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+public class EntityRegistrationTest extends BrooklynAppUnitTestSupport {
+
+    private static final int TIMEOUT_MS = 10*1000;
+    
+    private TestEntity entity;
+    private TestEntity entity2;
+
+    private List<Entity> added;
+    private List<Entity> removed;
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        
+        added = Lists.newCopyOnWriteArrayList();
+        removed = Lists.newCopyOnWriteArrayList();
+        
+        app.subscribe(app, AbstractEntity.CHILD_ADDED, new SensorEventListener<Entity>() {
+            @Override public void onEvent(SensorEvent<Entity> event) {
+                added.add(event.getValue());
+            }});
+        app.subscribe(app, AbstractEntity.CHILD_REMOVED, new SensorEventListener<Entity>() {
+                @Override public void onEvent(SensorEvent<Entity> event) {
+                    removed.add(event.getValue());
+                }});
+    }
+    
+    @Test
+    public void testAddAndRemoveChildrenEmitsEvent() {
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        assertCollectionEquals(app.getChildren(), ImmutableList.of(entity));
+        assertEqualsEventually(added, ImmutableList.of(entity));
+        
+        entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        assertCollectionEquals(app.getChildren(), ImmutableList.of(entity, entity2));
+        assertEqualsEventually(added, ImmutableList.of(entity, entity2));
+        
+        entity.removeChild(entity);
+        assertCollectionEquals(app.getChildren(), ImmutableList.of(entity2));
+        assertEqualsEventually(removed, ImmutableList.of(entity));
+        
+        Entities.unmanage(entity2);
+        assertCollectionEquals(app.getChildren(), ImmutableList.of());
+        assertEqualsEventually(removed, ImmutableList.of(entity, entity2));
+    }
+    
+    private <T> void assertEqualsEventually(final T actual, final T expected) {
+        Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+                @Override public void run() {
+                    assertEquals(actual, expected, "actual="+actual);
+                }});
+    }
+    
+    // Ignores order of vals in collection, but asserts each same size and same elements 
+    private <T> void assertCollectionEquals(Collection<?> actual, Collection<?> expected) {
+        assertEquals(ImmutableSet.copyOf(actual), ImmutableSet.copyOf(expected), "actual="+actual);
+        assertEquals(actual.size(), expected.size(), "actual="+actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntitySetFromFlagTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntitySetFromFlagTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntitySetFromFlagTest.java
new file mode 100644
index 0000000..d60c290
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntitySetFromFlagTest.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.PortRange;
+import org.apache.brooklyn.core.entity.AbstractEntity;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.core.PortRanges;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+
+public class EntitySetFromFlagTest {
+
+    @Test
+    public void testSetFromFlagUsingFieldName() {
+        MyEntity entity = new MyEntity(MutableMap.of("str1", "myval"));
+        assertEquals(entity.str1, "myval");
+    }
+    
+    @Test
+    public void testSetFromFlagUsingOverridenName() {
+        MyEntity entity = new MyEntity(MutableMap.of("altStr2", "myval"));
+        assertEquals(entity.str2, "myval");
+    }
+    
+    @Test
+    public void testSetFromFlagWhenNoDefaultIsNull() {
+        MyEntity entity = new MyEntity();
+        assertEquals(entity.str1, null);
+    }
+    
+    @Test
+    public void testSetFromFlagUsesDefault() {
+        MyEntity entity = new MyEntity();
+        assertEquals(entity.str3, "default str3");
+    }
+    
+    @Test
+    public void testSetFromFlagOverridingDefault() {
+        MyEntity entity = new MyEntity(MutableMap.of("str3", "overridden str3"));
+        assertEquals(entity.str3, "overridden str3");
+    }
+
+    @Test
+    public void testSetFromFlagCastsPrimitives() {
+        MyEntity entity = new MyEntity(MutableMap.of("double1", 1f));
+        assertEquals(entity.double1, 1d);
+    }
+
+    @Test
+    public void testSetFromFlagCastsDefault() {
+        MyEntity entity = new MyEntity();
+        assertEquals(entity.byte1, (byte)1);
+        assertEquals(entity.short1, (short)2);
+        assertEquals(entity.int1, 3);
+        assertEquals(entity.long1, 4l);
+        assertEquals(entity.float1, 5f);
+        assertEquals(entity.double1, 6d);
+         assertEquals(entity.char1, 'a');
+        assertEquals(entity.bool1, true);
+        
+        assertEquals(entity.byte2, Byte.valueOf((byte)1));
+        assertEquals(entity.short2, Short.valueOf((short)2));
+        assertEquals(entity.int2, Integer.valueOf(3));
+        assertEquals(entity.long2, Long.valueOf(4l));
+        assertEquals(entity.float2, Float.valueOf(5f));
+        assertEquals(entity.double2, Double.valueOf(6d));
+        assertEquals(entity.char2, Character.valueOf('a'));
+        assertEquals(entity.bool2, Boolean.TRUE);
+    }
+    
+    @Test
+    public void testSetFromFlagCoercesDefaultToPortRange() {
+        MyEntity entity = new MyEntity();
+        assertEquals(entity.portRange1, PortRanges.fromInteger(1234));
+    }
+    
+    @Test
+    public void testSetFromFlagCoercesStringValueToPortRange() {
+        MyEntity entity = new MyEntity(MutableMap.of("portRange1", "1-3"));
+        assertEquals(entity.portRange1, new PortRanges.LinearPortRange(1, 3));
+    }
+    
+    @Test
+    public void testSetFromFlagCoercesStringValueToInt() {
+        MyEntity entity = new MyEntity(MutableMap.of("int1", "123"));
+        assertEquals(entity.int1, 123);
+    }
+
+    @Test
+    public void testSetIconUrl() {
+        MyEntity entity = new MyEntity(MutableMap.of("iconUrl", "/img/myicon.gif"));
+        assertEquals(entity.getIconUrl(), "/img/myicon.gif");
+    }
+
+    @Test(expectedExceptions=IllegalArgumentException.class)
+    public void testFailsFastOnInvalidCoercion() {;
+        new MyEntity(MutableMap.of("int1", "thisisnotanint"));
+    }
+    
+    // Fails because configure being called from inside constructor; so field is set after configure called
+    @Test(enabled=false) 
+    public void testSetFromFlagWithFieldThatIsExplicitySet() {
+        MyEntity entity = new MyEntity(MutableMap.of("str4", "myval"));
+        assertEquals(entity.str4, "myval");
+        
+        MyEntity entity2 = new MyEntity();
+        assertEquals(entity2.str4, "explicit str4");
+    }
+    
+    private static class MyEntity extends AbstractEntity {
+
+        @SetFromFlag(defaultVal="1234")
+        PortRange portRange1;
+
+        @SetFromFlag
+        String str1;
+        
+        @SetFromFlag("altStr2")
+        String str2;
+        
+        @SetFromFlag(defaultVal="default str3")
+        String str3;
+
+        @SetFromFlag
+        String str4 = "explicit str4";
+        
+        @SetFromFlag(defaultVal="1")
+        byte byte1;
+
+        @SetFromFlag(defaultVal="2")
+        short short1;
+
+        @SetFromFlag(defaultVal="3")
+        int int1;
+
+        @SetFromFlag(defaultVal="4")
+        long long1;
+
+        @SetFromFlag(defaultVal="5")
+        float float1;
+
+        @SetFromFlag(defaultVal="6")
+        double double1;
+
+        @SetFromFlag(defaultVal="a")
+        char char1;
+
+        @SetFromFlag(defaultVal="true")
+        boolean bool1;
+
+        @SetFromFlag(defaultVal="1")
+        Byte byte2;
+
+        @SetFromFlag(defaultVal="2")
+        Short short2;
+
+        @SetFromFlag(defaultVal="3")
+        Integer int2;
+
+        @SetFromFlag(defaultVal="4")
+        Long long2;
+
+        @SetFromFlag(defaultVal="5")
+        Float float2;
+
+        @SetFromFlag(defaultVal="6")
+        Double double2;
+
+        @SetFromFlag(defaultVal="a")
+        Character char2;
+
+        @SetFromFlag(defaultVal="true")
+        Boolean bool2;
+
+        MyEntity() {
+            super(MutableMap.of(), null);
+        }
+        
+        MyEntity(Map flags) {
+            super(flags, null);
+        }
+        
+        MyEntity(Entity parent) {
+            super(MutableMap.of(), parent);
+        }
+        
+        MyEntity(Map flags, Entity parent) {
+            super(flags, parent);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntitySpecTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntitySpecTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntitySpecTest.java
new file mode 100644
index 0000000..af9eab0
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntitySpecTest.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.api.sensor.Enricher;
+import org.apache.brooklyn.api.sensor.EnricherSpec;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.core.test.entity.TestEntityImpl;
+import org.apache.brooklyn.core.test.entity.TestEntityNoEnrichersImpl;
+import org.apache.brooklyn.entity.group.BasicGroup;
+import org.apache.brooklyn.policy.core.AbstractPolicy;
+import org.apache.brooklyn.sensor.enricher.AbstractEnricher;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.core.SimulatedLocation;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class EntitySpecTest extends BrooklynAppUnitTestSupport {
+
+    private SimulatedLocation loc;
+    private TestEntity entity;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        loc = new SimulatedLocation();
+    }
+    
+    @Test
+    public void testSetsConfig() throws Exception {
+        // TODO Test other permutations
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .configure(TestEntity.CONF_NAME, "myname"));
+        assertEquals(entity.getConfig(TestEntity.CONF_NAME), "myname");
+    }
+
+    @Test
+    public void testAddsChildren() throws Exception {
+        entity = app.createAndManageChild( EntitySpec.create(TestEntity.class)
+            .displayName("child")
+            .child(EntitySpec.create(TestEntity.class)
+                .displayName("grandchild")) );
+        
+        Entity child = Iterables.getOnlyElement(app.getChildren());
+        assertEquals(child, entity);
+        assertEquals(child.getDisplayName(), "child");
+        Entity grandchild = Iterables.getOnlyElement(child.getChildren());
+        assertEquals(grandchild.getDisplayName(), "grandchild");
+    }
+    
+
+    @Test
+    public void testAddsPolicySpec() throws Exception {
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .policy(PolicySpec.create(MyPolicy.class)
+                        .displayName("mypolicyname")
+                        .configure(MyPolicy.CONF1, "myconf1val")
+                        .configure("myfield", "myfieldval")));
+        
+        Policy policy = Iterables.getOnlyElement(entity.getPolicies());
+        assertTrue(policy instanceof MyPolicy, "policy="+policy);
+        assertEquals(policy.getDisplayName(), "mypolicyname");
+        assertEquals(policy.getConfig(MyPolicy.CONF1), "myconf1val");
+    }
+    
+    @Test
+    public void testAddsPolicy() throws Exception {
+        MyPolicy policy = new MyPolicy();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .policy(policy));
+        
+        assertEquals(Iterables.getOnlyElement(entity.getPolicies()), policy);
+    }
+    
+    @Test
+    public void testAddsEnricherSpec() throws Exception {
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class, TestEntityNoEnrichersImpl.class)
+                .enricher(EnricherSpec.create(MyEnricher.class)
+                        .displayName("myenrichername")
+                        .configure(MyEnricher.CONF1, "myconf1val")
+                        .configure("myfield", "myfieldval")));
+        
+        Enricher enricher = Iterables.getOnlyElement(entity.getEnrichers());
+        assertTrue(enricher instanceof MyEnricher, "enricher="+enricher);
+        assertEquals(enricher.getDisplayName(), "myenrichername");
+        assertEquals(enricher.getConfig(MyEnricher.CONF1), "myconf1val");
+    }
+    
+    @Test
+    public void testAddsEnricher() throws Exception {
+        MyEnricher enricher = new MyEnricher();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class, TestEntityNoEnrichersImpl.class)
+                .enricher(enricher));
+        
+        assertEquals(Iterables.getOnlyElement(entity.getEnrichers()), enricher);
+    }
+    
+    @Test
+    public void testAddsMembers() throws Exception {
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        BasicGroup group = app.createAndManageChild(EntitySpec.create(BasicGroup.class)
+                .member(entity));
+        
+        Asserts.assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(entity));
+        Asserts.assertEqualsIgnoringOrder(entity.getGroups(), ImmutableSet.of(group));
+    }
+    
+    @Test
+    public void testAddsGroups() throws Exception {
+        BasicGroup group = app.createAndManageChild(EntitySpec.create(BasicGroup.class));
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .group(group));
+        
+        Asserts.assertEqualsIgnoringOrder(group.getMembers(), ImmutableSet.of(entity));
+        Asserts.assertEqualsIgnoringOrder(entity.getGroups(), ImmutableSet.of(group));
+    }
+    
+    @Test
+    public void testCallsConfigureAfterConstruction() throws Exception {
+        AbstractEntityLegacyTest.MyEntity entity = app.createAndManageChild(EntitySpec.create(AbstractEntityLegacyTest.MyEntity.class));
+        
+        assertEquals(entity.getConfigureCount(), 1);
+        assertEquals(entity.getConfigureDuringConstructionCount(), 0);
+    }
+    
+    @Test
+    public void testDisplayNameUsesDefault() throws Exception {
+        TestEntity entity = app.addChild(EntitySpec.create(TestEntity.class));
+        
+        assertTrue(entity.getDisplayName().startsWith("TestEntity:"+entity.getId().substring(0,4)), "displayName="+entity.getDisplayName());
+    }
+    
+    @Test
+    public void testDisplayNameUsesCustom() throws Exception {
+        TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).displayName("entityname"));
+        
+        assertEquals(entity.getDisplayName(), "entityname");
+    }
+
+    @Test
+    public void testDisplayNameUsesOverriddenDefault() throws Exception {
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .impl(TestEntityWithDefaultNameImpl.class)
+                .configure(TestEntityWithDefaultNameImpl.DEFAULT_NAME, "myOverriddenDefaultName"));
+        assertEquals(entity.getDisplayName(), "myOverriddenDefaultName");
+    }        
+
+    @Test
+    public void testDisplayNameUsesCustomWhenOverriddenDefault() throws Exception {
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .impl(TestEntityWithDefaultNameImpl.class)
+                .configure(TestEntityWithDefaultNameImpl.DEFAULT_NAME, "myOverriddenDefaultName")
+                .displayName("myEntityName"));
+        assertEquals(entity.getDisplayName(), "myEntityName");
+    }        
+
+    public static class TestEntityWithDefaultNameImpl extends TestEntityImpl {
+        public static final ConfigKey<String> DEFAULT_NAME = ConfigKeys.newStringConfigKey("defaultName");
+        
+        @Override
+        public void init() {
+            super.init();
+            if (getConfig(DEFAULT_NAME) != null) setDefaultDisplayName(getConfig(DEFAULT_NAME));
+        }
+    }
+    
+    public static class MyPolicy extends AbstractPolicy {
+        public static final BasicConfigKey<String> CONF1 = new BasicConfigKey<String>(String.class, "testpolicy.conf1", "my descr, conf1", "defaultval1");
+        public static final BasicConfigKey<Integer> CONF2 = new BasicConfigKey<Integer>(Integer.class, "testpolicy.conf2", "my descr, conf2", 2);
+        
+        @SetFromFlag
+        public String myfield;
+    }
+    
+    public static class MyEnricher extends AbstractEnricher {
+        public static final BasicConfigKey<String> CONF1 = new BasicConfigKey<String>(String.class, "testenricher.conf1", "my descr, conf1", "defaultval1");
+        public static final BasicConfigKey<Integer> CONF2 = new BasicConfigKey<Integer>(Integer.class, "testenricher.conf2", "my descr, conf2", 2);
+        
+        @SetFromFlag
+        public String myfield;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntitySubscriptionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntitySubscriptionTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntitySubscriptionTest.java
new file mode 100644
index 0000000..0abb2fc
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntitySubscriptionTest.java
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.SubscriptionHandle;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.entity.group.BasicGroup;
+import org.apache.brooklyn.sensor.core.BasicSensorEvent;
+import org.apache.brooklyn.test.Asserts;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.core.SimulatedLocation;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class EntitySubscriptionTest {
+
+    // TODO Duplication between this and PolicySubscriptionTest
+    
+    private SimulatedLocation loc;
+    private TestApplication app;
+    private TestEntity entity;
+    private TestEntity observedEntity;
+    private BasicGroup observedGroup;
+    private TestEntity observedChildEntity;
+    private TestEntity observedMemberEntity;
+    private TestEntity otherEntity;
+    private RecordingSensorEventListener<Object> listener;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        loc = app.newSimulatedLocation();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        observedEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        observedChildEntity = observedEntity.createAndManageChild(EntitySpec.create(TestEntity.class));
+
+        observedGroup = app.createAndManageChild(EntitySpec.create(BasicGroup.class));
+        observedMemberEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        observedGroup.addMember(observedMemberEntity);
+        
+        otherEntity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        
+        listener = new RecordingSensorEventListener<>();
+        
+        app.start(ImmutableList.of(loc));
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+    }
+    
+    @Test
+    public void testSubscriptionReceivesEvents() {
+        entity.subscribe(observedEntity, TestEntity.SEQUENCE, listener);
+        entity.subscribe(observedEntity, TestEntity.NAME, listener);
+        entity.subscribe(observedEntity, TestEntity.MY_NOTIF, listener);
+        
+        otherEntity.setAttribute(TestEntity.SEQUENCE, 123);
+        observedEntity.setAttribute(TestEntity.SEQUENCE, 123);
+        observedEntity.setAttribute(TestEntity.NAME, "myname");
+        observedEntity.emit(TestEntity.MY_NOTIF, 456);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, observedEntity, 123),
+                        new BasicSensorEvent<String>(TestEntity.NAME, observedEntity, "myname"),
+                        new BasicSensorEvent<Integer>(TestEntity.MY_NOTIF, observedEntity, 456)));
+            }});
+    }
+    
+    @Test
+    public void testSubscriptionToAllReceivesEvents() {
+        entity.subscribe(null, TestEntity.SEQUENCE, listener);
+        
+        observedEntity.setAttribute(TestEntity.SEQUENCE, 123);
+        otherEntity.setAttribute(TestEntity.SEQUENCE, 456);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, observedEntity, 123),
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, otherEntity, 456)));
+            }});
+    }
+    
+    @Test
+    public void testSubscribeToChildrenReceivesEvents() {
+        entity.subscribeToChildren(observedEntity, TestEntity.SEQUENCE, listener);
+        
+        observedChildEntity.setAttribute(TestEntity.SEQUENCE, 123);
+        observedEntity.setAttribute(TestEntity.SEQUENCE, 456);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, observedChildEntity, 123)));
+            }});
+    }
+    
+    @Test
+    public void testSubscribeToChildrenReceivesEventsForDynamicallyAddedChildren() {
+        entity.subscribeToChildren(observedEntity, TestEntity.SEQUENCE, listener);
+        
+        final TestEntity observedChildEntity2 = observedEntity.createAndManageChild(EntitySpec.create(TestEntity.class));
+        observedChildEntity2.setAttribute(TestEntity.SEQUENCE, 123);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, observedChildEntity2, 123)));
+            }});
+    }
+    
+    @Test
+    public void testSubscribeToMembersReceivesEvents() {
+        entity.subscribeToMembers(observedGroup, TestEntity.SEQUENCE, listener);
+        
+        observedMemberEntity.setAttribute(TestEntity.SEQUENCE, 123);
+        ((EntityLocal)observedGroup).setAttribute(TestEntity.SEQUENCE, 456);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, observedMemberEntity, 123)));
+            }});
+    }
+    
+    @Test
+    public void testSubscribeToMembersReceivesEventsForDynamicallyAddedMembers() {
+        entity.subscribeToMembers(observedGroup, TestEntity.SEQUENCE, listener);
+        
+        final TestEntity observedMemberEntity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        observedGroup.addMember(observedMemberEntity2);
+        observedMemberEntity2.setAttribute(TestEntity.SEQUENCE, 123);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, observedMemberEntity2, 123)));
+            }});
+    }
+    
+    @Test(groups="Integration")
+    public void testSubscribeToMembersIgnoresEventsForDynamicallyRemovedMembers() {
+        entity.subscribeToMembers(observedGroup, TestEntity.SEQUENCE, listener);
+        
+        observedGroup.removeMember(observedMemberEntity);
+        
+        observedMemberEntity.setAttribute(TestEntity.SEQUENCE, 123);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of());
+            }});
+    }
+    
+    @Test
+    public void testUnsubscribeRemovesAllSubscriptionsForThatEntity() {
+        entity.subscribe(observedEntity, TestEntity.SEQUENCE, listener);
+        entity.subscribe(observedEntity, TestEntity.NAME, listener);
+        entity.subscribe(observedEntity, TestEntity.MY_NOTIF, listener);
+        entity.subscribe(otherEntity, TestEntity.SEQUENCE, listener);
+        entity.unsubscribe(observedEntity);
+        
+        observedEntity.setAttribute(TestEntity.SEQUENCE, 123);
+        observedEntity.setAttribute(TestEntity.NAME, "myname");
+        observedEntity.emit(TestEntity.MY_NOTIF, 123);
+        otherEntity.setAttribute(TestEntity.SEQUENCE, 456);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, otherEntity, 456)));
+            }});
+    }
+    
+    @Test
+    public void testUnsubscribeUsingHandleStopsEvents() {
+        SubscriptionHandle handle1 = entity.subscribe(observedEntity, TestEntity.SEQUENCE, listener);
+        SubscriptionHandle handle2 = entity.subscribe(observedEntity, TestEntity.NAME, listener);
+        SubscriptionHandle handle3 = entity.subscribe(otherEntity, TestEntity.SEQUENCE, listener);
+        
+        entity.unsubscribe(observedEntity, handle2);
+        
+        observedEntity.setAttribute(TestEntity.SEQUENCE, 123);
+        observedEntity.setAttribute(TestEntity.NAME, "myname");
+        otherEntity.setAttribute(TestEntity.SEQUENCE, 456);
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(listener.getEvents(), ImmutableList.of(
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, observedEntity, 123),
+                        new BasicSensorEvent<Integer>(TestEntity.SEQUENCE, otherEntity, 456)));
+            }});
+    }
+    
+    @Test
+    public void testSubscriptionReceivesEventsInOrder() {
+        final int NUM_EVENTS = 100;
+        entity.subscribe(observedEntity, TestEntity.MY_NOTIF, listener);
+
+        for (int i = 0; i < NUM_EVENTS; i++) {
+            observedEntity.emit(TestEntity.MY_NOTIF, i);
+        }
+        
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                assertEquals(Iterables.size(listener.getEvents()), NUM_EVENTS);
+                for (int i = 0; i < NUM_EVENTS; i++) {
+                    assertEquals(Iterables.get(listener.getEvents(), i).getValue(), i);
+                }
+            }});
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntitySuppliersTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntitySuppliersTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntitySuppliersTest.java
new file mode 100644
index 0000000..1ed5b26
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntitySuppliersTest.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.entity.EntitySuppliers;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class EntitySuppliersTest extends BrooklynAppUnitTestSupport {
+
+    private TestEntity entity;
+    private Location loc;
+    private SshMachineLocation machine;
+    
+    @BeforeMethod(alwaysRun=true)
+    @SuppressWarnings("unchecked")
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).displayName("mydisplayname"));
+        loc = app.getManagementContext().getLocationRegistry().resolve("localhost");
+        machine = ((MachineProvisioningLocation<SshMachineLocation>)loc).obtain(ImmutableMap.of());
+    }
+
+    @Test
+    public void testUniqueSshMachineLocation() throws Exception {
+        entity.addLocations(ImmutableList.of(machine));
+        assertEquals(EntitySuppliers.uniqueSshMachineLocation(entity).get(), machine);
+    }
+    
+    @Test
+    public void testUniqueSshMachineLocationWhenNoLocation() throws Exception {
+        Supplier<SshMachineLocation> supplier = EntitySuppliers.uniqueSshMachineLocation(entity);
+        try {
+            supplier.get();
+            fail();
+        } catch (IllegalStateException e) {
+            // expected: success
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/EntityTypeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntityTypeTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntityTypeTest.java
new file mode 100644
index 0000000..c01c5c9
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntityTypeTest.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity;
+
+import static org.apache.brooklyn.core.entity.AbstractEntity.CHILD_ADDED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.CHILD_REMOVED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.EFFECTOR_ADDED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.EFFECTOR_CHANGED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.EFFECTOR_REMOVED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.GROUP_ADDED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.GROUP_REMOVED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.LOCATION_ADDED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.LOCATION_REMOVED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.POLICY_ADDED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.POLICY_REMOVED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.SENSOR_ADDED;
+import static org.apache.brooklyn.core.entity.AbstractEntity.SENSOR_REMOVED;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.api.sensor.Sensor;
+import org.apache.brooklyn.core.entity.AbstractEntity;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.core.test.entity.TestEntityImpl;
+import org.apache.brooklyn.effector.core.MethodEffector;
+import org.apache.brooklyn.sensor.core.BasicSensorEvent;
+import org.apache.brooklyn.sensor.core.Sensors;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class EntityTypeTest extends BrooklynAppUnitTestSupport {
+    private static final AttributeSensor<String> TEST_SENSOR = Sensors.newStringSensor("test.sensor");
+    private EntityInternal entity;
+    private RecordingSensorEventListener<Sensor> listener;
+
+    public final static Set<Sensor<?>> DEFAULT_SENSORS = ImmutableSet.<Sensor<?>>of(
+            SENSOR_ADDED, SENSOR_REMOVED,
+            EFFECTOR_ADDED, EFFECTOR_REMOVED, EFFECTOR_CHANGED,
+            POLICY_ADDED, POLICY_REMOVED,
+            CHILD_ADDED, CHILD_REMOVED,
+            LOCATION_ADDED, LOCATION_REMOVED,
+            GROUP_ADDED, GROUP_REMOVED); 
+
+    public static class EmptyEntityForTesting extends AbstractEntity {}
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception{
+        super.setUp();
+        entity = (EntityInternal) app.createAndManageChild(EntitySpec.create(Entity.class, EmptyEntityForTesting.class));
+        listener = new RecordingSensorEventListener<>();
+        app.getSubscriptionContext().subscribe(entity, SENSOR_ADDED, listener);
+        app.getSubscriptionContext().subscribe(entity, SENSOR_REMOVED, listener);
+    }
+
+    @Test
+    public void testGetName() throws Exception {
+        TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        assertEquals(entity2.getEntityType().getName(), TestEntity.class.getCanonicalName());
+    }
+    
+    @Test
+    public void testGetSimpleName() throws Exception {
+        TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        assertEquals(entity2.getEntityType().getSimpleName(), TestEntity.class.getSimpleName());
+    }
+
+    @Test
+    public void testGetEffectors() throws Exception {
+        TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        Set<Effector<?>> effectors = entity2.getEntityType().getEffectors();
+        
+        class MatchesNamePredicate implements Predicate<Effector<?>> {
+            private final String name;
+            public MatchesNamePredicate(String name) {
+                this.name = name;
+            }
+            @Override public boolean apply(@Nullable Effector<?> input) {
+                return name.equals(input.getName());
+            }
+        };
+        
+        assertNotNull(Iterables.find(effectors, new MatchesNamePredicate("myEffector")), null);
+        assertNotNull(Iterables.find(effectors, new MatchesNamePredicate("identityEffector")), null);
+    }
+
+    @Test
+    public void testGetEffector() throws Exception {
+        TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        Effector<?> effector = entity2.getEntityType().getEffectorByName("myEffector").get();
+        Effector<?> effector2 = entity2.getEntityType().getEffectorByName("identityEffector").get();
+        assertEquals(effector.getName(), "myEffector");
+        assertTrue(effector.getParameters().isEmpty(), "myEffector should have had no params, but had "+effector.getParameters());
+        assertEquals(effector2.getName(), "identityEffector");
+        assertEquals(effector2.getParameters().size(), 1, "identityEffector should have had one param, but had "+effector2.getParameters());
+        assertEquals(Iterables.getOnlyElement(effector2.getParameters()).getName(), "arg", "identityEffector should have had 'arg' param, but had "+effector2.getParameters());
+    }
+
+    @Test
+    public void testGetEffectorDeprecated() throws Exception {
+        TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        Effector<?> effector = entity2.getEntityType().getEffectorByName("myEffector").get();
+        Effector<?> effector2 = entity2.getEntityType().getEffectorByName("identityEffector").get();
+        assertEquals(effector.getName(), "myEffector");
+        assertEquals(effector2.getName(), "identityEffector");
+    }
+
+    @Test
+    public void testCustomSimpleName() throws Exception {
+        class CustomTypeNamedEntity extends AbstractEntity {
+            private final String typeName;
+            @SuppressWarnings("deprecation")
+            CustomTypeNamedEntity(Entity parent, String typeName) {
+                super(parent);
+                this.typeName = typeName;
+            }
+            @Override protected String getEntityTypeName() {
+                return typeName;
+            }
+        }
+        
+        CustomTypeNamedEntity entity2 = new CustomTypeNamedEntity(app, "a.b.with space");
+        Entities.manage(entity2);
+        assertEquals(entity2.getEntityType().getSimpleName(), "with_space");
+        
+        CustomTypeNamedEntity entity3 = new CustomTypeNamedEntity(app, "a.b.with$dollar");
+        Entities.manage(entity3);
+        assertEquals(entity3.getEntityType().getSimpleName(), "with_dollar");
+        
+        CustomTypeNamedEntity entity4 = new CustomTypeNamedEntity(app, "a.nothingafterdot.");
+        Entities.manage(entity4);
+        assertEquals(entity4.getEntityType().getSimpleName(), "a.nothingafterdot.");
+    }
+    
+    @Test
+    public void testGetSensors() throws Exception{
+        assertEquals(entity.getEntityType().getSensors(), DEFAULT_SENSORS);
+    }
+
+    protected <T> void assertEventuallyListenerEventsEqual(final List<T> sensorEvents) {
+        final RecordingSensorEventListener listener = this.listener;
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(listener.getEvents(), sensorEvents);
+            }
+        });
+    }
+    
+    @Test
+    public void testAddSensors() throws Exception{
+        entity.getMutableEntityType().addSensor(TEST_SENSOR);
+        assertEquals(entity.getEntityType().getSensors(), 
+                ImmutableSet.builder().addAll(DEFAULT_SENSORS).add(TEST_SENSOR).build());
+        
+        assertEventuallyListenerEventsEqual(ImmutableList.of(BasicSensorEvent.ofUnchecked(SENSOR_ADDED, entity, TEST_SENSOR)));
+    }
+
+    @Test
+    public void testAddSensorValueThroughEntity() throws Exception{
+        entity.setAttribute(TEST_SENSOR, "abc");
+        assertEquals(entity.getEntityType().getSensors(), 
+                ImmutableSet.builder().addAll(DEFAULT_SENSORS).add(TEST_SENSOR).build());
+        
+        assertEventuallyListenerEventsEqual(ImmutableList.of(BasicSensorEvent.ofUnchecked(SENSOR_ADDED, entity, TEST_SENSOR)));
+    }
+
+    @Test
+    public void testRemoveSensorThroughEntity() throws Exception{
+        entity.setAttribute(TEST_SENSOR, "abc");
+        entity.removeAttribute(TEST_SENSOR);
+        assertFalse(entity.getEntityType().getSensors().contains(TEST_SENSOR), "sensors="+entity.getEntityType().getSensors()); 
+        assertEquals(entity.getAttribute(TEST_SENSOR), null);
+        
+        assertEventuallyListenerEventsEqual(ImmutableList.of(BasicSensorEvent.ofUnchecked(SENSOR_ADDED, entity, TEST_SENSOR),
+            BasicSensorEvent.ofUnchecked(SENSOR_REMOVED, entity, TEST_SENSOR)));
+    }
+
+    @Test
+    public void testRemoveSensor() throws Exception {
+        entity.getMutableEntityType().removeSensor(SENSOR_ADDED);
+        assertEquals(entity.getEntityType().getSensors(), 
+                MutableSet.builder().addAll(DEFAULT_SENSORS).remove(SENSOR_ADDED).build().asUnmodifiable());
+        
+        assertEventuallyListenerEventsEqual(ImmutableList.of(
+            BasicSensorEvent.ofUnchecked(SENSOR_REMOVED, entity, SENSOR_ADDED)));
+    }
+
+    @Test
+    public void testRemoveSensors() throws Exception {
+        entity.getMutableEntityType().removeSensor(SENSOR_ADDED.getName());
+        entity.getMutableEntityType().removeSensor(POLICY_ADDED.getName());
+        assertEquals(entity.getEntityType().getSensors(), 
+                MutableSet.builder().addAll(DEFAULT_SENSORS).remove(SENSOR_ADDED).remove(POLICY_ADDED).build().asUnmodifiable());
+
+        final RecordingSensorEventListener<?> listener = this.listener;
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(Iterables.size(listener.getEvents()), 2);
+            }
+        });
+        assertEventuallyListenerEventsEqual(ImmutableList.of(
+            BasicSensorEvent.ofUnchecked(SENSOR_REMOVED, entity, SENSOR_ADDED),
+            BasicSensorEvent.ofUnchecked(SENSOR_REMOVED, entity, POLICY_ADDED)));
+    }
+
+    @Test
+    public void testGetSensor() throws Exception {
+        Sensor<?> sensor = entity.getEntityType().getSensor("entity.sensor.added");
+        assertEquals(sensor.getDescription(), "Sensor dynamically added to entity");
+        assertEquals(sensor.getName(), "entity.sensor.added");
+        
+        assertNull(entity.getEntityType().getSensor("does.not.exist"));
+    }
+
+    @Test
+    public void testHasSensor() throws Exception {
+        assertTrue(entity.getEntityType().hasSensor("entity.sensor.added"));
+        assertFalse(entity.getEntityType().hasSensor("does.not.exist"));
+    }
+    
+    // Previously EntityDynamicType's constructor when passed `entity` during the entity's construction (!)
+    // would pass this to EntityDynamicType.findEffectors, which would do log.warn in some cirumstances,
+    // with entity.toString as part of the log message. But if the toString called getConfig() this would 
+    // fail because we were still in the middle of constructing the entity.entityType!
+    @Test
+    public void testEntityDynamicTypeDoesNotCallToStringDuringConstruction() throws Exception {
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).impl(EntityWithToStringAccessingConfig.class));
+        entity.toString();
+    }
+    
+    public static class EntityWithToStringAccessingConfig extends TestEntityImpl {
+        
+        // to cause warning to be logged: non-static constant
+        public final MethodEffector<Void> NON_STATIC_EFFECTOR = new MethodEffector<Void>(EntityWithToStringAccessingConfig.class, "nonStaticEffector");
+
+        public void nonStaticEffector() {
+        }
+        
+        @Override
+        public String toString() {
+            return super.toString() + getConfig(CONF_NAME);
+        }
+    }
+}