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.nicl.metadata.NativeHeader; 28 import java.nicl.types.Pointer; 29 import java.nio.file.Files; 30 import java.nio.file.Path; 31 import java.util.Arrays; 32 import java.util.Map; 33 34 import static org.testng.Assert.assertEquals; 35 import static org.testng.Assert.assertFalse; 36 import static org.testng.Assert.assertNotNull; 37 import static org.testng.Assert.assertNull; 38 import static org.testng.Assert.assertTrue; 39 40 /* 41 * @test 42 * @modules jdk.jextract 43 * @build JextractToolProviderTest 44 * @run testng/othervm -Duser.language=en JextractToolProviderTest 45 */ 46 public class JextractToolProviderTest extends JextractToolRunner { 47 @Test 48 public void testHelp() { 49 checkFailure(null); // no options 50 checkSuccess(null, "--help"); 51 checkSuccess(null, "-h"); 52 checkSuccess(null, "-?"); 53 } 54 55 // error for non-existent header file 56 @Test 57 public void testNonExistentHeader() { 58 checkFailure("Cannot open header file", "--dry-run", 59 getInputFilePath("non_existent.h").toString()); 60 } 61 62 @Test 63 public void testDryRun() { 64 // only dry-run, don't produce any output 65 Path simpleJar = getOutputFilePath("simple.jar"); 66 deleteFile(simpleJar); 67 checkSuccess(null, "--dry-run", getInputFilePath("simple.h").toString()); 68 try { 69 assertFalse(Files.isRegularFile(simpleJar)); 70 } finally { 71 deleteFile(simpleJar); 72 } 73 } 74 75 @Test 76 public void testOutputFileOption() { 77 // simple output file check 78 Path simpleJar = getOutputFilePath("simple.jar"); 79 deleteFile(simpleJar); 80 checkSuccess(null, "-o", simpleJar.toString(), 81 getInputFilePath("simple.h").toString()); 82 try { 83 assertTrue(Files.isRegularFile(simpleJar)); 84 } finally { 85 deleteFile(simpleJar); 86 } 87 } 88 89 @Test 90 public void testOutputClass() { 91 Path helloJar = getOutputFilePath("hello.jar"); 92 deleteFile(helloJar); 93 Path helloH = getInputFilePath("hello.h"); 94 checkSuccess(null, "-o", helloJar.toString(), helloH.toString()); 95 try { 96 Class<?> cls = loadClass("hello", helloJar); 97 // check NativeHeader annotation 98 NativeHeader header = cls.getAnnotation(NativeHeader.class); 99 assertNotNull(header); 100 assertEquals(header.path(), helloH.toString()); 101 assertFalse(header.declarations().isEmpty()); 102 103 // check a method for "void func()" 104 assertNotNull(findMethod(cls, "func", Object[].class)); 105 } finally { 106 deleteFile(helloJar); 107 } 108 } 109 110 private void testTargetPackage(String targetPkgOption) { 111 Path helloJar = getOutputFilePath("hello.jar"); 112 deleteFile(helloJar); 113 Path helloH = getInputFilePath("hello.h"); 114 checkSuccess(null, targetPkgOption, "com.acme", "-o", helloJar.toString(), helloH.toString()); 115 try { 116 Class<?> cls = loadClass("com.acme.hello", helloJar); 117 // check NativeHeader annotation 118 NativeHeader header = cls.getAnnotation(NativeHeader.class); 119 assertNotNull(header); 120 assertEquals(header.path(), helloH.toString()); 121 122 // check a method for "void func()" 123 assertNotNull(findMethod(cls, "func", Object[].class)); 124 } finally { 125 deleteFile(helloJar); 126 } 127 } 128 129 @Test 130 public void testTargetPackageOption() { 131 testTargetPackage("-t"); 132 } 133 134 @Test 135 public void testTargetPackageLongOption() { 136 testTargetPackage("--target-package"); 137 } 138 139 private void testPackageMapping(String pkgMapOption) { 140 Path worldJar = getOutputFilePath("world.jar"); 141 deleteFile(worldJar); 142 Path mytypesJar = getOutputFilePath("mytypes.jar"); 143 deleteFile(mytypesJar); 144 145 Path worldH = getInputFilePath("world.h"); 146 Path include = getInputFilePath("include"); 147 // generate jar for mytypes.h 148 checkSuccess(null, "-t", "com.acme", "-o", mytypesJar.toString(), 149 include.resolve("mytypes.h").toString()); 150 // world.h include mytypes.h, use appropriate package for stuff from mytypes.h 151 checkSuccess(null, "-I", include.toString(), pkgMapOption, include.toString() + "=com.acme", 152 "-o", worldJar.toString(), worldH.toString()); 153 try { 154 Class<?> cls = loadClass("world", worldJar, mytypesJar); 155 Method m = findFirstMethod(cls, "distance"); 156 Class<?>[] params = m.getParameterTypes(); 157 assertEquals(params[0].getName(), "com.acme.mytypes$Point"); 158 } finally { 159 deleteFile(worldJar); 160 deleteFile(mytypesJar); 161 } 162 } 163 164 @Test 165 public void testPackageDirMappingOption() { 166 testPackageMapping("-m"); 167 } 168 169 @Test 170 public void testPackageDirMappingLongOption() { 171 testPackageMapping("--package-map"); 172 } 173 174 @Test 175 public void test_option_L_without_l() { 176 Path helloJar = getOutputFilePath("hello.jar"); 177 deleteFile(helloJar); 178 Path helloH = getInputFilePath("hello.h"); 179 Path linkDir = getInputFilePath("libs"); 180 String warning = "WARNING: -L option specified without any -l option"; 181 checkSuccess(warning, "-L", linkDir.toString(), "-o", helloJar.toString(), helloH.toString()); 182 } 183 184 @Test 185 public void test_option_rpath_without_l() { 186 Path helloJar = getOutputFilePath("hello.jar"); 187 deleteFile(helloJar); 188 Path helloH = getInputFilePath("hello.h"); 189 Path rpathDir = getInputFilePath("libs"); 190 String warning = "WARNING: -rpath option specified without any -l option"; 191 try { 192 checkSuccess(warning, "-rpath", rpathDir.toString(), "-o", helloJar.toString(), helloH.toString()); 193 } finally { 194 deleteFile(helloJar); 195 } 196 } 197 198 @Test 199 public void test_option_l() { 200 Path helloJar = getOutputFilePath("hello.jar"); 201 deleteFile(helloJar); 202 Path helloH = getInputFilePath("hello.h"); 203 checkSuccess(null, "-l", "hello", "-o", helloJar.toString(), helloH.toString()); 204 try { 205 Class<?> cls = loadClass("hello", helloJar); 206 // check that NativeHeader annotation captures -l value 207 NativeHeader header = cls.getAnnotation(NativeHeader.class); 208 assertNotNull(header); 209 assertEquals(header.libraries().length, 1); 210 assertEquals(header.libraries()[0], "hello"); 211 // no library paths (rpath) set 212 assertEquals(header.libraryPaths().length, 0); 213 } finally { 214 deleteFile(helloJar); 215 } 216 } 217 218 @Test 219 public void test_option_l_and_rpath() { 220 Path helloJar = getOutputFilePath("hello.jar"); 221 deleteFile(helloJar); 222 Path helloH = getInputFilePath("hello.h"); 223 Path rpathDir = getInputFilePath("libs"); 224 checkSuccess(null, "-l", "hello", "-rpath", rpathDir.toString(), 225 "-o", helloJar.toString(), helloH.toString()); 226 try { 227 Class<?> cls = loadClass("hello", helloJar); 228 // check that NativeHeader annotation captures -l and -rpath values 229 NativeHeader header = cls.getAnnotation(NativeHeader.class); 230 assertNotNull(header); 231 assertEquals(header.libraries().length, 1); 232 assertEquals(header.libraries()[0], "hello"); 233 assertEquals(header.libraryPaths().length, 1); 234 assertEquals(header.libraryPaths()[0], rpathDir.toString()); 235 } finally { 236 deleteFile(helloJar); 237 } 238 } 239 240 @Test 241 public void testUnionDeclaration() { 242 Path uniondeclJar = getOutputFilePath("uniondecl.jar"); 243 deleteFile(uniondeclJar); 244 Path uniondeclH = getInputFilePath("uniondecl.h"); 245 try { 246 checkSuccess(null, "-o", uniondeclJar.toString(), uniondeclH.toString()); 247 Class<?> unionCls = loadClass("uniondecl", uniondeclJar); 248 assertNotNull(unionCls); 249 boolean found = Arrays.stream(unionCls.getClasses()). 250 map(Class::getSimpleName). 251 filter(n -> n.equals("IntOrFloat")). 252 findFirst().isPresent(); 253 assertTrue(found, "uniondecl.IntOrFloat not found"); 254 } finally { 255 deleteFile(uniondeclJar); 256 } 257 } 258 259 private void testEnumValue(Class<?> enumCls, Map<String, Integer> values) { 260 values.entrySet().stream(). 261 forEach(e -> checkIntField(enumCls, e.getKey(), e.getValue())); 262 } 263 264 @Test 265 public void testAnonymousEnum() { 266 Path anonenumJar = getOutputFilePath("anonenum.jar"); 267 deleteFile(anonenumJar); 268 Path anonenumH = getInputFilePath("anonenum.h"); 269 try { 270 checkSuccess(null, "-o", anonenumJar.toString(), anonenumH.toString()); 271 Class<?> anonenumCls = loadClass("anonenum", anonenumJar); 272 assertNotNull(anonenumCls); 273 checkIntField(anonenumCls, "RED", 0xff0000); 274 checkIntField(anonenumCls, "GREEN", 0x00ff00); 275 checkIntField(anonenumCls, "BLUE", 0x0000ff); 276 testEnumValue(anonenumCls, Map.of( 277 "Java", 0, 278 "C", 1, 279 "CPP", 2, 280 "Python", 3, 281 "Ruby", 4)); 282 testEnumValue(anonenumCls, Map.of( 283 "XS", 0, 284 "S", 1, 285 "M", 2, 286 "L", 3, 287 "XL", 4, 288 "XXL", 5)); 289 testEnumValue(anonenumCls, Map.of( 290 "ONE", 1, 291 "TWO", 2)); 292 293 Class<?> enumClz[] = anonenumCls.getClasses(); 294 assert(enumClz.length >= 4); 295 296 Class<?> enumCls = findClass(enumClz, "codetype_t"); 297 assertNotNull(enumCls); 298 299 enumCls = findClass(enumClz, "SIZE"); 300 assertNotNull(enumCls); 301 302 enumCls = findClass(enumClz, "temp"); 303 assertNotNull(enumCls); 304 305 enumCls = findClass(enumClz, "temp_t"); 306 assertNotNull(enumCls); 307 } finally { 308 deleteFile(anonenumJar); 309 } 310 } 311 312 @Test 313 public void testExcludeSymbols() { 314 Path helloJar = getOutputFilePath("hello.jar"); 315 deleteFile(helloJar); 316 Path helloH = getInputFilePath("hello.h"); 317 checkSuccess(null, "-o", helloJar.toString(), helloH.toString()); 318 try { 319 Class<?> cls = loadClass("hello", helloJar); 320 // check a method for "void func()" 321 assertNotNull(findMethod(cls, "func", Object[].class)); 322 // check a method for "void junk()" 323 assertNotNull(findMethod(cls, "junk", Object[].class)); 324 } finally { 325 deleteFile(helloJar); 326 } 327 328 // try with --exclude-symbols" this time. 329 checkSuccess(null, "--exclude-symbols", "junk", "-o", helloJar.toString(), helloH.toString()); 330 try { 331 Class<?> cls = loadClass("hello", helloJar); 332 // check a method for "void func()" 333 assertNotNull(findMethod(cls, "func", Object[].class)); 334 // check a method for "void junk()" 335 assertNull(findMethod(cls, "junk", Object[].class)); 336 } finally { 337 deleteFile(helloJar); 338 } 339 } 340 341 @Test 342 public void testNestedStructsUnions() { 343 Path nestedJar = getOutputFilePath("nested.jar"); 344 deleteFile(nestedJar); 345 Path nestedH = getInputFilePath("nested.h"); 346 try { 347 checkSuccess(null, "-o", nestedJar.toString(), nestedH.toString()); 348 Class<?> headerCls = loadClass("nested", nestedJar); 349 assertNotNull(headerCls); 350 351 Class<?> fooCls = loadClass("nested$Foo", nestedJar); 352 assertNotNull(fooCls); 353 // struct Foo has no getters for "x", "y" etc. 354 assertNull(findStructFieldGet(fooCls, "x")); 355 assertNull(findStructFieldGet(fooCls, "y")); 356 // struct Foo has getters for bar and color 357 assertNotNull(findStructFieldGet(fooCls, "bar")); 358 assertNotNull(findStructFieldGet(fooCls, "color")); 359 // make sure nested types are handled without nested namespace! 360 assertNotNull(loadClass("nested$Bar", nestedJar)); 361 assertNotNull(loadClass("nested$Color", nestedJar)); 362 363 Class<?> uCls = loadClass("nested$U", nestedJar); 364 assertNotNull(uCls); 365 // union U has no getters for "x", "y" etc. 366 assertNull(findStructFieldGet(uCls, "x")); 367 assertNull(findStructFieldGet(uCls, "y")); 368 // union U has getters for point, rgb, i 369 assertNotNull(findStructFieldGet(uCls, "point")); 370 assertNotNull(findStructFieldGet(uCls, "rgb")); 371 assertNotNull(findStructFieldGet(uCls, "i")); 372 // make sure nested types are handled without nested namespace! 373 assertNotNull(loadClass("nested$Point", nestedJar)); 374 assertNotNull(loadClass("nested$RGB", nestedJar)); 375 376 Class<?> myStructCls = loadClass("nested$MyStruct", nestedJar); 377 assertNotNull(findStructFieldGet(myStructCls, "a")); 378 assertNotNull(findStructFieldGet(myStructCls, "b")); 379 assertNotNull(findStructFieldGet(myStructCls, "c")); 380 assertNotNull(findStructFieldGet(myStructCls, "d")); 381 // 'e' is named struct element - should not be in MyStruct 382 assertNull(findStructFieldGet(myStructCls, "e")); 383 assertNotNull(findStructFieldGet(myStructCls, "f")); 384 assertNotNull(findStructFieldGet(myStructCls, "g")); 385 assertNotNull(findStructFieldGet(myStructCls, "h")); 386 // 'i' is named struct element - should not be in MyStruct 387 assertNull(findStructFieldGet(myStructCls, "i")); 388 // 'j' is named struct element - should not be in MyStruct 389 assertNull(findStructFieldGet(myStructCls, "j")); 390 assertNotNull(findStructFieldGet(myStructCls, "k")); 391 // "X", "Y", "Z" are enum constants -should not be in MyStruct 392 assertNull(findStructFieldGet(myStructCls, "X")); 393 assertNull(findStructFieldGet(myStructCls, "Y")); 394 assertNull(findStructFieldGet(myStructCls, "Z")); 395 // anonymous enum constants are hoisted to containing scope 396 assertNotNull(findField(headerCls, "X")); 397 assertNotNull(findField(headerCls, "Y")); 398 assertNotNull(findField(headerCls, "Z")); 399 400 Class<?> myUnionCls = loadClass("nested$MyUnion", nestedJar); 401 assertNotNull(findStructFieldGet(myUnionCls, "a")); 402 assertNotNull(findStructFieldGet(myUnionCls, "b")); 403 assertNotNull(findStructFieldGet(myUnionCls, "c")); 404 assertNotNull(findStructFieldGet(myUnionCls, "d")); 405 // 'e' is named struct element - should not be in MyUnion 406 assertNull(findStructFieldGet(myUnionCls, "e")); 407 assertNotNull(findStructFieldGet(myUnionCls, "f")); 408 assertNotNull(findStructFieldGet(myUnionCls, "g")); 409 assertNotNull(findStructFieldGet(myUnionCls, "h")); 410 // 'i' is named struct element - should not be in MyUnion 411 assertNull(findStructFieldGet(myUnionCls, "i")); 412 // 'j' is named struct element - should not be in MyUnion 413 assertNull(findStructFieldGet(myUnionCls, "j")); 414 assertNotNull(findStructFieldGet(myUnionCls, "k")); 415 // "A", "B", "C" are enum constants -should not be in MyUnion 416 assertNull(findStructFieldGet(myUnionCls, "A")); 417 assertNull(findStructFieldGet(myUnionCls, "B")); 418 assertNull(findStructFieldGet(myUnionCls, "C")); 419 // anonymous enum constants are hoisted to containing scope 420 assertNotNull(findField(headerCls, "A")); 421 assertNotNull(findField(headerCls, "B")); 422 assertNotNull(findField(headerCls, "C")); 423 } finally { 424 deleteFile(nestedJar); 425 } 426 } 427 428 @Test 429 public void testAnonymousStructTypeGlobalVar() { 430 Path elaboratedTypeJar = getOutputFilePath("elaboratedtype.jar"); 431 deleteFile(elaboratedTypeJar); 432 Path elaboratedTypeH = getInputFilePath("elaboratedtype.h"); 433 try { 434 checkSuccess(null, "-o", elaboratedTypeJar.toString(), elaboratedTypeH.toString()); 435 Class<?> headerCls = loadClass("elaboratedtype", elaboratedTypeJar); 436 assertNotNull(findGlobalVariableGet(headerCls, "point")); 437 assertNotNull(findGlobalVariableGet(headerCls, "long_or_int")); 438 assertNotNull(findMethod(headerCls, "func", Pointer.class)); 439 } finally { 440 deleteFile(elaboratedTypeJar); 441 } 442 } 443 }