summaryrefslogtreecommitdiffstats
path: root/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetTest.java
blob: 01cf40e0d43b51cabc984a615255d9c63e10d8e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.harmony.tests.java.nio.charset;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
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 junit.framework.TestCase;

/**
 * Test class java.nio.Charset.
 */
public class CharsetTest extends TestCase {

  public void test_allAvailableCharsets() throws Exception {
    // Check that we can instantiate every Charset, CharsetDecoder, and CharsetEncoder.
    for (String charsetName : Charset.availableCharsets().keySet()) {
      if (charsetName.equals("UTF-32")) {
        // Our UTF-32 is broken. http://b/2702411
        // TODO: remove this hack when UTF-32 is fixed.
        continue;
      }

      Charset cs = Charset.forName(charsetName);
      assertNotNull(cs.newDecoder());
      if (cs.canEncode()) {
        CharsetEncoder enc = cs.newEncoder();
        assertNotNull(enc);
        assertNotNull(enc.replacement());
      }
    }
  }

  public void test_defaultCharset() {
    assertEquals("UTF-8", Charset.defaultCharset().name());
  }

  public void test_isRegistered() {
    // Regression for HARMONY-45

    // Will contain names of charsets registered with IANA
    Set<String> knownRegisteredCharsets = new HashSet<String>();

    // Will contain names of charsets not known to be registered with IANA
    Set<String> unknownRegisteredCharsets = new HashSet<String>();

    Set<String> names = Charset.availableCharsets().keySet();
    for (Iterator nameItr = names.iterator(); nameItr.hasNext();) {
      String name = (String) nameItr.next();
      if (name.toLowerCase(Locale.ROOT).startsWith("x-")) {
        unknownRegisteredCharsets.add(name);
      } else {
        knownRegisteredCharsets.add(name);
      }
    }

    for (Iterator nameItr = knownRegisteredCharsets.iterator(); nameItr.hasNext();) {
      String name = (String) nameItr.next();
      Charset cs = Charset.forName(name);
      if (!cs.isRegistered()) {
        System.err.println("isRegistered was false for " + name + " " + cs.name() + " " + cs.aliases());
      }
      assertTrue("isRegistered was false for " + name + " " + cs.name() + " " + cs.aliases(), cs.isRegistered());
    }
    for (Iterator nameItr = unknownRegisteredCharsets.iterator(); nameItr.hasNext();) {
      String name = (String) nameItr.next();
      Charset cs = Charset.forName(name);
      assertFalse("isRegistered was true for " + name + " " + cs.name() + " " + cs.aliases(), cs.isRegistered());
    }
  }

  public void test_guaranteedCharsetsAvailable() throws Exception {
    // All Java implementations must support these charsets.
    assertNotNull(Charset.forName("ISO-8859-1"));
    assertNotNull(Charset.forName("US-ASCII"));
    assertNotNull(Charset.forName("UTF-16"));
    assertNotNull(Charset.forName("UTF-16BE"));
    assertNotNull(Charset.forName("UTF-16LE"));
    assertNotNull(Charset.forName("UTF-8"));
  }

  // http://code.google.com/p/android/issues/detail?id=42769
  public void test_42769() throws Exception {
    ArrayList<Thread> threads = new ArrayList<Thread>();
    for (int i = 0; i < 10; ++i) {
      Thread t = new Thread(new Runnable() {
        public void run() {
          for (int i = 0; i < 50; ++i) {
            Charset.availableCharsets();
          }
        }
      });
      threads.add(t);
    }

    for (Thread t : threads) {
      t.start();
    }
    for (Thread t : threads) {
      t.join();
    }
  }

  public void test_have_canonical_EUC_JP() throws Exception {
    assertEquals("EUC-JP", Charset.forName("EUC-JP").name());
  }

  public void test_EUC_JP_replacement_character() throws Exception {
    // We have text either side of the replacement character, because all kinds of errors
    // could lead to a replacement character being returned.
    assertEncodes(Charset.forName("EUC-JP"), " \ufffd ", ' ', 0xf4, 0xfe, ' ');
    assertDecodes(Charset.forName("EUC-JP"), " \ufffd ", ' ', 0xf4, 0xfe, ' ');
  }

