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); + } +}
