Base64Test.java revision ab69e29c1927bdc6143324eba5ccd78f7c43128d
1/*
2 * Copyright (C) 2010 The Android Open Source Project
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
17package android.util;
18
19import junit.framework.TestCase;
20
21import java.io.ByteArrayInputStream;
22import java.io.ByteArrayOutputStream;
23import java.util.Random;
24
25public class Base64Test extends TestCase {
26    private static final String TAG = "Base64Test";
27
28    /** Decodes a string, returning a string. */
29    private String decodeString(String in) throws Exception {
30        byte[] out = Base64.decode(in, 0);
31        return new String(out);
32    }
33
34    /**
35     * Encodes the string 'in' using 'flags'.  Asserts that decoding
36     * gives the same string.  Returns the encoded string.
37     */
38    private String encodeToString(String in, int flags) throws Exception {
39        String b64 = Base64.encodeToString(in.getBytes(), flags);
40        String dec = decodeString(b64);
41        assertEquals(in, dec);
42        return b64;
43    }
44
45    /** Assert that decoding 'in' throws IllegalArgumentException. */
46    private void assertBad(String in) throws Exception {
47        try {
48            byte[] out = Base64.decode(in, 0);
49            fail("should have failed to decode");
50        } catch (IllegalArgumentException e) {
51        }
52    }
53
54    /** Assert that actual equals the first len bytes of expected. */
55    private void assertEquals(byte[] expected, int len, byte[] actual) {
56        assertEquals(len, actual.length);
57        for (int i = 0; i < len; ++i) {
58            assertEquals(expected[i], actual[i]);
59        }
60    }
61
62    /** Assert that actual equals the first len bytes of expected. */
63    private void assertEquals(byte[] expected, int len, byte[] actual, int alen) {
64        assertEquals(len, alen);
65        for (int i = 0; i < len; ++i) {
66            assertEquals(expected[i], actual[i]);
67        }
68    }
69
70    /** Assert that actual equals the first len bytes of expected. */
71    private void assertEquals(byte[] expected, byte[] actual) {
72        assertEquals(expected.length, actual.length);
73        for (int i = 0; i < expected.length; ++i) {
74            assertEquals(expected[i], actual[i]);
75        }
76    }
77
78    public void testDecodeExtraChars() throws Exception {
79        // padding 0
80        assertEquals("hello, world", decodeString("aGVsbG8sIHdvcmxk"));
81        assertBad("aGVsbG8sIHdvcmxk=");
82        assertBad("aGVsbG8sIHdvcmxk==");
83        assertBad("aGVsbG8sIHdvcmxk =");
84        assertBad("aGVsbG8sIHdvcmxk = = ");
85        assertEquals("hello, world", decodeString(" aGVs bG8s IHdv cmxk  "));
86        assertEquals("hello, world", decodeString(" aGV sbG8 sIHd vcmx k "));
87        assertEquals("hello, world", decodeString(" aG VsbG 8sIH dvcm xk "));
88        assertEquals("hello, world", decodeString(" a GVsb G8sI Hdvc mxk "));
89        assertEquals("hello, world", decodeString(" a G V s b G 8 s I H d v c m x k "));
90        assertEquals("hello, world", decodeString("_a*G_V*s_b*G_8*s_I*H_d*v_c*m_x*k_"));
91        assertEquals("hello, world", decodeString("aGVsbG8sIHdvcmxk"));
92
93        // padding 1
94        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPyE="));
95        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPyE"));
96        assertBad("aGVsbG8sIHdvcmxkPyE==");
97        assertBad("aGVsbG8sIHdvcmxkPyE ==");
98        assertBad("aGVsbG8sIHdvcmxkPyE = = ");
99        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E="));
100        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E"));
101        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E ="));
102        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E "));
103        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E = "));
104        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E   "));
105
106        // padding 2
107        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkLg=="));
108        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkLg"));
109        assertBad("aGVsbG8sIHdvcmxkLg=");
110        assertBad("aGVsbG8sIHdvcmxkLg =");
111        assertBad("aGVsbG8sIHdvcmxkLg = ");
112        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g=="));
113        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g"));
114        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g =="));
115        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g "));
116        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g = = "));
117        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g   "));
118    }
119
120    private static final byte[] BYTES = { (byte) 0xff, (byte) 0xee, (byte) 0xdd,
121                                          (byte) 0xcc, (byte) 0xbb, (byte) 0xaa,
122                                          (byte) 0x99, (byte) 0x88, (byte) 0x77 };
123
124    public void testBinaryDecode() throws Exception {
125        assertEquals(BYTES, 0, Base64.decode("", 0));
126        assertEquals(BYTES, 1, Base64.decode("/w==", 0));
127        assertEquals(BYTES, 2, Base64.decode("/+4=", 0));
128        assertEquals(BYTES, 3, Base64.decode("/+7d", 0));
129        assertEquals(BYTES, 4, Base64.decode("/+7dzA==", 0));
130        assertEquals(BYTES, 5, Base64.decode("/+7dzLs=", 0));
131        assertEquals(BYTES, 6, Base64.decode("/+7dzLuq", 0));
132        assertEquals(BYTES, 7, Base64.decode("/+7dzLuqmQ==", 0));
133        assertEquals(BYTES, 8, Base64.decode("/+7dzLuqmYg=", 0));
134    }
135
136    public void testWebSafe() throws Exception {
137        assertEquals(BYTES, 0, Base64.decode("", Base64.URL_SAFE));
138        assertEquals(BYTES, 1, Base64.decode("_w==", Base64.URL_SAFE));
139        assertEquals(BYTES, 2, Base64.decode("_-4=", Base64.URL_SAFE));
140        assertEquals(BYTES, 3, Base64.decode("_-7d", Base64.URL_SAFE));
141        assertEquals(BYTES, 4, Base64.decode("_-7dzA==", Base64.URL_SAFE));
142        assertEquals(BYTES, 5, Base64.decode("_-7dzLs=", Base64.URL_SAFE));
143        assertEquals(BYTES, 6, Base64.decode("_-7dzLuq", Base64.URL_SAFE));
144        assertEquals(BYTES, 7, Base64.decode("_-7dzLuqmQ==", Base64.URL_SAFE));
145        assertEquals(BYTES, 8, Base64.decode("_-7dzLuqmYg=", Base64.URL_SAFE));
146
147        assertEquals("", Base64.encodeToString(BYTES, 0, 0, Base64.URL_SAFE));
148        assertEquals("_w==\n", Base64.encodeToString(BYTES, 0, 1, Base64.URL_SAFE));
149        assertEquals("_-4=\n", Base64.encodeToString(BYTES, 0, 2, Base64.URL_SAFE));
150        assertEquals("_-7d\n", Base64.encodeToString(BYTES, 0, 3, Base64.URL_SAFE));
151        assertEquals("_-7dzA==\n", Base64.encodeToString(BYTES, 0, 4, Base64.URL_SAFE));
152        assertEquals("_-7dzLs=\n", Base64.encodeToString(BYTES, 0, 5, Base64.URL_SAFE));
153        assertEquals("_-7dzLuq\n", Base64.encodeToString(BYTES, 0, 6, Base64.URL_SAFE));
154        assertEquals("_-7dzLuqmQ==\n", Base64.encodeToString(BYTES, 0, 7, Base64.URL_SAFE));
155        assertEquals("_-7dzLuqmYg=\n", Base64.encodeToString(BYTES, 0, 8, Base64.URL_SAFE));
156    }
157
158    public void testFlags() throws Exception {
159        assertEquals("YQ==\n",       encodeToString("a", 0));
160        assertEquals("YQ==",         encodeToString("a", Base64.NO_WRAP));
161        assertEquals("YQ\n",         encodeToString("a", Base64.NO_PADDING));
162        assertEquals("YQ",           encodeToString("a", Base64.NO_PADDING | Base64.NO_WRAP));
163        assertEquals("YQ==\r\n",     encodeToString("a", Base64.CRLF));
164        assertEquals("YQ\r\n",       encodeToString("a", Base64.CRLF | Base64.NO_PADDING));
165
166        assertEquals("YWI=\n",       encodeToString("ab", 0));
167        assertEquals("YWI=",         encodeToString("ab", Base64.NO_WRAP));
168        assertEquals("YWI\n",        encodeToString("ab", Base64.NO_PADDING));
169        assertEquals("YWI",          encodeToString("ab", Base64.NO_PADDING | Base64.NO_WRAP));
170        assertEquals("YWI=\r\n",     encodeToString("ab", Base64.CRLF));
171        assertEquals("YWI\r\n",      encodeToString("ab", Base64.CRLF | Base64.NO_PADDING));
172
173        assertEquals("YWJj\n",       encodeToString("abc", 0));
174        assertEquals("YWJj",         encodeToString("abc", Base64.NO_WRAP));
175        assertEquals("YWJj\n",       encodeToString("abc", Base64.NO_PADDING));
176        assertEquals("YWJj",         encodeToString("abc", Base64.NO_PADDING | Base64.NO_WRAP));
177        assertEquals("YWJj\r\n",     encodeToString("abc", Base64.CRLF));
178        assertEquals("YWJj\r\n",     encodeToString("abc", Base64.CRLF | Base64.NO_PADDING));
179
180        assertEquals("YWJjZA==\n",   encodeToString("abcd", 0));
181        assertEquals("YWJjZA==",     encodeToString("abcd", Base64.NO_WRAP));
182        assertEquals("YWJjZA\n",     encodeToString("abcd", Base64.NO_PADDING));
183        assertEquals("YWJjZA",       encodeToString("abcd", Base64.NO_PADDING | Base64.NO_WRAP));
184        assertEquals("YWJjZA==\r\n", encodeToString("abcd", Base64.CRLF));
185        assertEquals("YWJjZA\r\n",   encodeToString("abcd", Base64.CRLF | Base64.NO_PADDING));
186    }
187
188    public void testLineLength() throws Exception {
189        String in_56 = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd";
190        String in_57 = in_56 + "e";
191        String in_58 = in_56 + "ef";
192        String in_59 = in_56 + "efg";
193        String in_60 = in_56 + "efgh";
194        String in_61 = in_56 + "efghi";
195
196        String prefix = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5emFi";
197        String out_56 = prefix + "Y2Q=\n";
198        String out_57 = prefix + "Y2Rl\n";
199        String out_58 = prefix + "Y2Rl\nZg==\n";
200        String out_59 = prefix + "Y2Rl\nZmc=\n";
201        String out_60 = prefix + "Y2Rl\nZmdo\n";
202        String out_61 = prefix + "Y2Rl\nZmdoaQ==\n";
203
204        // no newline for an empty input array.
205        assertEquals("", encodeToString("", 0));
206
207        assertEquals(out_56, encodeToString(in_56, 0));
208        assertEquals(out_57, encodeToString(in_57, 0));
209        assertEquals(out_58, encodeToString(in_58, 0));
210        assertEquals(out_59, encodeToString(in_59, 0));
211        assertEquals(out_60, encodeToString(in_60, 0));
212        assertEquals(out_61, encodeToString(in_61, 0));
213
214        assertEquals(out_56.replaceAll("=", ""), encodeToString(in_56, Base64.NO_PADDING));
215        assertEquals(out_57.replaceAll("=", ""), encodeToString(in_57, Base64.NO_PADDING));
216        assertEquals(out_58.replaceAll("=", ""), encodeToString(in_58, Base64.NO_PADDING));
217        assertEquals(out_59.replaceAll("=", ""), encodeToString(in_59, Base64.NO_PADDING));
218        assertEquals(out_60.replaceAll("=", ""), encodeToString(in_60, Base64.NO_PADDING));
219        assertEquals(out_61.replaceAll("=", ""), encodeToString(in_61, Base64.NO_PADDING));
220
221        assertEquals(out_56.replaceAll("\n", ""), encodeToString(in_56, Base64.NO_WRAP));
222        assertEquals(out_57.replaceAll("\n", ""), encodeToString(in_57, Base64.NO_WRAP));
223        assertEquals(out_58.replaceAll("\n", ""), encodeToString(in_58, Base64.NO_WRAP));
224        assertEquals(out_59.replaceAll("\n", ""), encodeToString(in_59, Base64.NO_WRAP));
225        assertEquals(out_60.replaceAll("\n", ""), encodeToString(in_60, Base64.NO_WRAP));
226        assertEquals(out_61.replaceAll("\n", ""), encodeToString(in_61, Base64.NO_WRAP));
227    }
228
229    /**
230     * Tests that Base64.Encoder.encode() does correct handling of the
231     * tail for each call.
232     *
233     * This test is disabled because while it passes if you can get it
234     * to run, android's test infrastructure currently doesn't allow
235     * us to get at package-private members (Base64.Encoder in
236     * this case).
237     */
238    public void XXXtestEncodeInternal() throws Exception {
239        byte[] input = { (byte) 0x61, (byte) 0x62, (byte) 0x63 };
240        byte[] output = new byte[100];
241
242        Base64.Encoder encoder = new Base64.Encoder(Base64.NO_PADDING | Base64.NO_WRAP,
243                                                    output);
244
245        encoder.process(input, 0, 3, false);
246        assertEquals("YWJj".getBytes(), 4, encoder.output, encoder.op);
247        assertEquals(0, encoder.tailLen);
248
249        encoder.process(input, 0, 3, false);
250        assertEquals("YWJj".getBytes(), 4, encoder.output, encoder.op);
251        assertEquals(0, encoder.tailLen);
252
253        encoder.process(input, 0, 1, false);
254        assertEquals(0, encoder.op);
255        assertEquals(1, encoder.tailLen);
256
257        encoder.process(input, 0, 1, false);
258        assertEquals(0, encoder.op);
259        assertEquals(2, encoder.tailLen);
260
261        encoder.process(input, 0, 1, false);
262        assertEquals("YWFh".getBytes(), 4, encoder.output, encoder.op);
263        assertEquals(0, encoder.tailLen);
264
265        encoder.process(input, 0, 2, false);
266        assertEquals(0, encoder.op);
267        assertEquals(2, encoder.tailLen);
268
269        encoder.process(input, 0, 2, false);
270        assertEquals("YWJh".getBytes(), 4, encoder.output, encoder.op);
271        assertEquals(1, encoder.tailLen);
272
273        encoder.process(input, 0, 2, false);
274        assertEquals("YmFi".getBytes(), 4, encoder.output, encoder.op);
275        assertEquals(0, encoder.tailLen);
276
277        encoder.process(input, 0, 1, true);
278        assertEquals("YQ".getBytes(), 2, encoder.output, encoder.op);
279    }
280
281    private static final String lipsum =
282            "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
283            "Quisque congue eleifend odio, eu ornare nulla facilisis eget. " +
284            "Integer eget elit diam, sit amet laoreet nibh. Quisque enim " +
285            "urna, pharetra vitae consequat eget, adipiscing eu ante. " +
286            "Aliquam venenatis arcu nec nibh imperdiet tempor. In id dui " +
287            "eget lorem aliquam rutrum vel vitae eros. In placerat ornare " +
288            "pretium. Curabitur non fringilla mi. Fusce ultricies, turpis " +
289            "eu ultrices suscipit, ligula nisi consectetur eros, dapibus " +
290            "aliquet dui sapien a turpis. Donec ultricies varius ligula, " +
291            "ut hendrerit arcu malesuada at. Praesent sed elit pretium " +
292            "eros luctus gravida. In ac dolor lorem. Cras condimentum " +
293            "convallis elementum. Phasellus vel felis in nulla ultrices " +
294            "venenatis. Nam non tortor non orci convallis convallis. " +
295            "Nam tristique lacinia hendrerit. Pellentesque habitant morbi " +
296            "tristique senectus et netus et malesuada fames ac turpis " +
297            "egestas. Vivamus cursus, nibh eu imperdiet porta, magna " +
298            "ipsum mollis mauris, sit amet fringilla mi nisl eu mi. " +
299            "Phasellus posuere, leo at ultricies vehicula, massa risus " +
300            "volutpat sapien, eu tincidunt diam ipsum eget nulla. Cras " +
301            "molestie dapibus commodo. Ut vel tellus at massa gravida " +
302            "semper non sed orci.";
303
304    public void testInputStream() throws Exception {
305        int[] flagses = { Base64.DEFAULT,
306                          Base64.NO_PADDING,
307                          Base64.NO_WRAP,
308                          Base64.NO_PADDING | Base64.NO_WRAP,
309                          Base64.CRLF,
310                          Base64.URL_SAFE };
311        int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 };
312        Random rng = new Random(32176L);
313
314        // Test input needs to be at least 2048 bytes to fill up the
315        // read buffer of Base64InputStream.
316        byte[] plain = (lipsum + lipsum + lipsum + lipsum + lipsum).getBytes();
317
318        for (int flags: flagses) {
319            byte[] encoded = Base64.encode(plain, flags);
320
321            ByteArrayInputStream bais;
322            Base64InputStream b64is;
323            byte[] actual = new byte[plain.length * 2];
324            int ap;
325            int b;
326
327            // ----- test decoding ("encoded" -> "plain") -----
328
329            // read as much as it will give us in one chunk
330            bais = new ByteArrayInputStream(encoded);
331            b64is = new Base64InputStream(bais, flags);
332            ap = 0;
333            while ((b = b64is.read(actual, ap, actual.length-ap)) != -1) {
334                ap += b;
335            }
336            assertEquals(actual, ap, plain);
337
338            // read individual bytes
339            bais = new ByteArrayInputStream(encoded);
340            b64is = new Base64InputStream(bais, flags);
341            ap = 0;
342            while ((b = b64is.read()) != -1) {
343                actual[ap++] = (byte) b;
344            }
345            assertEquals(actual, ap, plain);
346
347            // mix reads of variously-sized arrays with one-byte reads
348            bais = new ByteArrayInputStream(encoded);
349            b64is = new Base64InputStream(bais, flags);
350            ap = 0;
351            readloop: while (true) {
352                int l = writeLengths[rng.nextInt(writeLengths.length)];
353                if (l >= 0) {
354                    b = b64is.read(actual, ap, l);
355                    if (b == -1) break readloop;
356                    ap += b;
357                } else {
358                    for (int i = 0; i < -l; ++i) {
359                        if ((b = b64is.read()) == -1) break readloop;
360                        actual[ap++] = (byte) b;
361                    }
362                }
363            }
364            assertEquals(actual, ap, plain);
365
366            // ----- test encoding ("plain" -> "encoded") -----
367
368            // read as much as it will give us in one chunk
369            bais = new ByteArrayInputStream(plain);
370            b64is = new Base64InputStream(bais, flags, true);
371            ap = 0;
372            while ((b = b64is.read(actual, ap, actual.length-ap)) != -1) {
373                ap += b;
374            }
375            assertEquals(actual, ap, encoded);
376
377            // read individual bytes
378            bais = new ByteArrayInputStream(plain);
379            b64is = new Base64InputStream(bais, flags, true);
380            ap = 0;
381            while ((b = b64is.read()) != -1) {
382                actual[ap++] = (byte) b;
383            }
384            assertEquals(actual, ap, encoded);
385
386            // mix reads of variously-sized arrays with one-byte reads
387            bais = new ByteArrayInputStream(plain);
388            b64is = new Base64InputStream(bais, flags, true);
389            ap = 0;
390            readloop: while (true) {
391                int l = writeLengths[rng.nextInt(writeLengths.length)];
392                if (l >= 0) {
393                    b = b64is.read(actual, ap, l);
394                    if (b == -1) break readloop;
395                    ap += b;
396                } else {
397                    for (int i = 0; i < -l; ++i) {
398                        if ((b = b64is.read()) == -1) break readloop;
399                        actual[ap++] = (byte) b;
400                    }
401                }
402            }
403            assertEquals(actual, ap, encoded);
404        }
405    }
406
407    /**
408     * Tests that Base64OutputStream produces exactly the same results
409     * as calling Base64.encode/.decode on an in-memory array.
410     */
411    public void testOutputStream() throws Exception {
412        int[] flagses = { Base64.DEFAULT,
413                          Base64.NO_PADDING,
414                          Base64.NO_WRAP,
415                          Base64.NO_PADDING | Base64.NO_WRAP,
416                          Base64.CRLF,
417                          Base64.URL_SAFE };
418        int[] writeLengths = { -10, -5, -1, 0, 1, 1, 2, 2, 3, 10, 100 };
419        Random rng = new Random(32176L);
420
421        // Test input needs to be at least 1024 bytes to test filling
422        // up the write(int) buffer of Base64OutputStream.
423        byte[] plain = (lipsum + lipsum).getBytes();
424
425        for (int flags: flagses) {
426            byte[] encoded = Base64.encode(plain, flags);
427
428            ByteArrayOutputStream baos;
429            Base64OutputStream b64os;
430            byte[] actual;
431            int p;
432
433            // ----- test encoding ("plain" -> "encoded") -----
434
435            // one large write(byte[]) of the whole input
436            baos = new ByteArrayOutputStream();
437            b64os = new Base64OutputStream(baos, flags);
438            b64os.write(plain);
439            b64os.close();
440            actual = baos.toByteArray();
441            assertEquals(encoded, actual);
442
443            // many calls to write(int)
444            baos = new ByteArrayOutputStream();
445            b64os = new Base64OutputStream(baos, flags);
446            for (int i = 0; i < plain.length; ++i) {
447                b64os.write(plain[i]);
448            }
449            b64os.close();
450            actual = baos.toByteArray();
451            assertEquals(encoded, actual);
452
453            // intermixed sequences of write(int) with
454            // write(byte[],int,int) of various lengths.
455            baos = new ByteArrayOutputStream();
456            b64os = new Base64OutputStream(baos, flags);
457            p = 0;
458            while (p < plain.length) {
459                int l = writeLengths[rng.nextInt(writeLengths.length)];
460                l = Math.min(l, plain.length-p);
461                if (l >= 0) {
462                    b64os.write(plain, p, l);
463                    p += l;
464                } else {
465                    l = Math.min(-l, plain.length-p);
466                    for (int i = 0; i < l; ++i) {
467                        b64os.write(plain[p+i]);
468                    }
469                    p += l;
470                }
471            }
472            b64os.close();
473            actual = baos.toByteArray();
474            assertEquals(encoded, actual);
475
476            // ----- test decoding ("encoded" -> "plain") -----
477
478            // one large write(byte[]) of the whole input
479            baos = new ByteArrayOutputStream();
480            b64os = new Base64OutputStream(baos, flags, false);
481            b64os.write(encoded);
482            b64os.close();
483            actual = baos.toByteArray();
484            assertEquals(plain, actual);
485
486            // many calls to write(int)
487            baos = new ByteArrayOutputStream();
488            b64os = new Base64OutputStream(baos, flags, false);
489            for (int i = 0; i < encoded.length; ++i) {
490                b64os.write(encoded[i]);
491            }
492            b64os.close();
493            actual = baos.toByteArray();
494            assertEquals(plain, actual);
495
496            // intermixed sequences of write(int) with
497            // write(byte[],int,int) of various lengths.
498            baos = new ByteArrayOutputStream();
499            b64os = new Base64OutputStream(baos, flags, false);
500            p = 0;
501            while (p < encoded.length) {
502                int l = writeLengths[rng.nextInt(writeLengths.length)];
503                l = Math.min(l, encoded.length-p);
504                if (l >= 0) {
505                    b64os.write(encoded, p, l);
506                    p += l;
507                } else {
508                    l = Math.min(-l, encoded.length-p);
509                    for (int i = 0; i < l; ++i) {
510                        b64os.write(encoded[p+i]);
511                    }
512                    p += l;
513                }
514            }
515            b64os.close();
516            actual = baos.toByteArray();
517            assertEquals(plain, actual);
518        }
519    }
520}
521