Polish ExpressionState[Tests]

This commit is contained in:
Sam Brannen 2023-09-30 11:45:20 +02:00
parent 8fa428f825
commit dbf6f7dc9d
2 changed files with 45 additions and 72 deletions

View File

@ -93,6 +93,7 @@ public class ExpressionState {
public ExpressionState(EvaluationContext context, TypedValue rootObject, SpelParserConfiguration configuration) { public ExpressionState(EvaluationContext context, TypedValue rootObject, SpelParserConfiguration configuration) {
Assert.notNull(context, "EvaluationContext must not be null"); Assert.notNull(context, "EvaluationContext must not be null");
Assert.notNull(context, "'rootObject' must not be null");
Assert.notNull(configuration, "SpelParserConfiguration must not be null"); Assert.notNull(configuration, "SpelParserConfiguration must not be null");
this.relatedContext = context; this.relatedContext = context;
this.rootObject = rootObject; this.rootObject = rootObject;
@ -203,7 +204,7 @@ public class ExpressionState {
/* /*
* A new scope is entered when a function is invoked. * A new scope is entered when a function is invoked.
*/ */
public void enterScope(Map<String, Object> argMap) { public void enterScope(@Nullable Map<String, Object> argMap) {
initVariableScopes().push(new VariableScope(argMap)); initVariableScopes().push(new VariableScope(argMap));
initScopeRootObjects().push(getActiveContextObject()); initScopeRootObjects().push(getActiveContextObject());
} }
@ -300,9 +301,10 @@ public class ExpressionState {
} }
public VariableScope(String name, Object value) { public VariableScope(String name, Object value) {
this.vars.put(name,value); this.vars.put(name, value);
} }
@Nullable
public Object lookupVariable(String name) { public Object lookupVariable(String name) {
return this.vars.get(name); return this.vars.get(name);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
package org.springframework.expression.spel; package org.springframework.expression.spel;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -34,27 +33,31 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/** /**
* Tests for the expression state object - some features are not yet exploited in the language (eg nested scopes) * Tests for {@link ExpressionState}.
*
* <p>Some features are not yet exploited in the language, such as nested scopes
* or local variables scoped to the currently evaluated expression.
*
* <p>Local variables are in variable scopes which come and go during evaluation.
* Normal/global variables are accessible through the {@link EvaluationContext}.
* *
* @author Andy Clement * @author Andy Clement
* @author Juergen Hoeller * @author Juergen Hoeller
*/ */
public class ExpressionStateTests extends AbstractExpressionTests { class ExpressionStateTests extends AbstractExpressionTests {
private ExpressionState state = new ExpressionState(TestScenarioCreator.getTestEvaluationContext());
@Test @Test
public void testConstruction() { void construction() {
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
ExpressionState state = new ExpressionState(context); ExpressionState state = new ExpressionState(context);
assertThat(state.getEvaluationContext()).isEqualTo(context); assertThat(state.getEvaluationContext()).isEqualTo(context);
} }
// Local variables are in variable scopes which come and go during evaluation. Normal variables are
// accessible through the evaluation context
@Test @Test
public void testLocalVariables() { void localVariables() {
ExpressionState state = getState();
Object value = state.lookupLocalVariable("foo"); Object value = state.lookupLocalVariable("foo");
assertThat(value).isNull(); assertThat(value).isNull();
@ -68,8 +71,7 @@ public class ExpressionStateTests extends AbstractExpressionTests {
} }
@Test @Test
public void testVariables() { void globalVariables() {
ExpressionState state = getState();
TypedValue typedValue = state.lookupVariable("foo"); TypedValue typedValue = state.lookupVariable("foo");
assertThat(typedValue).isEqualTo(TypedValue.NULL); assertThat(typedValue).isEqualTo(TypedValue.NULL);
@ -85,8 +87,7 @@ public class ExpressionStateTests extends AbstractExpressionTests {
} }
@Test @Test
public void testNoVariableInterference() { void noVariableInterference() {
ExpressionState state = getState();
TypedValue typedValue = state.lookupVariable("foo"); TypedValue typedValue = state.lookupVariable("foo");
assertThat(typedValue).isEqualTo(TypedValue.NULL); assertThat(typedValue).isEqualTo(TypedValue.NULL);
@ -99,8 +100,7 @@ public class ExpressionStateTests extends AbstractExpressionTests {
} }
@Test @Test
public void testLocalVariableNestedScopes() { void localVariableNestedScopes() {
ExpressionState state = getState();
assertThat(state.lookupLocalVariable("foo")).isNull(); assertThat(state.lookupLocalVariable("foo")).isNull();
state.setLocalVariable("foo",12); state.setLocalVariable("foo",12);
@ -120,30 +120,26 @@ public class ExpressionStateTests extends AbstractExpressionTests {
} }
@Test @Test
public void testRootContextObject() { void rootContextObject() {
ExpressionState state = getState();
assertThat(state.getRootContextObject().getValue().getClass()).isEqualTo(Inventor.class); assertThat(state.getRootContextObject().getValue().getClass()).isEqualTo(Inventor.class);
// although the root object is being set on the evaluation context, the value in the 'state' remains what it was when constructed // Although the root object is being set on the evaluation context,
// the value in the 'state' remains what it was when constructed.
((StandardEvaluationContext) state.getEvaluationContext()).setRootObject(null); ((StandardEvaluationContext) state.getEvaluationContext()).setRootObject(null);
assertThat(state.getRootContextObject().getValue().getClass()).isEqualTo(Inventor.class); assertThat(state.getRootContextObject().getValue()).isInstanceOf(Inventor.class);
// assertEquals(null, state.getRootContextObject().getValue());
state = new ExpressionState(new StandardEvaluationContext()); state = new ExpressionState(new StandardEvaluationContext());
assertThat(state.getRootContextObject()).isEqualTo(TypedValue.NULL); assertThat(state.getRootContextObject()).isEqualTo(TypedValue.NULL);
((StandardEvaluationContext) state.getEvaluationContext()).setRootObject(null); ((StandardEvaluationContext) state.getEvaluationContext()).setRootObject(null);
assertThat(state.getRootContextObject().getValue()).isNull(); assertThat(state.getRootContextObject().getValue()).isNull();
} }
@Test @Test
public void testActiveContextObject() { void activeContextObject() {
ExpressionState state = getState();
assertThat(state.getActiveContextObject().getValue()).isEqualTo(state.getRootContextObject().getValue()); assertThat(state.getActiveContextObject().getValue()).isEqualTo(state.getRootContextObject().getValue());
assertThatIllegalStateException().isThrownBy( assertThatIllegalStateException().isThrownBy(state::popActiveContextObject);
state::popActiveContextObject);
state.pushActiveContextObject(new TypedValue(34)); state.pushActiveContextObject(new TypedValue(34));
assertThat(state.getActiveContextObject().getValue()).isEqualTo(34); assertThat(state.getActiveContextObject().getValue()).isEqualTo(34);
@ -162,8 +158,7 @@ public class ExpressionStateTests extends AbstractExpressionTests {
} }
@Test @Test
public void testPopulatedNestedScopes() { void populatedNestedScopes() {
ExpressionState state = getState();
assertThat(state.lookupLocalVariable("foo")).isNull(); assertThat(state.lookupLocalVariable("foo")).isNull();
state.enterScope("foo",34); state.enterScope("foo",34);
@ -181,27 +176,22 @@ public class ExpressionStateTests extends AbstractExpressionTests {
} }
@Test @Test
public void testRootObjectConstructor() { void rootObjectConstructor() {
EvaluationContext ctx = getContext(); EvaluationContext ctx = TestScenarioCreator.getTestEvaluationContext();
// TypedValue root = ctx.getRootObject(); // TypedValue root = ctx.getRootObject();
// supplied should override root on context // supplied should override root on context
ExpressionState state = new ExpressionState(ctx,new TypedValue("i am a string")); ExpressionState state = new ExpressionState(ctx, new TypedValue("i am a string"));
TypedValue stateRoot = state.getRootContextObject(); TypedValue stateRoot = state.getRootContextObject();
assertThat(stateRoot.getTypeDescriptor().getType()).isEqualTo(String.class); assertThat(stateRoot.getTypeDescriptor().getType()).isEqualTo(String.class);
assertThat(stateRoot.getValue()).isEqualTo("i am a string"); assertThat(stateRoot.getValue()).isEqualTo("i am a string");
} }
@Test @Test
public void testPopulatedNestedScopesMap() { void populatedNestedScopesMap() {
ExpressionState state = getState();
assertThat(state.lookupLocalVariable("foo")).isNull(); assertThat(state.lookupLocalVariable("foo")).isNull();
assertThat(state.lookupLocalVariable("goo")).isNull(); assertThat(state.lookupLocalVariable("goo")).isNull();
Map<String,Object> m = new HashMap<>(); state.enterScope(Map.of("foo", 34, "goo", "abc"));
m.put("foo", 34);
m.put("goo", "abc");
state.enterScope(m);
assertThat(state.lookupLocalVariable("foo")).isEqualTo(34); assertThat(state.lookupLocalVariable("foo")).isEqualTo(34);
assertThat(state.lookupLocalVariable("goo")).isEqualTo("abc"); assertThat(state.lookupLocalVariable("goo")).isEqualTo("abc");
@ -217,61 +207,42 @@ public class ExpressionStateTests extends AbstractExpressionTests {
} }
@Test @Test
public void testOperators() { void operators() {
ExpressionState state = getState(); assertThatExceptionOfType(SpelEvaluationException.class)
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> .isThrownBy(() -> state.operate(Operation.ADD,1,2))
state.operate(Operation.ADD,1,2))
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES)); .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES));
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> assertThatExceptionOfType(SpelEvaluationException.class)
state.operate(Operation.ADD,null,null)) .isThrownBy(() -> state.operate(Operation.ADD,null,null))
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES)); .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES));
} }
@Test @Test
public void testComparator() { void comparator() {
ExpressionState state = getState();
assertThat(state.getTypeComparator()).isEqualTo(state.getEvaluationContext().getTypeComparator()); assertThat(state.getTypeComparator()).isEqualTo(state.getEvaluationContext().getTypeComparator());
} }
@Test @Test
public void testTypeLocator() throws EvaluationException { void typeLocator() throws EvaluationException {
ExpressionState state = getState();
assertThat(state.getEvaluationContext().getTypeLocator()).isNotNull(); assertThat(state.getEvaluationContext().getTypeLocator()).isNotNull();
assertThat(state.findType("java.lang.Integer")).isEqualTo(Integer.class); assertThat(state.findType("java.lang.Integer")).isEqualTo(Integer.class);
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> assertThatExceptionOfType(SpelEvaluationException.class)
state.findType("someMadeUpName")) .isThrownBy(() -> state.findType("someMadeUpName"))
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.TYPE_NOT_FOUND)); .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.TYPE_NOT_FOUND));
} }
@Test @Test
public void testTypeConversion() throws EvaluationException { void typeConversion() throws EvaluationException {
ExpressionState state = getState();
String s = (String) state.convertValue(34, TypeDescriptor.valueOf(String.class)); String s = (String) state.convertValue(34, TypeDescriptor.valueOf(String.class));
assertThat(s).isEqualTo("34"); assertThat(s).isEqualTo("34");
s = (String)state.convertValue(new TypedValue(34), TypeDescriptor.valueOf(String.class)); s = (String) state.convertValue(new TypedValue(34), TypeDescriptor.valueOf(String.class));
assertThat(s).isEqualTo("34"); assertThat(s).isEqualTo("34");
} }
@Test @Test
public void testPropertyAccessors() { void propertyAccessors() {
ExpressionState state = getState();
assertThat(state.getPropertyAccessors()).isEqualTo(state.getEvaluationContext().getPropertyAccessors()); assertThat(state.getPropertyAccessors()).isEqualTo(state.getEvaluationContext().getPropertyAccessors());
} }
/**
* @return a new ExpressionState
*/
private ExpressionState getState() {
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
ExpressionState state = new ExpressionState(context);
return state;
}
private EvaluationContext getContext() {
return TestScenarioCreator.getTestEvaluationContext();
}
} }