Repository: brooklyn-server
Updated Branches:
  refs/heads/master 092edf1eb -> 007c4341f


Aggregator: support min/max

Extracts utility functions into MathAggregatorFunctions.

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

Branch: refs/heads/master
Commit: c15a64ba584b5a6d9ac60a37bfcae08ae8462ce7
Parents: 092edf1
Author: Aled Sage <[email protected]>
Authored: Wed Aug 2 14:39:14 2017 +0100
Committer: Aled Sage <[email protected]>
Committed: Wed Aug 2 14:39:14 2017 +0100

----------------------------------------------------------------------
 .../camp/brooklyn/AggregatorYamlTest.java       |  54 +++++
 .../brooklyn/enricher/stock/Aggregator.java     |  29 ++-
 .../brooklyn/enricher/stock/Enrichers.java      |  36 +++-
 .../enricher/stock/MathAggregatorFunctions.java | 203 +++++++++++++++++++
 .../stock/MathAggregatorFunctionsTest.java      | 118 +++++++++++
 5 files changed, 425 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c15a64ba/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AggregatorYamlTest.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AggregatorYamlTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AggregatorYamlTest.java
index b5dadf8..578aab7 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AggregatorYamlTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AggregatorYamlTest.java
@@ -100,6 +100,60 @@ public class AggregatorYamlTest extends AbstractYamlTest {
     }
     
     @Test
+    public void testMin() throws Exception {
+        Entity app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  brooklyn.children:",
+                "    - type: " + TestEntity.class.getName(),
+                "    - type: " + TestEntity.class.getName(),
+                "  brooklyn.enrichers:",
+                "    - type: " + Aggregator.class.getName(),
+                "      brooklyn.config:",
+                "        "+Aggregator.SOURCE_SENSOR.getName()+": myVal",
+                "        "+Aggregator.TARGET_SENSOR.getName()+": myResult",
+                "        "+Aggregator.TRANSFORMATION_UNTYPED.getName()+": 
min");
+        Entity child1 = Iterables.get(app.getChildren(), 0);
+        Entity child2 = Iterables.get(app.getChildren(), 1);
+        
+        child1.sensors().set(myVal, 3d);
+        EntityAsserts.assertAttributeEqualsEventually(app, myResult, 3d);
+        
+        child2.sensors().set(myVal, 1d);
+        EntityAsserts.assertAttributeEqualsEventually(app, myResult, 1d);
+        
+        child2.sensors().set(myVal, null);
+        EntityAsserts.assertAttributeEqualsEventually(app, myResult, 3d);
+    }
+    
+    @Test
+    public void testMax() throws Exception {
+        Entity app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  brooklyn.children:",
+                "    - type: " + TestEntity.class.getName(),
+                "    - type: " + TestEntity.class.getName(),
+                "  brooklyn.enrichers:",
+                "    - type: " + Aggregator.class.getName(),
+                "      brooklyn.config:",
+                "        "+Aggregator.SOURCE_SENSOR.getName()+": myVal",
+                "        "+Aggregator.TARGET_SENSOR.getName()+": myResult",
+                "        "+Aggregator.TRANSFORMATION_UNTYPED.getName()+": 
max");
+        Entity child1 = Iterables.get(app.getChildren(), 0);
+        Entity child2 = Iterables.get(app.getChildren(), 1);
+        
+        child1.sensors().set(myVal, 1d);
+        EntityAsserts.assertAttributeEqualsEventually(app, myResult, 1d);
+        
+        child2.sensors().set(myVal, 3d);
+        EntityAsserts.assertAttributeEqualsEventually(app, myResult, 3d);
+        
+        child2.sensors().set(myVal, null);
+        EntityAsserts.assertAttributeEqualsEventually(app, myResult, 1d);
+    }
+    
+    @Test
     public void testList() throws Exception {
         Entity app = createAndStartApplication(
                 "services:",

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c15a64ba/core/src/main/java/org/apache/brooklyn/enricher/stock/Aggregator.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/enricher/stock/Aggregator.java 
b/core/src/main/java/org/apache/brooklyn/enricher/stock/Aggregator.java
index 1ba4e6d..f1a85fa 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/Aggregator.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/Aggregator.java
@@ -61,7 +61,7 @@ public class Aggregator<T,U> extends AbstractAggregator<T,U> 
implements SensorEv
             "enricher.transformation.untyped",
             "Specifies a transformation, as a function from a collection to 
the value, or as a string " +
                     "matching a pre-defined named transformation, such as 
'average' (for numbers), " +
-                    "'sum' (for numbers), 'isQuorate' (to compute a quorum), " 
+
+                    "'sum' (for numbers), 'min' (for numbers), 'max' (for 
numbers), 'isQuorate' (to compute a quorum), " +
                     "'list' (the default, putting any collection of items into 
a list), " +
                     "or 'first' (the first value, or null if empty)");
 
