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