Add tests for read-only IndexAccessors in 6.2
This commit is contained in:
parent
b0999641dd
commit
a55207e88f
|
@ -25,6 +25,7 @@ import org.assertj.core.api.ThrowableTypeAssert;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.IndexAccessor;
|
||||||
import org.springframework.expression.spel.CompilableMapAccessor;
|
import org.springframework.expression.spel.CompilableMapAccessor;
|
||||||
import org.springframework.expression.spel.SpelEvaluationException;
|
import org.springframework.expression.spel.SpelEvaluationException;
|
||||||
import org.springframework.expression.spel.SpelMessage;
|
import org.springframework.expression.spel.SpelMessage;
|
||||||
|
@ -45,6 +46,9 @@ import static org.assertj.core.api.Assertions.entry;
|
||||||
*/
|
*/
|
||||||
class SimpleEvaluationContextTests {
|
class SimpleEvaluationContextTests {
|
||||||
|
|
||||||
|
private static final IndexAccessor colorsIndexAccessor =
|
||||||
|
new ReflectiveIndexAccessor(Colors.class, int.class, "get", "set");
|
||||||
|
|
||||||
private final SpelExpressionParser parser = new SpelExpressionParser();
|
private final SpelExpressionParser parser = new SpelExpressionParser();
|
||||||
|
|
||||||
private final Model model = new Model();
|
private final Model model = new Model();
|
||||||
|
@ -52,14 +56,18 @@ class SimpleEvaluationContextTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void forReadWriteDataBinding() {
|
void forReadWriteDataBinding() {
|
||||||
SimpleEvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
|
SimpleEvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding()
|
||||||
|
.withIndexAccessors(colorsIndexAccessor)
|
||||||
|
.build();
|
||||||
|
|
||||||
assertReadWriteMode(context);
|
assertReadWriteMode(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void forReadOnlyDataBinding() {
|
void forReadOnlyDataBinding() {
|
||||||
SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
|
SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding()
|
||||||
|
.withIndexAccessors(colorsIndexAccessor)
|
||||||
|
.build();
|
||||||
|
|
||||||
assertCommonReadOnlyModeBehavior(context);
|
assertCommonReadOnlyModeBehavior(context);
|
||||||
|
|
||||||
|
@ -96,12 +104,16 @@ class SimpleEvaluationContextTests {
|
||||||
|
|
||||||
// Object Index
|
// Object Index
|
||||||
assertAssignmentDisabled(context, "['name'] = 'rejected'");
|
assertAssignmentDisabled(context, "['name'] = 'rejected'");
|
||||||
|
|
||||||
|
// Custom Index
|
||||||
|
assertAssignmentDisabled(context, "colors[4] = 'rejected'");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void forPropertyAccessorsInReadWriteMode() {
|
void forPropertyAccessorsInReadWriteMode() {
|
||||||
SimpleEvaluationContext context = SimpleEvaluationContext
|
SimpleEvaluationContext context = SimpleEvaluationContext
|
||||||
.forPropertyAccessors(new CompilableMapAccessor(), DataBindingPropertyAccessor.forReadWriteAccess())
|
.forPropertyAccessors(new CompilableMapAccessor(), DataBindingPropertyAccessor.forReadWriteAccess())
|
||||||
|
.withIndexAccessors(colorsIndexAccessor)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
assertReadWriteMode(context);
|
assertReadWriteMode(context);
|
||||||
|
@ -126,12 +138,13 @@ class SimpleEvaluationContextTests {
|
||||||
@Test
|
@Test
|
||||||
void forPropertyAccessorsInMixedReadOnlyMode() {
|
void forPropertyAccessorsInMixedReadOnlyMode() {
|
||||||
SimpleEvaluationContext context = SimpleEvaluationContext
|
SimpleEvaluationContext context = SimpleEvaluationContext
|
||||||
.forPropertyAccessors(new CompilableMapAccessor(), DataBindingPropertyAccessor.forReadOnlyAccess())
|
.forPropertyAccessors(new CompilableMapAccessor(true), DataBindingPropertyAccessor.forReadOnlyAccess())
|
||||||
|
.withIndexAccessors(colorsIndexAccessor)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
assertCommonReadOnlyModeBehavior(context);
|
assertCommonReadOnlyModeBehavior(context);
|
||||||
|
|
||||||
// Map -- with key as property name supported by CompilableMapAccessor
|
// Map -- with key as property name supported by CompilableMapAccessor with allowWrite = true.
|
||||||
|
|
||||||
Expression expression;
|
Expression expression;
|
||||||
expression = parser.parseExpression("map.yellow");
|
expression = parser.parseExpression("map.yellow");
|
||||||
|
@ -156,20 +169,24 @@ class SimpleEvaluationContextTests {
|
||||||
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE));
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE));
|
||||||
|
|
||||||
// Array Index
|
// Array Index
|
||||||
parser.parseExpression("array[0]").setValue(context, model, "foo");
|
expression = parser.parseExpression("array[0] = 'quux'");
|
||||||
assertThat(model.array).containsExactly("foo");
|
assertThat(expression.getValue(context, model, String.class)).isEqualTo("quux");
|
||||||
|
assertThat(model.array).containsExactly("quux");
|
||||||
|
|
||||||
// List Index
|
// List Index
|
||||||
parser.parseExpression("list[0]").setValue(context, model, "cat");
|
expression = parser.parseExpression("list[0] = 'elephant'");
|
||||||
assertThat(model.list).containsExactly("cat");
|
assertThat(expression.getValue(context, model, String.class)).isEqualTo("elephant");
|
||||||
|
assertThat(model.list).containsExactly("elephant");
|
||||||
|
|
||||||
// Map Index -- key as String
|
// Map Index -- key as String
|
||||||
parser.parseExpression("map['red']").setValue(context, model, "cherry");
|
expression = parser.parseExpression("map['red'] = 'strawberry'");
|
||||||
assertThat(model.map).containsOnly(entry("red", "cherry"), entry("yellow", "banana"));
|
assertThat(expression.getValue(context, model, String.class)).isEqualTo("strawberry");
|
||||||
|
assertThat(model.map).containsOnly(entry("red", "strawberry"), entry("yellow", "banana"));
|
||||||
|
|
||||||
// Map Index -- key as pseudo property name
|
// Map Index -- key as pseudo property name
|
||||||
parser.parseExpression("map[yellow]").setValue(context, model, "lemon");
|
expression = parser.parseExpression("map[yellow] = 'star fruit'");
|
||||||
assertThat(model.map).containsOnly(entry("red", "cherry"), entry("yellow", "lemon"));
|
assertThat(expression.getValue(context, model, String.class)).isEqualTo("star fruit");
|
||||||
|
assertThat(model.map).containsOnly(entry("red", "strawberry"), entry("yellow", "star fruit"));
|
||||||
|
|
||||||
// String Index
|
// String Index
|
||||||
// The Indexer does not support writes when indexing into a String.
|
// The Indexer does not support writes when indexing into a String.
|
||||||
|
@ -178,10 +195,17 @@ class SimpleEvaluationContextTests {
|
||||||
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE));
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE));
|
||||||
|
|
||||||
// Object Index
|
// Object Index
|
||||||
|
// Although this goes through the Indexer, the PropertyAccessorValueRef actually uses
|
||||||
|
// registered PropertyAccessors to perform the write access, and that is disabled here.
|
||||||
assertThatSpelEvaluationException()
|
assertThatSpelEvaluationException()
|
||||||
.isThrownBy(() -> parser.parseExpression("['name'] = 'rejected'").getValue(context, model))
|
.isThrownBy(() -> parser.parseExpression("['name'] = 'rejected'").getValue(context, model))
|
||||||
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE));
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE));
|
||||||
|
|
||||||
|
// Custom Index
|
||||||
|
expression = parser.parseExpression("colors[5] = 'indigo'");
|
||||||
|
assertThat(expression.getValue(context, model, String.class)).isEqualTo("indigo");
|
||||||
|
assertThat(model.colors.get(5)).isEqualTo("indigo");
|
||||||
|
|
||||||
// WRITE -- via increment and decrement operators
|
// WRITE -- via increment and decrement operators
|
||||||
|
|
||||||
assertIncrementAndDecrementWritesForIndexedStructures(context);
|
assertIncrementAndDecrementWritesForIndexedStructures(context);
|
||||||
|
@ -216,6 +240,10 @@ class SimpleEvaluationContextTests {
|
||||||
parser.parseExpression("map[yellow]").setValue(context, model, "lemon");
|
parser.parseExpression("map[yellow]").setValue(context, model, "lemon");
|
||||||
assertThat(model.map).containsOnly(entry("red", "cherry"), entry("yellow", "lemon"));
|
assertThat(model.map).containsOnly(entry("red", "cherry"), entry("yellow", "lemon"));
|
||||||
|
|
||||||
|
// Custom Index
|
||||||
|
parser.parseExpression("colors[4]").setValue(context, model, "purple");
|
||||||
|
assertThat(model.colors.get(4)).isEqualTo("purple");
|
||||||
|
|
||||||
// READ
|
// READ
|
||||||
assertReadAccess(context);
|
assertReadAccess(context);
|
||||||
|
|
||||||
|
@ -270,6 +298,12 @@ class SimpleEvaluationContextTests {
|
||||||
expression = parser.parseExpression("['name']");
|
expression = parser.parseExpression("['name']");
|
||||||
assertThat(expression.getValue(context, model, String.class)).isEqualTo("new name");
|
assertThat(expression.getValue(context, model, String.class)).isEqualTo("new name");
|
||||||
|
|
||||||
|
// Custom Index
|
||||||
|
expression = parser.parseExpression("colors[5] = 'indigo'");
|
||||||
|
assertThat(expression.getValue(context, model, String.class)).isEqualTo("indigo");
|
||||||
|
expression = parser.parseExpression("colors[5]");
|
||||||
|
assertThat(expression.getValue(context, model, String.class)).isEqualTo("indigo");
|
||||||
|
|
||||||
// WRITE -- via increment and decrement operators
|
// WRITE -- via increment and decrement operators
|
||||||
|
|
||||||
assertIncrementAndDecrementWritesForProperties(context);
|
assertIncrementAndDecrementWritesForProperties(context);
|
||||||
|
@ -309,6 +343,10 @@ class SimpleEvaluationContextTests {
|
||||||
parser.parseExpression("map[yellow]").setValue(context, model, "lemon");
|
parser.parseExpression("map[yellow]").setValue(context, model, "lemon");
|
||||||
assertThat(model.map).containsOnly(entry("red", "cherry"), entry("yellow", "lemon"));
|
assertThat(model.map).containsOnly(entry("red", "cherry"), entry("yellow", "lemon"));
|
||||||
|
|
||||||
|
// Custom Index
|
||||||
|
parser.parseExpression("colors[4]").setValue(context, model, "purple");
|
||||||
|
assertThat(model.colors.get(4)).isEqualTo("purple");
|
||||||
|
|
||||||
// Since the setValue() attempts for "name" and "count" failed above, we have to set
|
// Since the setValue() attempts for "name" and "count" failed above, we have to set
|
||||||
// them directly for assertReadAccess().
|
// them directly for assertReadAccess().
|
||||||
model.name = "test";
|
model.name = "test";
|
||||||
|
@ -354,6 +392,10 @@ class SimpleEvaluationContextTests {
|
||||||
// Object Index
|
// Object Index
|
||||||
expression = parser.parseExpression("['name']");
|
expression = parser.parseExpression("['name']");
|
||||||
assertThat(expression.getValue(context, model, String.class)).isEqualTo("test");
|
assertThat(expression.getValue(context, model, String.class)).isEqualTo("test");
|
||||||
|
|
||||||
|
// Custom Index
|
||||||
|
expression = parser.parseExpression("colors[4]");
|
||||||
|
assertThat(expression.getValue(context, model, String.class)).isEqualTo("purple");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertIncrementAndDecrementWritesForProperties(SimpleEvaluationContext context) {
|
private void assertIncrementAndDecrementWritesForProperties(SimpleEvaluationContext context) {
|
||||||
|
@ -433,6 +475,7 @@ class SimpleEvaluationContextTests {
|
||||||
private final int[] numbers = {99};
|
private final int[] numbers = {99};
|
||||||
private final List<String> list = new ArrayList<>();
|
private final List<String> list = new ArrayList<>();
|
||||||
private final Map<String, String> map = new HashMap<>();
|
private final Map<String, String> map = new HashMap<>();
|
||||||
|
private final Colors colors = new Colors();
|
||||||
|
|
||||||
Model() {
|
Model() {
|
||||||
this.list.add("replace me");
|
this.list.add("replace me");
|
||||||
|
@ -472,6 +515,32 @@ class SimpleEvaluationContextTests {
|
||||||
return this.map;
|
return this.map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Colors getColors() {
|
||||||
|
return this.colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Colors {
|
||||||
|
|
||||||
|
private final Map<Integer, String> map = new HashMap<>();
|
||||||
|
|
||||||
|
{
|
||||||
|
this.map.put(1, "red");
|
||||||
|
this.map.put(2, "green");
|
||||||
|
this.map.put(3, "blue");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(int index) {
|
||||||
|
if (!this.map.containsKey(index)) {
|
||||||
|
throw new IndexOutOfBoundsException("No color for index " + index);
|
||||||
|
}
|
||||||
|
return this.map.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int index, String color) {
|
||||||
|
this.map.put(index, color);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue