You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sv...@apache.org on 2016/11/16 06:44:18 UTC

[3/6] brooklyn-server git commit: App defaults to requiring all children up

App defaults to requiring all children up


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

Branch: refs/heads/master
Commit: 555d59a8dbf62b5510ca365e417466ba9cc3a567
Parents: 9b24f7d
Author: Aled Sage <al...@gmail.com>
Authored: Fri Nov 4 16:25:04 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Mon Nov 14 11:09:54 2016 +0000

----------------------------------------------------------------------
 .../core/entity/AbstractApplication.java        |   5 +-
 .../core/entity/StartableApplication.java       |  14 ++
 .../entity/ApplicationLifecycleStateTest.java   | 216 +++++++++++++++++++
 .../core/entity/trait/FailingEntityImpl.java    |  16 ++
 .../util/collections/CollectionFunctionals.java |  14 +-
 .../collections/CollectionFunctionalsTest.java  |  36 +++-
 6 files changed, 298 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/555d59a8/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java
index 86c4cc4..400999e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java
@@ -128,7 +128,10 @@ public abstract class AbstractApplication extends AbstractEntity implements Star
         super.initEnrichers();
 
         // default app logic; easily overridable by adding a different enricher with the same tag
-        ServiceStateLogic.newEnricherFromChildren().checkChildrenAndMembers().addTo(this);
+        ServiceStateLogic.newEnricherFromChildren().checkChildrenAndMembers()
+                .configure(ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers.UP_QUORUM_CHECK, config().get(UP_QUORUM_CHECK))
+                .configure(ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers.RUNNING_QUORUM_CHECK, config().get(RUNNING_QUORUM_CHECK))
+                .addTo(this);
         ServiceStateLogic.ServiceNotUpLogic.updateNotUpIndicator(this, Attributes.SERVICE_STATE_ACTUAL, "Application created but not yet started, at "+Time.makeDateString());
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/555d59a8/core/src/main/java/org/apache/brooklyn/core/entity/StartableApplication.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/StartableApplication.java b/core/src/main/java/org/apache/brooklyn/core/entity/StartableApplication.java
index bdd4f2f..3bcde3c 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/StartableApplication.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/StartableApplication.java
@@ -20,12 +20,26 @@ package org.apache.brooklyn.core.entity;
 
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigInheritance;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.util.collections.QuorumCheck;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
 
 public interface StartableApplication extends Application, Startable {
     
+    ConfigKey<QuorumCheck> UP_QUORUM_CHECK = ConfigKeys.builder(QuorumCheck.class, "quorum.up")
+            .description("Logic for checking whether this service is up, based on children and members, defaulting to all must be up")
+            .defaultValue(QuorumCheck.QuorumChecks.all())
+            .runtimeInheritance(BasicConfigInheritance.NOT_REINHERITED)
+            .build();
+    
+    ConfigKey<QuorumCheck> RUNNING_QUORUM_CHECK = ConfigKeys.builder(QuorumCheck.class, "quorum.running") 
+            .description("Logic for checking whether this service is healthy, based on children and members running, defaulting to requiring none to be ON-FIRE")
+            .defaultValue(QuorumCheck.QuorumChecks.all())
+            .runtimeInheritance(BasicConfigInheritance.NOT_REINHERITED)
+            .build();
+
     ConfigKey<Boolean> DESTROY_ON_STOP = ConfigKeys.newBooleanConfigKey("application.stop.shouldDestroy",
         "Whether the app should be removed from management after a successful stop (if it is a root); "
         + "true by default.", true);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/555d59a8/core/src/test/java/org/apache/brooklyn/core/entity/ApplicationLifecycleStateTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/ApplicationLifecycleStateTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/ApplicationLifecycleStateTest.java
new file mode 100644
index 0000000..70057d4
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/ApplicationLifecycleStateTest.java
@@ -0,0 +1,216 @@
+/*
+ * 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.core.entity;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
+import org.apache.brooklyn.core.entity.trait.FailingEntity;
+import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport;
+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.collections.CollectionFunctionals;
+import org.apache.brooklyn.util.collections.QuorumCheck;
+import org.apache.brooklyn.util.core.task.ValueResolver;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+@Test
+public class ApplicationLifecycleStateTest extends BrooklynMgmtUnitTestSupport {
+
+    public void testHappyPathEmptyApp() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class));
+        
+        app.start(ImmutableList.<Location>of());
+        assertUpAndRunningEventually(app);
+    }
+    
+    public void testHappyPathWithChild() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .child(EntitySpec.create(TestEntity.class)));
+        
+        app.start(ImmutableList.<Location>of());
+        assertUpAndRunningEventually(app);
+    }
+    
+    public void testOnlyChildFailsToStartCausesAppToFail() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .child(EntitySpec.create(FailingEntity.class)
+                        .configure(FailingEntity.FAIL_ON_START, true)));
+        FailingEntity child = (FailingEntity) Iterables.get(app.getChildren(), 0);
+        
+        startAndAssertException(app, ImmutableList.<Location>of());
+        assertHealthEventually(child, Lifecycle.ON_FIRE, false);
+        assertHealthEventually(app, Lifecycle.ON_FIRE, false);
+    }
+    
+    public void testSomeChildFailsOnStartCausesAppToFail() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .child(EntitySpec.create(TestEntity.class))
+                .child(EntitySpec.create(FailingEntity.class)
+                        .configure(FailingEntity.FAIL_ON_START, true)));
+        
+        startAndAssertException(app, ImmutableList.<Location>of());
+        assertHealthEventually(app, Lifecycle.ON_FIRE, false);
+    }
+    
+    public void testOnlyChildFailsToStartThenRecoversCausesAppToRecover() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .child(EntitySpec.create(FailingEntity.class)
+                        .configure(FailingEntity.FAIL_ON_START, true)));
+        FailingEntity child = (FailingEntity) Iterables.get(app.getChildren(), 0);
+        
+        startAndAssertException(app, ImmutableList.<Location>of());
+        assertHealthEventually(app, Lifecycle.ON_FIRE, false);
+        
+        child.sensors().set(Attributes.SERVICE_UP, true);
+        child.sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+        assertUpAndRunningEventually(app);
+    }
+    
+    public void testSomeChildFailsToStartThenRecoversCausesAppToRecover() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .child(EntitySpec.create(TestEntity.class))
+                .child(EntitySpec.create(FailingEntity.class)
+                        .configure(FailingEntity.FAIL_ON_START, true)));
+        FailingEntity child = (FailingEntity) Iterables.find(app.getChildren(), Predicates.instanceOf(FailingEntity.class));
+        
+        startAndAssertException(app, ImmutableList.<Location>of());
+        assertHealthEventually(app, Lifecycle.ON_FIRE, false);
+        
+        child.sensors().set(Attributes.SERVICE_UP, true);
+        child.sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+        assertUpAndRunningEventually(app);
+    }
+    
+    public void testStartsThenOnlyChildFailsCausesAppToFail() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .child(EntitySpec.create(TestEntity.class)));
+        TestEntity child = (TestEntity) Iterables.get(app.getChildren(), 0);
+        
+        app.start(ImmutableList.<Location>of());
+        assertUpAndRunningEventually(app);
+
+        ServiceStateLogic.ServiceNotUpLogic.updateNotUpIndicator(child, "myIndicator", "Simulate not-up of child");
+        assertHealthEventually(app, Lifecycle.ON_FIRE, false);
+    }
+
+    public void testStartsThenSomeChildFailsCausesAppToFail() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .child(EntitySpec.create(TestEntity.class))
+                .child(EntitySpec.create(TestEntity.class)));
+        TestEntity child = (TestEntity) Iterables.get(app.getChildren(), 0);
+        
+        app.start(ImmutableList.<Location>of());
+        assertUpAndRunningEventually(app);
+
+        ServiceStateLogic.ServiceNotUpLogic.updateNotUpIndicator(child, "myIndicator", "Simulate not-up of child");
+        assertHealthEventually(app, Lifecycle.ON_FIRE, false);
+    }
+
+    public void testChildFailuresOnStartButWithQuorumCausesAppToSucceed() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .configure(StartableApplication.UP_QUORUM_CHECK, QuorumCheck.QuorumChecks.atLeastOne())
+                .configure(StartableApplication.RUNNING_QUORUM_CHECK, QuorumCheck.QuorumChecks.atLeastOne())
+                .child(EntitySpec.create(TestEntity.class))
+                .child(EntitySpec.create(FailingEntity.class)
+                        .configure(FailingEntity.FAIL_ON_START, true)));
+        
+        startAndAssertException(app, ImmutableList.<Location>of());
+        assertUpAndRunningEventually(app);
+    }
+    
+    public void testStartsThenChildFailsButWithQuorumCausesAppToSucceed() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .configure(StartableApplication.UP_QUORUM_CHECK, QuorumCheck.QuorumChecks.atLeastOne())
+                .configure(StartableApplication.RUNNING_QUORUM_CHECK, QuorumCheck.QuorumChecks.atLeastOne())
+                .child(EntitySpec.create(TestEntity.class))
+                .child(EntitySpec.create(TestEntity.class)));
+        TestEntity child = (TestEntity) Iterables.get(app.getChildren(), 0);
+        
+        app.start(ImmutableList.<Location>of());
+        assertUpAndRunningEventually(app);
+
+        ServiceStateLogic.ServiceNotUpLogic.updateNotUpIndicator(child, "myIndicator", "Simulate not-up of child");
+        assertHealthContinually(app, Lifecycle.RUNNING, true);
+    }
+
+    public void testStartsThenChildFailsButWithQuorumCausesAppToStayHealthy() throws Exception {
+        TestApplication app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .configure(StartableApplication.UP_QUORUM_CHECK, QuorumCheck.QuorumChecks.atLeastOne())
+                .configure(StartableApplication.RUNNING_QUORUM_CHECK, QuorumCheck.QuorumChecks.atLeastOne())
+                .child(EntitySpec.create(TestEntity.class))
+                .child(EntitySpec.create(TestEntity.class)));
+        TestEntity child = (TestEntity) Iterables.get(app.getChildren(), 0);
+        
+        app.start(ImmutableList.<Location>of());
+        assertUpAndRunningEventually(app);
+
+        ServiceStateLogic.ServiceNotUpLogic.updateNotUpIndicator(child, "myIndicator", "Simulate not-up of child");
+        assertUpAndRunningEventually(app);
+    }
+
+    private void assertHealthEventually(Entity entity, Lifecycle expectedState, Boolean expectedUp) {
+        EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, expectedState);
+        EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, expectedUp);
+    }
+    
+    private void assertHealthContinually(Entity entity, Lifecycle expectedState, Boolean expectedUp) {
+        // short wait, so unit tests don't take ages
+        Map<String, ?> flags = ImmutableMap.of("timeout", ValueResolver.REAL_QUICK_WAIT);
+        EntityAsserts.assertAttributeEqualsContinually(flags, entity, Attributes.SERVICE_STATE_ACTUAL, expectedState);
+        EntityAsserts.assertAttributeEqualsContinually(flags, entity, Attributes.SERVICE_UP, expectedUp);
+    }
+    
+    private void assertUpAndRunningEventually(Entity entity) {
+        try {
+            EntityAsserts.assertAttributeEventually(entity, Attributes.SERVICE_NOT_UP_INDICATORS, CollectionFunctionals.<String>mapEmptyOrNull());
+            EntityAsserts.assertAttributeEventually(entity, ServiceStateLogic.SERVICE_PROBLEMS, CollectionFunctionals.<String>mapEmptyOrNull());
+            EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+            EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
+        } catch (Throwable t) {
+            Entities.dumpInfo(entity);
+            String err = "(Dumped entity info - see log); entity=" + entity + "; " + 
+                    "state=" + entity.sensors().get(Attributes.SERVICE_STATE_ACTUAL) + "; " + 
+                    "up="+entity.sensors().get(Attributes.SERVICE_UP) + "; " +
+                    "notUpIndicators="+entity.sensors().get(Attributes.SERVICE_NOT_UP_INDICATORS) + "; " +
+                    "serviceProblems="+entity.sensors().get(Attributes.SERVICE_PROBLEMS);
+            throw new AssertionError(err, t);
+        }
+    }
+    
+    private void startAndAssertException(TestApplication app, Collection<? extends Location> locs) {
+        try {
+            app.start(locs);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureContains(e, "Error invoking start");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/555d59a8/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java b/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java
index 4a29092..6327c65 100644
--- a/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java
@@ -23,6 +23,8 @@ import java.util.Collection;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
 import org.apache.brooklyn.core.test.entity.TestEntityImpl;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
@@ -37,6 +39,10 @@ public class FailingEntityImpl extends TestEntityImpl implements FailingEntity {
     public void start(Collection<? extends Location> locs) {
         getConfig(LISTENER).onEvent(this, "start", new Object[] {locs});
         if (getConfig(FAIL_ON_START) || (getConfig(FAIL_ON_START_CONDITION) != null && getConfig(FAIL_ON_START_CONDITION).apply(this))) {
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+            sensors().set(SERVICE_UP, false);
+            ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
+            
             callHistory.add("start");
             getConfig(EXEC_ON_FAILURE).apply(this);
             throw fail("Simulating entity start failure for test");
@@ -48,6 +54,10 @@ public class FailingEntityImpl extends TestEntityImpl implements FailingEntity {
     public void stop() {
         getConfig(LISTENER).onEvent(this, "stop", new Object[0]);
         if (getConfig(FAIL_ON_STOP) || (getConfig(FAIL_ON_STOP_CONDITION) != null && getConfig(FAIL_ON_STOP_CONDITION).apply(this))) {
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPING);
+            sensors().set(SERVICE_UP, false);
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPED);
+            
             callHistory.add("stop");
             getConfig(EXEC_ON_FAILURE).apply(this);
             throw fail("Simulating entity stop failure for test");
@@ -59,6 +69,12 @@ public class FailingEntityImpl extends TestEntityImpl implements FailingEntity {
     public void restart() {
         getConfig(LISTENER).onEvent(this, "restart", new Object[0]);
         if (getConfig(FAIL_ON_RESTART) || (getConfig(FAIL_ON_RESTART_CONDITION) != null && getConfig(FAIL_ON_RESTART_CONDITION).apply(this))) {
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPING);
+            sensors().set(SERVICE_UP, false);
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPED);
+            ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+            ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE);
+
             callHistory.add("restart");
             getConfig(EXEC_ON_FAILURE).apply(this);
             throw fail("Simulating entity restart failure for test");

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/555d59a8/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionFunctionals.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionFunctionals.java b/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionFunctionals.java
index 91f53f9..a264b7e 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionFunctionals.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionFunctionals.java
@@ -152,14 +152,26 @@ public class CollectionFunctionals {
         return sizeEquals(0);
     }
 
+    public static Predicate<Iterable<?>> emptyOrNull() {
+        return Predicates.or(Predicates.isNull(), sizeEquals(0));
+    }
+
     public static Predicate<Iterable<?>> notEmpty() {
-        return Predicates.not(empty());
+        return Predicates.not(emptyOrNull());
     }
 
     public static <K> Predicate<Map<K,?>> mapSizeEquals(int targetSize) {
         return Predicates.compose(Predicates.equalTo(targetSize), CollectionFunctionals.<K>mapSize());
     }
 
+    public static <K> Predicate<Map<K,?>> mapEmptyOrNull() {
+        return Predicates.<Map<K,?>>or(Predicates.isNull(), CollectionFunctionals.<K>mapSizeEquals(0));
+    }
+
+    public static <K> Predicate<Map<K,?>> mapNotEmpty() {
+        return Predicates.not(CollectionFunctionals.<K>mapEmptyOrNull());
+    }
+
     public static <T,I extends Iterable<T>> Function<I, List<T>> limit(final int max) {
         return new LimitFunction<T,I>(max);
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/555d59a8/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java
index 1ce4015..cc5a3a9 100644
--- a/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java
+++ b/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java
@@ -18,7 +18,6 @@
  */
 package org.apache.brooklyn.util.collections;
 
-import org.apache.brooklyn.util.collections.CollectionFunctionals;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -51,6 +50,41 @@ public class CollectionFunctionalsTest {
     }
 
     @Test
+    public void testListEmpty() {
+        Assert.assertFalse(CollectionFunctionals.empty().apply(null));
+        Assert.assertTrue(CollectionFunctionals.empty().apply(ImmutableList.of()));
+        Assert.assertFalse(CollectionFunctionals.empty().apply(ImmutableList.of("x")));
+    }
+
+    @Test
+    public void testListEmptyOrNull() {
+        Assert.assertTrue(CollectionFunctionals.emptyOrNull().apply(null));
+        Assert.assertTrue(CollectionFunctionals.emptyOrNull().apply(ImmutableList.of()));
+        Assert.assertFalse(CollectionFunctionals.emptyOrNull().apply(ImmutableList.of("x")));
+    }
+
+    @Test
+    public void testListNotEmpty() {
+        Assert.assertFalse(CollectionFunctionals.notEmpty().apply(null));
+        Assert.assertFalse(CollectionFunctionals.notEmpty().apply(ImmutableList.of()));
+        Assert.assertTrue(CollectionFunctionals.notEmpty().apply(ImmutableList.of("x")));
+    }
+
+    @Test
+    public void testMapEmptyOrNull() {
+        Assert.assertTrue(CollectionFunctionals.mapEmptyOrNull().apply(null));
+        Assert.assertTrue(CollectionFunctionals.mapEmptyOrNull().apply(ImmutableMap.of()));
+        Assert.assertFalse(CollectionFunctionals.<String>mapEmptyOrNull().apply(ImmutableMap.of("mykey", "myval")));
+    }
+
+    @Test
+    public void testMapNotEmpty() {
+        Assert.assertEquals(CollectionFunctionals.mapNotEmpty().apply(null), false);
+        Assert.assertEquals(CollectionFunctionals.mapNotEmpty().apply(ImmutableMap.of()), false);
+        Assert.assertEquals(CollectionFunctionals.<String>mapNotEmpty().apply(ImmutableMap.of("mykey", "myval")), true);
+    }
+
+    @Test
     public void testFirstElement() {
         Assert.assertEquals(CollectionFunctionals.firstElement().apply(null), null);
         Assert.assertEquals(CollectionFunctionals.firstElement().apply(ImmutableList.of("a")), "a");