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