Home | History | Annotate | Download | only in lang
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package libcore.java.lang;
     18 
     19 import java.nio.ByteBuffer;
     20 import java.nio.CharBuffer;
     21 import java.nio.ReadOnlyBufferException;
     22 import java.nio.charset.Charset;
     23 import java.nio.charset.CharsetDecoder;
     24 import java.nio.charset.CharsetEncoder;
     25 import java.nio.charset.CoderResult;
     26 import java.nio.charset.CodingErrorAction;
     27 import java.util.Arrays;
     28 import java.util.Locale;
     29 import junit.framework.TestCase;
     30 
     31 public class StringTest extends TestCase {
     32     public void testIsEmpty() {
     33         assertTrue("".isEmpty());
     34         assertFalse("x".isEmpty());
     35     }
     36 
     37     // The evil decoder keeps hold of the CharBuffer it wrote to.
     38     private static final class EvilCharsetDecoder extends CharsetDecoder {
     39         private static char[] chars;
     40         public EvilCharsetDecoder(Charset cs) {
     41             super(cs, 1.0f, 1.0f);
     42         }
     43         protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
     44             chars = out.array();
     45             int inLength = in.remaining();
     46             for (int i = 0; i < inLength; ++i) {
     47                 in.put((byte) 'X');
     48                 out.put('Y');
     49             }
     50             return CoderResult.UNDERFLOW;
     51         }
     52         public static void corrupt() {
     53             for (int i = 0; i < chars.length; ++i) {
     54                 chars[i] = '$';
     55             }
     56         }
     57     }
     58 
     59     // The evil encoder tries to write to the CharBuffer it was given to
     60     // read from.
     61     private static final class EvilCharsetEncoder extends CharsetEncoder {
     62         public EvilCharsetEncoder(Charset cs) {
     63             super(cs, 1.0f, 1.0f);
     64         }
     65         protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
     66             int inLength = in.remaining();
     67             for (int i = 0; i < inLength; ++i) {
     68                 in.put('x');
     69                 out.put((byte) 'y');
     70             }
     71             return CoderResult.UNDERFLOW;
     72         }
     73     }
     74 
     75     private static final Charset EVIL_CHARSET = new Charset("evil", null) {
     76         public boolean contains(Charset charset) { return false; }
     77         public CharsetEncoder newEncoder() { return new EvilCharsetEncoder(this); }
     78         public CharsetDecoder newDecoder() { return new EvilCharsetDecoder(this); }
     79     };
     80 
     81     public void testGetBytes_MaliciousCharset() {
     82         try {
     83             String s = "hi";
     84             // Check that our encoder can't write to the input CharBuffer
     85             // it was given.
     86             s.getBytes(EVIL_CHARSET);
     87             fail(); // We shouldn't have got here!
     88         } catch (ReadOnlyBufferException expected) {
     89             // We caught you trying to be naughty!
     90         }
     91     }
     92 
     93     public void testString_BII() throws Exception {
     94         byte[] bytes = "xa\u0666bx".getBytes("UTF-8");
     95         assertEquals("a\u0666b", new String(bytes, 1, bytes.length - 2));
     96     }
     97 
     98     public void testString_BIIString() throws Exception {
     99         byte[] bytes = "xa\u0666bx".getBytes("UTF-8");
    100         assertEquals("a\u0666b", new String(bytes, 1, bytes.length - 2, "UTF-8"));
    101     }
    102 
    103     public void testString_BIICharset() throws Exception {
    104         byte[] bytes = "xa\u0666bx".getBytes("UTF-8");
    105         assertEquals("a\u0666b", new String(bytes, 1, bytes.length - 2, Charset.forName("UTF-8")));
    106     }
    107 
    108     public void testString_BCharset() throws Exception {
    109         byte[] bytes = "a\u0666b".getBytes("UTF-8");
    110         assertEquals("a\u0666b", new String(bytes, Charset.forName("UTF-8")));
    111     }
    112 
    113     public void testStringFromCharset_MaliciousCharset() {
    114         Charset cs = EVIL_CHARSET;
    115         byte[] bytes = new byte[] {(byte) 'h', (byte) 'i'};
    116         final String result = new String(bytes, cs);
    117         assertEquals("YY", result); // (Our decoder always outputs 'Y's.)
    118         // Check that even if the decoder messes with the output CharBuffer
    119         // after we've created a string from it, it doesn't affect the string.
    120         EvilCharsetDecoder.corrupt();
    121         assertEquals("YY", result);
    122     }
    123 
    124     public void test_getBytes_bad() throws Exception {
    125         // Check that we use '?' as the replacement byte for invalid characters.
    126         assertEquals("[97, 63, 98]", Arrays.toString("a\u0666b".getBytes("US-ASCII")));
    127         assertEquals("[97, 63, 98]", Arrays.toString("a\u0666b".getBytes(Charset.forName("US-ASCII"))));
    128     }
    129 
    130     public void test_getBytes_UTF_8() {
    131         // We have a fast path implementation of String.getBytes for UTF-8.
    132         Charset cs = Charset.forName("UTF-8");
    133 
    134         // Test the empty string.
    135         assertEquals("[]", Arrays.toString("".getBytes(cs)));
    136 
    137         // Test one-byte characters.
    138         assertEquals("[0]", Arrays.toString("\u0000".getBytes(cs)));
    139         assertEquals("[127]", Arrays.toString("\u007f".getBytes(cs)));
    140         assertEquals("[104, 105]", Arrays.toString("hi".getBytes(cs)));
    141 
    142         // Test two-byte characters.
    143         assertEquals("[-62, -128]", Arrays.toString("\u0080".getBytes(cs)));
    144         assertEquals("[-39, -90]", Arrays.toString("\u0666".getBytes(cs)));
    145         assertEquals("[-33, -65]", Arrays.toString("\u07ff".getBytes(cs)));
    146         assertEquals("[104, -39, -90, 105]", Arrays.toString("h\u0666i".getBytes(cs)));
    147 
    148         // Test three-byte characters.
    149         assertEquals("[-32, -96, -128]", Arrays.toString("\u0800".getBytes(cs)));
    150         assertEquals("[-31, -120, -76]", Arrays.toString("\u1234".getBytes(cs)));
    151         assertEquals("[-17, -65, -65]", Arrays.toString("\uffff".getBytes(cs)));
    152         assertEquals("[104, -31, -120, -76, 105]", Arrays.toString("h\u1234i".getBytes(cs)));
    153 
    154         // Test supplementary characters.
    155         // Minimum supplementary character: U+10000
    156         assertEquals("[-16, -112, -128, -128]", Arrays.toString("\ud800\udc00".getBytes(cs)));
    157         // Random supplementary character: U+10381 Ugaritic letter beta
    158         assertEquals("[-16, -112, -114, -127]", Arrays.toString("\ud800\udf81".getBytes(cs)));
    159         // Maximum supplementary character: U+10FFFF
    160         assertEquals("[-12, -113, -65, -65]", Arrays.toString("\udbff\udfff".getBytes(cs)));
    161         // A high surrogate at end of string is an error replaced with '?'.
    162         assertEquals("[104, 63]", Arrays.toString("h\ud800".getBytes(cs)));
    163         // A high surrogate not followed by a low surrogate is an error replaced with '?'.
    164         assertEquals("[104, 63, 105]", Arrays.toString("h\ud800i".getBytes(cs)));
    165     }
    166 
    167     public void test_new_String_bad() throws Exception {
    168         // Check that we use U+FFFD as the replacement string for invalid bytes.
    169         assertEquals("a\ufffdb", new String(new byte[] { 97, -2, 98 }, "US-ASCII"));
    170         assertEquals("a\ufffdb", new String(new byte[] { 97, -2, 98 }, Charset.forName("US-ASCII")));
    171     }
    172 
    173     /**
    174 
    175      * Test that strings interned manually and then later loaded as literals
    176      * maintain reference equality. http://b/3098960
    177      */
    178     public void testInternBeforeLiteralIsLoaded() throws Exception{
    179         String programmatic = Arrays.asList("5058", "9962", "1563", "5744").toString().intern();
    180         String literal = (String) Class.forName("libcore.java.lang.StringTest$HasLiteral")
    181                 .getDeclaredField("literal").get(null);
    182         assertEquals(System.identityHashCode(programmatic), System.identityHashCode(literal));
    183         assertSame(programmatic, literal);
    184     }
    185 
    186     static class HasLiteral {
    187         static String literal = "[5058, 9962, 1563, 5744]";
    188     }
    189 
    190     private static final String COMBINING_DOT_ABOVE = "\u0307";
    191     private static final String LATIN_CAPITAL_I = "I";
    192     private static final String LATIN_CAPITAL_I_WITH_DOT_ABOVE = "\u0130";
    193     private static final String LATIN_SMALL_I = "i";
    194     private static final String LATIN_SMALL_DOTLESS_I = "\u0131";
    195 
    196     private static final String[] LATIN_I_VARIANTS = {
    197         LATIN_SMALL_I,
    198         LATIN_SMALL_DOTLESS_I,
    199         LATIN_CAPITAL_I,
    200         LATIN_CAPITAL_I_WITH_DOT_ABOVE,
    201     };
    202 
    203     public void testCaseMapping_tr_TR() {
    204         Locale tr_TR = new Locale("tr", "TR");
    205         assertEquals(LATIN_SMALL_I, LATIN_SMALL_I.toLowerCase(tr_TR));
    206         assertEquals(LATIN_SMALL_I, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toLowerCase(tr_TR));
    207         assertEquals(LATIN_SMALL_DOTLESS_I, LATIN_SMALL_DOTLESS_I.toLowerCase(tr_TR));
    208 
    209         assertEquals(LATIN_CAPITAL_I, LATIN_CAPITAL_I.toUpperCase(tr_TR));
    210         assertEquals(LATIN_CAPITAL_I_WITH_DOT_ABOVE, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toUpperCase(tr_TR));
    211         assertEquals(LATIN_CAPITAL_I_WITH_DOT_ABOVE, LATIN_SMALL_I.toUpperCase(tr_TR));
    212 
    213         assertEquals(LATIN_CAPITAL_I, LATIN_SMALL_DOTLESS_I.toUpperCase(tr_TR));
    214         assertEquals(LATIN_SMALL_DOTLESS_I, LATIN_CAPITAL_I.toLowerCase(tr_TR));
    215     }
    216 
    217     public void testCaseMapping_en_US() {
    218         Locale en_US = new Locale("en", "US");
    219         assertEquals(LATIN_CAPITAL_I, LATIN_SMALL_I.toUpperCase(en_US));
    220         assertEquals(LATIN_CAPITAL_I, LATIN_CAPITAL_I.toUpperCase(en_US));
    221         assertEquals(LATIN_CAPITAL_I_WITH_DOT_ABOVE, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toUpperCase(en_US));
    222 
    223         assertEquals(LATIN_SMALL_I, LATIN_SMALL_I.toLowerCase(en_US));
    224         assertEquals(LATIN_SMALL_I, LATIN_CAPITAL_I.toLowerCase(en_US));
    225         assertEquals(LATIN_SMALL_DOTLESS_I, LATIN_SMALL_DOTLESS_I.toLowerCase(en_US));
    226 
    227         assertEquals(LATIN_CAPITAL_I, LATIN_SMALL_DOTLESS_I.toUpperCase(en_US));
    228         // http://b/3325799: the RI fails this because it's using an obsolete version of the Unicode rules.
    229         // Android correctly preserves canonical equivalence. (See the separate test for tr_TR.)
    230         assertEquals(LATIN_SMALL_I + COMBINING_DOT_ABOVE, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toLowerCase(en_US));
    231     }
    232 
    233     public void testCaseMapping_el() {
    234         Locale el_GR = new Locale("el", "GR");
    235         assertEquals("    O   ", "    o   ".toUpperCase(el_GR));
    236         assertEquals("    O   ", "    o   ".toUpperCase(el_GR));
    237         assertEquals("    O   ", "    o   ".toUpperCase(el_GR));
    238 
    239         Locale en_US = new Locale("en", "US");
    240         assertEquals("    O   ", "    o   ".toUpperCase(en_US));
    241     }
    242 
    243     public void testEqualsIgnoreCase_tr_TR() {
    244         testEqualsIgnoreCase(new Locale("tr", "TR"));
    245     }
    246 
    247     public void testEqualsIgnoreCase_en_US() {
    248         testEqualsIgnoreCase(new Locale("en", "US"));
    249     }
    250 
    251     /**
    252      * String.equalsIgnoreCase should not depend on the locale.
    253      */
    254     private void testEqualsIgnoreCase(Locale locale) {
    255         Locale defaultLocale = Locale.getDefault();
    256         Locale.setDefault(locale);
    257         try {
    258             for (String a : LATIN_I_VARIANTS) {
    259                 for (String b : LATIN_I_VARIANTS) {
    260                     if (!a.equalsIgnoreCase(b)) {
    261                         fail("Expected " + a + " to equal " + b + " in " +  locale);
    262                     }
    263                 }
    264             }
    265         } finally {
    266             Locale.setDefault(defaultLocale);
    267         }
    268     }
    269 
    270     public void testRegionMatches_ignoreCase_en_US() {
    271         testRegionMatches_ignoreCase(new Locale("en", "US"));
    272     }
    273 
    274     public void testRegionMatches_ignoreCase_tr_TR() {
    275         testRegionMatches_ignoreCase(new Locale("tr", "TR"));
    276     }
    277 
    278     private void testRegionMatches_ignoreCase(Locale locale) {
    279         Locale defaultLocale = Locale.getDefault();
    280         Locale.setDefault(locale);
    281         try {
    282             for (String a : LATIN_I_VARIANTS) {
    283                 for (String b : LATIN_I_VARIANTS) {
    284                     if (!a.regionMatches(true, 0, b, 0, b.length())) {
    285                         fail("Expected " + a + " to equal " + b + " in " +  locale);
    286                     }
    287                 }
    288             }
    289         } finally {
    290             Locale.setDefault(defaultLocale);
    291         }
    292     }
    293 
    294     // http://code.google.com/p/android/issues/detail?id=15266
    295     public void test_replaceAll() throws Exception {
    296         assertEquals("project_Id", "projectId".replaceAll("(?!^)(\\p{Upper})(?!$)", "_$1"));
    297     }
    298 
    299     // https://code.google.com/p/android/issues/detail?id=23831
    300     public void test_23831() throws Exception {
    301         byte[] bytes = { (byte) 0xf5, (byte) 0xa9, (byte) 0xea, (byte) 0x21 };
    302         String expected = "\ufffd\ufffd\u0021";
    303 
    304         // Since we use icu4c for CharsetDecoder...
    305         CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
    306         decoder.onMalformedInput(CodingErrorAction.REPLACE);
    307         assertEquals(expected, decoder.decode(ByteBuffer.wrap(bytes)).toString());
    308 
    309         // Our fast-path code in String should behave the same...
    310         assertEquals(expected, new String(bytes, "UTF-8"));
    311     }
    312 
    313     // https://code.google.com/p/android/issues/detail?id=55129
    314     public void test_55129() throws Exception {
    315         assertEquals("-h-e-l-l-o- -w-o-r-l-d-", "hello world".replace("", "-"));
    316         assertEquals("-w-o-r-l-d-", "hello world".substring(6).replace("", "-"));
    317         assertEquals("-*-w-*-o-*-r-*-l-*-d-*-", "hello world".substring(6).replace("", "-*-"));
    318 
    319         // Replace on an empty string with an empty target should insert the pattern
    320         // precisely once.
    321         assertEquals("", "".replace("", ""));
    322         assertEquals("food", "".replace("", "food"));
    323     }
    324 
    325     public void test_replace() {
    326         // Replace on an empty string is a no-op.
    327         assertEquals("", "".replace("foo", "bar"));
    328         // Replace on a string which doesn't contain the target sequence is a no-op.
    329         assertEquals("baz", "baz".replace("foo", "bar"));
    330         // Test that we iterate forward on the string.
    331         assertEquals("mmmba", "bababa".replace("baba", "mmm"));
    332         // Test replacements at the end of the string.
    333         assertEquals("foodie", "foolish".replace("lish", "die"));
    334         // Test a string that has multiple replacements.
    335         assertEquals("hahahaha", "kkkk".replace("k", "ha"));
    336     }
    337 
    338     public void test_String_getBytes() throws Exception {
    339         // http://b/11571917
    340         assertEquals("[-126, -96]", Arrays.toString("".getBytes("Shift_JIS")));
    341         assertEquals("[-126, -87]", Arrays.toString("".getBytes("Shift_JIS")));
    342         assertEquals("[-105, 67]", Arrays.toString("".getBytes("Shift_JIS")));
    343         assertEquals("[36]", Arrays.toString("$".getBytes("Shift_JIS")));
    344         assertEquals("[-29, -127, -117]", Arrays.toString("".getBytes("UTF-8")));
    345 
    346         // http://b/11639117
    347         assertEquals("[-79, -72, -70, -48]", Arrays.toString("".getBytes("EUC-KR")));
    348 
    349 
    350         // https://code.google.com/p/android/issues/detail?id=63188
    351         assertEquals("[-77, -10, -64, -76, -63, -53]", Arrays.toString("".getBytes("gbk")));
    352         assertEquals("[-77, -10, -64, -76]", Arrays.toString("".getBytes("gbk")));
    353         assertEquals("[-77, -10]", Arrays.toString("".getBytes("gbk")));
    354     }
    355 
    356     public void test_compareTo() throws Exception {
    357         // For strings where a character differs, the result is
    358         // the difference between the characters.
    359         assertEquals(-1, "a".compareTo("b"));
    360         assertEquals(-2, "a".compareTo("c"));
    361         assertEquals(1, "b".compareTo("a"));
    362         assertEquals(2, "c".compareTo("a"));
    363 
    364         // For strings where the characters match up to the length of the shorter,
    365         // the result is the difference between the strings' lengths.
    366         assertEquals(0, "a".compareTo("a"));
    367         assertEquals(-1, "a".compareTo("aa"));
    368         assertEquals(-1, "a".compareTo("az"));
    369         assertEquals(-2, "a".compareTo("aaa"));
    370         assertEquals(-2, "a".compareTo("azz"));
    371         assertEquals(-3, "a".compareTo("aaaa"));
    372         assertEquals(-3, "a".compareTo("azzz"));
    373         assertEquals(0, "a".compareTo("a"));
    374         assertEquals(1, "aa".compareTo("a"));
    375         assertEquals(1, "az".compareTo("a"));
    376         assertEquals(2, "aaa".compareTo("a"));
    377         assertEquals(2, "azz".compareTo("a"));
    378         assertEquals(3, "aaaa".compareTo("a"));
    379         assertEquals(3, "azzz".compareTo("a"));
    380     }
    381 
    382     public void test_compareToIgnoreCase() throws Exception {
    383         // For strings where a character differs, the result is
    384         // the difference between the characters.
    385         assertEquals(-1, "a".compareToIgnoreCase("b"));
    386         assertEquals(-1, "a".compareToIgnoreCase("B"));
    387         assertEquals(-2, "a".compareToIgnoreCase("c"));
    388         assertEquals(-2, "a".compareToIgnoreCase("C"));
    389         assertEquals(1, "b".compareToIgnoreCase("a"));
    390         assertEquals(1, "B".compareToIgnoreCase("a"));
    391         assertEquals(2, "c".compareToIgnoreCase("a"));
    392         assertEquals(2, "C".compareToIgnoreCase("a"));
    393 
    394         // For strings where the characters match up to the length of the shorter,
    395         // the result is the difference between the strings' lengths.
    396         assertEquals(0, "a".compareToIgnoreCase("a"));
    397         assertEquals(0, "a".compareToIgnoreCase("A"));
    398         assertEquals(0, "A".compareToIgnoreCase("a"));
    399         assertEquals(0, "A".compareToIgnoreCase("A"));
    400         assertEquals(-1, "a".compareToIgnoreCase("aa"));
    401         assertEquals(-1, "a".compareToIgnoreCase("aA"));
    402         assertEquals(-1, "a".compareToIgnoreCase("Aa"));
    403         assertEquals(-1, "a".compareToIgnoreCase("az"));
    404         assertEquals(-1, "a".compareToIgnoreCase("aZ"));
    405         assertEquals(-2, "a".compareToIgnoreCase("aaa"));
    406         assertEquals(-2, "a".compareToIgnoreCase("AAA"));
    407         assertEquals(-2, "a".compareToIgnoreCase("azz"));
    408         assertEquals(-2, "a".compareToIgnoreCase("AZZ"));
    409         assertEquals(-3, "a".compareToIgnoreCase("aaaa"));
    410         assertEquals(-3, "a".compareToIgnoreCase("AAAA"));
    411         assertEquals(-3, "a".compareToIgnoreCase("azzz"));
    412         assertEquals(-3, "a".compareToIgnoreCase("AZZZ"));
    413         assertEquals(1, "aa".compareToIgnoreCase("a"));
    414         assertEquals(1, "aA".compareToIgnoreCase("a"));
    415         assertEquals(1, "Aa".compareToIgnoreCase("a"));
    416         assertEquals(1, "az".compareToIgnoreCase("a"));
    417         assertEquals(2, "aaa".compareToIgnoreCase("a"));
    418         assertEquals(2, "azz".compareToIgnoreCase("a"));
    419         assertEquals(3, "aaaa".compareToIgnoreCase("a"));
    420         assertEquals(3, "azzz".compareToIgnoreCase("a"));
    421     }
    422 
    423     // http://b/25943996
    424     public void testSplit_trailingSeparators() {
    425         String[] splits = "test\0message\0\0\0\0\0\0".split("\0", -1);
    426         assertEquals("test", splits[0]);
    427         assertEquals("message", splits[1]);
    428         assertEquals("", splits[2]);
    429         assertEquals("", splits[3]);
    430         assertEquals("", splits[4]);
    431         assertEquals("", splits[5]);
    432         assertEquals("", splits[6]);
    433         assertEquals("", splits[7]);
    434     }
    435 
    436     // http://b/26126818
    437     public void testCodePointCount() {
    438         String hello = "Hello, fools";
    439 
    440         assertEquals(5, hello.codePointCount(0, 5));
    441         assertEquals(7, hello.codePointCount(5, 12));
    442         assertEquals(2, hello.codePointCount(10, 12));
    443     }
    444 
    445     // http://b/26444984
    446     public void testGetCharsOverflow() {
    447         int srcBegin = Integer.MAX_VALUE; //2147483647
    448         int srcEnd = srcBegin + 10;  //-2147483639
    449         try {
    450             // The output array size must be larger than |srcEnd - srcBegin|.
    451             "yes".getChars(srcBegin, srcEnd, new char[256], 0);
    452             fail();
    453         } catch (StringIndexOutOfBoundsException expected) {
    454         }
    455     }
    456 
    457     // http://b/28998511
    458     public void testGetCharsBoundsChecks() {
    459         // This is the explicit case from the bug: dstBegin == srcEnd - srcBegin
    460         assertGetCharsThrowsAIOOBException("abcd", 0, 4, new char[0], -4);
    461 
    462         // Some valid cases.
    463         char[] dst = new char[1];
    464         "abcd".getChars(0, 1, dst, 0);
    465         assertEquals('a', dst[0]);
    466         "abcd".getChars(3, 4, dst, 0);
    467         assertEquals('d', dst[0]);
    468         dst = new char[4];
    469         "abcd".getChars(0, 4, dst, 0);
    470         assertTrue(Arrays.equals("abcd".toCharArray(), dst));
    471 
    472         // Zero length src.
    473         "abcd".getChars(0, 0, new char[0], 0);  // dstBegin == 0 is ok if copying zero chars
    474         "abcd".getChars(0, 0, new char[1], 1);  // dstBegin == 1 is ok if copying zero chars
    475         "".getChars(0, 0, new char[0], 0);
    476         "abcd".getChars(1, 1, new char[1], 0);
    477         "abcd".getChars(1, 1, new char[1], 1);
    478 
    479         // Valid src args, invalid dst args.
    480         assertGetCharsThrowsAIOOBException("abcd", 3, 4, new char[1], 1); // Out of range dstBegin
    481         assertGetCharsThrowsAIOOBException("abcd", 0, 4, new char[3], 0); // Small dst
    482         assertGetCharsThrowsAIOOBException("abcd", 0, 4, new char[4], -1); // Negative dstBegin
    483 
    484         // dstBegin + (srcEnd - srcBegin) -> integer overflow OR dstBegin >= dst.length
    485         assertGetCharsThrowsAIOOBException("abcd", 0, 4, new char[4], Integer.MAX_VALUE - 1);
    486 
    487         // Invalid src args, valid dst args.
    488         assertGetCharsThrowsSIOOBException("abcd", 2, 1, new char[4], 0); // srcBegin > srcEnd
    489         assertGetCharsThrowsSIOOBException("abcd", -1, 3, new char[4], 0); // Negative srcBegin
    490         assertGetCharsThrowsSIOOBException("abcd", 0, 5, new char[4], 0); // Out of range srcEnd
    491         assertGetCharsThrowsSIOOBException("abcd", 0, -1, new char[4], 0); // Negative srcEnd
    492 
    493         // Valid src args, invalid dst args.
    494         assertGetCharsThrowsAIOOBException("abcd", 0, 4, new char[4], 1); // Bad dstBegin
    495 
    496         // Zero length src copy, invalid dst args.
    497         assertGetCharsThrowsAIOOBException("abcd", 0, 0, new char[4], -1); // Negative dstBegin
    498         assertGetCharsThrowsAIOOBException("abcd", 0, 0, new char[0], 1); // Out of range dstBegin
    499         assertGetCharsThrowsAIOOBException("abcd", 0, 0, new char[1], 2);  // Out of range dstBegin
    500         assertGetCharsThrowsAIOOBException("abcd", 0, 0, new char[4], 5); // Out of range dstBegin
    501     }
    502 
    503     private static void assertGetCharsThrowsAIOOBException(String s, int srcBegin, int srcEnd,
    504             char[] dst, int dstBegin) {
    505         try {
    506             s.getChars(srcBegin, srcEnd, dst, dstBegin);
    507             fail();
    508         } catch (ArrayIndexOutOfBoundsException expected) {
    509         }
    510     }
    511 
    512     private static void assertGetCharsThrowsSIOOBException(String s, int srcBegin, int srcEnd,
    513             char[] dst, int dstBegin) {
    514         try {
    515             s.getChars(srcBegin, srcEnd, dst, dstBegin);
    516             fail();
    517         } catch (StringIndexOutOfBoundsException expected) {
    518         }
    519     }
    520 
    521     public void testChars() {
    522         String s = "Hello\n\tworld";
    523         int[] expected = new int[s.length()];
    524         for (int i = 0; i < s.length(); ++i) {
    525             expected[i] = (int) s.charAt(i);
    526         }
    527         assertTrue(Arrays.equals(expected, s.chars().toArray()));
    528 
    529         // Surrogate code point
    530         char high = '\uD83D', low = '\uDE02';
    531         String surrogateCP = new String(new char[]{high, low, low});
    532         assertTrue(Arrays.equals(new int[]{high, low, low}, surrogateCP.chars().toArray()));
    533     }
    534 
    535     public void testCodePoints() {
    536         String s = "Hello\n\tworld";
    537         int[] expected = new int[s.length()];
    538         for (int i = 0; i < s.length(); ++i) {
    539             expected[i] = (int) s.charAt(i);
    540         }
    541         assertTrue(Arrays.equals(expected, s.codePoints().toArray()));
    542 
    543         // Surrogate code point
    544         char high = '\uD83D', low = '\uDE02';
    545         String surrogateCP = new String(new char[]{high, low, low, '0'});
    546         assertEquals(Character.toCodePoint(high, low), surrogateCP.codePoints().toArray()[0]);
    547         assertEquals((int) low, surrogateCP.codePoints().toArray()[1]); // Unmatched surrogate.
    548         assertEquals((int) '0', surrogateCP.codePoints().toArray()[2]);
    549     }
    550 }
    551