• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.base;
18 
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.annotations.GwtIncompatible;
21 import com.google.common.base.Joiner.MapJoiner;
22 import com.google.common.collect.ImmutableMap;
23 import com.google.common.collect.ImmutableMultimap;
24 import com.google.common.collect.ImmutableSet;
25 import com.google.common.collect.Iterators;
26 import com.google.common.collect.Lists;
27 import com.google.common.collect.Maps;
28 import com.google.common.testing.NullPointerTester;
29 
30 import junit.framework.TestCase;
31 
32 import java.io.IOException;
33 import java.util.Arrays;
34 import java.util.Iterator;
35 import java.util.Map;
36 import java.util.Set;
37 
38 /**
39  * Unit test for {@link Joiner}.
40  *
41  * @author Kevin Bourrillion
42  */
43 @GwtCompatible(emulated = true)
44 public 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 
testNoSpecialNullBehavior()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 
testOnCharOverride()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 
testSkipNulls()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 
testUseForNull()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 
checkNoOutput(Joiner joiner, Iterable<Integer> set)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 
checkResult(Joiner joiner, Iterable<Integer> parts, String expected)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 
testIterableIterator()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 
checkIterableIterator(Joiner joiner, String expected)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 
test_useForNull_skipNulls()254   public void test_useForNull_skipNulls() {
255     Joiner j = Joiner.on("x").useForNull("y");
256     try {
257       j.skipNulls();
258       fail();
259     } catch (UnsupportedOperationException expected) {
260     }
261   }
262 
test_skipNulls_useForNull()263   public void test_skipNulls_useForNull() {
264     Joiner j = Joiner.on("x").skipNulls();
265     try {
266       j.useForNull("y");
267       fail();
268     } catch (UnsupportedOperationException expected) {
269     }
270   }
271 
test_useForNull_twice()272   public void test_useForNull_twice() {
273     Joiner j = Joiner.on("x").useForNull("y");
274     try {
275       j.useForNull("y");
276       fail();
277     } catch (UnsupportedOperationException expected) {
278     }
279   }
280 
testMap()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 
testEntries()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 
test_skipNulls_onMap()341   public void test_skipNulls_onMap() {
342     Joiner j = Joiner.on(",").skipNulls();
343     try {
344       j.withKeyValueSeparator("/");
345       fail();
346     } catch (UnsupportedOperationException expected) {
347     }
348   }
349 
350   private static class DontStringMeBro implements CharSequence {
351     @Override
length()352     public int length() {
353       return 3;
354     }
355     @Override
charAt(int index)356     public char charAt(int index) {
357       return "foo".charAt(index);
358     }
359     @Override
subSequence(int start, int end)360     public CharSequence subSequence(int start, int end) {
361       return "foo".subSequence(start, end);
362     }
toString()363     @Override public String toString() {
364       fail("shouldn't be invoked");
365       return null;
366     }
367   }
368 
369   // Don't do this.
370   private static class IterableIterator implements Iterable<Integer>, Iterator<Integer> {
371     private static final ImmutableSet<Integer> INTEGERS = ImmutableSet.of(1, 2, 3, 4);
372     private final Iterator<Integer> iterator;
IterableIterator()373     public IterableIterator() {
374       this.iterator = iterator();
375     }
iterator()376     @Override public Iterator<Integer> iterator() {
377       return INTEGERS.iterator();
378     }
hasNext()379     @Override public boolean hasNext() {
380       return iterator.hasNext();
381     }
next()382     @Override public Integer next() {
383       return iterator.next();
384     }
remove()385     @Override public void remove() {
386       iterator.remove();
387     }
388   }
389 
390   @GwtIncompatible("StringBuilder.append in GWT invokes Object.toString(), unlike the JRE version.")
testDontConvertCharSequenceToString()391   public void testDontConvertCharSequenceToString() {
392     assertEquals("foo,foo", Joiner.on(",").join(
393         new DontStringMeBro(), new DontStringMeBro()));
394     assertEquals("foo,bar,foo", Joiner.on(",").useForNull("bar").join(
395         new DontStringMeBro(), null, new DontStringMeBro()));
396   }
397 
398   @GwtIncompatible("NullPointerTester")
testNullPointers()399   public void testNullPointers() throws Exception {
400     NullPointerTester tester = new NullPointerTester();
401     tester.setDefault(StringBuilder.class, new StringBuilder());
402     // This is necessary because of the generics hackery we have to temporarily support parameters
403     // which implement both Iterator and Iterable.
404     tester.setDefault(Object.class, Iterators.emptyIterator());
405     tester.testAllPublicStaticMethods(Joiner.class);
406     tester.testAllPublicInstanceMethods(Joiner.on(","));
407     tester.testAllPublicInstanceMethods(Joiner.on(",").skipNulls());
408     tester.testAllPublicInstanceMethods(Joiner.on(",").useForNull("x"));
409     tester.testAllPublicInstanceMethods(
410         Joiner.on(",").withKeyValueSeparator("="));
411   }
412 }
413