1 /* 2 * Copyright (C) 2007 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.io; 18 19 import static org.junit.Assert.assertThrows; 20 21 import com.google.common.base.Strings; 22 import com.google.common.collect.ImmutableList; 23 import java.io.EOFException; 24 import java.io.FilterReader; 25 import java.io.IOException; 26 import java.io.Reader; 27 import java.io.StringReader; 28 import java.io.StringWriter; 29 import java.io.Writer; 30 import java.nio.CharBuffer; 31 import java.util.List; 32 33 /** 34 * Unit test for {@link CharStreams}. 35 * 36 * @author Chris Nokleberg 37 */ 38 public class CharStreamsTest extends IoTestCase { 39 40 private static final String TEXT = "The quick brown fox jumped over the lazy dog."; 41 testToString()42 public void testToString() throws IOException { 43 assertEquals(TEXT, CharStreams.toString(new StringReader(TEXT))); 44 } 45 testReadLines()46 public void testReadLines() throws IOException { 47 List<String> lines = CharStreams.readLines(new StringReader("a\nb\nc")); 48 assertEquals(ImmutableList.of("a", "b", "c"), lines); 49 } 50 testReadLines_withLineProcessor()51 public void testReadLines_withLineProcessor() throws IOException { 52 String text = "a\nb\nc"; 53 54 // Test a LineProcessor that always returns false. 55 Reader r = new StringReader(text); 56 LineProcessor<Integer> alwaysFalse = 57 new LineProcessor<Integer>() { 58 int seen; 59 60 @Override 61 public boolean processLine(String line) { 62 seen++; 63 return false; 64 } 65 66 @Override 67 public Integer getResult() { 68 return seen; 69 } 70 }; 71 assertEquals( 72 "processLine was called more than once", 73 1, 74 CharStreams.readLines(r, alwaysFalse).intValue()); 75 76 // Test a LineProcessor that always returns true. 77 r = new StringReader(text); 78 LineProcessor<Integer> alwaysTrue = 79 new LineProcessor<Integer>() { 80 int seen; 81 82 @Override 83 public boolean processLine(String line) { 84 seen++; 85 return true; 86 } 87 88 @Override 89 public Integer getResult() { 90 return seen; 91 } 92 }; 93 assertEquals( 94 "processLine was not called for all the lines", 95 3, 96 CharStreams.readLines(r, alwaysTrue).intValue()); 97 98 // Test a LineProcessor that is conditional. 99 r = new StringReader(text); 100 final StringBuilder sb = new StringBuilder(); 101 LineProcessor<Integer> conditional = 102 new LineProcessor<Integer>() { 103 int seen; 104 105 @Override 106 public boolean processLine(String line) { 107 seen++; 108 sb.append(line); 109 return seen < 2; 110 } 111 112 @Override 113 public Integer getResult() { 114 return seen; 115 } 116 }; 117 assertEquals(2, CharStreams.readLines(r, conditional).intValue()); 118 assertEquals("ab", sb.toString()); 119 } 120 testSkipFully_eof()121 public void testSkipFully_eof() throws IOException { 122 Reader reader = new StringReader("abcde"); 123 assertThrows(EOFException.class, () -> CharStreams.skipFully(reader, 6)); 124 } 125 testSkipFully()126 public void testSkipFully() throws IOException { 127 String testString = "abcdef"; 128 Reader reader = new StringReader(testString); 129 130 assertEquals(testString.charAt(0), reader.read()); 131 CharStreams.skipFully(reader, 1); 132 assertEquals(testString.charAt(2), reader.read()); 133 CharStreams.skipFully(reader, 2); 134 assertEquals(testString.charAt(5), reader.read()); 135 136 assertEquals(-1, reader.read()); 137 } 138 testAsWriter()139 public void testAsWriter() { 140 // Should wrap Appendable in a new object 141 Appendable plainAppendable = new StringBuilder(); 142 Writer result = CharStreams.asWriter(plainAppendable); 143 assertNotSame(plainAppendable, result); 144 assertNotNull(result); 145 146 // A Writer should not be wrapped 147 Appendable secretlyAWriter = new StringWriter(); 148 result = CharStreams.asWriter(secretlyAWriter); 149 assertSame(secretlyAWriter, result); 150 } 151 152 // CharStreams.copy has type specific optimizations for Readers,StringBuilders and Writers 153 testCopy()154 public void testCopy() throws IOException { 155 StringBuilder builder = new StringBuilder(); 156 long copied = 157 CharStreams.copy( 158 wrapAsGenericReadable(new StringReader(ASCII)), wrapAsGenericAppendable(builder)); 159 assertEquals(ASCII, builder.toString()); 160 assertEquals(ASCII.length(), copied); 161 162 StringBuilder builder2 = new StringBuilder(); 163 copied = 164 CharStreams.copy( 165 wrapAsGenericReadable(new StringReader(I18N)), wrapAsGenericAppendable(builder2)); 166 assertEquals(I18N, builder2.toString()); 167 assertEquals(I18N.length(), copied); 168 } 169 testCopy_toStringBuilder_fromReader()170 public void testCopy_toStringBuilder_fromReader() throws IOException { 171 StringBuilder builder = new StringBuilder(); 172 long copied = CharStreams.copy(new StringReader(ASCII), builder); 173 assertEquals(ASCII, builder.toString()); 174 assertEquals(ASCII.length(), copied); 175 176 StringBuilder builder2 = new StringBuilder(); 177 copied = CharStreams.copy(new StringReader(I18N), builder2); 178 assertEquals(I18N, builder2.toString()); 179 assertEquals(I18N.length(), copied); 180 } 181 testCopy_toStringBuilder_fromReadable()182 public void testCopy_toStringBuilder_fromReadable() throws IOException { 183 StringBuilder builder = new StringBuilder(); 184 long copied = CharStreams.copy(wrapAsGenericReadable(new StringReader(ASCII)), builder); 185 assertEquals(ASCII, builder.toString()); 186 assertEquals(ASCII.length(), copied); 187 188 StringBuilder builder2 = new StringBuilder(); 189 copied = CharStreams.copy(wrapAsGenericReadable(new StringReader(I18N)), builder2); 190 assertEquals(I18N, builder2.toString()); 191 assertEquals(I18N.length(), copied); 192 } 193 testCopy_toWriter_fromReader()194 public void testCopy_toWriter_fromReader() throws IOException { 195 StringWriter writer = new StringWriter(); 196 long copied = CharStreams.copy(new StringReader(ASCII), writer); 197 assertEquals(ASCII, writer.toString()); 198 assertEquals(ASCII.length(), copied); 199 200 StringWriter writer2 = new StringWriter(); 201 copied = CharStreams.copy(new StringReader(I18N), writer2); 202 assertEquals(I18N, writer2.toString()); 203 assertEquals(I18N.length(), copied); 204 } 205 testCopy_toWriter_fromReadable()206 public void testCopy_toWriter_fromReadable() throws IOException { 207 StringWriter writer = new StringWriter(); 208 long copied = CharStreams.copy(wrapAsGenericReadable(new StringReader(ASCII)), writer); 209 assertEquals(ASCII, writer.toString()); 210 assertEquals(ASCII.length(), copied); 211 212 StringWriter writer2 = new StringWriter(); 213 copied = CharStreams.copy(wrapAsGenericReadable(new StringReader(I18N)), writer2); 214 assertEquals(I18N, writer2.toString()); 215 assertEquals(I18N.length(), copied); 216 } 217 218 /** 219 * Test for Guava issue 1061: http://code.google.com/p/guava-libraries/issues/detail?id=1061 220 * 221 * <p>CharStreams.copy was failing to clear its CharBuffer after each read call, which effectively 222 * reduced the available size of the buffer each time a call to read didn't fill up the available 223 * space in the buffer completely. In general this is a performance problem since the buffer size 224 * is permanently reduced, but with certain Reader implementations it could also cause the buffer 225 * size to reach 0, causing an infinite loop. 226 */ testCopyWithReaderThatDoesNotFillBuffer()227 public void testCopyWithReaderThatDoesNotFillBuffer() throws IOException { 228 // need a long enough string for the buffer to hit 0 remaining before the copy completes 229 String string = Strings.repeat("0123456789", 100); 230 StringBuilder b = new StringBuilder(); 231 // the main assertion of this test is here... the copy will fail if the buffer size goes down 232 // each time it is not filled completely 233 long copied = CharStreams.copy(newNonBufferFillingReader(new StringReader(string)), b); 234 assertEquals(string, b.toString()); 235 assertEquals(string.length(), copied); 236 } 237 testExhaust_reader()238 public void testExhaust_reader() throws IOException { 239 Reader reader = new StringReader(ASCII); 240 assertEquals(ASCII.length(), CharStreams.exhaust(reader)); 241 assertEquals(-1, reader.read()); 242 assertEquals(0, CharStreams.exhaust(reader)); 243 244 Reader empty = new StringReader(""); 245 assertEquals(0, CharStreams.exhaust(empty)); 246 assertEquals(-1, empty.read()); 247 } 248 testExhaust_readable()249 public void testExhaust_readable() throws IOException { 250 CharBuffer buf = CharBuffer.wrap(ASCII); 251 assertEquals(ASCII.length(), CharStreams.exhaust(buf)); 252 assertEquals(0, buf.remaining()); 253 assertEquals(0, CharStreams.exhaust(buf)); 254 255 CharBuffer empty = CharBuffer.wrap(""); 256 assertEquals(0, CharStreams.exhaust(empty)); 257 assertEquals(0, empty.remaining()); 258 } 259 testNullWriter()260 public void testNullWriter() throws Exception { 261 // create a null writer 262 Writer nullWriter = CharStreams.nullWriter(); 263 // write to the writer 264 nullWriter.write('n'); 265 String test = "Test string for NullWriter"; 266 nullWriter.write(test); 267 nullWriter.write(test, 2, 10); 268 nullWriter.append(null); 269 nullWriter.append(null, 0, 4); 270 271 assertThrows(IndexOutOfBoundsException.class, () -> nullWriter.append(null, -1, 4)); 272 273 assertThrows(IndexOutOfBoundsException.class, () -> nullWriter.append(null, 0, 5)); 274 275 // nothing really to assert? 276 assertSame(CharStreams.nullWriter(), CharStreams.nullWriter()); 277 } 278 279 /** 280 * Returns a reader wrapping the given reader that only reads half of the maximum number of 281 * characters that it could read in read(char[], int, int). 282 */ newNonBufferFillingReader(Reader reader)283 private static Reader newNonBufferFillingReader(Reader reader) { 284 return new FilterReader(reader) { 285 @Override 286 public int read(char[] cbuf, int off, int len) throws IOException { 287 // if a buffer isn't being cleared correctly, this method will eventually start being called 288 // with a len of 0 forever 289 if (len <= 0) { 290 fail("read called with a len of " + len); 291 } 292 // read fewer than the max number of chars to read 293 // shouldn't be a problem unless the buffer is shrinking each call 294 return in.read(cbuf, off, Math.max(len - 1024, 0)); 295 } 296 }; 297 } 298 299 /** Wrap an appendable in an appendable to defeat any type specific optimizations. */ 300 private static Appendable wrapAsGenericAppendable(final Appendable a) { 301 return new Appendable() { 302 303 @Override 304 public Appendable append(CharSequence csq) throws IOException { 305 a.append(csq); 306 return this; 307 } 308 309 @Override 310 public Appendable append(CharSequence csq, int start, int end) throws IOException { 311 a.append(csq, start, end); 312 return this; 313 } 314 315 @Override 316 public Appendable append(char c) throws IOException { 317 a.append(c); 318 return this; 319 } 320 }; 321 } 322 323 /** Wrap a readable in a readable to defeat any type specific optimizations. */ 324 private static Readable wrapAsGenericReadable(final Readable a) { 325 return new Readable() { 326 @Override 327 public int read(CharBuffer cb) throws IOException { 328 return a.read(cb); 329 } 330 }; 331 } 332 } 333