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 java.lang.reflect.Method;
  25 import java.io.IOException;
  26 import java.io.PrintWriter;
  27 import java.io.StringWriter;
  28 import java.net.URL;
  29 import java.net.URLClassLoader;
  30 import java.nicl.metadata.Header;
  31 import java.nicl.metadata.LibraryDependencies;
  32 import java.nio.file.Path;
  33 import java.nio.file.Paths;
  34 import java.nio.file.Files;
  35 import java.util.spi.ToolProvider;
  36 import org.testng.annotations.Test;
  37 
  38 import static org.testng.Assert.assertEquals;
  39 import static org.testng.Assert.assertNotEquals;
  40 import static org.testng.Assert.assertNotNull;
  41 import static org.testng.Assert.assertTrue;
  42 import static org.testng.Assert.assertFalse;
  43 
  44 /*
  45  * @test
  46  * @modules jdk.jextract
  47  * @build JextractToolProviderTest
  48  * @run testng/othervm -Duser.language=en JextractToolProviderTest
  49  */
  50 public class JextractToolProviderTest {
  51     private static final ToolProvider JEXTRACT_TOOL = ToolProvider.findFirst("jextract")
  52         .orElseThrow(() ->
  53             new RuntimeException("jextract tool not found")
  54         );
  55 
  56     private static String testSrcDir = System.getProperty("test.src", ".");
  57     private static String testClassesDir = System.getProperty("test.classes", ".");
  58 
  59     private static Path getFilePath(String dir, String fileName) {
  60         return Paths.get(dir, fileName).toAbsolutePath();
  61     }
  62 
  63     private static Path getInputFilePath(String fileName) {
  64         return getFilePath(testSrcDir, fileName);
  65     }
  66 
  67     private static Path getOutputFilePath(String fileName) {
  68         return getFilePath(testClassesDir, fileName);
  69     }
  70 
  71     private static int checkJextract(String expected, String... options) {
  72         StringWriter writer = new StringWriter();
  73         PrintWriter pw = new PrintWriter(writer);
  74 
  75         int result = JEXTRACT_TOOL.run(pw, pw, options);
  76         String output = writer.toString();
  77         System.err.println(output);
  78         if (expected != null) {
  79             if (!output.contains(expected)) {
  80                 throw new AssertionError("Output does not contain " + expected);
  81             }
  82         }
  83 
  84         return result;
  85     }
  86 
  87     private static void checkSuccess(String expected, String... options) {
  88         int result = checkJextract(null, options);
  89         assertEquals(result, 0, "Sucess excepted, failed: " + result);
  90     }
  91 
  92     private static void checkFailure(String expected, String... options) {
  93         int result = checkJextract(expected, options);
  94         assertNotEquals(result, 0, "Failure excepted, succeeded!");
  95     }
  96 
  97     private static void deleteFile(Path path) {
  98         try {
  99             Files.delete(path);
 100         } catch (IOException ioExp) {
 101             System.err.println(ioExp);
 102         }
 103     }
 104 
 105     private static Class<?> loadClass(Path path, String className) {
 106         try {
 107             URLClassLoader ucl = new URLClassLoader(new URL[] { path.toUri().toURL() }, null);
 108             return Class.forName(className, false, ucl);
 109         } catch (RuntimeException re) {
 110             throw re;
 111         } catch (Exception e) {
 112             throw new RuntimeException(e);
 113         }
 114     }
 115 
 116     private static Method findMethod(Class<?> cls, String name, Class<?>... argTypes) {
 117         try {
 118             return cls.getMethod(name, argTypes);
 119         } catch (RuntimeException re) {
 120             throw re;
 121         } catch (Exception e) {
 122             throw new RuntimeException(e);
 123         }
 124     }
 125 
 126     @Test
 127     public void testHelp() {
 128         checkFailure(null); // no options
 129         checkSuccess(null, "--help");
 130         checkSuccess(null, "-h");
 131         checkSuccess(null, "-?");
 132     }
 133 
 134     // error for non-existent header file
 135     @Test
 136     public void testNonExistentHeader() {
 137         checkFailure("Cannot open header file", "--dry-run",
 138             getInputFilePath("non_existent.h").toString());
 139     }
 140 
 141     @Test
 142     public void testDryRun() {
 143         // only dry-run, don't produce any output
 144         Path simpleJar = getOutputFilePath("simple.jar");
 145         deleteFile(simpleJar);
 146         checkSuccess(null, "--dry-run", getInputFilePath("simple.h").toString());
 147         try {
 148             assertFalse(Files.isRegularFile(simpleJar));
 149         } finally {
 150             deleteFile(simpleJar);
 151         }
 152     }
 153 
 154     @Test
 155     public void testOutputFileOption() {
 156         // simple output file check
 157         Path simpleJar = getOutputFilePath("simple.jar");
 158         deleteFile(simpleJar);
 159         checkSuccess(null, "-o", simpleJar.toString(),
 160             getInputFilePath("simple.h").toString());
 161         try {
 162             assertTrue(Files.isRegularFile(simpleJar));
 163         } finally {
 164             deleteFile(simpleJar);
 165         }
 166     }
 167 
 168     @Test
 169     public void testOutputClass() {
 170         Path helloJar = getOutputFilePath("hello.jar");
 171         deleteFile(helloJar);
 172         Path helloH = getInputFilePath("hello.h");
 173         checkSuccess(null, "-o", helloJar.toString(), helloH.toString());
 174         try {
 175             Class<?> cls = loadClass(helloJar, "hello");
 176             // check header annotation
 177             Header header = cls.getAnnotation(Header.class);
 178             assertNotNull(header);
 179             assertEquals(header.path(), helloH.toString());
 180 
 181             // check a method for "void func()"
 182             assertNotNull(findMethod(cls, "func", Object[].class));
 183         } finally {
 184             deleteFile(helloJar);
 185         }
 186     }
 187 
 188     @Test
 189     public void testOutputClassPackageOption() {
 190         Path helloJar = getOutputFilePath("hello.jar");
 191         deleteFile(helloJar);
 192         Path helloH = getInputFilePath("hello.h");
 193         checkSuccess(null, "-t", "com.acme", "-o", helloJar.toString(), helloH.toString());
 194         try {
 195             Class<?> cls = loadClass(helloJar, "com.acme.hello");
 196             // check header annotation
 197             Header header = cls.getAnnotation(Header.class);
 198             assertNotNull(header);
 199             assertEquals(header.path(), helloH.toString());
 200 
 201             // check a method for "void func()"
 202             assertNotNull(findMethod(cls, "func", Object[].class));
 203         } finally {
 204             deleteFile(helloJar);
 205         }
 206     }
 207 
 208     @Test
 209     public void test_option_L_without_l() {
 210         Path helloJar = getOutputFilePath("hello.jar");
 211         deleteFile(helloJar);
 212         Path helloH = getInputFilePath("hello.h");
 213         Path linkDir = getInputFilePath("libs");
 214         String warning = "WARNING: -L option specified without any -l option";
 215         checkSuccess(warning, "-L", linkDir.toString(), "-o", helloJar.toString(), helloH.toString());
 216     }
 217 
 218     @Test
 219     public void test_option_rpath_without_l() {
 220         Path helloJar = getOutputFilePath("hello.jar");
 221         deleteFile(helloJar);
 222         Path helloH = getInputFilePath("hello.h");
 223         Path rpathDir = getInputFilePath("libs");
 224         String warning = "WARNING: -rpath option specified without any -l option";
 225         checkSuccess(warning, "-rpath", rpathDir.toString(), "-o", helloJar.toString(), helloH.toString());
 226     }
 227 
 228     @Test
 229     public void test_option_l() {
 230         Path helloJar = getOutputFilePath("hello.jar");
 231         deleteFile(helloJar);
 232         Path helloH = getInputFilePath("hello.h");
 233         checkSuccess(null, "-l", "hello", "-o", helloJar.toString(), helloH.toString());
 234         try {
 235             Class<?> cls = loadClass(helloJar, "hello");
 236             // check LibraryDependencies annotation capture -l value
 237             LibraryDependencies libDeps = cls.getAnnotation(LibraryDependencies.class);
 238             assertNotNull(libDeps);
 239             assertEquals(libDeps.names().length, 1);
 240             assertEquals(libDeps.names()[0], "hello");
 241             // no library paths (rpath) set
 242             assertEquals(libDeps.paths().length, 0);
 243         } finally {
 244             deleteFile(helloJar);
 245         }
 246     }
 247 
 248     @Test
 249     public void test_option_l_and_rpath() {
 250         Path helloJar = getOutputFilePath("hello.jar");
 251         deleteFile(helloJar);
 252         Path helloH = getInputFilePath("hello.h");
 253         Path rpathDir = getInputFilePath("libs");
 254         checkSuccess(null, "-l", "hello", "-rpath", rpathDir.toString(),
 255              "-o", helloJar.toString(), helloH.toString());
 256         try {
 257             Class<?> cls = loadClass(helloJar, "hello");
 258             // check LibraryDependencies annotation captures -l and -rpath values
 259             LibraryDependencies libDeps = cls.getAnnotation(LibraryDependencies.class);
 260             assertNotNull(libDeps);
 261             assertEquals(libDeps.names().length, 1);
 262             assertEquals(libDeps.names()[0], "hello");
 263             assertEquals(libDeps.paths().length, 1);
 264             assertEquals(libDeps.paths()[0], rpathDir.toString());
 265         } finally {
 266             deleteFile(helloJar);
 267         }
 268     }
 269 }