Test lifecycle phases of BasicStartable
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/379d6a1d Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/379d6a1d Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/379d6a1d Branch: refs/heads/master Commit: 379d6a1d5908dd25cccaf82e853ff9d7693734ed Parents: 614cbb2 Author: Sam Corbett <[email protected]> Authored: Thu Aug 6 16:01:36 2015 +0100 Committer: Sam Corbett <[email protected]> Committed: Thu Aug 6 16:01:57 2015 +0100 ---------------------------------------------------------------------- .../entity/basic/ServiceStateLogic.java | 7 +- .../entity/basic/BasicStartableTest.java | 33 +++++- .../basic/RecordingSensorEventListener.java | 115 +++++++++++++++++++ 3 files changed, 148 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/379d6a1d/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java b/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java index eff8e2d..3b1efbe 100644 --- a/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java +++ b/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java @@ -149,8 +149,11 @@ public class ServiceStateLogic { if (!Boolean.TRUE.equals(up) && !Boolean.TRUE.equals(Entities.isReadOnly(entity))) { // pause briefly to allow any recent problem-clearing processing to complete Stopwatch timer = Stopwatch.createStarted(); - boolean nowUp = Repeater.create().every(ValueResolver.REAL_QUICK_PERIOD).limitTimeTo(ValueResolver.PRETTY_QUICK_WAIT).until(entity, - EntityPredicates.attributeEqualTo(Attributes.SERVICE_UP, true)).run(); + boolean nowUp = Repeater.create() + .every(ValueResolver.REAL_QUICK_PERIOD) + .limitTimeTo(ValueResolver.PRETTY_QUICK_WAIT) + .until(entity, EntityPredicates.attributeEqualTo(Attributes.SERVICE_UP, true)) + .run(); if (nowUp) { log.debug("Had to wait "+Duration.of(timer)+" for "+entity+" "+Attributes.SERVICE_UP+" to be true before setting "+state); } else { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/379d6a1d/core/src/test/java/brooklyn/entity/basic/BasicStartableTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/basic/BasicStartableTest.java b/core/src/test/java/brooklyn/entity/basic/BasicStartableTest.java index b7d47e5..2f8db0c 100644 --- a/core/src/test/java/brooklyn/entity/basic/BasicStartableTest.java +++ b/core/src/test/java/brooklyn/entity/basic/BasicStartableTest.java @@ -21,13 +21,20 @@ package brooklyn.entity.basic; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import org.apache.brooklyn.entity.basic.RecordingSensorEventListener; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + import brooklyn.entity.Entity; import brooklyn.entity.proxying.EntitySpec; import brooklyn.location.Location; @@ -40,11 +47,6 @@ import brooklyn.test.entity.TestApplication; import brooklyn.test.entity.TestEntity; import brooklyn.util.collections.MutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - public class BasicStartableTest { private ManagementContext managementContext; @@ -141,6 +143,27 @@ public class BasicStartableTest { assertEqualsIgnoringOrder(entity.getLocations(), ImmutableSet.of()); assertNull(called.get()); } + + @Test + public void testTransitionsThroughLifecycles() throws Exception { + startable = app.addChild(EntitySpec.create(BasicStartable.class)); + RecordingSensorEventListener<Lifecycle> listener = new RecordingSensorEventListener<Lifecycle>(true); + managementContext.getSubscriptionContext(startable) + .subscribe(startable, Attributes.SERVICE_STATE_ACTUAL, listener); + + Entities.startManagement(startable); + app.start(ImmutableList.of(loc1)); + app.stop(); + + ArrayList<Lifecycle> expected = Lists.newArrayList( + Lifecycle.STARTING, + Lifecycle.RUNNING, + Lifecycle.STOPPING, + Lifecycle.STOPPED); + Iterable<Lifecycle> actual = listener.getEventValuesSortedByTimestamp(); + assertEquals(actual, expected, + "Expected=" + Iterables.toString(expected) + ", actual=" + Iterables.toString(actual)); + } private void assertEqualsIgnoringOrder(Iterable<? extends Object> col1, Iterable<? extends Object> col2) { assertEquals(Iterables.size(col1), Iterables.size(col2), "col2="+col1+"; col2="+col2); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/379d6a1d/core/src/test/java/org/apache/brooklyn/entity/basic/RecordingSensorEventListener.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/entity/basic/RecordingSensorEventListener.java b/core/src/test/java/org/apache/brooklyn/entity/basic/RecordingSensorEventListener.java new file mode 100644 index 0000000..067b7d4 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/entity/basic/RecordingSensorEventListener.java @@ -0,0 +1,115 @@ +/* + * 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.entity.basic; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import com.google.common.base.Function; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.primitives.Longs; + +import brooklyn.event.SensorEvent; +import brooklyn.event.SensorEventListener; + +/** + * An event listener that records each event and allows callers to access all values and + * all values sorted by event timestamp. + */ +public class RecordingSensorEventListener<T> implements SensorEventListener<T>, Iterable<SensorEvent<T>> { + + private final List<SensorEvent<T>> events = Lists.newCopyOnWriteArrayList(); + private final boolean suppressDuplicates; + private T lastValue; + + public RecordingSensorEventListener() { + this(false); + } + + public RecordingSensorEventListener(boolean suppressDuplicates) { + this.suppressDuplicates = suppressDuplicates; + } + + @Override + public void onEvent(SensorEvent<T> event) { + if (!suppressDuplicates || events.isEmpty() || !Objects.equals(lastValue, event.getValue())) { + events.add(event); + lastValue = event.getValue(); + } + } + + /** + * @return An immutable iterable of the recorded events. + */ + public Iterable<SensorEvent<T>> getEvents() { + return ImmutableList.copyOf(events); + } + + /** + * @return A live read-only view of recorded events. + */ + public Iterable<T> getEventValues() { + return FluentIterable.from(events) + .transform(new GetValueFunction<T>()); + } + + /** + * @return A static read-only view of event values sorted by the time at which they occurred. + */ + public Iterable<T> getEventValuesSortedByTimestamp() { + List<SensorEvent<T>> copy = Lists.newArrayList(events); + Collections.sort(copy, new EventTimestampComparator()); + return FluentIterable.from(copy) + .transform(new GetValueFunction<T>()); + } + + /** + * Clears all events recorded by the listener. + */ + public void clearEvents() { + this.events.clear(); + lastValue = null; + } + + @Override + public Iterator<SensorEvent<T>> iterator() { + return getEvents().iterator(); + } + + private static class GetValueFunction<T> implements Function<SensorEvent<T>, T> { + @Override + public T apply(SensorEvent<T> input) { + return input.getValue(); + } + } + + private static class EventTimestampComparator implements Comparator<SensorEvent<?>> { + @Override + public int compare(SensorEvent<?> o1, SensorEvent<?> o2) { + return Longs.compare(o1.getTimestamp(), o2.getTimestamp()); + } + } + +}
