View Javadoc
1   /*
2      Copyright 2011 Atlassian
3   
4      Licensed under the Apache License, Version 2.0 (the "License");
5      you may not use this file except in compliance with the License.
6      You may obtain a copy of the License at
7   
8          http://www.apache.org/licenses/LICENSE-2.0
9   
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
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.map;
29  import static io.atlassian.fugue.Iterables.memoize;
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      // iterate over it a few times
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      // iterate over it a few times
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      // iterate over it a few times
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 }