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