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