JoinerTest.java revision 7dd252788645e940eada959bdde927426e2531c9
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
17package com.google.common.base;
18
19import com.google.common.annotations.GwtCompatible;
20import com.google.common.annotations.GwtIncompatible;
21import com.google.common.base.Joiner.MapJoiner;
22import com.google.common.collect.ImmutableMap;
23import com.google.common.collect.ImmutableMultimap;
24import com.google.common.collect.ImmutableSet;
25import com.google.common.collect.Iterators;
26import com.google.common.collect.Lists;
27import com.google.common.collect.Maps;
28import com.google.common.testing.NullPointerTester;
29
30import java.io.IOException;
31import java.util.Arrays;
32import java.util.Iterator;
33import java.util.Map;
34import java.util.Set;
35
36import junit.framework.TestCase;
37
38/**
39 * Unit test for {@link Joiner}.
40 *
41 * @author Kevin Bourrillion
42 */
43@GwtCompatible(emulated = true)
44public class JoinerTest extends TestCase {
45  private static final Joiner J = Joiner.on("-");
46
47  // <Integer> needed to prevent warning :(
48  private static final Iterable<Integer> ITERABLE_ = Arrays.<Integer>asList();
49  private static final Iterable<Integer> ITERABLE_1 = Arrays.asList(1);
50  private static final Iterable<Integer> ITERABLE_12 = Arrays.asList(1, 2);
51  private static final Iterable<Integer> ITERABLE_123 = Arrays.asList(1, 2, 3);
52  private static final Iterable<Integer> ITERABLE_NULL = Arrays.asList((Integer) null);
53  private static final Iterable<Integer> ITERABLE_NULL_NULL
54      = Arrays.asList((Integer) null, null);
55  private static final Iterable<Integer> ITERABLE_NULL_1 = Arrays.asList(null, 1);
56  private static final Iterable<Integer> ITERABLE_1_NULL = Arrays.asList(1, null);
57  private static final Iterable<Integer> ITERABLE_1_NULL_2 = Arrays.asList(1, null, 2);
58  private static final Iterable<Integer> ITERABLE_FOUR_NULLS
59      = Arrays.asList((Integer) null, null, null, null);
60
61  public void testNoSpecialNullBehavior() {
62    checkNoOutput(J, ITERABLE_);
63    checkResult(J, ITERABLE_1, "1");
64    checkResult(J, ITERABLE_12, "1-2");
65    checkResult(J, ITERABLE_123, "1-2-3");
66
67    try {
68      J.join(ITERABLE_NULL);
69      fail();
70    } catch (NullPointerException expected) {
71    }
72    try {
73      J.join(ITERABLE_1_NULL_2);
74      fail();
75    } catch (NullPointerException expected) {
76    }
77
78    try {
79      J.join(ITERABLE_NULL.iterator());
80      fail();
81    } catch (NullPointerException expected) {
82    }
83    try {
84      J.join(ITERABLE_1_NULL_2.iterator());
85      fail();
86    } catch (NullPointerException expected) {
87    }
88  }
89
90  public void testOnCharOverride() {
91    Joiner onChar = Joiner.on('-');
92    checkNoOutput(onChar, ITERABLE_);
93    checkResult(onChar, ITERABLE_1, "1");
94    checkResult(onChar, ITERABLE_12, "1-2");
95    checkResult(onChar, ITERABLE_123, "1-2-3");
96  }
97
98  public void testSkipNulls() {
99    Joiner skipNulls = J.skipNulls();
100    checkNoOutput(skipNulls, ITERABLE_);
101    checkNoOutput(skipNulls, ITERABLE_NULL);
102    checkNoOutput(skipNulls, ITERABLE_NULL_NULL);
103    checkNoOutput(skipNulls, ITERABLE_FOUR_NULLS);
104    checkResult(skipNulls, ITERABLE_1, "1");
105    checkResult(skipNulls, ITERABLE_12, "1-2");
106    checkResult(skipNulls, ITERABLE_123, "1-2-3");
107    checkResult(skipNulls, ITERABLE_NULL_1, "1");
108    checkResult(skipNulls, ITERABLE_1_NULL, "1");
109    checkResult(skipNulls, ITERABLE_1_NULL_2, "1-2");
110  }
111
112  public void testUseForNull() {
113    Joiner zeroForNull = J.useForNull("0");
114    checkNoOutput(zeroForNull, ITERABLE_);
115    checkResult(zeroForNull, ITERABLE_1, "1");
116    checkResult(zeroForNull, ITERABLE_12, "1-2");
117    checkResult(zeroForNull, ITERABLE_123, "1-2-3");
118    checkResult(zeroForNull, ITERABLE_NULL, "0");
119    checkResult(zeroForNull, ITERABLE_NULL_NULL, "0-0");
120    checkResult(zeroForNull, ITERABLE_NULL_1, "0-1");
121    checkResult(zeroForNull, ITERABLE_1_NULL, "1-0");
122    checkResult(zeroForNull, ITERABLE_1_NULL_2, "1-0-2");
123    checkResult(zeroForNull, ITERABLE_FOUR_NULLS, "0-0-0-0");
124  }
125
126  private static void checkNoOutput(Joiner joiner, Iterable<Integer> set) {
127    assertEquals("", joiner.join(set));
128    assertEquals("", joiner.join(set.iterator()));
129
130    Object[] array = Lists.newArrayList(set).toArray(new Integer[0]);
131    assertEquals("", joiner.join(array));
132
133    StringBuilder sb1FromIterable = new StringBuilder();
134    assertSame(sb1FromIterable, joiner.appendTo(sb1FromIterable, set));
135    assertEquals(0, sb1FromIterable.length());
136
137    StringBuilder sb1FromIterator = new StringBuilder();
138    assertSame(sb1FromIterator, joiner.appendTo(sb1FromIterator, set));
139    assertEquals(0, sb1FromIterator.length());
140
141    StringBuilder sb2 = new StringBuilder();
142    assertSame(sb2, joiner.appendTo(sb2, array));
143    assertEquals(0, sb2.length());
144
145    try {
146      joiner.appendTo(NASTY_APPENDABLE, set);
147    } catch (IOException e) {
148      throw new AssertionError(e);
149    }
150
151    try {
152      joiner.appendTo(NASTY_APPENDABLE, set.iterator());
153    } catch (IOException e) {
154      throw new AssertionError(e);
155    }
156
157    try {
158      joiner.appendTo(NASTY_APPENDABLE, array);
159    } catch (IOException e) {
160      throw new AssertionError(e);
161    }
162  }
163
164  private static final Appendable NASTY_APPENDABLE = new Appendable() {
165    @Override
166    public Appendable append(CharSequence csq) throws IOException {
167      throw new IOException();
168    }
169    @Override
170    public Appendable append(CharSequence csq, int start, int end) throws IOException {
171      throw new IOException();
172    }
173    @Override
174    public Appendable append(char c) throws IOException {
175      throw new IOException();
176    }
177  };
178
179  private static void checkResult(Joiner joiner, Iterable<Integer> parts, String expected) {
180    assertEquals(expected, joiner.join(parts));
181    assertEquals(expected, joiner.join(parts.iterator()));
182
183    StringBuilder sb1FromIterable = new StringBuilder().append('x');
184    joiner.appendTo(sb1FromIterable, parts);
185    assertEquals("x" + expected, sb1FromIterable.toString());
186
187    StringBuilder sb1FromIterator = new StringBuilder().append('x');
188    joiner.appendTo(sb1FromIterator, parts.iterator());
189    assertEquals("x" + expected, sb1FromIterator.toString());
190
191    Integer[] partsArray = Lists.newArrayList(parts).toArray(new Integer[0]);
192    assertEquals(expected, joiner.join(partsArray));
193
194    StringBuilder sb2 = new StringBuilder().append('x');
195    joiner.appendTo(sb2, partsArray);
196    assertEquals("x" + expected, sb2.toString());
197
198    int num = partsArray.length - 2;
199    if (num >= 0) {
200      Object[] rest = new Integer[num];
201      for (int i = 0; i < num; i++) {
202        rest[i] = partsArray[i + 2];
203      }
204
205      assertEquals(expected, joiner.join(partsArray[0], partsArray[1], rest));
206
207      StringBuilder sb3 = new StringBuilder().append('x');
208      joiner.appendTo(sb3, partsArray[0], partsArray[1], rest);
209      assertEquals("x" + expected, sb3.toString());
210    }
211  }
212
213  public void testIterableIterator() {
214    Joiner onChar = Joiner.on('-');
215    checkIterableIterator(onChar, "1-2-3-4");
216
217    Joiner skipNulls = J.skipNulls();
218    checkIterableIterator(skipNulls, "1-2-3-4");
219
220    Joiner zeroForNull = J.useForNull("0");
221    checkIterableIterator(zeroForNull, "1-2-3-4");
222  }
223
224  private static void checkIterableIterator(Joiner joiner, String expected) {
225    assertEquals(expected, joiner.join(new IterableIterator()));
226
227    StringBuilder sb1 = new StringBuilder().append('x');
228    joiner.appendTo(sb1, new IterableIterator());
229    assertEquals("x" + expected, sb1.toString());
230
231    Integer[] partsArray =
232        Lists.newArrayList(new IterableIterator().iterator()).toArray(new Integer[0]);
233    assertEquals(expected, joiner.join(partsArray));
234
235    StringBuilder sb2 = new StringBuilder().append('x');
236    joiner.appendTo(sb2, partsArray);
237    assertEquals("x" + expected, sb2.toString());
238
239    int num = partsArray.length - 2;
240    if (num >= 0) {
241      Object[] rest = new Integer[num];
242      for (int i = 0; i < num; i++) {
243        rest[i] = partsArray[i + 2];
244      }
245
246      assertEquals(expected, joiner.join(partsArray[0], partsArray[1], rest));
247
248      StringBuilder sb3 = new StringBuilder().append('x');
249      joiner.appendTo(sb3, partsArray[0], partsArray[1], rest);
250      assertEquals("x" + expected, sb3.toString());
251    }
252  }
253
254  public void test_useForNull_skipNulls() {
255    Joiner j = Joiner.on("x").useForNull("y");
256    try {
257      j = j.skipNulls();
258      fail();
259    } catch (UnsupportedOperationException expected) {
260    }
261  }
262
263  public void test_skipNulls_useForNull() {
264    Joiner j = Joiner.on("x").skipNulls();
265    try {
266      j = j.useForNull("y");
267      fail();
268    } catch (UnsupportedOperationException expected) {
269    }
270  }
271
272  public void test_useForNull_twice() {
273    Joiner j = Joiner.on("x").useForNull("y");
274    try {
275      j = j.useForNull("y");
276      fail();
277    } catch (UnsupportedOperationException expected) {
278    }
279  }
280
281  public void testMap() {
282    MapJoiner j = Joiner.on(";").withKeyValueSeparator(":");
283    assertEquals("", j.join(ImmutableMap.of()));
284    assertEquals(":", j.join(ImmutableMap.of("", "")));
285
286    Map<String, String> mapWithNulls = Maps.newLinkedHashMap();
287    mapWithNulls.put("a", null);
288    mapWithNulls.put(null, "b");
289
290    try {
291      j.join(mapWithNulls);
292      fail();
293    } catch (NullPointerException expected) {
294    }
295
296    assertEquals("a:00;00:b", j.useForNull("00").join(mapWithNulls));
297
298    StringBuilder sb = new StringBuilder();
299    j.appendTo(sb, ImmutableMap.of(1, 2, 3, 4, 5, 6));
300    assertEquals("1:2;3:4;5:6", sb.toString());
301  }
302
303  public void testEntries() {
304    MapJoiner j = Joiner.on(";").withKeyValueSeparator(":");
305    assertEquals("", j.join(ImmutableMultimap.of().entries()));
306    assertEquals("", j.join(ImmutableMultimap.of().entries().iterator()));
307    assertEquals(":", j.join(ImmutableMultimap.of("", "").entries()));
308    assertEquals(":", j.join(ImmutableMultimap.of("", "").entries().iterator()));
309    assertEquals("1:a;1:b", j.join(ImmutableMultimap.of("1", "a", "1", "b").entries()));
310    assertEquals("1:a;1:b", j.join(ImmutableMultimap.of("1", "a", "1", "b").entries().iterator()));
311
312    Map<String, String> mapWithNulls = Maps.newLinkedHashMap();
313    mapWithNulls.put("a", null);
314    mapWithNulls.put(null, "b");
315    Set<Map.Entry<String, String>> entriesWithNulls = mapWithNulls.entrySet();
316
317    try {
318      j.join(entriesWithNulls);
319      fail();
320    } catch (NullPointerException expected) {
321    }
322
323    try {
324      j.join(entriesWithNulls.iterator());
325      fail();
326    } catch (NullPointerException expected) {
327    }
328
329    assertEquals("a:00;00:b", j.useForNull("00").join(entriesWithNulls));
330    assertEquals("a:00;00:b", j.useForNull("00").join(entriesWithNulls.iterator()));
331
332    StringBuilder sb1 = new StringBuilder();
333    j.appendTo(sb1, ImmutableMultimap.of(1, 2, 3, 4, 5, 6, 1, 3, 5, 10).entries());
334    assertEquals("1:2;1:3;3:4;5:6;5:10", sb1.toString());
335
336    StringBuilder sb2 = new StringBuilder();
337    j.appendTo(sb2, ImmutableMultimap.of(1, 2, 3, 4, 5, 6, 1, 3, 5, 10).entries().iterator());
338    assertEquals("1:2;1:3;3:4;5:6;5:10", sb2.toString());
339  }
340
341  @SuppressWarnings("ReturnValueIgnored")
342  public void test_skipNulls_onMap() {
343    Joiner j = Joiner.on(",").skipNulls();
344    try {
345      j.withKeyValueSeparator("/");
346      fail();
347    } catch (UnsupportedOperationException expected) {
348    }
349  }
350
351  private static class DontStringMeBro implements CharSequence {
352    @Override
353    public int length() {
354      return 3;
355    }
356    @Override
357    public char charAt(int index) {
358      return "foo".charAt(index);
359    }
360    @Override
361    public CharSequence subSequence(int start, int end) {
362      return "foo".subSequence(start, end);
363    }
364    @Override public String toString() {
365      fail("shouldn't be invoked");
366      return null;
367    }
368  }
369
370  // Don't do this.
371  private static class IterableIterator implements Iterable<Integer>, Iterator<Integer> {
372    private static final ImmutableSet<Integer> INTEGERS = ImmutableSet.of(1, 2, 3, 4);
373    private final Iterator<Integer> iterator;
374    public IterableIterator() {
375      this.iterator = iterator();
376    }
377    @Override public Iterator<Integer> iterator() {
378      return INTEGERS.iterator();
379    }
380    @Override public boolean hasNext() {
381      return iterator.hasNext();
382    }
383    @Override public Integer next() {
384      return iterator.next();
385    }
386    @Override public void remove() {
387      iterator.remove();
388    }
389  }
390
391  @GwtIncompatible("StringBuilder.append in GWT invokes Object.toString(), unlike the JRE version.")
392  public void testDontConvertCharSequenceToString() {
393    assertEquals("foo,foo", Joiner.on(",").join(
394        new DontStringMeBro(), new DontStringMeBro()));
395    assertEquals("foo,bar,foo", Joiner.on(",").useForNull("bar").join(
396        new DontStringMeBro(), null, new DontStringMeBro()));
397  }
398
399  @GwtIncompatible("NullPointerTester")
400  public void testNullPointers() {
401    NullPointerTester tester = new NullPointerTester()
402        // This is necessary because of the generics hackery we have to temporarily support
403        // parameters which implement both Iterator and Iterable.;
404        .setDefault(Object.class, Iterators.emptyIterator());
405    tester.testAllPublicStaticMethods(Joiner.class);
406    tester.testInstanceMethods(Joiner.on(","), NullPointerTester.Visibility.PACKAGE);
407    tester.testInstanceMethods(Joiner.on(",").skipNulls(), NullPointerTester.Visibility.PACKAGE);
408    tester.testInstanceMethods(
409        Joiner.on(",").useForNull("x"), NullPointerTester.Visibility.PACKAGE);
410    tester.testInstanceMethods(
411        Joiner.on(",").withKeyValueSeparator("="), NullPointerTester.Visibility.PACKAGE);
412  }
413}
414