@@ -116,13 +116,28 @@ public class Aggregator<T,U> extends 
AbstractAggregator<T,U> implements SensorEv
         
     @SuppressWarnings({ "rawtypes", "unchecked" })
     protected Function<? super Collection<?>, ?> lookupTransformation(String 
t1) {
-        if ("average".equalsIgnoreCase(t1)) return new 
Enrichers.ComputingAverage(null, null, targetSensor.getTypeToken());
-        if ("sum".equalsIgnoreCase(t1)) return new 
Enrichers.ComputingSum(null, null, targetSensor.getTypeToken());
-        if ("isQuorate".equalsIgnoreCase(t1)) return new 
Enrichers.ComputingIsQuorate(targetSensor.getTypeToken(),
+        //return MathAggregatorFunctions.computingAverage(
+        TypeToken<U> targetType = targetSensor.getTypeToken();
+        Function<?, ?> result;
+        if ("average".equalsIgnoreCase(t1)) {
+            result = MathAggregatorFunctions.computingAverage(null, null, 
(TypeToken<? extends Number>) targetType);
+        } else if ("sum".equalsIgnoreCase(t1)) {
+            result = MathAggregatorFunctions.computingSum(null, null, 
(TypeToken<? extends Number>) targetType);
+        } else if ("min".equalsIgnoreCase(t1)) {
+            result = MathAggregatorFunctions.computingMin(null, null, 
(TypeToken<? extends Number>) targetType);
+        } else if ("max".equalsIgnoreCase(t1)) {
+            result = MathAggregatorFunctions.computingMax(null, null, 
(TypeToken<? extends Number>) targetType);
+        } else if ("isQuorate".equalsIgnoreCase(t1)) {
+            result = new Enrichers.ComputingIsQuorate(targetType,
                 QuorumChecks.of(config().get(QUORUM_CHECK_TYPE)), 
config().get(QUORUM_TOTAL_SIZE));
-        if ("list".equalsIgnoreCase(t1)) return new ComputingList();
-        if ("first".equalsIgnoreCase(t1)) return new FirstOrNull();
-        return null;
+        } else if ("list".equalsIgnoreCase(t1)) {
+            result = new ComputingList();
+        } else if ("first".equalsIgnoreCase(t1)) {
+            result = new FirstOrNull();
+        } else {
+            result = null;
+        }
+        return (Function<? super Collection<?>, ?>) result;
     }
 
     private class ComputingList<TT> implements Function<Collection<TT>, 
List<TT>> {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c15a64ba/core/src/main/java/org/apache/brooklyn/enricher/stock/Enrichers.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/enricher/stock/Enrichers.java 
b/core/src/main/java/org/apache/brooklyn/enricher/stock/Enrichers.java
index 51b0358..cd438cb 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/Enrichers.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/Enrichers.java
@@ -833,7 +833,11 @@ public class Enrichers {
         }
     }
 
-    @Beta
+    /**
+     * @deprecated since 0.12.0; see {@link MathAggregatorFunctions};
+     * Will be kept for backwards compatibility with persisted state.
+     */
+    @Deprecated
     private abstract static class ComputingNumber<T extends Number> implements 
Function<Collection<T>, T> {
         protected final Number defaultValueForUnreportedSensors;
         protected final Number valueToReportIfNoSensors;
@@ -854,28 +858,38 @@ public class Enrichers {
         @Override public abstract T apply(Collection<T> input);
     }
 
-    @Beta
+    /**
+     * @deprecated since 0.12.0; see {@link MathAggregatorFunctions};
+     * Will be made private and kept for backwards compatibility with 
persisted state.
+     */
+    @Deprecated
     public static class ComputingSum<T extends Number> extends 
ComputingNumber<T> {
         public ComputingSum(Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, TypeToken<T> typeToken) {
             super(defaultValueForUnreportedSensors, valueToReportIfNoSensors, 
typeToken);
         }
-        @SuppressWarnings({ "rawtypes", "unchecked" })
         @Override public T apply(Collection<T> input) {
             return (T) sum(input, defaultValueForUnreportedSensors, 
valueToReportIfNoSensors, typeToken);
         }
     }
 
-    @Beta
+    /**
+     * @deprecated since 0.12.0; see {@link MathAggregatorFunctions};
+     * Will be made private and kept for backwards compatibility with 
persisted state.
+     */
+    @Deprecated
     public static class ComputingAverage<T extends Number> extends 
ComputingNumber<T> {
         public ComputingAverage(Number defaultValueForUnreportedSensors, 
Number valueToReportIfNoSensors, TypeToken<T> typeToken) {
             super(defaultValueForUnreportedSensors, valueToReportIfNoSensors, 
typeToken);
         }
-        @SuppressWarnings({ "rawtypes", "unchecked" })
         @Override public T apply(Collection<T> input) {
             return (T) average(input, defaultValueForUnreportedSensors, 
valueToReportIfNoSensors, typeToken);
         }
     }
 
+    /**
+     * @deprecated since 0.12.0; see {@link MathAggregatorFunctions};
+     */
+    @Deprecated
     protected static <T extends Number> T average(Collection<T> vals, Number 
defaultValueForUnreportedSensors, Number valueToReportIfNoSensors, TypeToken<T> 
type) {
         Double doubleValueToReportIfNoSensors = (valueToReportIfNoSensors == 
null) ? null : valueToReportIfNoSensors.doubleValue();
         int count = count(vals, defaultValueForUnreportedSensors!=null);
@@ -885,12 +899,14 @@ public class Enrichers {
         return cast(result, type);
     }
     
-    @SuppressWarnings("unchecked")
     protected static <N extends Number> N cast(Number n, TypeToken<? extends 
N> numberType) {
-        return (N) TypeCoercions.castPrimitive(n, numberType.getRawType());
+        return (N) TypeCoercions.coerce(n, numberType);
     }
 
-    @Beta  //may be moved
+    /**
+     * @deprecated since 0.12.0; see {@link MathAggregatorFunctions};
+     */
+    @Deprecated
     public static <N extends Number> N sum(Iterable<? extends Number> vals, 
Number valueIfNull, Number valueIfNone, TypeToken<N> type) {
         double result = 0d;
         int count = 0;
@@ -909,6 +925,10 @@ public class Enrichers {
         return cast(result, type);
     }
     
+    /**
+     * @deprecated since 0.12.0; see {@link MathAggregatorFunctions};
+     */
+    @Deprecated
     protected static int count(Iterable<? extends Object> vals, boolean 
includeNullValues) {
         int result = 0;
         if (vals != null) 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c15a64ba/core/src/main/java/org/apache/brooklyn/enricher/stock/MathAggregatorFunctions.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/enricher/stock/MathAggregatorFunctions.java
 
b/core/src/main/java/org/apache/brooklyn/enricher/stock/MathAggregatorFunctions.java
new file mode 100644
index 0000000..a67befd
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/enricher/stock/MathAggregatorFunctions.java
@@ -0,0 +1,203 @@
+/*
+ * 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.enricher.stock;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.reflect.TypeToken;
+
+@Beta
+public class MathAggregatorFunctions {
+
+    private MathAggregatorFunctions() {}
+    
+    @Beta
+    public static <T extends Number> Function<Collection<? extends Number>, T> 
computingSum(
+            Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, Class<T> type) {
+        return computingSum(defaultValueForUnreportedSensors, 
valueToReportIfNoSensors, TypeToken.of(type));
+    }
+    
+    @Beta
+    public static <T extends Number> Function<Collection<? extends Number>, T> 
computingSum(
+            Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, TypeToken<T> typeToken) {
+        return new ComputingSum<T>(defaultValueForUnreportedSensors, 
valueToReportIfNoSensors, typeToken);
+    }
+    
+    @Beta
+    public static <T extends Number> Function<Collection<? extends Number>, T> 
computingAverage(
+            Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, Class<T> type) {
+        return computingAverage(defaultValueForUnreportedSensors, 
valueToReportIfNoSensors, TypeToken.of(type));
+    }
+    
+    @Beta
+    public static <T extends Number> Function<Collection<? extends Number>, T> 
computingAverage(
+            Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, TypeToken<T> typeToken) {
+        return new ComputingAverage<T>(defaultValueForUnreportedSensors, 
valueToReportIfNoSensors, typeToken);
+    }
+    
+    @Beta
+    public static <T extends Number> Function<Collection<? extends Number>, T> 
computingMin(
+            Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, Class<T> type) {
+        return computingMin(defaultValueForUnreportedSensors, 
valueToReportIfNoSensors, TypeToken.of(type));
+    }
+    
+    @Beta
+    public static <T extends Number> Function<Collection<? extends Number>, T> 
computingMin(
+            Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, TypeToken<T> typeToken) {
+        return new ComputingMin<T>(defaultValueForUnreportedSensors, 
valueToReportIfNoSensors, typeToken);
+    }
+    
+    @Beta
+    public static <T extends Number> Function<Collection<? extends Number>, T> 
computingMax(
+            Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, Class<T> type) {
+        return computingMax(defaultValueForUnreportedSensors, 
valueToReportIfNoSensors, TypeToken.of(type));
+    }
+    
+    @Beta
+    public static <T extends Number> Function<Collection<? extends Number>, T> 
computingMax(
+            Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, TypeToken<T> typeToken) {
+        return new ComputingMax<T>(defaultValueForUnreportedSensors, 
valueToReportIfNoSensors, typeToken);
+    }
+
+    @Beta
+    protected abstract static class AbstractComputingNumber<T extends Number> 
implements Function<Collection<? extends Number>, T> {
+        protected final Number defaultValueForUnreportedSensors;
+        protected final Number valueToReportIfNoSensors;
+        protected final TypeToken<T> typeToken;
+        
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        public AbstractComputingNumber(Number 
defaultValueForUnreportedSensors, Number valueToReportIfNoSensors, TypeToken<T> 
typeToken) {
+            this.defaultValueForUnreportedSensors = 
defaultValueForUnreportedSensors;
+            this.valueToReportIfNoSensors = valueToReportIfNoSensors;
+            if (typeToken != null && 
TypeToken.of(Number.class).isAssignableFrom(typeToken.getType())) {
+                this.typeToken = typeToken;
+            } else if (typeToken == null || 
typeToken.isAssignableFrom(Number.class)) {
+                // use double if e.g. Object is supplied
+                this.typeToken = (TypeToken)TypeToken.of(Double.class);
+            } else {
+                throw new IllegalArgumentException("Type "+typeToken+" is not 
valid for "+this);
+            }
+        }
+        
+        @Override
+        public abstract T apply(Collection<? extends Number> vals);
+    }
+
+    @Beta
+    protected abstract static class BasicComputingNumber<T extends Number> 
extends AbstractComputingNumber<T> {
+        public BasicComputingNumber(Number defaultValueForUnreportedSensors, 
Number valueToReportIfNoSensors, TypeToken<T> typeToken) {
+            super(defaultValueForUnreportedSensors, valueToReportIfNoSensors, 
typeToken);
+        }
+        
+        @Override
+        public T apply(@Nullable Collection<? extends Number> vals) {
+            List<Number> postProcessedVals = new ArrayList<>();
+            int count = 0;
+            if (vals != null) {
+                for (Number val : vals) { 
+                    if (val != null) {
+                        postProcessedVals.add(val);
+                        count++;
+                    } else if (defaultValueForUnreportedSensors != null) {
+                        
postProcessedVals.add(defaultValueForUnreportedSensors);
+                        count++;
+                    }
+                }
+            }
+            if (count==0) return cast(valueToReportIfNoSensors, typeToken);
+            
+            Number result = applyImpl(postProcessedVals);
+            return cast(result, typeToken);
+        }
+        
+        public abstract Number applyImpl(Collection<Number> vals);
+    }
+
+    @Beta
+    protected static class ComputingSum<T extends Number> extends 
BasicComputingNumber<T> {
+        public ComputingSum(Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, TypeToken<T> typeToken) {
+            super(defaultValueForUnreportedSensors, valueToReportIfNoSensors, 
typeToken);
+        }
+        @Override
+        public Number applyImpl(Collection<Number> vals) {
+            double result = 0d;
+            for (Number val : vals) { 
+                result += val.doubleValue();
+            }
+            return result;
+        }
+    }
+
+    @Beta
+    protected static class ComputingAverage<T extends Number> extends 
BasicComputingNumber<T> {
+        public ComputingAverage(Number defaultValueForUnreportedSensors, 
Number valueToReportIfNoSensors, TypeToken<T> typeToken) {
+            super(defaultValueForUnreportedSensors, valueToReportIfNoSensors, 
typeToken);
+        }
+        @Override
+        public Number applyImpl(Collection<Number> vals) {
+            double sum = 0d;
+            for (Number val : vals) { 
+                sum += val.doubleValue();
+            }
+            return (sum / vals.size());
+        }
+    }
+
+    @Beta
+    protected static class ComputingMin<T extends Number> extends 
BasicComputingNumber<T> {
+        public ComputingMin(Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, TypeToken<T> typeToken) {
+            super(defaultValueForUnreportedSensors, valueToReportIfNoSensors, 
typeToken);
+        }
+        @Override
+        public Number applyImpl(Collection<Number> vals) {
+            Double result = null;
+            for (Number val : vals) { 
+                result = (result == null) ? val.doubleValue() : 
Math.min(result, val.doubleValue());
+            }
+            return result;
+        }
+    }
+
+    @Beta
+    protected static class ComputingMax<T extends Number> extends 
BasicComputingNumber<T> {
+        public ComputingMax(Number defaultValueForUnreportedSensors, Number 
valueToReportIfNoSensors, TypeToken<T> typeToken) {
+            super(defaultValueForUnreportedSensors, valueToReportIfNoSensors, 
typeToken);
+        }
+        @Override
+        public Number applyImpl(Collection<Number> vals) {
+            Double result = null;
+            for (Number val : vals) { 
+                result = (result == null) ? val.doubleValue() : 
Math.max(result, val.doubleValue());
+            }
+            return result;
+        }
+    }
+
+    protected static <N extends Number> N cast(Number n, TypeToken<? extends 
N> numberType) {
+        return (N) TypeCoercions.coerce(n, numberType);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c15a64ba/core/src/test/java/org/apache/brooklyn/enricher/stock/MathAggregatorFunctionsTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/enricher/stock/MathAggregatorFunctionsTest.java
 
b/core/src/test/java/org/apache/brooklyn/enricher/stock/MathAggregatorFunctionsTest.java
new file mode 100644
index 0000000..d3f4308
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/enricher/stock/MathAggregatorFunctionsTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.enricher.stock;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.brooklyn.util.collections.MutableList;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.reflect.TypeToken;
+
+public class MathAggregatorFunctionsTest {
+
+    @SuppressWarnings("serial")
+    private final TypeToken<Double> doubleTypeToken = new TypeToken<Double>() 
{};
+    
+    @Test
+    public void testValueIfNone() throws Exception {
+        List<Function<Collection<? extends Number>, Integer>> funcs = new 
ArrayList<>();
+        funcs.add(MathAggregatorFunctions.computingSum(null, 999, 
Integer.class));
+        funcs.add(MathAggregatorFunctions.computingAverage(null, 999, 
Integer.class));
+        funcs.add(MathAggregatorFunctions.computingMin(null, 999, 
Integer.class));
+        funcs.add(MathAggregatorFunctions.computingMax(null, 999, 
Integer.class));
+        
+        for (Function<Collection<? extends Number>, Integer> func : funcs) {
+            assertEquals(func.apply(ImmutableList.<Number>of()), (Integer)999);
+        }
+    }
+    
+    @Test
+    public void testValueIfNull() throws Exception {
+        List<Function<Collection<? extends Number>, Integer>> funcs = new 
ArrayList<>();
+        funcs.add(MathAggregatorFunctions.computingSum(999, null, 
Integer.class));
+        funcs.add(MathAggregatorFunctions.computingAverage(999, null, 
Integer.class));
+        funcs.add(MathAggregatorFunctions.computingMin(999, null, 
Integer.class));
+        funcs.add(MathAggregatorFunctions.computingMax(999, null, 
Integer.class));
+        
+        for (Function<Collection<? extends Number>, Integer> func : funcs) {
+            assertEquals(func.apply(MutableList.<Number>of(null)), 
(Integer)999);
+        }
+    }
+    
+    @Test
+    public void testCastValue() throws Exception {
+        List<Function<Collection<? extends Number>, Double>> funcs = new 
ArrayList<>();
+        funcs.add(MathAggregatorFunctions.computingSum(999, null, 
Double.class));
+        funcs.add(MathAggregatorFunctions.computingAverage(999, null, 
Double.class));
+        funcs.add(MathAggregatorFunctions.computingMin(999, null, 
Double.class));
+        funcs.add(MathAggregatorFunctions.computingMax(999, null, 
Double.class));
+        
+        for (Function<Collection<? extends Number>, Double> func : funcs) {
+            assertEquals(func.apply(MutableList.<Number>of(null)), 
(Double)999d);
+        }
+    }
+    
+    @Test
+    public void testCastValueWithTypeToken() throws Exception {
+        List<Function<Collection<? extends Number>, Double>> funcs = new 
ArrayList<>();
+        funcs.add(MathAggregatorFunctions.computingSum(999, null, 
doubleTypeToken));
+        funcs.add(MathAggregatorFunctions.computingAverage(999, null, 
doubleTypeToken));
+        funcs.add(MathAggregatorFunctions.computingMin(999, null, 
doubleTypeToken));
+        funcs.add(MathAggregatorFunctions.computingMax(999, null, 
doubleTypeToken));
+        
+        for (Function<Collection<? extends Number>, Double> func : funcs) {
+            assertEquals(func.apply(MutableList.<Number>of(null)), 
(Double)999d);
+        }
+    }
+    
+    @Test
+    public void testSum() throws Exception {
+        Function<Collection<? extends Number>, Integer> func = 
MathAggregatorFunctions.computingSum(null, null, Integer.class);
+        assertEquals(func.apply(MutableList.<Number>of(1, 2, 4)), (Integer)7);
+        assertEquals(func.apply(MutableList.<Number>of(1, null, 4)), 
(Integer)5);
+    }
+    
+    @Test
+    public void testAverage() throws Exception {
+        Function<Collection<? extends Number>, Integer> func = 
MathAggregatorFunctions.computingAverage(null, null, Integer.class);
+        assertEquals(func.apply(MutableList.<Number>of(1, 3, 5)), (Integer)3);
+        assertEquals(func.apply(MutableList.<Number>of(1, null, 3)), 
(Integer)2);
+    }
+    
+    @Test
+    public void testMin() throws Exception {
+        Function<Collection<? extends Number>, Integer> func = 
MathAggregatorFunctions.computingMin(null, null, Integer.class);
+        assertEquals(func.apply(MutableList.<Number>of(1, 3, 5)), (Integer)1);
+        assertEquals(func.apply(MutableList.<Number>of(3, null, 1)), 
(Integer)1);
+    }
+    
+    @Test
+    public void testMax() throws Exception {
+        Function<Collection<? extends Number>, Integer> func = 
MathAggregatorFunctions.computingMax(null, null, Integer.class);
+        assertEquals(func.apply(MutableList.<Number>of(1, 3, 5)), (Integer)5);
+        assertEquals(func.apply(MutableList.<Number>of(3, null, 1)), 
(Integer)3);
+    }
+}

Reply via email to