001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.codec.binary;
019
020import static org.junit.Assert.assertTrue;
021import static org.junit.Assert.fail;
022
023import java.io.ByteArrayOutputStream;
024import java.io.OutputStream;
025import java.util.Arrays;
026
027import org.junit.Test;
028
029public class Base32OutputStreamTest {
030
031    private final static byte[] CRLF = {(byte) '\r', (byte) '\n'};
032
033    private final static byte[] LF = {(byte) '\n'};
034
035
036
037//    /**
038//     * Test the Base32OutputStream implementation against the special NPE inducing input
039//     * identified in the CODEC-98 bug.
040//     *
041//     * @throws Exception for some failure scenarios.
042//     */
043//    @Test
044//    public void testCodec98NPE() throws Exception {
045//        byte[] codec98 = StringUtils.getBytesUtf8(Base32TestData.CODEC_98_NPE);
046//        byte[] codec98_1024 = new byte[1024];
047//        System.arraycopy(codec98, 0, codec98_1024, 0, codec98.length);
048//        ByteArrayOutputStream data = new ByteArrayOutputStream(1024);
049//        Base32OutputStream stream = new Base32OutputStream(data, false);
050//        stream.write(codec98_1024, 0, 1024);
051//        stream.close();
052//
053//        byte[] decodedBytes = data.toByteArray();
054//        String decoded = StringUtils.newStringUtf8(decodedBytes);
055//        assertEquals(
056//            "codec-98 NPE Base32OutputStream", Base32TestData.CODEC_98_NPE_DECODED, decoded
057//        );
058//    }
059
060
061    /**
062     * Test the Base32OutputStream implementation against empty input.
063     *
064     * @throws Exception
065     *             for some failure scenarios.
066     */
067    @Test
068    public void testBase32EmptyOutputStreamMimeChunkSize() throws Exception {
069        testBase32EmptyOutputStream(BaseNCodec.MIME_CHUNK_SIZE);
070    }
071
072    /**
073     * Test the Base32OutputStream implementation against empty input.
074     *
075     * @throws Exception
076     *             for some failure scenarios.
077     */
078    @Test
079    public void testBase32EmptyOutputStreamPemChunkSize() throws Exception {
080        testBase32EmptyOutputStream(BaseNCodec.PEM_CHUNK_SIZE);
081    }
082
083    private void testBase32EmptyOutputStream(final int chunkSize) throws Exception {
084        final byte[] emptyEncoded = new byte[0];
085        final byte[] emptyDecoded = new byte[0];
086        testByteByByte(emptyEncoded, emptyDecoded, chunkSize, CRLF);
087        testByChunk(emptyEncoded, emptyDecoded, chunkSize, CRLF);
088    }
089
090    /**
091     * Test the Base32OutputStream implementation
092     *
093     * @throws Exception
094     *             for some failure scenarios.
095     */
096    @Test
097    public void testBase32OutputStreamByChunk() throws Exception {
098        // Hello World test.
099        byte[] encoded = StringUtils.getBytesUtf8(Base32TestData.BASE32_FIXTURE);
100        byte[] decoded = StringUtils.getBytesUtf8(Base32TestData.STRING_FIXTURE);
101        testByChunk(encoded, decoded, BaseNCodec.MIME_CHUNK_SIZE, CRLF);
102
103//        // Single Byte test.
104//        encoded = StringUtils.getBytesUtf8("AA==\r\n");
105//        decoded = new byte[]{(byte) 0};
106//        testByChunk(encoded, decoded, Base32.MIME_CHUNK_SIZE, CRLF);
107
108
109//        // Single Line test.
110//        String singleLine = Base32TestData.ENCODED_64_CHARS_PER_LINE.replaceAll("\n", "");
111//        encoded = StringUtils.getBytesUtf8(singleLine);
112//        decoded = Base32TestData.DECODED;
113//        testByChunk(encoded, decoded, 0, LF);
114
115        // test random data of sizes 0 thru 150
116        final BaseNCodec codec = new Base32();
117        for (int i = 0; i <= 150; i++) {
118            final byte[][] randomData = Base32TestData.randomData(codec, i);
119            encoded = randomData[1];
120            decoded = randomData[0];
121            testByChunk(encoded, decoded, 0, LF);
122        }
123    }
124
125    /**
126     * Test the Base32OutputStream implementation
127     *
128     * @throws Exception
129     *             for some failure scenarios.
130     */
131    @Test
132    public void testBase32OutputStreamByteByByte() throws Exception {
133        // Hello World test.
134        byte[] encoded = StringUtils.getBytesUtf8(Base32TestData.BASE32_FIXTURE);
135        byte[] decoded = StringUtils.getBytesUtf8(Base32TestData.STRING_FIXTURE);
136        testByteByByte(encoded, decoded, 76, CRLF);
137
138//        // Single Byte test.
139//        encoded = StringUtils.getBytesUtf8("AA==\r\n");
140//        decoded = new byte[]{(byte) 0};
141//        testByteByByte(encoded, decoded, 76, CRLF);
142
143
144//        // Single Line test.
145//        String singleLine = Base32TestData.ENCODED_64_CHARS_PER_LINE.replaceAll("\n", "");
146//        encoded = StringUtils.getBytesUtf8(singleLine);
147//        decoded = Base32TestData.DECODED;
148//        testByteByByte(encoded, decoded, 0, LF);
149
150        // test random data of sizes 0 thru 150
151        final BaseNCodec codec = new Base32();
152        for (int i = 0; i <= 150; i++) {
153            final byte[][] randomData = Base32TestData.randomData(codec, i);
154            encoded = randomData[1];
155            decoded = randomData[0];
156            testByteByByte(encoded, decoded, 0, LF);
157        }
158    }
159
160    /**
161     * Test method does three tests on the supplied data: 1. encoded ---[DECODE]--> decoded 2. decoded ---[ENCODE]-->
162     * encoded 3. decoded ---[WRAP-WRAP-WRAP-etc...] --> decoded
163     * <p/>
164     * By "[WRAP-WRAP-WRAP-etc...]" we mean situation where the Base32OutputStream wraps itself in encode and decode
165     * mode over and over again.
166     *
167     * @param encoded
168     *            Base32 encoded data
169     * @param decoded
170     *            the data from above, but decoded
171     * @param chunkSize
172     *            chunk size (line-length) of the Base32 encoded data.
173     * @param separator
174     *            Line separator in the Base32 encoded data.
175     * @throws Exception
176     *             Usually signifies a bug in the Base32 commons-codec implementation.
177     */
178    private void testByChunk(final byte[] encoded, final byte[] decoded, final int chunkSize, final byte[] separator) throws Exception {
179
180        // Start with encode.
181        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
182        OutputStream out = new Base32OutputStream(byteOut, true, chunkSize, separator);
183        out.write(decoded);
184        out.close();
185        byte[] output = byteOut.toByteArray();
186        assertTrue("Streaming chunked Base32 encode", Arrays.equals(output, encoded));
187
188        // Now let's try decode.
189        byteOut = new ByteArrayOutputStream();
190        out = new Base32OutputStream(byteOut, false);
191        out.write(encoded);
192        out.close();
193        output = byteOut.toByteArray();
194        assertTrue("Streaming chunked Base32 decode", Arrays.equals(output, decoded));
195
196        // I always wanted to do this! (wrap encoder with decoder etc etc).
197        byteOut = new ByteArrayOutputStream();
198        out = byteOut;
199        for (int i = 0; i < 10; i++) {
200            out = new Base32OutputStream(out, false);
201            out = new Base32OutputStream(out, true, chunkSize, separator);
202        }
203        out.write(decoded);
204        out.close();
205        output = byteOut.toByteArray();
206
207        assertTrue("Streaming chunked Base32 wrap-wrap-wrap!", Arrays.equals(output, decoded));
208    }
209
210    /**
211     * Test method does three tests on the supplied data: 1. encoded ---[DECODE]--> decoded 2. decoded ---[ENCODE]-->
212     * encoded 3. decoded ---[WRAP-WRAP-WRAP-etc...] --> decoded
213     * <p/>
214     * By "[WRAP-WRAP-WRAP-etc...]" we mean situation where the Base32OutputStream wraps itself in encode and decode
215     * mode over and over again.
216     *
217     * @param encoded
218     *            Base32 encoded data
219     * @param decoded
220     *            the data from above, but decoded
221     * @param chunkSize
222     *            chunk size (line-length) of the Base32 encoded data.
223     * @param separator
224     *            Line separator in the Base32 encoded data.
225     * @throws Exception
226     *             Usually signifies a bug in the Base32 commons-codec implementation.
227     */
228    private void testByteByByte(final byte[] encoded, final byte[] decoded, final int chunkSize, final byte[] separator) throws Exception {
229
230        // Start with encode.
231        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
232        OutputStream out = new Base32OutputStream(byteOut, true, chunkSize, separator);
233        for (final byte element : decoded) {
234            out.write(element);
235        }
236        out.close();
237        byte[] output = byteOut.toByteArray();
238        assertTrue("Streaming byte-by-byte Base32 encode", Arrays.equals(output, encoded));
239
240        // Now let's try decode.
241        byteOut = new ByteArrayOutputStream();
242        out = new Base32OutputStream(byteOut, false);
243        for (final byte element : encoded) {
244            out.write(element);
245        }
246        out.close();
247        output = byteOut.toByteArray();
248        assertTrue("Streaming byte-by-byte Base32 decode", Arrays.equals(output, decoded));
249
250        // Now let's try decode with tonnes of flushes.
251        byteOut = new ByteArrayOutputStream();
252        out = new Base32OutputStream(byteOut, false);
253        for (final byte element : encoded) {
254            out.write(element);
255            out.flush();
256        }
257        out.close();
258        output = byteOut.toByteArray();
259        assertTrue("Streaming byte-by-byte flush() Base32 decode", Arrays.equals(output, decoded));
260
261        // I always wanted to do this! (wrap encoder with decoder etc etc).
262        byteOut = new ByteArrayOutputStream();
263        out = byteOut;
264        for (int i = 0; i < 10; i++) {
265            out = new Base32OutputStream(out, false);
266            out = new Base32OutputStream(out, true, chunkSize, separator);
267        }
268        for (final byte element : decoded) {
269            out.write(element);
270        }
271        out.close();
272        output = byteOut.toByteArray();
273
274        assertTrue("Streaming byte-by-byte Base32 wrap-wrap-wrap!", Arrays.equals(output, decoded));
275    }
276
277    /**
278     * Tests Base32OutputStream.write for expected IndexOutOfBoundsException conditions.
279     *
280     * @throws Exception
281     *             for some failure scenarios.
282     */
283    @Test
284    public void testWriteOutOfBounds() throws Exception {
285        final byte[] buf = new byte[1024];
286        final ByteArrayOutputStream bout = new ByteArrayOutputStream();
287        final Base32OutputStream out = new Base32OutputStream(bout);
288
289        try {
290            out.write(buf, -1, 1);
291            fail("Expected Base32OutputStream.write(buf, -1, 1) to throw a IndexOutOfBoundsException");
292        } catch (final IndexOutOfBoundsException ioobe) {
293            // Expected
294        }
295
296        try {
297            out.write(buf, 1, -1);
298            fail("Expected Base32OutputStream.write(buf, 1, -1) to throw a IndexOutOfBoundsException");
299        } catch (final IndexOutOfBoundsException ioobe) {
300            // Expected
301        }
302
303        try {
304            out.write(buf, buf.length + 1, 0);
305            fail("Expected Base32OutputStream.write(buf, buf.length + 1, 0) to throw a IndexOutOfBoundsException");
306        } catch (final IndexOutOfBoundsException ioobe) {
307            // Expected
308        }
309
310        try {
311            out.write(buf, buf.length - 1, 2);
312            fail("Expected Base32OutputStream.write(buf, buf.length - 1, 2) to throw a IndexOutOfBoundsException");
313        } catch (final IndexOutOfBoundsException ioobe) {
314            // Expected
315        }
316        out.close();
317    }
318
319    /**
320     * Tests Base32OutputStream.write(null).
321     *
322     * @throws Exception
323     *             for some failure scenarios.
324     */
325    @Test
326    public void testWriteToNullCoverage() throws Exception {
327        final ByteArrayOutputStream bout = new ByteArrayOutputStream();
328        final Base32OutputStream out = new Base32OutputStream(bout);
329        try {
330            out.write(null, 0, 0);
331            fail("Expcted Base32OutputStream.write(null) to throw a NullPointerException");
332        } catch (final NullPointerException e) {
333            // Expected
334        }
335        out.close();
336    }
337
338}