• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.collect;
16 
17 import static com.google.common.collect.Streams.stream;
18 
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.annotations.GwtIncompatible;
21 import com.google.common.collect.testing.SpliteratorTester;
22 import com.google.common.primitives.Doubles;
23 import com.google.common.truth.IterableSubject;
24 import com.google.common.truth.Truth;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.LinkedHashSet;
29 import java.util.List;
30 import java.util.OptionalDouble;
31 import java.util.OptionalInt;
32 import java.util.OptionalLong;
33 import java.util.concurrent.atomic.AtomicInteger;
34 import java.util.function.Function;
35 import java.util.stream.DoubleStream;
36 import java.util.stream.IntStream;
37 import java.util.stream.LongStream;
38 import java.util.stream.Stream;
39 import junit.framework.TestCase;
40 
41 /** Unit test for {@link Streams}. */
42 @GwtCompatible(emulated = true)
43 public class StreamsTest extends TestCase {
44   /*
45    * Full and proper black-box testing of a Stream-returning method is extremely involved, and is
46    * overkill when nearly all Streams are produced using well-tested JDK calls. So, we cheat and
47    * just test that the toArray() contents are as expected.
48    */
testStream_nonCollection()49   public void testStream_nonCollection() {
50     assertThat(stream(FluentIterable.of())).isEmpty();
51     assertThat(stream(FluentIterable.of("a"))).containsExactly("a");
52     assertThat(stream(FluentIterable.of(1, 2, 3)).filter(n -> n > 1)).containsExactly(2, 3);
53   }
54 
55   @SuppressWarnings("deprecation")
testStream_collection()56   public void testStream_collection() {
57     assertThat(stream(Arrays.asList())).isEmpty();
58     assertThat(stream(Arrays.asList("a"))).containsExactly("a");
59     assertThat(stream(Arrays.asList(1, 2, 3)).filter(n -> n > 1)).containsExactly(2, 3);
60   }
61 
testStream_iterator()62   public void testStream_iterator() {
63     assertThat(stream(Arrays.asList().iterator())).isEmpty();
64     assertThat(stream(Arrays.asList("a").iterator())).containsExactly("a");
65     assertThat(stream(Arrays.asList(1, 2, 3).iterator()).filter(n -> n > 1)).containsExactly(2, 3);
66   }
67 
testStream_googleOptional()68   public void testStream_googleOptional() {
69     assertThat(stream(com.google.common.base.Optional.absent())).isEmpty();
70     assertThat(stream(com.google.common.base.Optional.of("a"))).containsExactly("a");
71   }
72 
testStream_javaOptional()73   public void testStream_javaOptional() {
74     assertThat(stream(java.util.Optional.empty())).isEmpty();
75     assertThat(stream(java.util.Optional.of("a"))).containsExactly("a");
76   }
77 
testConcat_refStream()78   public void testConcat_refStream() {
79     assertThat(Streams.concat(Stream.of("a"), Stream.of("b"), Stream.empty(), Stream.of("c", "d")))
80         .containsExactly("a", "b", "c", "d")
81         .inOrder();
82     SpliteratorTester.of(
83             () ->
84                 Streams.concat(Stream.of("a"), Stream.of("b"), Stream.empty(), Stream.of("c", "d"))
85                     .spliterator())
86         .expect("a", "b", "c", "d");
87   }
88 
testConcat_refStream_closeIsPropagated()89   public void testConcat_refStream_closeIsPropagated() {
90     AtomicInteger closeCountB = new AtomicInteger(0);
91     Stream<String> streamB = Stream.of("b").onClose(closeCountB::incrementAndGet);
92     Stream<String> concatenated =
93         Streams.concat(Stream.of("a"), streamB, Stream.empty(), Stream.of("c", "d"));
94     assertThat(concatenated).containsExactly("a", "b", "c", "d").inOrder();
95     concatenated.close();
96     Truth.assertThat(closeCountB.get()).isEqualTo(1);
97   }
98 
testConcat_refStream_closeIsPropagated_Stream_concat()99   public void testConcat_refStream_closeIsPropagated_Stream_concat() {
100     // Just to demonstrate behavior of Stream::concat in the standard library
101     AtomicInteger closeCountB = new AtomicInteger(0);
102     Stream<String> streamB = Stream.of("b").onClose(closeCountB::incrementAndGet);
103     Stream<String> concatenated =
104         Stream.<Stream<String>>of(Stream.of("a"), streamB, Stream.empty(), Stream.of("c", "d"))
105             .reduce(Stream.empty(), Stream::concat);
106     assertThat(concatenated).containsExactly("a", "b", "c", "d").inOrder();
107     concatenated.close();
108     Truth.assertThat(closeCountB.get()).isEqualTo(1);
109   }
110 
testConcat_refStream_closeIsPropagated_Stream_flatMap()111   public void testConcat_refStream_closeIsPropagated_Stream_flatMap() {
112     // Just to demonstrate behavior of Stream::flatMap in the standard library
113     AtomicInteger closeCountB = new AtomicInteger(0);
114     Stream<String> streamB = Stream.of("b").onClose(closeCountB::incrementAndGet);
115     Stream<String> concatenated =
116         Stream.<Stream<String>>of(Stream.of("a"), streamB, Stream.empty(), Stream.of("c", "d"))
117             .flatMap(x -> x);
118     assertThat(concatenated).containsExactly("a", "b", "c", "d").inOrder();
119     concatenated.close();
120     // even without close, see doc for flatMap
121     Truth.assertThat(closeCountB.get()).isEqualTo(1);
122   }
123 
testConcat_refStream_parallel()124   public void testConcat_refStream_parallel() {
125     Truth.assertThat(
126             Streams.concat(Stream.of("a"), Stream.of("b"), Stream.empty(), Stream.of("c", "d"))
127                 .parallel()
128                 .toArray())
129         .asList()
130         .containsExactly("a", "b", "c", "d")
131         .inOrder();
132   }
133 
testConcat_intStream()134   public void testConcat_intStream() {
135     assertThat(
136             Streams.concat(IntStream.of(1), IntStream.of(2), IntStream.empty(), IntStream.of(3, 4)))
137         .containsExactly(1, 2, 3, 4)
138         .inOrder();
139   }
140 
testConcat_longStream()141   public void testConcat_longStream() {
142     assertThat(
143             Streams.concat(
144                 LongStream.of(1), LongStream.of(2), LongStream.empty(), LongStream.of(3, 4)))
145         .containsExactly(1L, 2L, 3L, 4L)
146         .inOrder();
147   }
148 
testConcat_doubleStream()149   public void testConcat_doubleStream() {
150     assertThat(
151             Streams.concat(
152                 DoubleStream.of(1),
153                 DoubleStream.of(2),
154                 DoubleStream.empty(),
155                 DoubleStream.of(3, 4)))
156         .containsExactly(1.0, 2.0, 3.0, 4.0)
157         .inOrder();
158   }
159 
testStream_optionalInt()160   public void testStream_optionalInt() {
161     assertThat(stream(OptionalInt.empty())).isEmpty();
162     assertThat(stream(OptionalInt.of(5))).containsExactly(5);
163   }
164 
testStream_optionalLong()165   public void testStream_optionalLong() {
166     assertThat(stream(OptionalLong.empty())).isEmpty();
167     assertThat(stream(OptionalLong.of(5L))).containsExactly(5L);
168   }
169 
testStream_optionalDouble()170   public void testStream_optionalDouble() {
171     assertThat(stream(OptionalDouble.empty())).isEmpty();
172     assertThat(stream(OptionalDouble.of(5.0))).containsExactly(5.0);
173   }
174 
testConcatInfiniteStream()175   public void testConcatInfiniteStream() {
176     assertThat(Streams.concat(Stream.of(1, 2, 3), Stream.generate(() -> 5)).limit(5))
177         .containsExactly(1, 2, 3, 5, 5)
178         .inOrder();
179   }
180 
testConcatInfiniteStream_int()181   public void testConcatInfiniteStream_int() {
182     assertThat(Streams.concat(IntStream.of(1, 2, 3), IntStream.generate(() -> 5)).limit(5))
183         .containsExactly(1, 2, 3, 5, 5)
184         .inOrder();
185   }
186 
testConcatInfiniteStream_long()187   public void testConcatInfiniteStream_long() {
188     assertThat(Streams.concat(LongStream.of(1, 2, 3), LongStream.generate(() -> 5)).limit(5))
189         .containsExactly(1L, 2L, 3L, 5L, 5L)
190         .inOrder();
191   }
192 
testConcatInfiniteStream_double()193   public void testConcatInfiniteStream_double() {
194     assertThat(Streams.concat(DoubleStream.of(1, 2, 3), DoubleStream.generate(() -> 5)).limit(5))
195         .containsExactly(1., 2., 3., 5., 5.)
196         .inOrder();
197   }
198 
testMapWithIndex(Function<Collection<String>, Stream<String>> collectionImpl)199   private void testMapWithIndex(Function<Collection<String>, Stream<String>> collectionImpl) {
200     SpliteratorTester.of(
201             () ->
202                 Streams.mapWithIndex(
203                         collectionImpl.apply(ImmutableList.of()), (str, i) -> str + ":" + i)
204                     .spliterator())
205         .expect(ImmutableList.of());
206     SpliteratorTester.of(
207             () ->
208                 Streams.mapWithIndex(
209                         collectionImpl.apply(ImmutableList.of("a", "b", "c", "d", "e")),
210                         (str, i) -> str + ":" + i)
211                     .spliterator())
212         .expect("a:0", "b:1", "c:2", "d:3", "e:4");
213   }
214 
testMapWithIndex_arrayListSource()215   public void testMapWithIndex_arrayListSource() {
216     testMapWithIndex(elems -> new ArrayList<>(elems).stream());
217   }
218 
testMapWithIndex_linkedHashSetSource()219   public void testMapWithIndex_linkedHashSetSource() {
220     testMapWithIndex(elems -> new LinkedHashSet<>(elems).stream());
221   }
222 
testMapWithIndex_unsizedSource()223   public void testMapWithIndex_unsizedSource() {
224     testMapWithIndex(
225         elems -> Stream.of((Object) null).flatMap(unused -> ImmutableList.copyOf(elems).stream()));
226   }
227 
testMapWithIndex_closeIsPropagated_sizedSource()228   public void testMapWithIndex_closeIsPropagated_sizedSource() {
229     testMapWithIndex_closeIsPropagated(Stream.of("a", "b", "c"));
230   }
231 
testMapWithIndex_closeIsPropagated_unsizedSource()232   public void testMapWithIndex_closeIsPropagated_unsizedSource() {
233     testMapWithIndex_closeIsPropagated(
234         Stream.of((Object) null).flatMap(unused -> Stream.of("a", "b", "c")));
235   }
236 
testMapWithIndex_closeIsPropagated(Stream<String> source)237   private void testMapWithIndex_closeIsPropagated(Stream<String> source) {
238     AtomicInteger stringsCloseCount = new AtomicInteger();
239     Stream<String> strings = source.onClose(stringsCloseCount::incrementAndGet);
240     Stream<String> withIndex = Streams.mapWithIndex(strings, (str, i) -> str + ":" + i);
241 
242     withIndex.close();
243 
244     Truth.assertThat(stringsCloseCount.get()).isEqualTo(1);
245   }
246 
testMapWithIndex_intStream()247   public void testMapWithIndex_intStream() {
248     SpliteratorTester.of(
249             () -> Streams.mapWithIndex(IntStream.of(0, 1, 2), (x, i) -> x + ":" + i).spliterator())
250         .expect("0:0", "1:1", "2:2");
251   }
252 
testMapWithIndex_intStream_closeIsPropagated_sized()253   public void testMapWithIndex_intStream_closeIsPropagated_sized() {
254     testMapWithIndex_intStream_closeIsPropagated(IntStream.of(1, 2, 3));
255   }
256 
testMapWithIndex_intStream_closeIsPropagated_unsized()257   public void testMapWithIndex_intStream_closeIsPropagated_unsized() {
258     testMapWithIndex_intStream_closeIsPropagated(
259         IntStream.of(0).flatMap(unused -> IntStream.of(1, 2, 3)));
260   }
261 
testMapWithIndex_intStream_closeIsPropagated(IntStream source)262   private void testMapWithIndex_intStream_closeIsPropagated(IntStream source) {
263     AtomicInteger intStreamCloseCount = new AtomicInteger();
264     IntStream intStream = source.onClose(intStreamCloseCount::incrementAndGet);
265     Stream<String> withIndex = Streams.mapWithIndex(intStream, (str, i) -> str + ":" + i);
266 
267     withIndex.close();
268 
269     Truth.assertThat(intStreamCloseCount.get()).isEqualTo(1);
270   }
271 
testMapWithIndex_longStream()272   public void testMapWithIndex_longStream() {
273     SpliteratorTester.of(
274             () -> Streams.mapWithIndex(LongStream.of(0, 1, 2), (x, i) -> x + ":" + i).spliterator())
275         .expect("0:0", "1:1", "2:2");
276   }
277 
testMapWithIndex_longStream_closeIsPropagated_sized()278   public void testMapWithIndex_longStream_closeIsPropagated_sized() {
279     testMapWithIndex_longStream_closeIsPropagated(LongStream.of(1, 2, 3));
280   }
281 
testMapWithIndex_longStream_closeIsPropagated_unsized()282   public void testMapWithIndex_longStream_closeIsPropagated_unsized() {
283     testMapWithIndex_longStream_closeIsPropagated(
284         LongStream.of(0).flatMap(unused -> LongStream.of(1, 2, 3)));
285   }
286 
testMapWithIndex_longStream_closeIsPropagated(LongStream source)287   private void testMapWithIndex_longStream_closeIsPropagated(LongStream source) {
288     AtomicInteger longStreamCloseCount = new AtomicInteger();
289     LongStream longStream = source.onClose(longStreamCloseCount::incrementAndGet);
290     Stream<String> withIndex = Streams.mapWithIndex(longStream, (str, i) -> str + ":" + i);
291 
292     withIndex.close();
293 
294     Truth.assertThat(longStreamCloseCount.get()).isEqualTo(1);
295   }
296 
297   @GwtIncompatible // TODO(b/38490623): reenable after GWT double-to-string conversion is fixed
testMapWithIndex_doubleStream()298   public void testMapWithIndex_doubleStream() {
299     SpliteratorTester.of(
300             () ->
301                 Streams.mapWithIndex(DoubleStream.of(0, 1, 2), (x, i) -> x + ":" + i).spliterator())
302         .expect("0.0:0", "1.0:1", "2.0:2");
303   }
304 
testMapWithIndex_doubleStream_closeIsPropagated_sized()305   public void testMapWithIndex_doubleStream_closeIsPropagated_sized() {
306     testMapWithIndex_doubleStream_closeIsPropagated(DoubleStream.of(1, 2, 3));
307   }
308 
testMapWithIndex_doubleStream_closeIsPropagated_unsized()309   public void testMapWithIndex_doubleStream_closeIsPropagated_unsized() {
310     testMapWithIndex_doubleStream_closeIsPropagated(
311         DoubleStream.of(0).flatMap(unused -> DoubleStream.of(1, 2, 3)));
312   }
313 
testMapWithIndex_doubleStream_closeIsPropagated(DoubleStream source)314   private void testMapWithIndex_doubleStream_closeIsPropagated(DoubleStream source) {
315     AtomicInteger doubleStreamCloseCount = new AtomicInteger();
316     DoubleStream doubleStream = source.onClose(doubleStreamCloseCount::incrementAndGet);
317     Stream<String> withIndex = Streams.mapWithIndex(doubleStream, (str, i) -> str + ":" + i);
318 
319     withIndex.close();
320 
321     Truth.assertThat(doubleStreamCloseCount.get()).isEqualTo(1);
322   }
323 
testZip()324   public void testZip() {
325     assertThat(Streams.zip(Stream.of("a", "b", "c"), Stream.of(1, 2, 3), (a, b) -> a + ":" + b))
326         .containsExactly("a:1", "b:2", "c:3")
327         .inOrder();
328   }
329 
testZip_closeIsPropagated()330   public void testZip_closeIsPropagated() {
331     AtomicInteger lettersCloseCount = new AtomicInteger();
332     Stream<String> letters = Stream.of("a", "b", "c").onClose(lettersCloseCount::incrementAndGet);
333     AtomicInteger numbersCloseCount = new AtomicInteger();
334     Stream<Integer> numbers = Stream.of(1, 2, 3).onClose(numbersCloseCount::incrementAndGet);
335 
336     Stream<String> zipped = Streams.zip(letters, numbers, (a, b) -> a + ":" + b);
337 
338     zipped.close();
339 
340     Truth.assertThat(lettersCloseCount.get()).isEqualTo(1);
341     Truth.assertThat(numbersCloseCount.get()).isEqualTo(1);
342   }
343 
testZipFiniteWithInfinite()344   public void testZipFiniteWithInfinite() {
345     assertThat(
346             Streams.zip(
347                 Stream.of("a", "b", "c"), Stream.iterate(1, i -> i + 1), (a, b) -> a + ":" + b))
348         .containsExactly("a:1", "b:2", "c:3")
349         .inOrder();
350   }
351 
testZipInfiniteWithInfinite()352   public void testZipInfiniteWithInfinite() {
353     // zip is doing an infinite zip, but we truncate the result so we can actually test it
354     // but we want the zip itself to work
355     assertThat(
356             Streams.zip(
357                     Stream.iterate(1, i -> i + 1).map(String::valueOf),
358                     Stream.iterate(1, i -> i + 1),
359                     (String str, Integer i) -> str.equals(Integer.toString(i)))
360                 .limit(100))
361         .doesNotContain(false);
362   }
363 
testZipDifferingLengths()364   public void testZipDifferingLengths() {
365     assertThat(
366             Streams.zip(Stream.of("a", "b", "c", "d"), Stream.of(1, 2, 3), (a, b) -> a + ":" + b))
367         .containsExactly("a:1", "b:2", "c:3")
368         .inOrder();
369     assertThat(Streams.zip(Stream.of("a", "b", "c"), Stream.of(1, 2, 3, 4), (a, b) -> a + ":" + b))
370         .containsExactly("a:1", "b:2", "c:3")
371         .inOrder();
372   }
373 
testForEachPair()374   public void testForEachPair() {
375     List<String> list = new ArrayList<>();
376     Streams.forEachPair(
377         Stream.of("a", "b", "c"), Stream.of(1, 2, 3), (a, b) -> list.add(a + ":" + b));
378     Truth.assertThat(list).containsExactly("a:1", "b:2", "c:3");
379   }
380 
testForEachPair_differingLengths1()381   public void testForEachPair_differingLengths1() {
382     List<String> list = new ArrayList<>();
383     Streams.forEachPair(
384         Stream.of("a", "b", "c", "d"), Stream.of(1, 2, 3), (a, b) -> list.add(a + ":" + b));
385     Truth.assertThat(list).containsExactly("a:1", "b:2", "c:3");
386   }
387 
testForEachPair_differingLengths2()388   public void testForEachPair_differingLengths2() {
389     List<String> list = new ArrayList<>();
390     Streams.forEachPair(
391         Stream.of("a", "b", "c"), Stream.of(1, 2, 3, 4), (a, b) -> list.add(a + ":" + b));
392     Truth.assertThat(list).containsExactly("a:1", "b:2", "c:3");
393   }
394 
testForEachPair_oneEmpty()395   public void testForEachPair_oneEmpty() {
396     Streams.forEachPair(Stream.of("a"), Stream.empty(), (a, b) -> fail());
397   }
398 
testForEachPair_finiteWithInfinite()399   public void testForEachPair_finiteWithInfinite() {
400     List<String> list = new ArrayList<>();
401     Streams.forEachPair(
402         Stream.of("a", "b", "c"), Stream.iterate(1, i -> i + 1), (a, b) -> list.add(a + ":" + b));
403     Truth.assertThat(list).containsExactly("a:1", "b:2", "c:3");
404   }
405 
testForEachPair_parallel()406   public void testForEachPair_parallel() {
407     Stream<String> streamA = IntStream.range(0, 100000).mapToObj(String::valueOf).parallel();
408     Stream<Integer> streamB = IntStream.range(0, 100000).mapToObj(i -> i).parallel();
409 
410     AtomicInteger count = new AtomicInteger(0);
411     Streams.forEachPair(
412         streamA,
413         streamB,
414         (a, b) -> {
415           count.incrementAndGet();
416           Truth.assertThat(a.equals(String.valueOf(b))).isTrue();
417         });
418     Truth.assertThat(count.get()).isEqualTo(100000);
419     // of course, this test doesn't prove that anything actually happened in parallel...
420   }
421 
422   // TODO(kevinb): switch to importing Truth's assertThat(Stream) if we get that added
assertThat(Stream<?> stream)423   private static IterableSubject assertThat(Stream<?> stream) {
424     return Truth.assertThat(stream.toArray()).asList();
425   }
426 
assertThat(IntStream stream)427   private static IterableSubject assertThat(IntStream stream) {
428     return Truth.assertThat(stream.toArray()).asList();
429   }
430 
assertThat(LongStream stream)431   private static IterableSubject assertThat(LongStream stream) {
432     return Truth.assertThat(stream.toArray()).asList();
433   }
434 
assertThat(DoubleStream stream)435   private static IterableSubject assertThat(DoubleStream stream) {
436     return Truth.assertThat(Doubles.asList(stream.toArray()));
437   }
438 }
439