Supplementary.java revision 3261:a06412e13bf7
1/*
2 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 *
26 * @test
27 * @bug 4533872 4915683 4985217 5017280 6937112
28 * @summary Unit tests for supplementary character support (JSR-204)
29 */
30
31public class Supplementary {
32
33    public static void main(String[] args) {
34        test1();        // Test for codePointAt(int index)
35        test2();        // Test for codePointBefore(int index)
36        test3();        // Test for reverse()
37        test4();        // Test for appendCodePoint(int codePoint)
38        test5();        // Test for codePointCount(int beginIndex, int endIndex)
39        test6();        // Test for offsetByCodePoints(int index, int offset)
40    }
41
42    /* Text strings which are used as input data.
43     * The comment above each text string means the index of each 16-bit char
44     * for convenience.
45     */
46    static final String[] input = {
47      /*                               111     1     111111     22222
48         0123     4     5678     9     012     3     456789     01234 */
49        "abc\uD800\uDC00def\uD800\uD800ab\uD800\uDC00cdefa\uDC00bcdef",
50      /*                          1     1111     1111     1     222
51         0     12345     6789     0     1234     5678     9     012     */
52        "\uD800defg\uD800hij\uD800\uDC00klm\uDC00nop\uDC00\uD800rt\uDC00",
53      /*                          11     1     1111     1     112     222
54         0     12345     6     78901     2     3456     7     890     123     */
55        "\uDC00abcd\uDBFF\uDFFFefgh\uD800\uDC009ik\uDC00\uDC00lm\uDC00no\uD800",
56      /*                                    111     111111     1 22     2
57         0     1     2345     678     9     012     345678     9 01     2     */
58        "\uD800\uDC00!#$\uD800%&\uD800\uDC00;+\uDC00<>;=^\uDC00\\@\uD800\uDC00",
59
60        // includes an undefined supplementary character in Unicode 4.0.0
61      /*                                    1     11     1     1111     1
62         0     1     2345     6     789     0     12     3     4567     8     */
63        "\uDB40\uDE00abc\uDE01\uDB40de\uDB40\uDE02f\uDB40\uDE03ghi\uDB40\uDE02",
64    };
65
66
67    /* Expected results for:
68     *     test1(): for codePointAt()
69     *
70     * Each character in each array is the golden data for each text string
71     * in the above input data. For example, the first data in each array is
72     * for the first input string.
73     */
74    static final int[][] golden1 = {
75        {'a',    0xD800, 0xDC00,  0x10000, 0xE0200}, // codePointAt(0)
76        {0xD800, 0x10000, 'g',    0xDC00,  0xE0202}, // codePointAt(9)
77        {'f',    0xDC00,  0xD800, 0xDC00,  0xDE02},  // codePointAt(length-1)
78    };
79
80    /*
81     * Test for codePointAt(int index) method
82     */
83    static void test1() {
84
85        for (int i = 0; i < input.length; i++) {
86            StringBuffer sb = new StringBuffer(input[i]);
87
88            /*
89             * Normal case
90             */
91            testCodePoint(At, sb, 0, golden1[0][i]);
92            testCodePoint(At, sb, 9, golden1[1][i]);
93            testCodePoint(At, sb, sb.length()-1, golden1[2][i]);
94
95            /*
96             * Abnormal case - verify that an exception is thrown.
97             */
98            testCodePoint(At, sb, -1);
99            testCodePoint(At, sb, sb.length());
100        }
101    }
102
103
104    /* Expected results for:
105     *     test2(): for codePointBefore()
106     *
107     * Each character in each array is the golden data for each text string
108     * in the above input data. For example, the first data in each array is
109     * for the first input string.
110     */
111    static final int[][] golden2 = {
112        {'a',    0xD800, 0xDC00,  0xD800,  0xDB40},  // codePointBefore(1)
113        {0xD800, 'l',    0x10000, 0xDC00,  0xDB40},  // codePointBefore(13)
114        {'f',    0xDC00, 0xD800,  0x10000, 0xE0202}, // codePointBefore(length)
115    };
116
117    /*
118     * Test for codePointBefore(int index) method
119     */
120    static void test2() {
121
122        for (int i = 0; i < input.length; i++) {
123            StringBuffer sb = new StringBuffer(input[i]);
124
125            /*
126             * Normal case
127             */
128            testCodePoint(Before, sb, 1, golden2[0][i]);
129            testCodePoint(Before, sb, 13, golden2[1][i]);
130            testCodePoint(Before, sb, sb.length(), golden2[2][i]);
131
132            /*
133             * Abnormal case - verify that an exception is thrown.
134             */
135            testCodePoint(Before, sb, 0);
136            testCodePoint(Before, sb, sb.length()+1);
137        }
138    }
139
140
141    /* Expected results for:
142     *     test3(): for reverse()
143     *
144     * Unlike golden1 and golden2, each array is the golden data for each text
145     * string in the above input data. For example, the first array is  for
146     * the first input string.
147     */
148    static final String[] golden3 = {
149        "fedcb\uDC00afedc\uD800\uDC00ba\uD800\uD800fed\uD800\uDC00cba",
150        "\uDC00tr\uD800\uDC00pon\uDC00mlk\uD800\uDC00jih\uD800gfed\uD800",
151        "\uD800on\uDC00ml\uDC00\uDC00ki9\uD800\uDC00hgfe\uDBFF\uDFFFdcba\uDC00",
152        "\uD800\uDC00@\\\uDC00^=;><\uDC00+;\uD800\uDC00&%\uD800$#!\uD800\uDC00",
153
154        // includes an undefined supplementary character in Unicode 4.0.0
155        "\uDB40\uDE02ihg\uDB40\uDE03f\uDB40\uDE02ed\uDB40\uDE01cba\uDB40\uDE00",
156    };
157
158    // Additional input data & expected result for test3()
159    static final String[][] testdata1 = {
160        {"a\uD800\uDC00", "\uD800\uDC00a"},
161        {"a\uDC00\uD800", "\uD800\uDC00a"},
162        {"\uD800\uDC00a", "a\uD800\uDC00"},
163        {"\uDC00\uD800a", "a\uD800\uDC00"},
164        {"\uDC00\uD800\uD801", "\uD801\uD800\uDC00"},
165        {"\uDC00\uD800\uDC01", "\uD800\uDC01\uDC00"},
166        {"\uD801\uD800\uDC00", "\uD800\uDC00\uD801"},
167        {"\uD800\uDC01\uDC00", "\uDC00\uD800\uDC01"},
168        {"\uD800\uDC00\uDC01\uD801", "\uD801\uDC01\uD800\uDC00"},
169    };
170
171    /*
172     * Test for reverse() method
173     */
174    static void test3() {
175        for (int i = 0; i < input.length; i++) {
176            StringBuffer sb = new StringBuffer(input[i]).reverse();
177
178            check(!golden3[i].equals(new String(sb)),
179                 "reverse() for <" + toHexString(input[i]) + ">",
180                 sb, golden3[i]);
181        }
182
183        for (int i = 0; i < testdata1.length; i++) {
184            StringBuffer sb = new StringBuffer(testdata1[i][0]).reverse();
185
186            check(!testdata1[i][1].equals(new String(sb)),
187                 "reverse() for <" + toHexString(testdata1[i][0]) + ">",
188                 sb, testdata1[i][1]);
189        }
190    }
191
192    /**
193     * Test for appendCodePoint() method
194     */
195    static void test4() {
196        for (int i = 0; i < input.length; i++) {
197            String s = input[i];
198            StringBuffer sb = new StringBuffer();
199            int c;
200            for (int j = 0; j < s.length(); j += Character.charCount(c)) {
201                c = s.codePointAt(j);
202                StringBuffer rsb = sb.appendCodePoint(c);
203                check(sb != rsb, "appendCodePoint returned a wrong object");
204                int sbc = sb.codePointAt(j);
205                check(sbc != c, "appendCodePoint(j) != c", sbc, c);
206            }
207            check(!s.equals(sb.toString()),
208                  "appendCodePoint() produced a wrong result with input["+i+"]");
209        }
210
211        // test exception
212        testAppendCodePoint(-1, IllegalArgumentException.class);
213        testAppendCodePoint(Character.MAX_CODE_POINT+1, IllegalArgumentException.class);
214    }
215
216    /**
217     * Test codePointCount(int, int)
218     *
219     * This test case assumes that
220     * Character.codePointCount(CharSequence, int, int) works
221     * correctly.
222     */
223    static void test5() {
224        for (int i = 0; i < input.length; i++) {
225            String s = input[i];
226            StringBuffer sb = new StringBuffer(s);
227            int length = sb.length();
228            for (int j = 0; j <= length; j++) {
229                int result = sb.codePointCount(j, length);
230                int expected = Character.codePointCount(sb, j, length);
231                check(result != expected, "codePointCount(input["+i+"], "+j+", "+length+")",
232                      result, expected);
233            }
234            for (int j = length; j >= 0; j--) {
235                int result = sb.codePointCount(0, j);
236                int expected = Character.codePointCount(sb, 0, j);
237                check(result != expected, "codePointCount(input["+i+"], 0, "+j+")",
238                      result, expected);
239            }
240
241            // test exceptions
242            testCodePointCount(null, 0, 0, NullPointerException.class);
243            testCodePointCount(sb, -1, length, IndexOutOfBoundsException.class);
244            testCodePointCount(sb, 0, length+1, IndexOutOfBoundsException.class);
245            testCodePointCount(sb, length, length-1, IndexOutOfBoundsException.class);
246        }
247    }
248
249    /**
250     * Test offsetByCodePoints(int, int)
251     *
252     * This test case assumes that
253     * Character.codePointCount(CharSequence, int, int) works
254     * correctly.
255     */
256    static void test6() {
257        for (int i = 0; i < input.length; i++) {
258            String s = input[i];
259            StringBuffer sb = new StringBuffer(s);
260            int length = s.length();
261            for (int j = 0; j <= length; j++) {
262                int nCodePoints = Character.codePointCount(sb, j, length);
263                int result = sb.offsetByCodePoints(j, nCodePoints);
264                check(result != length,
265                      "offsetByCodePoints(input["+i+"], "+j+", "+nCodePoints+")",
266                      result, length);
267                result = sb.offsetByCodePoints(length, -nCodePoints);
268                int expected = j;
269                if (j > 0 && j < length) {
270                    int cp = sb.codePointBefore(j+1);
271                    if (Character.isSupplementaryCodePoint(cp)) {
272                        expected--;
273                    }
274                }
275                check(result != expected,
276                      "offsetByCodePoints(input["+i+"], "+j+", "+(-nCodePoints)+")",
277                      result, expected);
278            }
279            for (int j = length; j >= 0; j--) {
280                int nCodePoints = Character.codePointCount(sb, 0, j);
281                int result = sb.offsetByCodePoints(0, nCodePoints);
282                int expected = j;
283                if (j > 0 && j < length) {
284                    int cp = sb.codePointAt(j-1);
285                     if (Character.isSupplementaryCodePoint(cp)) {
286                        expected++;
287                    }
288                }
289                check(result != expected,
290                      "offsetByCodePoints(input["+i+"], 0, "+nCodePoints+")",
291                      result, expected);
292                result = sb.offsetByCodePoints(j, -nCodePoints);
293                check(result != 0,
294                      "offsetBycodePoints(input["+i+"], "+j+", "+(-nCodePoints)+")",
295                      result, 0);
296            }
297
298            // test exceptions
299            testOffsetByCodePoints(null, 0, 0, NullPointerException.class);
300            testOffsetByCodePoints(sb, -1, length, IndexOutOfBoundsException.class);
301            testOffsetByCodePoints(sb, 0, length+1, IndexOutOfBoundsException.class);
302            testOffsetByCodePoints(sb, 1, -2, IndexOutOfBoundsException.class);
303            testOffsetByCodePoints(sb, length, length-1, IndexOutOfBoundsException.class);
304            testOffsetByCodePoints(sb, length, -(length+1), IndexOutOfBoundsException.class);
305        }
306    }
307
308
309    static final boolean At = true, Before = false;
310
311    static void testCodePoint(boolean isAt, StringBuffer sb, int index, int expected) {
312        int c = isAt ? sb.codePointAt(index) : sb.codePointBefore(index);
313
314        check(c != expected,
315              "codePoint" + (isAt ? "At" : "Before") + "(" + index + ") for <"
316              + sb + ">", c, expected);
317    }
318
319    static void testCodePoint(boolean isAt, StringBuffer sb, int index) {
320        boolean exceptionOccurred = false;
321
322        try {
323            int c = isAt ? sb.codePointAt(index) : sb.codePointBefore(index);
324        }
325        catch (StringIndexOutOfBoundsException e) {
326            exceptionOccurred = true;
327        }
328        check(!exceptionOccurred,
329              "codePoint" + (isAt ? "At" : "Before") + "(" + index + ") for <"
330              + sb + "> should throw StringIndexOutOfBoundsPointerException.");
331    }
332
333    static void testAppendCodePoint(int codePoint, Class expectedException) {
334        try {
335            new StringBuffer().appendCodePoint(codePoint);
336        } catch (Exception e) {
337            if (expectedException.isInstance(e)) {
338                return;
339            }
340            throw new RuntimeException("Error: Unexpected exception", e);
341        }
342        check(true, "appendCodePoint(" + toHexString(codePoint) + ") didn't throw "
343              + expectedException.getName());
344    }
345
346    static void testCodePointCount(StringBuffer sb, int beginIndex, int endIndex,
347                                   Class expectedException) {
348        try {
349            int n = sb.codePointCount(beginIndex, endIndex);
350        } catch (Exception e) {
351            if (expectedException.isInstance(e)) {
352                return;
353            }
354            throw new RuntimeException("Error: Unexpected exception", e);
355        }
356        check(true, "codePointCount() didn't throw " + expectedException.getName());
357    }
358
359    static void testOffsetByCodePoints(StringBuffer sb, int index, int offset,
360                                       Class expectedException) {
361        try {
362            int n = sb.offsetByCodePoints(index, offset);
363        } catch (Exception e) {
364            if (expectedException.isInstance(e)) {
365                return;
366            }
367            throw new RuntimeException("Error: Unexpected exception", e);
368        }
369        check(true, "offsetByCodePoints() didn't throw " + expectedException.getName());
370    }
371
372    static void check(boolean err, String msg) {
373        if (err) {
374            throw new RuntimeException("Error: " + msg);
375        }
376    }
377
378    static void check(boolean err, String s, int got, int expected) {
379        if (err) {
380            throw new RuntimeException("Error: " + s
381                                       + " returned an unexpected value. got "
382                                       + toHexString(got)
383                                       + ", expected "
384                                       + toHexString(expected));
385        }
386    }
387
388    static void check(boolean err, String s, StringBuffer got, String expected) {
389        if (err) {
390            throw new RuntimeException("Error: " + s
391                                       + " returned an unexpected value. got <"
392                                       + toHexString(new String(got))
393                                       + ">, expected <"
394                                       + toHexString(expected)
395                                       + ">");
396        }
397    }
398
399    private static String toHexString(int c) {
400        return "0x" + Integer.toHexString(c);
401    }
402
403    private static String toHexString(String s) {
404        StringBuffer sb = new StringBuffer();
405        for (int i = 0; i < s.length(); i++) {
406            char c = s.charAt(i);
407
408            sb.append(" 0x");
409            if (c < 0x10) sb.append('0');
410            if (c < 0x100) sb.append('0');
411            if (c < 0x1000) sb.append('0');
412            sb.append(Integer.toHexString(c));
413        }
414        sb.append(' ');
415        return sb.toString();
416    }
417}
418