Skip to content

Commit 43bbe8f

Browse files
committed
Add tests for collection selection with Iterables
1 parent 7d612e8 commit 43bbe8f

File tree

1 file changed

+62
-9
lines changed

1 file changed

+62
-9
lines changed

spring-expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Map;
2525
import java.util.Set;
2626
import java.util.TreeMap;
27+
import java.util.stream.IntStream;
2728

2829
import org.junit.jupiter.api.Nested;
2930
import org.junit.jupiter.api.Test;
@@ -38,7 +39,6 @@
3839
import org.springframework.expression.spel.support.StandardEvaluationContext;
3940

4041
import static org.assertj.core.api.Assertions.assertThat;
41-
import static org.assertj.core.api.InstanceOfAssertFactories.LIST;
4242
import static org.assertj.core.api.InstanceOfAssertFactories.array;
4343
import static org.springframework.expression.spel.SpelMessage.INVALID_TYPE_FOR_SELECTION;
4444
import static org.springframework.expression.spel.SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE;
@@ -141,11 +141,56 @@ void selectLastItemInSet() {
141141
}
142142

143143
@ParameterizedTest
144-
@ValueSource(strings = {"#root.?[#this < 3]", "?[#this < 3]"})
144+
@ValueSource(strings = {"#root.?[#this <= 3]", "?[#this <= 3]"})
145+
@SuppressWarnings("unchecked")
145146
void selectionWithIterable(String expressionString) {
146147
Expression expression = new SpelExpressionParser().parseRaw(expressionString);
147-
EvaluationContext context = new StandardEvaluationContext(new IterableTestBean());
148-
assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(1, 2);
148+
EvaluationContext context = new StandardEvaluationContext(new Counter(5));
149+
assertThat(expression.getValue(context, List.class)).containsExactly(1, 2, 3);
150+
}
151+
152+
@ParameterizedTest
153+
@ValueSource(strings = {"#root.^[#this <= 3]", "^[#this <= 3]"})
154+
void selectFirstItemInIterable(String expressionString) {
155+
Expression expression = new SpelExpressionParser().parseRaw(expressionString);
156+
EvaluationContext context = new StandardEvaluationContext(new Counter(5));
157+
assertThat(expression.getValue(context, Integer.class)).isEqualTo(1);
158+
}
159+
160+
@ParameterizedTest
161+
@ValueSource(strings = {"#root.$[#this <= 3]", "$[#this <= 3]"})
162+
void selectLastItemInIterable(String expressionString) {
163+
Expression expression = new SpelExpressionParser().parseRaw(expressionString);
164+
EvaluationContext context = new StandardEvaluationContext(new Counter(5));
165+
assertThat(expression.getValue(context, Integer.class)).isEqualTo(3);
166+
}
167+
168+
@Test
169+
void indexIntoIterableSelection() {
170+
EvaluationContext context = new StandardEvaluationContext();
171+
context.setVariable("counter", new Counter(5));
172+
ExpressionParser parser = new SpelExpressionParser();
173+
174+
// Select first (selection expression is hard-coded to true)
175+
Expression expression = parser.parseExpression("#counter.^[true]");
176+
assertThat(expression.getValue(context, Integer.class)).isEqualTo(1);
177+
178+
// Select last (selection expression is hard-coded to true)
179+
expression = parser.parseExpression("#counter.$[true]");
180+
assertThat(expression.getValue(context, Integer.class)).isEqualTo(5);
181+
182+
// Select index (selection expression is hard-coded to true)
183+
184+
// The following will not work, since we cannot index into an Iterable.
185+
// expression = parser.parseExpression("#counter[1]");
186+
187+
// The following works, since project selection with a selection expression
188+
// that always evaluates to true results in a List containing all elements
189+
// of the Iterable, and we can index into that List.
190+
expression = parser.parseExpression("#counter.?[true][1]");
191+
assertThat(expression.getValue(context, Integer.class)).isEqualTo(2);
192+
expression = parser.parseExpression("#counter.?[true][2]");
193+
assertThat(expression.getValue(context, Integer.class)).isEqualTo(3);
149194
}
150195

151196
@Test
@@ -273,10 +318,11 @@ void projectionWithSet() {
273318

274319
@ParameterizedTest
275320
@ValueSource(strings = {"#root.![#this * 2]", "![#this * 2]"})
321+
@SuppressWarnings("unchecked")
276322
void projectionWithIterable(String expressionString) {
277323
Expression expression = new SpelExpressionParser().parseRaw(expressionString);
278-
EvaluationContext context = new StandardEvaluationContext(new IterableTestBean());
279-
assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(2, 4, 6, 8, 10);
324+
EvaluationContext context = new StandardEvaluationContext(new Counter(5));
325+
assertThat(expression.getValue(context, List.class)).containsExactly(2, 4, 6, 8, 10);
280326
}
281327

282328
@Test
@@ -323,13 +369,20 @@ public Set<Integer> getIntegers() {
323369
}
324370

325371

326-
static class IterableTestBean implements Iterable<Integer> {
372+
/**
373+
* Simulates a custom {@link Iterable} which is itself not a {@link Collection}.
374+
*/
375+
class Counter implements Iterable<Integer> {
327376

328-
private final Collection<Integer> integers = List.of(1, 2, 3, 4, 5);
377+
private final List<Integer> list = new ArrayList<>();
378+
379+
Counter(int size) {
380+
IntStream.rangeClosed(1, size).forEach(this.list::add);
381+
}
329382

330383
@Override
331384
public Iterator<Integer> iterator() {
332-
return integers.iterator();
385+
return this.list.iterator();
333386
}
334387
}
335388

0 commit comments

Comments
 (0)