View Javadoc
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    *      https://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  package org.apache.commons.io.output;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.fail;
21  
22  import java.io.IOException;
23  import java.io.StringWriter;
24  import java.nio.charset.Charset;
25  import java.nio.charset.CharsetDecoder;
26  import java.nio.charset.StandardCharsets;
27  import java.nio.file.StandardOpenOption;
28  import java.util.Random;
29  
30  import org.apache.commons.io.Charsets;
31  import org.apache.commons.io.charset.CharsetDecoders;
32  import org.apache.commons.lang3.StringUtils;
33  import org.apache.commons.lang3.SystemProperties;
34  import org.junit.jupiter.api.Test;
35  
36  /**
37   * Tests {@link WriterOutputStream}.
38   */
39  class WriterOutputStreamTest {
40  
41      private static final String UTF_16LE = StandardCharsets.UTF_16LE.name();
42      private static final String UTF_16BE = StandardCharsets.UTF_16BE.name();
43      private static final String UTF_16 = StandardCharsets.UTF_16.name();
44      private static final String UTF_8 = StandardCharsets.UTF_8.name();
45      private static final String TEST_STRING = "\u00e0 peine arriv\u00e9s nous entr\u00e2mes dans sa chambre";
46      private static final String LARGE_TEST_STRING = StringUtils.repeat(TEST_STRING, 100);
47  
48      private final Random random = new Random();
49  
50      @Test
51      void testFlush() throws IOException {
52          final StringWriter writer = new StringWriter();
53          try (WriterOutputStream out = new WriterOutputStream(writer, "us-ascii", 1024, false)) {
54              out.write("abc".getBytes(StandardCharsets.US_ASCII));
55              assertEquals(0, writer.getBuffer().length());
56              out.flush();
57              assertEquals("abc", writer.toString());
58          }
59      }
60  
61      @Test
62      void testIO887() throws IOException {
63          final StringWriter stringWriter1 = new StringWriter();
64          final Charset charset = StandardCharsets.UTF_8;
65          try (WriterOutputStream writerOutputStream = new WriterOutputStream(stringWriter1, charset)) {
66              writerOutputStream.write("¿Cómo estás".getBytes("Cp850"));
67          }
68          final String expected = "?C?mo est?s";
69          assertEquals(expected, stringWriter1.toString());
70          final StringWriter stringWriter2 = new StringWriter();
71          try (WriterOutputStream writerOutputStream = WriterOutputStream.builder().setWriter(stringWriter2).setCharset(charset).get()) {
72              writerOutputStream.write("¿Cómo estás".getBytes("Cp850"));
73          }
74          assertEquals(expected, stringWriter2.toString());
75      }
76  
77      @Test
78      void testLargeUTF8CharsetWithBufferedWrite() throws IOException {
79          testWithBufferedWrite(LARGE_TEST_STRING, UTF_8);
80      }
81  
82      @Test
83      void testLargeUTF8CharsetWithSingleByteWrite() throws IOException {
84          testWithSingleByteWrite(LARGE_TEST_STRING, StandardCharsets.UTF_8);
85      }
86  
87      @Test
88      void testLargeUTF8WithBufferedWrite() throws IOException {
89          testWithBufferedWrite(LARGE_TEST_STRING, UTF_8);
90      }
91  
92      @Test
93      void testLargeUTF8WithSingleByteWrite() throws IOException {
94          testWithSingleByteWrite(LARGE_TEST_STRING, UTF_8);
95      }
96  
97      @Test
98      void testNullCharsetDecoderWithSingleByteWrite() throws IOException {
99          testWithSingleByteWrite(TEST_STRING, (CharsetDecoder) null);
100     }
101 
102     @Test
103     void testNullCharsetNameWithSingleByteWrite() throws IOException {
104         testWithSingleByteWrite(TEST_STRING, (String) null);
105     }
106 
107     @Test
108     void testNullCharsetWithSingleByteWrite() throws IOException {
109         testWithSingleByteWrite(TEST_STRING, (Charset) null);
110     }
111 
112     @Test
113     void testUTF16BEWithBufferedWrite() throws IOException {
114         testWithBufferedWrite(TEST_STRING, UTF_16BE);
115     }
116 
117     @Test
118     void testUTF16BEWithSingleByteWrite() throws IOException {
119         testWithSingleByteWrite(TEST_STRING, UTF_16BE);
120     }
121 
122     @Test
123     void testUTF16LEWithBufferedWrite() throws IOException {
124         testWithBufferedWrite(TEST_STRING, UTF_16LE);
125     }
126 
127     @Test
128     void testUTF16LEWithSingleByteWrite() throws IOException {
129         testWithSingleByteWrite(TEST_STRING, UTF_16LE);
130     }
131 
132     @Test
133     void testUTF16WithBufferedWrite() throws IOException {
134         try {
135             testWithBufferedWrite(TEST_STRING, UTF_16);
136         } catch (final UnsupportedOperationException e) {
137             if (!SystemProperties.getJavaVendor().contains("IBM")) {
138                 fail("This test should only throw UOE on IBM JDKs with broken UTF-16");
139             }
140         }
141     }
142 
143     @Test
144     void testUTF16WithSingleByteWrite() throws IOException {
145         try {
146             testWithSingleByteWrite(TEST_STRING, UTF_16);
147         } catch (final UnsupportedOperationException e) {
148             if (!SystemProperties.getJavaVendor().contains("IBM")) {
149                 fail("This test should only throw UOE on IBM JDKs with broken UTF-16");
150             }
151         }
152     }
153 
154     @Test
155     void testUTF8WithBufferedWrite() throws IOException {
156         testWithBufferedWrite(TEST_STRING, UTF_8);
157     }
158 
159     @Test
160     void testUTF8WithSingleByteWrite() throws IOException {
161         testWithSingleByteWrite(TEST_STRING, UTF_8);
162     }
163 
164     private void testWithBufferedWrite(final String testString, final String charsetName) throws IOException {
165         final byte[] expected = testString.getBytes(charsetName);
166         final StringWriter writer = new StringWriter();
167         try (WriterOutputStream out = WriterOutputStream.builder().setWriter(writer).setCharset(charsetName).get()) {
168             int offset = 0;
169             while (offset < expected.length) {
170                 final int length = Math.min(random.nextInt(128), expected.length - offset);
171                 out.write(expected, offset, length);
172                 offset += length;
173             }
174         }
175         assertEquals(testString, writer.toString());
176     }
177 
178     private void testWithSingleByteWrite(final String testString, final Charset charset) throws IOException {
179         final byte[] bytes = testString.getBytes(Charsets.toCharset(charset));
180         StringWriter writer = new StringWriter();
181         try (WriterOutputStream out = new WriterOutputStream(writer, charset)) {
182             writeOneAtATime(bytes, out);
183         }
184         assertEquals(testString, writer.toString());
185         //
186         writer = new StringWriter();
187         try (WriterOutputStream out = WriterOutputStream.builder().setWriter(writer).setCharset(charset).get()) {
188             writeOneAtATime(bytes, out);
189         }
190         assertEquals(testString, writer.toString());
191     }
192 
193     private void testWithSingleByteWrite(final String testString, final CharsetDecoder charsetDecoder) throws IOException {
194         final byte[] bytes = testString.getBytes(CharsetDecoders.toCharsetDecoder(charsetDecoder).charset());
195         StringWriter writer = new StringWriter();
196         try (WriterOutputStream out = new WriterOutputStream(writer, charsetDecoder)) {
197             writeOneAtATime(bytes, out);
198         }
199         assertEquals(testString, writer.toString());
200         //
201         writer = new StringWriter();
202         try (WriterOutputStream out = WriterOutputStream.builder().setWriter(writer).setCharsetDecoder(charsetDecoder).get()) {
203             writeOneAtATime(bytes, out);
204         }
205         assertEquals(testString, writer.toString());
206     }
207 
208     private void testWithSingleByteWrite(final String testString, final String charsetName) throws IOException {
209         final byte[] bytes = testString.getBytes(Charsets.toCharset(charsetName));
210         StringWriter writer = new StringWriter();
211         try (WriterOutputStream out = new WriterOutputStream(writer, charsetName)) {
212             writeOneAtATime(bytes, out);
213         }
214         assertEquals(testString, writer.toString());
215         //
216         writer = new StringWriter();
217         try (WriterOutputStream out = WriterOutputStream.builder().setWriter(writer).setCharset(charsetName).get()) {
218             writeOneAtATime(bytes, out);
219         }
220         assertEquals(testString, writer.toString());
221     }
222 
223     @Test
224     void testWriteImmediately() throws IOException {
225         final StringWriter writer = new StringWriter();
226         try (WriterOutputStream out = new WriterOutputStream(writer, "us-ascii", 1024, true)) {
227             out.write("abc".getBytes(StandardCharsets.US_ASCII));
228             assertEquals("abc", writer.toString());
229         }
230         // @formatter:off
231         try (WriterOutputStream out = WriterOutputStream.builder()
232                 .setWriter(writer)
233                 .setCharset("us-ascii")
234                 .setBufferSize(1024)
235                 .setWriteImmediately(true)
236                 .setOpenOptions(StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)
237                 .get()) {
238             // @formatter:on
239             out.write("abc".getBytes(StandardCharsets.US_ASCII));
240             assertEquals("abcabc", writer.toString());
241         }
242     }
243 
244     private void writeOneAtATime(final byte[] bytes, final WriterOutputStream out) throws IOException {
245         for (final byte b : bytes) {
246             out.write(b);
247         }
248     }
249 }