Path: blob/master/test/jdk/sun/nio/cs/FindDecoderBugs.java
41149 views
/*1* Copyright (c) 2008, 2017, 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* @test25* @bug 638072326* @summary Decode many byte sequences in many ways (use -Dseed=X to set PRNG seed)27* @library /test/lib28* @build jdk.test.lib.RandomFactory29* @run main/timeout=1800 FindDecoderBugs30* @author Martin Buchholz31* @key randomness32*/3334import java.util.*;35import java.util.regex.*;36import java.nio.*;37import java.nio.charset.*;38import jdk.test.lib.RandomFactory;3940public class FindDecoderBugs {4142static boolean isBroken(String csn) {43if (csn.equals("x-COMPOUND_TEXT")) return true;44return false;45}4647static <T extends Comparable<? super T>> List<T> sort(Collection<T> c) {48List<T> list = new ArrayList<T>(c);49Collections.sort(list);50return list;51}5253static class TooManyFailures extends RuntimeException {54private static final long serialVersionUID = 0L;55}5657static String string(byte[] a) {58final StringBuilder sb = new StringBuilder();59for (byte b : a) {60if (sb.length() != 0) sb.append(' ');61sb.append(String.format("%02x", b & 0xff));62}63return sb.toString();64}6566static String string(char[] a) {67final StringBuilder sb = new StringBuilder();68for (char c : a) {69if (sb.length() != 0) sb.append(' ');70sb.append(String.format("\\u%04x", (int) c));71}72return sb.toString();73}7475static class Reporter {76// Some machinery to make sure only a small number of errors77// that are "too similar" are reported.78static class Counts extends HashMap<String, Long> {79private static final long serialVersionUID = -1;80long inc(String signature) {81Long count = get(signature);82if (count == null) count = 0L;83put(signature, count+1);84return count+1;85}86}8788final Counts failureCounts = new Counts();89final static long maxFailures = 2;9091final static Pattern hideBytes = Pattern.compile("\"[0-9a-f ]+\"");92final static Pattern hideChars = Pattern.compile("\\\\u[0-9a-f]{4}");9394boolean bug(String format, Object... args) {95String signature = String.format(format, args);96signature = hideBytes.matcher(signature).replaceAll("\"??\"");97signature = hideChars.matcher(signature).replaceAll("\\u????");98failed++;99if (failureCounts.inc(signature) <= maxFailures) {100System.out.printf(format, args);101System.out.println();102return true;103}104return false;105}106107void summarize() {108for (String key : sort(failureCounts.keySet()))109System.out.printf("-----%n%s%nfailures=%d%n",110key, failureCounts.get(key));111}112}113114static final Reporter reporter = new Reporter();115116static class Result {117final int limit;118final int ipos;119final boolean direct;120final byte[] ia;121final char[] oa;122final CoderResult cr;123124Result(ByteBuffer ib, CharBuffer ob, CoderResult cr) {125ipos = ib.position();126ia = toArray(ib);127oa = toArray(ob);128direct = ib.isDirect();129limit = ob.limit();130this.cr = cr;131}132133static byte[] toArray(ByteBuffer b) {134int pos = b.position();135byte[] a = new byte[b.limit()];136b.position(0);137b.get(a);138b.position(pos);139return a;140}141142static char[] toArray(CharBuffer b) {143char[] a = new char[b.position()];144b.position(0);145b.get(a);146return a;147}148149static boolean eq(Result x, Result y) {150return x == y ||151(x != null && y != null &&152(Arrays.equals(x.oa, y.oa) &&153x.ipos == y.ipos &&154x.cr == y.cr));155}156157public String toString() {158return String.format("\"%s\"[%d/%d] => %s \"%s\"[%d/%d]%s",159string(ia), ipos, ia.length,160cr, string(oa), oa.length, limit,161(direct ? " (direct)" : ""));162}163}164165// legend: r=regular d=direct In=Input Ou=Output166static final int maxBufSize = 20;167static final ByteBuffer[] ribs = new ByteBuffer[maxBufSize];168static final ByteBuffer[] dibs = new ByteBuffer[maxBufSize];169170static final CharBuffer[] robs = new CharBuffer[maxBufSize];171static final CharBuffer[] dobs = new CharBuffer[maxBufSize];172static {173for (int i = 0; i < maxBufSize; i++) {174ribs[i] = ByteBuffer.allocate(i);175dibs[i] = ByteBuffer.allocateDirect(i);176robs[i] = CharBuffer.allocate(i);177dobs[i] = ByteBuffer.allocateDirect(i*2).asCharBuffer();178}179}180181static class CharsetTester {182private final Charset cs;183private static final long maxFailures = 5;184private long failures = 0;185// private static final long maxCharsetFailures = Long.MAX_VALUE;186private static final long maxCharsetFailures = 10000L;187private final long failed0 = failed;188189CharsetTester(Charset cs) {190this.cs = cs;191}192193static boolean bug(String format, Object... args) {194return reporter.bug(format, args);195}196197Result recode(ByteBuffer ib, CharBuffer ob) {198try {199char canary = '\u4242';200ib.clear(); // Prepare to read201ob.clear(); // Prepare to write202for (int i = 0; i < ob.limit(); i++)203ob.put(i, canary);204CharsetDecoder coder = cs.newDecoder();205CoderResult cr = coder.decode(ib, ob, false);206equal(ib.limit(), ib.capacity());207equal(ob.limit(), ob.capacity());208Result r = new Result(ib, ob, cr);209if (cr.isError())210check(cr.length() > 0);211if (cr.isOverflow() && ob.remaining() > 10)212bug("OVERFLOW, but there's lots of room: %s %s",213cs, r);214// if (cr.isOverflow() && ib.remaining() == 0)215// bug("OVERFLOW, yet remaining() == 0: %s %s",216// cs, r);217if (cr.isError() && ib.remaining() < cr.length())218bug("remaining() < CoderResult.length(): %s %s",219cs, r);220// if (ib.position() == 0 && ob.position() > 0)221// reporter. bug("output only if input consumed: %s %s",222// cs, r);223// Should we warn if cr.isUnmappable() ??224CoderResult cr2 = coder.decode(ib, ob, false);225if (ib.position() != r.ipos ||226ob.position() != r.oa.length ||227cr != cr2)228bug("Coding operation not idempotent: %s%n %s%n %s",229cs, r, new Result(ib, ob, cr2));230if (ob.position() < ob.limit() &&231ob.get(ob.position()) != canary)232bug("Buffer overrun: %s %s %s",233cs, r, ob.get(ob.position()));234return r;235} catch (Throwable t) {236if (bug("Unexpected exception: %s %s %s",237cs, t.getClass().getSimpleName(),238new Result(ib, ob, null)))239t.printStackTrace();240return null;241}242}243244Result recode2(byte[] ia, int n) {245int len = ia.length;246ByteBuffer rib = ByteBuffer.wrap(ia);247ByteBuffer dib = dibs[len];248dib.clear(); dib.put(ia); dib.clear();249CharBuffer rob = robs[n];250CharBuffer dob = dobs[n];251equal(rob.limit(), n);252equal(dob.limit(), n);253check(dib.isDirect());254check(dob.isDirect());255Result r1 = recode(rib, rob);256Result r2 = recode(dib, dob);257if (r1 != null && r2 != null && ! Result.eq(r1, r2))258bug("Results differ for direct buffers: %s%n %s%n %s",259cs, r1, r2);260return r1;261}262263Result test(byte[] ia) {264if (failed - failed0 >= maxCharsetFailures)265throw new TooManyFailures();266267Result roomy = recode2(ia, maxBufSize - 1);268if (roomy == null) return roomy;269int olen = roomy.oa.length;270if (olen > 0) {271if (roomy.ipos == roomy.ia.length) {272Result perfectFit = recode2(ia, olen);273if (! Result.eq(roomy, perfectFit))274bug("Results differ: %s%n %s%n %s",275cs, roomy, perfectFit);276}277for (int i = 0; i < olen; i++) {278Result claustrophobic = recode2(ia, i);279if (claustrophobic == null) return roomy;280if (roomy.cr.isUnderflow() &&281! claustrophobic.cr.isOverflow())282bug("Expected OVERFLOW: %s%n %s%n %s",283cs, roomy, claustrophobic);284}285}286return roomy;287}288289void testExhaustively(byte[] prefix, int n) {290int len = prefix.length;291byte[] ia = Arrays.copyOf(prefix, len + 1);292for (int i = 0; i < 0x100; i++) {293ia[len] = (byte) i;294if (n == 1)295test(ia);296else297testExhaustively(ia, n - 1);298}299}300301void testRandomly(byte[] prefix, int n) {302int len = prefix.length;303byte[] ia = Arrays.copyOf(prefix, len + n);304for (int i = 0; i < 5000; i++) {305for (int j = 0; j < n; j++)306ia[len + j] = randomByte();307test(ia);308}309}310311void testPrefix(byte[] prefix) {312if (prefix.length > 0)313System.out.printf("Testing prefix %s%n", string(prefix));314315test(prefix);316317testExhaustively(prefix, 1);318testExhaustively(prefix, 2);319// Can you spare a week of CPU time?320// testExhaustively(cs, tester, prefix, 3);321322testRandomly(prefix, 3);323testRandomly(prefix, 4);324}325}326327private final static Random rnd = RandomFactory.getRandom();328private static byte randomByte() {329return (byte) rnd.nextInt(0x100);330}331private static byte[] randomBytes(int len) {332byte[] a = new byte[len];333for (int i = 0; i < len; i++)334a[i] = randomByte();335return a;336}337338private static final byte SS2 = (byte) 0x8e;339private static final byte SS3 = (byte) 0x8f;340private static final byte ESC = (byte) 0x1b;341private static final byte SO = (byte) 0x0e;342private static final byte SI = (byte) 0x0f;343344private final static byte[][] stateChangers = {345{SS2}, {SS3}, {SO}, {SI}346};347348private final static byte[][]escapeSequences = {349{ESC, '(', 'B'},350{ESC, '(', 'I'},351{ESC, '(', 'J'},352{ESC, '$', '@'},353{ESC, '$', 'A'},354{ESC, '$', ')', 'A'},355{ESC, '$', ')', 'C'},356{ESC, '$', ')', 'G'},357{ESC, '$', '*', 'H'},358{ESC, '$', '+', 'I'},359{ESC, '$', 'B'},360{ESC, 'N'},361{ESC, 'O'},362{ESC, '$', '(', 'D'},363};364365private static boolean isStateChanger(Charset cs, byte[] ia) {366Result r = new CharsetTester(cs).recode2(ia, 9);367return r == null ? false :368(r.cr.isUnderflow() &&369r.ipos == ia.length &&370r.oa.length == 0);371}372373private final static byte[][] incompletePrefixes = {374{ESC},375{ESC, '('},376{ESC, '$'},377{ESC, '$', '(',},378};379380private static boolean isIncompletePrefix(Charset cs, byte[] ia) {381Result r = new CharsetTester(cs).recode2(ia, 9);382return r == null ? false :383(r.cr.isUnderflow() &&384r.ipos == 0 &&385r.oa.length == 0);386}387388private static void testCharset(Charset cs) throws Throwable {389final String csn = cs.name();390391if (isBroken(csn)) {392System.out.printf("Skipping possibly broken charset %s%n", csn);393return;394}395System.out.println(csn);396CharsetTester tester = new CharsetTester(cs);397398tester.testPrefix(new byte[0]);399400if (! csn.matches("(?:x-)?(?:UTF|JIS(?:_X)?0).*")) {401for (byte[] prefix : stateChangers)402if (isStateChanger(cs, prefix))403tester.testPrefix(prefix);404405for (byte[] prefix : incompletePrefixes)406if (isIncompletePrefix(cs, prefix))407tester.testPrefix(prefix);408409if (isIncompletePrefix(cs, new byte[] {ESC}))410for (byte[] prefix : escapeSequences)411if (isStateChanger(cs, prefix))412tester.testPrefix(prefix);413}414}415416private static void realMain(String[] args) {417for (Charset cs : sort(Charset.availableCharsets().values())) {418try {419testCharset(cs);420} catch (TooManyFailures e) {421System.out.printf("Too many failures for %s%n", cs);422} catch (Throwable t) {423unexpected(t);424}425}426reporter.summarize();427}428429//--------------------- Infrastructure ---------------------------430static volatile long passed = 0, failed = 0;431static void pass() {passed++;}432static void fail() {failed++; Thread.dumpStack();}433static void fail(String format, Object... args) {434System.out.println(String.format(format, args)); failed++;}435static void fail(String msg) {System.out.println(msg); fail();}436static void unexpected(Throwable t) {failed++; t.printStackTrace();}437static void check(boolean cond) {if (cond) pass(); else fail();}438static void equal(Object x, Object y) {439if (x == null ? y == null : x.equals(y)) pass();440else fail(x + " not equal to " + y);}441static void equal(int x, int y) {442if (x == y) pass();443else fail(x + " not equal to " + y);}444public static void main(String[] args) throws Throwable {445try {realMain(args);} catch (Throwable t) {unexpected(t);}446System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);447if (failed > 0) throw new AssertionError("Some tests failed");}448}449450451