From 1895955344b23f4d8a01596906a62f521c5463ef Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Mon, 12 Jan 2015 17:15:45 +0000 Subject: Add a unit test to demonstrate ICU CharsetProvider problems Also add a single FakeCharsetProvider for unit tests that does nothing by default, so that we can stub it in for tests that do care about it and none of our other tests are affected. Change-Id: I03abb8f1aff53c160935b4cdbeaef764d30f240a --- expectations/knownfailures.txt | 7 ++ .../tests/java/nio/charset/CharsetTest.java | 110 ++++++++++++++------- .../services/java.nio.charset.spi.CharsetProvider | 2 - .../java/nio/charset/SettableCharsetProvider.java | 57 +++++++++++ .../services/java.nio.charset.spi.CharsetProvider | 1 + 5 files changed, 141 insertions(+), 36 deletions(-) delete mode 100644 harmony-tests/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider create mode 100644 luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java create mode 100644 luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt index cf93bab..0da87d6 100644 --- a/expectations/knownfailures.txt +++ b/expectations/knownfailures.txt @@ -1529,5 +1529,12 @@ names: [ "org.apache.harmony.tests.java.lang.MathTest#test_cbrt_D" ] +}, +{ + description: "Recursive calls to Charset.forName from within providers will overflow the stack.", + result: EXEC_FAILED, + names: [ + "org.apache.harmony.tests.java.nio.charset.CharsetTest#testForName_withProviderWithRecursiveCall" + ] } ] diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetTest.java index 01cf40e..2cf278d 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetTest.java @@ -25,15 +25,14 @@ import java.nio.charset.CoderResult; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.spi.CharsetProvider; import java.nio.charset.UnsupportedCharsetException; -import java.security.Permission; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Locale; import java.util.Set; -import java.util.SortedMap; import java.util.Vector; +import libcore.java.nio.charset.SettableCharsetProvider; import junit.framework.TestCase; @@ -819,51 +818,94 @@ public class CharsetTest extends TestCase { // Test the method isSupported(String) with charset supported by multiple providers. public void testIsSupported_And_ForName_NormalProvider() throws Exception { - assertTrue(Charset.isSupported("mockCharset10")); - // ignore case problem in mock, intended - assertTrue(Charset.isSupported("MockCharset11")); - assertTrue(Charset.isSupported("MockCharset12")); - assertTrue(Charset.isSupported("MOCKCharset10")); - // intended case problem in mock - assertTrue(Charset.isSupported("MOCKCharset11")); - assertTrue(Charset.isSupported("MOCKCharset12")); + SettableCharsetProvider.setDelegate(new MockCharsetProvider()); + try { + assertTrue(Charset.isSupported("mockCharset10")); + // ignore case problem in mock, intended + assertTrue(Charset.isSupported("MockCharset11")); + assertTrue(Charset.isSupported("MockCharset12")); + assertTrue(Charset.isSupported("MOCKCharset10")); + // intended case problem in mock + assertTrue(Charset.isSupported("MOCKCharset11")); + assertTrue(Charset.isSupported("MOCKCharset12")); - assertTrue(Charset.forName("mockCharset10") instanceof MockCharset); - assertTrue(Charset.forName("mockCharset11") instanceof MockCharset); - assertTrue(Charset.forName("mockCharset12") instanceof MockCharset); + assertTrue(Charset.forName("mockCharset10") instanceof MockCharset); + assertTrue(Charset.forName("mockCharset11") instanceof MockCharset); + assertTrue(Charset.forName("mockCharset12") instanceof MockCharset); - assertTrue(Charset.forName("mockCharset10") == charset2); - // intended case problem in mock - Charset.forName("mockCharset11"); - assertTrue(Charset.forName("mockCharset12") == charset2); + assertTrue(Charset.forName("mockCharset10") == charset2); + // intended case problem in mock + Charset.forName("mockCharset11"); + assertTrue(Charset.forName("mockCharset12") == charset2); + } finally { + SettableCharsetProvider.clearDelegate(); + } } // Test the method availableCharsets() with charset supported by multiple providers. public void testAvailableCharsets_NormalProvider() throws Exception { - assertTrue(Charset.availableCharsets().containsKey("mockCharset00")); - assertTrue(Charset.availableCharsets().containsKey("MOCKCharset00")); - assertTrue(Charset.availableCharsets().get("mockCharset00") instanceof MockCharset); - assertTrue(Charset.availableCharsets().get("MOCKCharset00") instanceof MockCharset); - assertFalse(Charset.availableCharsets().containsKey("mockCharset01")); - assertFalse(Charset.availableCharsets().containsKey("mockCharset02")); + SettableCharsetProvider.setDelegate(new MockCharsetProvider()); + try { + assertTrue(Charset.availableCharsets().containsKey("mockCharset00")); + assertTrue(Charset.availableCharsets().containsKey("MOCKCharset00")); + assertTrue(Charset.availableCharsets().get("mockCharset00") instanceof MockCharset); + assertTrue(Charset.availableCharsets().get("MOCKCharset00") instanceof MockCharset); + assertFalse(Charset.availableCharsets().containsKey("mockCharset01")); + assertFalse(Charset.availableCharsets().containsKey("mockCharset02")); - assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2); - assertTrue(Charset.availableCharsets().get("MOCKCharset10") == charset2); - assertFalse(Charset.availableCharsets().containsKey("mockCharset11")); - assertFalse(Charset.availableCharsets().containsKey("mockCharset12")); + assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2); + assertTrue(Charset.availableCharsets().get("MOCKCharset10") == charset2); + assertFalse(Charset.availableCharsets().containsKey("mockCharset11")); + assertFalse(Charset.availableCharsets().containsKey("mockCharset12")); - assertTrue(Charset.availableCharsets().containsKey("mockCharset10")); - assertTrue(Charset.availableCharsets().containsKey("MOCKCharset10")); - assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2); - assertFalse(Charset.availableCharsets().containsKey("mockCharset11")); - assertFalse(Charset.availableCharsets().containsKey("mockCharset12")); + assertTrue(Charset.availableCharsets().containsKey("mockCharset10")); + assertTrue(Charset.availableCharsets().containsKey("MOCKCharset10")); + assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2); + assertFalse(Charset.availableCharsets().containsKey("mockCharset11")); + assertFalse(Charset.availableCharsets().containsKey("mockCharset12")); + } finally { + SettableCharsetProvider.clearDelegate(); + } } // Test the method forName(String) when the charset provider supports a // built-in charset. public void testForName_DuplicateWithBuiltInCharset() throws Exception { - assertFalse(Charset.forName("us-ascii") instanceof MockCharset); - assertFalse(Charset.availableCharsets().get("us-ascii") instanceof MockCharset); + SettableCharsetProvider.setDelegate(new MockCharsetProviderASCII()); + try { + assertFalse(Charset.forName("us-ascii") instanceof MockCharset); + assertFalse(Charset.availableCharsets().get("us-ascii") instanceof MockCharset); + } finally { + SettableCharsetProvider.clearDelegate(); + } + } + + // Fails on Android with a StackOverflowException. + public void testForName_withProviderWithRecursiveCall() throws Exception { + SettableCharsetProvider.setDelegate(new MockCharsetProviderWithRecursiveCall()); + try { + Charset.forName("poop"); + fail(); + } catch (UnsupportedCharsetException expected) { + } finally { + SettableCharsetProvider.clearDelegate(); + } + } + + public static class MockCharsetProviderWithRecursiveCall extends CharsetProvider { + @Override + public Iterator charsets() { + return null; + } + + @Override + public Charset charsetForName(String charsetName) { + if (Charset.isSupported(charsetName)) { + return Charset.forName(charsetName); + } + + return null; + } } public static class MockCharsetProvider extends CharsetProvider { diff --git a/harmony-tests/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider b/harmony-tests/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider deleted file mode 100644 index 9cc124a..0000000 --- a/harmony-tests/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider +++ /dev/null @@ -1,2 +0,0 @@ -org.apache.harmony.tests.java.nio.charset.CharsetTest$MockCharsetProvider -org.apache.harmony.tests.java.nio.charset.CharsetTest$MockCharsetProviderASCII diff --git a/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java b/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java new file mode 100644 index 0000000..b4886d2 --- /dev/null +++ b/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package libcore.java.nio.charset; + +import java.nio.charset.Charset; +import java.nio.charset.spi.CharsetProvider; +import java.util.Collections; +import java.util.Iterator; + +/** + * This class is registered as a charset provider by the META-INF in the libcore + * tests jar. Since there isn't any convenient API to dynamically register and de-register + * charset-providers, this class allows tests to plug in a delegate that lives for the + * duration of the test. + */ +public final class SettableCharsetProvider extends CharsetProvider { + private static CharsetProvider delegate; + + public static void setDelegate(CharsetProvider cp) { + delegate = cp; + } + + public static void clearDelegate() { + delegate = null; + } + + @Override + public Iterator charsets() { + if (delegate != null) { + return delegate.charsets(); + } + + return Collections.emptyIterator(); + } + + @Override + public Charset charsetForName(String charsetName) { + if (delegate != null) { + return delegate.charsetForName(charsetName); + } + + return null; + } +} diff --git a/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider b/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider new file mode 100644 index 0000000..1a53312 --- /dev/null +++ b/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider @@ -0,0 +1 @@ +libcore.java.nio.charset.SettableCharsetProvider -- cgit v1.1