View Javadoc
1   /*
2    * Copyright (C) 2008 The Guava Authors
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  
17  package com.google.common.collect;
18  
19  import static com.google.common.truth.Truth.assertThat;
20  import static java.util.Arrays.asList;
21  
22  import com.google.common.annotations.GwtCompatible;
23  import com.google.common.annotations.GwtIncompatible;
24  import com.google.common.base.Function;
25  import com.google.common.base.Functions;
26  import com.google.common.base.Joiner;
27  import com.google.common.base.Optional;
28  import com.google.common.base.Predicate;
29  import com.google.common.base.Predicates;
30  import com.google.common.collect.testing.IteratorFeature;
31  import com.google.common.collect.testing.IteratorTester;
32  import com.google.common.testing.NullPointerTester;
33  
34  import junit.framework.AssertionFailedError;
35  import junit.framework.TestCase;
36  
37  import java.util.ArrayList;
38  import java.util.Collection;
39  import java.util.Collections;
40  import java.util.Iterator;
41  import java.util.List;
42  import java.util.Set;
43  import java.util.SortedSet;
44  
45  import javax.annotation.Nullable;
46  
47  /**
48   * Unit test for {@link FluentIterable}.
49   *
50   * @author Marcin Mikosik
51   */
52  @GwtCompatible(emulated = true)
53  public class FluentIterableTest extends TestCase {
54  
55    @GwtIncompatible("NullPointerTester")
56    public void testNullPointerExceptions() {
57      NullPointerTester tester = new NullPointerTester();
58      tester.testAllPublicStaticMethods(FluentIterable.class);
59    }
60  
61    public void testFrom() {
62      assertEquals(ImmutableList.of(1, 2, 3, 4),
63          Lists.newArrayList(FluentIterable.from(ImmutableList.of(1, 2, 3, 4))));
64    }
65  
66    @SuppressWarnings("deprecation") // test of deprecated method
67    public void testFrom_alreadyFluentIterable() {
68      FluentIterable<Integer> iterable = FluentIterable.from(asList(1));
69      assertSame(iterable, FluentIterable.from(iterable));
70    }
71  
72    public void testOfArray() {
73      assertEquals(ImmutableList.of("1", "2", "3", "4"),
74          Lists.newArrayList(FluentIterable.of(new Object[] {"1", "2", "3", "4"})));
75    }
76  
77    public void testSize1Collection() {
78      assertEquals(1, FluentIterable.from(asList("a")).size());
79    }
80  
81    public void testSize2NonCollection() {
82      Iterable<Integer> iterable = new Iterable<Integer>() {
83        @Override
84        public Iterator<Integer> iterator() {
85          return asList(0, 1).iterator();
86        }
87      };
88      assertEquals(2, FluentIterable.from(iterable).size());
89    }
90  
91    public void testSize_collectionDoesntIterate() {
92      List<Integer> nums = asList(1, 2, 3, 4, 5);
93      List<Integer> collection = new ArrayList<Integer>(nums) {
94        @Override public Iterator<Integer> iterator() {
95          throw new AssertionFailedError("Don't iterate me!");
96        }
97      };
98      assertEquals(5, FluentIterable.from(collection).size());
99    }
100 
101   public void testContains_nullSetYes() {
102     Iterable<String> set = Sets.newHashSet("a", null, "b");
103     assertTrue(FluentIterable.from(set).contains(null));
104   }
105 
106   public void testContains_nullSetNo() {
107     Iterable<String> set = ImmutableSortedSet.of("a", "b");
108     assertFalse(FluentIterable.from(set).contains(null));
109   }
110 
111   public void testContains_nullIterableYes() {
112     Iterable<String> iterable = iterable("a", null, "b");
113     assertTrue(FluentIterable.from(iterable).contains(null));
114   }
115 
116   public void testContains_nullIterableNo() {
117     Iterable<String> iterable = iterable("a", "b");
118     assertFalse(FluentIterable.from(iterable).contains(null));
119   }
120 
121   public void testContains_nonNullSetYes() {
122     Iterable<String> set = Sets.newHashSet("a", null, "b");
123     assertTrue(FluentIterable.from(set).contains("b"));
124   }
125 
126   public void testContains_nonNullSetNo() {
127     Iterable<String> set = Sets.newHashSet("a", "b");
128     assertFalse(FluentIterable.from(set).contains("c"));
129   }
130 
131   public void testContains_nonNullIterableYes() {
132     Iterable<String> set = iterable("a", null, "b");
133     assertTrue(FluentIterable.from(set).contains("b"));
134   }
135 
136   public void testContains_nonNullIterableNo() {
137     Iterable<String> iterable = iterable("a", "b");
138     assertFalse(FluentIterable.from(iterable).contains("c"));
139   }
140 
141   public void testCycle() {
142     FluentIterable<String> cycle = FluentIterable.from(asList("a", "b")).cycle();
143 
144     int howManyChecked = 0;
145     for (String string : cycle) {
146       String expected = (howManyChecked % 2 == 0) ? "a" : "b";
147       assertEquals(expected, string);
148       if (howManyChecked++ == 5) {
149         break;
150       }
151     }
152 
153     // We left the last iterator pointing to "b". But a new iterator should
154     // always point to "a".
155     assertEquals("a", cycle.iterator().next());
156   }
157 
158   public void testCycle_removingAllElementsStopsCycle() {
159     FluentIterable<Integer> cycle = fluent(1, 2).cycle();
160     Iterator<Integer> iterator = cycle.iterator();
161     iterator.next();
162     iterator.remove();
163     iterator.next();
164     iterator.remove();
165     assertFalse(iterator.hasNext());
166     assertFalse(cycle.iterator().hasNext());
167   }
168 
169   public void testAppend() {
170     FluentIterable<Integer> result =
171         FluentIterable.<Integer>from(asList(1, 2, 3)).append(Lists.newArrayList(4, 5, 6));
172     assertEquals(asList(1, 2, 3, 4, 5, 6), Lists.newArrayList(result));
173     assertEquals("[1, 2, 3, 4, 5, 6]", result.toString());
174 
175     result = FluentIterable.<Integer>from(asList(1, 2, 3)).append(4, 5, 6);
176     assertEquals(asList(1, 2, 3, 4, 5, 6), Lists.newArrayList(result));
177     assertEquals("[1, 2, 3, 4, 5, 6]", result.toString());
178   }
179 
180   public void testAppend_emptyList() {
181     FluentIterable<Integer> result =
182         FluentIterable.<Integer>from(asList(1, 2, 3)).append(Lists.<Integer>newArrayList());
183     assertEquals(asList(1, 2, 3), Lists.newArrayList(result));
184   }
185 
186   @SuppressWarnings("ReturnValueIgnored")
187   public void testAppend_nullPointerException() {
188     try {
189       FluentIterable.<Integer>from(asList(1, 2)).append((List<Integer>) null);
190       fail("Appending null iterable should throw NPE.");
191     } catch (NullPointerException expected) {
192     }
193   }
194 
195   /*
196    * Tests for partition(int size) method.
197    */
198 
199   /*
200    * Tests for partitionWithPadding(int size) method.
201    */
202 
203   public void testFilter() {
204     FluentIterable<String> filtered =
205         FluentIterable.from(asList("foo", "bar")).filter(Predicates.equalTo("foo"));
206 
207     List<String> expected = Collections.singletonList("foo");
208     List<String> actual = Lists.newArrayList(filtered);
209     assertEquals(expected, actual);
210     assertCanIterateAgain(filtered);
211     assertEquals("[foo]", filtered.toString());
212   }
213 
214   private static class TypeA {}
215   private interface TypeB {}
216   private static class HasBoth extends TypeA implements TypeB {}
217 
218   @GwtIncompatible("Iterables.filter(Iterable, Class)")
219   public void testFilterByType() throws Exception {
220     HasBoth hasBoth = new HasBoth();
221     FluentIterable<TypeA> alist =
222         FluentIterable.from(asList(new TypeA(), new TypeA(), hasBoth, new TypeA()));
223     Iterable<TypeB> blist = alist.filter(TypeB.class);
224     assertThat(blist).iteratesAs(hasBoth);
225   }
226 
227   public void testAnyMatch() {
228     ArrayList<String> list = Lists.newArrayList();
229     FluentIterable<String> iterable = FluentIterable.<String>from(list);
230     Predicate<String> predicate = Predicates.equalTo("pants");
231 
232     assertFalse(iterable.anyMatch(predicate));
233     list.add("cool");
234     assertFalse(iterable.anyMatch(predicate));
235     list.add("pants");
236     assertTrue(iterable.anyMatch(predicate));
237   }
238 
239   public void testAllMatch() {
240     List<String> list = Lists.newArrayList();
241     FluentIterable<String> iterable = FluentIterable.<String>from(list);
242     Predicate<String> predicate = Predicates.equalTo("cool");
243 
244     assertTrue(iterable.allMatch(predicate));
245     list.add("cool");
246     assertTrue(iterable.allMatch(predicate));
247     list.add("pants");
248     assertFalse(iterable.allMatch(predicate));
249   }
250 
251   public void testFirstMatch() {
252     FluentIterable<String> iterable = FluentIterable.from(Lists.newArrayList("cool", "pants"));
253     assertEquals(Optional.of("cool"), iterable.firstMatch(Predicates.equalTo("cool")));
254     assertEquals(Optional.of("pants"), iterable.firstMatch(Predicates.equalTo("pants")));
255     assertEquals(Optional.absent(), iterable.firstMatch(Predicates.alwaysFalse()));
256     assertEquals(Optional.of("cool"), iterable.firstMatch(Predicates.alwaysTrue()));
257   }
258 
259   private static final class IntegerValueOfFunction implements Function<String, Integer> {
260     @Override
261     public Integer apply(String from) {
262       return Integer.valueOf(from);
263     }
264   }
265 
266   public void testTransformWith() {
267     List<String> input = asList("1", "2", "3");
268     Iterable<Integer> iterable =
269         FluentIterable.from(input).transform(new IntegerValueOfFunction());
270 
271     assertEquals(asList(1, 2, 3), Lists.newArrayList(iterable));
272     assertCanIterateAgain(iterable);
273     assertEquals("[1, 2, 3]", iterable.toString());
274   }
275 
276   public void testTransformWith_poorlyBehavedTransform() {
277     List<String> input = asList("1", null, "3");
278     Iterable<Integer> iterable =
279         FluentIterable.from(input).transform(new IntegerValueOfFunction());
280 
281     Iterator<Integer> resultIterator = iterable.iterator();
282     resultIterator.next();
283 
284     try {
285       resultIterator.next();
286       fail("Transforming null to int should throw NumberFormatException");
287     } catch (NumberFormatException expected) {
288     }
289   }
290 
291   private static final class StringValueOfFunction implements Function<Integer, String> {
292     @Override
293     public String apply(Integer from) {
294       return String.valueOf(from);
295     }
296   }
297 
298   public void testTransformWith_nullFriendlyTransform() {
299     List<Integer> input = asList(1, 2, null, 3);
300     Iterable<String> result = FluentIterable.from(input).transform(new StringValueOfFunction());
301 
302     assertEquals(asList("1", "2", "null", "3"), Lists.newArrayList(result));
303   }
304 
305   private static final class RepeatedStringValueOfFunction
306       implements Function<Integer, List<String>> {
307     @Override
308     public List<String> apply(Integer from) {
309       String value = String.valueOf(from);
310       return ImmutableList.of(value, value);
311     }
312   }
313 
314   public void testTransformAndConcat() {
315     List<Integer> input = asList(1, 2, 3);
316     Iterable<String> result =
317         FluentIterable.from(input).transformAndConcat(new RepeatedStringValueOfFunction());
318     assertEquals(asList("1", "1", "2", "2", "3", "3"), Lists.newArrayList(result));
319   }
320 
321   private static final class RepeatedStringValueOfWildcardFunction
322       implements Function<Integer, List<? extends String>> {
323     @Override
324     public List<String> apply(Integer from) {
325       String value = String.valueOf(from);
326       return ImmutableList.of(value, value);
327     }
328   }
329 
330   public void testTransformAndConcat_wildcardFunctionGenerics() {
331     List<Integer> input = asList(1, 2, 3);
332     FluentIterable.from(input).transformAndConcat(new RepeatedStringValueOfWildcardFunction());
333   }
334 
335   public void testFirst_list() {
336     List<String> list = Lists.newArrayList("a", "b", "c");
337     assertEquals("a", FluentIterable.from(list).first().get());
338   }
339 
340   public void testFirst_null() {
341     List<String> list = Lists.newArrayList(null, "a", "b");
342     try {
343       FluentIterable.from(list).first();
344       fail();
345     } catch (NullPointerException expected) {
346     }
347   }
348 
349   public void testFirst_emptyList() {
350     List<String> list = Collections.emptyList();
351     assertEquals(Optional.absent(), FluentIterable.from(list).first());
352   }
353 
354   public void testFirst_sortedSet() {
355     SortedSet<String> sortedSet = ImmutableSortedSet.of("b", "c", "a");
356     assertEquals("a", FluentIterable.from(sortedSet).first().get());
357   }
358 
359   public void testFirst_emptySortedSet() {
360     SortedSet<String> sortedSet = ImmutableSortedSet.of();
361     assertEquals(Optional.absent(), FluentIterable.from(sortedSet).first());
362   }
363 
364   public void testFirst_iterable() {
365     Set<String> set = ImmutableSet.of("a", "b", "c");
366     assertEquals("a", FluentIterable.from(set).first().get());
367   }
368 
369   public void testFirst_emptyIterable() {
370     Set<String> set = Sets.newHashSet();
371     assertEquals(Optional.absent(), FluentIterable.from(set).first());
372   }
373 
374   public void testLast_list() {
375     List<String> list = Lists.newArrayList("a", "b", "c");
376     assertEquals("c", FluentIterable.from(list).last().get());
377   }
378 
379   public void testLast_null() {
380     List<String> list = Lists.newArrayList("a", "b", null);
381     try {
382       FluentIterable.from(list).last();
383       fail();
384     } catch (NullPointerException expected) {
385     }
386   }
387 
388   public void testLast_emptyList() {
389     List<String> list = Collections.emptyList();
390     assertEquals(Optional.absent(), FluentIterable.from(list).last());
391   }
392 
393   public void testLast_sortedSet() {
394     SortedSet<String> sortedSet = ImmutableSortedSet.of("b", "c", "a");
395     assertEquals("c", FluentIterable.from(sortedSet).last().get());
396   }
397 
398   public void testLast_emptySortedSet() {
399     SortedSet<String> sortedSet = ImmutableSortedSet.of();
400     assertEquals(Optional.absent(), FluentIterable.from(sortedSet).last());
401   }
402 
403   public void testLast_iterable() {
404     Set<String> set = ImmutableSet.of("a", "b", "c");
405     assertEquals("c", FluentIterable.from(set).last().get());
406   }
407 
408   public void testLast_emptyIterable() {
409     Set<String> set = Sets.newHashSet();
410     assertEquals(Optional.absent(), FluentIterable.from(set).last());
411   }
412 
413   public void testSkip_simple() {
414     Collection<String> set = ImmutableSet.of("a", "b", "c", "d", "e");
415     assertEquals(Lists.newArrayList("c", "d", "e"),
416         Lists.newArrayList(FluentIterable.from(set).skip(2)));
417     assertEquals("[c, d, e]", FluentIterable.from(set).skip(2).toString());
418   }
419 
420   public void testSkip_simpleList() {
421     Collection<String> list = Lists.newArrayList("a", "b", "c", "d", "e");
422     assertEquals(Lists.newArrayList("c", "d", "e"),
423         Lists.newArrayList(FluentIterable.from(list).skip(2)));
424     assertEquals("[c, d, e]", FluentIterable.from(list).skip(2).toString());
425   }
426 
427   public void testSkip_pastEnd() {
428     Collection<String> set = ImmutableSet.of("a", "b");
429     assertEquals(Collections.emptyList(), Lists.newArrayList(FluentIterable.from(set).skip(20)));
430   }
431 
432   public void testSkip_pastEndList() {
433     Collection<String> list = Lists.newArrayList("a", "b");
434     assertEquals(Collections.emptyList(), Lists.newArrayList(FluentIterable.from(list).skip(20)));
435   }
436 
437   public void testSkip_skipNone() {
438     Collection<String> set = ImmutableSet.of("a", "b");
439     assertEquals(Lists.newArrayList("a", "b"),
440         Lists.newArrayList(FluentIterable.from(set).skip(0)));
441   }
442 
443   public void testSkip_skipNoneList() {
444     Collection<String> list = Lists.newArrayList("a", "b");
445     assertEquals(Lists.newArrayList("a", "b"),
446         Lists.newArrayList(FluentIterable.from(list).skip(0)));
447   }
448 
449   public void testSkip_iterator() throws Exception {
450     new IteratorTester<Integer>(5, IteratorFeature.MODIFIABLE, Lists.newArrayList(2, 3),
451         IteratorTester.KnownOrder.KNOWN_ORDER) {
452       @Override protected Iterator<Integer> newTargetIterator() {
453         Collection<Integer> collection = Sets.newLinkedHashSet();
454         Collections.addAll(collection, 1, 2, 3);
455         return FluentIterable.from(collection).skip(1).iterator();
456       }
457     }.test();
458   }
459 
460   public void testSkip_iteratorList() throws Exception {
461     new IteratorTester<Integer>(5, IteratorFeature.MODIFIABLE, Lists.newArrayList(2, 3),
462         IteratorTester.KnownOrder.KNOWN_ORDER) {
463       @Override protected Iterator<Integer> newTargetIterator() {
464         return FluentIterable.from(Lists.newArrayList(1, 2, 3)).skip(1).iterator();
465       }
466     }.test();
467   }
468 
469   public void testSkip_nonStructurallyModifiedList() throws Exception {
470     List<String> list = Lists.newArrayList("a", "b", "c");
471     FluentIterable<String> tail = FluentIterable.from(list).skip(1);
472     Iterator<String> tailIterator = tail.iterator();
473     list.set(2, "c2");
474     assertEquals("b", tailIterator.next());
475     assertEquals("c2", tailIterator.next());
476     assertFalse(tailIterator.hasNext());
477   }
478 
479   public void testSkip_structurallyModifiedSkipSome() throws Exception {
480     Collection<String> set = Sets.newLinkedHashSet();
481     Collections.addAll(set, "a", "b", "c");
482     FluentIterable<String> tail = FluentIterable.from(set).skip(1);
483     set.remove("b");
484     set.addAll(Lists.newArrayList("X", "Y", "Z"));
485     assertThat(tail).iteratesAs("c", "X", "Y", "Z");
486   }
487 
488   public void testSkip_structurallyModifiedSkipSomeList() throws Exception {
489     List<String> list = Lists.newArrayList("a", "b", "c");
490     FluentIterable<String> tail = FluentIterable.from(list).skip(1);
491     list.subList(1, 3).clear();
492     list.addAll(0, Lists.newArrayList("X", "Y", "Z"));
493     assertThat(tail).iteratesAs("Y", "Z", "a");
494   }
495 
496   public void testSkip_structurallyModifiedSkipAll() throws Exception {
497     Collection<String> set = Sets.newLinkedHashSet();
498     Collections.addAll(set, "a", "b", "c");
499     FluentIterable<String> tail = FluentIterable.from(set).skip(2);
500     set.remove("a");
501     set.remove("b");
502     assertFalse(tail.iterator().hasNext());
503   }
504 
505   public void testSkip_structurallyModifiedSkipAllList() throws Exception {
506     List<String> list = Lists.newArrayList("a", "b", "c");
507     FluentIterable<String> tail = FluentIterable.from(list).skip(2);
508     list.subList(0, 2).clear();
509     assertThat(tail).isEmpty();
510   }
511 
512   @SuppressWarnings("ReturnValueIgnored")
513   public void testSkip_illegalArgument() {
514     try {
515       FluentIterable.from(asList("a", "b", "c")).skip(-1);
516       fail("Skipping negative number of elements should throw IllegalArgumentException.");
517     } catch (IllegalArgumentException expected) {
518     }
519   }
520 
521   public void testLimit() {
522     Iterable<String> iterable = Lists.newArrayList("foo", "bar", "baz");
523     FluentIterable<String> limited = FluentIterable.from(iterable).limit(2);
524 
525     assertEquals(ImmutableList.of("foo", "bar"), Lists.newArrayList(limited));
526     assertCanIterateAgain(limited);
527     assertEquals("[foo, bar]", limited.toString());
528   }
529 
530   @SuppressWarnings("ReturnValueIgnored")
531   public void testLimit_illegalArgument() {
532     try {
533       FluentIterable.from(Lists.newArrayList("a", "b", "c")).limit(-1);
534       fail("Passing negative number to limit(...) method should throw IllegalArgumentException");
535     } catch (IllegalArgumentException expected) {
536     }
537   }
538 
539   public void testIsEmpty() {
540     assertTrue(FluentIterable.<String>from(Collections.<String>emptyList()).isEmpty());
541     assertFalse(FluentIterable.<String>from(Lists.newArrayList("foo")).isEmpty());
542   }
543 
544   public void testToList() {
545     assertEquals(Lists.newArrayList(1, 2, 3, 4), fluent(1, 2, 3, 4).toList());
546   }
547 
548   public void testToList_empty() {
549     assertTrue(fluent().toList().isEmpty());
550   }
551 
552   public void testToSortedList_withComparator() {
553     assertEquals(Lists.newArrayList(4, 3, 2, 1),
554         fluent(4, 1, 3, 2).toSortedList(Ordering.<Integer>natural().reverse()));
555   }
556 
557   public void testToSortedList_withDuplicates() {
558     assertEquals(Lists.newArrayList(4, 3, 1, 1),
559         fluent(1, 4, 1, 3).toSortedList(Ordering.<Integer>natural().reverse()));
560   }
561 
562   public void testToSet() {
563     assertThat(fluent(1, 2, 3, 4).toSet()).has().exactly(1, 2, 3, 4).inOrder();
564   }
565 
566   public void testToSet_removeDuplicates() {
567     assertThat(fluent(1, 2, 1, 2).toSet()).has().exactly(1, 2).inOrder();
568   }
569 
570   public void testToSet_empty() {
571     assertTrue(fluent().toSet().isEmpty());
572   }
573 
574   public void testToSortedSet() {
575     assertThat(fluent(1, 4, 2, 3).toSortedSet(Ordering.<Integer>natural().reverse()))
576         .has().exactly(4, 3, 2, 1).inOrder();
577   }
578 
579   public void testToSortedSet_removeDuplicates() {
580     assertThat(fluent(1, 4, 1, 3).toSortedSet(Ordering.<Integer>natural().reverse()))
581         .has().exactly(4, 3, 1).inOrder();
582   }
583 
584   public void testToMap() {
585     assertThat(fluent(1, 2, 3).toMap(Functions.toStringFunction()).entrySet())
586         .has().exactly(
587             Maps.immutableEntry(1, "1"),
588             Maps.immutableEntry(2, "2"),
589             Maps.immutableEntry(3, "3")).inOrder();
590   }
591 
592   public void testToMap_nullKey() {
593     try {
594       fluent(1, null, 2).toMap(Functions.constant("foo"));
595       fail();
596     } catch (NullPointerException expected) {
597     }
598   }
599 
600   public void testToMap_nullValue() {
601     try {
602       fluent(1, 2, 3).toMap(Functions.constant(null));
603       fail();
604     } catch (NullPointerException expected) {
605     }
606   }
607 
608   public void testIndex() {
609     ImmutableListMultimap<Integer, String> expected =
610         ImmutableListMultimap.<Integer, String>builder()
611             .putAll(3, "one", "two")
612             .put(5, "three")
613             .put(4, "four")
614             .build();
615     ImmutableListMultimap<Integer, String> index =
616         FluentIterable.from(asList("one", "two", "three", "four")).index(
617             new Function<String, Integer>() {
618               @Override
619               public Integer apply(String input) {
620                 return input.length();
621               }
622             });
623     assertEquals(expected, index);
624   }
625 
626   public void testIndex_nullKey() {
627     try {
628       fluent(1, 2, 3).index(Functions.constant(null));
629       fail();
630     } catch (NullPointerException expected) {
631     }
632   }
633 
634   public void testIndex_nullValue() {
635     try {
636       fluent(1, null, 2).index(Functions.constant("foo"));
637       fail();
638     } catch (NullPointerException expected) {
639     }
640   }
641 
642   public void testUniqueIndex() {
643     ImmutableMap<Integer, String> expected =
644         ImmutableMap.of(3, "two", 5, "three", 4, "four");
645     ImmutableMap<Integer, String> index =
646         FluentIterable.from(asList("two", "three", "four")).uniqueIndex(
647             new Function<String, Integer>() {
648               @Override
649               public Integer apply(String input) {
650                 return input.length();
651               }
652             });
653     assertEquals(expected, index);
654   }
655 
656   public void testUniqueIndex_duplicateKey() {
657     try {
658       FluentIterable.from(asList("one", "two", "three", "four")).uniqueIndex(
659           new Function<String, Integer>() {
660             @Override
661             public Integer apply(String input) {
662               return input.length();
663             }
664           });
665       fail();
666     } catch (IllegalArgumentException expected) {
667     }
668   }
669 
670   public void testUniqueIndex_nullKey() {
671     try {
672       fluent(1, 2, 3).uniqueIndex(Functions.constant(null));
673       fail();
674     } catch (NullPointerException expected) {
675     }
676   }
677 
678   public void testUniqueIndex_nullValue() {
679     try {
680       fluent(1, null, 2).uniqueIndex(new Function<Integer, Object>() {
681         @Override
682         public Object apply(@Nullable Integer input) {
683           return String.valueOf(input);
684         }
685       });
686       fail();
687     } catch (NullPointerException expected) {
688     }
689   }
690 
691   public void testCopyInto_List() {
692     assertThat(fluent(1, 3, 5).copyInto(Lists.newArrayList(1, 2)))
693         .has().exactly(1, 2, 1, 3, 5).inOrder();
694   }
695 
696   public void testCopyInto_Set() {
697     assertThat(fluent(1, 3, 5).copyInto(Sets.newHashSet(1, 2)))
698         .has().exactly(1, 2, 3, 5);
699   }
700 
701   public void testCopyInto_SetAllDuplicates() {
702     assertThat(fluent(1, 3, 5).copyInto(Sets.newHashSet(1, 2, 3, 5)))
703         .has().exactly(1, 2, 3, 5);
704   }
705 
706   public void testCopyInto_NonCollection() {
707     final ArrayList<Integer> list = Lists.newArrayList(1, 2, 3);
708 
709     final ArrayList<Integer> iterList = Lists.newArrayList(9, 8, 7);
710     Iterable<Integer> iterable = new Iterable<Integer>() {
711       @Override
712       public Iterator<Integer> iterator() {
713         return iterList.iterator();
714       }
715     };
716 
717     assertThat(FluentIterable.from(iterable).copyInto(list))
718         .has().exactly(1, 2, 3, 9, 8, 7).inOrder();
719   }
720 
721   public void testJoin() {
722     assertEquals("2,1,3,4", fluent(2, 1, 3, 4).join(Joiner.on(",")));
723   }
724 
725   public void testJoin_empty() {
726     assertEquals("", fluent().join(Joiner.on(",")));
727   }
728 
729   public void testGet() {
730     assertEquals("a", FluentIterable
731         .from(Lists.newArrayList("a", "b", "c")).get(0));
732     assertEquals("b", FluentIterable
733         .from(Lists.newArrayList("a", "b", "c")).get(1));
734     assertEquals("c", FluentIterable
735         .from(Lists.newArrayList("a", "b", "c")).get(2));
736   }
737 
738   public void testGet_outOfBounds() {
739     try {
740       FluentIterable.from(Lists.newArrayList("a", "b", "c")).get(-1);
741       fail();
742     } catch (IndexOutOfBoundsException expected) {
743     }
744 
745     try {
746       FluentIterable.from(Lists.newArrayList("a", "b", "c")).get(3);
747       fail();
748     } catch (IndexOutOfBoundsException expected) {
749     }
750   }
751 
752   private static void assertCanIterateAgain(Iterable<?> iterable) {
753     for (@SuppressWarnings("unused") Object obj : iterable) {
754     }
755   }
756 
757   private static FluentIterable<Integer> fluent(Integer... elements) {
758     return FluentIterable.from(Lists.newArrayList(elements));
759   }
760 
761   private static Iterable<String> iterable(String... elements) {
762     final List<String> list = asList(elements);
763     return new Iterable<String>() {
764       @Override
765       public Iterator<String> iterator() {
766         return list.iterator();
767       }
768     };
769   }
770 }