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