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.Lists; 26 import com.google.common.collect.Maps; 27 import com.google.common.testing.NullPointerTester; 28 import java.io.IOException; 29 import java.util.Arrays; 30 import java.util.Iterator; 31 import java.util.Map; 32 import java.util.Map.Entry; 33 import java.util.Set; 34 import junit.framework.AssertionFailedError; 35 import junit.framework.TestCase; 36 37 /** 38 * Unit test for {@link Joiner}. 39 * 40 * @author Kevin Bourrillion 41 */ 42 @GwtCompatible(emulated = true) 43 public class JoinerTest extends TestCase { 44 private static final Joiner J = Joiner.on("-"); 45 46 // <Integer> needed to prevent warning :( 47 private static final Iterable<Integer> ITERABLE_ = Arrays.<Integer>asList(); 48 private static final Iterable<Integer> ITERABLE_1 = Arrays.asList(1); 49 private static final Iterable<Integer> ITERABLE_12 = Arrays.asList(1, 2); 50 private static final Iterable<Integer> ITERABLE_123 = Arrays.asList(1, 2, 3); 51 private static final Iterable<Integer> ITERABLE_NULL = Arrays.asList((Integer) null); 52 private static final Iterable<Integer> ITERABLE_NULL_NULL = 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 testNoSpecialNullBehavior()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 testOnCharOverride()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 testSkipNulls()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 testUseForNull()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 checkNoOutput(Joiner joiner, Iterable<Integer> set)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 = 163 new Appendable() { 164 @Override 165 public Appendable append(CharSequence csq) throws IOException { 166 throw new IOException(); 167 } 168 169 @Override 170 public Appendable append(CharSequence csq, int start, int end) throws IOException { 171 throw new IOException(); 172 } 173 174 @Override 175 public Appendable append(char c) throws IOException { 176 throw new IOException(); 177 } 178 }; 179 checkResult(Joiner joiner, Iterable<Integer> parts, String expected)180 private static void checkResult(Joiner joiner, Iterable<Integer> parts, String expected) { 181 assertEquals(expected, joiner.join(parts)); 182 assertEquals(expected, joiner.join(parts.iterator())); 183 184 StringBuilder sb1FromIterable = new StringBuilder().append('x'); 185 joiner.appendTo(sb1FromIterable, parts); 186 assertEquals("x" + expected, sb1FromIterable.toString()); 187 188 StringBuilder sb1FromIterator = new StringBuilder().append('x'); 189 joiner.appendTo(sb1FromIterator, parts.iterator()); 190 assertEquals("x" + expected, sb1FromIterator.toString()); 191 192 Integer[] partsArray = Lists.newArrayList(parts).toArray(new Integer[0]); 193 assertEquals(expected, joiner.join(partsArray)); 194 195 StringBuilder sb2 = new StringBuilder().append('x'); 196 joiner.appendTo(sb2, partsArray); 197 assertEquals("x" + expected, sb2.toString()); 198 199 int num = partsArray.length - 2; 200 if (num >= 0) { 201 Object[] rest = new Integer[num]; 202 for (int i = 0; i < num; i++) { 203 rest[i] = partsArray[i + 2]; 204 } 205 206 assertEquals(expected, joiner.join(partsArray[0], partsArray[1], rest)); 207 208 StringBuilder sb3 = new StringBuilder().append('x'); 209 joiner.appendTo(sb3, partsArray[0], partsArray[1], rest); 210 assertEquals("x" + expected, sb3.toString()); 211 } 212 } 213 test_useForNull_skipNulls()214 public void test_useForNull_skipNulls() { 215 Joiner j = Joiner.on("x").useForNull("y"); 216 try { 217 j = j.skipNulls(); 218 fail(); 219 } catch (UnsupportedOperationException expected) { 220 } 221 } 222 test_skipNulls_useForNull()223 public void test_skipNulls_useForNull() { 224 Joiner j = Joiner.on("x").skipNulls(); 225 try { 226 j = j.useForNull("y"); 227 fail(); 228 } catch (UnsupportedOperationException expected) { 229 } 230 } 231 test_useForNull_twice()232 public void test_useForNull_twice() { 233 Joiner j = Joiner.on("x").useForNull("y"); 234 try { 235 j = j.useForNull("y"); 236 fail(); 237 } catch (UnsupportedOperationException expected) { 238 } 239 } 240 testMap()241 public void testMap() { 242 MapJoiner j = Joiner.on(';').withKeyValueSeparator(':'); 243 assertEquals("", j.join(ImmutableMap.of())); 244 assertEquals(":", j.join(ImmutableMap.of("", ""))); 245 246 Map<String, String> mapWithNulls = Maps.newLinkedHashMap(); 247 mapWithNulls.put("a", null); 248 mapWithNulls.put(null, "b"); 249 250 try { 251 j.join(mapWithNulls); 252 fail(); 253 } catch (NullPointerException expected) { 254 } 255 256 assertEquals("a:00;00:b", j.useForNull("00").join(mapWithNulls)); 257 258 StringBuilder sb = new StringBuilder(); 259 j.appendTo(sb, ImmutableMap.of(1, 2, 3, 4, 5, 6)); 260 assertEquals("1:2;3:4;5:6", sb.toString()); 261 } 262 testEntries()263 public void testEntries() { 264 MapJoiner j = Joiner.on(";").withKeyValueSeparator(":"); 265 assertEquals("", j.join(ImmutableMultimap.of().entries())); 266 assertEquals("", j.join(ImmutableMultimap.of().entries().iterator())); 267 assertEquals(":", j.join(ImmutableMultimap.of("", "").entries())); 268 assertEquals(":", j.join(ImmutableMultimap.of("", "").entries().iterator())); 269 assertEquals("1:a;1:b", j.join(ImmutableMultimap.of("1", "a", "1", "b").entries())); 270 assertEquals("1:a;1:b", j.join(ImmutableMultimap.of("1", "a", "1", "b").entries().iterator())); 271 272 Map<String, String> mapWithNulls = Maps.newLinkedHashMap(); 273 mapWithNulls.put("a", null); 274 mapWithNulls.put(null, "b"); 275 Set<Entry<String, String>> entriesWithNulls = mapWithNulls.entrySet(); 276 277 try { 278 j.join(entriesWithNulls); 279 fail(); 280 } catch (NullPointerException expected) { 281 } 282 283 try { 284 j.join(entriesWithNulls.iterator()); 285 fail(); 286 } catch (NullPointerException expected) { 287 } 288 289 assertEquals("a:00;00:b", j.useForNull("00").join(entriesWithNulls)); 290 assertEquals("a:00;00:b", j.useForNull("00").join(entriesWithNulls.iterator())); 291 292 StringBuilder sb1 = new StringBuilder(); 293 j.appendTo(sb1, ImmutableMultimap.of(1, 2, 3, 4, 5, 6, 1, 3, 5, 10).entries()); 294 assertEquals("1:2;1:3;3:4;5:6;5:10", sb1.toString()); 295 296 StringBuilder sb2 = new StringBuilder(); 297 j.appendTo(sb2, ImmutableMultimap.of(1, 2, 3, 4, 5, 6, 1, 3, 5, 10).entries().iterator()); 298 assertEquals("1:2;1:3;3:4;5:6;5:10", sb2.toString()); 299 } 300 test_skipNulls_onMap()301 public void test_skipNulls_onMap() { 302 Joiner j = Joiner.on(",").skipNulls(); 303 try { 304 j.withKeyValueSeparator("/"); 305 fail(); 306 } catch (UnsupportedOperationException expected) { 307 } 308 } 309 310 private static class DontStringMeBro implements CharSequence { 311 @Override length()312 public int length() { 313 return 3; 314 } 315 316 @Override charAt(int index)317 public char charAt(int index) { 318 return "foo".charAt(index); 319 } 320 321 @Override subSequence(int start, int end)322 public CharSequence subSequence(int start, int end) { 323 return "foo".subSequence(start, end); 324 } 325 326 @Override toString()327 public String toString() { 328 throw new AssertionFailedError("shouldn't be invoked"); 329 } 330 } 331 332 // Don't do this. 333 private static class IterableIterator implements Iterable<Integer>, Iterator<Integer> { 334 private static final ImmutableSet<Integer> INTEGERS = ImmutableSet.of(1, 2, 3, 4); 335 private final Iterator<Integer> iterator; 336 IterableIterator()337 public IterableIterator() { 338 this.iterator = iterator(); 339 } 340 341 @Override iterator()342 public Iterator<Integer> iterator() { 343 return INTEGERS.iterator(); 344 } 345 346 @Override hasNext()347 public boolean hasNext() { 348 return iterator.hasNext(); 349 } 350 351 @Override next()352 public Integer next() { 353 return iterator.next(); 354 } 355 356 @Override remove()357 public void remove() { 358 iterator.remove(); 359 } 360 } 361 362 @GwtIncompatible // StringBuilder.append in GWT invokes Object.toString(), unlike the JRE version. testDontConvertCharSequenceToString()363 public void testDontConvertCharSequenceToString() { 364 assertEquals("foo,foo", Joiner.on(",").join(new DontStringMeBro(), new DontStringMeBro())); 365 assertEquals( 366 "foo,bar,foo", 367 Joiner.on(",").useForNull("bar").join(new DontStringMeBro(), null, new DontStringMeBro())); 368 } 369 370 @GwtIncompatible // NullPointerTester testNullPointers()371 public void testNullPointers() { 372 NullPointerTester tester = new NullPointerTester(); 373 tester.testAllPublicStaticMethods(Joiner.class); 374 tester.testInstanceMethods(Joiner.on(","), NullPointerTester.Visibility.PACKAGE); 375 tester.testInstanceMethods(Joiner.on(",").skipNulls(), NullPointerTester.Visibility.PACKAGE); 376 tester.testInstanceMethods( 377 Joiner.on(",").useForNull("x"), NullPointerTester.Visibility.PACKAGE); 378 tester.testInstanceMethods( 379 Joiner.on(",").withKeyValueSeparator("="), NullPointerTester.Visibility.PACKAGE); 380 } 381 } 382