1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.tests.java.io;
19
20import junit.framework.TestCase;
21import java.io.ByteArrayInputStream;
22import java.io.ByteArrayOutputStream;
23import java.io.File;
24import java.io.FileInputStream;
25import java.io.FileReader;
26import java.io.FileWriter;
27import java.io.IOException;
28import java.io.InputStreamReader;
29import java.io.OutputStreamWriter;
30import java.io.UnsupportedEncodingException;
31import java.nio.charset.Charset;
32import java.nio.charset.CharsetEncoder;
33
34public class OutputStreamWriterTest extends TestCase {
35
36    private static final int UPPER = 0xd800;
37
38    private static final int BUFFER_SIZE = 10000;
39
40    private ByteArrayOutputStream out;
41
42    private OutputStreamWriter writer;
43
44    static private final String source = "This is a test message with Unicode character. \u4e2d\u56fd is China's name in Chinese";
45
46    static private final String[] MINIMAL_CHARSETS = new String[] { "US-ASCII",
47            "ISO-8859-1", "UTF-16BE", "UTF-16LE", "UTF-16", "UTF-8" };
48
49    OutputStreamWriter osw;
50
51    InputStreamReader isr;
52
53    private ByteArrayOutputStream fos;
54
55    String testString = "Test_All_Tests\nTest_java_io_BufferedInputStream\nTest_java_io_BufferedOutputStream\nTest_java_io_ByteArrayInputStream\nTest_java_io_ByteArrayOutputStream\nTest_java_io_DataInputStream\n";
56
57    /*
58     * @see TestCase#setUp()
59     */
60    @Override
61    protected void setUp() throws Exception {
62        super.setUp();
63        out = new ByteArrayOutputStream();
64        writer = new OutputStreamWriter(out, "utf-8");
65
66        fos = new ByteArrayOutputStream();
67        osw = new OutputStreamWriter(fos);
68    }
69
70    /*
71     * @see TestCase#tearDown()
72     */
73    @Override
74    protected void tearDown() throws Exception {
75        try {
76            writer.close();
77
78            if (isr != null) {
79                isr.close();
80            }
81            osw.close();
82        } catch (Exception e) {
83            // Ignored
84        }
85
86        super.tearDown();
87    }
88
89    public void testClose() throws Exception {
90        writer.flush();
91        writer.close();
92        try {
93            writer.flush();
94            fail();
95        } catch (IOException e) {
96            // Expected
97        }
98    }
99
100    public void testFlush() throws Exception {
101        writer.write(source);
102        writer.flush();
103        String result = out.toString("utf-8");
104        assertEquals(source, result);
105    }
106
107    /*
108     * Class under test for void write(char[], int, int)
109     */
110    public void testWritecharArrayintint() throws IOException {
111        char[] chars = source.toCharArray();
112
113        // Throws IndexOutOfBoundsException if offset is negative
114        try {
115            writer.write((char[]) null, -1, -1);
116            fail();
117        } catch (NullPointerException exception) {
118        } catch (IndexOutOfBoundsException exception) {
119        }
120
121        // throws NullPointerException though count is negative
122        try {
123            writer.write((char[]) null, 1, -1);
124            fail();
125        } catch (NullPointerException exception) {
126        } catch (IndexOutOfBoundsException exception) {
127        }
128
129        try {
130            writer.write((char[]) null, 1, 1);
131            fail();
132        } catch (NullPointerException e) {
133            // Expected
134        }
135        try {
136            writer.write(new char[0], 0, 1);
137            fail();
138        } catch (IndexOutOfBoundsException e) {
139            // Expected
140        }
141        try {
142            writer.write(chars, -1, 1);
143            fail();
144        } catch (IndexOutOfBoundsException e) {
145            // Expected
146        }
147        try {
148            writer.write(chars, 0, -1);
149            fail();
150        } catch (IndexOutOfBoundsException e) {
151            // Expected
152        }
153        try {
154            writer.write(chars, 1, chars.length);
155            fail();
156        } catch (IndexOutOfBoundsException e) {
157            // Expected
158        }
159        writer.write(chars, 1, 2);
160        writer.flush();
161        assertEquals("hi", out.toString("utf-8"));
162        writer.write(chars, 0, chars.length);
163        writer.flush();
164        assertEquals("hi" + source, out.toString("utf-8"));
165
166        writer.close();
167        // After the stream is closed, should throw IOException first
168        try {
169            writer.write((char[]) null, -1, -1);
170            fail("should throw IOException");
171        } catch (IOException e) {
172            // Expected
173        }
174    }
175
176    /*
177     * Class under test for void write(int)
178     */
179    public void testWriteint() throws IOException {
180        writer.write(1);
181        writer.flush();
182        String str = new String(out.toByteArray(), "utf-8");
183        assertEquals("\u0001", str);
184
185        writer.write(2);
186        writer.flush();
187        str = new String(out.toByteArray(), "utf-8");
188        assertEquals("\u0001\u0002", str);
189
190        writer.write(-1);
191        writer.flush();
192        str = new String(out.toByteArray(), "utf-8");
193        assertEquals("\u0001\u0002\uffff", str);
194
195        writer.write(0xfedcb);
196        writer.flush();
197        str = new String(out.toByteArray(), "utf-8");
198        assertEquals("\u0001\u0002\uffff\uedcb", str);
199
200        writer.close();
201        // After the stream is closed, should throw IOException
202        try {
203            writer.write(1);
204            fail("should throw IOException");
205        } catch (IOException e) {
206            // expected
207        }
208    }
209
210    /*
211     * Class under test for void write(String, int, int)
212     */
213    public void testWriteStringintint() throws IOException {
214        try {
215            writer.write((String) null, 1, 1);
216            fail();
217        } catch (NullPointerException e) {
218            // Expected
219        }
220        try {
221            writer.write("", 0, 1);
222            fail();
223        } catch (StringIndexOutOfBoundsException e) {
224            // Expected
225        }
226        try {
227            writer.write("abc", -1, 1);
228            fail();
229        } catch (StringIndexOutOfBoundsException e) {
230            // Expected
231        }
232        try {
233            writer.write("abc", 0, -1);
234            fail();
235        } catch (IndexOutOfBoundsException e) {
236            // Expected
237        }
238        try {
239            writer.write("abc", 1, 3);
240            fail();
241        } catch (StringIndexOutOfBoundsException e) {
242            // Expected
243        }
244
245        try {
246            writer.write((String) null, -1, -1);
247            fail();
248        } catch (NullPointerException expected) {
249        } catch (IndexOutOfBoundsException expected) {
250        }
251
252        // Throws NullPointerException before StringIndexOutOfBoundsException
253        try {
254            writer.write((String) null, -1, 0);
255            fail();
256        } catch (NullPointerException expected) {
257        } catch (IndexOutOfBoundsException expected) {
258        }
259
260        writer.write("abc", 1, 2);
261        writer.flush();
262        assertEquals("bc", out.toString("utf-8"));
263        writer.write(source, 0, source.length());
264        writer.flush();
265        assertEquals("bc" + source, out.toString("utf-8"));
266
267        writer.close();
268        // Throws IndexOutOfBoundsException first if count is negative
269        try {
270            writer.write((String) null, 0, -1);
271            fail();
272        } catch (NullPointerException expected) {
273        } catch (IndexOutOfBoundsException expected) {
274        }
275
276        try {
277            writer.write((String) null, -1, 0);
278            fail();
279        } catch (NullPointerException expected) {
280        } catch (IndexOutOfBoundsException expected) {
281        }
282
283        try {
284            writer.write("abc", -1, 0);
285            fail("should throw StringIndexOutOfBoundsException");
286        } catch (StringIndexOutOfBoundsException e) {
287            // Expected
288        }
289
290        // Throws IOException
291        try {
292            writer.write("abc", 0, 1);
293            fail("should throw IOException");
294        } catch (IOException e) {
295            // expected
296        }
297    }
298
299    /*
300     * Class under test for void OutputStreamWriter(OutputStream)
301     */
302    public void testOutputStreamWriterOutputStream() throws IOException {
303        try {
304            writer = new OutputStreamWriter(null);
305            fail();
306        } catch (NullPointerException e) {
307            // Expected
308        }
309        OutputStreamWriter writer2 = new OutputStreamWriter(out);
310        writer2.close();
311    }
312
313    /*
314     * Class under test for void OutputStreamWriter(OutputStream, String)
315     */
316    public void testOutputStreamWriterOutputStreamString() throws IOException {
317        try {
318            writer = new OutputStreamWriter(null, "utf-8");
319            fail();
320        } catch (NullPointerException e) {
321            // Expected
322        }
323        try {
324            writer = new OutputStreamWriter(out, "");
325            fail();
326        } catch (UnsupportedEncodingException e) {
327            // Expected
328        }
329        try {
330            writer = new OutputStreamWriter(out, "badname");
331            fail();
332        } catch (UnsupportedEncodingException e) {
333            // Expected
334        }
335        try {
336            writer = new OutputStreamWriter(out, (String) null);
337            fail();
338        } catch (NullPointerException e) {
339            // Expected
340        }
341        OutputStreamWriter writer2 = new OutputStreamWriter(out, "ascii");
342        assertEquals(Charset.forName("ascii"), Charset.forName(writer2
343                .getEncoding()));
344        writer2.close();
345    }
346
347    /*
348     * Class under test for void OutputStreamWriter(OutputStream)
349     */
350    public void testOutputStreamWriterOutputStreamCharset() throws IOException {
351        Charset cs = Charset.forName("ascii");
352        try {
353            writer = new OutputStreamWriter(null, cs);
354            fail();
355        } catch (NullPointerException e) {
356            // Expected
357        }
358        try {
359            writer = new OutputStreamWriter(out, (Charset) null);
360            fail();
361        } catch (NullPointerException e) {
362            // Expected
363        }
364        OutputStreamWriter writer2 = new OutputStreamWriter(out, cs);
365        assertEquals(cs, Charset.forName(writer2.getEncoding()));
366        writer2.close();
367    }
368
369    /*
370     * Class under test for void OutputStreamWriter(OutputStream, String)
371     */
372    public void testOutputStreamWriterOutputStreamCharsetEncoder()
373            throws IOException {
374        Charset cs = Charset.forName("ascii");
375        CharsetEncoder enc = cs.newEncoder();
376        try {
377            writer = new OutputStreamWriter(null, enc);
378            fail();
379        } catch (NullPointerException e) {
380            // Expected
381        }
382        try {
383            writer = new OutputStreamWriter(out, (CharsetEncoder) null);
384            fail();
385        } catch (NullPointerException e) {
386            // Expected
387        }
388        OutputStreamWriter writer2 = new OutputStreamWriter(out, enc);
389        assertEquals(cs, Charset.forName(writer2.getEncoding()));
390        writer2.close();
391    }
392
393    public void testGetEncoding() {
394        Charset cs = Charset.forName("utf-8");
395        assertEquals(cs, Charset.forName(writer.getEncoding()));
396    }
397
398    public void testHandleEarlyEOFChar_1() throws IOException {
399        String str = "All work and no play makes Jack a dull boy\n"; //$NON-NLS-1$
400        int NUMBER = 2048;
401        int j = 0;
402        int len = str.length() * NUMBER;
403        char[] strChars = new char[len];
404        for (int i = 0; i < NUMBER; ++i) {
405            for (int k = 0; k < str.length(); ++k) {
406                strChars[j++] = str.charAt(k);
407            }
408        }
409
410        File f = File.createTempFile("one", "by_one");
411        f.deleteOnExit();
412        FileWriter fw = new FileWriter(f);
413        fw.write(strChars);
414        fw.close();
415        FileInputStream fis = new FileInputStream(f);
416        InputStreamReader in = new InputStreamReader(fis);
417        for (int offset = 0; offset < strChars.length; ++offset) {
418            int b = in.read();
419            assertFalse("Early EOF at offset", -1 == b);
420        }
421    }
422
423    public void testHandleEarlyEOFChar_2() throws IOException {
424        int capacity = 65536;
425        byte[] bytes = new byte[capacity];
426        byte[] bs = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };
427        for (int i = 0; i < bytes.length; i++) {
428            bytes[i] = bs[i / 8192];
429        }
430        String inputStr = new String(bytes);
431        int len = inputStr.length();
432        File f = File.createTempFile("FileWriterBugTest ", null); //$NON-NLS-1$
433        f.deleteOnExit();
434        FileWriter writer = new FileWriter(f);
435        writer.write(inputStr);
436        writer.close();
437        long flen = f.length();
438
439        FileReader reader = new FileReader(f);
440        char[] outChars = new char[capacity];
441        int outCount = reader.read(outChars);
442        String outStr = new String(outChars, 0, outCount);
443
444        assertEquals(len, flen);
445        assertEquals(inputStr, outStr);
446    }
447
448    public void testSingleCharIO() throws Exception {
449        InputStreamReader isr = null;
450        for (int i = 0; i < MINIMAL_CHARSETS.length; ++i) {
451            try {
452                out = new ByteArrayOutputStream();
453                writer = new OutputStreamWriter(out, MINIMAL_CHARSETS[i]);
454
455                int upper = UPPER;
456                switch (i) {
457                    case 0:
458                        upper = 128;
459                        break;
460                    case 1:
461                        upper = 256;
462                        break;
463                }
464
465                for (int c = 0; c < upper; ++c) {
466                    writer.write(c);
467                }
468                writer.flush();
469                byte[] result = out.toByteArray();
470
471                isr = new InputStreamReader(new ByteArrayInputStream(result),
472                        MINIMAL_CHARSETS[i]);
473                for (int expected = 0; expected < upper; ++expected) {
474                    assertEquals("Error when reading bytes in "
475                            + MINIMAL_CHARSETS[i], expected, isr.read());
476                }
477            } finally {
478                try {
479                    isr.close();
480                } catch (Exception e) {
481                }
482                try {
483                    writer.close();
484                } catch (Exception e) {
485                }
486            }
487        }
488    }
489
490    public void testBlockIO() throws Exception {
491        InputStreamReader isr = null;
492        char[] largeBuffer = new char[BUFFER_SIZE];
493        for (int i = 0; i < MINIMAL_CHARSETS.length; ++i) {
494            try {
495                out = new ByteArrayOutputStream();
496                writer = new OutputStreamWriter(out, MINIMAL_CHARSETS[i]);
497
498                int upper = UPPER;
499                switch (i) {
500                    case 0:
501                        upper = 128;
502                        break;
503                    case 1:
504                        upper = 256;
505                        break;
506                }
507
508                int m = 0;
509                for (int c = 0; c < upper; ++c) {
510                    largeBuffer[m++] = (char) c;
511                    if (m == BUFFER_SIZE) {
512                        writer.write(largeBuffer);
513                        m = 0;
514                    }
515                }
516                writer.write(largeBuffer, 0, m);
517                writer.flush();
518                byte[] result = out.toByteArray();
519
520                isr = new InputStreamReader(new ByteArrayInputStream(result),
521                        MINIMAL_CHARSETS[i]);
522                int expected = 0, read = 0, j = 0;
523                while (expected < upper) {
524                    if (j == read) {
525                        read = isr.read(largeBuffer);
526                        j = 0;
527                    }
528                    assertEquals("Error when reading bytes in "
529                            + MINIMAL_CHARSETS[i], expected++, largeBuffer[j++]);
530                }
531            } finally {
532                try {
533                    isr.close();
534                } catch (Exception e) {
535                }
536                try {
537                    writer.close();
538                } catch (Exception e) {
539                }
540            }
541        }
542    }
543
544    /**
545     * java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream)
546     */
547    public void test_ConstructorLjava_io_OutputStream() {
548        assertTrue("Used in tests", true);
549    }
550
551    /**
552     * java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream,
553     *java.lang.String)
554     */
555    public void test_ConstructorLjava_io_OutputStreamLjava_lang_String()
556            throws UnsupportedEncodingException {
557        osw = new OutputStreamWriter(fos, "8859_1");
558        try {
559            osw = new OutputStreamWriter(fos, "Bogus");
560            fail("Failed to throw Unsupported Encoding exception");
561        } catch (UnsupportedEncodingException e) {
562            // Expected
563        }
564    }
565
566    /**
567     * java.io.OutputStreamWriter#close()
568     */
569    public void test_close() throws IOException {
570        osw.close();
571
572        try {
573            osw.write(testString, 0, testString.length());
574            fail("Chars written after close");
575        } catch (IOException e) {
576            // Expected
577        }
578
579        ByteArrayOutputStream bout = new ByteArrayOutputStream();
580        try {
581            OutputStreamWriter writer = new OutputStreamWriter(bout,
582                    "ISO2022JP");
583            writer.write(new char[] { 'a' });
584            writer.close();
585            // the default is ASCII, there should not be any mode changes
586            String converted = new String(bout.toByteArray(), "ISO8859_1");
587            assertTrue("invalid conversion 1: " + converted, converted
588                    .equals("a"));
589
590            bout.reset();
591            writer = new OutputStreamWriter(bout, "ISO2022JP");
592            writer.write(new char[] { '\u3048' });
593            writer.flush();
594            // the byte sequence should not switch to ASCII mode until the
595            // stream is closed
596            converted = new String(bout.toByteArray(), "ISO8859_1");
597            assertTrue("invalid conversion 2: " + converted, converted
598                    .equals("\u001b$B$("));
599            writer.close();
600            converted = new String(bout.toByteArray(), "ISO8859_1");
601            assertTrue("invalid conversion 3: " + converted, converted
602                    .equals("\u001b$B$(\u001b(B"));
603
604            bout.reset();
605            writer = new OutputStreamWriter(bout, "ISO2022JP");
606            writer.write(new char[] { '\u3048' });
607            writer.write(new char[] { '\u3048' });
608            writer.close();
609            // there should not be a mode switch between writes
610            assertEquals("invalid conversion 4", "\u001b$B$($(\u001b(B",
611                    new String(bout.toByteArray(), "ISO8859_1"));
612        } catch (UnsupportedEncodingException e) {
613            // Can't test missing converter
614            System.out.println(e);
615        }
616    }
617
618    /**
619     * java.io.OutputStreamWriter#flush()
620     */
621    public void test_flush() throws IOException {
622        char[] buf = new char[testString.length()];
623        osw.write(testString, 0, testString.length());
624        osw.flush();
625        openInputStream();
626        isr.read(buf, 0, buf.length);
627        assertTrue("Chars not flushed", new String(buf, 0, buf.length)
628                .equals(testString));
629    }
630
631    /**
632     * Unlike the RI, we return the canonical encoding name and not something
633     * java specific.
634     */
635    public void test_getEncoding() throws IOException {
636        try {
637            osw = new OutputStreamWriter(fos, "8859_1");
638        } catch (UnsupportedEncodingException e) {
639            assertEquals("Returned incorrect encoding", "8859_1", osw
640                    .getEncoding());
641        }
642
643        OutputStreamWriter out = new OutputStreamWriter(
644                new ByteArrayOutputStream(), "UTF-16BE");
645        out.close();
646
647        String result = out.getEncoding();
648        assertNull(result);
649
650        out = null;
651        try {
652            out = new OutputStreamWriter(new ByteArrayOutputStream(),
653                    "UTF-16BE");
654        } catch (UnsupportedEncodingException e) {
655            // ok
656        }
657        result = out.getEncoding();
658        assertEquals("UTF-16BE", result);
659    }
660
661    /**
662     * java.io.OutputStreamWriter#write(char[], int, int)
663     */
664    public void test_write$CII() throws IOException {
665        char[] buf = new char[testString.length()];
666        osw.write(testString, 0, testString.length());
667        osw.close();
668        openInputStream();
669        isr.read(buf, 0, buf.length);
670        assertTrue("Incorrect chars returned", new String(buf, 0, buf.length)
671                .equals(testString));
672    }
673
674    /**
675     * java.io.OutputStreamWriter#write(int)
676     */
677    public void test_writeI() throws IOException {
678        osw.write('T');
679        osw.close();
680        openInputStream();
681        int c = isr.read();
682        assertEquals("Incorrect char returned", 'T', (char) c);
683    }
684
685    /**
686     * java.io.OutputStreamWriter#write(java.lang.String, int, int)
687     */
688    public void test_writeLjava_lang_StringII() throws IOException {
689        char[] buf = new char[testString.length()];
690        osw.write(testString, 0, testString.length());
691        osw.close();
692        openInputStream();
693        isr.read(buf);
694        assertTrue("Incorrect chars returned", new String(buf, 0, buf.length)
695                .equals(testString));
696    }
697
698    private void openInputStream() {
699        isr = new InputStreamReader(new ByteArrayInputStream(fos.toByteArray()));
700    }
701}
702