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