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