1 /*
   2  * Copyright (c) 2018, 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 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"));
 514             // union U has getters for point, rgb, i
 515             assertNotNull(findStructFieldGet(uCls, "point"));
 516             assertNotNull(findStructFieldGet(uCls, "rgb"));
 517             assertNotNull(findStructFieldGet(uCls, "i"));
 518             // make sure nested types are handled without nested namespace!
 519             assertNotNull(loadClass("nested$Point", nestedJar));
 520             assertNotNull(loadClass("nested$RGB", nestedJar));
 521 
 522             Class<?> myStructCls = loadClass("nested$MyStruct", nestedJar);
 523             assertNotNull(findStructFieldGet(myStructCls, "a"));
 524             assertNotNull(findStructFieldGet(myStructCls, "b"));
 525             assertNotNull(findStructFieldGet(myStructCls, "c"));
 526             assertNotNull(findStructFieldGet(myStructCls, "d"));
 527             // 'e' is named struct element - should not be in MyStruct
 528             assertNull(findStructFieldGet(myStructCls, "e"));
 529             assertNotNull(findStructFieldGet(myStructCls, "f"));
 530             assertNotNull(findStructFieldGet(myStructCls, "g"));
 531             assertNotNull(findStructFieldGet(myStructCls, "h"));
 532             // 'i' is named struct element - should not be in MyStruct
 533             assertNull(findStructFieldGet(myStructCls, "i"));
 534             // 'j' is named struct element - should not be in MyStruct
 535             assertNull(findStructFieldGet(myStructCls, "j"));
 536             assertNotNull(findStructFieldGet(myStructCls, "k"));
 537             // "X", "Y", "Z" are enum constants -should not be in MyStruct
 538             assertNull(findStructFieldGet(myStructCls, "X"));
 539             assertNull(findStructFieldGet(myStructCls, "Y"));
 540             assertNull(findStructFieldGet(myStructCls, "Z"));
 541             // anonymous enum constants are hoisted to containing scope
 542             assertNotNull(findEnumConstGet(headerCls, "X"));
 543             assertNotNull(findEnumConstGet(headerCls, "Y"));
 544             assertNotNull(findEnumConstGet(headerCls, "Z"));
 545 
 546             Class<?> myUnionCls = loadClass("nested$MyUnion", nestedJar);
 547             assertNotNull(findStructFieldGet(myUnionCls, "a"));
 548             assertNotNull(findStructFieldGet(myUnionCls, "b"));
 549             assertNotNull(findStructFieldGet(myUnionCls, "c"));
 550             assertNotNull(findStructFieldGet(myUnionCls, "d"));
 551             // 'e' is named struct element - should not be in MyUnion
 552             assertNull(findStructFieldGet(myUnionCls, "e"));
 553             assertNotNull(findStructFieldGet(myUnionCls, "f"));
 554             assertNotNull(findStructFieldGet(myUnionCls, "g"));
 555             assertNotNull(findStructFieldGet(myUnionCls, "h"));
 556             // 'i' is named struct element - should not be in MyUnion
 557             assertNull(findStructFieldGet(myUnionCls, "i"));
 558             // 'j' is named struct element - should not be in MyUnion
 559             assertNull(findStructFieldGet(myUnionCls, "j"));
 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 }