1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  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
17package org.apache.harmony.tests.java.nio.charset;
18
19import java.nio.ByteBuffer;
20import java.nio.CharBuffer;
21import java.nio.charset.Charset;
22import java.nio.charset.CharsetDecoder;
23import java.nio.charset.CharsetEncoder;
24import java.nio.charset.CoderResult;
25import java.nio.charset.IllegalCharsetNameException;
26import java.nio.charset.spi.CharsetProvider;
27import java.nio.charset.UnsupportedCharsetException;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.HashSet;
31import java.util.Iterator;
32import java.util.Locale;
33import java.util.Set;
34import java.util.Vector;
35import libcore.java.nio.charset.SettableCharsetProvider;
36
37import junit.framework.TestCase;
38
39/**
40 * Test class java.nio.Charset.
41 */
42public class CharsetTest extends TestCase {
43
44  public void test_allAvailableCharsets() throws Exception {
45    // Check that we can instantiate every Charset, CharsetDecoder, and CharsetEncoder.
46    for (String charsetName : Charset.availableCharsets().keySet()) {
47      if (charsetName.equals("UTF-32")) {
48        // Our UTF-32 is broken. http://b/2702411
49        // TODO: remove this hack when UTF-32 is fixed.
50        continue;
51      }
52
53      Charset cs = Charset.forName(charsetName);
54      assertNotNull(cs.newDecoder());
55      if (cs.canEncode()) {
56        CharsetEncoder enc = cs.newEncoder();
57        assertNotNull(enc);
58        assertNotNull(enc.replacement());
59      }
60    }
61  }
62
63  public void test_defaultCharset() {
64    assertEquals("UTF-8", Charset.defaultCharset().name());
65  }
66
67  public void test_isRegistered() {
68    // Regression for HARMONY-45
69
70    // Will contain names of charsets registered with IANA
71    Set<String> knownRegisteredCharsets = new HashSet<String>();
72
73    // Will contain names of charsets not known to be registered with IANA
74    Set<String> unknownRegisteredCharsets = new HashSet<String>();
75
76    Set<String> names = Charset.availableCharsets().keySet();
77    for (Iterator nameItr = names.iterator(); nameItr.hasNext();) {
78      String name = (String) nameItr.next();
79      if (name.toLowerCase(Locale.ROOT).startsWith("x-")) {
80        unknownRegisteredCharsets.add(name);
81      } else {
82        knownRegisteredCharsets.add(name);
83      }
84    }
85
86    for (Iterator nameItr = knownRegisteredCharsets.iterator(); nameItr.hasNext();) {
87      String name = (String) nameItr.next();
88      Charset cs = Charset.forName(name);
89      if (!cs.isRegistered()) {
90        System.err.println("isRegistered was false for " + name + " " + cs.name() + " " + cs.aliases());
91      }
92      assertTrue("isRegistered was false for " + name + " " + cs.name() + " " + cs.aliases(), cs.isRegistered());
93    }
94    for (Iterator nameItr = unknownRegisteredCharsets.iterator(); nameItr.hasNext();) {
95      String name = (String) nameItr.next();
96      Charset cs = Charset.forName(name);
97      assertFalse("isRegistered was true for " + name + " " + cs.name() + " " + cs.aliases(), cs.isRegistered());
98    }
99  }
100
101  public void test_guaranteedCharsetsAvailable() throws Exception {
102    // All Java implementations must support these charsets.
103    assertNotNull(Charset.forName("ISO-8859-1"));
104    assertNotNull(Charset.forName("US-ASCII"));
105    assertNotNull(Charset.forName("UTF-16"));
106    assertNotNull(Charset.forName("UTF-16BE"));
107    assertNotNull(Charset.forName("UTF-16LE"));
108    assertNotNull(Charset.forName("UTF-8"));
109  }
110
111  // http://code.google.com/p/android/issues/detail?id=42769
112  public void test_42769() throws Exception {
113    ArrayList<Thread> threads = new ArrayList<Thread>();
114    for (int i = 0; i < 10; ++i) {
115      Thread t = new Thread(new Runnable() {
116        public void run() {
117          for (int i = 0; i < 50; ++i) {
118            Charset.availableCharsets();
119          }
120        }
121      });
122      threads.add(t);
123    }
124
125    for (Thread t : threads) {
126      t.start();
127    }
128    for (Thread t : threads) {
129      t.join();
130    }
131  }
132
133  public void test_have_canonical_EUC_JP() throws Exception {
134    assertEquals("EUC-JP", Charset.forName("EUC-JP").name());
135  }
136
137  public void test_EUC_JP_replacement_character() throws Exception {
138    // We have text either side of the replacement character, because all kinds of errors
139    // could lead to a replacement character being returned.
140    assertEncodes(Charset.forName("EUC-JP"), " \ufffd ", ' ', 0xf4, 0xfe, ' ');
141    assertDecodes(Charset.forName("EUC-JP"), " \ufffd ", ' ', 0xf4, 0xfe, ' ');
142  }
143
144  public void test_SCSU_replacement_character() throws Exception {
145    // We have text either side of the replacement character, because all kinds of errors
146    // could lead to a replacement character being returned.
147    assertEncodes(Charset.forName("SCSU"), " \ufffd ", ' ', 14, 0xff, 0xfd, ' ');
148    assertDecodes(Charset.forName("SCSU"), " \ufffd ", ' ', 14, 0xff, 0xfd, ' ');
149  }
150
151  public void test_Shift_JIS_replacement_character() throws Exception {
152    // We have text either side of the replacement character, because all kinds of errors
153    // could lead to a replacement character being returned.
154    assertEncodes(Charset.forName("Shift_JIS"), " \ufffd ", ' ', 0xfc, 0xfc, ' ');
155    assertDecodes(Charset.forName("Shift_JIS"), " \ufffd ", ' ', 0xfc, 0xfc, ' ');
156  }
157
158  public void test_UTF_16() throws Exception {
159    Charset cs = Charset.forName("UTF-16");
160    // Writes big-endian, with a big-endian BOM.
161    assertEncodes(cs, "a\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
162    // Reads whatever the BOM tells it to read...
163    assertDecodes(cs, "a\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
164    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
165    // ...and defaults to reading big-endian if there's no BOM.
166    assertDecodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
167  }
168
169  public void test_UTF_16BE() throws Exception {
170    Charset cs = Charset.forName("UTF-16BE");
171    // Writes big-endian, with no BOM.
172    assertEncodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
173    // Treats a little-endian BOM as an error and continues to read big-endian.
174    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
175    assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 'a', 0x06, 0x66);
176    // Accepts a big-endian BOM and includes U+FEFF in the decoded output.
177    assertDecodes(cs, "\ufeffa\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
178    // Defaults to reading big-endian.
179    assertDecodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
180  }
181
182  public void test_UTF_16LE() throws Exception {
183    Charset cs = Charset.forName("UTF-16LE");
184    // Writes little-endian, with no BOM.
185    assertEncodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
186    // Accepts a little-endian BOM and includes U+FEFF in the decoded output.
187    assertDecodes(cs, "\ufeffa\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
188    // Treats a big-endian BOM as an error and continues to read little-endian.
189    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
190    assertDecodes(cs, "\ufffda\u0666", 0xfe, 0xff, 'a', 0, 0x66, 0x06);
191    // Defaults to reading little-endian.
192    assertDecodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
193  }
194
195  public void test_x_UTF_16LE_BOM() throws Exception {
196    Charset cs = Charset.forName("x-UTF-16LE-BOM");
197    // Writes little-endian, with a BOM.
198    assertEncodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
199    // Accepts a little-endian BOM and swallows the BOM.
200    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
201    // Swallows a big-endian BOM, but continues to read little-endian!
202    assertDecodes(cs, "\u6100\u6606", 0xfe, 0xff, 'a', 0, 0x66, 0x06);
203    // Defaults to reading little-endian.
204    assertDecodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
205  }
206
207  public void test_UTF_32() throws Exception {
208    Charset cs = Charset.forName("UTF-32");
209    // Writes big-endian, with no BOM.
210    assertEncodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
211    // Reads whatever the BOM tells it to read...
212    assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
213    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
214    // ...and defaults to reading big-endian if there's no BOM.
215    assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
216  }
217
218  public void test_UTF_32BE() throws Exception {
219    Charset cs = Charset.forName("UTF-32BE");
220    // Writes big-endian, with no BOM.
221    assertEncodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
222    // Treats a little-endian BOM as an error and continues to read big-endian.
223    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
224    assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 0, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
225    // Accepts a big-endian BOM and swallows the BOM.
226    assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
227    // Defaults to reading big-endian.
228    assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
229  }
230
231  public void test_UTF_32LE() throws Exception {
232    Charset cs = Charset.forName("UTF-32LE");
233    // Writes little-endian, with no BOM.
234    assertEncodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
235    // Accepts a little-endian BOM and swallows the BOM.
236    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
237    // Treats a big-endian BOM as an error and continues to read little-endian.
238    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
239    assertDecodes(cs, "\ufffda\u0666", 0, 0, 0xfe, 0xff, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
240    // Defaults to reading little-endian.
241    assertDecodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
242  }
243
244  public void test_X_UTF_32BE_BOM() throws Exception {
245    Charset cs = Charset.forName("X-UTF-32BE-BOM");
246    // Writes big-endian, with a big-endian BOM.
247    assertEncodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
248    // Treats a little-endian BOM as an error and continues to read big-endian.
249    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
250    assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 0, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
251    // Swallows a big-endian BOM, and continues to read big-endian.
252    assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
253    // Defaults to reading big-endian.
254    assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
255  }
256
257  public void test_X_UTF_32LE_BOM() throws Exception {
258    Charset cs = Charset.forName("X-UTF-32LE-BOM");
259    // Writes little-endian, with a little-endian BOM.
260    assertEncodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
261    // Accepts a little-endian BOM and swallows the BOM.
262    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
263    // Treats a big-endian BOM as an error and continues to read little-endian.
264    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
265    assertDecodes(cs, "\ufffda\u0666", 0, 0, 0xfe, 0xff, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
266    // Defaults to reading little-endian.
267    assertDecodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
268  }
269
270  private byte[] toByteArray(int[] ints) {
271    byte[] result = new byte[ints.length];
272    for (int i = 0; i < ints.length; ++i) {
273      result[i] = (byte) ints[i];
274    }
275    return result;
276  }
277
278  private void assertEncodes(Charset cs, String s, int... expectedByteInts) throws Exception {
279    ByteBuffer out = cs.encode(s);
280    byte[] bytes = new byte[out.remaining()];
281    out.get(bytes);
282    assertEquals(Arrays.toString(toByteArray(expectedByteInts)), Arrays.toString(bytes));
283  }
284
285  private void assertDecodes(Charset cs, String s, int... byteInts) throws Exception {
286    ByteBuffer in = ByteBuffer.wrap(toByteArray(byteInts));
287    CharBuffer out = cs.decode(in);
288    assertEquals(s, out.toString());
289  }
290
291  public void test_forNameLjava_lang_String() {
292    // Invoke forName two times with the same canonical name.
293    // It should return the same reference.
294    Charset cs1 = Charset.forName("UTF-8");
295    Charset cs2 = Charset.forName("UTF-8");
296    assertSame(cs1, cs2);
297
298    // test forName: invoke forName two times for the same Charset using
299    // canonical name and alias, it should return the same reference.
300    Charset cs3 = Charset.forName("ASCII");
301    Charset cs4 = Charset.forName("US-ASCII");
302    assertSame(cs3, cs4);
303  }
304
305  static MockCharset charset1 = new MockCharset("mockCharset00",
306                                                new String[] { "mockCharset01", "mockCharset02" });
307
308  static MockCharset charset2 = new MockCharset("mockCharset10",
309                                                new String[] { "mockCharset11", "mockCharset12" });
310
311  // Test the required 6 charsets are supported.
312  public void testRequiredCharsetSupported() {
313    assertTrue(Charset.isSupported("US-ASCII"));
314    assertTrue(Charset.isSupported("ASCII"));
315    assertTrue(Charset.isSupported("ISO-8859-1"));
316    assertTrue(Charset.isSupported("ISO8859_1"));
317    assertTrue(Charset.isSupported("UTF-8"));
318    assertTrue(Charset.isSupported("UTF8"));
319    assertTrue(Charset.isSupported("UTF-16"));
320    assertTrue(Charset.isSupported("UTF-16BE"));
321    assertTrue(Charset.isSupported("UTF-16LE"));
322
323    Charset c1 = Charset.forName("US-ASCII");
324    assertEquals("US-ASCII", Charset.forName("US-ASCII").name());
325    assertEquals("US-ASCII", Charset.forName("ASCII").name());
326    assertEquals("ISO-8859-1", Charset.forName("ISO-8859-1").name());
327    assertEquals("ISO-8859-1", Charset.forName("ISO8859_1").name());
328    assertEquals("UTF-8", Charset.forName("UTF-8").name());
329    assertEquals("UTF-8", Charset.forName("UTF8").name());
330    assertEquals("UTF-16", Charset.forName("UTF-16").name());
331    assertEquals("UTF-16BE", Charset.forName("UTF-16BE").name());
332    assertEquals("UTF-16LE", Charset.forName("UTF-16LE").name());
333
334    assertNotSame(Charset.availableCharsets(), Charset.availableCharsets());
335    // assertSame(Charset.forName("US-ASCII"), Charset.availableCharsets().get("US-ASCII"));
336    // assertSame(Charset.forName("US-ASCII"), c1);
337    assertTrue(Charset.availableCharsets().containsKey("US-ASCII"));
338    assertTrue(Charset.availableCharsets().containsKey("ISO-8859-1"));
339    assertTrue(Charset.availableCharsets().containsKey("UTF-8"));
340    assertTrue(Charset.availableCharsets().containsKey("UTF-16"));
341    assertTrue(Charset.availableCharsets().containsKey("UTF-16BE"));
342    assertTrue(Charset.availableCharsets().containsKey("UTF-16LE"));
343  }
344
345  public void testIsSupported_Null() {
346    try {
347      Charset.isSupported(null);
348      fail();
349    } catch (IllegalArgumentException expected) {
350    }
351  }
352
353  public void testIsSupported_EmptyString() {
354    try {
355      Charset.isSupported("");
356      fail();
357    } catch (IllegalArgumentException expected) {
358    }
359  }
360
361  public void testIsSupported_InvalidInitialCharacter() {
362    try {
363      Charset.isSupported(".char");
364      fail();
365    } catch (IllegalArgumentException expected) {
366    }
367  }
368
369  public void testIsSupported_IllegalName() {
370    try {
371      Charset.isSupported(" ///#$$");
372      fail();
373    } catch (IllegalCharsetNameException expected) {
374    }
375  }
376
377  public void testIsSupported_NotSupported() {
378    assertFalse(Charset.isSupported("well-formed-name-of-a-charset-that-does-not-exist"));
379  }
380
381  public void testForName_Null() {
382    try {
383      Charset.forName(null);
384      fail();
385    } catch (IllegalArgumentException expected) {
386    }
387  }
388
389  public void testForName_EmptyString() {
390    try {
391      Charset.forName("");
392      fail();
393    } catch (IllegalArgumentException expected) {
394    }
395  }
396
397  public void testForName_InvalidInitialCharacter() {
398    try {
399      Charset.forName(".char");
400      fail();
401    } catch (IllegalArgumentException expected) {
402    }
403  }
404
405  public void testForName_IllegalName() {
406    try {
407      Charset.forName(" ///#$$");
408      fail();
409    } catch (IllegalCharsetNameException expected) {
410    }
411  }
412
413  public void testForName_NotSupported() {
414    try {
415      Charset.forName("impossible");
416      fail();
417    } catch (UnsupportedCharsetException expected) {
418    }
419  }
420
421  public void testConstructor_Normal() {
422    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
423    MockCharset c = new MockCharset(mockName, new String[] { "mock" });
424    assertEquals(mockName, c.name());
425    assertEquals(mockName, c.displayName());
426    assertEquals(mockName, c.displayName(Locale.getDefault()));
427    assertEquals("mock", c.aliases().toArray()[0]);
428    assertEquals(1, c.aliases().toArray().length);
429  }
430
431  public void testConstructor_EmptyCanonicalName() {
432    try {
433      new MockCharset("", new String[0]);
434      fail();
435    } catch (IllegalCharsetNameException expected) {
436    }
437  }
438
439  public void testConstructor_IllegalCanonicalName_Initial() {
440    try {
441      new MockCharset("-123", new String[] { "mock" });
442      fail();
443    } catch (IllegalCharsetNameException expected) {
444    }
445  }
446
447  public void testConstructor_IllegalCanonicalName_Middle() {
448    try {
449      new MockCharset("1%%23", new String[] { "mock" });
450      fail();
451    } catch (IllegalCharsetNameException expected) {
452    }
453    try {
454      new MockCharset("1//23", new String[] { "mock" });
455      fail();
456    } catch (IllegalCharsetNameException expected) {
457    }
458  }
459
460  public void testConstructor_NullCanonicalName() {
461    try {
462      MockCharset c = new MockCharset(null, new String[] { "mock" });
463      fail();
464    } catch (NullPointerException expected) {
465    }
466  }
467
468  public void testConstructor_NullAliases() {
469    MockCharset c = new MockCharset("mockChar", null);
470    assertEquals("mockChar", c.name());
471    assertEquals("mockChar", c.displayName());
472    assertEquals("mockChar", c.displayName(Locale.getDefault()));
473    assertEquals(0, c.aliases().toArray().length);
474  }
475
476  public void testConstructor_NullAliase() {
477    try {
478      new MockCharset("mockChar", new String[] { "mock", null });
479      fail();
480    } catch (NullPointerException expected) {
481    }
482  }
483
484  public void testConstructor_NoAliases() {
485    MockCharset c = new MockCharset("mockChar", new String[0]);
486    assertEquals("mockChar", c.name());
487    assertEquals("mockChar", c.displayName());
488    assertEquals("mockChar", c.displayName(Locale.getDefault()));
489    assertEquals(0, c.aliases().toArray().length);
490  }
491
492  public void testConstructor_EmptyAliases() {
493    try {
494      new MockCharset("mockChar", new String[] { "" });
495      fail();
496    } catch (IllegalCharsetNameException expected) {
497    }
498  }
499
500  // Test the constructor with illegal aliases: starting with neither a digit nor a letter.
501  public void testConstructor_IllegalAliases_Initial() {
502    try {
503      new MockCharset("mockChar", new String[] { "mock", "-123" });
504      fail();
505    } catch (IllegalCharsetNameException e) {
506    }
507  }
508
509  public void testConstructor_IllegalAliases_Middle() {
510    try {
511      new MockCharset("mockChar", new String[] { "mock", "22##ab" });
512      fail();
513    } catch (IllegalCharsetNameException expected) {
514    }
515    try {
516      new MockCharset("mockChar", new String[] { "mock", "22%%ab" });
517      fail();
518    } catch (IllegalCharsetNameException expected) {
519    }
520  }
521
522  public void testAliases_Multiple() {
523    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
524    MockCharset c = new MockCharset("mockChar", new String[] { "mock", mockName, "mock2" });
525    assertEquals("mockChar", c.name());
526    assertEquals(3, c.aliases().size());
527    assertTrue(c.aliases().contains("mock"));
528    assertTrue(c.aliases().contains(mockName));
529    assertTrue(c.aliases().contains("mock2"));
530
531    try {
532      c.aliases().clear();
533      fail();
534    } catch (UnsupportedOperationException expected) {
535    }
536  }
537
538  public void testAliases_Duplicate() {
539    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
540    MockCharset c = new MockCharset("mockChar", new String[] { "mockChar",
541                                                                  "mock", mockName, "mock", "mockChar", "mock", "mock2" });
542    assertEquals("mockChar", c.name());
543    assertEquals(4, c.aliases().size());
544    assertTrue(c.aliases().contains("mockChar"));
545    assertTrue(c.aliases().contains("mock"));
546    assertTrue(c.aliases().contains(mockName));
547    assertTrue(c.aliases().contains("mock2"));
548  }
549
550  public void testCanEncode() {
551    MockCharset c = new MockCharset("mock", null);
552    assertTrue(c.canEncode());
553  }
554
555  public void testIsRegistered() {
556    MockCharset c = new MockCharset("mock", null);
557    assertTrue(c.isRegistered());
558  }
559
560  public void testDisplayName_Locale_Null() {
561    MockCharset c = new MockCharset("mock", null);
562    assertEquals("mock", c.displayName(null));
563  }
564
565  public void testCompareTo_Normal() {
566    MockCharset c1 = new MockCharset("mock", null);
567    assertEquals(0, c1.compareTo(c1));
568
569    MockCharset c2 = new MockCharset("Mock", null);
570    assertEquals(0, c1.compareTo(c2));
571
572    c2 = new MockCharset("mock2", null);
573    assertTrue(c1.compareTo(c2) < 0);
574    assertTrue(c2.compareTo(c1) > 0);
575
576    c2 = new MockCharset("mack", null);
577    assertTrue(c1.compareTo(c2) > 0);
578    assertTrue(c2.compareTo(c1) < 0);
579
580    c2 = new MockCharset("m.", null);
581    assertTrue(c1.compareTo(c2) > 0);
582    assertTrue(c2.compareTo(c1) < 0);
583
584    c2 = new MockCharset("m:", null);
585    assertEquals("mock".compareToIgnoreCase("m:"), c1.compareTo(c2));
586    assertEquals("m:".compareToIgnoreCase("mock"), c2.compareTo(c1));
587
588    c2 = new MockCharset("m-", null);
589    assertTrue(c1.compareTo(c2) > 0);
590    assertTrue(c2.compareTo(c1) < 0);
591
592    c2 = new MockCharset("m_", null);
593    assertTrue(c1.compareTo(c2) > 0);
594    assertTrue(c2.compareTo(c1) < 0);
595  }
596
597  public void testCompareTo_Null() {
598    MockCharset c1 = new MockCharset("mock", null);
599    try {
600      c1.compareTo(null);
601      fail();
602    } catch (NullPointerException expected) {
603    }
604  }
605
606  public void testCompareTo_DiffCharsetClass() {
607    MockCharset c1 = new MockCharset("mock", null);
608    MockCharset2 c2 = new MockCharset2("Mock", new String[] { "myname" });
609    assertEquals(0, c1.compareTo(c2));
610    assertEquals(0, c2.compareTo(c1));
611  }
612
613  public void testEquals_Normal() {
614    MockCharset c1 = new MockCharset("mock", null);
615    MockCharset2 c2 = new MockCharset2("mock", null);
616    assertTrue(c1.equals(c2));
617    assertTrue(c2.equals(c1));
618
619    c2 = new MockCharset2("Mock", null);
620    assertFalse(c1.equals(c2));
621    assertFalse(c2.equals(c1));
622  }
623
624  public void testEquals_Null() {
625    MockCharset c1 = new MockCharset("mock", null);
626    assertFalse(c1.equals(null));
627  }
628
629  public void testEquals_NonCharsetObject() {
630    MockCharset c1 = new MockCharset("mock", null);
631    assertFalse(c1.equals("test"));
632  }
633
634  public void testEquals_DiffCharsetClass() {
635    MockCharset c1 = new MockCharset("mock", null);
636    MockCharset2 c2 = new MockCharset2("mock", null);
637    assertTrue(c1.equals(c2));
638    assertTrue(c2.equals(c1));
639  }
640
641  public void testHashCode_DiffCharsetClass() {
642    MockCharset c1 = new MockCharset("mock", null);
643    assertEquals(c1.hashCode(), "mock".hashCode());
644
645    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
646    c1 = new MockCharset(mockName, new String[] { "mockChar", "mock",
647                                                     mockName, "mock", "mockChar", "mock", "mock2" });
648    assertEquals(mockName.hashCode(), c1.hashCode());
649  }
650
651  public void testEncode_CharBuffer_Normal() throws Exception {
652    MockCharset c1 = new MockCharset("testEncode_CharBuffer_Normal_mock", null);
653    ByteBuffer bb = c1.encode(CharBuffer.wrap("abcdefg"));
654    assertEquals("abcdefg", new String(bb.array(), "iso8859-1"));
655    bb = c1.encode(CharBuffer.wrap(""));
656    assertEquals("", new String(bb.array(), "iso8859-1"));
657  }
658
659  public void testEncode_CharBuffer_Unmappable() throws Exception {
660    Charset c1 = Charset.forName("iso8859-1");
661    ByteBuffer bb = c1.encode(CharBuffer.wrap("abcd\u5D14efg"));
662    assertEquals(new String(bb.array(), "iso8859-1"),
663                 "abcd" + new String(c1.newEncoder().replacement(), "iso8859-1") + "efg");
664  }
665
666  public void testEncode_CharBuffer_NullCharBuffer() {
667    MockCharset c = new MockCharset("mock", null);
668    try {
669      c.encode((CharBuffer) null);
670      fail();
671    } catch (NullPointerException expected) {
672    }
673  }
674
675  public void testEncode_CharBuffer_NullEncoder() {
676    MockCharset2 c = new MockCharset2("mock2", null);
677    try {
678      c.encode(CharBuffer.wrap("hehe"));
679      fail();
680    } catch (NullPointerException expected) {
681    }
682  }
683
684  public void testEncode_String_Normal() throws Exception {
685    MockCharset c1 = new MockCharset("testEncode_String_Normal_mock", null);
686    ByteBuffer bb = c1.encode("abcdefg");
687    assertEquals("abcdefg", new String(bb.array(), "iso8859-1"));
688    bb = c1.encode("");
689    assertEquals("", new String(bb.array(), "iso8859-1"));
690  }
691
692  public void testEncode_String_Unmappable() throws Exception {
693    Charset c1 = Charset.forName("iso8859-1");
694    ByteBuffer bb = c1.encode("abcd\u5D14efg");
695    assertEquals(new String(bb.array(), "iso8859-1"),
696                 "abcd" + new String(c1.newEncoder().replacement(), "iso8859-1") + "efg");
697  }
698
699  public void testEncode_String_NullString() {
700    MockCharset c = new MockCharset("mock", null);
701    try {
702      c.encode((String) null);
703      fail();
704    } catch (NullPointerException expected) {
705    }
706  }
707
708  public void testEncode_String_NullEncoder() {
709    MockCharset2 c = new MockCharset2("mock2", null);
710    try {
711      c.encode("hehe");
712      fail();
713    } catch (NullPointerException expected) {
714    }
715  }
716
717  public void testDecode_Normal() throws Exception {
718    MockCharset c1 = new MockCharset("mock", null);
719    CharBuffer cb = c1.decode(ByteBuffer.wrap("abcdefg".getBytes("iso8859-1")));
720    assertEquals("abcdefg", new String(cb.array()));
721    cb = c1.decode(ByteBuffer.wrap("".getBytes("iso8859-1")));
722    assertEquals("", new String(cb.array()));
723  }
724
725  public void testDecode_Malformed() throws Exception {
726    Charset c1 = Charset.forName("iso8859-1");
727    CharBuffer cb = c1.decode(ByteBuffer.wrap("abcd\u5D14efg".getBytes("iso8859-1")));
728    byte[] replacement = c1.newEncoder().replacement();
729    assertEquals(new String(cb.array()).trim(), "abcd" + new String(replacement, "iso8859-1") + "efg");
730  }
731
732  public void testDecode_NullByteBuffer() {
733    MockCharset c = new MockCharset("mock", null);
734    try {
735      c.decode(null);
736      fail();
737    } catch (NullPointerException expected) {
738    }
739  }
740
741  public void testDecode_NullDecoder() {
742    MockCharset2 c = new MockCharset2("mock2", null);
743    try {
744      c.decode(ByteBuffer.wrap("hehe".getBytes()));
745      fail();
746    } catch (NullPointerException expected) {
747    }
748  }
749
750  public void testToString() {
751    MockCharset c1 = new MockCharset("mock", null);
752    assertTrue(-1 != c1.toString().indexOf("mock"));
753  }
754
755  static final class MockCharset extends Charset {
756    public MockCharset(String canonicalName, String[] aliases) {
757      super(canonicalName, aliases);
758    }
759
760    public boolean contains(Charset cs) {
761      return false;
762    }
763
764    public CharsetDecoder newDecoder() {
765      return new MockDecoder(this);
766    }
767
768    public CharsetEncoder newEncoder() {
769      return new MockEncoder(this);
770    }
771  }
772
773  static class MockCharset2 extends Charset {
774    public MockCharset2(String canonicalName, String[] aliases) {
775      super(canonicalName, aliases);
776    }
777
778    public boolean contains(Charset cs) {
779      return false;
780    }
781
782    public CharsetDecoder newDecoder() {
783      return null;
784    }
785
786    public CharsetEncoder newEncoder() {
787      return null;
788    }
789  }
790
791  static class MockEncoder extends java.nio.charset.CharsetEncoder {
792    public MockEncoder(Charset cs) {
793      super(cs, 1, 3, new byte[] { (byte) '?' });
794    }
795
796    protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
797      while (in.remaining() > 0) {
798        out.put((byte) in.get());
799        // out.put((byte) '!');
800      }
801      return CoderResult.UNDERFLOW;
802    }
803  }
804
805  static class MockDecoder extends java.nio.charset.CharsetDecoder {
806    public MockDecoder(Charset cs) {
807      super(cs, 1, 10);
808    }
809
810    protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
811      while (in.remaining() > 0) {
812        out.put((char) in.get());
813      }
814      return CoderResult.UNDERFLOW;
815    }
816  }
817
818
819  // Test the method isSupported(String) with charset supported by multiple providers.
820  public void testIsSupported_And_ForName_NormalProvider() throws Exception {
821    SettableCharsetProvider.setDelegate(new MockCharsetProvider());
822    try {
823      assertTrue(Charset.isSupported("mockCharset10"));
824      // ignore case problem in mock, intended
825      assertTrue(Charset.isSupported("MockCharset11"));
826      assertTrue(Charset.isSupported("MockCharset12"));
827      assertTrue(Charset.isSupported("MOCKCharset10"));
828      // intended case problem in mock
829      assertTrue(Charset.isSupported("MOCKCharset11"));
830      assertTrue(Charset.isSupported("MOCKCharset12"));
831
832      assertTrue(Charset.forName("mockCharset10") instanceof MockCharset);
833      assertTrue(Charset.forName("mockCharset11") instanceof MockCharset);
834      assertTrue(Charset.forName("mockCharset12") instanceof MockCharset);
835
836      assertTrue(Charset.forName("mockCharset10") == charset2);
837      // intended case problem in mock
838      Charset.forName("mockCharset11");
839      assertTrue(Charset.forName("mockCharset12") == charset2);
840    } finally {
841      SettableCharsetProvider.clearDelegate();
842    }
843  }
844
845  // Test the method availableCharsets() with charset supported by multiple providers.
846  public void testAvailableCharsets_NormalProvider() throws Exception {
847    SettableCharsetProvider.setDelegate(new MockCharsetProvider());
848    try {
849      assertTrue(Charset.availableCharsets().containsKey("mockCharset00"));
850      assertTrue(Charset.availableCharsets().containsKey("MOCKCharset00"));
851      assertTrue(Charset.availableCharsets().get("mockCharset00") instanceof MockCharset);
852      assertTrue(Charset.availableCharsets().get("MOCKCharset00") instanceof MockCharset);
853      assertFalse(Charset.availableCharsets().containsKey("mockCharset01"));
854      assertFalse(Charset.availableCharsets().containsKey("mockCharset02"));
855
856      assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2);
857      assertTrue(Charset.availableCharsets().get("MOCKCharset10") == charset2);
858      assertFalse(Charset.availableCharsets().containsKey("mockCharset11"));
859      assertFalse(Charset.availableCharsets().containsKey("mockCharset12"));
860
861      assertTrue(Charset.availableCharsets().containsKey("mockCharset10"));
862      assertTrue(Charset.availableCharsets().containsKey("MOCKCharset10"));
863      assertTrue(Charset.availableCharsets().get("mockCharset10") == charset2);
864      assertFalse(Charset.availableCharsets().containsKey("mockCharset11"));
865      assertFalse(Charset.availableCharsets().containsKey("mockCharset12"));
866    } finally {
867      SettableCharsetProvider.clearDelegate();
868    }
869  }
870
871  // Test the method forName(String) when the charset provider supports a
872  // built-in charset.
873  public void testForName_DuplicateWithBuiltInCharset() throws Exception {
874    SettableCharsetProvider.setDelegate(new MockCharsetProviderASCII());
875    try {
876      assertFalse(Charset.forName("us-ascii") instanceof MockCharset);
877      assertFalse(Charset.availableCharsets().get("us-ascii") instanceof MockCharset);
878    } finally {
879      SettableCharsetProvider.clearDelegate();
880    }
881  }
882
883  // Fails on Android with a StackOverflowException.
884  public void testForName_withProviderWithRecursiveCall() throws Exception {
885    SettableCharsetProvider.setDelegate(new MockCharsetProviderWithRecursiveCall());
886    try {
887      Charset.forName("poop");
888      fail();
889    } catch (UnsupportedCharsetException expected) {
890    } finally {
891      SettableCharsetProvider.clearDelegate();
892    }
893  }
894
895  public static class MockCharsetProviderWithRecursiveCall extends CharsetProvider {
896      @Override
897      public Iterator<Charset> charsets() {
898          return null;
899      }
900
901      @Override
902      public Charset charsetForName(String charsetName) {
903          if (Charset.isSupported(charsetName)) {
904              return Charset.forName(charsetName);
905          }
906
907          return null;
908      }
909  }
910
911  public static class MockCharsetProvider extends CharsetProvider {
912    public Charset charsetForName(String charsetName) {
913      if ("MockCharset00".equalsIgnoreCase(charsetName) ||
914          "MockCharset01".equalsIgnoreCase(charsetName) ||
915          "MockCharset02".equalsIgnoreCase(charsetName)) {
916        return charset1;
917      } else if ("MockCharset10".equalsIgnoreCase(charsetName) ||
918          "MockCharset11".equalsIgnoreCase(charsetName) ||
919          "MockCharset12".equalsIgnoreCase(charsetName)) {
920        return charset2;
921      }
922      return null;
923    }
924
925    public Iterator charsets() {
926      Vector v = new Vector();
927      v.add(charset1);
928      v.add(charset2);
929      return v.iterator();
930    }
931  }
932
933  // Another mock charset provider attempting to provide the built-in charset "ascii" again.
934  public static class MockCharsetProviderASCII extends CharsetProvider {
935    public Charset charsetForName(String charsetName) {
936      if ("US-ASCII".equalsIgnoreCase(charsetName) || "ASCII".equalsIgnoreCase(charsetName)) {
937        return new MockCharset("US-ASCII", new String[] { "ASCII" });
938      }
939      return null;
940    }
941
942    public Iterator charsets() {
943      Vector v = new Vector();
944      v.add(new MockCharset("US-ASCII", new String[] { "ASCII" }));
945      return v.iterator();
946    }
947  }
948}
949