  public void test_SCSU_replacement_character() throws Exception {
    // We have text either side of the replacement character, because all kinds of errors
    // could lead to a replacement character being returned.
    assertEncodes(Charset.forName("SCSU"), " \ufffd ", ' ', 14, 0xff, 0xfd, ' ');
    assertDecodes(Charset.forName("SCSU"), " \ufffd ", ' ', 14, 0xff, 0xfd, ' ');
  }

  public void test_Shift_JIS_replacement_character() throws Exception {
    // We have text either side of the replacement character, because all kinds of errors
    // could lead to a replacement character being returned.
    assertEncodes(Charset.forName("Shift_JIS"), " \ufffd ", ' ', 0xfc, 0xfc, ' ');
    assertDecodes(Charset.forName("Shift_JIS"), " \ufffd ", ' ', 0xfc, 0xfc, ' ');
  }

  public void test_UTF_16() throws Exception {
    Charset cs = Charset.forName("UTF-16");
    // Writes big-endian, with a big-endian BOM.
    assertEncodes(cs, "a\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
    // Reads whatever the BOM tells it to read...
    assertDecodes(cs, "a\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
    // ...and defaults to reading big-endian if there's no BOM.
    assertDecodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
  }

  public void test_UTF_16BE() throws Exception {
    Charset cs = Charset.forName("UTF-16BE");
    // Writes big-endian, with no BOM.
    assertEncodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
    // Treats a little-endian BOM as an error and continues to read big-endian.
    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
    assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 'a', 0x06, 0x66);
    // Accepts a big-endian BOM and includes U+FEFF in the decoded output.
    assertDecodes(cs, "\ufeffa\u0666", 0xfe, 0xff, 0, 'a', 0x06, 0x66);
    // Defaults to reading big-endian.
    assertDecodes(cs, "a\u0666", 0, 'a', 0x06, 0x66);
  }

  public void test_UTF_16LE() throws Exception {
    Charset cs = Charset.forName("UTF-16LE");
    // Writes little-endian, with no BOM.
    assertEncodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
    // Accepts a little-endian BOM and includes U+FEFF in the decoded output.
    assertDecodes(cs, "\ufeffa\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
    // Treats a big-endian BOM as an error and continues to read little-endian.
    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
    assertDecodes(cs, "\ufffda\u0666", 0xfe, 0xff, 'a', 0, 0x66, 0x06);
    // Defaults to reading little-endian.
    assertDecodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
  }

  public void test_x_UTF_16LE_BOM() throws Exception {
    Charset cs = Charset.forName("x-UTF-16LE-BOM");
    // Writes little-endian, with a BOM.
    assertEncodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
    // Accepts a little-endian BOM and swallows the BOM.
    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 'a', 0, 0x66, 0x06);
    // Swallows a big-endian BOM, but continues to read little-endian!
    assertDecodes(cs, "\u6100\u6606", 0xfe, 0xff, 'a', 0, 0x66, 0x06);
    // Defaults to reading little-endian.
    assertDecodes(cs, "a\u0666", 'a', 0, 0x66, 0x06);
  }

  public void test_UTF_32() throws Exception {
    Charset cs = Charset.forName("UTF-32");
    // Writes big-endian, with no BOM.
    assertEncodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
    // Reads whatever the BOM tells it to read...
    assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
    // ...and defaults to reading big-endian if there's no BOM.
    assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
  }

  public void test_UTF_32BE() throws Exception {
    Charset cs = Charset.forName("UTF-32BE");
    // Writes big-endian, with no BOM.
    assertEncodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
    // Treats a little-endian BOM as an error and continues to read big-endian.
    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
    assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 0, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
    // Accepts a big-endian BOM and swallows the BOM.
    assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
    // Defaults to reading big-endian.
    assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
  }

  public void test_UTF_32LE() throws Exception {
    Charset cs = Charset.forName("UTF-32LE");
    // Writes little-endian, with no BOM.
    assertEncodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
    // Accepts a little-endian BOM and swallows the BOM.
    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
    // Treats a big-endian BOM as an error and continues to read little-endian.
    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
    assertDecodes(cs, "\ufffda\u0666", 0, 0, 0xfe, 0xff, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
    // Defaults to reading little-endian.
    assertDecodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
  }

  public void test_X_UTF_32BE_BOM() throws Exception {
    Charset cs = Charset.forName("X-UTF-32BE-BOM");
    // Writes big-endian, with a big-endian BOM.
    assertEncodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
    // Treats a little-endian BOM as an error and continues to read big-endian.
    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
    assertDecodes(cs, "\ufffda\u0666", 0xff, 0xfe, 0, 0, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
    // Swallows a big-endian BOM, and continues to read big-endian.
    assertDecodes(cs, "a\u0666", 0, 0, 0xfe, 0xff, 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
    // Defaults to reading big-endian.
    assertDecodes(cs, "a\u0666", 0, 0, 0, 'a', 0, 0, 0x06, 0x66);
  }

  public void test_X_UTF_32LE_BOM() throws Exception {
    Charset cs = Charset.forName("X-UTF-32LE-BOM");
    // Writes little-endian, with a little-endian BOM.
    assertEncodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
    // Accepts a little-endian BOM and swallows the BOM.
    assertDecodes(cs, "a\u0666", 0xff, 0xfe, 0, 0, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
    // Treats a big-endian BOM as an error and continues to read little-endian.
    // This test uses REPLACE mode, so we get the U+FFFD replacement character in the result.
    assertDecodes(cs, "\ufffda\u0666", 0, 0, 0xfe, 0xff, 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
    // Defaults to reading little-endian.
    assertDecodes(cs, "a\u0666", 'a', 0, 0, 0, 0x66, 0x06, 0, 0);
  }

  private byte[] toByteArray(int[] ints) {
    byte[] result = new byte[ints.length];
    for (int i = 0; i < ints.length; ++i) {
      result[i] = (byte) ints[i];
    }
    return result;
  }

  private void assertEncodes(Charset cs, String s, int... expectedByteInts) throws Exception {
    ByteBuffer out = cs.encode(s);
    byte[] bytes = new byte[out.remaining()];
    out.get(bytes);
    assertEquals(Arrays.toString(toByteArray(expectedByteInts)), Arrays.toString(bytes));
  }

  private void assertDecodes(Charset cs, String s, int... byteInts) throws Exception {
    ByteBuffer in = ByteBuffer.wrap(toByteArray(byteInts));
    CharBuffer out = cs.decode(in);
    assertEquals(s, out.toString());
  }

  public void test_forNameLjava_lang_String() {
    // Invoke forName two times with the same canonical name.
    // It should return the same reference.
    Charset cs1 = Charset.forName("UTF-8");
    Charset cs2 = Charset.forName("UTF-8");
    assertSame(cs1, cs2);

    // test forName: invoke forName two times for the same Charset using
    // canonical name and alias, it should return the same reference.
    Charset cs3 = Charset.forName("ASCII");
    Charset cs4 = Charset.forName("US-ASCII");
    assertSame(cs3, cs4);
  }

  static MockCharset charset1 = new MockCharset("mockCharset00",
                                                new String[] { "mockCharset01", "mockCharset02" });

  static MockCharset charset2 = new MockCharset("mockCharset10",
                                                new String[] { "mockCharset11", "mockCharset12" });

  // Test the required 6 charsets are supported.
  public void testRequiredCharsetSupported() {
    assertTrue(Charset.isSupported("US-ASCII"));
    assertTrue(Charset.isSupported("ASCII"));
    assertTrue(Charset.isSupported("ISO-8859-1"));
    assertTrue(Charset.isSupported("ISO8859_1"));
    assertTrue(Charset.isSupported("UTF-8"));
    assertTrue(Charset.isSupported("UTF8"));
    assertTrue(Charset.isSupported("UTF-16"));
    assertTrue(Charset.isSupported("UTF-16BE"));
    assertTrue(Charset.isSupported("UTF-16LE"));

    Charset c1 = Charset.forName("US-ASCII");
    assertEquals("US-ASCII", Charset.forName("US-ASCII").name());
    assertEquals("US-ASCII", Charset.forName("ASCII").name());
    assertEquals("ISO-8859-1", Charset.forName("ISO-8859-1").name());
    assertEquals("ISO-8859-1", Charset.forName("ISO8859_1").name());
    assertEquals("UTF-8", Charset.forName("UTF-8").name());
    assertEquals("UTF-8", Charset.forName("UTF8").name());
    assertEquals("UTF-16", Charset.forName("UTF-16").name());
    assertEquals("UTF-16BE", Charset.forName("UTF-16BE").name());
    assertEquals("UTF-16LE", Charset.forName("UTF-16LE").name());

    assertNotSame(Charset.availableCharsets(), Charset.availableCharsets());
    // assertSame(Charset.forName("US-ASCII"), Charset.availableCharsets().get("US-ASCII"));
    // assertSame(Charset.forName("US-ASCII"), c1);
    assertTrue(Charset.availableCharsets().containsKey("US-ASCII"));
    assertTrue(Charset.availableCharsets().containsKey("ISO-8859-1"));
    assertTrue(Charset.availableCharsets().containsKey("UTF-8"));
    assertTrue(Charset.availableCharsets().containsKey("UTF-16"));
    assertTrue(Charset.availableCharsets().containsKey("UTF-16BE"));
    assertTrue(Charset.availableCharsets().containsKey("UTF-16LE"));
  }

  public void testIsSupported_Null() {
    try {
      Charset.isSupported(null);
      fail();
    } catch (IllegalArgumentException expected) {
    }
  }

  public void testIsSupported_EmptyString() {
    try {
      Charset.isSupported("");
      fail();
    } catch (IllegalArgumentException expected) {
    }
  }

  public void testIsSupported_InvalidInitialCharacter() {
    try {
      Charset.isSupported(".char");
      fail();
    } catch (IllegalArgumentException expected) {
    }
  }

  public void testIsSupported_IllegalName() {
    try {
      Charset.isSupported(" ///#$$");
      fail();
    } catch (IllegalCharsetNameException expected) {
    }
  }

  public void testIsSupported_NotSupported() {
    assertFalse(Charset.isSupported("well-formed-name-of-a-charset-that-does-not-exist"));
  }

  public void testForName_Null() {
    try {
      Charset.forName(null);
      fail();
    } catch (IllegalArgumentException expected) {
    }
  }

  public void testForName_EmptyString() {
    try {
      Charset.forName("");
      fail();
    } catch (IllegalArgumentException expected) {
    }
  }

  public void testForName_InvalidInitialCharacter() {
    try {
      Charset.forName(".char");
      fail();
    } catch (IllegalArgumentException expected) {
    }
  }

  public void testForName_IllegalName() {
    try {
      Charset.forName(" ///#$$");
      fail();
    } catch (IllegalCharsetNameException expected) {
    }
  }

  public void testForName_NotSupported() {
    try {
      Charset.forName("impossible");
      fail();
    } catch (UnsupportedCharsetException expected) {
    }
  }

  public void testConstructor_Normal() {
    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
    MockCharset c = new MockCharset(mockName, new String[] { "mock" });
    assertEquals(mockName, c.name());
    assertEquals(mockName, c.displayName());
    assertEquals(mockName, c.displayName(Locale.getDefault()));
    assertEquals("mock", c.aliases().toArray()[0]);
    assertEquals(1, c.aliases().toArray().length);
  }

  public void testConstructor_EmptyCanonicalName() {
    try {
      new MockCharset("", new String[0]);
      fail();
    } catch (IllegalCharsetNameException expected) {
    }
  }

  public void testConstructor_IllegalCanonicalName_Initial() {
    try {
      new MockCharset("-123", new String[] { "mock" });
      fail();
    } catch (IllegalCharsetNameException expected) {
    }
  }

  public void testConstructor_IllegalCanonicalName_Middle() {
    try {
      new MockCharset("1%%23", new String[] { "mock" });
      fail();
    } catch (IllegalCharsetNameException expected) {
    }
    try {
      new MockCharset("1//23", new String[] { "mock" });
      fail();
    } catch (IllegalCharsetNameException expected) {
    }
  }

  public void testConstructor_NullCanonicalName() {
    try {
      MockCharset c = new MockCharset(null, new String[] { "mock" });
      fail();
    } catch (NullPointerException expected) {
    }
  }

  public void testConstructor_NullAliases() {
    MockCharset c = new MockCharset("mockChar", null);
    assertEquals("mockChar", c.name());
    assertEquals("mockChar", c.displayName());
    assertEquals("mockChar", c.displayName(Locale.getDefault()));
    assertEquals(0, c.aliases().toArray().length);
  }

  public void testConstructor_NullAliase() {
    try {
      new MockCharset("mockChar", new String[] { "mock", null });
      fail();
    } catch (NullPointerException expected) {
    }
  }

  public void testConstructor_NoAliases() {
    MockCharset c = new MockCharset("mockChar", new String[0]);
    assertEquals("mockChar", c.name());
    assertEquals("mockChar", c.displayName());
    assertEquals("mockChar", c.displayName(Locale.getDefault()));
    assertEquals(0, c.aliases().toArray().length);
  }

  public void testConstructor_EmptyAliases() {
    try {
      new MockCharset("mockChar", new String[] { "" });
      fail();
    } catch (IllegalCharsetNameException expected) {
    }
  }

  // Test the constructor with illegal aliases: starting with neither a digit nor a letter.
  public void testConstructor_IllegalAliases_Initial() {
    try {
      new MockCharset("mockChar", new String[] { "mock", "-123" });
      fail();
    } catch (IllegalCharsetNameException e) {
    }
  }

  public void testConstructor_IllegalAliases_Middle() {
    try {
      new MockCharset("mockChar", new String[] { "mock", "22##ab" });
      fail();
    } catch (IllegalCharsetNameException expected) {
    }
    try {
      new MockCharset("mockChar", new String[] { "mock", "22%%ab" });
      fail();
    } catch (IllegalCharsetNameException expected) {
    }
  }

  public void testAliases_Multiple() {
    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
    MockCharset c = new MockCharset("mockChar", new String[] { "mock", mockName, "mock2" });
    assertEquals("mockChar", c.name());
    assertEquals(3, c.aliases().size());
    assertTrue(c.aliases().contains("mock"));
    assertTrue(c.aliases().contains(mockName));
    assertTrue(c.aliases().contains("mock2"));

    try {
      c.aliases().clear();
      fail();
    } catch (UnsupportedOperationException expected) {
    }
  }

  public void testAliases_Duplicate() {
    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
    MockCharset c = new MockCharset("mockChar", new String[] { "mockChar",
                                                                  "mock", mockName, "mock", "mockChar", "mock", "mock2" });
    assertEquals("mockChar", c.name());
    assertEquals(4, c.aliases().size());
    assertTrue(c.aliases().contains("mockChar"));
    assertTrue(c.aliases().contains("mock"));
    assertTrue(c.aliases().contains(mockName));
    assertTrue(c.aliases().contains("mock2"));
  }

  public void testCanEncode() {
    MockCharset c = new MockCharset("mock", null);
    assertTrue(c.canEncode());
  }

  public void testIsRegistered() {
    MockCharset c = new MockCharset("mock", null);
    assertTrue(c.isRegistered());
  }

  public void testDisplayName_Locale_Null() {
    MockCharset c = new MockCharset("mock", null);
    assertEquals("mock", c.displayName(null));
  }

  public void testCompareTo_Normal() {
    MockCharset c1 = new MockCharset("mock", null);
    assertEquals(0, c1.compareTo(c1));

    MockCharset c2 = new MockCharset("Mock", null);
    assertEquals(0, c1.compareTo(c2));

    c2 = new MockCharset("mock2", null);
    assertTrue(c1.compareTo(c2) < 0);
    assertTrue(c2.compareTo(c1) > 0);

    c2 = new MockCharset("mack", null);
    assertTrue(c1.compareTo(c2) > 0);
    assertTrue(c2.compareTo(c1) < 0);

    c2 = new MockCharset("m.", null);
    assertTrue(c1.compareTo(c2) > 0);
    assertTrue(c2.compareTo(c1) < 0);

    c2 = new MockCharset("m:", null);
    assertEquals("mock".compareToIgnoreCase("m:"), c1.compareTo(c2));
    assertEquals("m:".compareToIgnoreCase("mock"), c2.compareTo(c1));

    c2 = new MockCharset("m-", null);
    assertTrue(c1.compareTo(c2) > 0);
    assertTrue(c2.compareTo(c1) < 0);

    c2 = new MockCharset("m_", null);
    assertTrue(c1.compareTo(c2) > 0);
    assertTrue(c2.compareTo(c1) < 0);
  }

  public void testCompareTo_Null() {
    MockCharset c1 = new MockCharset("mock", null);
    try {
      c1.compareTo(null);
      fail();
    } catch (NullPointerException expected) {
    }
  }

  public void testCompareTo_DiffCharsetClass() {
    MockCharset c1 = new MockCharset("mock", null);
    MockCharset2 c2 = new MockCharset2("Mock", new String[] { "myname" });
    assertEquals(0, c1.compareTo(c2));
    assertEquals(0, c2.compareTo(c1));
  }

  public void testEquals_Normal() {
    MockCharset c1 = new MockCharset("mock", null);
    MockCharset2 c2 = new MockCharset2("mock", null);
    assertTrue(c1.equals(c2));
    assertTrue(c2.equals(c1));

    c2 = new MockCharset2("Mock", null);
    assertFalse(c1.equals(c2));
    assertFalse(c2.equals(c1));
  }

  public void testEquals_Null() {
    MockCharset c1 = new MockCharset("mock", null);
    assertFalse(c1.equals(null));
  }

  public void testEquals_NonCharsetObject() {
    MockCharset c1 = new MockCharset("mock", null);
    assertFalse(c1.equals("test"));
  }

  public void testEquals_DiffCharsetClass() {
    MockCharset c1 = new MockCharset("mock", null);
    MockCharset2 c2 = new MockCharset2("mock", null);
    assertTrue(c1.equals(c2));
    assertTrue(c2.equals(c1));
  }

  public void testHashCode_DiffCharsetClass() {
    MockCharset c1 = new MockCharset("mock", null);
    assertEquals(c1.hashCode(), "mock".hashCode());

    final String mockName = "mockChar1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:-_";
    c1 = new MockCharset(mockName, new String[] { "mockChar", "mock",
                                                     mockName, "mock", "mockChar", "mock", "mock2" });
    assertEquals(mockName.hashCode(), c1.hashCode());
  }

  public void testEncode_CharBuffer_Normal() throws Exception {
    MockCharset c1 = new MockCharset("testEncode_CharBuffer_Normal_mock", null);
    ByteBuffer bb = c1.encode(CharBuffer.wrap("abcdefg"));
    assertEquals("abcdefg", new String(bb.array(), "iso8859-1"));
    bb = c1.encode(CharBuffer.wrap(""));
    assertEquals("", new String(bb.array(), "iso8859-1"));
  }

  public void testEncode_CharBuffer_Unmappable() throws Exception {
    Charset c1 = Charset.forName("iso8859-1");
    ByteBuffer bb = c1.encode(CharBuffer.wrap("abcd\u5D14efg"));
    assertEquals(new String(bb.array(), "iso8859-1"),
                 "abcd" + new String(c1.newEncoder().replacement(), "iso8859-1") + "efg");
  }

  public void testEncode_CharBuffer_NullCharBuffer() {
    MockCharset c = new MockCharset("mock", null);
    try {
      c.encode((CharBuffer) null);
      fail();
    } catch (NullPointerException expected) {
    }
  }

  public void testEncode_CharBuffer_NullEncoder() {
    MockCharset2 c = new MockCharset2("mock2", null);
    try {
      c.encode(CharBuffer.wrap("hehe"));
      fail();
    } catch (NullPointerException expected) {
    }
  }

  public void testEncode_String_Normal() throws Exception {
    MockCharset c1 = new MockCharset("testEncode_String_Normal_mock", null);
    ByteBuffer bb = c1.encode("abcdefg");
    assertEquals("abcdefg", new String(bb.array(), "iso8859-1"));
    bb = c1.encode("");
    assertEquals("", new String(bb.array(), "iso8859-1"));
  }

  public void testEncode_String_Unmappable() throws Exception {
    Charset c1 = Charset.forName("iso8859-1");
    ByteBuffer bb = c1.encode("abcd\u5D14efg");
    assertEquals(new String(bb.array(), "iso8859-1"),
                 "abcd" + new String(c1.newEncoder().replacement(), "iso8859-1") + "efg");
  }

  public void testEncode_String_NullString() {
    MockCharset c = new MockCharset("mock", null);
    try {
      c.encode((String) null);
      fail();
    } catch (NullPointerException expected) {
    }
  }

  public void testEncode_String_NullEncoder() {
    MockCharset2 c = new MockCharset2("mock2", null);
    try {
      c.encode("hehe");
      fail();
    } catch (NullPointerException expected) {
    }
  }

  public void testDecode_Normal() throws Exception {
    MockCharset c1 = new MockCharset("mock", null);
    CharBuffer cb = c1.decode(ByteBuffer.wrap("abcdefg".getBytes("iso8859-1")));
    assertEquals("abcdefg", new String(cb.array()));
    cb = c1.decode(ByteBuffer.wrap("".getBytes("iso8859-1")));
    assertEquals("", new String(cb.array()));
  }

  public void testDecode_Malformed() throws Exception {
    Charset c1 = Charset.forName("iso8859-1");
    CharBuffer cb = c1.decode(ByteBuffer.wrap("abcd\u5D14efg".getBytes("iso8859-1")));
    byte[] replacement = c1.newEncoder().replacement();
    assertEquals(new String(cb.array()).trim(), "abcd" + new String(replacement, "iso8859-1") + "efg");
  }

  public void testDecode_NullByteBuffer() {
    MockCharset c = new MockCharset("mock", null);
    try {
      c.decode(null);
      fail();
    } catch (NullPointerException expected) {
    }
  }

  public void testDecode_NullDecoder() {
    MockCharset2 c = new MockCharset2("mock2", null);
    try {
      c.decode(ByteBuffer.wrap("hehe".getBytes()));
      fail();
    } catch (NullPointerException expected) {
    }
  }

  public void testToString() {
    MockCharset c1 = new MockCharset("mock", null);
    assertTrue(-1 != c1.toString().indexOf("mock"));
  }

  static final class MockCharset extends Charset {
    public MockCharset(String canonicalName, String[] aliases) {
      super(canonicalName, aliases);
    }

    public boolean contains(Charset cs) {
      return false;
    }

    public CharsetDecoder newDecoder() {
      return new MockDecoder(this);
    }

    public CharsetEncoder newEncoder() {
      return new MockEncoder(this);
    }
  }

  static class MockCharset2 extends Charset {
    public MockCharset2(String canonicalName, String[] aliases) {
      super(canonicalName, aliases);
    }

    public boolean contains(Charset cs) {
      return false;
    }

    public CharsetDecoder newDecoder() {
      return null;
    }

    public CharsetEncoder newEncoder() {
      return null;
    }
  }

  static class MockEncoder extends java.nio.charset.CharsetEncoder {
    public MockEncoder(Charset cs) {
      super(cs, 1, 3, new byte[] { (byte) '?' });
    }

    protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
      while (in.remaining() > 0) {
        out.put((byte) in.get());
        // out.put((byte) '!');
      }
      return CoderResult.UNDERFLOW;
    }
  }

  static class MockDecoder extends java.nio.charset.CharsetDecoder {
    public MockDecoder(Charset cs) {
      super(cs, 1, 10);
    }

    protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
      while (in.remaining() > 0) {
        out.put((char) in.get());
      }
      return CoderResult.UNDERFLOW;
    }
  }


  // 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"));

    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);
  }

  // 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"));

    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"));
  }

  // 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);
  }

