You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sj...@apache.org on 2016/07/13 17:29:44 UTC

[4/5] brooklyn-server git commit: Add RelativeEntityTestCaseImpl

Add RelativeEntityTestCaseImpl


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

Branch: refs/heads/master
Commit: 07942b12168c591fe2a0b1cd1c791c36e5f1afc5
Parents: 0c0ec60
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Mon Jun 20 18:45:50 2016 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Wed Jul 6 12:02:07 2016 +0100

----------------------------------------------------------------------
 .../brooklyn/spi/dsl/methods/DslComponent.java  |  14 +-
 .../test/framework/RelativeEntityTestCase.java  |  65 +++++++++
 .../framework/RelativeEntityTestCaseImpl.java   | 144 +++++++++++++++++++
 .../framework/RelativeEntityTestCaseTest.java   | 135 +++++++++++++++++
 4 files changed, 356 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/07942b12/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
index cfb343f..b02f736 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
@@ -61,14 +61,24 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> {
     private final DslComponent scopeComponent;
     private final Scope scope;
 
+    /**
+     * Resolve componentId in the {@link Scope#GLOBAL} scope.
+     */
     public DslComponent(String componentId) {
         this(Scope.GLOBAL, componentId);
     }
-    
+
+    /**
+     * Resolve componentId in scope relative to the current
+     * {@link BrooklynTaskTags#getTargetOrContextEntity) target or context} entity.
+     */
     public DslComponent(Scope scope, String componentId) {
         this(null, scope, componentId);
     }
-    
+
+    /**
+     * Resolve componentId in scope relative to scopeComponent.
+     */
     public DslComponent(DslComponent scopeComponent, Scope scope, String componentId) {
         Preconditions.checkNotNull(scope, "scope");
         this.scopeComponent = scopeComponent;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/07942b12/test-framework/src/main/java/org/apache/brooklyn/test/framework/RelativeEntityTestCase.java
----------------------------------------------------------------------
diff --git a/test-framework/src/main/java/org/apache/brooklyn/test/framework/RelativeEntityTestCase.java b/test-framework/src/main/java/org/apache/brooklyn/test/framework/RelativeEntityTestCase.java
new file mode 100644
index 0000000..3796bf4
--- /dev/null
+++ b/test-framework/src/main/java/org/apache/brooklyn/test/framework/RelativeEntityTestCase.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.brooklyn.test.framework;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
+import org.apache.brooklyn.core.sensor.Sensors;
+
+import com.google.common.base.Predicates;
+
+/**
+ * A test case that resolves target ID relative to an anchor entity.
+ * <p>
+ * For example, to run tests against a named child of an entity:
+ * <pre>
+ * services
+ * - id: app
+ *   brooklyn.children:
+ *     id: child-id
+ * - id: test-case
+ *   brooklyn.config:
+ *     anchor: $brooklyn:component("app").descendant("child-id")
+ * </pre>
+ * The anchor entity is resolved from the <code>anchor</code>, <code>target</code> or
+ * <code>targetId</code>, in order of preference. The latter two are useful when using
+ * the test with with entities like {@link LoopOverGroupMembersTestCase}. Values for
+ * <code>target</code> will be overwritten with the resolved entity so child test
+ * cases work as expected.
+ */
+@ImplementedBy(RelativeEntityTestCaseImpl.class)
+public interface RelativeEntityTestCase extends TargetableTestComponent {
+
+    AttributeSensorAndConfigKey<Entity, Entity> ANCHOR = ConfigKeys.newSensorAndConfigKey(Entity.class,
+            "anchor",
+            "Entity from which component should be resolved.");
+
+    ConfigKey<DslComponent> COMPONENT = ConfigKeys.builder(DslComponent.class)
+            .name("component")
+            .description("The component to resolve against target")
+            .constraint(Predicates.<DslComponent>notNull())
+            .build();
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/07942b12/test-framework/src/main/java/org/apache/brooklyn/test/framework/RelativeEntityTestCaseImpl.java
----------------------------------------------------------------------
diff --git a/test-framework/src/main/java/org/apache/brooklyn/test/framework/RelativeEntityTestCaseImpl.java b/test-framework/src/main/java/org/apache/brooklyn/test/framework/RelativeEntityTestCaseImpl.java
new file mode 100644
index 0000000..ea78e7b
--- /dev/null
+++ b/test-framework/src/main/java/org/apache/brooklyn/test/framework/RelativeEntityTestCaseImpl.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.brooklyn.test.framework;
+
+import java.util.Collection;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Lists;
+
+public class RelativeEntityTestCaseImpl extends TargetableTestComponentImpl implements RelativeEntityTestCase {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RelativeEntityTestCaseImpl.class);
+
+    @Override
+    public Entity resolveTarget() {
+        Entity anchor = config().get(ANCHOR);
+        if (anchor == null) {
+            anchor = super.resolveTarget();
+        }
+        if (anchor == null) {
+            throw new IllegalArgumentException("No anchor entity found for " + this);
+        }
+        sensors().set(ANCHOR, anchor);
+        Maybe<Object> component = config().getRaw(COMPONENT);
+        if (component.isAbsentOrNull()) {
+            throw new IllegalArgumentException("No component found for " + this);
+        } else if (!(component.get() instanceof DslComponent)) {
+            throw new IllegalArgumentException("Expected DslComponent value for component, found " + component.get());
+        }
+        DslComponent finder = DslComponent.class.cast(component.get());
+        Task<Entity> task = Entities.submit(anchor, finder);
+        return task.getUnchecked();
+    }
+
+    @Override
+    public void start(Collection<? extends Location> locations) {
+        sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STARTING);
+
+        Entity target = resolveTarget();
+        if (target == null) {
+            LOG.debug("Tasks NOT successfully run. RelativeEntityTestCaseImpl target unset");
+            setServiceState(false, Lifecycle.ON_FIRE);
+            return;
+        }
+        config().set(BaseTest.TARGET_ENTITY, target);
+
+        boolean success = true;
+        try {
+            for (Entity child : getChildren()) {
+                if (child instanceof Startable) {
+                    Startable test = Startable.class.cast(child);
+                    test.start(locations);
+                    if (Lifecycle.RUNNING.equals(child.sensors().get(Attributes.SERVICE_STATE_ACTUAL))) {
+                        LOG.debug("Task of {} successfully run, targeting {}", this, target);
+                    } else {
+                        LOG.warn("Problem in child test-case of {}, targeting {}", this, target);
+                        success = false;
+                    }
+                } else {
+                    LOG.info("Ignored child of {} that is not Startable: {}", this, child);
+                }
+                if (!success) {
+                    break;
+                }
+            }
+        } catch (Throwable t) {
+            Exceptions.propagateIfFatal(t);
+            LOG.warn("Problem in child test-case of " + this + ", targeting " + target, t);
+            success = false;
+        }
+
+        if (success) {
+            LOG.debug("Tasks successfully run. Update state of {} to RUNNING.", this);
+            setServiceState(true, Lifecycle.RUNNING);
+        } else {
+            LOG.debug("Tasks NOT successfully run. Update state of {} to ON_FIRE.", this);
+            setServiceState(false, Lifecycle.ON_FIRE);
+        }
+    }
+
+    @Override
+    public void stop() {
+        sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPING);
+        try {
+            for (Entity child : this.getChildren()) {
+                if (child instanceof Startable) ((Startable) child).stop();
+            }
+            LOG.debug("Tasks successfully run. Update state of {} to STOPPED.", this);
+            setServiceState(false, Lifecycle.STOPPED);
+        } catch (Throwable t) {
+            LOG.debug("Tasks NOT successfully run. Update state of {} to ON_FIRE.", this);
+            setServiceState(false, Lifecycle.ON_FIRE);
+            throw Exceptions.propagate(t);
+        }
+    }
+
+    @Override
+    public void restart() {
+        final Collection<Location> locations = Lists.newArrayList(getLocations());
+        stop();
+        start(locations);
+    }
+
+    /**
+     * Sets the state of the Entity. Useful so that the GUI shows the correct icon.
+     *
+     * @param serviceUpState     Whether or not the entity is up.
+     * @param serviceStateActual The actual state of the entity.
+     */
+    private void setServiceState(final boolean serviceUpState, final Lifecycle serviceStateActual) {
+        sensors().set(SERVICE_UP, serviceUpState);
+        sensors().set(Attributes.SERVICE_STATE_ACTUAL, serviceStateActual);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/07942b12/test-framework/src/test/java/org/apache/brooklyn/test/framework/RelativeEntityTestCaseTest.java
----------------------------------------------------------------------
diff --git a/test-framework/src/test/java/org/apache/brooklyn/test/framework/RelativeEntityTestCaseTest.java b/test-framework/src/test/java/org/apache/brooklyn/test/framework/RelativeEntityTestCaseTest.java
new file mode 100644
index 0000000..c35b265
--- /dev/null
+++ b/test-framework/src/test/java/org/apache/brooklyn/test/framework/RelativeEntityTestCaseTest.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.brooklyn.test.framework;
+
+import static org.apache.brooklyn.test.Asserts.assertTrue;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntityInitializer;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.sensor.Sensor;
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
+import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.sensor.StaticSensor;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.entity.group.Cluster;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class RelativeEntityTestCaseTest extends BrooklynAppUnitTestSupport {
+
+    @Test
+    public void testParentAndChildScope() {
+        TestEntity parent = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .configure(BrooklynCampConstants.PLAN_ID, "parent-plan"));
+        TestEntity child = parent.addChild(EntitySpec.create(TestEntity.class)
+                .configure(BrooklynCampConstants.PLAN_ID, "child-plan"));
+
+        parent.sensors().set(TestEntity.NAME, "parent");
+        child.sensors().set(TestEntity.NAME, "child");
+
+        app.start(ImmutableList.of(app.newSimulatedLocation()));
+
+        TestCase testCase = app.createAndManageChild(EntitySpec.create(TestCase.class)
+                .child(relativeEntityTestCaseEntitySpec(parent, "child-plan", DslComponent.Scope.CHILD, "child"))
+                .child(relativeEntityTestCaseEntitySpec(child, "parent-plan", DslComponent.Scope.PARENT, "parent")));
+
+        testCase.start(app.getLocations());
+        assertTrue(testCase.sensors().get(Attributes.SERVICE_UP), "Test case did not pass: " + testCase);
+    }
+
+    @Test
+    public void testSiblingScope() {
+        TestEntity brother = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .configure(BrooklynCampConstants.PLAN_ID, "brother-plan"));
+        TestEntity sister = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .configure(BrooklynCampConstants.PLAN_ID, "sister-plan"));
+
+        brother.sensors().set(TestEntity.NAME, "brother");
+        sister.sensors().set(TestEntity.NAME, "sister");
+
+        app.start(ImmutableList.of(app.newSimulatedLocation()));
+
+        TestCase testCase = app.createAndManageChild(EntitySpec.create(TestCase.class)
+                .child(relativeEntityTestCaseEntitySpec(brother, "sister-plan", DslComponent.Scope.SIBLING, "sister"))
+                .child(relativeEntityTestCaseEntitySpec(sister, "brother-plan", DslComponent.Scope.SIBLING, "brother")));
+
+        testCase.start(app.getLocations());
+        assertTrue(testCase.sensors().get(Attributes.SERVICE_UP), "Test case did not pass: " + testCase);
+    }
+
+    @Test
+    public void testCombinationWithLoopOverGroupMembersTest() {
+        final String sensorName = TestEntity.NAME.getName();
+        final String sensorValue = "test-sensor-value";
+        EntityInitializer staticSensor = new StaticSensor<>(ConfigBag.newInstance(ImmutableMap.of(
+                StaticSensor.SENSOR_NAME, sensorName,
+                StaticSensor.STATIC_VALUE, sensorValue)));
+
+        // Application entities
+
+        EntitySpec<TestEntity> childSpec = EntitySpec.create(TestEntity.class)
+                .configure(BrooklynCampConstants.PLAN_ID, "child-plan")
+                .addInitializer(staticSensor);
+        EntitySpec<TestEntity> groupMemberSpec = EntitySpec.create(TestEntity.class)
+                .configure(BrooklynCampConstants.PLAN_ID, "group-member-plan")
+                .child(childSpec);
+        Entity cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
+                .configure(DynamicCluster.MEMBER_SPEC, groupMemberSpec)
+                .configure(Cluster.INITIAL_SIZE, 3));
+
+        // Start the cluster.
+        app.start(ImmutableList.of(app.newSimulatedLocation()));
+
+        LoopOverGroupMembersTestCase groupTest = app.createAndManageChild(EntitySpec.create(LoopOverGroupMembersTestCase.class)
+                .configure(LoopOverGroupMembersTestCase.TARGET_ENTITY, cluster)
+                .configure(LoopOverGroupMembersTestCase.TEST_SPEC, relativeEntityTestCaseEntitySpec(
+                        /* set by group-loop */ null, "child-plan", DslComponent.Scope.CHILD, sensorValue)));
+
+        groupTest.start(app.getLocations());
+
+        // Specifically check the result of the loop test.
+        assertTrue(groupTest.sensors().get(Attributes.SERVICE_UP), "Test case did not pass: " + groupTest);
+    }
+
+    private EntitySpec<RelativeEntityTestCase> relativeEntityTestCaseEntitySpec(
+            Entity testRoot, String targetEntityPlanId, DslComponent.Scope scope, String expectedSensorValue) {
+        EntitySpec<TestSensor> sensorTest = sensorHasValueTest(TestEntity.NAME, expectedSensorValue);
+
+        return EntitySpec.create(RelativeEntityTestCase.class)
+                .configure(RelativeEntityTestCase.TARGET_ENTITY, testRoot)
+                .configure(RelativeEntityTestCase.COMPONENT, new DslComponent(scope, targetEntityPlanId))
+                .child(sensorTest);
+    }
+
+    private EntitySpec<TestSensor> sensorHasValueTest(Sensor<?> sensorName, Object expectedValue) {
+        return EntitySpec.create(TestSensor.class)
+                .configure(TestSensor.SENSOR_NAME, sensorName.getName())
+                .configure(TestSensor.ASSERTIONS, ImmutableMap.of(
+                        TestFrameworkAssertions.EQUAL_TO, expectedValue));
+    }
+
+}