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