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 import org.testng.annotations.Test; 25 26 import java.lang.reflect.Method; 27 import java.foreign.annotations.NativeHeader; 28 import java.foreign.annotations.NativeLocation; 29 import java.foreign.memory.Pointer; 30 import java.nio.file.Files; 31 import java.nio.file.Path; 32 import java.util.Arrays; 33 import java.util.List; 34 35 import static org.testng.Assert.assertEquals; 36 import static org.testng.Assert.assertFalse; 37 import static org.testng.Assert.assertNotNull; 38 import static org.testng.Assert.assertNull; 39 import static org.testng.Assert.assertTrue; 40 41 /* 42 * @test 43 * @modules jdk.jextract 44 * @build JextractToolProviderTest 45 * @run testng/othervm -Duser.language=en JextractToolProviderTest 46 */ 47 public class JextractToolProviderTest extends JextractToolRunner { 48 @Test 49 public void testHelp() { 50 checkFailure(null); // no options 51 checkSuccess(null, "--help"); 52 checkSuccess(null, "-h"); 53 checkSuccess(null, "-?"); 54 } 55 56 // error for non-existent header file 57 @Test 58 public void testNonExistentHeader() { 59 checkFailure("Cannot open header file", "--dry-run", 60 getInputFilePath("non_existent.h").toString()); 61 } 62 63 @Test 64 public void testDryRun() { 65 // only dry-run, don't produce any output 66 Path simpleJar = getOutputFilePath("simple.jar"); 67 deleteFile(simpleJar); 68 checkSuccess(null, "--dry-run", getInputFilePath("simple.h").toString()); 69 try { 70 assertFalse(Files.isRegularFile(simpleJar)); 71 } finally { 72 deleteFile(simpleJar); 73 } 74 } 75 76 @Test 77 public void testOutputFileOption() { 78 // simple output file check 79 Path simpleJar = getOutputFilePath("simple.jar"); 80 deleteFile(simpleJar); 81 checkSuccess(null, "-o", simpleJar.toString(), 82 getInputFilePath("simple.h").toString()); 83 try { 84 assertTrue(Files.isRegularFile(simpleJar)); 85 } finally { 86 deleteFile(simpleJar); 87 } 88 } 89 90 @Test 91 public void testOutputClass() { 92 Path helloJar = getOutputFilePath("hello.jar"); 93 deleteFile(helloJar); 94 Path helloH = getInputFilePath("hello.h"); 95 checkSuccess(null, "-o", helloJar.toString(), helloH.toString()); 96 try { 97 Class<?> cls = loadClass("hello", helloJar); 98 // check NativeHeader annotation 99 NativeHeader header = cls.getAnnotation(NativeHeader.class); 100 assertNotNull(header); 101 assertEquals(header.path(), helloH.toString()); 102 assertFalse(header.declarations().isEmpty()); 103 104 // check a method for "void func()" 105 assertNotNull(findMethod(cls, "func", Object[].class)); 106 } finally { 107 deleteFile(helloJar); 108 } 109 } 110 111 private void testTargetPackage(String targetPkgOption) { 112 Path helloJar = getOutputFilePath("hello.jar"); 113 deleteFile(helloJar); 114 Path helloH = getInputFilePath("hello.h"); 115 checkSuccess(null, targetPkgOption, "com.acme", "-o", helloJar.toString(), helloH.toString()); 116 try { 117 Class<?> cls = loadClass("com.acme.hello", helloJar); 118 // check NativeHeader annotation 119 NativeHeader header = cls.getAnnotation(NativeHeader.class); 120 assertNotNull(header); 121 assertEquals(header.path(), helloH.toString()); 122 123 // check a method for "void func()" 124 assertNotNull(findMethod(cls, "func", Object[].class)); 125 } finally { 126 deleteFile(helloJar); 127 } 128 } 129 130 @Test 131 public void testTargetPackageOption() { 132 testTargetPackage("-t"); 133 } 134 135 @Test 136 public void testTargetPackageLongOption() { 137 testTargetPackage("--target-package"); 138 } 139 140 private void testPackageMapping(String pkgMapOption) { 141 Path worldJar = getOutputFilePath("world.jar"); 142 deleteFile(worldJar); 143 Path mytypesJar = getOutputFilePath("mytypes.jar"); 144 deleteFile(mytypesJar); 145 146 Path worldH = getInputFilePath("world.h"); 147 Path include = getInputFilePath("include"); 148 // generate jar for mytypes.h 149 checkSuccess(null, "-t", "com.acme", "-o", mytypesJar.toString(), 150 include.resolve("mytypes.h").toString()); 151 // world.h include mytypes.h, use appropriate package for stuff from mytypes.h 152 checkSuccess(null, "-I", include.toString(), pkgMapOption, include.toString() + "=com.acme", 153 "-o", worldJar.toString(), worldH.toString()); 154 try { 155 Class<?> cls = loadClass("world", worldJar, mytypesJar); 156 Method m = findFirstMethod(cls, "distance"); 157 Class<?>[] params = m.getParameterTypes(); 158 assertEquals(params[0].getName(), "com.acme.mytypes$Point"); 159 } finally { 160 deleteFile(worldJar); 161 deleteFile(mytypesJar); 162 } 163 } 164 165 @Test 166 public void testPackageDirMappingOption() { 167 testPackageMapping("-m"); 168 } 169 170 @Test 171 public void testPackageDirMappingLongOption() { 172 testPackageMapping("--package-map"); 173 } 174 175 @Test 176 public void test_no_input_files() { 177 String err = "No input files"; 178 checkFailure(err, "-L", "foo"); 179 } 180 181 @Test 182 public void test_option_L_without_l() { 183 Path helloJar = getOutputFilePath("hello.jar"); 184 deleteFile(helloJar); 185 Path helloH = getInputFilePath("hello.h"); 186 Path linkDir = getInputFilePath("libs"); 187 String warning = "WARNING: -L option specified without any -l option"; 188 checkSuccess(warning, "-L", linkDir.toString(), "-o", helloJar.toString(), helloH.toString()); 189 } 190 191 @Test 192 public void test_option_rpath_without_l() { 193 Path helloJar = getOutputFilePath("hello.jar"); 194 deleteFile(helloJar); 195 Path helloH = getInputFilePath("hello.h"); 196 Path rpathDir = getInputFilePath("libs"); 197 String warning = "WARNING: -rpath option specified without any -l option"; 198 try { 199 checkSuccess(warning, "-rpath", rpathDir.toString(), "-o", helloJar.toString(), helloH.toString()); 200 } finally { 201 deleteFile(helloJar); 202 } 203 } 204 205 @Test 206 public void test_option_conflicting_rpaths() { 207 Path helloJar = getOutputFilePath("hello.jar"); 208 deleteFile(helloJar); 209 Path helloH = getInputFilePath("hello.h"); 210 Path rpathDir = getInputFilePath("libs"); 211 String warning = "WARNING: -infer-rpath used in conjunction with explicit -rpath paths"; 212 try { 213 checkSuccess(warning, "-rpath", rpathDir.toString(), 214 "-infer-rpath", 215 "-o", helloJar.toString(), helloH.toString()); 216 } finally { 217 deleteFile(helloJar); 218 } 219 } 220 221 @Test 222 public void test_option_rpath_no_libs() { 223 Path helloJar = getOutputFilePath("hello.jar"); 224 deleteFile(helloJar); 225 Path helloH = getInputFilePath("hello.h"); 226 String warning = "WARNING: -infer-rpath option specified without any -L option"; 227 try { 228 checkSuccess(warning, 229 "-infer-rpath", 230 "-o", helloJar.toString(), helloH.toString()); 231 } finally { 232 deleteFile(helloJar); 233 } 234 } 235 236 @Test 237 public void test_option_l_no_crash_missing_lib() { 238 Path helloJar = getOutputFilePath("hello.jar"); 239 deleteFile(helloJar); 240 Path helloH = getInputFilePath("hello.h"); 241 String warning = "WARNING: Some library names could not be resolved"; 242 try { 243 checkSuccess(warning, 244 "-L", "nonExistent", 245 "-l", "nonExistent", 246 "-o", helloJar.toString(), helloH.toString()); 247 } finally { 248 deleteFile(helloJar); 249 } 250 } 251 252 @Test 253 public void test_option_l() { 254 Path helloJar = getOutputFilePath("hello.jar"); 255 deleteFile(helloJar); 256 Path helloH = getInputFilePath("hello.h"); 257 checkSuccess(null, "-l", "hello", "-o", helloJar.toString(), helloH.toString()); 258 try { 259 Class<?> cls = loadClass("hello", helloJar); 260 // check that NativeHeader annotation captures -l value 261 NativeHeader header = cls.getAnnotation(NativeHeader.class); 262 assertNotNull(header); 263 assertEquals(header.libraries().length, 1); 264 assertEquals(header.libraries()[0], "hello"); 265 // no library paths (rpath) set 266 assertEquals(header.libraryPaths().length, 0); 267 } finally { 268 deleteFile(helloJar); 269 } 270 } 271 272 @Test 273 public void test_option_l_and_rpath() { 274 Path helloJar = getOutputFilePath("hello.jar"); 275 deleteFile(helloJar); 276 Path helloH = getInputFilePath("hello.h"); 277 Path rpathDir = getInputFilePath("libs"); 278 checkSuccess(null, "-l", "hello", "-rpath", rpathDir.toString(), 279 "-o", helloJar.toString(), helloH.toString()); 280 try { 281 Class<?> cls = loadClass("hello", helloJar); 282 // check that NativeHeader annotation captures -l and -rpath values 283 NativeHeader header = cls.getAnnotation(NativeHeader.class); 284 assertNotNull(header); 285 assertEquals(header.libraries().length, 1); 286 assertEquals(header.libraries()[0], "hello"); 287 assertEquals(header.libraryPaths().length, 1); 288 assertEquals(header.libraryPaths()[0], rpathDir.toString()); 289 } finally { 290 deleteFile(helloJar); 291 } 292 } 293 294 @Test 295 public void testUnionDeclaration() { 296 Path uniondeclJar = getOutputFilePath("uniondecl.jar"); 297 deleteFile(uniondeclJar); 298 Path uniondeclH = getInputFilePath("uniondecl.h"); 299 try { 300 checkSuccess(null, "-o", uniondeclJar.toString(), uniondeclH.toString()); 301 Class<?> unionCls = loadClass("uniondecl", uniondeclJar); 302 assertNotNull(unionCls); 303 boolean found = Arrays.stream(unionCls.getClasses()). 304 map(Class::getSimpleName). 305 filter(n -> n.equals("IntOrFloat")). 306 findFirst().isPresent(); 307 assertTrue(found, "uniondecl.IntOrFloat not found"); 308 } finally { 309 deleteFile(uniondeclJar); 310 } 311 } 312 313 private void testEnumConstGetters(Class<?> enumCls, List<String> names) { 314 for (String name : names) { 315 if (findEnumConstGet(enumCls, name) == null) { 316 throw new RuntimeException(enumCls.getName() + " misses " + name); 317 } 318 } 319 } 320 321 @Test 322 public void testAnonymousEnum() { 323 Path anonenumJar = getOutputFilePath("anonenum.jar"); 324 deleteFile(anonenumJar); 325 Path anonenumH = getInputFilePath("anonenum.h"); 326 try { 327 checkSuccess(null, "-o", anonenumJar.toString(), anonenumH.toString()); 328 Class<?> anonenumCls = loadClass("anonenum", anonenumJar); 329 assertNotNull(anonenumCls); 330 testEnumConstGetters(anonenumCls, List.of("RED", "GREEN", "BLUE")); 331 testEnumConstGetters(anonenumCls, List.of( 332 "Java", "C", "CPP", "Python", "Ruby")); 333 testEnumConstGetters(anonenumCls, List.of( 334 "XS", "S", "M", "L", "XL", "XXL")); 335 testEnumConstGetters(anonenumCls, List.of( 336 "ONE", "TWO")); 337 338 Class<?> enumClz[] = anonenumCls.getClasses(); 339 assert(enumClz.length >= 4); 340 341 Class<?> enumCls = findClass(enumClz, "codetype_t"); 342 assertNotNull(enumCls); 343 344 enumCls = findClass(enumClz, "SIZE"); 345 assertNotNull(enumCls); 346 347 enumCls = findClass(enumClz, "temp"); 348 assertNotNull(enumCls); 349 350 enumCls = findClass(enumClz, "temp_t"); 351 assertNotNull(enumCls); 352 } finally { 353 deleteFile(anonenumJar); 354 } 355 } 356 357 @Test 358 public void testExcludeSymbols() { 359 Path helloJar = getOutputFilePath("hello.jar"); 360 deleteFile(helloJar); 361 Path helloH = getInputFilePath("hello.h"); 362 checkSuccess(null, "-o", helloJar.toString(), helloH.toString()); 363 try { 364 Class<?> cls = loadClass("hello", helloJar); 365 // check a method for "void func()" 366 assertNotNull(findMethod(cls, "func", Object[].class)); 367 assertNotNull(findMethod(cls, "func2", Object[].class)); 368 assertNotNull(findMethod(cls, "func3", Object[].class)); 369 // check a method for "void junk()" 370 assertNotNull(findMethod(cls, "junk", Object[].class)); 371 assertNotNull(findMethod(cls, "junk2", Object[].class)); 372 assertNotNull(findMethod(cls, "junk3", Object[].class)); 373 } finally { 374 deleteFile(helloJar); 375 } 376 377 // try with --exclude-symbols" this time. 378 checkSuccess(null, "--exclude-symbols", "junk.*", "-o", helloJar.toString(), helloH.toString()); 379 try { 380 Class<?> cls = loadClass("hello", helloJar); 381 // check a method for "void func()" 382 assertNotNull(findMethod(cls, "func", Object[].class)); 383 assertNotNull(findMethod(cls, "func2", Object[].class)); 384 assertNotNull(findMethod(cls, "func3", Object[].class)); 385 // check a method for "void junk()" 386 assertNull(findMethod(cls, "junk", Object[].class)); 387 assertNull(findMethod(cls, "junk2", Object[].class)); 388 assertNull(findMethod(cls, "junk3", Object[].class)); 389 } finally { 390 deleteFile(helloJar); 391 } 392 } 393 394 @Test 395 public void testIncludeSymbols() { 396 Path helloJar = getOutputFilePath("hello.jar"); 397 deleteFile(helloJar); 398 Path helloH = getInputFilePath("hello.h"); 399 checkSuccess(null, "-o", helloJar.toString(), helloH.toString()); 400 try { 401 Class<?> cls = loadClass("hello", helloJar); 402 // check a method for "void func()" 403 assertNotNull(findMethod(cls, "func", Object[].class)); 404 assertNotNull(findMethod(cls, "func2", Object[].class)); 405 assertNotNull(findMethod(cls, "func3", Object[].class)); 406 // check a method for "void junk()" 407 assertNotNull(findMethod(cls, "junk", Object[].class)); 408 assertNotNull(findMethod(cls, "junk2", Object[].class)); 409 assertNotNull(findMethod(cls, "junk3", Object[].class)); 410 } finally { 411 deleteFile(helloJar); 412 } 413 414 // try with --include-symbols" this time. 415 checkSuccess(null, "--include-symbols", "junk.*", "-o", helloJar.toString(), helloH.toString()); 416 try { 417 Class<?> cls = loadClass("hello", helloJar); 418 // check a method for "void junk()" 419 assertNotNull(findMethod(cls, "junk", Object[].class)); 420 assertNotNull(findMethod(cls, "junk2", Object[].class)); 421 assertNotNull(findMethod(cls, "junk3", Object[].class)); 422 // check a method for "void func()" 423 assertNull(findMethod(cls, "func", Object[].class)); 424 assertNull(findMethod(cls, "func2", Object[].class)); 425 assertNull(findMethod(cls, "func3", Object[].class)); 426 } finally { 427 deleteFile(helloJar); 428 } 429 } 430 431 @Test 432 public void testNoLocations() { 433 Path simpleJar = getOutputFilePath("simple.jar"); 434 deleteFile(simpleJar); 435 Path simpleH = getInputFilePath("simple.h"); 436 checkSuccess(null, "--no-locations", "-o", simpleJar.toString(), simpleH.toString()); 437 try { 438 Class<?> simpleCls = loadClass("simple", simpleJar); 439 Method func = findFirstMethod(simpleCls, "func"); 440 assertFalse(func.isAnnotationPresent(NativeLocation.class)); 441 Class<?> anonymousCls = loadClass("simple$anonymous", simpleJar); 442 assertFalse(simpleCls.isAnnotationPresent(NativeLocation.class)); 443 } finally { 444 deleteFile(simpleJar); 445 } 446 } 447 448 @Test 449 public void testIncludeExcludeSymbols() { 450 Path helloJar = getOutputFilePath("hello.jar"); 451 deleteFile(helloJar); 452 Path helloH = getInputFilePath("hello.h"); 453 checkSuccess(null, "-o", helloJar.toString(), helloH.toString()); 454 try { 455 Class<?> cls = loadClass("hello", helloJar); 456 // check a method for "void func()" 457 assertNotNull(findMethod(cls, "func", Object[].class)); 458 assertNotNull(findMethod(cls, "func2", Object[].class)); 459 assertNotNull(findMethod(cls, "func3", Object[].class)); 460 // check a method for "void junk()" 461 assertNotNull(findMethod(cls, "junk", Object[].class)); 462 assertNotNull(findMethod(cls, "junk2", Object[].class)); 463 assertNotNull(findMethod(cls, "junk3", Object[].class)); 464 } finally { 465 deleteFile(helloJar); 466 } 467 468 // try with --include-symbols" this time. 469 checkSuccess(null, "--include-symbols", "junk.*", "--exclude-symbols", "junk3", 470 "-o", helloJar.toString(), helloH.toString()); 471 try { 472 Class<?> cls = loadClass("hello", helloJar); 473 // check a method for "void junk()" 474 assertNotNull(findMethod(cls, "junk", Object[].class)); 475 assertNotNull(findMethod(cls, "junk2", Object[].class)); 476 // check a method for "void func()" - not included 477 assertNull(findMethod(cls, "func", Object[].class)); 478 assertNull(findMethod(cls, "func2", Object[].class)); 479 assertNull(findMethod(cls, "func3", Object[].class)); 480 // excluded among the included set! 481 assertNull(findMethod(cls, "junk3", Object[].class)); 482 } finally { 483 deleteFile(helloJar); 484 } 485 } 486 487 @Test 488 public void testNestedStructsUnions() { 489 Path nestedJar = getOutputFilePath("nested.jar"); 490 deleteFile(nestedJar); 491 Path nestedH = getInputFilePath("nested.h"); 492 try { 493 checkSuccess(null, "-o", nestedJar.toString(), nestedH.toString()); 494 Class<?> headerCls = loadClass("nested", nestedJar); 495 assertNotNull(headerCls); 496 497 Class<?> fooCls = loadClass("nested$Foo", nestedJar); 498 assertNotNull(fooCls); 499 // struct Foo has no getters for "x", "y" etc. 500 assertNull(findStructFieldGet(fooCls, "x")); 501 assertNull(findStructFieldGet(fooCls, "y")); 502 // struct Foo has getters for bar and color 503 assertNotNull(findStructFieldGet(fooCls, "bar")); 504 assertNotNull(findStructFieldGet(fooCls, "color")); 505 // make sure nested types are handled without nested namespace! 506 assertNotNull(loadClass("nested$Bar", nestedJar)); 507 assertNotNull(loadClass("nested$Color", nestedJar)); 508 509 Class<?> uCls = loadClass("nested$U", nestedJar); 510 assertNotNull(uCls); 511 // union U has no getters for "x", "y" etc. 512 assertNull(findStructFieldGet(uCls, "x")); 513 assertNull(findStructFieldGet(uCls, "y")); 560 assertNotNull(findStructFieldGet(myUnionCls, "k")); 561 // "A", "B", "C" are enum constants -should not be in MyUnion 562 assertNull(findStructFieldGet(myUnionCls, "A")); 563 assertNull(findStructFieldGet(myUnionCls, "B")); 564 assertNull(findStructFieldGet(myUnionCls, "C")); 565 // anonymous enum constants are hoisted to containing scope 566 assertNotNull(findEnumConstGet(headerCls, "A")); 567 assertNotNull(findEnumConstGet(headerCls, "B")); 568 assertNotNull(findEnumConstGet(headerCls, "C")); 569 } finally { 570 deleteFile(nestedJar); 571 } 572 } 573 574 @Test 575 public void testAnonymousStructTypeGlobalVar() { 576 Path elaboratedTypeJar = getOutputFilePath("elaboratedtype.jar"); 577 deleteFile(elaboratedTypeJar); 578 Path elaboratedTypeH = getInputFilePath("elaboratedtype.h"); 579 try { 580 checkSuccess(null, "-o", elaboratedTypeJar.toString(), elaboratedTypeH.toString()); 581 Class<?> headerCls = loadClass("elaboratedtype", elaboratedTypeJar); 582 assertNotNull(findGlobalVariableGet(headerCls, "point")); 583 assertNotNull(findGlobalVariableGet(headerCls, "long_or_int")); 584 assertNotNull(findMethod(headerCls, "func", Pointer.class)); 585 } finally { 586 deleteFile(elaboratedTypeJar); 587 } 588 } 589 590 private void testBuiltinInclude(String name) { 591 Path fileJar = getOutputFilePath(name + "inc.jar"); 592 deleteFile(fileJar); 593 Path fileH = getInputFilePath(name + "inc.h"); 594 checkSuccess(null, "-o", fileJar.toString(), fileH.toString()); 595 deleteFile(fileJar); 596 } 597 598 @Test 599 public void testBuiltinHeader() { 600 testBuiltinInclude("stdarg"); 601 testBuiltinInclude("stdbool"); 602 } 603 604 @Test 605 public void testGlobalFuncPointerCallback() { 606 Path globalFuncPointerJar = getOutputFilePath("globalFuncPointer.jar"); 607 deleteFile(globalFuncPointerJar); 608 Path globalFuncPointerH = getInputFilePath("globalFuncPointer.h"); 609 checkSuccess(null, "-o", globalFuncPointerJar.toString(), globalFuncPointerH.toString()); 610 Class<?> callbackCls = loadClass("globalFuncPointer$FI1", globalFuncPointerJar); 611 Method callback = findFirstMethod(callbackCls, "fn"); 612 assertNotNull(callback); 613 assertTrue(callback.isVarArgs()); 614 deleteFile(globalFuncPointerJar); 615 } 616 617 @Test 618 public void testFuncPtrTypedef() { 619 Path funcPtrTypedefJar = getOutputFilePath("funcPtrTypedef.jar"); 620 deleteFile(funcPtrTypedefJar); 621 Path funcPtrTypedefH = getInputFilePath("funcPtrTypedef.h"); 622 checkSuccess(null, "-o", funcPtrTypedefJar.toString(), funcPtrTypedefH.toString()); 623 // force parsing of class, method 624 Class<?> headerCls = loadClass("funcPtrTypedef", funcPtrTypedefJar); 625 Method getter = findFirstMethod(headerCls, "my_function$get"); 626 assertNotNull(getter); 627 assertNotNull(getter.getGenericParameterTypes()); 628 deleteFile(funcPtrTypedefJar); 629 } 630 631 @Test 632 public void testDuplicatedecls() { 633 Path duplicatedeclsJar = getOutputFilePath("duplicatedecls.jar"); 634 deleteFile(duplicatedeclsJar); 635 Path duplicatedeclsH = getInputFilePath("duplicatedecls.h"); 636 checkSuccess(null, "-o", duplicatedeclsJar.toString(), duplicatedeclsH.toString()); 637 // load the class to make sure no duplicate methods generated in it 638 Class<?> headerCls = loadClass("duplicatedecls", duplicatedeclsJar); 639 assertNotNull(headerCls); 640 deleteFile(duplicatedeclsJar); 641 } 642 } | 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 import org.testng.annotations.Test; 25 26 import java.lang.reflect.Field; 27 import java.lang.reflect.Method; 28 import java.foreign.annotations.NativeHeader; 29 import java.foreign.annotations.NativeLocation; 30 import java.foreign.memory.Pointer; 31 import java.nio.file.Files; 32 import java.nio.file.Path; 33 import java.util.Arrays; 34 import java.util.List; 35 import java.util.stream.Stream; 36 37 import static org.testng.Assert.assertEquals; 38 import static org.testng.Assert.assertFalse; 39 import static org.testng.Assert.assertNotNull; 40 import static org.testng.Assert.assertNull; 41 import static org.testng.Assert.assertTrue; 42 43 /* 44 * @test 45 * @modules jdk.jextract 46 * @build JextractToolProviderTest 47 * @run testng/othervm -Duser.language=en JextractToolProviderTest 48 */ 49 public class JextractToolProviderTest extends JextractToolRunner { 50 @Test 51 public void testHelp() { 52 run().checkFailure(); // no options 53 run("--help").checkSuccess(); 54 run("-h").checkSuccess(); 55 run("-?").checkSuccess(); 56 } 57 58 // error for non-existent header file 59 @Test 60 public void testNonExistentHeader() { 61 run("--dry-run", getInputFilePath("non_existent.h").toString()) 62 .checkFailure() 63 .checkContainsOutput("Cannot open header file"); 64 } 65 66 @Test 67 public void testDryRun() { 68 // only dry-run, don't produce any output 69 Path simpleJar = getOutputFilePath("simple.jar"); 70 deleteFile(simpleJar); 71 run("--dry-run", getInputFilePath("simple.h").toString()).checkSuccess(); 72 try { 73 assertFalse(Files.isRegularFile(simpleJar)); 74 } finally { 75 deleteFile(simpleJar); 76 } 77 } 78 79 @Test 80 public void testOutputFileOption() { 81 // simple output file check 82 Path simpleJar = getOutputFilePath("simple.jar"); 83 deleteFile(simpleJar); 84 run("-o", simpleJar.toString(), 85 getInputFilePath("simple.h").toString()).checkSuccess(); 86 try { 87 assertTrue(Files.isRegularFile(simpleJar)); 88 } finally { 89 deleteFile(simpleJar); 90 } 91 } 92 93 @Test 94 public void testOutputClass() { 95 Path helloJar = getOutputFilePath("hello.jar"); 96 deleteFile(helloJar); 97 Path helloH = getInputFilePath("hello.h"); 98 run("-o", helloJar.toString(), helloH.toString()).checkSuccess(); 99 try { 100 Class<?> cls = loadClass("hello", helloJar); 101 // check NativeHeader annotation 102 NativeHeader header = cls.getAnnotation(NativeHeader.class); 103 assertNotNull(header); 104 assertEquals(header.path(), helloH.toString()); 105 assertFalse(header.declarations().isEmpty()); 106 107 // check a method for "void func()" 108 assertNotNull(findMethod(cls, "func", Object[].class)); 109 } finally { 110 deleteFile(helloJar); 111 } 112 } 113 114 private void testTargetPackage(String targetPkgOption) { 115 Path helloJar = getOutputFilePath("hello.jar"); 116 deleteFile(helloJar); 117 Path helloH = getInputFilePath("hello.h"); 118 run(targetPkgOption, "com.acme", "-o", helloJar.toString(), helloH.toString()).checkSuccess(); 119 try { 120 Class<?> cls = loadClass("com.acme.hello", helloJar); 121 // check NativeHeader annotation 122 NativeHeader header = cls.getAnnotation(NativeHeader.class); 123 assertNotNull(header); 124 assertEquals(header.path(), helloH.toString()); 125 126 // check a method for "void func()" 127 assertNotNull(findMethod(cls, "func", Object[].class)); 128 } finally { 129 deleteFile(helloJar); 130 } 131 } 132 133 @Test 134 public void testTargetPackageOption() { 135 testTargetPackage("-t"); 136 } 137 138 @Test 139 public void testTargetPackageLongOption() { 140 testTargetPackage("--target-package"); 141 } 142 143 private void testPackageMapping(String pkgMapOption) { 144 Path worldJar = getOutputFilePath("world.jar"); 145 deleteFile(worldJar); 146 Path worldH = getInputFilePath("world.h"); 147 Path include = getInputFilePath("include"); 148 // world.h include mytypes.h, use appropriate package for stuff from mytypes.h 149 run("-I", include.toString(), pkgMapOption, include.toString() + "=com.acme", 150 "-o", worldJar.toString(), worldH.toString()).checkSuccess(); 151 try { 152 Class<?> cls = loadClass("world", worldJar); 153 Method m = findFirstMethod(cls, "distance"); 154 Class<?>[] params = m.getParameterTypes(); 155 assertEquals(params[0].getName(), "com.acme.mytypes$Point"); 156 } finally { 157 deleteFile(worldJar); 158 } 159 } 160 161 @Test 162 public void testPackageDirMappingOption() { 163 testPackageMapping("-m"); 164 } 165 166 @Test 167 public void testPackageDirMappingLongOption() { 168 testPackageMapping("--package-map"); 169 } 170 171 @Test 172 public void test_no_input_files() { 173 run("-L", "foo") 174 .checkContainsOutput("No input files") 175 .checkFailure(); 176 } 177 178 @Test 179 public void test_option_L_without_l() { 180 Path helloJar = getOutputFilePath("hello.jar"); 181 deleteFile(helloJar); 182 Path helloH = getInputFilePath("hello.h"); 183 Path linkDir = getInputFilePath("libs"); 184 String warning = "WARNING: -L option specified without any -l option"; 185 run("-L", linkDir.toString(), "-o", helloJar.toString(), helloH.toString()) 186 .checkContainsOutput(warning) 187 .checkSuccess(); 188 } 189 190 @Test 191 public void test_option_rpath_without_l() { 192 Path helloJar = getOutputFilePath("hello.jar"); 193 deleteFile(helloJar); 194 Path helloH = getInputFilePath("hello.h"); 195 Path rpathDir = getInputFilePath("libs"); 196 String warning = "WARNING: -rpath option specified without any -l option"; 197 try { 198 run("-rpath", rpathDir.toString(), "-o", helloJar.toString(), helloH.toString()) 199 .checkContainsOutput(warning) 200 .checkSuccess(); 201 } finally { 202 deleteFile(helloJar); 203 } 204 } 205 206 @Test 207 public void test_option_conflicting_rpaths() { 208 Path helloJar = getOutputFilePath("hello.jar"); 209 deleteFile(helloJar); 210 Path helloH = getInputFilePath("hello.h"); 211 Path rpathDir = getInputFilePath("libs"); 212 String warning = "WARNING: -infer-rpath used in conjunction with explicit -rpath paths"; 213 try { 214 run("-rpath", rpathDir.toString(), 215 "-infer-rpath", 216 "-o", helloJar.toString(), helloH.toString()) 217 .checkContainsOutput(warning) 218 .checkSuccess(); 219 } finally { 220 deleteFile(helloJar); 221 } 222 } 223 224 @Test 225 public void test_option_rpath_no_libs() { 226 Path helloJar = getOutputFilePath("hello.jar"); 227 deleteFile(helloJar); 228 Path helloH = getInputFilePath("hello.h"); 229 String warning = "WARNING: -infer-rpath option specified without any -L option"; 230 try { 231 run("-infer-rpath", 232 "-o", helloJar.toString(), helloH.toString()) 233 .checkContainsOutput(warning) 234 .checkSuccess(); 235 } finally { 236 deleteFile(helloJar); 237 } 238 } 239 240 @Test 241 public void test_option_l_no_crash_missing_lib() { 242 Path helloJar = getOutputFilePath("hello.jar"); 243 deleteFile(helloJar); 244 Path helloH = getInputFilePath("hello.h"); 245 String warning = "WARNING: Some library names could not be resolved"; 246 try { 247 run("-L", "nonExistent", 248 "-l", "nonExistent", 249 "-o", helloJar.toString(), helloH.toString()) 250 .checkContainsOutput(warning) 251 .checkSuccess(); 252 } finally { 253 deleteFile(helloJar); 254 } 255 } 256 257 @Test 258 public void test_option_l() { 259 Path helloJar = getOutputFilePath("hello.jar"); 260 deleteFile(helloJar); 261 Path helloH = getInputFilePath("hello.h"); 262 run("-l", "hello", "-o", helloJar.toString(), helloH.toString()).checkSuccess(); 263 try { 264 Class<?> cls = loadClass("hello", helloJar); 265 // check that NativeHeader annotation captures -l value 266 NativeHeader header = cls.getAnnotation(NativeHeader.class); 267 assertNotNull(header); 268 assertEquals(header.libraries().length, 1); 269 assertEquals(header.libraries()[0], "hello"); 270 // no library paths (rpath) set 271 assertEquals(header.libraryPaths().length, 0); 272 } finally { 273 deleteFile(helloJar); 274 } 275 } 276 277 @Test 278 public void test_option_l_and_rpath() { 279 Path helloJar = getOutputFilePath("hello.jar"); 280 deleteFile(helloJar); 281 Path helloH = getInputFilePath("hello.h"); 282 Path rpathDir = getInputFilePath("libs"); 283 run("-l", "hello", "-rpath", rpathDir.toString(), 284 "-o", helloJar.toString(), helloH.toString()).checkSuccess(); 285 try { 286 Class<?> cls = loadClass("hello", helloJar); 287 // check that NativeHeader annotation captures -l and -rpath values 288 NativeHeader header = cls.getAnnotation(NativeHeader.class); 289 assertNotNull(header); 290 assertEquals(header.libraries().length, 1); 291 assertEquals(header.libraries()[0], "hello"); 292 assertEquals(header.libraryPaths().length, 1); 293 assertEquals(header.libraryPaths()[0], rpathDir.toString()); 294 } finally { 295 deleteFile(helloJar); 296 } 297 } 298 299 @Test 300 public void testUnionDeclaration() { 301 Path uniondeclJar = getOutputFilePath("uniondecl.jar"); 302 deleteFile(uniondeclJar); 303 Path uniondeclH = getInputFilePath("uniondecl.h"); 304 try { 305 run("-o", uniondeclJar.toString(), uniondeclH.toString()).checkSuccess(); 306 Class<?> unionCls = loadClass("uniondecl", uniondeclJar); 307 assertNotNull(unionCls); 308 boolean found = Arrays.stream(unionCls.getClasses()). 309 map(Class::getSimpleName). 310 filter(n -> n.equals("IntOrFloat")). 311 findFirst().isPresent(); 312 assertTrue(found, "uniondecl.IntOrFloat not found"); 313 } finally { 314 deleteFile(uniondeclJar); 315 } 316 } 317 318 private void testEnumConstGetters(Class<?> enumCls, List<String> names) { 319 for (String name : names) { 320 if (findEnumConstGet(enumCls, name) == null) { 321 throw new RuntimeException(enumCls.getName() + " misses " + name); 322 } 323 } 324 } 325 326 @Test 327 public void testAnonymousEnum() { 328 Path anonenumJar = getOutputFilePath("anonenum.jar"); 329 deleteFile(anonenumJar); 330 Path anonenumH = getInputFilePath("anonenum.h"); 331 try { 332 run("-o", anonenumJar.toString(), anonenumH.toString()).checkSuccess(); 333 Class<?> anonenumCls = loadClass("anonenum", anonenumJar); 334 assertNotNull(anonenumCls); 335 testEnumConstGetters(anonenumCls, List.of("RED", "GREEN", "BLUE")); 336 testEnumConstGetters(anonenumCls, List.of( 337 "Java", "C", "CPP", "Python", "Ruby")); 338 testEnumConstGetters(anonenumCls, List.of( 339 "XS", "S", "M", "L", "XL", "XXL")); 340 testEnumConstGetters(anonenumCls, List.of( 341 "ONE", "TWO")); 342 343 Class<?> enumClz[] = anonenumCls.getClasses(); 344 assert(enumClz.length >= 4); 345 346 Class<?> enumCls = findClass(enumClz, "codetype_t"); 347 assertNotNull(enumCls); 348 349 enumCls = findClass(enumClz, "SIZE"); 350 assertNotNull(enumCls); 351 352 enumCls = findClass(enumClz, "temp"); 353 assertNotNull(enumCls); 354 355 enumCls = findClass(enumClz, "temp_t"); 356 assertNotNull(enumCls); 357 } finally { 358 deleteFile(anonenumJar); 359 } 360 } 361 362 @Test 363 public void testExcludeSymbols() { 364 Path helloJar = getOutputFilePath("hello.jar"); 365 deleteFile(helloJar); 366 Path helloH = getInputFilePath("hello.h"); 367 run("-o", helloJar.toString(), helloH.toString()).checkSuccess(); 368 try { 369 Class<?> cls = loadClass("hello", helloJar); 370 // check a method for "void func()" 371 assertNotNull(findMethod(cls, "func", Object[].class)); 372 assertNotNull(findMethod(cls, "func2", Object[].class)); 373 assertNotNull(findMethod(cls, "func3", Object[].class)); 374 // check a method for "void junk()" 375 assertNotNull(findMethod(cls, "junk", Object[].class)); 376 assertNotNull(findMethod(cls, "junk2", Object[].class)); 377 assertNotNull(findMethod(cls, "junk3", Object[].class)); 378 } finally { 379 deleteFile(helloJar); 380 } 381 382 // try with --exclude-symbols" this time. 383 run("--exclude-symbols", "junk.*", "-o", helloJar.toString(), helloH.toString()) 384 .checkSuccess(); 385 try { 386 Class<?> cls = loadClass("hello", helloJar); 387 // check a method for "void func()" 388 assertNotNull(findMethod(cls, "func", Object[].class)); 389 assertNotNull(findMethod(cls, "func2", Object[].class)); 390 assertNotNull(findMethod(cls, "func3", Object[].class)); 391 // check a method for "void junk()" 392 assertNull(findMethod(cls, "junk", Object[].class)); 393 assertNull(findMethod(cls, "junk2", Object[].class)); 394 assertNull(findMethod(cls, "junk3", Object[].class)); 395 } finally { 396 deleteFile(helloJar); 397 } 398 } 399 400 @Test 401 public void testIncludeSymbols() { 402 Path helloJar = getOutputFilePath("hello.jar"); 403 deleteFile(helloJar); 404 Path helloH = getInputFilePath("hello.h"); 405 run("-o", helloJar.toString(), helloH.toString()).checkSuccess(); 406 try { 407 Class<?> cls = loadClass("hello", helloJar); 408 // check a method for "void func()" 409 assertNotNull(findMethod(cls, "func", Object[].class)); 410 assertNotNull(findMethod(cls, "func2", Object[].class)); 411 assertNotNull(findMethod(cls, "func3", Object[].class)); 412 // check a method for "void junk()" 413 assertNotNull(findMethod(cls, "junk", Object[].class)); 414 assertNotNull(findMethod(cls, "junk2", Object[].class)); 415 assertNotNull(findMethod(cls, "junk3", Object[].class)); 416 } finally { 417 deleteFile(helloJar); 418 } 419 420 // try with --include-symbols" this time. 421 run("--include-symbols", "junk.*", "-o", helloJar.toString(), helloH.toString()).checkSuccess(); 422 try { 423 Class<?> cls = loadClass("hello", helloJar); 424 // check a method for "void junk()" 425 assertNotNull(findMethod(cls, "junk", Object[].class)); 426 assertNotNull(findMethod(cls, "junk2", Object[].class)); 427 assertNotNull(findMethod(cls, "junk3", Object[].class)); 428 // check a method for "void func()" 429 assertNull(findMethod(cls, "func", Object[].class)); 430 assertNull(findMethod(cls, "func2", Object[].class)); 431 assertNull(findMethod(cls, "func3", Object[].class)); 432 } finally { 433 deleteFile(helloJar); 434 } 435 } 436 437 @Test 438 public void testNoLocations() { 439 Path simpleJar = getOutputFilePath("simple.jar"); 440 deleteFile(simpleJar); 441 Path simpleH = getInputFilePath("simple.h"); 442 run("--no-locations", "-o", simpleJar.toString(), simpleH.toString()).checkSuccess(); 443 try { 444 Class<?> simpleCls = loadClass("simple", simpleJar); 445 Method func = findFirstMethod(simpleCls, "func"); 446 assertFalse(func.isAnnotationPresent(NativeLocation.class)); 447 Class<?> anonymousCls = loadClass("simple$anonymous", simpleJar); 448 assertFalse(simpleCls.isAnnotationPresent(NativeLocation.class)); 449 } finally { 450 deleteFile(simpleJar); 451 } 452 } 453 454 @Test 455 public void testIncludeExcludeSymbols() { 456 Path helloJar = getOutputFilePath("hello.jar"); 457 deleteFile(helloJar); 458 Path helloH = getInputFilePath("hello.h"); 459 run("-o", helloJar.toString(), helloH.toString()).checkSuccess(); 460 try { 461 Class<?> cls = loadClass("hello", helloJar); 462 // check a method for "void func()" 463 assertNotNull(findMethod(cls, "func", Object[].class)); 464 assertNotNull(findMethod(cls, "func2", Object[].class)); 465 assertNotNull(findMethod(cls, "func3", Object[].class)); 466 // check a method for "void junk()" 467 assertNotNull(findMethod(cls, "junk", Object[].class)); 468 assertNotNull(findMethod(cls, "junk2", Object[].class)); 469 assertNotNull(findMethod(cls, "junk3", Object[].class)); 470 } finally { 471 deleteFile(helloJar); 472 } 473 474 // try with --include-symbols" this time. 475 run("--include-symbols", "junk.*", "--exclude-symbols", "junk3", 476 "-o", helloJar.toString(), helloH.toString()).checkSuccess(); 477 try { 478 Class<?> cls = loadClass("hello", helloJar); 479 // check a method for "void junk()" 480 assertNotNull(findMethod(cls, "junk", Object[].class)); 481 assertNotNull(findMethod(cls, "junk2", Object[].class)); 482 // check a method for "void func()" - not included 483 assertNull(findMethod(cls, "func", Object[].class)); 484 assertNull(findMethod(cls, "func2", Object[].class)); 485 assertNull(findMethod(cls, "func3", Object[].class)); 486 // excluded among the included set! 487 assertNull(findMethod(cls, "junk3", Object[].class)); 488 } finally { 489 deleteFile(helloJar); 490 } 491 } 492 493 @Test 494 public void testNestedStructsUnions() { 495 Path nestedJar = getOutputFilePath("nested.jar"); 496 deleteFile(nestedJar); 497 Path nestedH = getInputFilePath("nested.h"); 498 try { 499 run("-o", nestedJar.toString(), nestedH.toString()).checkSuccess(); 500 Class<?> headerCls = loadClass("nested", nestedJar); 501 assertNotNull(headerCls); 502 503 Class<?> fooCls = loadClass("nested$Foo", nestedJar); 504 assertNotNull(fooCls); 505 // struct Foo has no getters for "x", "y" etc. 506 assertNull(findStructFieldGet(fooCls, "x")); 507 assertNull(findStructFieldGet(fooCls, "y")); 508 // struct Foo has getters for bar and color 509 assertNotNull(findStructFieldGet(fooCls, "bar")); 510 assertNotNull(findStructFieldGet(fooCls, "color")); 511 // make sure nested types are handled without nested namespace! 512 assertNotNull(loadClass("nested$Bar", nestedJar)); 513 assertNotNull(loadClass("nested$Color", nestedJar)); 514 515 Class<?> uCls = loadClass("nested$U", nestedJar); 516 assertNotNull(uCls); 517 // union U has no getters for "x", "y" etc. 518 assertNull(findStructFieldGet(uCls, "x")); 519 assertNull(findStructFieldGet(uCls, "y")); 566 assertNotNull(findStructFieldGet(myUnionCls, "k")); 567 // "A", "B", "C" are enum constants -should not be in MyUnion 568 assertNull(findStructFieldGet(myUnionCls, "A")); 569 assertNull(findStructFieldGet(myUnionCls, "B")); 570 assertNull(findStructFieldGet(myUnionCls, "C")); 571 // anonymous enum constants are hoisted to containing scope 572 assertNotNull(findEnumConstGet(headerCls, "A")); 573 assertNotNull(findEnumConstGet(headerCls, "B")); 574 assertNotNull(findEnumConstGet(headerCls, "C")); 575 } finally { 576 deleteFile(nestedJar); 577 } 578 } 579 580 @Test 581 public void testAnonymousStructTypeGlobalVar() { 582 Path elaboratedTypeJar = getOutputFilePath("elaboratedtype.jar"); 583 deleteFile(elaboratedTypeJar); 584 Path elaboratedTypeH = getInputFilePath("elaboratedtype.h"); 585 try { 586 run("-o", elaboratedTypeJar.toString(), elaboratedTypeH.toString()).checkSuccess(); 587 Class<?> headerCls = loadClass("elaboratedtype", elaboratedTypeJar); 588 assertNotNull(findGlobalVariableGet(headerCls, "point")); 589 assertNotNull(findGlobalVariableGet(headerCls, "long_or_int")); 590 assertNotNull(findMethod(headerCls, "func", Pointer.class)); 591 } finally { 592 deleteFile(elaboratedTypeJar); 593 } 594 } 595 596 private void testBuiltinInclude(String name) { 597 Path fileJar = getOutputFilePath(name + "inc.jar"); 598 deleteFile(fileJar); 599 Path fileH = getInputFilePath(name + "inc.h"); 600 run("-o", fileJar.toString(), fileH.toString()).checkSuccess(); 601 deleteFile(fileJar); 602 } 603 604 @Test 605 public void testBuiltinHeader() { 606 testBuiltinInclude("stdarg"); 607 testBuiltinInclude("stdbool"); 608 } 609 610 @Test 611 public void testGlobalFuncPointerCallback() { 612 Path globalFuncPointerJar = getOutputFilePath("globalFuncPointer.jar"); 613 deleteFile(globalFuncPointerJar); 614 Path globalFuncPointerH = getInputFilePath("globalFuncPointer.h"); 615 run("-o", globalFuncPointerJar.toString(), globalFuncPointerH.toString()).checkSuccess(); 616 Class<?> callbackCls = loadClass("globalFuncPointer$FI1", globalFuncPointerJar); 617 Method callback = findFirstMethod(callbackCls, "fn"); 618 assertNotNull(callback); 619 assertTrue(callback.isVarArgs()); 620 deleteFile(globalFuncPointerJar); 621 } 622 623 @Test 624 public void testFuncPtrTypedef() { 625 Path funcPtrTypedefJar = getOutputFilePath("funcPtrTypedef.jar"); 626 deleteFile(funcPtrTypedefJar); 627 Path funcPtrTypedefH = getInputFilePath("funcPtrTypedef.h"); 628 run("-o", funcPtrTypedefJar.toString(), funcPtrTypedefH.toString()).checkSuccess(); 629 // force parsing of class, method 630 Class<?> headerCls = loadClass("funcPtrTypedef", funcPtrTypedefJar); 631 Method getter = findFirstMethod(headerCls, "my_function$get"); 632 assertNotNull(getter); 633 assertNotNull(getter.getGenericParameterTypes()); 634 deleteFile(funcPtrTypedefJar); 635 } 636 637 @Test 638 public void testDuplicatedecls() { 639 Path duplicatedeclsJar = getOutputFilePath("duplicatedecls.jar"); 640 deleteFile(duplicatedeclsJar); 641 Path duplicatedeclsH = getInputFilePath("duplicatedecls.h"); 642 run("-o", duplicatedeclsJar.toString(), duplicatedeclsH.toString()).checkSuccess(); 643 // load the class to make sure no duplicate methods generated in it 644 Class<?> headerCls = loadClass("duplicatedecls", duplicatedeclsJar); 645 assertNotNull(headerCls); 646 deleteFile(duplicatedeclsJar); 647 } 648 } |