1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.atlassian.fugue;
17
18 import org.junit.Test;
19
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.Iterator;
23 import java.util.concurrent.atomic.AtomicInteger;
24 import java.util.function.Function;
25 import java.util.function.Predicate;
26
27 import static io.atlassian.fugue.Iterables.filter;
28 import static io.atlassian.fugue.Iterables.memoize;
29 import static io.atlassian.fugue.Iterables.map;
30 import static org.hamcrest.MatcherAssert.assertThat;
31 import static org.hamcrest.Matchers.contains;
32 import static org.hamcrest.Matchers.equalTo;
33 import static org.hamcrest.Matchers.is;
34
35 @SuppressWarnings("unused") public class IterablesMemoizeTest {
36 @Test public void assertThatFunctionTransformingSingletonIterableIsOnlyCalledOnce() {
37 final CountingFunction<Integer, String> toString = counting(Object::toString);
38 final Iterable<String> memoized = memoize(map(Collections.singletonList(1), toString::apply));
39
40
41 for (final String ignore : memoized) {}
42 for (final String ignore : memoized) {}
43
44 assertThat(toString.count.get(), is(equalTo(1)));
45 }
46
47 @Test public void assertThatFunctionTransformingMultiElementIterableIsOnlyCalledOncePerElement() {
48 final CountingFunction<Integer, String> toString = counting(Object::toString);
49 final Iterable<String> memoized = memoize(map(Arrays.asList(1, 2, 3, 4), toString::apply));
50
51
52 for (final String ignore : memoized) {}
53 for (final String ignore : memoized) {}
54
55 assertThat(toString.count.get(), is(equalTo(4)));
56 }
57
58 @Test public void assertThatMemoizedTransformedIterableHasSameElementsAsOriginalIterable() {
59 final CountingFunction<Integer, String> counting = counting(Object::toString);
60 assertThat(memoize(map(Arrays.asList(1, 2, 3, 4), counting::apply)), contains("1", "2", "3", "4"));
61 }
62
63 @Test public void assertThatMemoizedTransformedIterableHasSameElementsAsOriginalIterableOnSecondIteration() {
64 final CountingFunction<Integer, String> counting = counting(Object::toString);
65 final Iterable<String> memoized = memoize(map(Arrays.asList(1, 2, 3, 4), counting::apply));
66 for (final String ignore : memoized) {}
67 assertThat(memoized, contains("1", "2", "3", "4"));
68 }
69
70 @Test public void assertThatPredicateUsedWhenFilteringIterableIsOnlyCalledOncePerElement() {
71 final CountingPredicate<Integer> even = counting(even());
72 final Iterable<Integer> filtered = filter(Arrays.asList(1, 2, 3, 4), even::test);
73 final Iterable<Integer> memoized = memoize(filtered);
74
75
76 for (final Integer ignore : memoized) {}
77 for (final Integer ignore : memoized) {}
78
79 assertThat(even.count.get(), is(equalTo(4)));
80 }
81
82 @Test public void assertThatMemoizedFilteredIterableHasSameElementsAsOriginalIterableMinusFilteredElements() {
83 final CountingPredicate<Integer> even = counting(even());
84 final Iterable<Integer> filtered = filter(Arrays.asList(1, 2, 3, 4), even::test);
85 assertThat(memoize(filtered), contains(2, 4));
86 }
87
88 @Test public void assertThatMemoizedFilteredIterableHasSameElementsAsOriginalIterableMinusFitleredElementsOnSecondIteration() {
89 final CountingPredicate<Integer> even = counting(even());
90 final Iterable<Integer> filtered = filter(Arrays.asList(1, 2, 3, 4), even::test);
91 final Iterable<Integer> memoized = memoize(filtered);
92
93 for (final Integer ignore : memoized) {}
94
95 assertThat(memoized, contains(2, 4));
96 }
97
98 @Test public void assertThatIteratingHalfWayThroughMemoizedIterableAndThenIteratingCompletelyHasSameElementsOriginalIterable() {
99 final Iterable<String> memoized = memoize(map(Arrays.asList(1, 2, 3, 4), Object::toString));
100 final Iterator<String> memIt = memoized.iterator();
101 memIt.next();
102 memIt.next();
103 assertThat(memoized, contains("1", "2", "3", "4"));
104 }
105
106 @Test public void assertToString() {
107 final Iterable<String> memoized = memoize(map(Arrays.asList(1, 2, 3, 4), Object::toString));
108 final Iterator<String> memIt = memoized.iterator();
109 memIt.next();
110 memIt.next();
111 assertThat(memoized.toString(), is("[1, 2, 3, 4]"));
112 }
113
114 private <A, B> CountingFunction<A, B> counting(final Function<A, B> f) {
115 return new CountingFunction<>(f);
116 }
117
118 static final class CountingFunction<A, B> implements Function<A, B> {
119 private final Function<A, B> f;
120 private final AtomicInteger count = new AtomicInteger();
121
122 public CountingFunction(final Function<A, B> f) {
123 this.f = f;
124 }
125
126 public B apply(final A a) {
127 count.incrementAndGet();
128 return f.apply(a);
129 }
130 }
131
132 private <A> CountingPredicate<A> counting(final Predicate<A> p) {
133 return new CountingPredicate<A>(p);
134 }
135
136 static final class CountingPredicate<A> implements Predicate<A> {
137 private final Predicate<A> p;
138 private final AtomicInteger count = new AtomicInteger();
139
140 public CountingPredicate(final Predicate<A> p) {
141 this.p = p;
142 }
143
144 public boolean test(final A a) {
145 count.incrementAndGet();
146 return p.test(a);
147 }
148 }
149
150 private Predicate<Integer> even() {
151 return EvenPredicate.INSTANCE;
152 }
153
154 private enum EvenPredicate implements Predicate<Integer> {
155 INSTANCE;
156
157 public boolean test(final Integer i) {
158 return i % 2 == 0;
159 }
160 }
161
162 }