You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ge...@apache.org on 2016/12/20 11:10:26 UTC

[08/13] brooklyn-server git commit: Move test objects in the allowed package scope

Move test objects in the allowed package scope

Fixes tests which expect the DSL evaluated objects to be in the correct package. Adds tests to verify that objects outside of the package scope are inaccessible to DSL.


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/6607ccbc
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/6607ccbc
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/6607ccbc

Branch: refs/heads/master
Commit: 6607ccbc3adff741c23d1aff7fb13fa9e6e16a8d
Parents: e831d75
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Tue Dec 13 08:52:29 2016 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Tue Dec 13 09:15:37 2016 +0200

----------------------------------------------------------------------
 .../brooklyn/camp/brooklyn/dsl/DslTest.java     | 535 ---------------
 .../camp/brooklyn/dsl/DslYamlBlockingTest.java  | 674 -------------------
 .../brooklyn/camp/brooklyn/spi/dsl/DslTest.java | 533 +++++++++++++++
 .../camp/brooklyn/spi/dsl/DslYamlTest.java      | 655 ++++++++++++++++++
 .../spi/dsl/methods/DslTestObjects.java         |  78 +++
 .../methods/custom/UserSuppliedPackageType.java |  22 +
 6 files changed, 1288 insertions(+), 1209 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6607ccbc/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslTest.java
