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.AssertionFailedError; 31 import junit.framework.TestCase; 32 33 import java.io.IOException; 34 import java.util.Arrays; 35 import java.util.Iterator; 36 import java.util.Map; 37 import java.util.Set; 38 39 /** 40 * Unit test for {@link Joiner}. 41 * 42 * @author Kevin Bourrillion 43 */ 44 @GwtCompatible(emulated = true) 45 public class JoinerTest extends TestCase { 46 private static final Joiner J = Joiner.on("-"); 47 48 // <Integer> needed to prevent warning :( 49 private static final Iterable<Integer> ITERABLE_ = Arrays.<Integer>asList(); 50 private static final Iterable<Integer> ITERABLE_1 = Arrays.asList(1); 51 private static final Iterable<Integer> ITERABLE_12 = Arrays.asList(1, 2); 52 private static final Iterable<Integer> ITERABLE_123 = Arrays.asList(1, 2, 3); 53 private static final Iterable<Integer> ITERABLE_NULL = Arrays.asList((Integer) null); 54 private static final Iterable<Integer> ITERABLE_NULL_NULL 55 = 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 = new Appendable() { 166 @Override 167 public Appendable append(CharSequence csq) throws IOException { 168 throw new IOException(); 169 } 170 @Override 171 public Appendable append(CharSequence csq, int start, int end) throws IOException { 172 throw new IOException(); 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<Map.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 301 @SuppressWarnings("ReturnValueIgnored") // testing for exception test_skipNulls_onMap()302 public void test_skipNulls_onMap() { 303 Joiner j = Joiner.on(",").skipNulls(); 304 try { 305 j.withKeyValueSeparator("/"); 306 fail(); 307 } catch (UnsupportedOperationException expected) { 308 } 309 } 310 311 private static class DontStringMeBro implements CharSequence { 312 @Override length()313 public int length() { 314 return 3; 315 } 316 @Override charAt(int index)317 public char charAt(int index) { 318 return "foo".charAt(index); 319 } 320 @Override subSequence(int start, int end)321 public CharSequence subSequence(int start, int end) { 322 return "foo".subSequence(start, end); 323 } toString()324 @Override public String toString() { 325 throw new AssertionFailedError("shouldn't be invoked"); 326 } 327 } 328 329 // Don't do this. 330 private static class IterableIterator implements Iterable<Integer>, Iterator<Integer> { 331 private static final ImmutableSet<Integer> INTEGERS = ImmutableSet.of(1, 2, 3, 4); 332 private final Iterator<Integer> iterator; IterableIterator()333 public IterableIterator() { 334 this.iterator = iterator(); 335 } iterator()336 @Override public Iterator<Integer> iterator() { 337 return INTEGERS.iterator(); 338 } hasNext()339 @Override public boolean hasNext() { 340 return iterator.hasNext(); 341 } next()342 @Override public Integer next() { 343 return iterator.next(); 344 } remove()345 @Override public void remove() { 346 iterator.remove(); 347 } 348 } 349 350 @GwtIncompatible("StringBuilder.append in GWT invokes Object.toString(), unlike the JRE version.") testDontConvertCharSequenceToString()351 public void testDontConvertCharSequenceToString() { 352 assertEquals("foo,foo", Joiner.on(",").join( 353 new DontStringMeBro(), new DontStringMeBro())); 354 assertEquals("foo,bar,foo", Joiner.on(",").useForNull("bar").join( 355 new DontStringMeBro(), null, new DontStringMeBro())); 356 } 357 358 @GwtIncompatible("NullPointerTester") testNullPointers()359 public void testNullPointers() { 360 NullPointerTester tester = new NullPointerTester() 361 // This is necessary because of the generics hackery we have to temporarily support 362 // parameters which implement both Iterator and Iterable.; 363 .setDefault(Object.class, Iterators.emptyIterator()); 364 tester.testAllPublicStaticMethods(Joiner.class); 365 tester.testInstanceMethods(Joiner.on(","), NullPointerTester.Visibility.PACKAGE); 366 tester.testInstanceMethods(Joiner.on(",").skipNulls(), NullPointerTester.Visibility.PACKAGE); 367 tester.testInstanceMethods( 368 Joiner.on(",").useForNull("x"), NullPointerTester.Visibility.PACKAGE); 369 tester.testInstanceMethods( 370 Joiner.on(",").withKeyValueSeparator("="), NullPointerTester.Visibility.PACKAGE); 371 } 372 } 373