Path: blob/master/test/jdk/sun/nio/cs/FindEncoderBugs.java
41149 views
/*1* Copyright (c) 2008, 2018, 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 6233345 6381699 6381702 6381705 638170626* @summary Encode many char sequences in many ways27* @library /test/lib28* @build jdk.test.lib.RandomFactory29* @run main/timeout=1200 FindEncoderBugs30* @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 FindEncoderBugs {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);96// signature = hideBytes.matcher(signature).replaceAll("\"??\"");97// signature = 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 char[] ia;121final byte[] oa;122final CoderResult cr;123124private static byte[] toByteArray(ByteBuffer bb) {125byte[] bytes = new byte[bb.position()];126for (int i = 0; i < bytes.length; i++)127bytes[i] = bb.get(i);128return bytes;129}130131Result(CharBuffer ib, ByteBuffer ob, CoderResult cr) {132ipos = ib.position();133ia = toArray(ib);134oa = toArray(ob);135direct = ib.isDirect();136limit = ob.limit();137this.cr = cr;138}139140static char[] toArray(CharBuffer b) {141int pos = b.position();142char[] a = new char[b.limit()];143b.position(0);144b.get(a);145b.position(pos);146return a;147}148149static byte[] toArray(ByteBuffer b) {150byte[] a = new byte[b.position()];151b.position(0);152b.get(a);153return a;154}155156static boolean eq(Result x, Result y) {157return x == y ||158(x != null && y != null &&159(Arrays.equals(x.oa, y.oa) &&160x.ipos == y.ipos &&161x.cr == y.cr));162}163164public String toString() {165return String.format("\"%s\"[%d/%d] => %s \"%s\"[%d/%d]%s",166string(ia), ipos, ia.length,167cr, string(oa), oa.length, limit,168(direct ? " (direct)" : ""));169}170}171172static class CharsetTester {173private final Charset cs;174private final boolean hasBom;175private static final int maxFailures = 5;176private int failures = 0;177// private static final long maxCharsetFailures = Long.MAX_VALUE;178private static final long maxCharsetFailures = 10000L;179private final long failed0 = failed;180181// legend: r=regular d=direct In=Input Ou=Output182static final int maxBufSize = 40;183static final CharBuffer[] rInBuffers = new CharBuffer[maxBufSize];184static final CharBuffer[] dInBuffers = new CharBuffer[maxBufSize];185186static final ByteBuffer[] rOuBuffers = new ByteBuffer[maxBufSize];187static final ByteBuffer[] dOuBuffers = new ByteBuffer[maxBufSize];188static {189for (int i = 0; i < maxBufSize; i++) {190rInBuffers[i] = CharBuffer.allocate(i);191dInBuffers[i] = ByteBuffer.allocateDirect(i*2).asCharBuffer();192rOuBuffers[i] = ByteBuffer.allocate(i);193dOuBuffers[i] = ByteBuffer.allocateDirect(i);194}195}196197CharsetTester(Charset cs) {198this.cs = cs;199this.hasBom =200cs.name().matches(".*BOM.*") ||201cs.name().equals("UTF-16");202}203204static boolean bug(String format, Object... args) {205return reporter.bug(format, args);206}207208static boolean hasBom(byte[] a) {209switch (a.length) {210case 2: case 4:211int sum = 0;212for (byte x : a)213sum += x;214return sum == (byte) 0xfe + (byte) 0xff;215default: return false;216}217}218219void testSurrogates() {220int failures = 0;221for (int i = 0; i < 10; i++) {222Result r = test(new char[] { randomHighSurrogate() });223if (r == null) break;224if (! (r.cr.isUnderflow() &&225r.ipos == 0))226bug("Lone high surrogate not UNDERFLOW: %s %s",227cs, r);228}229for (int i = 0; i < 10; i++) {230Result r = test(new char[] { randomLowSurrogate() });231if (r == null) break;232if (! (r.cr.isMalformed() && r.cr.length() == 1))233bug("Lone low surrogate not MALFORMED[1]: %s %s",234cs, r);235}236char[] chars = new char[2];237for (int i = 0; i < 10; i++) {238chars[0] = randomLowSurrogate(); // Always illegal239chars[1] = randomChar();240Result r = test(chars);241if (r == null) break;242if (! (r.cr.isMalformed() &&243r.cr.length() == 1 &&244(r.ipos == 0 || (hasBom && hasBom(r.oa))))) {245if (failures++ > 5) return;246bug("Unpaired low surrogate not MALFORMED[1]: %s %s",247cs, r);248}249}250for (int i = 0; i < 10; i++) {251chars[0] = randomHighSurrogate();252do {253chars[1] = randomChar();254} while (Character.isLowSurrogate(chars[1]));255Result r = test(chars);256if (r == null) break;257if (! (r.cr.isMalformed() &&258r.cr.length() == 1 &&259(r.ipos == 0 || (hasBom && hasBom(r.oa))))) {260if (failures++ > 5) return;261bug("Unpaired high surrogate not MALFORMED[1]: %s %s",262cs, r);263}264}265for (int i = 0; i < 1000; i++) {266chars[0] = randomHighSurrogate();267chars[1] = randomLowSurrogate();268Result r = test(chars);269if (r == null) break;270if (! ((r.cr.isUnmappable() &&271r.cr.length() == 2 &&272r.oa.length == 0)273||274(r.cr.isUnderflow() &&275r.oa.length > 0 &&276r.ipos == 2))) {277if (failures++ > 5) return;278bug("Legal supplementary character bug: %s %s",279cs, r);280}281}282}283284// if (! (r.cr.isMalformed() &&285// r.cr.length() == 1 &&286// (rob.position() == 0 || hasBom(rob)))) {287// if (failures++ > 5) return;288// bug("Unpaired surrogate not malformed: %s %s",289// cs, r);290// }291// }292293// dib.clear(); dib.put(chars); dib.flip();294// rib.position(0);295// rob.clear(); rob.limit(lim);296// for (CharBuffer ib : new CharBuffer[] { rib, dib }) {297// Result r = recode(ib, rob);298// if (! (r.cr.isMalformed() &&299// r.cr.length() == 1 &&300// (rob.position() == 0 || hasBom(rob)))) {301// if (failures++ > 5) return;302// bug("Unpaired surrogate not malformed: %s %s",303// cs, r);304// }305// }306// //}307// for (int i = 0; i < 10000; i++) {308// chars[0] = randomHighSurrogate();309// chars[1] = randomLowSurrogate();310// dib.clear(); dib.put(chars); dib.flip();311// rib.position(0);312// rob.clear(); rob.limit(lim);313// for (CharBuffer ib : new CharBuffer[] { rib, dib }) {314// Result r = recode(ib, rob);315// if (! ((r.cr.isUnmappable() &&316// r.cr.length() == 2 &&317// rob.position() == 0)318// ||319// (r.cr.isUnderflow() &&320// rob.position() > 0 &&321// ib.position() == 2))) {322// if (failures++ > 5) return;323// bug("Legal supplementary character bug: %s %s",324// cs, r);325// }326// }327// }328// }329// }330331Result recode(CharBuffer ib, ByteBuffer ob) {332try {333byte canary = 22;334ib.clear(); // Prepare to read335ob.clear(); // Prepare to write336for (int i = 0; i < ob.limit(); i++)337ob.put(i, canary);338CharsetEncoder coder = cs.newEncoder();339CoderResult cr = coder.encode(ib, ob, false);340equal(ib.limit(), ib.capacity());341equal(ob.limit(), ob.capacity());342Result r = new Result(ib, ob, cr);343if (cr.isError())344check(cr.length() > 0);345if (cr.isOverflow() && ob.remaining() > 10)346bug("OVERFLOW, but there's lots of room: %s %s",347cs, r);348// if (cr.isOverflow() && ib.remaining() == 0 && ! hasBom)349// bug("OVERFLOW, yet remaining() == 0: %s %s",350// cs, r);351if (cr.isError() && ib.remaining() < cr.length())352bug("remaining() < CoderResult.length(): %s %s",353cs, r);354// if (ib.position() == 0355// && ob.position() > 0356// && ! hasBom(r.oa))357// bug("output only if input consumed: %s %s",358// cs, r);359CoderResult cr2 = coder.encode(ib, ob, false);360if (ib.position() != r.ipos ||361ob.position() != r.oa.length ||362cr != cr2)363bug("Coding operation not idempotent: %s%n %s%n %s",364cs, r, new Result(ib, ob, cr2));365if (ob.position() < ob.limit() &&366ob.get(ob.position()) != canary)367bug("Buffer overrun: %s %s %s",368cs, r, ob.get(ob.position()));369return r;370} catch (Throwable t) {371if (bug("Unexpected exception: %s %s %s",372cs, t.getClass().getSimpleName(),373new Result(ib, ob, null)))374t.printStackTrace();375return null;376}377}378379Result recode2(char[] ia, int n) {380int len = ia.length;381CharBuffer rib = CharBuffer.wrap(ia);382CharBuffer dib = dInBuffers[len];383dib.clear(); dib.put(ia); dib.clear();384ByteBuffer rob = rOuBuffers[n];385ByteBuffer dob = dOuBuffers[n];386equal(rob.limit(), n);387equal(dob.limit(), n);388check(dib.isDirect());389check(dob.isDirect());390Result r1 = recode(rib, rob);391Result r2 = recode(dib, dob);392if (r1 != null && r2 != null && ! Result.eq(r1, r2))393bug("Results differ for direct buffers: %s%n %s%n %s",394cs, r1, r2);395return r1;396}397398Result test(char[] ia) {399if (failed - failed0 >= maxCharsetFailures)400throw new TooManyFailures();401402Result roomy = recode2(ia, maxBufSize - 1);403if (roomy == null) return roomy;404int olen = roomy.oa.length;405if (olen > 0) {406if (roomy.ipos == roomy.ia.length) {407Result perfectFit = recode2(ia, olen);408if (! Result.eq(roomy, perfectFit))409bug("Results differ: %s%n %s%n %s",410cs, roomy, perfectFit);411}412for (int i = 0; i < olen; i++) {413Result claustrophobic = recode2(ia, i);414if (claustrophobic == null) return roomy;415if (roomy.cr.isUnderflow() &&416! claustrophobic.cr.isOverflow())417bug("Expected OVERFLOW: %s%n %s%n %s",418cs, roomy, claustrophobic);419}420}421return roomy;422}423424void testExhaustively(char[] prefix, int n) {425int len = prefix.length;426char[] ia = Arrays.copyOf(prefix, len + 1);427for (int i = 0; i < 0x10000; i++) {428ia[len] = (char) i;429if (n == 1)430test(ia);431else432testExhaustively(ia, n - 1);433}434}435436void testRandomly(char[] prefix, int n) {437int len = prefix.length;438char[] ia = Arrays.copyOf(prefix, len + n);439for (int i = 0; i < 10000; i++) {440for (int j = 0; j < n; j++)441ia[len + j] = randomChar();442test(ia);443}444}445446void testISO88591InvalidChar() {447// Several architectures implement the ISO-8859-1 encoder as an448// intrinsic where the vectorised assembly has separate cases449// for different input sizes, so exhaustively test all sizes450// from 0 to maxBufSize to ensure we get coverage451452for (int i = 0; i < CharsetTester.maxBufSize; i++) {453char[] ia = new char[i];454for (int j = 0; j < i; j++)455ia[j] = randomChar();456457test(ia);458459// Test break on unrepresentable character460for (int j = 0; j < i; j++) {461char[] iaInvalid = ia.clone();462iaInvalid[j] = (char)(randomChar() | 0x100);463test(iaInvalid);464}465}466}467468void testPrefix(char[] prefix) {469if (prefix.length > 0)470System.out.printf("Testing prefix %s%n", string(prefix));471472test(prefix);473474testExhaustively(prefix, 1);475// Can you spare a year of CPU time?476//testExhaustively(prefix, 2);477478testRandomly(prefix, 2);479testRandomly(prefix, 3);480}481}482483private final static Random rnd = RandomFactory.getRandom();484private static char randomChar() {485return (char) rnd.nextInt(Character.MAX_VALUE);486}487private static char randomHighSurrogate() {488return (char) (Character.MIN_HIGH_SURROGATE + rnd.nextInt(1024));489}490private static char randomLowSurrogate() {491return (char) (Character.MIN_LOW_SURROGATE + rnd.nextInt(1024));492}493494private static void testCharset(Charset cs) throws Throwable {495if (! cs.canEncode())496return;497498final String csn = cs.name();499500if (isBroken(csn)) {501System.out.printf("Skipping possibly broken charset %s%n", csn);502return;503}504System.out.println(csn);505506CharsetTester tester = new CharsetTester(cs);507508tester.testSurrogates();509510tester.testPrefix(new char[] {});511512if (csn.equals("x-ISCII91")) {513System.out.println("More ISCII testing...");514new CharsetTester(cs).testPrefix(new char[]{'\u094d'}); // Halant515new CharsetTester(cs).testPrefix(new char[]{'\u093c'}); // Nukta516} else if (csn.equals("ISO-8859-1")) {517System.out.println("More ISO-8859-1 testing...");518new CharsetTester(cs).testISO88591InvalidChar();519}520}521522private static void realMain(String[] args) {523for (Charset cs : sort(Charset.availableCharsets().values())) {524try {525testCharset(cs);526} catch (TooManyFailures e) {527System.out.printf("Too many failures for %s%n", cs);528} catch (Throwable t) {529unexpected(t);530}531}532reporter.summarize();533}534535//--------------------- Infrastructure ---------------------------536static volatile long passed = 0, failed = 0;537static void pass() {passed++;}538static void fail() {failed++; Thread.dumpStack();}539static void fail(String format, Object... args) {540System.out.println(String.format(format, args)); failed++;}541static void fail(String msg) {System.out.println(msg); fail();}542static void unexpected(Throwable t) {failed++; t.printStackTrace();}543static void check(boolean cond) {if (cond) pass(); else fail();}544static void equal(Object x, Object y) {545if (x == null ? y == null : x.equals(y)) pass();546else fail(x + " not equal to " + y);}547public static void main(String[] args) throws Throwable {548try {realMain(args);} catch (Throwable t) {unexpected(t);}549System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);550if (failed > 0) throw new AssertionError("Some tests failed");}551}552553554