Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/java/util/Base64/TestBase64.java
38812 views
/*1* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/**24* @test 4235519 8004212 8005394 8007298 8006295 8006315 8006530 8007379 800892525* 8014217 8025003 8026330 8028397 816524326* @summary tests java.util.Base6427* @key randomness28* @library /lib/testlibrary29* @build jdk.testlibrary.*30* @run main TestBase6431*/3233import java.io.ByteArrayInputStream;34import java.io.ByteArrayOutputStream;35import java.io.InputStream;36import java.io.IOException;37import java.io.OutputStream;38import java.nio.ByteBuffer;39import java.util.Arrays;40import java.util.ArrayList;41import java.util.Base64;42import java.util.List;43import java.util.Random;4445import jdk.testlibrary.RandomFactory;4647public class TestBase64 {4849private static final Random rnd = RandomFactory.getRandom();5051public static void main(String args[]) throws Throwable {52int numRuns = 10;53int numBytes = 200;54if (args.length > 1) {55numRuns = Integer.parseInt(args[0]);56numBytes = Integer.parseInt(args[1]);57}5859test(Base64.getEncoder(), Base64.getDecoder(), numRuns, numBytes);60test(Base64.getUrlEncoder(), Base64.getUrlDecoder(), numRuns, numBytes);61test(Base64.getMimeEncoder(), Base64.getMimeDecoder(), numRuns, numBytes);6263byte[] nl_1 = new byte[] {'\n'};64byte[] nl_2 = new byte[] {'\n', '\r'};65byte[] nl_3 = new byte[] {'\n', '\r', '\n'};66for (int i = 0; i < 10; i++) {67int len = rnd.nextInt(200) + 4;68test(Base64.getMimeEncoder(len, nl_1),69Base64.getMimeDecoder(),70numRuns, numBytes);71test(Base64.getMimeEncoder(len, nl_2),72Base64.getMimeDecoder(),73numRuns, numBytes);74test(Base64.getMimeEncoder(len, nl_3),75Base64.getMimeDecoder(),76numRuns, numBytes);77}7879testNull(Base64.getEncoder());80testNull(Base64.getUrlEncoder());81testNull(Base64.getMimeEncoder());82testNull(Base64.getMimeEncoder(10, new byte[]{'\n'}));83testNull(Base64.getDecoder());84testNull(Base64.getUrlDecoder());85testNull(Base64.getMimeDecoder());86checkNull(() -> Base64.getMimeEncoder(10, null));8788testIOE(Base64.getEncoder());89testIOE(Base64.getUrlEncoder());90testIOE(Base64.getMimeEncoder());91testIOE(Base64.getMimeEncoder(10, new byte[]{'\n'}));9293byte[] src = new byte[1024];94rnd.nextBytes(src);95final byte[] decoded = Base64.getEncoder().encode(src);96testIOE(Base64.getDecoder(), decoded);97testIOE(Base64.getMimeDecoder(), decoded);98testIOE(Base64.getUrlDecoder(), Base64.getUrlEncoder().encode(src));99100// illegal line separator101checkIAE(() -> Base64.getMimeEncoder(10, new byte[]{'\r', 'N'}));102103// malformed padding/ending104testMalformedPadding();105106// illegal base64 character107decoded[2] = (byte)0xe0;108checkIAE(() -> Base64.getDecoder().decode(decoded));109checkIAE(() -> Base64.getDecoder().decode(decoded, new byte[1024]));110checkIAE(() -> Base64.getDecoder().decode(ByteBuffer.wrap(decoded)));111112// test single-non-base64 character for mime decoding113testSingleNonBase64MimeDec();114115// test decoding of unpadded data116testDecodeUnpadded();117118// test mime decoding with ignored character after padding119testDecodeIgnoredAfterPadding();120121// given invalid args, encoder should not produce output122testEncoderKeepsSilence(Base64.getEncoder());123testEncoderKeepsSilence(Base64.getUrlEncoder());124testEncoderKeepsSilence(Base64.getMimeEncoder());125126// given invalid args, decoder should not consume input127testDecoderKeepsAbstinence(Base64.getDecoder());128testDecoderKeepsAbstinence(Base64.getUrlDecoder());129testDecoderKeepsAbstinence(Base64.getMimeDecoder());130}131132private static sun.misc.BASE64Encoder sunmisc = new sun.misc.BASE64Encoder();133134private static void test(Base64.Encoder enc, Base64.Decoder dec,135int numRuns, int numBytes) throws Throwable {136Random rnd = new java.util.Random();137138enc.encode(new byte[0]);139dec.decode(new byte[0]);140141for (boolean withoutPadding : new boolean[] { false, true}) {142if (withoutPadding) {143enc = enc.withoutPadding();144}145for (int i=0; i<numRuns; i++) {146for (int j=1; j<numBytes; j++) {147byte[] orig = new byte[j];148rnd.nextBytes(orig);149150// --------testing encode/decode(byte[])--------151byte[] encoded = enc.encode(orig);152byte[] decoded = dec.decode(encoded);153154checkEqual(orig, decoded,155"Base64 array encoding/decoding failed!");156if (withoutPadding) {157if (encoded[encoded.length - 1] == '=')158throw new RuntimeException(159"Base64 enc.encode().withoutPadding() has padding!");160}161// compare to sun.misc.BASE64Encoder162163byte[] encoded2 = sunmisc.encode(orig).getBytes("ASCII");164if (!withoutPadding) { // don't test for withoutPadding()165checkEqual(normalize(encoded), normalize(encoded2),166"Base64 enc.encode() does not match sun.misc.base64!");167}168// remove padding '=' to test non-padding decoding case169if (encoded[encoded.length -2] == '=')170encoded2 = Arrays.copyOf(encoded, encoded.length -2);171else if (encoded[encoded.length -1] == '=')172encoded2 = Arrays.copyOf(encoded, encoded.length -1);173else174encoded2 = null;175176// --------testing encodetoString(byte[])/decode(String)--------177String str = enc.encodeToString(orig);178if (!Arrays.equals(str.getBytes("ASCII"), encoded)) {179throw new RuntimeException(180"Base64 encodingToString() failed!");181}182byte[] buf = dec.decode(new String(encoded, "ASCII"));183checkEqual(buf, orig, "Base64 decoding(String) failed!");184185if (encoded2 != null) {186buf = dec.decode(new String(encoded2, "ASCII"));187checkEqual(buf, orig, "Base64 decoding(String) failed!");188}189190//-------- testing encode/decode(Buffer)--------191testEncode(enc, ByteBuffer.wrap(orig), encoded);192ByteBuffer bin = ByteBuffer.allocateDirect(orig.length);193bin.put(orig).flip();194testEncode(enc, bin, encoded);195196testDecode(dec, ByteBuffer.wrap(encoded), orig);197bin = ByteBuffer.allocateDirect(encoded.length);198bin.put(encoded).flip();199testDecode(dec, bin, orig);200201if (encoded2 != null)202testDecode(dec, ByteBuffer.wrap(encoded2), orig);203204// --------testing decode.wrap(input stream)--------205// 1) random buf length206ByteArrayInputStream bais = new ByteArrayInputStream(encoded);207InputStream is = dec.wrap(bais);208buf = new byte[orig.length + 10];209int len = orig.length;210int off = 0;211while (true) {212int n = rnd.nextInt(len);213if (n == 0)214n = 1;215n = is.read(buf, off, n);216if (n == -1) {217checkEqual(off, orig.length,218"Base64 stream decoding failed");219break;220}221off += n;222len -= n;223if (len == 0)224break;225}226buf = Arrays.copyOf(buf, off);227checkEqual(buf, orig, "Base64 stream decoding failed!");228229// 2) read one byte each230bais.reset();231is = dec.wrap(bais);232buf = new byte[orig.length + 10];233off = 0;234int b;235while ((b = is.read()) != -1) {236buf[off++] = (byte)b;237}238buf = Arrays.copyOf(buf, off);239checkEqual(buf, orig, "Base64 stream decoding failed!");240241// --------testing encode.wrap(output stream)--------242ByteArrayOutputStream baos = new ByteArrayOutputStream((orig.length + 2) / 3 * 4 + 10);243OutputStream os = enc.wrap(baos);244off = 0;245len = orig.length;246for (int k = 0; k < 5; k++) {247if (len == 0)248break;249int n = rnd.nextInt(len);250if (n == 0)251n = 1;252os.write(orig, off, n);253off += n;254len -= n;255}256if (len != 0)257os.write(orig, off, len);258os.close();259buf = baos.toByteArray();260checkEqual(buf, encoded, "Base64 stream encoding failed!");261262// 2) write one byte each263baos.reset();264os = enc.wrap(baos);265off = 0;266while (off < orig.length) {267os.write(orig[off++]);268}269os.close();270buf = baos.toByteArray();271checkEqual(buf, encoded, "Base64 stream encoding failed!");272273// --------testing encode(in, out); -> bigger buf--------274buf = new byte[encoded.length + rnd.nextInt(100)];275int ret = enc.encode(orig, buf);276checkEqual(ret, encoded.length,277"Base64 enc.encode(src, null) returns wrong size!");278buf = Arrays.copyOf(buf, ret);279checkEqual(buf, encoded,280"Base64 enc.encode(src, dst) failed!");281282// --------testing decode(in, out); -> bigger buf--------283buf = new byte[orig.length + rnd.nextInt(100)];284ret = dec.decode(encoded, buf);285checkEqual(ret, orig.length,286"Base64 enc.encode(src, null) returns wrong size!");287buf = Arrays.copyOf(buf, ret);288checkEqual(buf, orig,289"Base64 dec.decode(src, dst) failed!");290291}292}293}294}295296private static final byte[] ba_null = null;297private static final String str_null = null;298private static final ByteBuffer bb_null = null;299300private static void testNull(Base64.Encoder enc) {301checkNull(() -> enc.encode(ba_null));302checkNull(() -> enc.encodeToString(ba_null));303checkNull(() -> enc.encode(ba_null, new byte[10]));304checkNull(() -> enc.encode(new byte[10], ba_null));305checkNull(() -> enc.encode(bb_null));306checkNull(() -> enc.wrap((OutputStream)null));307}308309private static void testNull(Base64.Decoder dec) {310checkNull(() -> dec.decode(ba_null));311checkNull(() -> dec.decode(str_null));312checkNull(() -> dec.decode(ba_null, new byte[10]));313checkNull(() -> dec.decode(new byte[10], ba_null));314checkNull(() -> dec.decode(bb_null));315checkNull(() -> dec.wrap((InputStream)null));316}317318@FunctionalInterface319private static interface Testable {320public void test() throws Throwable;321}322323private static void testIOE(Base64.Encoder enc) throws Throwable {324ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);325OutputStream os = enc.wrap(baos);326os.write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9});327os.close();328checkIOE(() -> os.write(10));329checkIOE(() -> os.write(new byte[] {10}));330checkIOE(() -> os.write(new byte[] {10}, 1, 4));331}332333private static void testIOE(Base64.Decoder dec, byte[] decoded) throws Throwable {334ByteArrayInputStream bais = new ByteArrayInputStream(decoded);335InputStream is = dec.wrap(bais);336is.read(new byte[10]);337is.close();338checkIOE(() -> is.read());339checkIOE(() -> is.read(new byte[] {10}));340checkIOE(() -> is.read(new byte[] {10}, 1, 4));341checkIOE(() -> is.available());342checkIOE(() -> is.skip(20));343}344345private static final void checkNull(Runnable r) {346try {347r.run();348throw new RuntimeException("NPE is not thrown as expected");349} catch (NullPointerException npe) {}350}351352private static final void checkIOE(Testable t) throws Throwable {353try {354t.test();355throw new RuntimeException("IOE is not thrown as expected");356} catch (IOException ioe) {}357}358359private static final void checkIAE(Runnable r) throws Throwable {360try {361r.run();362throw new RuntimeException("IAE is not thrown as expected");363} catch (IllegalArgumentException iae) {}364}365366private static void testDecodeIgnoredAfterPadding() throws Throwable {367for (byte nonBase64 : new byte[] {'#', '(', '!', '\\', '-', '_', '\n', '\r'}) {368byte[][] src = new byte[][] {369"A".getBytes("ascii"),370"AB".getBytes("ascii"),371"ABC".getBytes("ascii"),372"ABCD".getBytes("ascii"),373"ABCDE".getBytes("ascii")374};375Base64.Encoder encM = Base64.getMimeEncoder();376Base64.Decoder decM = Base64.getMimeDecoder();377Base64.Encoder enc = Base64.getEncoder();378Base64.Decoder dec = Base64.getDecoder();379for (int i = 0; i < src.length; i++) {380// decode(byte[])381byte[] encoded = encM.encode(src[i]);382encoded = Arrays.copyOf(encoded, encoded.length + 1);383encoded[encoded.length - 1] = nonBase64;384checkEqual(decM.decode(encoded), src[i], "Non-base64 char is not ignored");385byte[] decoded = new byte[src[i].length];386decM.decode(encoded, decoded);387checkEqual(decoded, src[i], "Non-base64 char is not ignored");388389try {390dec.decode(encoded);391throw new RuntimeException("No IAE for non-base64 char");392} catch (IllegalArgumentException iae) {}393}394}395}396397private static void testMalformedPadding() throws Throwable {398Object[] data = new Object[] {399"$=#", "", 0, // illegal ending unit400"A", "", 0, // dangling single byte401"A=", "", 0,402"A==", "", 0,403"QUJDA", "ABC", 4,404"QUJDA=", "ABC", 4,405"QUJDA==", "ABC", 4,406407"=", "", 0, // unnecessary padding408"QUJD=", "ABC", 4, //"ABC".encode() -> "QUJD"409410"AA=", "", 0, // incomplete padding411"QQ=", "", 0,412"QQ=N", "", 0, // incorrect padding413"QQ=?", "", 0,414"QUJDQQ=", "ABC", 4,415"QUJDQQ=N", "ABC", 4,416"QUJDQQ=?", "ABC", 4,417};418419Base64.Decoder[] decs = new Base64.Decoder[] {420Base64.getDecoder(),421Base64.getUrlDecoder(),422Base64.getMimeDecoder()423};424425for (Base64.Decoder dec : decs) {426for (int i = 0; i < data.length; i += 3) {427final String srcStr = (String)data[i];428final byte[] srcBytes = srcStr.getBytes("ASCII");429final ByteBuffer srcBB = ByteBuffer.wrap(srcBytes);430byte[] expected = ((String)data[i + 1]).getBytes("ASCII");431int pos = (Integer)data[i + 2];432433// decode(byte[])434checkIAE(() -> dec.decode(srcBytes));435436// decode(String)437checkIAE(() -> dec.decode(srcStr));438439// decode(ByteBuffer)440checkIAE(() -> dec.decode(srcBB));441442// wrap stream443checkIOE(new Testable() {444public void test() throws IOException {445try (InputStream is = dec.wrap(new ByteArrayInputStream(srcBytes))) {446while (is.read() != -1);447}448}});449}450}451}452453private static void testDecodeUnpadded() throws Throwable {454byte[] srcA = new byte[] { 'Q', 'Q' };455byte[] srcAA = new byte[] { 'Q', 'Q', 'E'};456Base64.Decoder dec = Base64.getDecoder();457byte[] ret = dec.decode(srcA);458if (ret[0] != 'A')459throw new RuntimeException("Decoding unpadding input A failed");460ret = dec.decode(srcAA);461if (ret[0] != 'A' && ret[1] != 'A')462throw new RuntimeException("Decoding unpadding input AA failed");463ret = new byte[10];464if (dec.wrap(new ByteArrayInputStream(srcA)).read(ret) != 1 &&465ret[0] != 'A')466throw new RuntimeException("Decoding unpadding input A from stream failed");467if (dec.wrap(new ByteArrayInputStream(srcA)).read(ret) != 2 &&468ret[0] != 'A' && ret[1] != 'A')469throw new RuntimeException("Decoding unpadding input AA from stream failed");470}471472// single-non-base64-char should be ignored for mime decoding, but473// iae for basic decoding474private static void testSingleNonBase64MimeDec() throws Throwable {475for (String nonBase64 : new String[] {"#", "(", "!", "\\", "-", "_"}) {476if (Base64.getMimeDecoder().decode(nonBase64).length != 0) {477throw new RuntimeException("non-base64 char is not ignored");478}479try {480Base64.getDecoder().decode(nonBase64);481throw new RuntimeException("No IAE for single non-base64 char");482} catch (IllegalArgumentException iae) {}483}484}485486private static final void testEncode(Base64.Encoder enc, ByteBuffer bin, byte[] expected)487throws Throwable {488489ByteBuffer bout = enc.encode(bin);490byte[] buf = new byte[bout.remaining()];491bout.get(buf);492if (bin.hasRemaining()) {493throw new RuntimeException(494"Base64 enc.encode(ByteBuffer) failed!");495}496checkEqual(buf, expected, "Base64 enc.encode(bf, bf) failed!");497}498499private static final void testDecode(Base64.Decoder dec, ByteBuffer bin, byte[] expected)500throws Throwable {501502ByteBuffer bout = dec.decode(bin);503byte[] buf = new byte[bout.remaining()];504bout.get(buf);505checkEqual(buf, expected, "Base64 dec.decode(bf) failed!");506}507508private static final void checkEqual(int v1, int v2, String msg)509throws Throwable {510if (v1 != v2) {511System.out.printf(" v1=%d%n", v1);512System.out.printf(" v2=%d%n", v2);513throw new RuntimeException(msg);514}515}516517private static final void checkEqual(byte[] r1, byte[] r2, String msg)518throws Throwable {519if (!Arrays.equals(r1, r2)) {520System.out.printf(" r1[%d]=[%s]%n", r1.length, new String(r1));521System.out.printf(" r2[%d]=[%s]%n", r2.length, new String(r2));522throw new RuntimeException(msg);523}524}525526// remove line feeds,527private static final byte[] normalize(byte[] src) {528int n = 0;529boolean hasUrl = false;530for (int i = 0; i < src.length; i++) {531if (src[i] == '\r' || src[i] == '\n')532n++;533if (src[i] == '-' || src[i] == '_')534hasUrl = true;535}536if (n == 0 && hasUrl == false)537return src;538byte[] ret = new byte[src.length - n];539int j = 0;540for (int i = 0; i < src.length; i++) {541if (src[i] == '-')542ret[j++] = '+';543else if (src[i] == '_')544ret[j++] = '/';545else if (src[i] != '\r' && src[i] != '\n')546ret[j++] = src[i];547}548return ret;549}550551private static void testEncoderKeepsSilence(Base64.Encoder enc)552throws Throwable {553List<Integer> vals = Arrays.asList(Integer.MIN_VALUE,554Integer.MIN_VALUE + 1, -1111, -2, -1, 0, 1, 2, 3, 1111,555Integer.MAX_VALUE - 1, Integer.MAX_VALUE,556rnd.nextInt(), rnd.nextInt(), rnd.nextInt(),557rnd.nextInt());558byte[] buf = new byte[] {1, 0, 91};559for (int off : vals) {560for (int len : vals) {561if (off >= 0 && len >= 0 && off <= buf.length - len) {562// valid args, skip them563continue;564}565// invalid args, test them566System.out.println("testing off=" + off + ", len=" + len);567568ByteArrayOutputStream baos = new ByteArrayOutputStream(100);569try (OutputStream os = enc.wrap(baos)) {570os.write(buf, off, len);571throw new RuntimeException("Expected IOOBEx was not thrown");572} catch (IndexOutOfBoundsException expected) {573}574if (baos.size() > 0)575throw new RuntimeException("No output was expected, but got "576+ baos.size() + " bytes");577}578}579}580581private static void testDecoderKeepsAbstinence(Base64.Decoder dec)582throws Throwable {583List<Integer> vals = Arrays.asList(Integer.MIN_VALUE,584Integer.MIN_VALUE + 1, -1111, -2, -1, 0, 1, 2, 3, 1111,585Integer.MAX_VALUE - 1, Integer.MAX_VALUE,586rnd.nextInt(), rnd.nextInt(), rnd.nextInt(),587rnd.nextInt());588byte[] buf = new byte[3];589for (int off : vals) {590for (int len : vals) {591if (off >= 0 && len >= 0 && off <= buf.length - len) {592// valid args, skip them593continue;594}595// invalid args, test them596System.out.println("testing off=" + off + ", len=" + len);597598String input = "AAAAAAAAAAAAAAAAAAAAAA";599ByteArrayInputStream bais =600new ByteArrayInputStream(input.getBytes("Latin1"));601try (InputStream is = dec.wrap(bais)) {602is.read(buf, off, len);603throw new RuntimeException("Expected IOOBEx was not thrown");604} catch (IndexOutOfBoundsException expected) {605}606if (bais.available() != input.length())607throw new RuntimeException("No input should be consumed, "608+ "but consumed " + (input.length() - bais.available())609+ " bytes");610}611}612}613}614615616