brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [07/10] brooklyn-server git commit: tests for activity limits/depths
Date Sat, 22 Jul 2017 02:14:46 GMT
tests for activity limits/depths


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

Branch: refs/heads/master
Commit: 157c3858e62d3345ac81cefb093cae1d64480c43
Parents: 36fd318
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Thu Jul 6 23:43:01 2017 +0100
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Thu Jul 6 23:43:01 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/core/effector/AddEffector.java     |   2 +-
 .../apache/brooklyn/core/entity/Entities.java   |   4 +
 .../apache/brooklyn/util/core/task/Tasks.java   |  29 +-
 .../core/effector/SampleManyTasksEffector.java  |  23 +-
 .../rest/resources/ActivityResource.java        |   4 +-
 .../rest/resources/ActivityRestTest.java        | 344 +++++++++++++++++++
 .../rest/resources/ApplicationResourceTest.java |  13 +-
 .../rest/resources/EffectorUtilsRestTest.java   |   1 +
 .../rest/resources/ErrorResponseTest.java       |   2 -
 9 files changed, 403 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/157c3858/core/src/main/java/org/apache/brooklyn/core/effector/AddEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/AddEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/AddEffector.java
index 9590bcf..04c1364 100644
--- a/core/src/main/java/org/apache/brooklyn/core/effector/AddEffector.java
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/AddEffector.java
@@ -63,7 +63,7 @@ public class AddEffector implements EntityInitializer {
     
     public static final ConfigKey<Map<String,Object>> EFFECTOR_PARAMETER_DEFS
= new MapConfigKey<Object>(Object.class, "parameters");
 
-    final Effector<?> effector;
+    protected final Effector<?> effector;
     
     public AddEffector(Effector<?> effector) {
         this.effector = Preconditions.checkNotNull(effector, "effector");

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/157c3858/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java b/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
index 46a9cea..df904d3 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
@@ -480,6 +480,10 @@ public class Entities {
         out.flush();
     }
 
+    public static void dumpInfo(Task<?> t) {
+        Tasks.dumpInfo(t);
+    }
+    
     public static void dumpInfo(Enricher enr) {
         try {
             dumpInfo(enr, new PrintWriter(System.out), "", "  ");

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/157c3858/core/src/main/java/org/apache/brooklyn/util/core/task/Tasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/Tasks.java b/core/src/main/java/org/apache/brooklyn/util/core/task/Tasks.java
index 09116af..0677f74 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/task/Tasks.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/task/Tasks.java
@@ -18,6 +18,9 @@
  */
 package org.apache.brooklyn.util.core.task;
 
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Callable;
@@ -39,7 +42,6 @@ import org.apache.brooklyn.util.repeat.Repeater;
 import org.apache.brooklyn.util.time.CountdownTimer;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -492,4 +494,29 @@ public class Tasks {
         return "in "+timeout;
     }
 
+
+    public static void dumpInfo(Task<?> t) {
+        try {
+            dumpInfo(t, new PrintWriter(System.out), "", "  ");
+        } catch (IOException exc) {
+            // system.out throwing an exception is odd, so don't have IOException on signature
+            throw new RuntimeException(exc);
+        }
+    }
+    public static void dumpInfo(Task<?> t, Writer out) throws IOException {
+        dumpInfo(t, out, "", "  ");
+    }
+    public static void dumpInfo(Task<?> t, String currentIndentation, String tab) throws
IOException {
+        dumpInfo(t, new PrintWriter(System.out), currentIndentation, tab);
+    }
+    public static void dumpInfo(Task<?> t, Writer out, String currentIndentation, String
tab) throws IOException {
+        out.append(currentIndentation+t+": "+t.getStatusDetail(false)+"\n");
+
+        if (t instanceof HasTaskChildren) {
+            for (Task<?> child: ((HasTaskChildren)t).getChildren()) {
+                dumpInfo(child, out, currentIndentation+tab, tab);
+            }
+        }
+        out.flush();
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/157c3858/core/src/test/java/org/apache/brooklyn/core/effector/SampleManyTasksEffector.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/SampleManyTasksEffector.java
b/core/src/test/java/org/apache/brooklyn/core/effector/SampleManyTasksEffector.java
index 6cf4e2c..da36de3 100644
--- a/core/src/test/java/org/apache/brooklyn/core/effector/SampleManyTasksEffector.java
+++ b/core/src/test/java/org/apache/brooklyn/core/effector/SampleManyTasksEffector.java
@@ -1,12 +1,15 @@
 package org.apache.brooklyn.core.effector;
 
 import java.util.List;
+import java.util.Random;
 import java.util.concurrent.Callable;
 
 import org.apache.brooklyn.api.effector.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.mgmt.TaskAdaptable;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.effector.EffectorTasks.EffectorTaskFactory;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.core.config.ConfigBag;
@@ -39,11 +42,21 @@ services:
  */
 public class SampleManyTasksEffector extends AddEffector {
 
+    public static final ConfigKey<Integer> RANDOM_SEED = ConfigKeys.newIntegerConfigKey("random.seed");
+
     public SampleManyTasksEffector(ConfigBag params) {
         super(Effectors.effector(String.class, params.get(EFFECTOR_NAME)).name("eatand").impl(body(params)).build());
     }
 
+    public Effector<?> getEffector() {
+        return effector;
+    }
+    
     private static EffectorTaskFactory<String> body(ConfigBag params) {
+        Integer seed = params.get(RANDOM_SEED);
+        final Random random = seed!=null ? new Random(seed) : new Random();
+        
+        // NOTE: not nicely serializable
         return new EffectorTaskFactory<String>() {
             @Override
             public TaskAdaptable<String> newTask(Entity entity, Effector<String>
effector, ConfigBag parameters) {
@@ -53,13 +66,13 @@ public class SampleManyTasksEffector extends AddEffector {
                 List<Task<Object>> result = MutableList.of();
                 do {
                     TaskBuilder<Object> t = Tasks.builder();
-                    double x = Math.random();
-                    if (depth>4) x *= Math.random();
-                    if (depth>6) x *= Math.random();
+                    double x = random.nextDouble();
+                    if (depth>4) x *= random.nextDouble();
+                    if (depth>6) x *= random.nextDouble();
                     if (x<0.3) {
                         t.displayName("eat").body(new Callable<Object>() { public Object
call() { return "eat"; }});
                     } else if (x<0.6) {
-                        final Duration time = Duration.millis(Math.round(10*1000*Math.random()*Math.random()*Math.random()*Math.random()*Math.random()));
+                        final Duration time = Duration.millis(Math.round(10*1000*random.nextDouble()*random.nextDouble()*random.nextDouble()*random.nextDouble()*random.nextDouble()));
                         t.displayName("sleep").description("Sleeping "+time).body(new Callable<Object>()
{ public Object call() {
                             Tasks.setBlockingDetails("sleeping "+time);
                             Time.sleep(time);
@@ -78,7 +91,7 @@ public class SampleManyTasksEffector extends AddEffector {
                     }
                     result.add(t.build());
                     
-                } while (Math.random()<0.8);
+                } while (random.nextDouble()<0.8);
                 return result;
             }
         };

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/157c3858/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ActivityResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ActivityResource.java
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ActivityResource.java
index 2bc90ad..6e3c2eb 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ActivityResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ActivityResource.java
@@ -79,13 +79,13 @@ public class ActivityResource extends AbstractBrooklynRestResource implements
Ac
             return result;
         }
         Set<Task<?>> nextLayer = MutableSet.copyOf( ((HasTaskChildren) parentTask).getChildren()
);
-        outer: while (!nextLayer.isEmpty() && maxDepth-- != 0) {
+        outer: while (limit!=0 && !nextLayer.isEmpty() && maxDepth-- != 0)
{
             Set<Task<?>> thisLayer = nextLayer;
             nextLayer = MutableSet.of();
             for (final Task<?> childTask : thisLayer) {
                 TaskSummary wasThere = result.put(childTask.getId(), TaskTransformer.fromTask(ui.getBaseUriBuilder()).apply(childTask));
                 if (wasThere==null) {
-                    if (limit-- == 0) {
+                    if (--limit == 0) {
                         break outer;
                     }
                     if (childTask instanceof HasTaskChildren) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/157c3858/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ActivityRestTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ActivityRestTest.java
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ActivityRestTest.java
new file mode 100644
index 0000000..a5424f8
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ActivityRestTest.java
@@ -0,0 +1,344 @@
+/*
+ * 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.rest.resources;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.HasTaskChildren;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.effector.SampleManyTasksEffector;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
+import org.apache.brooklyn.core.mgmt.EntityManagementUtils.CreationResult;
+import org.apache.brooklyn.core.mgmt.internal.TestEntityWithEffectors;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.brooklyn.rest.domain.TaskSummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.http.HttpAsserts;
+import org.apache.brooklyn.util.time.CountdownTimer;
+import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Iterables;
+
+/** Tests {@link ActivityResource} and activity methods on {@link EntityResource} */
+public class ActivityRestTest extends BrooklynRestResourceTest {
+
+    private static final Logger log = LoggerFactory.getLogger(ActivityRestTest.class);
+    
+    /* a nice seed, initial run as follows;
+
+Task[eatand]@J90TKfIX: Waiting on Task[eat-sleep-rave-repeat]@QPa5o4kF
+  Task[eat-sleep-rave-repeat]@QPa5o4kF: Waiting on Task[rave]@yP9KjuWD
+    Task[rave]@yP9KjuWD: Waiting on Task[repeat]@Dd1AqB7Q
+      Task[repeat]@Dd1AqB7Q: Waiting on Task[repeat]@remQL5eD
+        Task[repeat]@remQL5eD: Waiting on Task[repeat]@g1ReP4BP
+          Task[sleep]@iV3iWg2N: Completed, result: slept 46ms
+          Task[eat]@fpIttX07: Completed, result: eat
+          Task[eat]@w6sxLefq: Completed, result: eat
+          Task[repeat]@g1ReP4BP: Waiting on Task[sleep]@zRTOQ4ak
+            Task[eat]@TvcdOUx7: Completed, result: eat
+            Task[rave]@yJndzNLf: Completed, result: raved with 1 tasks
+              Task[eat]@oiJ3eZZQ: Completed, result: eat
+            Task[sleep]@zRTOQ4ak: sleeping 74ms
+            Task[eat]@qoFRPEfM: Not submitted
+            Task[sleep]@fNX16uvi: Not submitted
+
+     */
+    private final int SEED = 1;
+    
+    private Entity entity;
+    private Effector<?> effector;
+
+    private Task<?> lastTask;
+    
+    @BeforeClass(alwaysRun = true)
+    public void setUp() throws Exception {
+        startServer();
+    }
+    
+    @BeforeMethod(alwaysRun = true)
+    public void setUpOneTest() throws Exception {
+        initEntity(SEED);
+    }
+
+    @SuppressWarnings("deprecation")
+    protected void initEntity(int seed) {
+        if (entity!=null) {
+            Entities.destroy(entity.getApplication());
+        }
+        
+        CreationResult<BasicApplication, Void> app = EntityManagementUtils.createStarting(getManagementContext(),
+            EntitySpec.create(BasicApplication.class)
+                .child(EntitySpec.create(TestEntityWithEffectors.class)) );
+        app.blockUntilComplete();
+        entity = Iterables.getOnlyElement( app.get().getChildren() );
+        
+        SampleManyTasksEffector manyTasksAdder = new SampleManyTasksEffector(ConfigBag.newInstance().configure(SampleManyTasksEffector.RANDOM_SEED,
seed));
+        effector = manyTasksAdder.getEffector();
+        manyTasksAdder.apply((org.apache.brooklyn.api.entity.EntityLocal) entity);
+    }
+
+    /** finds a good seed, in case the effector changes */
+    public static void main(String[] args) throws Exception {
+        ActivityRestTest me = new ActivityRestTest();
+        me.setUpClass();
+        int i=0;
+        do {
+            me.initEntity(i);
+            try {
+                log.info("Trying seed "+i+"...");
+                me.testGood(Duration.millis(200));
+                break;
+            } catch (Throwable e) {
+                log.info("  "+Exceptions.collapseText(e));
+                // e.printStackTrace();
+                // continue
+            }
+            i++;
+        } while (true);
+        Tasks.dumpInfo(me.lastTask);
+        log.info("Seed "+i+" is good ^");
+    }
+    
+    @Test
+    public void testGood() {
+        testGood(Duration.ONE_SECOND);
+    }
+    
+    void testGood(Duration timeout) {
+        lastTask = entity.invoke(effector, null);
+        Task<?> leaf = waitForCompletedDescendantWithChildAndSibling(lastTask, lastTask,
CountdownTimer.newInstanceStarted(timeout), 0);
+        Assert.assertTrue(depthOf(leaf)>=4, "Not deep enough: "+depthOf(leaf));
+    }
+    
+    @Test
+    public void testGetActivity() {
+        Task<?> t = entity.invoke(effector, null);
+        
+        Response response = client().path("/activities/"+t.getId())
+            .accept(MediaType.APPLICATION_JSON)
+            .get();
+        assertHealthy(response);
+        TaskSummary task = response.readEntity(TaskSummary.class);
+        Assert.assertEquals(task.getId(), t.getId());
+    }
+    
+    @Test
+    public void testGetActivitiesChildren() {
+        Task<?> t = entity.invoke(effector, null);
+        Task<?> leaf = waitForCompletedDescendantWithChildAndSibling(t, t, CountdownTimer.newInstanceStarted(Duration.ONE_SECOND),
0);
+        
+        Response response = client().path("/activities/"+leaf.getSubmittedByTask().getId()+"/children")
+            .accept(MediaType.APPLICATION_JSON)
+            .get();
+        assertHealthy(response);
+        List<TaskSummary> tasks = response.readEntity(new GenericType<List<TaskSummary>>()
{});
+        log.info("Tasks children: "+tasks.size());
+        Assert.assertTrue(tasksContain(tasks, leaf), "tasks should have included leaf "+leaf+";
was "+tasks);
+    }
+    
+    @Test
+    public void testGetActivitiesRecursiveAndWithLimit() {
+        Task<?> t = entity.invoke(effector, null);
+        Task<?> leaf = waitForCompletedDescendantWithChildAndSibling(t, t, CountdownTimer.newInstanceStarted(Duration.ONE_SECOND),
0);
+        Task<?> leafParent = leaf.getSubmittedByTask();
+        Task<?> leafGrandparent = leafParent.getSubmittedByTask();
+        
+        Response response = client().path("/activities/"+leafGrandparent.getId()+"/children")
+            .accept(MediaType.APPLICATION_JSON)
+            .get();
+        assertHealthy(response);
+        List<TaskSummary> tasksL = response.readEntity(new GenericType<List<TaskSummary>>()
{});
+        Assert.assertFalse(tasksContain(tasksL, leaf), "non-recursive tasks should not have
included leaf "+leaf+"; was "+tasksL);
+        Assert.assertTrue(tasksContain(tasksL, leafParent), "non-recursive tasks should have
included leaf parent "+leafParent+"; was "+tasksL);
+        Assert.assertFalse(tasksContain(tasksL, leafGrandparent), "non-recursive tasks should
not have included leaf grandparent "+leafGrandparent+"; was "+tasksL);
+        
+        response = client().path("/activities/"+leafGrandparent.getId()+"/children/recurse")
+            .accept(MediaType.APPLICATION_JSON)
+            .get();
+        assertHealthy(response);
+        Map<String,TaskSummary> tasks = response.readEntity(new GenericType<Map<String,TaskSummary>>()
{});
+        Assert.assertTrue(tasksContain(tasks, leaf), "recursive tasks should have included
leaf "+leaf+"; was "+tasks);
+        Assert.assertTrue(tasksContain(tasks, leafParent), "recursive tasks should have included
leaf parent "+leafParent+"; was "+tasks);
+        Assert.assertFalse(tasksContain(tasks, leafGrandparent), "recursive tasks should
not have included leaf grandparent "+leafGrandparent+"; was "+tasks);
+        
+        response = client().path("/activities/"+leafGrandparent.getId()+"/children/recurse")
+            .query("maxDepth", 1)
+            .accept(MediaType.APPLICATION_JSON)
+            .get();
+        assertHealthy(response);
+        tasks = response.readEntity(new GenericType<Map<String,TaskSummary>>()
{});
+        Assert.assertFalse(tasksContain(tasks, leaf), "depth 1 recursive tasks should nont
have included leaf "+leaf+"; was "+tasks);
+        Assert.assertTrue(tasksContain(tasks, leafParent), "depth 1 recursive tasks should
have included leaf parent "+leafParent+"; was "+tasks);
+
+        response = client().path("/activities/"+leafGrandparent.getId()+"/children/recurse")
+            .query("maxDepth", 2)
+            .accept(MediaType.APPLICATION_JSON)
+            .get();
+        assertHealthy(response);
+        tasks = response.readEntity(new GenericType<Map<String,TaskSummary>>()
{});
+        Assert.assertTrue(tasksContain(tasks, leaf), "depth 2 recursive tasks should have
included leaf "+leaf+"; was "+tasks);
+        Assert.assertTrue(tasksContain(tasks, leafParent), "depth 2 recursive tasks should
have included leaf parent "+leafParent+"; was "+tasks);
+        Assert.assertFalse(tasksContain(tasks, leafGrandparent), "depth 2 recursive tasks
should not have included leaf grandparent "+leafGrandparent+"; was "+tasks);
+        
+        Assert.assertTrue(children(leafGrandparent).size() >= 2, "children: "+children(leafGrandparent));
+        response = client().path("/activities/"+leafGrandparent.getId()+"/children/recurse")
+            .query("limit", children(leafGrandparent).size())
+            .accept(MediaType.APPLICATION_JSON)
+            .get();
+        assertHealthy(response);
+        tasks = response.readEntity(new GenericType<Map<String,TaskSummary>>()
{});
+        Assert.assertEquals(tasks.size(), children(leafGrandparent).size());
+        Assert.assertTrue(tasksContain(tasks, leafParent), "count limited recursive tasks
should have included leaf parent "+leafParent+"; was "+tasks);
+        Assert.assertFalse(tasksContain(tasks, leaf), "count limited recursive tasks should
not have included leaf "+leaf+"; was "+tasks);
+        
+        response = client().path("/activities/"+leafGrandparent.getId()+"/children/recurse")
+            .query("limit", children(leafGrandparent).size()+1)
+            .accept(MediaType.APPLICATION_JSON)
+            .get();
+        assertHealthy(response);
+        tasks = response.readEntity(new GenericType<Map<String,TaskSummary>>()
{});
+        Assert.assertEquals(tasks.size(), children(leafGrandparent).size()+1);
+        tasks = response.readEntity(new GenericType<Map<String,TaskSummary>>()
{});
+        Assert.assertTrue(tasksContain(tasks, leafParent), "count+1 limited recursive tasks
should have included leaf parent "+leafParent+"; was "+tasks);
+        Assert.assertTrue(tasksContain(tasks, leaf), "count+1 limited recursive tasks should
have included leaf "+leaf+"; was "+tasks);
+    }
+
+    private boolean tasksContain(Map<String, TaskSummary> tasks, Task<?> leaf)
{
+        return tasks.keySet().contains(leaf.getId());
+    }
+
+    private List<Task<?>> children(Task<?> t) {
+        return MutableList.copyOf( ((HasTaskChildren)t).getChildren() );
+    }
+
+    @Test
+    public void testGetEntityActivitiesAndWithLimit() {
+        Task<?> t = entity.invoke(effector, null);
+        Task<?> leaf = waitForCompletedDescendantWithChildAndSibling(t, t, CountdownTimer.newInstanceStarted(Duration.ONE_SECOND),
0);
+        
+        Response response = client().path("/applications/"+entity.getApplicationId()+
+                "/entities/"+entity.getId()+"/activities")
+            .accept(MediaType.APPLICATION_JSON)
+            .get();
+        assertHealthy(response);
+        List<TaskSummary> tasks = response.readEntity(new GenericType<List<TaskSummary>>()
{});
+        log.info("Tasks now: "+tasks.size());
+        Assert.assertTrue(tasks.size() > 4, "tasks should have been big; was "+tasks);
+        Assert.assertTrue(tasksContain(tasks, leaf), "tasks should have included leaf "+leaf+";
was "+tasks);
+        
+        response = client().path("/applications/"+entity.getApplicationId()+
+            "/entities/"+entity.getId()+"/activities")
+            .query("limit", 3)
+            .accept(MediaType.APPLICATION_JSON)
+            .get();
+        assertHealthy(response);
+        tasks = response.readEntity(new GenericType<List<TaskSummary>>() {});
+        log.info("Tasks limited: "+tasks.size());
+        Assert.assertEquals(tasks.size(), 3, "tasks should have been limited; was "+tasks);
+        Assert.assertFalse(tasksContain(tasks, leaf), "tasks should not have included leaf
"+leaf+"; was "+tasks);
+    }
+
+    private void assertHealthy(Response response) {
+        if (!HttpAsserts.isHealthyStatusCode(response.getStatus())) {
+            Asserts.fail("Bad response: "+response.getStatus()+" "+response.readEntity(String.class));
+        }
+    }
+
+    private static boolean tasksContain(List<TaskSummary> tasks, Task<?> leaf)
{
+        for (TaskSummary ts: tasks) {
+            if (ts.getId().equals(leaf.getId())) return true;
+        }
+        return false;
+    }
+
+    private int depthOf(Task<?> t) {
+        int depth = -1;
+        while (t!=null) {
+            t = t.getSubmittedByTask();
+            depth++;
+        }
+        return depth;
+    }
+    
+    private Task<?> waitForCompletedDescendantWithChildAndSibling(Task<?> tRoot,
Task<?> t, CountdownTimer timer, int depthSoFar) {
+        while (timer.isLive()) {
+            Iterable<Task<?>> children = ((HasTaskChildren)t).getChildren();
+            Iterator<Task<?>> ci = children.iterator();
+            Task<?> bestFinishedDescendant = null;
+            while (ci.hasNext()) {
+                Task<?> tc = ci.next();
+                Task<?> finishedDescendant = waitForCompletedDescendantWithChildAndSibling(tRoot,
tc, timer, depthSoFar+1);
+                if (depthOf(finishedDescendant) > depthOf(bestFinishedDescendant)) {
+                    bestFinishedDescendant = finishedDescendant;
+                }
+                int finishedDescendantDepth = depthOf(bestFinishedDescendant);
+                // log.info("finished "+tc+", depth "+finishedDescendantDepth);
+                if (finishedDescendantDepth < 2) {
+                    if (ci.hasNext()) continue;
+                    throw new IllegalStateException("not deep enough: "+finishedDescendantDepth);
+                }
+                if (finishedDescendantDepth == depthSoFar+1) {
+                    // child completed; now check we complete soon, and assert we have siblings
+                    if (ci.hasNext()) continue;
+                    if (!t.blockUntilEnded(timer.getDurationRemaining())) {
+                        Entities.dumpInfo(tRoot);
+                        // log.info("Incomplete after "+t+": "+t.getStatusDetail(false));
+                        throw Exceptions.propagate( new TimeoutException("parent didn't complete
after child depth "+finishedDescendantDepth) );
+                    }
+                }
+                if (finishedDescendantDepth == depthSoFar+2) {
+                    if (Iterables.size(children)<2) {
+                        Entities.dumpInfo(tRoot);
+                        throw new IllegalStateException("finished child's parent has no sibling");
+                    }
+                }
+                
+                return bestFinishedDescendant;
+            }
+            Thread.yield();
+            
+            // leaf nodeƄ
+            if (t.isDone()) return t;
+        }
+        throw Exceptions.propagate( new TimeoutException("expired waiting for children")
);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/157c3858/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
index 277fd32..76c08e8 100644
--- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
@@ -32,7 +32,11 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeoutException;
 
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 
@@ -71,7 +75,7 @@ import org.apache.brooklyn.util.collections.CollectionFunctionals;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.http.HttpAsserts;
 import org.apache.brooklyn.util.text.Strings;
-import org.apache.brooklyn.util.time.Duration;
+import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.http.HttpHeaders;
 import org.apache.http.entity.ContentType;
 import org.slf4j.Logger;
@@ -87,13 +91,6 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.core.GenericType;
-import javax.ws.rs.core.MultivaluedHashMap;
-
-import org.apache.cxf.jaxrs.client.WebClient;
-
 @Test(singleThreaded = true,
         // by using a different suite name we disallow interleaving other tests between the
methods of this test class, which wrecks the test fixtures
         suiteName = "ApplicationResourceTest")

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/157c3858/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EffectorUtilsRestTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EffectorUtilsRestTest.java
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EffectorUtilsRestTest.java
index 5fe729d..d7157fd 100644
--- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EffectorUtilsRestTest.java
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EffectorUtilsRestTest.java
@@ -85,6 +85,7 @@ public class EffectorUtilsRestTest extends BrooklynAppUnitTestSupport {
         } catch (InterruptedException|ExecutionException e) {
             throw Exceptions.propagate(e);
         }
+        log.debug("Result description: "+summary.getDescription());
         assertEquals(
                 summary.getDescription(),
                 "Invoking effector resetPassword on "+TestEntityWithEffectors.class.getSimpleName()+":"+entity.getId().substring(0,4)

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/157c3858/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
index 27d8ea5..af5007d 100644
--- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
@@ -23,7 +23,6 @@ import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
 import java.io.IOException;
-import java.io.InputStream;
 
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -36,7 +35,6 @@ import org.apache.brooklyn.rest.domain.PolicySummary;
 import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
 import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
 import org.apache.brooklyn.rest.testing.mocks.RestMockSimplePolicy;
-import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 


Mime
View raw message