  public static class MockCharsetProvider extends CharsetProvider {
    public Charset charsetForName(String charsetName) {
      if ("MockCharset00".equalsIgnoreCase(charsetName) ||
          "MockCharset01".equalsIgnoreCase(charsetName) ||
          "MockCharset02".equalsIgnoreCase(charsetName)) {
        return charset1;
      } else if ("MockCharset10".equalsIgnoreCase(charsetName) ||
          "MockCharset11".equalsIgnoreCase(charsetName) ||
          "MockCharset12".equalsIgnoreCase(charsetName)) {
        return charset2;
      }
      return null;
    }

    public Iterator charsets() {
      Vector v = new Vector();
      v.add(charset1);
      v.add(charset2);
      return v.iterator();
    }
  }

  // Another mock charset provider attempting to provide the built-in charset "ascii" again.
  public static class MockCharsetProviderASCII extends CharsetProvider {
    public Charset charsetForName(String charsetName) {
      if ("US-ASCII".equalsIgnoreCase(charsetName) || "ASCII".equalsIgnoreCase(charsetName)) {
        return new MockCharset("US-ASCII", new String[] { "ASCII" });
      }
      return null;
    }

    public Iterator charsets() {
      Vector v = new Vector();
      v.add(new MockCharset("US-ASCII", new String[] { "ASCII" }));
      return v.iterator();
    }
  }
}