You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by ce...@apache.org on 2016/12/08 14:14:50 UTC
incubator-metron git commit: METRON-364: Preserve Type for Arithmetic
Expressions in Stellar closes apache/incubator-metron#390
Repository: incubator-metron
Updated Branches:
refs/heads/master a96c4ed2e -> b36f4b2fb
METRON-364: Preserve Type for Arithmetic Expressions in Stellar closes apache/incubator-metron#390
Project: http://git-wip-us.apache.org/repos/asf/incubator-metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-metron/commit/b36f4b2f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-metron/tree/b36f4b2f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-metron/diff/b36f4b2f
Branch: refs/heads/master
Commit: b36f4b2fbf8cc4578ef3f39ac4d8306544f6bcb4
Parents: a96c4ed
Author: JJ <jj...@gmail.com>
Authored: Thu Dec 8 09:14:24 2016 -0500
Committer: cstella <ce...@gmail.com>
Committed: Thu Dec 8 09:14:24 2016 -0500
----------------------------------------------------------------------
.../stellar/DefaultStellarExecutorTest.java | 2 +-
.../dsl/functions/ConversionFunctions.java | 10 +
.../common/stellar/BaseStellarProcessor.java | 3 +-
.../metron/common/stellar/StellarCompiler.java | 50 +--
.../stellar/evaluators/ArithmeticEvaluator.java | 102 +++++
.../dsl/functions/ConversionFunctionsTest.java | 44 ++
.../common/stellar/StellarArithmeticTest.java | 181 ++++++++
.../metron/common/stellar/StellarTest.java | 93 +----
.../evaluators/ArithmeticEvaluatorTest.java | 412 +++++++++++++++++++
9 files changed, 779 insertions(+), 118 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/stellar/DefaultStellarExecutorTest.java
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/stellar/DefaultStellarExecutorTest.java b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/stellar/DefaultStellarExecutorTest.java
index 3110329..9a77f8e 100644
--- a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/stellar/DefaultStellarExecutorTest.java
+++ b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/stellar/DefaultStellarExecutorTest.java
@@ -113,7 +113,7 @@ public class DefaultStellarExecutorTest {
// verify
Object var = executor.getState().get("sum");
- assertEquals(6.0, var);
+ assertEquals(6, var);
}
/**
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/main/java/org/apache/metron/common/dsl/functions/ConversionFunctions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/dsl/functions/ConversionFunctions.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/dsl/functions/ConversionFunctions.java
index e9d2436..4ddff99 100644
--- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/dsl/functions/ConversionFunctions.java
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/dsl/functions/ConversionFunctions.java
@@ -70,4 +70,14 @@ public class ConversionFunctions {
public TO_LONG() { super(Long.class); }
}
+
+ @Stellar(name="TO_FLOAT"
+ , description="Transforms the first argument to a float"
+ , params = { "input - Object of string or numeric type"}
+ , returns = "Float version of the first argument"
+ )
+ public static class TO_FLOAT extends Cast<Float> {
+
+ public TO_FLOAT() { super(Float.class); }
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/BaseStellarProcessor.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/BaseStellarProcessor.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/BaseStellarProcessor.java
index 1a7f626..f20cf43 100644
--- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/BaseStellarProcessor.java
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/BaseStellarProcessor.java
@@ -26,6 +26,7 @@ import java.util.HashSet;
import java.util.Set;
import org.apache.metron.common.dsl.*;
import org.apache.metron.common.dsl.functions.resolver.FunctionResolver;
+import org.apache.metron.common.stellar.evaluators.ArithmeticEvaluator;
import org.apache.metron.common.stellar.generated.StellarBaseListener;
import org.apache.metron.common.stellar.generated.StellarLexer;
import org.apache.metron.common.stellar.generated.StellarParser;
@@ -82,7 +83,7 @@ public class BaseStellarProcessor<T> {
TokenStream tokens = new CommonTokenStream(lexer);
StellarParser parser = new StellarParser(tokens);
- StellarCompiler treeBuilder = new StellarCompiler(variableResolver, functionResolver, context);
+ StellarCompiler treeBuilder = new StellarCompiler(variableResolver, functionResolver, context, new ArithmeticEvaluator());
parser.addParseListener(treeBuilder);
parser.removeErrorListeners();
parser.addErrorListener(new ErrorListener());
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/StellarCompiler.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/StellarCompiler.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/StellarCompiler.java
index 3ac3d98..a5c4cf3 100644
--- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/StellarCompiler.java
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/StellarCompiler.java
@@ -20,6 +20,7 @@ package org.apache.metron.common.stellar;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
+import org.apache.commons.lang3.tuple.Pair;
import org.apache.metron.common.dsl.Context;
import org.apache.metron.common.dsl.FunctionMarker;
import org.apache.metron.common.dsl.functions.resolver.FunctionResolver;
@@ -27,6 +28,7 @@ import org.apache.metron.common.dsl.ParseException;
import org.apache.metron.common.dsl.StellarFunction;
import org.apache.metron.common.dsl.Token;
import org.apache.metron.common.dsl.VariableResolver;
+import org.apache.metron.common.stellar.evaluators.ArithmeticEvaluator;
import org.apache.metron.common.stellar.generated.StellarBaseListener;
import org.apache.metron.common.stellar.generated.StellarParser;
import org.apache.metron.common.utils.ConversionUtils;
@@ -49,11 +51,13 @@ public class StellarCompiler extends StellarBaseListener {
private FunctionResolver functionResolver;
private VariableResolver variableResolver;
private Throwable actualException = null;
+ private final ArithmeticEvaluator arithmeticEvaluator;
- public StellarCompiler(VariableResolver variableResolver, FunctionResolver functionResolver, Context context) {
+ public StellarCompiler(VariableResolver variableResolver, FunctionResolver functionResolver, Context context, ArithmeticEvaluator arithmeticEvaluator) {
this.variableResolver = variableResolver;
this.functionResolver = functionResolver;
this.context = context;
+ this.arithmeticEvaluator = arithmeticEvaluator;
}
@Override
@@ -80,15 +84,6 @@ public class StellarCompiler extends StellarBaseListener {
return set.contains(key);
}
- private Double getDouble(Token<?> token) {
- Number n = (Number) token.getValue();
- if (n == null) {
- return 0d;
- } else {
- return n.doubleValue();
- }
- }
-
@Override
public void exitNullConst(StellarParser.NullConstContext ctx) {
tokenStack.push(new Token<>(null, Object.class));
@@ -96,38 +91,33 @@ public class StellarCompiler extends StellarBaseListener {
@Override
public void exitArithExpr_plus(StellarParser.ArithExpr_plusContext ctx) {
- Token<?> right = popStack();
- Token<?> left = popStack();
- Double r = getDouble(right);
- Double l = getDouble(left);
- tokenStack.push(new Token<>(l + r, Double.class));
+ Pair<Token<? extends Number>, Token<? extends Number>> p = getArithExpressionPair();
+ tokenStack.push(arithmeticEvaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p));
}
@Override
public void exitArithExpr_minus(StellarParser.ArithExpr_minusContext ctx) {
- Token<?> right = popStack();
- Token<?> left = popStack();
- Double r = getDouble(right);
- Double l = getDouble(left);
- tokenStack.push(new Token<>(l - r, Double.class));
+ Pair<Token<? extends Number>, Token<? extends Number>> p = getArithExpressionPair();
+ tokenStack.push(arithmeticEvaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), p));
}
@Override
public void exitArithExpr_div(StellarParser.ArithExpr_divContext ctx) {
- Token<?> right = popStack();
- Token<?> left = popStack();
- Double r = getDouble(right);
- Double l = getDouble(left);
- tokenStack.push(new Token<>(l / r, Double.class));
+ Pair<Token<? extends Number>, Token<? extends Number>> p = getArithExpressionPair();
+ tokenStack.push(arithmeticEvaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p));
}
@Override
public void exitArithExpr_mul(StellarParser.ArithExpr_mulContext ctx) {
- Token<?> right = popStack();
- Token<?> left = popStack();
- Double r = getDouble(right);
- Double l = getDouble(left);
- tokenStack.push(new Token<>(l * r, Double.class));
+ Pair<Token<? extends Number>, Token<? extends Number>> p = getArithExpressionPair();
+ tokenStack.push(arithmeticEvaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), p));
+ }
+
+ @SuppressWarnings("unchecked")
+ private Pair<Token<? extends Number>, Token<? extends Number>> getArithExpressionPair() {
+ Token<? extends Number> right = (Token<? extends Number>) popStack();
+ Token<? extends Number> left = (Token<? extends Number>) popStack();
+ return Pair.of(left, right);
}
private void handleConditional() {
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluator.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluator.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluator.java
new file mode 100644
index 0000000..6fd372a
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluator.java
@@ -0,0 +1,102 @@
+/*
+ * 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.metron.common.stellar.evaluators;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.metron.common.dsl.ParseException;
+import org.apache.metron.common.dsl.Token;
+
+import java.util.function.BiFunction;
+
+public class ArithmeticEvaluator {
+
+ public Token<? extends Number> evaluate(BiFunction<Number, Number, Token<? extends Number>> function,
+ Pair<Token<? extends Number>, Token<? extends Number>> p) {
+ if (p == null || p.getKey() == null || p.getValue() == null) {
+ throw new IllegalArgumentException();
+ }
+
+ final Number l = p.getKey().getValue();
+ final Number r = p.getValue().getValue();
+
+ return function.apply(l == null ? 0 : l, r == null ? 0 : r);
+ }
+
+ /**
+ * This is a helper class that defines how to handle arithmetic operations. The conversion between number
+ * types is taken for the Java spec: http://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.6.2
+ */
+ public static final class ArithmeticEvaluatorFunctions {
+ public static BiFunction<Number, Number, Token<? extends Number>> addition() {
+ return (Number l, Number r) -> {
+ if (l instanceof Double || r instanceof Double) {
+ return new Token<>(l.doubleValue() + r.doubleValue(), Double.class);
+ } else if (l instanceof Float || r instanceof Float) {
+ return new Token<>(l.floatValue() + r.floatValue(), Float.class);
+ } else if (l instanceof Long || r instanceof Long) {
+ return new Token<>(l.longValue() + r.longValue(), Long.class);
+ } else {
+ return new Token<>(l.intValue() + r.intValue(), Integer.class);
+ }
+ };
+ }
+
+ public static BiFunction<Number, Number, Token<? extends Number>> multiplication() {
+ return (Number l, Number r) -> {
+ if (l instanceof Double || r instanceof Double) {
+ return new Token<>(l.doubleValue() * r.doubleValue(), Double.class);
+ } else if (l instanceof Float || r instanceof Float) {
+ return new Token<>(l.floatValue() * r.floatValue(), Float.class);
+ } else if (l instanceof Long || r instanceof Long) {
+ return new Token<>(l.longValue() * r.longValue(), Long.class);
+ } else {
+ return new Token<>(l.intValue() * r.intValue(), Integer.class);
+ }
+ };
+ }
+
+ public static BiFunction<Number, Number, Token<? extends Number>> subtraction() {
+ return (Number l, Number r) -> {
+ if (l instanceof Double || r instanceof Double) {
+ return new Token<>(l.doubleValue() - r.doubleValue(), Double.class);
+ } else if (l instanceof Float || r instanceof Float) {
+ return new Token<>(l.floatValue() - r.floatValue(), Float.class);
+ } else if (l instanceof Long || r instanceof Long) {
+ return new Token<>(l.longValue() - r.longValue(), Long.class);
+ } else {
+ return new Token<>(l.intValue() - r.intValue(), Integer.class);
+ }
+ };
+ }
+
+ public static BiFunction<Number, Number, Token<? extends Number>> division() {
+ return (Number l, Number r) -> {
+ if (l instanceof Double || r instanceof Double) {
+ return new Token<>(l.doubleValue() / r.doubleValue(), Double.class);
+ } else if (l instanceof Float || r instanceof Float) {
+ return new Token<>(l.floatValue() / r.floatValue(), Float.class);
+ } else if (l instanceof Long || r instanceof Long) {
+ return new Token<>(l.longValue() / r.longValue(), Long.class);
+ } else {
+ return new Token<>(l.intValue() / r.intValue(), Integer.class);
+ }
+ };
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/test/java/org/apache/metron/common/dsl/functions/ConversionFunctionsTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/dsl/functions/ConversionFunctionsTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/dsl/functions/ConversionFunctionsTest.java
new file mode 100644
index 0000000..b82ee49
--- /dev/null
+++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/dsl/functions/ConversionFunctionsTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.metron.common.dsl.functions;
+
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+
+public class ConversionFunctionsTest {
+
+ @Test
+ public void conversionFunctionsShouldProperlyConvertToSpecificType() throws Exception {
+ assertEquals(1D, new ConversionFunctions.TO_DOUBLE().apply(Collections.singletonList(1)));
+ assertEquals(1F, new ConversionFunctions.TO_FLOAT().apply(Collections.singletonList(1.0D)));
+ assertEquals(1, new ConversionFunctions.TO_INTEGER().apply(Collections.singletonList(1.0D)));
+ assertEquals(1L, new ConversionFunctions.TO_LONG().apply(Collections.singletonList(1F)));
+ }
+
+ @Test
+ public void conversionFunctionsShouldProperlyHandleNull() throws Exception {
+ assertEquals(null, new ConversionFunctions.TO_DOUBLE().apply(Collections.singletonList(null)));
+ assertEquals(null, new ConversionFunctions.TO_FLOAT().apply(Collections.singletonList(null)));
+ assertEquals(null, new ConversionFunctions.TO_INTEGER().apply(Collections.singletonList(null)));
+ assertEquals(null, new ConversionFunctions.TO_LONG().apply(Collections.singletonList(null)));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarArithmeticTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarArithmeticTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarArithmeticTest.java
new file mode 100644
index 0000000..8f65a6f
--- /dev/null
+++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarArithmeticTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.metron.common.stellar;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.metron.common.dsl.Token;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.metron.common.utils.StellarProcessorUtils.run;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SuppressWarnings("unchecked")
+public class StellarArithmeticTest {
+
+ @Test
+ public void addingLongsShouldYieldLong() throws Exception {
+ final long timestamp = 1452013350000L;
+ String query = "TO_EPOCH_TIMESTAMP('2016-01-05 17:02:30', 'yyyy-MM-dd HH:mm:ss', 'UTC') + 2";
+ Assert.assertEquals(timestamp + 2, run(query, new HashMap<>()));
+ }
+
+ @Test
+ public void addingIntegersShouldYieldAnInteger() throws Exception {
+ String query = "1 + 2";
+ Assert.assertEquals(3, run(query, new HashMap<>()));
+ }
+
+ @Test
+ public void addingDoublesShouldYieldADouble() throws Exception {
+ String query = "1.0 + 2.0";
+ Assert.assertEquals(3.0, run(query, new HashMap<>()));
+ }
+
+ @Test
+ public void addingDoubleAndIntegerWhereSubjectIsDoubleShouldYieldADouble() throws Exception {
+ String query = "2.1 + 1";
+ Assert.assertEquals(3.1, run(query, new HashMap<>()));
+ }
+
+ @Test
+ public void addingDoubleAndIntegerWhereSubjectIsIntegerShouldYieldADouble() throws Exception {
+ String query = "1 + 2.1";
+ Assert.assertEquals(3.1, run(query, new HashMap<>()));
+ }
+
+ @Test
+ public void testArithmetic() {
+ {
+ String query = "1 + 2";
+ Assert.assertEquals(3, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
+ }
+ {
+ String query = "1.2 + 2";
+ Assert.assertEquals(3.2, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
+ }
+ {
+ String query = "1.2e-3 + 2";
+ Assert.assertEquals(1.2e-3 + 2, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
+ }
+ }
+
+ @Test
+ public void testNumericOperations() {
+ {
+ String query = "TO_INTEGER(1 + 2*2 + 3 - 4 - 0.5)";
+ Assert.assertEquals(3, (Integer) run(query, new HashMap<>()), 1e-6);
+ }
+ {
+ String query = "1 + 2*2 + 3 - 4 - 0.5";
+ Assert.assertEquals(3.5, (Double) run(query, new HashMap<>()), 1e-6);
+ }
+ {
+ String query = "2*one*(1 + 2*2 + 3 - 4)";
+ Assert.assertEquals(8, run(query, ImmutableMap.of("one", 1)));
+ }
+ {
+ String query = "2*(1 + 2 + 3 - 4)";
+ Assert.assertEquals(4, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+ }
+ {
+ String query = "1 + 2 + 3 - 4 - 2";
+ Assert.assertEquals(0, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+ }
+ {
+ String query = "1 + 2 + 3 + 4";
+ Assert.assertEquals(10, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+ }
+ {
+ String query = "(one + 2)*3";
+ Assert.assertEquals(9, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+ }
+ {
+ String query = "TO_INTEGER((one + 2)*3.5)";
+ Assert.assertEquals(10, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+ }
+ {
+ String query = "1 + 2*3";
+ Assert.assertEquals(7, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+ }
+ {
+ String query = "TO_LONG(foo)";
+ Assert.assertNull(run(query,ImmutableMap.of("foo","not a number")));
+ }
+ {
+ String query = "TO_LONG(foo)";
+ Assert.assertEquals(232321L,run(query,ImmutableMap.of("foo","00232321")));
+ }
+ {
+ String query = "TO_LONG(foo)";
+ Assert.assertEquals(Long.MAX_VALUE,run(query,ImmutableMap.of("foo", Long.toString(Long.MAX_VALUE))));
+ }
+ }
+
+ @Test
+ public void verifyExpectedReturnTypes() throws Exception {
+ Token<Integer> integer = mock(Token.class);
+ when(integer.getValue()).thenReturn(1);
+
+ Token<Long> lng = mock(Token.class);
+ when(lng.getValue()).thenReturn(1L);
+
+ Token<Double> dbl = mock(Token.class);
+ when(dbl.getValue()).thenReturn(1.0D);
+
+ Token<Float> flt = mock(Token.class);
+ when(flt.getValue()).thenReturn(1.0F);
+
+ Map<Pair<String, String>, Class<? extends Number>> expectedReturnTypeMappings =
+ new HashMap<Pair<String, String>, Class<? extends Number>>() {{
+ put(Pair.of("TO_FLOAT(3.0)", "TO_LONG(1)"), Float.class);
+ put(Pair.of("TO_FLOAT(3)", "3.0"), Double.class);
+ put(Pair.of("TO_FLOAT(3)", "TO_FLOAT(3)"), Float.class);
+ put(Pair.of("TO_FLOAT(3)", "3"), Float.class);
+
+ put(Pair.of("TO_LONG(1)", "TO_LONG(1)"), Long.class);
+ put(Pair.of("TO_LONG(1)", "3.0"), Double.class);
+ put(Pair.of("TO_LONG(1)", "TO_FLOAT(3)"), Float.class);
+ put(Pair.of("TO_LONG(1)", "3"), Long.class);
+
+ put(Pair.of("3.0", "TO_LONG(1)"), Double.class);
+ put(Pair.of("3.0", "3.0"), Double.class);
+ put(Pair.of("3.0", "TO_FLOAT(3)"), Double.class);
+ put(Pair.of("3.0", "3"), Double.class);
+
+ put(Pair.of("3", "TO_LONG(1)"), Long.class);
+ put(Pair.of("3", "3.0"), Double.class);
+ put(Pair.of("3", "TO_FLOAT(3)"), Float.class);
+ put(Pair.of("3", "3"), Integer.class);
+ }};
+
+ expectedReturnTypeMappings.forEach( (pair, expectedClass) -> {
+ assertTrue(run(pair.getLeft() + " * " + pair.getRight(), ImmutableMap.of()).getClass() == expectedClass);
+ assertTrue(run(pair.getLeft() + " + " + pair.getRight(), ImmutableMap.of()).getClass() == expectedClass);
+ assertTrue(run(pair.getLeft() + " - " + pair.getRight(), ImmutableMap.of()).getClass() == expectedClass);
+ assertTrue(run(pair.getLeft() + " / " + pair.getRight(), ImmutableMap.of()).getClass() == expectedClass);
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarTest.java
index 921fb7c..1f68e5d 100644
--- a/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarTest.java
+++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarTest.java
@@ -1,4 +1,4 @@
-/**
+/*
* 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
@@ -18,22 +18,12 @@
package org.apache.metron.common.stellar;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.metron.common.dsl.ParseException;
import org.apache.metron.common.dsl.Stellar;
import org.apache.metron.common.dsl.StellarFunction;
-import org.apache.metron.common.dsl.Context;
-import org.apache.metron.common.dsl.MapVariableResolver;
-import org.apache.metron.common.dsl.ParseException;
-import org.apache.metron.common.dsl.Stellar;
-import org.apache.metron.common.dsl.StellarFunction;
-import org.apache.metron.common.dsl.StellarFunctions;
-import org.apache.metron.common.dsl.VariableResolver;
-import org.apache.metron.common.utils.SerDeUtils;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
@@ -49,9 +39,8 @@ import java.util.HashSet;
import java.util.Map;
import static org.apache.metron.common.dsl.functions.resolver.ClasspathFunctionResolver.effectiveClassPathUrls;
-import static java.util.function.Function.identity;
-import static org.apache.metron.common.utils.StellarProcessorUtils.runPredicate;
import static org.apache.metron.common.utils.StellarProcessorUtils.run;
+import static org.apache.metron.common.utils.StellarProcessorUtils.runPredicate;
public class StellarTest {
@@ -90,24 +79,24 @@ public class StellarTest {
@Test
public void testIfThenElseBug1() {
String query = "50 + (true == true ? 10 : 20)";
- Assert.assertEquals(60.0, run(query, new HashMap<>()));
+ Assert.assertEquals(60, run(query, new HashMap<>()));
}
@Test
public void testIfThenElseBug2() {
String query = "50 + (true == false ? 10 : 20)";
- Assert.assertEquals(70.0, run(query, new HashMap<>()));
+ Assert.assertEquals(70, run(query, new HashMap<>()));
}
@Test
public void testIfThenElseBug3() {
String query = "50 * (true == false ? 2 : 10) + 20";
- Assert.assertEquals(520.0, run(query, new HashMap<>()));
+ Assert.assertEquals(520, run(query, new HashMap<>()));
}
@Test
public void testIfThenElseBug4() {
- String query = "TO_INTEGER(true == true ? 10 : 20 )";
+ String query = "TO_INTEGER(true == true ? 10.0 : 20.0 )";
Assert.assertEquals(10, run(query, new HashMap<>()));
}
@@ -191,22 +180,6 @@ public class StellarTest {
}
@Test
- public void testArithmetic() {
- {
- String query = "1 + 2";
- Assert.assertEquals(3, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
- }
- {
- String query = "1.2 + 2";
- Assert.assertEquals(3.2, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
- }
- {
- String query = "1.2e-3 + 2";
- Assert.assertEquals(1.2e-3 + 2, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
- }
- }
-
- @Test
public void testIfThenElse() {
{
String query = "if STARTS_WITH(casey, 'case') then 'one' else 'two'";
@@ -251,62 +224,11 @@ public class StellarTest {
}
{
String query = "1 < 2 ? one*3 : 'two'";
- Assert.assertTrue(Math.abs(3.0 - (double)run(query, ImmutableMap.of("one", 1))) < 1e-6);
+ Assert.assertTrue(Math.abs(3 - (int) run(query, ImmutableMap.of("one", 1))) < 1e-6);
}
}
@Test
- public void testNumericOperations() {
- {
- String query = "TO_INTEGER(1 + 2*2 + 3 - 4 - 0.5)";
- Assert.assertEquals(3, (Integer)run(query, new HashMap<>()), 1e-6);
- }
- {
- String query = "1 + 2*2 + 3 - 4 - 0.5";
- Assert.assertEquals(3.5, (Double)run(query, new HashMap<>()), 1e-6);
- }
- {
- String query = "2*one*(1 + 2*2 + 3 - 4)";
- Assert.assertEquals(8, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
- }
- {
- String query = "2*(1 + 2 + 3 - 4)";
- Assert.assertEquals(4, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
- }
- {
- String query = "1 + 2 + 3 - 4 - 2";
- Assert.assertEquals(0, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
- }
- {
- String query = "1 + 2 + 3 + 4";
- Assert.assertEquals(10, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
- }
- {
- String query = "(one + 2)*3";
- Assert.assertEquals(9, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
- }
- {
- String query = "TO_INTEGER((one + 2)*3.5)";
- Assert.assertEquals(10, (Integer)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
- }
- {
- String query = "1 + 2*3";
- Assert.assertEquals(7, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
- }
- {
- String query = "TO_LONG(foo)";
- Assert.assertNull(run(query,ImmutableMap.of("foo","not a number")));
- }
- {
- String query = "TO_LONG(foo)";
- Assert.assertEquals(232321L,run(query,ImmutableMap.of("foo","00232321")));
- }
- {
- String query = "TO_LONG(foo)";
- Assert.assertEquals(Long.MAX_VALUE,run(query,ImmutableMap.of("foo", Long.toString(Long.MAX_VALUE))));
- }
- }
- @Test
public void testHappyPath() {
String query = "TO_UPPER(TRIM(foo))";
Assert.assertEquals("CASEY", run(query, ImmutableMap.of("foo", "casey ")));
@@ -626,5 +548,4 @@ public class StellarTest {
thrown.expectMessage("The rule 'TO_UPPER(protocol)' does not return a boolean value.");
runPredicate("TO_UPPER(protocol)", v -> variableMap.get(v));
}
-
}
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluatorTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluatorTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluatorTest.java
new file mode 100644
index 0000000..042f9ac
--- /dev/null
+++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluatorTest.java
@@ -0,0 +1,412 @@
+/*
+ * 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.metron.common.stellar.evaluators;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.metron.common.dsl.Token;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SuppressWarnings("unchecked")
+public class ArithmeticEvaluatorTest {
+ ArithmeticEvaluator evaluator = new ArithmeticEvaluator();
+
+ @Test
+ public void evaluateDoubleShouldReturnDoubleAdd() throws Exception {
+ Token<Integer> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1);
+
+ Token<Double> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2D);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+ assertTrue(evaluated.getValue() instanceof Double);
+ assertEquals(3.0D, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateIntegerShouldReturnIntegerAdd() throws Exception {
+ Token<Integer> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+ assertTrue(evaluated.getValue() instanceof Integer);
+ assertEquals(3, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateFloatsShouldReturnFloatAdd() throws Exception {
+ Token<Float> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1F);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+ assertTrue(evaluated.getValue() instanceof Float);
+ assertEquals(3F, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateLongsShouldReturnLongAdd() throws Exception {
+ Token<Long> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1L);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+ assertTrue(evaluated.getValue() instanceof Long);
+ assertEquals(3L, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateIntegerShouldReturnDoubleMul() throws Exception {
+ Token<Integer> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1);
+
+ Token<Double> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2D);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), p);
+
+ assertTrue(evaluated.getValue() instanceof Double);
+ assertEquals(2.0D, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateIntegerShouldReturnIntegerMul() throws Exception {
+ Token<Integer> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), p);
+
+ assertTrue(evaluated.getValue() instanceof Integer);
+ assertEquals(2, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateFloatsShouldReturnFloatMul() throws Exception {
+ Token<Float> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1F);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), p);
+
+ assertTrue(evaluated.getValue() instanceof Float);
+ assertEquals(2F, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateLongsShouldReturnLongMul() throws Exception {
+ Token<Long> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1L);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), p);
+
+ assertTrue(evaluated.getValue() instanceof Long);
+ assertEquals(2L, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateDoubleShouldReturnDoubleSub() throws Exception {
+ Token<Integer> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1);
+
+ Token<Double> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2D);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), p);
+
+ assertTrue(evaluated.getValue() instanceof Double);
+ assertEquals(-1.0D, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateIntegerShouldReturnIntegerSub() throws Exception {
+ Token<Integer> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), p);
+
+ assertTrue(evaluated.getValue() instanceof Integer);
+ assertEquals(-1, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateFloatsShouldReturnFloatSub() throws Exception {
+ Token<Float> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1F);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), p);
+
+ assertTrue(evaluated.getValue() instanceof Float);
+ assertEquals(-1F, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateLongsShouldReturnLongSub() throws Exception {
+ Token<Long> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1L);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), p);
+
+ assertTrue(evaluated.getValue() instanceof Long);
+ assertEquals(-1L, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateDoubleShouldReturnDoubleDiv() throws Exception {
+ Token<Integer> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1);
+
+ Token<Double> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2D);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+
+ assertTrue(evaluated.getValue() instanceof Double);
+ assertEquals(1 / 2D, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateIntegerShouldReturnIntegerDiv() throws Exception {
+ Token<Integer> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+
+ assertTrue(evaluated.getValue() instanceof Integer);
+ assertEquals(1 / 2, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateFloatsShouldReturnFloatDiv() throws Exception {
+ Token<Float> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1F);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+
+ assertTrue(evaluated.getValue() instanceof Float);
+ assertEquals(0.5F, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateLongsShouldReturnLongDiv() throws Exception {
+ Token<Long> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1L);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+
+ assertTrue(evaluated.getValue() instanceof Long);
+ assertEquals(0L, evaluated.getValue());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void evaluateShouldThroughIllegalArgumentExceptionWhenInputIsNull() throws Exception {
+ evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void evaluateShouldThroughIllegalArgumentExceptionWhenInputsKeyIsNull() throws Exception {
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(null, mock(Token.class));
+ evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void evaluateShouldThroughIllegalArgumentExceptionWhenInputsValueIsNull() throws Exception {
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(mock(Token.class), null);
+ evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+ }
+
+ @Test
+ public void evaluateShouldConvertShortsToIntegersType() throws Exception {
+ Token<Short> l = mock(Token.class);
+ when(l.getValue()).thenReturn((short) 2);
+
+ Token<Short> r = mock(Token.class);
+ when(r.getValue()).thenReturn((short) 3);
+
+ Token<? extends Number> evaluated0 = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), Pair.of(l, r));
+ Token<? extends Number> evaluated1 = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), Pair.of(l, r));
+ Token<? extends Number> evaluated2 = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), Pair.of(l, r));
+ Token<? extends Number> evaluated3 = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), Pair.of(l, r));
+
+ assertTrue(evaluated0.getValue() instanceof Integer);
+ assertEquals(5, evaluated0.getValue());
+
+ assertTrue(evaluated1.getValue() instanceof Integer);
+ assertEquals(-1, evaluated1.getValue());
+
+ assertTrue(evaluated2.getValue() instanceof Integer);
+ assertEquals(6, evaluated2.getValue());
+
+ assertTrue(evaluated3.getValue() instanceof Integer);
+ assertEquals(0, evaluated3.getValue());
+ }
+
+ @Test
+ public void evaluateIntegerShouldReturnIntegerWhenLeftsValueIsNull() throws Exception {
+ Token<Integer> l = mock(Token.class);
+ when(l.getValue()).thenReturn(null);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(2);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+ assertTrue(evaluated.getValue() instanceof Integer);
+ assertEquals(2, evaluated.getValue());
+ }
+
+ @Test
+ public void evaluateIntegerShouldReturnIntegerWhenRightsValueIsNull() throws Exception {
+ Token<Integer> l = mock(Token.class);
+ when(l.getValue()).thenReturn(1);
+
+ Token<Integer> r = mock(Token.class);
+ when(r.getValue()).thenReturn(null);
+
+ Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+ Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+ assertTrue(evaluated.getValue() instanceof Integer);
+ assertEquals(1, evaluated.getValue());
+ }
+
+ @Test
+ public void verifyExpectedReturnTypes() throws Exception {
+ Token<Integer> integer = mock(Token.class);
+ when(integer.getValue()).thenReturn(1);
+
+ Token<Long> lng = mock(Token.class);
+ when(lng.getValue()).thenReturn(1L);
+
+ Token<Double> dbl = mock(Token.class);
+ when(dbl.getValue()).thenReturn(1.0D);
+
+ Token<Float> flt = mock(Token.class);
+ when(flt.getValue()).thenReturn(1.0F);
+
+ Map<Pair<Token<? extends Number>, Token<? extends Number>>, Class<? extends Number>> expectedReturnTypeMappings =
+ new HashMap<Pair<Token<? extends Number>, Token<? extends Number>>, Class<? extends Number>>() {{
+ put(Pair.of(flt, lng), Float.class);
+ put(Pair.of(flt, dbl), Double.class);
+ put(Pair.of(flt, flt), Float.class);
+ put(Pair.of(flt, integer), Float.class);
+
+ put(Pair.of(lng, lng), Long.class);
+ put(Pair.of(lng, dbl), Double.class);
+ put(Pair.of(lng, flt), Float.class);
+ put(Pair.of(lng, integer), Long.class);
+
+ put(Pair.of(dbl, lng), Double.class);
+ put(Pair.of(dbl, dbl), Double.class);
+ put(Pair.of(dbl, flt), Double.class);
+ put(Pair.of(dbl, integer), Double.class);
+
+ put(Pair.of(integer, lng), Long.class);
+ put(Pair.of(integer, dbl), Double.class);
+ put(Pair.of(integer, flt), Float.class);
+ put(Pair.of(integer, integer), Integer.class);
+ }};
+
+ expectedReturnTypeMappings.forEach( (pair, expectedClass) -> {
+ assertTrue(evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), pair).getValue().getClass() == expectedClass);
+ assertTrue(evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), pair).getValue().getClass() == expectedClass);
+ assertTrue(evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), pair).getValue().getClass() == expectedClass);
+ assertTrue(evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), pair).getValue().getClass() == expectedClass);
+ });
+ }
+}