am 6dc0ed72: Merge "Add a unit test to demonstrate ICU CharsetProvider problems"

* commit '6dc0ed7247a7520bf14a89db9b4bc3015d3cdcb2':
  Add a unit test to demonstrate ICU CharsetProvider problems
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index 0526b64..d85df07 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1494,5 +1494,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.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 @@
 
   // 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<Charset> 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<Charset> 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