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