Repository: brooklyn-server Updated Branches: refs/heads/master 923abe7b3 -> 58b1de717
Basic coverage of DSL YAML parsing and evaluation Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/2e0ee180 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/2e0ee180 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/2e0ee180 Branch: refs/heads/master Commit: 2e0ee180db8713a8251c68538a63550c2c29a537 Parents: 7f1846c Author: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com> Authored: Thu Dec 8 11:27:04 2016 +0200 Committer: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com> Committed: Tue Dec 13 09:15:34 2016 +0200 ---------------------------------------------------------------------- .../camp/brooklyn/dsl/DslYamlBlockingTest.java | 673 +++++++++++++++++++ 1 file changed, 673 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2e0ee180/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 new file mode 100644 index 0000000..280b060 --- /dev/null +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslYamlBlockingTest.java @@ -0,0 +1,673 @@ +/* + * 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.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 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); + } +}