deleted file mode 100644
index ae55379..0000000
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslTest.java
+++ /dev/null
@@ -1,535 +0,0 @@
-/*
- * Copyright 2016 The Apache Software Foundation.
- *
- * Licensed 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.camp.brooklyn.dsl;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.NoSuchElementException;
-import java.util.Random;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.EntitySpec;
-import org.apache.brooklyn.api.mgmt.Task;
-import org.apache.brooklyn.api.sensor.AttributeSensor;
-import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
-import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier;
-import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.config.ConfigKeys;
-import org.apache.brooklyn.core.entity.EntityInternal;
-import org.apache.brooklyn.core.objs.BasicSpecParameter;
-import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
-import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
-import org.apache.brooklyn.core.test.entity.TestApplication;
-import org.apache.brooklyn.core.test.entity.TestEntity;
-import org.apache.brooklyn.test.Asserts;
-import org.apache.brooklyn.util.core.task.Tasks;
-import org.apache.brooklyn.util.core.task.ValueResolver;
-import org.apache.brooklyn.util.exceptions.Exceptions;
-import org.apache.brooklyn.util.guava.Maybe;
-import org.apache.brooklyn.util.text.Identifiers;
-import org.apache.brooklyn.util.time.Duration;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import com.google.common.base.Function;
-import com.google.common.base.Functions;
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.ListenableScheduledFuture;
-import com.google.common.util.concurrent.ListeningScheduledExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-
-/**
- * Also see org.apache.brooklyn.camp.brooklyn.DslAndRebindYamlTest for pure-yaml tests.
- * 
- * The purpose of this class is to test at the java-api level, giving more control for
- * repeated assertions etc (e.g. concurrent calls, looping round to create entities
- * repeatedly, etc).
- */
-public class DslTest extends BrooklynAppUnitTestSupport {
-
-    private static final int MAX_PARALLEL_RESOLVERS = 50;
-    private static final int MANY_RESOLVER_ITERATIONS = 100;
-    
-    private ListeningScheduledExecutorService executor;
-    private Random random = new Random();
-    
-    @BeforeMethod(alwaysRun=true)
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor());
-    }
-    
-    @AfterMethod(alwaysRun=true)
-    @Override
-    public void tearDown() throws Exception {
-        try {
-            if (executor != null) executor.shutdownNow();
-        } finally {
-            super.tearDown();
-        }
-    }
-    
-    @Test
-    public void testAttributeWhenReadyEmptyDoesNotBlock() throws Exception {
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
-        Maybe<?> actualValue = execDslRealRealQuick(dsl, TestApplication.MY_ATTRIBUTE.getType(), app);
-        assertTrue(actualValue.isAbsent());
-    }
-
-    @Test
-    public void testAttributeWhenReadyEmptyImmediatelyDoesNotBlock() throws Exception {
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
-        Maybe<?> actualValue = execDslImmediately(dsl, TestApplication.MY_ATTRIBUTE.getType(), app, true);
-        assertTrue(actualValue.isAbsent());
-    }
-
-    @Test
-    public void testAttributeWhenReady() throws Exception {
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestEntity.NAME.getName());
-        new AttributeWhenReadyTestWorker(app, TestEntity.NAME, dsl).run();
-    }
-
-    @Test
-    public void testAttributeWhenReadyBlocksUntilReady() throws Exception {
-        // Fewer iterations, because there is a sleep each time
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestEntity.NAME.getName());
-        new AttributeWhenReadyTestWorker(app, TestEntity.NAME, dsl).satisfiedAsynchronously(true).resolverIterations(2).run();
-    }
-
-    @Test(groups="Integration")
-    public void testAttributeWhenReadyConcurrent() throws Exception {
-        final BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestEntity.NAME.getName());
-        runConcurrentWorker(new Supplier<Runnable>() {
-            @Override
-            public Runnable get() {
-                return new AttributeWhenReadyTestWorker(app, TestEntity.NAME, dsl);
-            }
-        });
-    }
-
-    @Test
-    public void testConfig() throws Exception {
-        ConfigKey<String> configKey = ConfigKeys.newStringConfigKey("testConfig");
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.config(configKey.getName());
-        new ConfigTestWorker(app, configKey, dsl).run();
-    }
-
-    @Test
-    public void testConfigWithDsl() throws Exception {
-        ConfigKey<?> configKey = ConfigKeys.newConfigKey(Entity.class, "testConfig");
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.config(configKey.getName());
-        Supplier<ConfigValuePair> valueSupplier = new Supplier<ConfigValuePair>() {
-            @Override public ConfigValuePair get() {
-                return new ConfigValuePair(BrooklynDslCommon.root(), app);
-            }
-        };
-        new ConfigTestWorker(app, configKey, valueSupplier, dsl).run();
-    }
-
-    @Test
-    public void testConfigWithDslNotReadyImmediately() throws Exception {
-        final ConfigKey<String> configKey = ConfigKeys.newStringConfigKey("testConfig");
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.config(configKey.getName());
-        Function<Entity, ConfigValuePair> valueSupplier = new Function<Entity, ConfigValuePair>() {
-            private ListenableScheduledFuture<?> future;
-            @Override
-            public ConfigValuePair apply(final Entity entity) {
-                try {
-                    // If executed in a loop, then wait for previous call's future to complete.
-                    // If previous assertion used getImmediately, then it won't have waited for the future to complete.
-                    if (future != null) {
-                        future.get(Asserts.DEFAULT_LONG_TIMEOUT.toMilliseconds(), TimeUnit.MILLISECONDS);
-                        future = null;
-                    }
-    
-                    // Reset sensor - otherwise if run in a loop the old value will be picked up, before our execute sets the new value
-                    entity.sensors().set(TestApplication.MY_ATTRIBUTE, null);
-                    
-                    final String expectedValue = Identifiers.makeRandomId(10);
-                    Runnable job = new Runnable() {
-                        public void run() {
-                            entity.sensors().set(TestApplication.MY_ATTRIBUTE, expectedValue);
-                        }
-                    };
-                    future = executor.schedule(job, random.nextInt(20), TimeUnit.MILLISECONDS);
-    
-                    BrooklynDslDeferredSupplier<?> attributeDsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
-                    return new ConfigValuePair(attributeDsl, expectedValue);
-
-                } catch (Exception e) {
-                    throw Exceptions.propagate(e);
-                }
-            }
-        };
-        new ConfigTestWorker(app, configKey, valueSupplier, dsl).satisfiedAsynchronously(true).resolverIterations(2).run();
-    }
-    
-    @Test
-    public void testConfigUsesParameterDefaultValue() throws Exception {
-        final ConfigKey<String> configKey = ConfigKeys.newStringConfigKey("testConfig");
-        ConfigKey<String> configParam = ConfigKeys.newStringConfigKey("testParam", "myDescription", "myDefaultConfigValue");
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.config(configKey.getName());
-        Supplier<ConfigValuePair> valueSupplier = new Supplier<ConfigValuePair>() {
-            @Override public ConfigValuePair get() {
-                return new ConfigValuePair(BrooklynDslCommon.config("testParam"), "myDefaultConfigValue");
-            }
-        };
-        new ConfigTestWorker(app, configKey, valueSupplier, dsl)
-                .childSpec(EntitySpec.create(TestEntity.class).parameters(ImmutableList.of(new BasicSpecParameter<String>("myLabel", true, configParam))))
-                .run();
-    }
-    
-    @Test
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public void testConfigImmediatelyDoesNotBlock() throws Exception {
-        ConfigKey<String> configKey = ConfigKeys.newStringConfigKey("testConfig");
-        BrooklynDslDeferredSupplier<?> attributeDsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
-        app.config().set((ConfigKey)configKey, attributeDsl); // ugly cast because val is DSL, resolving to a string
-        BrooklynDslDeferredSupplier<?> configDsl = BrooklynDslCommon.config(configKey.getName());
-        Maybe<?> actualValue = execDslImmediately(configDsl, configKey.getType(), app, true);
-        assertTrue(actualValue.isAbsent());
-    }
-
-    @Test
-    public void testSelf() throws Exception {
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.self();
-        new SelfTestWorker(app, dsl).run();
-    }
-
-    @Test(groups="Integration")
-    public void testSelfConcurrent() throws Exception {
-        final BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.self();
-        runConcurrentWorker(new Supplier<Runnable>() {
-            @Override
-            public Runnable get() {
-                return new SelfTestWorker(app, dsl);
-            }
-        });
-    }
-
-    @Test
-    public void testParent() throws Exception {
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.parent();
-        new ParentTestWorker(app, dsl).run();
-    }
-
-    @Test(groups="Integration")
-    public void testParentConcurrent() throws Exception {
-        final BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.parent();
-        runConcurrentWorker(new Supplier<Runnable>() {
-            @Override
-            public Runnable get() {
-                return new ParentTestWorker(app, dsl);
-            }
-        });
-    }
-
-    @Test
-    public void testEntity() throws Exception {
-        TestEntity entity = app.addChild(EntitySpec.create(TestEntity.class).configure(BrooklynCampConstants.PLAN_ID, "myId"));
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.entity("myId");
-        Maybe<?> actualValue = execDslImmediately(dsl, Entity.class, app, true);
-        assertEquals(actualValue.get(), entity);
-    }
-
-    @Test
-    public void testEntityNotFound() throws Exception {
-        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.entity("myIdDoesNotExist");
-        try {
-            Maybe<?> actualValue = execDslImmediately(dsl, Entity.class, app, true);
-            Asserts.shouldHaveFailedPreviously("actual="+actualValue);
-        } catch (Exception e) {
-            NoSuchElementException nsee = Exceptions.getFirstThrowableOfType(e, NoSuchElementException.class);
-            if (nsee == null) {
-                throw e;
-            }
-        }
-    }
-
-    // Different from testParentConcurrent() only in the execution context the task is submitted in (global vs app)
-    @Test(invocationCount=10)
-    public void testTaskContext() {
-        final TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        // context entity here = none
-        Task<Entity> task = Tasks.<Entity>builder()
-            .body(new Callable<Entity>() {
-                @Override
-                public Entity call() throws Exception {
-                    // context entity here = entity
-                    return BrooklynTaskTags.getContextEntity(Tasks.current());
-                }
-            }).build();
-        Task<Entity> result = entity.getExecutionContext().submit(task);
-        assertEquals(result.getUnchecked(), entity);
-    }
-
-    protected void runConcurrentWorker(Supplier<Runnable> taskSupplier) {
-        Collection<Task<?>> results = new ArrayList<>();
-        for (int i = 0; i < MAX_PARALLEL_RESOLVERS; i++) {
-            Task<?> result = app.getExecutionContext().submit(taskSupplier.get());
-            results.add(result);
-        }
-        for (Task<?> result : results) {
-            result.getUnchecked();
-        }
-    }
-    
-    private static class DslTestWorker implements Runnable {
-        protected final TestApplication parent;
-        protected final BrooklynDslDeferredSupplier<?> dsl;
-        protected final Class<?> type;
-        protected EntitySpec<? extends TestEntity> childSpec = EntitySpec.create(TestEntity.class);
-        protected int resolverIterations = MANY_RESOLVER_ITERATIONS;
-        protected boolean satisfiedAsynchronously = false;
-        private boolean wrapInTaskForImmediately = true;
-        
-        public DslTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl, Class<?> type) {
-            this.parent = checkNotNull(parent, "parent");
-            this.dsl = checkNotNull(dsl, "dsl");
-            this.type = checkNotNull(type, "type");
-        }
-
-        public DslTestWorker childSpec(EntitySpec<? extends TestEntity> val) {
-            childSpec = val;
-            return this;
-        }
-        
-        public DslTestWorker resolverIterations(int val) {
-            resolverIterations = val;
-            return this;
-        }
-        
-        public DslTestWorker satisfiedAsynchronously(boolean val) {
-            satisfiedAsynchronously = val;
-            return this;
-        }
-        
-        public DslTestWorker wrapInTaskForImmediately(boolean val) {
-            wrapInTaskForImmediately = val;
-            return this;
-        }
-        
-        @Override
-        public void run() {
-            try {
-                TestEntity entity = parent.addChild(childSpec);
-                for (int i = 0; i < resolverIterations; i++) {
-                    // Call dsl.getImmediately()
-                    preResolve(entity);
-                    Maybe<?> immediateValue;
-                    try {
-                        immediateValue = execDslImmediately(dsl, type, entity, wrapInTaskForImmediately);
-                    } catch (Exception e) {
-                        throw Exceptions.propagate(e);
-                    }
-                    postResolve(entity, immediateValue, true);
-                    
-                    // Call dsl.get()
-                    preResolve(entity);
-                    Maybe<?> eventualValue = execDslEventually(dsl, type, entity, Duration.ONE_MINUTE);
-                    postResolve(entity, eventualValue, false);
-                }
-            } catch (Exception e) {
-                Exceptions.propagate(e);
-            }
-        }
-
-        protected void preResolve(TestEntity entity) throws Exception {
-        }
-
-        protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isImmediate) throws Exception {
-        }
-    }
-
-    private class AttributeWhenReadyTestWorker extends DslTestWorker {
-        private AttributeSensor<String> sensor;
-        private String expectedValue;
-        private ListenableScheduledFuture<?> future;
-
-        public AttributeWhenReadyTestWorker(TestApplication parent, AttributeSensor<String> sensor, BrooklynDslDeferredSupplier<?> dsl) {
-            super(parent, dsl, sensor.getType());
-            this.sensor = sensor;
-        }
-
-        @Override
-        protected void preResolve(final TestEntity entity) {
-            expectedValue = Identifiers.makeRandomId(10);
-            Runnable job = new Runnable() {
-                public void run() {
-                    entity.sensors().set(sensor, expectedValue);
-                }
-            };
-            if (satisfiedAsynchronously) {
-                future = executor.schedule(job, random.nextInt(20), TimeUnit.MILLISECONDS);
-            } else {
-                job.run();
-            }
-        }
-
-        @Override
-        protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isImmediate) throws Exception {
-            if (satisfiedAsynchronously && isImmediate) {
-                // We accept a maybe.absent if we called getImmediately when satisfiedAsynchronously
-                assertTrue(actualValue.isAbsent() || expectedValue.equals(actualValue.get()), "actual="+actualValue+"; expected="+expectedValue);
-            } else {
-                assertEquals(actualValue.get(), expectedValue);
-            }
-            
-            if (future != null) {
-                future.get(Asserts.DEFAULT_LONG_TIMEOUT.toMilliseconds(), TimeUnit.MILLISECONDS);
-                future = null;
-            }
-            // Reset sensor - otherwise if run in a loop the old value will be picked up, before our execute sets the new value
-            entity.sensors().set(sensor, null);
-        }
-    }
-
-    private static class SelfTestWorker extends DslTestWorker {
-        public SelfTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl) {
-            super(parent, dsl, Entity.class);
-        }
-
-        @Override
-        protected void preResolve(TestEntity entity) {
-        }
-
-        @Override
-        protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isImmediate) {
-            assertEquals(actualValue.get(), entity);
-        }
-
-    }
-
-    private static class ParentTestWorker extends DslTestWorker {
-        public ParentTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl) {
-            super(parent, dsl, Entity.class);
-        }
-
-        @Override
-        protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isImmediate) {
-            assertEquals(actualValue.get(), parent);
-        }
-    }
-    
-    private class ConfigTestWorker extends DslTestWorker {
-        private ConfigKey<?> config;
-        private Object expectedValue;
-        private Function<? super Entity, ConfigValuePair> valueFunction;
-        
-        public ConfigTestWorker(TestApplication parent, ConfigKey<?> config, BrooklynDslDeferredSupplier<?> dsl) {
-            this(parent, config, newRandomConfigValueSupplier(), dsl);
-        }
-
-        public ConfigTestWorker(TestApplication parent, ConfigKey<?> config, Supplier<ConfigValuePair> valueSupplier, BrooklynDslDeferredSupplier<?> dsl) {
-            this(parent, config, Functions.forSupplier(valueSupplier), dsl);
-        }
-        
-        public ConfigTestWorker(TestApplication parent, ConfigKey<?> config, Function<? super Entity, ConfigValuePair> valueFunction, BrooklynDslDeferredSupplier<?> dsl) {
-            super(parent, dsl, config.getType());
-            this.config = config;
-            this.valueFunction = valueFunction;
-        }
-
-        @Override
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        protected void preResolve(final TestEntity entity) {
-            ConfigValuePair pair = valueFunction.apply(entity);
-            expectedValue = pair.expectedResolvedVal;
-            entity.config().set((ConfigKey)config, pair.configVal); // nasty cast, because val might be a DSL
-        }
-
-        @Override
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isImmediate) throws Exception {
-            if (satisfiedAsynchronously && isImmediate) {
-                // We accept a maybe.absent if we called getImmediately when satisfiedAsynchronously
-                assertTrue(actualValue.isAbsent() || expectedValue.equals(actualValue.get()), "actual="+actualValue+"; expected="+expectedValue);
-            } else {
-                assertEquals(actualValue.get(), expectedValue);
-            }
-            
-            // Reset config - otherwise if run in a loop the old value will be picked up, before our execute sets the new value
-            entity.config().set((ConfigKey)config, (Object)null); // ugly cast from ConfigKey<?>
-        }
-    }
-
-    static class ConfigValuePair {
-        public final Object configVal;
-        public final Object expectedResolvedVal;
-        
-        public ConfigValuePair(Object configVal, Object expectedResolvedVal) {
-            this.configVal = configVal;
-            this.expectedResolvedVal = expectedResolvedVal;
-        }
-    }
-
-    private static Supplier<ConfigValuePair> newRandomConfigValueSupplier() {
-        return new Supplier<ConfigValuePair>() {
-            @Override public ConfigValuePair get() {
-                String val = Identifiers.makeRandomId(10);
-                return new ConfigValuePair(val, val);
-            }
-        };
-    }
-
-    static Maybe<?> execDslImmediately(final BrooklynDslDeferredSupplier<?> dsl, final Class<?> type, final Entity context, boolean execInTask) throws Exception {
-        // Exec'ing immediately will call DSL in current thread. It needs to find the context entity,
-        // and does this using BrooklynTaskTags.getTargetOrContextEntity(Tasks.current()).
-        // If we are not in a task executed by the context entity, then this lookup will fail. 
-        Callable<Maybe<?>> job = new Callable<Maybe<?>>() {
-            public Maybe<?> call() throws Exception {
-                return Tasks.resolving(dsl).as(type)
-                        .context(context)
-                        .description("Computing "+dsl)
-                        .immediately(true)
-                        .getMaybe();
-            }
-        };
-        if (execInTask) {
-            Task<Maybe<?>> task = ((EntityInternal)context).getExecutionContext().submit(job);
-            task.get(Asserts.DEFAULT_LONG_TIMEOUT);
-            assertTrue(task.isDone());
-            return task.get();
-            
-        } else {
-            return job.call();
-        }
-    }
-    
-    static Maybe<?> execDslRealRealQuick(BrooklynDslDeferredSupplier<?> dsl, Class<?> type, Entity context) {
-        return execDslEventually(dsl, type, context, ValueResolver.REAL_REAL_QUICK_WAIT);
-    }
-    
-    static Maybe<?> execDslEventually(BrooklynDslDeferredSupplier<?> dsl, Class<?> type, Entity context, Duration timeout) {
-        return Tasks.resolving(dsl).as(type)
-                .context(context)
-                .description("Computing "+dsl)
-                .timeout(timeout)
-                .getMaybe();
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6607ccbc/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslYamlBlockingTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslYamlBlockingTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslYamlBlockingTest.java
deleted file mode 100644
index 772a28f..0000000
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslYamlBlockingTest.java
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
- * Copyright 2016 The Apache Software Foundation.
- *
- * Licensed 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.camp.brooklyn.dsl;
-
-import static org.testng.Assert.assertEquals;
-
-import java.util.concurrent.Callable;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.mgmt.Task;
-import org.apache.brooklyn.api.sensor.AttributeSensor;
-import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
-import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslCallable;
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.config.ConfigKeys;
-import org.apache.brooklyn.core.entity.EntityInternal;
-import org.apache.brooklyn.core.sensor.Sensors;
-import org.apache.brooklyn.core.test.entity.TestApplication;
-import org.apache.brooklyn.entity.stock.BasicApplication;
-import org.apache.brooklyn.entity.stock.BasicEntity;
-import org.apache.brooklyn.test.Asserts;
-import org.apache.brooklyn.util.core.task.DeferredSupplier;
-import org.apache.brooklyn.util.core.task.ImmediateSupplier;
-import org.apache.brooklyn.util.guava.Maybe;
-import org.testng.annotations.Test;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
-
-// Doesn't test executing the DSL from different contexts (i.e. fetching the config from children inheriting it)
-public class DslYamlBlockingTest extends AbstractYamlTest {
-    private static final ConfigKey<Object> DEST = ConfigKeys.newConfigKey(Object.class, "dest");
-    private static final ConfigKey<Object> DEST2 = ConfigKeys.newConfigKey(Object.class, "dest2");
-    private static final ConfigKey<Object> DEST3 = ConfigKeys.newConfigKey(Object.class, "dest3");
-
-    // See also test-referencing-entities.yaml
-
-    // No tests for entitySpec, object, formatString, external - relying on extensive tests elsewhere
-
-    @Test
-    public void testDslSelf() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:self()");
-        assertEquals(getConfigEventually(app, DEST), app);
-    }
-
-    @Test
-    public void testDslEntity() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:entity(\"child\")",
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: child");
-        assertEquals(getConfigEventually(app, DEST), Iterables.getOnlyElement(app.getChildren()));
-    }
-
-    @Test
-    public void testDslParent() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    brooklyn.config:",
-                "      dest: $brooklyn:parent()");
-        final Entity child = Iterables.getOnlyElement(app.getChildren());
-        assertEquals(getConfigEventually(child, DEST), app);
-    }
-
-    @Test
-    public void testDslChild() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:child(\"child\")",
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: child",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: another-child");
-        assertEquals(getConfigEventually(app, DEST), app.getChildren().iterator().next());
-    }
-
-    @Test
-    public void testDslSibling() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: child",
-                "    brooklyn.config:",
-                "      dest: $brooklyn:sibling(\"another-child\")",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: another-child");
-        final Entity child1 = Iterables.get(app.getChildren(), 0);
-        final Entity child2 = Iterables.get(app.getChildren(), 1);
-        assertEquals(getConfigEventually(child1, DEST), child2);
-    }
-
-    @Test
-    public void testDslDescendant() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  id: self",
-                "  brooklyn.config:",
-                "    dest: $brooklyn:descendant(\"child\")",
-                "    dest2: $brooklyn:descendant(\"grand-child\")",
-                "    dest3: $brooklyn:descendant(\"self\")",
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: child",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: another-child",
-                "    brooklyn.children:",
-                "    - type: " + BasicEntity.class.getName(),
-                "      id: grand-child");
-        final Entity child1 = Iterables.get(app.getChildren(), 0);
-        final Entity child2 = Iterables.get(app.getChildren(), 1);
-        final Entity grandChild = Iterables.getOnlyElement(child2.getChildren());
-        assertEquals(getConfigEventually(app, DEST), child1);
-        assertEquals(getConfigEventually(app, DEST2), grandChild);
-        try {
-            assertEquals(getConfigEventually(app, DEST3), app);
-            Asserts.shouldHaveFailedPreviously("Self not in descendant scope");
-        } catch (Exception e) {
-            Asserts.expectedFailureContains(e, "No entity matching id self");
-        }
-    }
-
-    @Test
-    public void testDslAncestor() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  id: app",
-                "  brooklyn.config:",
-                "    dest: $brooklyn:ancestor(\"app\")",
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    brooklyn.config:",
-                "      dest: $brooklyn:ancestor(\"app\")",
-                "  - type: " + BasicEntity.class.getName(),
-                "    brooklyn.config:",
-                "      dest: $brooklyn:ancestor(\"app\")",
-                "    brooklyn.children:",
-                "    - type: " + BasicEntity.class.getName(),
-                "      brooklyn.config:",
-                "        dest: $brooklyn:ancestor(\"app\")");
-        final Entity child1 = Iterables.get(app.getChildren(), 0);
-        final Entity child2 = Iterables.get(app.getChildren(), 1);
-        final Entity grandChild = Iterables.getOnlyElement(child2.getChildren());
-        assertEquals(getConfigEventually(child1, DEST), app);
-        assertEquals(getConfigEventually(child2, DEST), app);
-        assertEquals(getConfigEventually(grandChild, DEST), app);
-        try {
-            assertEquals(getConfigEventually(app, DEST), app);
-            Asserts.shouldHaveFailedPreviously("App not in ancestor scope");
-        } catch (Exception e) {
-            Asserts.expectedFailureContains(e, "No entity matching id app");
-        }
-    }
-
-    @Test
-    public void testDslRoot() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  id: app",
-                "  brooklyn.config:",
-                "    dest: $brooklyn:root()",
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    brooklyn.config:",
-                "      dest: $brooklyn:root()",
-                "  - type: " + BasicEntity.class.getName(),
-                "    brooklyn.config:",
-                "      dest: $brooklyn:root()",
-                "    brooklyn.children:",
-                "    - type: " + BasicEntity.class.getName(),
-                "      brooklyn.config:",
-                "        dest: $brooklyn:root()");
-        final Entity child1 = Iterables.get(app.getChildren(), 0);
-        final Entity child2 = Iterables.get(app.getChildren(), 1);
-        final Entity grandChild = Iterables.getOnlyElement(child2.getChildren());
-        assertEquals(getConfigEventually(child1, DEST), app);
-        assertEquals(getConfigEventually(child2, DEST), app);
-        assertEquals(getConfigEventually(grandChild, DEST), app);
-        assertEquals(getConfigEventually(app, DEST), app);
-    }
-
-    @Test
-    public void testDslScopeRoot() throws Exception {
-        addCatalogItems(
-                "brooklyn.catalog:",
-                "  version: " + TEST_VERSION,
-                "  items:",
-                "  - id: simple-item",
-                "    itemType: entity",
-                "    item:",
-                "      type: "+ BasicEntity.class.getName(),
-                "  - id: wrapping-plain",
-                "    itemType: entity",
-                "    item:",
-                "      type: "+ BasicEntity.class.getName(),
-                "      brooklyn.children:",
-                "      - type: " + BasicEntity.class.getName(),
-                "        brooklyn.config:",
-                "          dest: $brooklyn:scopeRoot()",
-                "  - id: wrapping-simple",
-                "    itemType: entity",
-                "    item:",
-                "      type: "+ BasicEntity.class.getName(),
-                "      brooklyn.children:",
-                "      - type: simple-item",
-                "        brooklyn.config:",
-                "          dest: $brooklyn:scopeRoot()");
-
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.children:",
-                "  - type: wrapping-plain",
-                "  - type: wrapping-simple");
-        Entity child1 = Iterables.get(app.getChildren(), 0);
-        Entity child2 = Iterables.get(app.getChildren(), 1);
-        assertScopeRoot(child1, false);
-        // TODO Not the result I'd expect - in both cases the entity argument should the the scopeRoot element, not its child
-        assertScopeRoot(child2, true);
-    }
-
-    private void assertScopeRoot(Entity entity, boolean isScopeBugged) throws Exception {
-        Entity child = Iterables.getOnlyElement(entity.getChildren());
-        if (!isScopeBugged) {
-            assertEquals(getConfigEventually(child, DEST), entity);
-        } else {
-            assertEquals(getConfigEventually(child, DEST), child);
-        }
-    }
-
-    @Test
-    public void testDslConfig() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    source: myvalue",
-                "    dest: $brooklyn:config(\"source\")");
-        assertEquals(getConfigEventually(app, DEST), "myvalue");
-    }
-
-    @Test
-    public void testDslConfigOnEntity() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:entity(\"sourceEntity\").config(\"source\")",
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: sourceEntity",
-                "    brooklyn.config:",
-                "      source: myvalue");
-        assertEquals(getConfigEventually(app, DEST), "myvalue");
-    }
-
-    @Test(groups="WIP") // config accepts strings only, no suppliers
-    public void testDslConfigWithDeferredArg() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    source: myvalue",
-                "    configName: source",
-                "    dest: $brooklyn:config(config(\"configName\"))");
-        assertEquals(getConfigEventually(app, DEST), "myvalue");
-    }
-
-    @Test(groups="WIP") // config accepts strings only, no suppliers
-    public void testDslConfigOnEntityWithDeferredArg() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    entityName: sourceEntity",
-                "    configName: source",
-                "    dest: $brooklyn:entity(config(\"entityName\")).config(config(\"configName\"))",
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: sourceEntity",
-                "    brooklyn.config:",
-                "      source: myvalue");
-        assertEquals(getConfigEventually(app, DEST), "myvalue");
-    }
-
-    @Test
-    public void testDslAttributeWhenReady() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.initializers:",
-                "  - type: org.apache.brooklyn.core.sensor.StaticSensor",
-                "    brooklyn.config:",
-                "      name: source",
-                "      static.value: myvalue",
-                "  brooklyn.config:",
-                "    dest: $brooklyn:attributeWhenReady(\"source\")");
-        assertEquals(getConfigEventually(app, DEST), "myvalue");
-    }
-
-    @Test
-    public void testDslAttributeWhenReadyOnEntity() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:entity(\"sourceEntity\").attributeWhenReady(\"source\")",
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: sourceEntity",
-                "    brooklyn.initializers:",
-                "    - type: org.apache.brooklyn.core.sensor.StaticSensor",
-                "      brooklyn.config:",
-                "        name: source",
-                "        static.value: myvalue");
-        assertEquals(getConfigEventually(app, DEST), "myvalue");
-    }
-
-    @Test(groups="WIP") // config accepts strings only, no suppliers
-    public void testDslAttributeWhenReadyWithDeferredArg() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.initializers:",
-                "  - type: org.apache.brooklyn.core.sensor.StaticSensor",
-                "    brooklyn.config:",
-                "      name: source",
-                "      static.value: myvalue",
-                "  brooklyn.config:",
-                "    configName: source",
-                "    dest: $brooklyn:attributeWhenReady(config(\"configName\"))");
-        assertEquals(getConfigEventually(app, DEST), "myvalue");
-    }
-
-    @Test(groups="WIP") // config accepts strings only, no suppliers
-    public void testDslAttributeWhenReadyOnEntityWithDeferredArg() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    entityName: sourceEntity",
-                "    configName: source",
-                "    dest: $brooklyn:entity(config(\"entityName\")).attributeWhenReady(config(\"configName\"))",
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: sourceEntity",
-                "    brooklyn.initializers:",
-                "    - type: org.apache.brooklyn.core.sensor.StaticSensor",
-                "      brooklyn.config:",
-                "        name: source",
-                "        static.value: myvalue");
-        assertEquals(getConfigEventually(app, DEST), "myvalue");
-    }
-    
-    @Test
-    public void testDslEntityId() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:entityId()");
-        assertEquals(getConfigEventually(app, DEST), app.getId());
-    }
-
-    @Test
-    public void testDslEntityIdOnEntity() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:entity(\"sourceEntity\").entityId()",
-                "  brooklyn.children:",
-                "  - type: " + BasicEntity.class.getName(),
-                "    id: sourceEntity");
-        final Entity child = Iterables.getOnlyElement(app.getChildren());
-        assertEquals(getConfigEventually(app, DEST), child.getId());
-    }
-
-    @Test
-    public void testDslSensor() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + TestApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:sensor(\"test.myattribute\")");
-        assertEquals(getConfigEventually(app, DEST), TestApplication.MY_ATTRIBUTE);
-    }
-
-    @Test
-    public void testDslSensorOnEntity() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:entity(\"sourceEntity\").sensor(\"test.myattribute\")",
-                "  brooklyn.children:",
-                "  - type: " + TestApplication.class.getName(),
-                "    id: sourceEntity");
-        assertEquals(getConfigEventually(app, DEST), TestApplication.MY_ATTRIBUTE);
-    }
-
-    @Test
-    public void testDslSensorWithClass() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:sensor(\"org.apache.brooklyn.core.test.entity.TestApplication\", \"test.myattribute\")");
-        assertEquals(getConfigEventually(app, DEST), TestApplication.MY_ATTRIBUTE);
-    }
-
-    @Test
-    public void testDslLiteral() throws Exception {
-        final String literal = "custom(), $brooklyn:root(), invalid; syntax";
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + TestApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:literal(\"" + literal + "\")");
-        assertEquals(getConfigEventually(app, DEST), literal);
-    }
-
-    @Test
-    public void testDslRegexReplacement() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + TestApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:regexReplacement(\"Broooklyn\", \"o+\", \"oo\")");
-        assertEquals(getConfigEventually(app, DEST), "Brooklyn");
-    }
-
-    @Test
-    public void testDslRegexReplacementWithDeferredArg() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + TestApplication.class.getName(),
-                "  brooklyn.config:",
-                "    source: Broooklyn",
-                "    pattern: o+",
-                "    replacement: oo",
-                "    dest: $brooklyn:regexReplacement(config(\"source\"), config(\"pattern\"), config(\"replacement\"))");
-        assertEquals(getConfigEventually(app, DEST), "Brooklyn");
-    }
-
-    @Test
-    public void testDslFunctionRegexReplacement() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + TestApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:function.regexReplacement(\"o+\", \"oo\")");
-        @SuppressWarnings("unchecked")
-        Function<String, String> replacementFn = (Function<String, String>) getConfigEventually(app, DEST);
-        assertEquals(replacementFn.apply("Broooklyn"), "Brooklyn");
-    }
-
-    @Test
-    public void testDslFunctionRegexReplacementWithDeferredArg() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + TestApplication.class.getName(),
-                "  brooklyn.config:",
-                "    source: Broooklyn",
-                "    pattern: o+",
-                "    replacement: oo",
-                "    dest: $brooklyn:function.regexReplacement(config(\"pattern\"), config(\"replacement\"))");
-        @SuppressWarnings("unchecked")
-        Function<String, String> replacementFn = (Function<String, String>) getConfigEventually(app, DEST);
-        assertEquals(replacementFn.apply("Broooklyn"), "Brooklyn");
-    }
-
-    @Test
-    public void testDeferredDslChainingOnConfig() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    targetEntity: $brooklyn:self()",
-                "    dest: $brooklyn:config(\"targetEntity\").getId()");
-        assertEquals(getConfigEventually(app, DEST), app.getId());
-    }
-
-    @Test
-    public void testDeferredDslChainingOnConfigNoFunction() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:config(\"targetValue\").getNonExistent()");
-        ConfigKey<TestDslSupplierValue> targetValueKey = ConfigKeys.newConfigKey(TestDslSupplierValue.class, "targetValue");
-        app.config().set(targetValueKey, new TestDslSupplierValue());
-        try {
-            assertEquals(getConfigEventually(app, DEST), app.getId());
-            Asserts.shouldHaveFailedPreviously("Expected to fail because method does not exist");
-        } catch (Exception e) {
-            Asserts.expectedFailureContains(e, "No such function 'getNonExistent()'");
-        }
-    }
-
-    @Test
-    public void testDeferredDslChainingOnSensor() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:attributeWhenReady(\"targetEntity\").getId()");
-        AttributeSensor<Entity> targetEntitySensor = Sensors.newSensor(Entity.class, "targetEntity");
-        app.sensors().set(targetEntitySensor, app);
-        assertEquals(getConfigEventually(app, DEST), app.getId());
-    }
-
-    @Test(groups="WIP")
-    public void testDeferredDslWrapsIntermediates() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:attributeWhenReady(\"targetEntity\").attributeWhenReady(\"entity.id\")");
-        AttributeSensor<Entity> targetEntitySensor = Sensors.newSensor(Entity.class, "targetEntity");
-        app.sensors().set(targetEntitySensor, app);
-        assertEquals(getConfigEventually(app, DEST), app.getId());
-    }
-
-    @Test
-    public void testDeferredDslChainingOnNullConfig() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:config(\"targetEntity\").getId()");
-        try {
-            assertEquals(getConfigEventually(app, DEST), app.getId());
-            Asserts.shouldHaveFailedPreviously("Expected to fail because targetEntity config is null");
-        } catch (Exception e) {
-            Asserts.expectedFailureContains(e, "config(\"targetEntity\") evaluates to null");
-        }
-    }
-
-    public static class DslTestSupplierWrapper {
-        private Object supplier;
-        
-        public DslTestSupplierWrapper(Object supplier) {
-            this.supplier = supplier;
-        }
-
-        public Object getSupplier() {
-            return supplier;
-        }
-    }
-
-    public static class TestDslSupplierValue {
-        public boolean isSupplierEvaluated() {
-            return true;
-        }
-    }
-
-    public static class TestDslSupplier implements DeferredSupplier<Object>, ImmediateSupplier<Object> {
-        private Object value;
-
-        public TestDslSupplier(Object value) {
-            this.value = value;
-        }
-
-        @Override
-        public Object get() {
-            return getImmediately().get();
-        }
-
-        @Override
-        public Maybe<Object> getImmediately() {
-            return Maybe.of(value);
-        }
-    }
-
-    @Test
-    public void testDeferredDslChainingWithCustomSupplier() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:config(\"customSupplierWrapper\").getSupplier().isSupplierEvaluated()");
-        ConfigKey<DslTestSupplierWrapper> customSupplierWrapperKey = ConfigKeys.newConfigKey(DslTestSupplierWrapper.class, "customSupplierWrapper");
-        app.config().set(customSupplierWrapperKey, new DslTestSupplierWrapper(new TestDslSupplier(new TestDslSupplierValue())));
-        assertEquals(getConfigEventually(app, DEST), Boolean.TRUE);
-    }
-
-    public static class DslTestCallable implements DslCallable, DeferredSupplier<TestDslSupplier>, ImmediateSupplier<TestDslSupplier> {
-
-        @Override
-        public Maybe<TestDslSupplier> getImmediately() {
-            throw new IllegalStateException("Not to be called");
-        }
-
-        @Override
-        public TestDslSupplier get() {
-            throw new IllegalStateException("Not to be called");
-        }
-
-        public boolean isSupplierCallable() {
-            return true;
-        }
-    }
-
-    @Test
-    public void testDeferredDslChainingWithCustomCallable() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:config(\"customCallableWrapper\").getSupplier().isSupplierCallable()");
-        ConfigKey<DslTestSupplierWrapper> customCallableWrapperKey = ConfigKeys.newConfigKey(DslTestSupplierWrapper.class, "customCallableWrapper");
-        app.config().set(customCallableWrapperKey, new DslTestSupplierWrapper(new DslTestCallable()));
-        assertEquals(getConfigEventually(app, DEST), Boolean.TRUE);
-    }
-
-    @Test
-    public void testDeferredDslChainingWithNestedEvaluation() throws Exception {
-        final Entity app = createAndStartApplication(
-                "services:",
-                "- type: " + BasicApplication.class.getName(),
-                "  brooklyn.config:",
-                "    dest: $brooklyn:config(\"customCallableWrapper\").getSupplier().isSupplierCallable()");
-        ConfigKey<TestDslSupplier> customCallableWrapperKey = ConfigKeys.newConfigKey(TestDslSupplier.class, "customCallableWrapper");
-        app.config().set(customCallableWrapperKey, new TestDslSupplier(new DslTestSupplierWrapper(new DslTestCallable())));
-        assertEquals(getConfigEventually(app, DEST), Boolean.TRUE);
-    }
-
-    private static <T> T getConfigEventually(final Entity entity, final ConfigKey<T> configKey) throws Exception {
-        Task<T> result = ((EntityInternal)entity).getExecutionContext().submit(new Callable<T>() {
-            @Override
-            public T call() throws Exception {
-                // TODO Move the getNonBlocking call out of the task after #280 is merged.
-                // Currently doesn't work because no execution context available.
-                Maybe<T> immediateValue = ((EntityInternal)entity).config().getNonBlocking(configKey);
-                T blockingValue = entity.config().get(configKey);
-                assertEquals(immediateValue.get(), blockingValue);
-                return blockingValue;
-            }
-        });
-        return result.get(Asserts.DEFAULT_LONG_TIMEOUT);
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6607ccbc/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java
new file mode 100644
index 0000000..4292dbe
--- /dev/null
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java
@@ -0,0 +1,533 @@
+/*
+ * Copyright 2016 The Apache Software Foundation.
+ *
+ * Licensed 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.camp.brooklyn.spi.dsl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
+import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.core.objs.BasicSpecParameter;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.core.task.ValueResolver;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.text.Identifiers;
+import org.apache.brooklyn.util.time.Duration;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableScheduledFuture;
+import com.google.common.util.concurrent.ListeningScheduledExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+/**
+ * Also see org.apache.brooklyn.camp.brooklyn.DslAndRebindYamlTest for pure-yaml tests.
+ * 
+ * The purpose of this class is to test at the java-api level, giving more control for
+ * repeated assertions etc (e.g. concurrent calls, looping round to create entities
+ * repeatedly, etc).
+ */
+public class DslTest extends BrooklynAppUnitTestSupport {
+
+    private static final int MAX_PARALLEL_RESOLVERS = 50;
+    private static final int MANY_RESOLVER_ITERATIONS = 100;
+    
+    private ListeningScheduledExecutorService executor;
+    private Random random = new Random();
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor());
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        try {
+            if (executor != null) executor.shutdownNow();
+        } finally {
+            super.tearDown();
+        }
+    }
+    
+    @Test
+    public void testAttributeWhenReadyEmptyDoesNotBlock() throws Exception {
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
+        Maybe<?> actualValue = execDslRealRealQuick(dsl, TestApplication.MY_ATTRIBUTE.getType(), app);
+        assertTrue(actualValue.isAbsent());
+    }
+
+    @Test
+    public void testAttributeWhenReadyEmptyImmediatelyDoesNotBlock() throws Exception {
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
+        Maybe<?> actualValue = execDslImmediately(dsl, TestApplication.MY_ATTRIBUTE.getType(), app, true);
+        assertTrue(actualValue.isAbsent());
+    }
+
+    @Test
+    public void testAttributeWhenReady() throws Exception {
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestEntity.NAME.getName());
+        new AttributeWhenReadyTestWorker(app, TestEntity.NAME, dsl).run();
+    }
+
+    @Test
+    public void testAttributeWhenReadyBlocksUntilReady() throws Exception {
+        // Fewer iterations, because there is a sleep each time
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestEntity.NAME.getName());
+        new AttributeWhenReadyTestWorker(app, TestEntity.NAME, dsl).satisfiedAsynchronously(true).resolverIterations(2).run();
+    }
+
+    @Test(groups="Integration")
+    public void testAttributeWhenReadyConcurrent() throws Exception {
+        final BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestEntity.NAME.getName());
+        runConcurrentWorker(new Supplier<Runnable>() {
+            @Override
+            public Runnable get() {
+                return new AttributeWhenReadyTestWorker(app, TestEntity.NAME, dsl);
+            }
+        });
+    }
+
+    @Test
+    public void testConfig() throws Exception {
+        ConfigKey<String> configKey = ConfigKeys.newStringConfigKey("testConfig");
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.config(configKey.getName());
+        new ConfigTestWorker(app, configKey, dsl).run();
+    }
+
+    @Test
+    public void testConfigWithDsl() throws Exception {
+        ConfigKey<?> configKey = ConfigKeys.newConfigKey(Entity.class, "testConfig");
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.config(configKey.getName());
+        Supplier<ConfigValuePair> valueSupplier = new Supplier<ConfigValuePair>() {
+            @Override public ConfigValuePair get() {
+                return new ConfigValuePair(BrooklynDslCommon.root(), app);
+            }
+        };
+        new ConfigTestWorker(app, configKey, valueSupplier, dsl).run();
+    }
+
+    @Test
+    public void testConfigWithDslNotReadyImmediately() throws Exception {
+        final ConfigKey<String> configKey = ConfigKeys.newStringConfigKey("testConfig");
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.config(configKey.getName());
+        Function<Entity, ConfigValuePair> valueSupplier = new Function<Entity, ConfigValuePair>() {
+            private ListenableScheduledFuture<?> future;
+            @Override
+            public ConfigValuePair apply(final Entity entity) {
+                try {
+                    // If executed in a loop, then wait for previous call's future to complete.
+                    // If previous assertion used getImmediately, then it won't have waited for the future to complete.
+                    if (future != null) {
+                        future.get(Asserts.DEFAULT_LONG_TIMEOUT.toMilliseconds(), TimeUnit.MILLISECONDS);
+                        future = null;
+                    }
+    
+                    // Reset sensor - otherwise if run in a loop the old value will be picked up, before our execute sets the new value
+                    entity.sensors().set(TestApplication.MY_ATTRIBUTE, null);
+                    
+                    final String expectedValue = Identifiers.makeRandomId(10);
+                    Runnable job = new Runnable() {
+                        public void run() {
+                            entity.sensors().set(TestApplication.MY_ATTRIBUTE, expectedValue);
+                        }
+                    };
+                    future = executor.schedule(job, random.nextInt(20), TimeUnit.MILLISECONDS);
+    
+                    BrooklynDslDeferredSupplier<?> attributeDsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
+                    return new ConfigValuePair(attributeDsl, expectedValue);
+
+                } catch (Exception e) {
+                    throw Exceptions.propagate(e);
+                }
+            }
+        };
+        new ConfigTestWorker(app, configKey, valueSupplier, dsl).satisfiedAsynchronously(true).resolverIterations(2).run();
+    }
+    
+    @Test
+    public void testConfigUsesParameterDefaultValue() throws Exception {
+        final ConfigKey<String> configKey = ConfigKeys.newStringConfigKey("testConfig");
+        ConfigKey<String> configParam = ConfigKeys.newStringConfigKey("testParam", "myDescription", "myDefaultConfigValue");
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.config(configKey.getName());
+        Supplier<ConfigValuePair> valueSupplier = new Supplier<ConfigValuePair>() {
+            @Override public ConfigValuePair get() {
+                return new ConfigValuePair(BrooklynDslCommon.config("testParam"), "myDefaultConfigValue");
+            }
+        };
+        new ConfigTestWorker(app, configKey, valueSupplier, dsl)
+                .childSpec(EntitySpec.create(TestEntity.class).parameters(ImmutableList.of(new BasicSpecParameter<String>("myLabel", true, configParam))))
+                .run();
+    }
+    
+    @Test
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testConfigImmediatelyDoesNotBlock() throws Exception {
+        ConfigKey<String> configKey = ConfigKeys.newStringConfigKey("testConfig");
+        BrooklynDslDeferredSupplier<?> attributeDsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
+        app.config().set((ConfigKey)configKey, attributeDsl); // ugly cast because val is DSL, resolving to a string
+        BrooklynDslDeferredSupplier<?> configDsl = BrooklynDslCommon.config(configKey.getName());
+        Maybe<?> actualValue = execDslImmediately(configDsl, configKey.getType(), app, true);
+        assertTrue(actualValue.isAbsent());
+    }
+
+    @Test
+    public void testSelf() throws Exception {
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.self();
+        new SelfTestWorker(app, dsl).run();
+    }
+
+    @Test(groups="Integration")
+    public void testSelfConcurrent() throws Exception {
+        final BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.self();
+        runConcurrentWorker(new Supplier<Runnable>() {
+            @Override
+            public Runnable get() {
+                return new SelfTestWorker(app, dsl);
+            }
+        });
+    }
+
+    @Test
+    public void testParent() throws Exception {
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.parent();
+        new ParentTestWorker(app, dsl).run();
+    }
+
+    @Test(groups="Integration")
+    public void testParentConcurrent() throws Exception {
+        final BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.parent();
+        runConcurrentWorker(new Supplier<Runnable>() {
+            @Override
+            public Runnable get() {
+                return new ParentTestWorker(app, dsl);
+            }
+        });
+    }
+
+    @Test
+    public void testEntity() throws Exception {
+        TestEntity entity = app.addChild(EntitySpec.create(TestEntity.class).configure(BrooklynCampConstants.PLAN_ID, "myId"));
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.entity("myId");
+        Maybe<?> actualValue = execDslImmediately(dsl, Entity.class, app, true);
+        assertEquals(actualValue.get(), entity);
+    }
+
+    @Test
+    public void testEntityNotFound() throws Exception {
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.entity("myIdDoesNotExist");
+        try {
+            Maybe<?> actualValue = execDslImmediately(dsl, Entity.class, app, true);
+            Asserts.shouldHaveFailedPreviously("actual="+actualValue);
+        } catch (Exception e) {
+            NoSuchElementException nsee = Exceptions.getFirstThrowableOfType(e, NoSuchElementException.class);
+            if (nsee == null) {
+                throw e;
+            }
+        }
+    }
+
+    // Different from testParentConcurrent() only in the execution context the task is submitted in (global vs app)
+    @Test(invocationCount=10)
+    public void testTaskContext() {
+        final TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+        // context entity here = none
+        Task<Entity> task = Tasks.<Entity>builder()
+            .body(new Callable<Entity>() {
+                @Override
+                public Entity call() throws Exception {
+                    // context entity here = entity
+                    return BrooklynTaskTags.getContextEntity(Tasks.current());
+                }
+            }).build();
+        Task<Entity> result = entity.getExecutionContext().submit(task);
+        assertEquals(result.getUnchecked(), entity);
+    }
+
+    protected void runConcurrentWorker(Supplier<Runnable> taskSupplier) {
+        Collection<Task<?>> results = new ArrayList<>();
+        for (int i = 0; i < MAX_PARALLEL_RESOLVERS; i++) {
+            Task<?> result = app.getExecutionContext().submit(taskSupplier.get());
+            results.add(result);
+        }
+        for (Task<?> result : results) {
+            result.getUnchecked();
+        }
+    }
+    
+    private static class DslTestWorker implements Runnable {
+        protected final TestApplication parent;
+        protected final BrooklynDslDeferredSupplier<?> dsl;
+        protected final Class<?> type;
+        protected EntitySpec<? extends TestEntity> childSpec = EntitySpec.create(TestEntity.class);
+        protected int resolverIterations = MANY_RESOLVER_ITERATIONS;
+        protected boolean satisfiedAsynchronously = false;
+        private boolean wrapInTaskForImmediately = true;
+        
+        public DslTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl, Class<?> type) {
+            this.parent = checkNotNull(parent, "parent");
+            this.dsl = checkNotNull(dsl, "dsl");
+            this.type = checkNotNull(type, "type");
+        }
+
+        public DslTestWorker childSpec(EntitySpec<? extends TestEntity> val) {
+            childSpec = val;
+            return this;
+        }
+        
+        public DslTestWorker resolverIterations(int val) {
+            resolverIterations = val;
+            return this;
+        }
+        
+        public DslTestWorker satisfiedAsynchronously(boolean val) {
+            satisfiedAsynchronously = val;
+            return this;
+        }
+        
+        public DslTestWorker wrapInTaskForImmediately(boolean val) {
+            wrapInTaskForImmediately = val;
+            return this;
+        }
+        
+        @Override
+        public void run() {
+            try {
+                TestEntity entity = parent.addChild(childSpec);
+                for (int i = 0; i < resolverIterations; i++) {
+                    // Call dsl.getImmediately()
+                    preResolve(entity);
+                    Maybe<?> immediateValue;
+                    try {
+                        immediateValue = execDslImmediately(dsl, type, entity, wrapInTaskForImmediately);
+                    } catch (Exception e) {
+                        throw Exceptions.propagate(e);
+                    }
+                    postResolve(entity, immediateValue, true);
+                    
+                    // Call dsl.get()
+                    preResolve(entity);
+                    Maybe<?> eventualValue = execDslEventually(dsl, type, entity, Duration.ONE_MINUTE);
+                    postResolve(entity, eventualValue, false);
+                }
+            } catch (Exception e) {
+                Exceptions.propagate(e);
+            }
+        }
+
+        protected void preResolve(TestEntity entity) throws Exception {
+        }
+
+        protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isImmediate) throws Exception {
+        }
+    }
+
+    private class AttributeWhenReadyTestWorker extends DslTestWorker {
+        private AttributeSensor<String> sensor;
+        private String expectedValue;
+        private ListenableScheduledFuture<?> future;
+
+        public AttributeWhenReadyTestWorker(TestApplication parent, AttributeSensor<String> sensor, BrooklynDslDeferredSupplier<?> dsl) {
+            super(parent, dsl, sensor.getType());
+            this.sensor = sensor;
+        }
+
+        @Override
+        protected void preResolve(final TestEntity entity) {
+            expectedValue = Identifiers.makeRandomId(10);
+            Runnable job = new Runnable() {
+                public void run() {
+                    entity.sensors().set(sensor, expectedValue);
+                }
+            };
+            if (satisfiedAsynchronously) {
+                future = executor.schedule(job, random.nextInt(20), TimeUnit.MILLISECONDS);
+            } else {
+                job.run();
+            }
+        }
+
+        @Override
+        protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isImmediate) throws Exception {
+            if (satisfiedAsynchronously && isImmediate) {
+                // We accept a maybe.absent if we called getImmediately when satisfiedAsynchronously
+                assertTrue(actualValue.isAbsent() || expectedValue.equals(actualValue.get()), "actual="+actualValue+"; expected="+expectedValue);
+            } else {
+                assertEquals(actualValue.get(), expectedValue);
+            }
+            
+            if (future != null) {
+                future.get(Asserts.DEFAULT_LONG_TIMEOUT.toMilliseconds(), TimeUnit.MILLISECONDS);
+                future = null;
+            }
+            // Reset sensor - otherwise if run in a loop the old value will be picked up, before our execute sets the new value
+            entity.sensors().set(sensor, null);
+        }
+    }
+
+    private static class SelfTestWorker extends DslTestWorker {
+        public SelfTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl) {
+            super(parent, dsl, Entity.class);
+        }
+
+        @Override
+        protected void preResolve(TestEntity entity) {
+        }
+
+        @Override
+        protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isImmediate) {
+            assertEquals(actualValue.get(), entity);
+        }
+
+    }
+
+    private static class ParentTestWorker extends DslTestWorker {
+        public ParentTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl) {
+            super(parent, dsl, Entity.class);
+        }
+
+        @Override
+        protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isImmediate) {
+            assertEquals(actualValue.get(), parent);
+        }
+    }
+    
+    private class ConfigTestWorker extends DslTestWorker {
+        private ConfigKey<?> config;
+        private Object expectedValue;
+        private Function<? super Entity, ConfigValuePair> valueFunction;
+        
+        public ConfigTestWorker(TestApplication parent, ConfigKey<?> config, BrooklynDslDeferredSupplier<?> dsl) {
+            this(parent, config, newRandomConfigValueSupplier(), dsl);
+        }
+
+        public ConfigTestWorker(TestApplication parent, ConfigKey<?> config, Supplier<ConfigValuePair> valueSupplier, BrooklynDslDeferredSupplier<?> dsl) {
+            this(parent, config, Functions.forSupplier(valueSupplier), dsl);
+        }
+        
+        public ConfigTestWorker(TestApplication parent, ConfigKey<?> config, Function<? super Entity, ConfigValuePair> valueFunction, BrooklynDslDeferredSupplier<?> dsl) {
+            super(parent, dsl, config.getType());
+            this.config = config;
+            this.valueFunction = valueFunction;
+        }
+
+        @Override
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        protected void preResolve(final TestEntity entity) {
+            ConfigValuePair pair = valueFunction.apply(entity);
+            expectedValue = pair.expectedResolvedVal;
+            entity.config().set((ConfigKey)config, pair.configVal); // nasty cast, because val might be a DSL
+        }
+
+        @Override
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        protected void postResolve(TestEntity entity, Maybe<?> actualValue, boolean isImmediate) throws Exception {
+            if (satisfiedAsynchronously && isImmediate) {
+                // We accept a maybe.absent if we called getImmediately when satisfiedAsynchronously
+                assertTrue(actualValue.isAbsent() || expectedValue.equals(actualValue.get()), "actual="+actualValue+"; expected="+expectedValue);
+            } else {
+                assertEquals(actualValue.get(), expectedValue);
+            }
+            
+            // Reset config - otherwise if run in a loop the old value will be picked up, before our execute sets the new value
+            entity.config().set((ConfigKey)config, (Object)null); // ugly cast from ConfigKey<?>
+        }
+    }
+
+    static class ConfigValuePair {
+        public final Object configVal;
+        public final Object expectedResolvedVal;
+        
+        public ConfigValuePair(Object configVal, Object expectedResolvedVal) {
+            this.configVal = configVal;
+            this.expectedResolvedVal = expectedResolvedVal;
+        }
+    }
+
+    private static Supplier<ConfigValuePair> newRandomConfigValueSupplier() {
+        return new Supplier<ConfigValuePair>() {
+            @Override public ConfigValuePair get() {
+                String val = Identifiers.makeRandomId(10);
+                return new ConfigValuePair(val, val);
+            }
+        };
+    }
+
+    static Maybe<?> execDslImmediately(final BrooklynDslDeferredSupplier<?> dsl, final Class<?> type, final Entity context, boolean execInTask) throws Exception {
+        // Exec'ing immediately will call DSL in current thread. It needs to find the context entity,
+        // and does this using BrooklynTaskTags.getTargetOrContextEntity(Tasks.current()).
+        // If we are not in a task executed by the context entity, then this lookup will fail. 
+        Callable<Maybe<?>> job = new Callable<Maybe<?>>() {
+            public Maybe<?> call() throws Exception {
+                return Tasks.resolving(dsl).as(type)
+                        .context(context)
+                        .description("Computing "+dsl)
+                        .immediately(true)
+                        .getMaybe();
+            }
+        };
+        if (execInTask) {
+            Task<Maybe<?>> task = ((EntityInternal)context).getExecutionContext().submit(job);
+            task.get(Asserts.DEFAULT_LONG_TIMEOUT);
+            assertTrue(task.isDone());
+            return task.get();
+            
+        } else {
+            return job.call();
+        }
+    }
+    
+    static Maybe<?> execDslRealRealQuick(BrooklynDslDeferredSupplier<?> dsl, Class<?> type, Entity context) {
+        return execDslEventually(dsl, type, context, ValueResolver.REAL_REAL_QUICK_WAIT);
+    }
+    
+    static Maybe<?> execDslEventually(BrooklynDslDeferredSupplier<?> dsl, Class<?> type, Entity context, Duration timeout) {
+        return Tasks.resolving(dsl).as(type)
+                .context(context)
+                .description("Computing "+dsl)
+                .timeout(timeout)
+                .getMaybe();
+    }
+}