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 29 import junit.framework.AssertionFailedError; 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 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 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 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 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 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 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 213 public void test_useForNull_skipNulls() { 214 Joiner j = Joiner.on("x").useForNull("y"); 215 try { 216 j = j.skipNulls(); 217 fail(); 218 } catch (UnsupportedOperationException expected) { 219 } 220 } 221 222 public void test_skipNulls_useForNull() { 223 Joiner j = Joiner.on("x").skipNulls(); 224 try { 225 j = j.useForNull("y"); 226 fail(); 227 } catch (UnsupportedOperationException expected) { 228 } 229 } 230 231 public void test_useForNull_twice() { 232 Joiner j = Joiner.on("x").useForNull("y"); 233 try { 234 j = j.useForNull("y"); 235 fail(); 236 } catch (UnsupportedOperationException expected) { 237 } 238 } 239 240 public void testMap() { 241 MapJoiner j = Joiner.on(";").withKeyValueSeparator(":"); 242 assertEquals("", j.join(ImmutableMap.of())); 243 assertEquals(":", j.join(ImmutableMap.of("", ""))); 244 245 Map<String, String> mapWithNulls = Maps.newLinkedHashMap(); 246 mapWithNulls.put("a", null); 247 mapWithNulls.put(null, "b"); 248 249 try { 250 j.join(mapWithNulls); 251 fail(); 252 } catch (NullPointerException expected) { 253 } 254 255 assertEquals("a:00;00:b", j.useForNull("00").join(mapWithNulls)); 256 257 StringBuilder sb = new StringBuilder(); 258 j.appendTo(sb, ImmutableMap.of(1, 2, 3, 4, 5, 6)); 259 assertEquals("1:2;3:4;5:6", sb.toString()); 260 } 261 262 public void testEntries() { 263 MapJoiner j = Joiner.on(";").withKeyValueSeparator(":"); 264 assertEquals("", j.join(ImmutableMultimap.of().entries())); 265 assertEquals("", j.join(ImmutableMultimap.of().entries().iterator())); 266 assertEquals(":", j.join(ImmutableMultimap.of("", "").entries())); 267 assertEquals(":", j.join(ImmutableMultimap.of("", "").entries().iterator())); 268 assertEquals("1:a;1:b", j.join(ImmutableMultimap.of("1", "a", "1", "b").entries())); 269 assertEquals("1:a;1:b", j.join(ImmutableMultimap.of("1", "a", "1", "b").entries().iterator())); 270 271 Map<String, String> mapWithNulls = Maps.newLinkedHashMap(); 272 mapWithNulls.put("a", null); 273 mapWithNulls.put(null, "b"); 274 Set<Map.Entry<String, String>> entriesWithNulls = mapWithNulls.entrySet(); 275 276 try { 277 j.join(entriesWithNulls); 278 fail(); 279 } catch (NullPointerException expected) { 280 } 281 282 try { 283 j.join(entriesWithNulls.iterator()); 284 fail(); 285 } catch (NullPointerException expected) { 286 } 287 288 assertEquals("a:00;00:b", j.useForNull("00").join(entriesWithNulls)); 289 assertEquals("a:00;00:b", j.useForNull("00").join(entriesWithNulls.iterator())); 290 291 StringBuilder sb1 = new StringBuilder(); 292 j.appendTo(sb1, ImmutableMultimap.of(1, 2, 3, 4, 5, 6, 1, 3, 5, 10).entries()); 293 assertEquals("1:2;1:3;3:4;5:6;5:10", sb1.toString()); 294 295 StringBuilder sb2 = new StringBuilder(); 296 j.appendTo(sb2, ImmutableMultimap.of(1, 2, 3, 4, 5, 6, 1, 3, 5, 10).entries().iterator()); 297 assertEquals("1:2;1:3;3:4;5:6;5:10", sb2.toString()); 298 } 299 300 @SuppressWarnings("ReturnValueIgnored") // testing for exception 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 312 public int length() { 313 return 3; 314 } 315 @Override 316 public char charAt(int index) { 317 return "foo".charAt(index); 318 } 319 @Override 320 public CharSequence subSequence(int start, int end) { 321 return "foo".subSequence(start, end); 322 } 323 @Override public String toString() { 324 throw new AssertionFailedError("shouldn't be invoked"); 325 } 326 } 327 328 // Don't do this. 329 private static class IterableIterator implements Iterable<Integer>, Iterator<Integer> { 330 private static final ImmutableSet<Integer> INTEGERS = ImmutableSet.of(1, 2, 3, 4); 331 private final Iterator<Integer> iterator; 332 public IterableIterator() { 333 this.iterator = iterator(); 334 } 335 @Override public Iterator<Integer> iterator() { 336 return INTEGERS.iterator(); 337 } 338 @Override public boolean hasNext() { 339 return iterator.hasNext(); 340 } 341 @Override public Integer next() { 342 return iterator.next(); 343 } 344 @Override public void remove() { 345 iterator.remove(); 346 } 347 } 348 349 @GwtIncompatible("StringBuilder.append in GWT invokes Object.toString(), unlike the JRE version.") 350 public void testDontConvertCharSequenceToString() { 351 assertEquals("foo,foo", Joiner.on(",").join( 352 new DontStringMeBro(), new DontStringMeBro())); 353 assertEquals("foo,bar,foo", Joiner.on(",").useForNull("bar").join( 354 new DontStringMeBro(), null, new DontStringMeBro())); 355 } 356 357 @GwtIncompatible("NullPointerTester") 358 public void testNullPointers() { 359 NullPointerTester tester = new NullPointerTester(); 360 tester.testAllPublicStaticMethods(Joiner.class); 361 tester.testInstanceMethods(Joiner.on(","), NullPointerTester.Visibility.PACKAGE); 362 tester.testInstanceMethods(Joiner.on(",").skipNulls(), NullPointerTester.Visibility.PACKAGE); 363 tester.testInstanceMethods( 364 Joiner.on(",").useForNull("x"), NullPointerTester.Visibility.PACKAGE); 365 tester.testInstanceMethods( 366 Joiner.on(",").withKeyValueSeparator("="), NullPointerTester.Visibility.PACKAGE); 367 } 368 } 369