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