Skip to content

Commit

Permalink
Merge branch 'master' into functions
Browse files Browse the repository at this point in the history
  • Loading branch information
tfij authored Oct 14, 2016
2 parents d719e84 + 665a5dd commit c38281d
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public CompletableFuture<?> getValue(EvalContext context) {

private Object methodCall(Object subject, String methodName, List<?> args) {
try {
Class[] argsTypes = args.stream().map(Object::getClass).toArray(Class[]::new);
Class[] argsTypes = args.stream().map(it -> it == null ? null : it.getClass()).toArray(Class[]::new);
ImmutablePair<Object, Method> chosenMethod = implicitConversion.getAllPossibleConversions(subject)
.map(convertedSubject -> ImmutablePair.of(convertedSubject, findMatchingMethod(convertedSubject, methodName, args)))
.filter(it -> it.getRight().isPresent())
Expand Down Expand Up @@ -97,6 +97,9 @@ private boolean areArgsMatchForMethod(Method method, List<?> args) {
for (int i = 0; i < expectedArgumentsTypes.length; i++) {
Class<?> expectedType = expectedArgumentsTypes[i];
Object arg = args.get(i);
if (arg == null) {
continue;
}
Class<?> givenType = arg.getClass();
if (!ClassUtils.isAssignable(givenType, expectedType) && !implicitConversion.hasConverter(arg, expectedType)) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,40 @@ class OpelEngineConversionsIntegrationSpec extends Specification {
input || expResult
"'opel'.rev()" || "lepo"
"'abc'.rev()" || "cba"
}
def "should use left site as primary type for sum operator and throw exception when conversion failed"() {
def engine = create()
.withImplicitConversion(String, BigDecimal, { string -> new BigDecimal(string) })
.withImplicitConversion(BigDecimal, String, { decimal -> decimal.toPlainString() })
.build()
def variables = ['o': CompletableFuture.completedFuture(new Object())]
when:
engine.eval(input, EvalContext.fromMaps(variables, [:])).get()
then:
thrown Exception
where:
input << ["123+'abc'"]
}
@Unroll
def "should firstly convert right argument when comparing objects in #input"() {
given:
def engine = create()
.withImplicitConversion(String, BigDecimal, { string -> new BigDecimal(string) })
.withImplicitConversion(BigDecimal, String, { decimal -> decimal.toPlainString() })
.build()
expect:
engine.eval(input).get() == expResult
where:
input || expResult
" '55' < 7 " || true //compare strings
" 55 > '7' " || true //compare numbers
}
public static class RichString {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,6 @@ class OpelEngineIntegrationSpec extends Specification {
input << ["123+'abc';;", ";123+'abc';"]
}

def "should use left site as primary type for sum operator and throw exception when conversion failed"() {
def engine = create()
.withImplicitConversion(String, BigDecimal, { string -> new BigDecimal(string) })
.withImplicitConversion(BigDecimal, String, { decimal -> decimal.toPlainString() })
.build()
def variables = ['o': CompletableFuture.completedFuture(new Object())]

when:
engine.eval(input, EvalContext.fromMaps(variables, [:])).get()

then:
thrown Exception

where:
input << ["123+'abc'"]
}

def 'should return parsing result with errors for multi line string'() {
given:
def engine = create().build()
Expand Down Expand Up @@ -140,23 +123,6 @@ xyz'"""
"\t'123' != 124" || true
}
@Unroll
def "should firstly convert right argument when comparing objects in #input"() {
given:
def engine = create()
.withImplicitConversion(String, BigDecimal, { string -> new BigDecimal(string) })
.withImplicitConversion(BigDecimal, String, { decimal -> decimal.toPlainString() })
.build()
expect:
engine.eval(input).get() == expResult
where:
input || expResult
" '55' < 7 " || true //compare strings
" 55 > '7' " || true //compare numbers
}
@Unroll
def "should throw an exception when comparing invalid objects in #input expression"() {
given:
Expand Down Expand Up @@ -255,47 +221,6 @@ xyz'"""
| ^\n'''.stripMargin()
}
@Unroll
def 'should call object methods for input #input'() {
given:
def engine = create()
.withImplicitConversion(String, BigDecimal, { string -> new BigDecimal(string) })
.withImplicitConversion(BigDecimal, String, { decimal -> decimal.toPlainString() })
.build()
def variables = ["var": CompletableFuture.completedFuture(["a", "b", "c"]), "arg": CompletableFuture.completedFuture("a")]
def evalContext = EvalContextBuilder.create().withValues(variables).withFunction("fun", constFunctionReturning('Hello, World!')).build()
expect:
engine.parse(input).eval(evalContext).get() == expResult
where:
input || expResult
"fun().length()" || 13
"fun().contains('ello')" || true
"fun().charAt(1)" || 'e'
"fun().substring(5).length()" || 8
"'Hello, World!'.length()" || 13
"var.contains('a')" || true
"var.contains(arg)" || true
}
@Unroll
def 'should call methods on wrapped object for input #input'() {
def engine = create()
.withImplicitConversion(String, RichString, { string -> new RichString(string) })
.build()
expect:
engine.eval(input).get() == expResult
where:
input || expResult
"'opel'.rev()" || "lepo"
"'abc'.rev()" || "cba"
}
public static class RichString {
private final String delegate;
Expand Down Expand Up @@ -356,7 +281,6 @@ xyz'"""
"if (1 == 1 && 2 == 3) 1 == 2 && 2 == 2 else 1 == 1 && 2 == 2" || true
"if (false || false || false || true) 'a' else 'b'" || 'a'
"(if (true) 'a' else 'b').length()" || 1
}
def 'should calculate only left value when condition result result is true'() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package pl.allegro.tech.opel

import spock.lang.Specification
import spock.lang.Unroll

import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException

import static OpelEngineBuilder.create
import static pl.allegro.tech.opel.TestUtil.constFunctionReturning
import static pl.allegro.tech.opel.TestUtil.functions

class OpelEngineMethodCallIntegrationSpec extends Specification {

@Unroll
def 'should call object methods for input #input'() {
given:
def engine = create()
.withImplicitConversion(String, BigDecimal, { string -> new BigDecimal(string) })
.withImplicitConversion(BigDecimal, String, { decimal -> decimal.toPlainString() })
.build()

def variables = ["var": CompletableFuture.completedFuture(["a", "b", "c"]), "arg": CompletableFuture.completedFuture("a")]

def evalContext = EvalContextBuilder.create().withValues(variables).withFunction("fun", constFunctionReturning('Hello, World!')).build()

expect:
engine.parse(input).eval(evalContext).get() == expResult

where:
input || expResult
"fun().length()" || 13
"fun().contains('ello')" || true
"fun().charAt(1)" || 'e'
"fun().substring(5).length()" || 8
"'Hello, World!'.length()" || 13
"var.contains('a')" || true
"var.contains(arg)" || true
}

def "should call method with null value as argument"() {
given:
def engine = create().build()

expect:
engine.eval("['a', 'b', 'c'].contains(null)").get() == false
}

@Unroll
def 'should call methods on wrapped object for input #input'() {
def engine = create()
.withImplicitConversion(String, RichString, { string -> new RichString(string) })
.build()

expect:
engine.eval(input).get() == expResult

where:
input || expResult
"'opel'.rev()" || "lepo"
"'abc'.rev()" || "cba"
}

public static class RichString {
private final String delegate;

RichString(String delegate) {
this.delegate = delegate
}

String rev() {
return delegate.reverse()
}
}
}

0 comments on commit c38281d

Please sign in to comment.